using System; using GeoAPI.Geometries; namespace GisSharpBlog.NetTopologySuite.Algorithm { /// /// A non-robust version of LineIntersector. /// public class NonRobustLineIntersector : LineIntersector { /// /// /// public NonRobustLineIntersector() {} /// /// /// /// /// /// /// true if both numbers are positive or if both numbers are negative, /// false if both numbers are zero. /// public static bool IsSameSignAndNonZero(double a, double b) { if (a == 0 || b == 0) { return false; } return (a < 0 && b < 0) || (a > 0 && b > 0); } /// /// /// /// /// /// public override void ComputeIntersection(ICoordinate p, ICoordinate p1, ICoordinate p2) { double a1; double b1; double c1; /* * Coefficients of line eqns. */ double r; /* * 'Sign' values */ isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 * is "a1 x + b1 y + c1 = 0". */ a1 = p2.Y - p1.Y; b1 = p1.X - p2.X; c1 = p2.X*p1.Y - p1.X*p2.Y; /* * Compute r3 and r4. */ r = a1*p.X + b1*p.Y + c1; // if r != 0 the point does not lie on the line if (r != 0) { result = DontIntersect; return; } // Point lies on line - check to see whether it lies in line segment. double dist = RParameter(p1, p2, p); if (dist < 0.0 || dist > 1.0) { result = DontIntersect; return; } isProper = true; if (p.Equals(p1) || p.Equals(p2)) { isProper = false; } result = DoIntersect; } /// /// /// /// /// /// /// /// public override int ComputeIntersect(ICoordinate p1, ICoordinate p2, ICoordinate p3, ICoordinate p4) { double a1; double b1; double c1; double a2; double b2; double c2; /* * Coefficients of line eqns. */ double r1; double r2; double r3; double r4; /* * 'Sign' values */ isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 * is "a1 x + b1 y + c1 = 0". */ a1 = p2.Y - p1.Y; b1 = p1.X - p2.X; c1 = p2.X*p1.Y - p1.X*p2.Y; /* * Compute r3 and r4. */ r3 = a1*p3.X + b1*p3.Y + c1; r4 = a1*p4.X + b1*p4.Y + c1; /* * Check signs of r3 and r4. If both point 3 and point 4 lie on * same side of line 1, the line segments do not intersect. */ if (r3 != 0 && r4 != 0 && IsSameSignAndNonZero(r3, r4)) { return DontIntersect; } /* * Compute a2, b2, c2 */ a2 = p4.Y - p3.Y; b2 = p3.X - p4.X; c2 = p4.X*p3.Y - p3.X*p4.Y; /* * Compute r1 and r2 */ r1 = a2*p1.X + b2*p1.Y + c2; r2 = a2*p2.X + b2*p2.Y + c2; /* * Check signs of r1 and r2. If both point 1 and point 2 lie * on same side of second line segment, the line segments do * not intersect. */ if (r1 != 0 && r2 != 0 && IsSameSignAndNonZero(r1, r2)) { return DontIntersect; } /* * Line segments intersect: compute intersection point. */ double denom = a1*b2 - a2*b1; if (denom == 0) { return ComputeCollinearIntersection(p1, p2, p3, p4); } double numX = b1*c2 - b2*c1; pa.X = numX/denom; double numY = a2*c1 - a1*c2; pa.Y = numY/denom; // check if this is a proper intersection BEFORE truncating values, // to avoid spurious equality comparisons with endpoints isProper = true; if (pa.Equals(p1) || pa.Equals(p2) || pa.Equals(p3) || pa.Equals(p4)) { isProper = false; } // truncate computed point to precision grid if (precisionModel != null) { precisionModel.MakePrecise(pa); } return DoIntersect; } /// /// /// /// /// /// /// /// private int ComputeCollinearIntersection(ICoordinate p1, ICoordinate p2, ICoordinate p3, ICoordinate p4) { double r1; double r2; double r3; double r4; ICoordinate q3; ICoordinate q4; double t3; double t4; r1 = 0; r2 = 1; r3 = RParameter(p1, p2, p3); r4 = RParameter(p1, p2, p4); // make sure p3-p4 is in same direction as p1-p2 if (r3 < r4) { q3 = p3; t3 = r3; q4 = p4; t4 = r4; } else { q3 = p4; t3 = r4; q4 = p3; t4 = r3; } // check for no intersection if (t3 > r2 || t4 < r1) { return DontIntersect; } // check for single point intersection if (q4 == p1) { pa.CoordinateValue = p1; return DoIntersect; } if (q3 == p2) { pa.CoordinateValue = p2; return DoIntersect; } // intersection MUST be a segment - compute endpoints pa.CoordinateValue = p1; if (t3 > r1) { pa.CoordinateValue = q3; } pb.CoordinateValue = p2; if (t4 < r2) { pb.CoordinateValue = q4; } return Collinear; } /// /// RParameter computes the parameter for the point p /// in the parameterized equation /// of the line from p1 to p2. /// This is equal to the 'distance' of p along p1-p2. /// private double RParameter(ICoordinate p1, ICoordinate p2, ICoordinate p) { // compute maximum delta, for numerical stability // also handle case of p1-p2 being vertical or horizontal double r; double dx = Math.Abs(p2.X - p1.X); double dy = Math.Abs(p2.Y - p1.Y); if (dx > dy) { r = (p.X - p1.X)/(p2.X - p1.X); } else { r = (p.Y - p1.Y)/(p2.Y - p1.Y); } return r; } } }