//------------------------- DistToLineSegmentSq ---------------------------- // // as above, but avoiding Math.Sqrt //------------------------------------------------------------------------ public static double DistToLineSegmentSq(Vector2D A, Vector2D B, Vector2D P) { //if the angle is obtuse between PA and AB is obtuse then the closest //vertex must be A double dotA = (P.X - A.X) * (B.X - A.X) + (P.Y - A.Y) * (B.Y - A.Y); if (dotA <= 0) { return(Vector2D.Vec2DDistanceSq(A, P)); } //if the angle is obtuse between PB and AB is obtuse then the closest //vertex must be B double dotB = (P.X - B.X) * (A.X - B.X) + (P.Y - B.Y) * (A.Y - B.Y); if (dotB <= 0) { return(Vector2D.Vec2DDistanceSq(B, P)); } //calculate the point along AB that is the closest to P Vector2D Point = A + ((B - A) * dotA) / (dotA + dotB); //calculate the distance P-Point return(Vector2D.Vec2DDistanceSq(P, Point)); }
//-------------------- GetEntityLineSegmentIntersections ---------------------- // // tests a line segment AB against a container of entities. First of all // a test is made to confirm that the entity is within a specified range of // the one_to_ignore (positioned at A). If within range the intersection test // is made. // // returns a list of all the entities that tested positive for intersection //----------------------------------------------------------------------------- public static List <BaseGameEntity> GetEntityLineSegmentIntersections(List <BaseGameEntity> entities, int the_one_to_ignore, Vector2D A, Vector2D B, double range) { List <BaseGameEntity> hits = new List <BaseGameEntity>(); //iterate through all entities checking against the line segment AB foreach (BaseGameEntity it in entities) { //if not within range or the entity being checked is the_one_to_ignore //just continue with the next entity if ((it.ID() == the_one_to_ignore) || (Vector2D.Vec2DDistanceSq(it.Pos, A) > range * range)) { continue; } //if the distance to AB is less than the entities bounding radius then //there is an intersection so add it to hits if (DistToLineSegment(A, B, it.Pos) < it.BRadius) { hits.Add(it); } } return(hits); }
//------------------- GetLineSegmentCircleClosestIntersectionPoint ------------ // // given a line segment AB and a circle position and radius, this function // determines if there is an intersection and stores the position of the // closest intersection in the reference IntersectionPoint // // returns false if no intersection point is found //----------------------------------------------------------------------------- public static bool GetLineSegmentCircleClosestIntersectionPoint(Vector2D A, Vector2D B, Vector2D pos, double radius, ref Vector2D IntersectionPoint) { Vector2D toBNorm = Vector2D.Vec2DNormalize(B - A); //move the circle into the local space defined by the vector B-A with origin //at A Vector2D LocalPos = PointToLocalSpace(pos, toBNorm, toBNorm.Perp(), A); bool ipFound = false; //if the local position + the radius is negative then the circle lays behind //point A so there is no intersection possible. If the local x pos minus the //radius is greater than length A-B then the circle cannot intersect the //line segment if ((LocalPos.X + radius >= 0) && ((LocalPos.X - radius) * (LocalPos.X - radius) <= Vector2D.Vec2DDistanceSq(B, A))) { //if the distance from the x axis to the object's position is less //than its radius then there is a potential intersection. if (Math.Abs(LocalPos.Y) < radius) { //now to do a line/circle intersection test. The center of the //circle is represented by A, B. The intersection points are //given by the formulae x = A +/-Math.Sqrt(r^2-B^2), y=0. We only //need to look at the smallest positive value of x. double a = LocalPos.X; double b = LocalPos.Y; double ip = a - Math.Sqrt(radius * radius - b * b); if (ip <= 0) { ip = a + Math.Sqrt(radius * radius - b * b); } ipFound = true; IntersectionPoint = A + toBNorm * ip; } } return(ipFound); }
//------------------------ GetClosestEntityLineSegmentIntersection ------------ // // tests a line segment AB against a container of entities. First of all // a test is made to confirm that the entity is within a specified range of // the one_to_ignore (positioned at A). If within range the intersection test // is made. // // returns the closest entity that tested positive for intersection or NULL // if none found //----------------------------------------------------------------------------- public static BaseGameEntity GetClosestEntityLineSegmentIntersection(List <BaseGameEntity> entities, int the_one_to_ignore, Vector2D A, Vector2D B, double range) { BaseGameEntity ClosestEntity = null; double ClosestDist = Double.MaxValue; //iterate through all entities checking against the line segment AB foreach (BaseGameEntity it in entities) { double distSq = Vector2D.Vec2DDistanceSq(it.Pos, A); //if not within range or the entity being checked is the_one_to_ignore //just continue with the next entity if ((it.ID() == the_one_to_ignore) || (distSq > range * range)) { continue; } //if the distance to AB is less than the entities bounding radius then //there is an intersection so add it to hits if (DistToLineSegment(A, B, it.Pos) < it.BRadius) { if (distSq < ClosestDist) { ClosestDist = distSq; ClosestEntity = it; } } } return(ClosestEntity); }