/// <summary> /// Uses the intersection count to detect if there is an intersection /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Intersects(Segment other) { return (IntersectionCount(other) > 0); }
/// <summary> /// Determines the shortest distance between two segments /// </summary> /// <param name="lineSegment">Segment, The line segment to test against this segment</param> /// <returns>Double, the shortest distance between two segments</returns> public double DistanceTo(Segment lineSegment) { //http://www.geometryalgorithms.com/Archive/algorithm_0106/algorithm_0106.htm const double smallNum = 0.00000001; Vector u = ToVector(); // Segment 1 Vector v = lineSegment.ToVector(); // Segment 2 Vector w = ToVector(); double a = u.Dot(u); // length of segment 1 double b = u.Dot(v); // length of segment 2 projected onto line 1 double c = v.Dot(v); // length of segment 2 double d = u.Dot(w); // double e = v.Dot(w); double dist = a * c - b * b; double sc, sN, sD = dist; double tc, tN, tD = dist; // compute the line parameters of the two closest points if (dist < smallNum) { // the lines are almost parallel force using point P0 on segment 1 // to prevent possible division by 0 later sN = 0.0; sD = 1.0; tN = e; tD = c; } else { // get the closest points on the infinite lines sN = (b * e - c * d); tN = (a * e - b * d); if (sN < 0.0) { // sc < 0 => the s=0 edge is visible sN = 0.0; tN = e; tD = c; } else if (sN > sD) { // sc > 1 => the s=1 edge is visible sN = sD; tN = e + b; tD = c; } } if (tN < 0.0) { // tc < 0 => the t=0 edge is visible tN = 0.0; // recompute sc for this edge if (-d < 0.0) { sN = 0.0; } else if (-d > a) { sN = sD; } else { sN = -d; sD = a; } } else if (tN > tD) { // tc > 1 => the t = 1 edge is visible // recompute sc for this edge if ((-d + b) < 0.0) { sN = 0; } else if ((-d + b) > a) { sN = sD; } else { sN = (-d + b); sD = a; } } // finally do the division to get sc and tc if (Math.Abs(sN) < smallNum) { sc = 0.0; } else { sc = sN / sD; } if (Math.Abs(tN) < smallNum) { tc = 0.0; } else { tc = tN / tD; } // get the difference of the two closest points Vector dU = u.Multiply(sc); Vector dV = v.Multiply(tc); Vector dP = (w.Add(dU)).Subtract(dV); // S1(sc) - S2(tc) return dP.Length2D; }
/// <summary> /// Returns 0 if no intersections occur, 1 if an intersection point is found, /// and 2 if the segments are colinear and overlap. /// </summary> /// <param name="other"></param> /// <returns></returns> public int IntersectionCount(Segment other) { double x1 = P1.X; double y1 = P1.Y; double x2 = P2.X; double y2 = P2.Y; double x3 = other.P1.X; double y3 = other.P1.Y; double x4 = other.P2.X; double y4 = other.P2.Y; double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); //The case of two degenerate segements if ((x1 == x2) && (y1 == y2) && (x3 == x4) && (y3 == y4)) { if ((x1 != x3) || (y1 != y3)) return 0; } // if denom is 0, then the two lines are parallel double na = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); double nb = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); // if denom is 0 AND na and nb are 0, then the lines are coincident and DO intersect if (Math.Abs(denom) < Epsilon && Math.Abs(na) < Epsilon && Math.Abs(nb) < Epsilon) return 2; // If denom is 0, but na or nb are not 0, then the lines are parallel and not coincident if (denom == 0) return 0; double ua = na / denom; double ub = nb / denom; if (ua < 0 || ua > 1) return 0; // not intersecting with segment a if (ub < 0 || ub > 1) return 0; // not intersecting with segment b // If we get here, then one intersection exists and it is found on both line segments return 1; }
/// <summary> /// For each coordinate in the other part, if it falls in the extent of this polygon, a /// ray crossing test is used for point in polygon testing. If it is not in the extent, /// it is skipped. /// </summary> /// <param name="polygonShape">The part of the polygon to analyze polygon</param> /// <param name="otherPart">The other part</param> /// <returns>Boolean, true if any coordinate falls inside the polygon</returns> private static bool ContainsVertex(ShapeRange polygonShape, PartRange otherPart) { // Create an extent for faster checking in most cases Extent ext = polygonShape.Extent; foreach (Vertex point in otherPart) { // This extent check shortcut should help speed things up for large polygon parts if (!ext.Intersects(point)) continue; // Imagine a ray on the horizontal starting from point.X -> infinity. (In practice this can be ext.XMax) // Count the intersections of segments with that line. If the resulting count is odd, the point is inside. Segment ray = new Segment(point.X, point.Y, ext.MaxX, point.Y); int[] numCrosses = new int[polygonShape.NumParts]; // A cross is a complete cross. Coincident doesn't count because it is either 0 or 2 crosses. int totalCrosses = 0; int iPart = 0; foreach (PartRange ring in polygonShape.Parts) { foreach (Segment segment in ring.Segments) { if (segment.IntersectionCount(ray) != 1) continue; numCrosses[iPart]++; totalCrosses++; } iPart++; } // If we didn't actually have any polygons we cant intersect with anything if (polygonShape.NumParts < 1) return false; // For shapes with only one part, we don't need to test part-containment. if (polygonShape.NumParts == 1 && totalCrosses % 2 == 1) return true; // This used to check to see if totalCrosses == 1, but now checks to see if totalCrosses is an odd number. // This change was made to solve the issue described in HD Issue 8593 (http://hydrodesktop.codeplex.com/workitem/8593). if (totalCrosses % 2 == 1) return true; totalCrosses = 0; for (iPart = 0; iPart < numCrosses.Length; iPart++) { int count = numCrosses[iPart]; // If this part does not contain the point, don't bother trying to figure out if the part is a hole or not. if (count % 2 == 0) continue; // If this particular part is a hole, subtract the total crosses by 1, otherwise add one. // This takes time, so we want to do this as few times as possible. if (polygonShape.Parts[iPart].IsHole()) { totalCrosses--; } else { totalCrosses++; } } return totalCrosses > 0; } return false; }