/// <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); // 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> /// 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> /// 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> /// polygonshape and the shape extent is the same as the part extent.</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. double x1 = point.X; double y1 = point.Y; double x2 = ext.XMax; Segment ray = new Segment(point.X, point.Y, ext.XMax, 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; // Regardless of number of parts or holiness, 1 crossing can only be inside. if(totalCrosses == 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; }