// http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ vec2 barycenter(const Polygon2d& P) { gx_assert(P.size() > 0) ; double A = signed_area(P) ; if(::fabs(A) < 1e-30) { return P[0] ; } double x = 0.0 ; double y = 0.0 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = (i+1) % P.size() ; const vec2& t1 = P[i] ; const vec2& t2 = P[j] ; double d = (t1.x * t2.y - t2.x * t1.y) ; x += (t1.x + t2.x) * d ; y += (t1.y + t2.y) * d ; } return vec2( x / (6.0 * A), y / (6.0 * A) ) ; }
bool GD_EXTENSION_API SingleTileCollision(std::map<gd::String, std::vector<RuntimeObject*>*> tileMapList, int layer, int column, int row, std::map<gd::String, std::vector<RuntimeObject*>*> objectLists, bool conditionInverted) { return TwoObjectListsTest(tileMapList, objectLists, conditionInverted, [layer, column, row](RuntimeObject* tileMapObject_, RuntimeObject * object) { RuntimeTileMapObject *tileMapObject = dynamic_cast<RuntimeTileMapObject*>(tileMapObject_); if(!tileMapObject || tileMapObject->tileSet.Get().IsDirty()) return false; //Get the tile hitbox int tileId = tileMapObject->tileMap.Get().GetTile(layer, column, row); if(tileId < 0 || tileId >= tileMapObject->tileSet.Get().GetTilesCount()) return false; Polygon2d tileHitbox = tileMapObject->tileSet.Get().GetTileHitbox(tileId).hitbox; tileHitbox.Move(tileMapObject->GetX() + column * tileMapObject->tileSet.Get().tileSize.x, tileMapObject->GetY() + row * tileMapObject->tileSet.Get().tileSize.y); //Get the object hitbox std::vector<Polygon2d> objectHitboxes = object->GetHitBoxes(); for(std::vector<Polygon2d>::iterator hitboxIt = objectHitboxes.begin(); hitboxIt != objectHitboxes.end(); ++hitboxIt) { if(PolygonCollisionTest(tileHitbox, *hitboxIt).collision) { return true; } } return false; }); }
std::vector<Polygon2d> GenerateHitboxes(TileSet &tileSet, TileMap &tileMap) { std::vector<Polygon2d> hitboxes; const int tileWidth = tileSet.tileSize.x; const int tileHeight = tileSet.tileSize.y; if(tileSet.IsDirty()) return hitboxes; for(int layer = 0; layer < 3; layer++) { for(int col = 0; col < tileMap.GetColumnsCount(); col++) { for(int row = 0; row < tileMap.GetRowsCount(); row++) { //Note : a hitbox is also added for empty/non-collidable tiles to ease the hitbox update when changing a tile Polygon2d newPolygon; if(tileMap.GetTile(layer, col, row) != -1 && tileSet.GetTileHitbox(tileMap.GetTile(layer, col, row)).collidable) { newPolygon = tileSet.GetTileHitbox(tileMap.GetTile(layer, col, row)).hitbox; } newPolygon.Move(col * tileWidth, row * tileHeight); hitboxes.push_back(newPolygon); } } } return hitboxes; }
std::vector<Polygon2d> RuntimeObject::GetHitBoxes() const { std::vector<Polygon2d> mask; Polygon2d rectangle = Polygon2d::CreateRectangle(GetWidth(), GetHeight()); rectangle.Rotate(GetAngle() / 180 * 3.14159); rectangle.Move(GetX() + GetCenterX(), GetY() + GetCenterY()); mask.push_back(rectangle); return mask; }
void minimum_area_enclosing_rectangle( const Polygon2d& PP, vec2& S, vec2& T ) { // Note: this implementation has O(n2) complexity :-( // (where n is the number of vertices in the convex hull) // If this appears to be a bottleneck, use a smarter // implementation with better complexity. Polygon2d P ; convex_hull(PP, P) ; int N = P.size() ; // Add the first vertex at the end of P P.push_back(P[0]) ; double min_area = Numeric::big_double ; for(int i=1; i<=N; i++) { vec2 Si = P[i] - P[i-1] ; if( ( Si.length2() ) < 1e-20) { continue ; } vec2 Ti(-Si.y, Si.x) ; normalize(Si) ; normalize(Ti) ; double s0 = Numeric::big_double ; double s1 = -Numeric::big_double ; double t0 = Numeric::big_double ; double t1 = -Numeric::big_double ; for(int j=1; j<N; j++) { vec2 D = P[j] - P[0] ; double s = dot(Si, D) ; s0 = gx_min(s0, s) ; s1 = gx_max(s1, s) ; double t = dot(Ti, D) ; t0 = gx_min(t0, t) ; t1 = gx_max(t1, t) ; } double area = (s1 - s0) * (t1 - t0) ; if(area < min_area) { min_area = area ; if((s1 - s0) < (t1 - t0)) { S = Si ; T = Ti ; } else { S = Ti ; T = Si ; } } } }
void convex_clip_segment( Segment2d& S, const Polygon2d& window ) { gx_parano_assert(polygon_is_convex(window)) ; bool invert = (signed_area(window) < 0) ; for(unsigned int i=0; i<window.size(); i++) { unsigned int j = ((i+1) % window.size()) ; clip_segment_by_half_plane(S, window[i], window[j], invert) ; } }
// http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ double signed_area(const Polygon2d& P) { double result = 0 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = (i+1) % P.size() ; const vec2& t1 = P[i] ; const vec2& t2 = P[j] ; result += t1.x * t2.y - t2.x * t1.y ; } result /= 2.0 ; return result ; }
Polygon2d Polygon2d::scaleaboutcentroid(float scale) { Polygon2d n; V2d cen=centroid(); for (int i=1;i<=vs.len;i++) { V2d x=vs.num(i); x=x-cen; x=x*scale; x=x+cen; n.add(x); } return n; }
void save_polygon(const Polygon2d& P, const std::string& file_name) { std::ofstream out(file_name.c_str()) ; {for(unsigned int i=0; i<P.size(); i++) { out << "v " << P[i].x << " " << P[i].y << std::endl ; out << "vt " << P[i].x << " " << P[i].y << std::endl ; }} out << "f " ; {for(unsigned int i=0; i<P.size(); i++) { out << i+1 << "/" << i+1 << " " ; }} out << std::endl ; }
vec2 vertices_barycenter(const Polygon2d& P) { gx_assert(P.size() != 0) ; double x = 0 ; double y = 0 ; for(unsigned int i=0; i<P.size(); i++) { x += P[i].x ; y += P[i].y ; } x /= double(P.size()) ; y /= double(P.size()) ; return vec2(x,y) ; }
void clip_polygon_by_half_plane( const Polygon2d& P, const vec2& q1, const vec2& q2, Polygon2d& result, bool invert ) { result.clear() ; if(P.size() == 0) { return ; } if(P.size() == 1) { if(point_is_in_half_plane(P[0], q1, q2, invert)) { result.push_back(P[0]) ; } return ; } vec2 prev_p = P[P.size() - 1] ; Sign prev_status = point_is_in_half_plane( prev_p, q1, q2, invert ) ; for(unsigned int i=0; i<P.size(); i++) { vec2 p = P[i] ; Sign status = point_is_in_half_plane( p, q1, q2, invert ) ; if( status != prev_status && status != ZERO && prev_status != ZERO ) { vec2 intersect ; if(intersect_segments(prev_p, p, q1, q2, intersect)) { result.push_back(intersect) ; } else { } } switch(status) { case NEGATIVE: break ; case ZERO: result.push_back(p) ; break ; case POSITIVE: result.push_back(p) ; break ; } prev_p = p ; prev_status = status ; } }
void convex_hull(const Polygon2d& PP, Polygon2d& result) { result.clear() ; int n = PP.size() ; vec2* P = new vec2[n+1] ; { for(int i=0; i<n; i++) { P[i] = PP[i] ; }} int u = make_chain(P, n, cmpl); P[n] = P[0]; int ch = u+make_chain(P+u, n-u+1, cmph); {for(int i=0; i<ch; i++) { result.push_back(P[i]) ; }} delete[] P ; }
bool polygon_is_convex(const Polygon2d& P) { Sign s = ZERO ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; unsigned int k = ((j+1) % P.size()) ; Sign cur_s = orient(P[i],P[j],P[k]) ; if(s != ZERO && cur_s != ZERO && cur_s != s) { return false ; } if(cur_s != ZERO) { s = cur_s ; } } return true ; }
PolygonShape::PolygonShape(const Polygon2d& polygon, Material& attrs): m_polygon(polygon) { b2Vec2* vertecies = new b2Vec2[polygon.VerteciesCount()]; const Vertecies& polygonVertecies = polygon.GetVertecies(); for (size_t vertexNo = 0; vertexNo < polygonVertecies.size(); ++vertexNo) { vertecies[vertexNo] = ToBox2dVec(polygonVertecies[vertexNo]); } m_polygonShapePtr = new b2PolygonShape(); m_polygonShapePtr->Set(vertecies, polygon.VerteciesCount()); delete[] vertecies; Construct(m_polygonShapePtr, attrs); }
bool point_is_in_kernel(const Polygon2d& P, const vec2& p) { Sign sign = ZERO ; for(unsigned int i=0 ; i<P.size() ; i++) { unsigned int j = (i+1) % P.size() ; const vec2& p1 = P[i] ; const vec2& p2 = P[j] ; Sign cur_sign = orient(p, p1, p2) ; if(sign == ZERO) { sign = cur_sign ; } else { if(cur_sign != ZERO && cur_sign != sign) { return false ; } } } return true ; }
// Clipping with convex window using Sutherland-Hogdman reentrant clipping void convex_clip_polygon( const Polygon2d& P, const Polygon2d& clip, Polygon2d& result ) { gx_parano_assert(polygon_is_convex(clip)) ; Polygon2d tmp1 = P ; bool invert = (signed_area(tmp1) != signed_area(clip)) ; Polygon2d tmp2 ; Polygon2d* src = &tmp1 ; Polygon2d* dst = &tmp2 ; for(unsigned int i=0; i<clip.size(); i++) { unsigned int j = ((i+1) % clip.size()) ; const vec2& p1 = clip[i] ; const vec2& p2 = clip[j] ; clip_polygon_by_half_plane(*src, p1, p2, *dst, invert) ; gx_swap(src, dst) ; } result = *src ; }
// Compute the kernel using Sutherland-Hogdman reentrant clipping // The kernel is obtained by clipping the polygon with each // half-plane yielded by its sides. void kernel(const Polygon2d& P, Polygon2d& result) { Array1d<Sign> sign(P.size()) ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; unsigned int k = ((j+1) % P.size()) ; sign(j) = orient(P[i],P[j],P[k]) ; } bool invert = (signed_area(P) < 0) ; Polygon2d tmp1 = P ; Polygon2d tmp2 ; Polygon2d* src = &tmp1 ; Polygon2d* dst = &tmp2 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; const vec2& p1 = P[i] ; const vec2& p2 = P[j] ; if((p2-p1).length() == 0) { std::cerr << "null edge in poly" << std::endl ; continue ; } // Optimization: do not clip by convex-convex edges // (Thanks to Rodrigo Toledo for the tip !) if(!invert && sign(i) != NEGATIVE && sign(j) != NEGATIVE) { continue ; } if(invert && sign(i) != POSITIVE && sign(j) != POSITIVE) { continue ; } clip_polygon_by_half_plane(*src, p1, p2, *dst, invert) ; gx_swap(src, dst) ; } result = *src ; }
bool Polygon2d::overlaps(Polygon2d p) { // does not seem to work // return overlaps(&p); for (int i=1;i<=p.vs.len;i++) if (crosses(p.linefrom(i))) return true; bool pin=true; bool thisin=true; for (int i=1;i<=p.vs.len && pin;i++) if (!contains(p.vs.num(i))) pin=false; for (int i=1;i<=vs.len && thisin;i++) if (!p.contains(vs.num(i))) thisin=false; if (pin) return container; // this polygon completely contains p if (thisin) return contained; return false; }
void D3DRenderContext::RenderSolidPolygon(const Polygon2d& p, const ivec3& color) const { WireGeometryVertex* points = new WireGeometryVertex[p.VerteciesCount() + 1]; for (uint i = 0; i <= p.VerteciesCount(); ++i) { if (i == p.VerteciesCount()) { points[i].x = p[0].x; points[i].y = p[0].y; points[i].z = MAX_ZCHOOORD; points[i].color = D3DCOLOR_XRGB(color.x, color.y, color.z); } else { points[i].x = p[i].x; points[i].y = p[i].y; points[i].z = MAX_ZCHOOORD; points[i].color = D3DCOLOR_XRGB(color.x, color.y, color.z); } } m_d3dDevice->SetFVF(D3DFVF_WIRE_GEOMETRY_VERTEX); m_d3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, p.VerteciesCount(), static_cast<void*>(points), sizeof(WireGeometryVertex)); delete[] points; }
float offness(Polygon2d p) { Region r=Region(p,oi->width,oi->height); r.makelist(); V2d v=V2d(0,0); int cnt=0; for (int i=1;i<=r.list->len;i++) { int x=r.list->num(i).x; int y=r.list->num(i).y; if (angs->inmap(x,y)) { v=v+mag->pos[x][y]*V2d::rotate(V2d(1,0),angs->pos[x][y]); cnt++; } } float offness=v.mod()/(float)cnt; printf("Quad has offness %f count %i area %f\n",offness,cnt,p.area()); return offness; }
float areaofpoly(Polygon2d p) { return p.area(); }
float Polygon2d::area(Polygon2d p) { return p.area(); }
Polygon2d Polygon2d::operator*(float f) { Polygon2d p; for (int i=1;i<=vs.len;i++) p.add(vs.num(i)*f); return p; }
bool TileMapImporter::ImportTileMap(TileSet &tileSet, TileMap &tileMap, bool importTileMap, bool importTileSetConf, bool importTileSetImage, bool importHitboxes, gd::Project &project) { //Checks the map type if(m_map->GetOrientation() != Tmx::TMX_MO_ORTHOGONAL) { gd::LogError(_("Only orthogonal maps are supported !")); return false; } //Get the tileset list if(m_map->GetNumTilesets() < 1) { gd::LogError(_("There are no tilesets in this file !")); return false; } else if(m_map->GetNumTilesets() > 1) { gd::LogWarning(_("Only the first tileset will be taken into account. Tiles from supplementary tilesets may be lost.")); } //Import the tileset image if needed if(importTileSetImage) { const Tmx::Image *importedImage = m_map->GetTileset(0)->GetImage(); wxFileName imageFileName(importedImage->GetSource()); imageFileName.MakeAbsolute(wxFileName(m_filePath).GetPath()); if(!imageFileName.FileExists()) { gd::LogError(_("The image can't be found !")); return false; } gd::String newResourceName = gd::NewNameGenerator::Generate( u8"imported_" + imageFileName.GetFullName(), [&project](const gd::String &name) -> bool { return project.GetResourcesManager().HasResource(name); } ); gd::LogMessage(_("The image is imported as ") + "\"" + newResourceName + "\"."); imageFileName.MakeRelativeTo(wxFileName(project.GetProjectFile()).GetPath()); project.GetResourcesManager().AddResource(newResourceName, imageFileName.GetFullPath(), "image"); tileSet.textureName = newResourceName; //Reload the texture tileSet.LoadResources(project); gd::LogStatus(_("Tileset image importation completed.")); } //Import the tileset configuration if wanted if(importTileSetConf) { const Tmx::Tileset *importedTileset = m_map->GetTileset(0); if(importedTileset->GetImage()->GetWidth() != tileSet.GetWxBitmap().GetWidth() || importedTileset->GetImage()->GetHeight() != tileSet.GetWxBitmap().GetHeight()) { gd::LogWarning(_("Tileset image size is not the same. Some tiles may not be rendered correctly.")); } tileSet.tileSize.x = importedTileset->GetTileWidth(); tileSet.tileSize.y = importedTileset->GetTileHeight(); tileSet.tileSpacing.x = tileSet.tileSpacing.y = importedTileset->GetSpacing(); if(importedTileset->GetMargin() > 0) { gd::LogWarning(_("Tilemap objects don't handle tilesets with margins around the images. Consider cutting the picture.")); } gd::LogStatus(_("Tileset configuration importation completed.")); } //Import the tilemap tiles if wanted if(importTileMap) { //Tilemap size if(tileMap.GetColumnsCount() != m_map->GetWidth() || tileMap.GetRowsCount() != m_map->GetHeight()) gd::LogMessage(_("Tilemap size is different.")); tileMap.SetSize(0, 0); tileMap.SetSize(m_map->GetWidth(), m_map->GetHeight()); if(!importTileSetConf && !importTileSetImage) CheckTilesCount(tileSet); //Import layers and tiles if(m_map->GetNumTileLayers() > 3) { gd::LogWarning(_("There are more than 3 tiles layers. Only the 3 firsts will be imported.")); } else if(m_map->GetNumTileLayers() < 3) { gd::LogMessage(_("There are less than 3 tiles layers. Upper layer(s) will be empty.")); } for(std::size_t i = 0; i < std::min(3, m_map->GetNumTileLayers()); i++) { const Tmx::TileLayer *layer = m_map->GetTileLayer(i); for(std::size_t x = 0; x < tileMap.GetColumnsCount(); x++) { for(std::size_t y = 0; y < tileMap.GetRowsCount(); y++) { //Only tiles provided by the first tileset are imported (and also tests for empty tiles) if(m_map->FindTilesetIndex(layer->GetTileGid(x, y)) == 0) { tileMap.SetTile(i, x, y, layer->GetTileId(x, y)); } } } } gd::LogStatus(_("Tilemap content importation completed.")); } //Import the hitboxes if(importHitboxes) { const Tmx::Tileset *importedTileset = m_map->GetTileset(0); //Set all tiles not collidable in the tileset tileSet.ResetHitboxes(); for(std::size_t i = 0; i < tileSet.GetTilesCount(); i++) tileSet.SetTileCollidable(i, false); if(!importTileSetConf && !importTileSetImage) CheckTilesCount(tileSet); bool hasMoreThanOneObjectPerTile = false; bool hasNotPolygoneObject = false; bool hasNotConvexPolygon = false; for(auto it = importedTileset->GetTiles().cbegin(); it != importedTileset->GetTiles().cend(); ++it) { const Tmx::Tile *importedTile = *it; if(importedTile->GetId() < tileSet.GetTilesCount()) //Check if the tileset has enough tiles to receive the imported hitboxes { if(importedTile->HasObjects()) { //Set the tile collidable and gets its hitbox tileSet.SetTileCollidable(importedTile->GetId(), true); TileHitbox &tileHitbox = tileSet.GetTileHitboxRef(importedTile->GetId()); //Warn the user if more than one hitbox per tile is found if(importedTile->GetNumObjects() > 1) hasMoreThanOneObjectPerTile = true; const Tmx::Object *importedObj = importedTile->GetObject(0); if(!importedObj->GetPolyline() && !importedObj->GetEllipse()) { Polygon2d polygonHitbox; if(!importedObj->GetPolygon()) { //This is a rectangle polygonHitbox = Polygon2d::CreateRectangle(importedObj->GetWidth(), importedObj->GetHeight()); polygonHitbox.Move( importedObj->GetWidth() / 2.f, importedObj->GetHeight() / 2.f ); } else { //This is a polygon const Tmx::Polygon *importedPolygon = importedObj->GetPolygon(); for(int i = 0; i < importedPolygon->GetNumPoints(); i++) { polygonHitbox.vertices.emplace_back( importedPolygon->GetPoint(i).x, importedPolygon->GetPoint(i).y ); } } polygonHitbox.Move(importedObj->GetX(), importedObj->GetY()); polygonHitbox.Rotate(importedObj->GetRot()); if(polygonHitbox.IsConvex()) tileHitbox.hitbox = polygonHitbox; else hasNotConvexPolygon = true; } else { //This is not a supported shape hasNotPolygoneObject = true; } } } } if(hasMoreThanOneObjectPerTile) gd::LogWarning(_("Some tiles have more than 1 hitbox. Only the first one is imported.")); if(hasNotPolygoneObject) gd::LogWarning(_("Some tiles have a polyline or a ellipsis hitbox. Only rectangle and polygon hitboxes are supported.")); if(hasNotConvexPolygon) gd::LogWarning(_("Some tiles have a concave polygon. It has been ignored and set to a rectangular hitbox as this object only supports convex hitboxes for tiles.")); gd::LogStatus(_("Tiles hitboxes importation completed.")); } return true; }
CollisionResult GD_API PolygonCollisionTest(Polygon2d& p1, Polygon2d& p2, bool ignoreTouchingEdges) { if (p1.vertices.size() < 3 || p2.vertices.size() < 3) { CollisionResult result; result.collision = false; result.move_axis.x = 0.0f; result.move_axis.y = 0.0f; return result; } p1.ComputeEdges(); p2.ComputeEdges(); sf::Vector2f edge; sf::Vector2f move_axis(0, 0); sf::Vector2f mtd(0, 0); float min_dist = FLT_MAX; CollisionResult result; // Iterate over all the edges composing the polygons for (std::size_t i = 0; i < p1.vertices.size() + p2.vertices.size(); i++) { if (i < p1.vertices.size()) // or <= { edge = p1.edges[i]; } else { edge = p2.edges[i - p1.vertices.size()]; } sf::Vector2f axis( -edge.y, edge.x); // Get the axis to which polygons will be projected normalise(axis); float minA = 0; float minB = 0; float maxA = 0; float maxB = 0; project(axis, p1, minA, maxA); project(axis, p2, minB, maxB); float dist = distance(minA, maxA, minB, maxB); if (dist > 0.0f || (dist == 0.0 && ignoreTouchingEdges)) { // If the projections on the axis do not overlap, then // there is no collision result.collision = false; result.move_axis.x = 0.0f; result.move_axis.y = 0.0f; return result; } float absDist = std::abs(dist); if (absDist < min_dist) { min_dist = absDist; move_axis = axis; } } result.collision = true; sf::Vector2f d = p1.ComputeCenter() - p2.ComputeCenter(); if (dotProduct(d, move_axis) < 0.0f) move_axis = -move_axis; result.move_axis = move_axis * min_dist; return result; }
RaycastResult GD_API PolygonRaycastTest( Polygon2d& poly, float startX, float startY, float endX, float endY) { RaycastResult result; result.collision = false; if (poly.vertices.size() < 2) { return result; } poly.ComputeEdges(); sf::Vector2f p, q, r, s; float minSqDist = FLT_MAX; // Ray segment: p + t*r, with p = start and r = end - start p.x = startX; p.y = startY; r.x = endX - startX; r.y = endY - startY; for (int i = 0; i < poly.edges.size(); i++) { // Edge segment: q + u*s q = poly.vertices[i]; s = poly.edges[i]; sf::Vector2f deltaQP = q - p; float crossRS = crossProduct(r, s); float t = crossProduct(deltaQP, s) / crossRS; float u = crossProduct(deltaQP, r) / crossRS; // Collinear if (abs(crossRS) <= 0.0001 && abs(crossProduct(deltaQP, r)) <= 0.0001) { // Project the ray and the edge to work on floats, keeping linearity // through t sf::Vector2f axis(r.x, r.y); normalise(axis); float rayA = 0.0f; float rayB = dotProduct(axis, r); float edgeA = dotProduct(axis, deltaQP); float edgeB = dotProduct(axis, deltaQP + s); // Get overlapping range float minOverlap = std::max(std::min(rayA, rayB), std::min(edgeA, edgeB)); float maxOverlap = std::min(std::max(rayA, rayB), std::max(edgeA, edgeB)); if (minOverlap > maxOverlap) { return result; } result.collision = true; // Zero distance ray if (rayB == 0.0f) { result.closePoint = p; result.closeSqDist = 0.0f; result.farPoint = p; result.farSqDist = 0.0f; } float t1 = minOverlap / abs(rayB); float t2 = maxOverlap / abs(rayB); result.closePoint = p + t1 * r; result.closeSqDist = t1 * t1 * (r.x * r.x + r.y * r.y); result.farPoint = p + t2 * r; result.farSqDist = t2 * t2 * (r.x * r.x + r.y * r.y); return result; } else if (crossRS != 0 && 0 <= t && t <= 1 && 0 <= u && u <= 1) { sf::Vector2f point = p + t * r; float sqDist = (point.x - startX) * (point.x - startX) + (point.y - startY) * (point.y - startY); if (sqDist < minSqDist) { if (!result.collision) { result.farPoint = point; result.farSqDist = sqDist; } minSqDist = sqDist; result.closePoint = point; result.closeSqDist = sqDist; result.collision = true; } else { result.farPoint = point; result.farSqDist = sqDist; } } } return result; }