/// <summary> /// Updates info on the longest horizontal span that crosses this polygon (used /// by <see cref="GetLabelPosition"/>) /// </summary> /// <param name="y">The Y-value of the scan line.</param> /// <param name="minx">The X-value for the western end of the scan line.</param> /// <param name="maxx">The X-value for the eastern end of the scan line.</param> /// <param name="weight">Weighting factor to use when comparing span lengths versus /// the initial best length.</param> /// <param name="beststart">The position of the start of the best span.</param> /// <param name="bestend">The position of the end of the best span.</param> /// <param name="bestlen">The weighted length of the best span. This is what defines /// the meaning of "best".</param> void GetLabelSpan(double y, double minx, double maxx, double weight, ref IPosition beststart, ref IPosition bestend, ref double bestlen) { // Define scan line. ITerminal sloc = new FloatingTerminal(minx, y); ITerminal eloc = new FloatingTerminal(maxx, y); SegmentGeometry seg = new SegmentGeometry(sloc, eloc); // Intersect with the map IntersectionFinder xseg = new IntersectionFinder(seg, true); // Arrange intersections along the scan line. IntersectionResult xres = new IntersectionResult(xseg); xres.Sort(true); // Define start of the first span IPosition start = sloc; IPosition end = null; // Go through each successive intersection, to locate spans // that run through the interior of the polygon. foreach (IntersectionData d in xres.Intersections) { // If the intersection is a graze if (d.IsGraze) { // Just define the end of the graze as the end point (there's // no point in trying to locate a label along the graze). end = d.P2; } else { // Simple intersection... // Get the next intersection end = d.P1; // Get the midpoint of the span. IPosition mid = Position.CreateMidpoint(start, end); // If the midpoint really falls inside this polygon, see whether the span // length is bigger than what we already have (if anything). if (this.IsEnclosing(mid)) { double len = (end.X - start.X) / weight; if (len > bestlen) { bestlen = len; beststart = start; bestend = end; } } } start = end; } }
/// <summary> /// Checks whether this closed shape intersects (overlaps) a line. /// This assumes that a window-window overlap has already been checked for. /// </summary> /// <param name="line">The line to compare with the shape</param> /// <returns>True if intersection found.</returns> bool IsIntersect(LineGeometry line) { //IntersectionResult xres = new IntersectionResult(line); //return (xres.IntersectMultiSegment(this) > 0); IntersectionResult xres = new IntersectionResult(line); // Intersect each segment of this shape with the line. IPointGeometry[] data = this.Data; for (int i = 1; i < data.Length; i++) { LineSegmentGeometry thisSeg = new LineSegmentGeometry(data[i - 1], data[i]); if (xres.IntersectSegment(thisSeg) > 0) { return(true); } } return(false); }
internal override uint IntersectSegment(IntersectionResult results, ILineSegmentGeometry seg) { return Make().IntersectSegment(results, seg); }
internal override uint IntersectCircle(IntersectionResult results, ICircleGeometry circle) { return Make().IntersectCircle(results, circle); }
internal override uint IntersectArc(IntersectionResult results, ICircularArcGeometry that) { return(IntersectionHelper.Intersect(results, (ILineSegmentGeometry)this, that)); }
/// <summary> /// Intersects this segment with another segment, where at least one end of the /// segment exactly coincides with one end of the other segment. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="start">The start of the other segment.</param> /// <param name="end">The end of the other segment.</param> /// <param name="xend">The end of THIS segment that coincides with one end /// of the other one (the geometry for either m_Start or m_End).</param> /// <param name="othend">The other end of THIS segment (may or may not coincide /// with one end of the other segment).</param> /// <returns>The number of intersections (always 1).</returns> static uint EndIntersect( IntersectionResult xsect , IPointGeometry start , IPointGeometry end , IPointGeometry xend , IPointGeometry othend) { // If the other end of this segment coincides with either end // of the other segment, we've got a total graze. if (othend.IsCoincident(start) || othend.IsCoincident(end)) { xsect.Append(start, end); return 1; } // Get the locations that define the longer segment, together // with the location that is different from the exactly matching end. IPointGeometry startLong; IPointGeometry endLong; IPointGeometry test; if (Geom.DistanceSquared(xend, othend) < Geom.DistanceSquared(start, end)) { test = othend; startLong = start; endLong = end; } else { startLong = xend; endLong = othend; if (xend.IsCoincident(start)) test = end; else test = start; } // If it is coincident (to within the resolution) AND the // position ratio of the perpendicular point is ON this // segment, it's a graze. double tolsq = (Constants.XYRES * Constants.XYRES); if (PointGeometry.IsCoincidentWith(test, startLong, endLong, tolsq)) { double prat = Geom.GetPositionRatio(test.X , test.Y , startLong.X , startLong.Y , endLong.X , endLong.Y); if (prat>0.0 && prat<1.0) { xsect.Append(xend, test); return 1; } } // ONE intersection at the end that exactly matches. xsect.Append(xend); return 1; }
internal static uint Intersect( IntersectionResult results , IPointGeometry a , IPointGeometry b , IPointGeometry p , IPointGeometry q) { // 04-APR-2003: This isn't supposed to happen, but if we've somehow // got a null segment, it NEVER intersects anything. if (a.IsCoincident(b)) return 0; // If the segment EXACTLY meets either end of the other segment, // it gets handled seperately. if (a.IsCoincident(p) || a.IsCoincident(q)) return EndIntersect(results, p, q, a, b); if (b.IsCoincident(p) || b.IsCoincident(q)) return EndIntersect(results, p, q, b, a); // Return if the windows don't overlap. Window winab = new Window(a, b); Window winpq = new Window(p, q); if (!winab.IsOverlap(winpq)) return 0; // Check whether this line is completely coincident with the other one. // double tolsq = Constants.XYTOLSQ; double tolsq = (Constants.XYRES*Constants.XYRES); uint xcase = 0; if (PointGeometry.IsCoincidentWith(a, p, q, tolsq)) xcase |= 0x08; if (PointGeometry.IsCoincidentWith(b, p, q, tolsq)) xcase |= 0x04; if (xcase==12) // bits 1100 { results.Append(a, b); return 1; } // Check the end points of the other line to this one. if (PointGeometry.IsCoincidentWith(p, a, b, tolsq)) xcase |= 0x02; if (PointGeometry.IsCoincidentWith(q, a, b, tolsq)) xcase |= 0x01; // Return intersections. Note that in cases 3,7,11, the intersections // are not necessarily ordered with respect to THIS segment. switch (xcase) { case 0: { // Try to get a simple intersection. Do not accept // virtual intersections, since they should have been // trapped via the calculation of the xcase. double xi, yi; int xcode = Geom.CalcIntersect(a.X, a.Y, b.X, b.Y, p.X, p.Y, q.X, q.Y, out xi, out yi, true); if (xcode < 0) { results.Append(xi, yi, 0.0); return 1; } return 0; } case 1: { results.Append(q); return 1; } case 2: { results.Append(p); return 1; } case 3: { results.Append(p, q); // order? return 1; } case 4: { results.Append(b); return 1; } case 5: { results.Append(q, b); return 1; } case 6: { results.Append(p, b); return 1; } case 7: { results.Append(p, q); // order? return 1; } case 8: { results.Append(a); return 1; } case 9: { results.Append(a, q); return 1; } case 10: { results.Append(a, p); return 1; } case 11: { results.Append(p, q); // order? return 1; } } // end switch throw new Exception("LineSegmentFeature.Intersect - Unexpected case"); }
/// <summary> /// Intersects a pair of circular arcs. /// </summary> /// <param name="results">Where to stick the results</param> /// <param name="a">The first arc</param> /// <param name="b">The second arc</param> /// <returns></returns> internal static uint Intersect(IntersectionResult results, ICircularArcGeometry ab, ICircularArcGeometry pq) { // Special handling if the two arcs share the same circle if (CircleGeometry.IsCoincident(ab.Circle, pq.Circle, Constants.XYRES)) return ArcIntersect(results, ab, pq); // Arcs that meet exactly end to end get handled seperately. IPointGeometry a = ab.First; IPointGeometry b = ab.Second; IPointGeometry p = pq.First; IPointGeometry q = pq.Second; if (a.IsCoincident(p) || a.IsCoincident(q)) return EndIntersect(results, ab, pq, true); if (b.IsCoincident(p) || b.IsCoincident(q)) return EndIntersect(results, ab, pq, false); // Intersect the circle for the two arcs IPosition x1, x2; uint nx = Intersect(ab.Circle, pq.Circle, out x1, out x2); // Return if the circles don't intersect. if (nx==0) return 0; // Remember the intersection(s) if they fall in BOTH curve sectors. IPointGeometry thiscen = ab.Circle.Center; IPointGeometry othrcen = pq.Circle.Center; if (nx==1) { // If we got 1 intersection, it may be VERY close to the end // points. Make sure we also consider the precise check made up top. // Otherwise check if the intersection is in both sectors. IPointGeometry loc = PointGeometry.Create(x1); // rounded to nearest micron if (Geom.IsInSector(loc, thiscen, a, b, 0.0) && Geom.IsInSector(loc, othrcen, p, q, 0.0)) { results.Append(loc); return 1; } return 0; } else { // Two intersections. They are valid if they fall within the arc's sector. // Again, make sure we consider any precise end-point check made above. uint nok=0; IPointGeometry loc1 = PointGeometry.Create(x1); IPointGeometry loc2 = PointGeometry.Create(x2); if (Geom.IsInSector(loc1, thiscen, a, b, 0.0) && Geom.IsInSector(loc1, othrcen, p, q, 0.0)) { results.Append(loc1); nok++; } if (Geom.IsInSector(loc2, thiscen, a, b, 0.0) && Geom.IsInSector(loc2, othrcen, p, q, 0.0)) { results.Append(loc2); nok++; } return nok; } }
internal static uint Intersect(IntersectionResult result, ILineSegmentGeometry seg, ICircleGeometry circle) { return Intersect(result, seg.Start, seg.End, circle.Center, circle.Radius); }
internal override uint Intersect(IntersectionResult results) { return(Make().Intersect(results)); }
internal override uint IntersectCircle(IntersectionResult results, ICircleGeometry circle) { return(Make().IntersectCircle(results, circle)); }
internal override uint IntersectArc(IntersectionResult results, ICircularArcGeometry arc) { return(Make().IntersectArc(results, arc)); }
internal override uint IntersectMultiSegment(IntersectionResult results, IMultiSegmentGeometry line) { return(Make().IntersectMultiSegment(results, line)); }
internal override uint IntersectSegment(IntersectionResult results, ILineSegmentGeometry seg) { return(Make().IntersectSegment(results, seg)); }
/// <summary> /// Intersects a line segment with a circle /// </summary> /// <param name="result"></param> /// <param name="a">The start of the line segment</param> /// <param name="b">The end of the line segment</param> /// <param name="c">The center of the circle</param> /// <param name="r">The radius of the circle</param> /// <returns></returns> static uint Intersect(IntersectionResult result, IPointGeometry a, IPointGeometry b, IPointGeometry c, double r) { // Get intersections (if any). Return if nothing. IPosition x1, x2; bool isGraze; uint nx = GetXCircle(a, b, c, r, out x1, out x2, out isGraze); if (nx==0) return 0; // If we got just one intersection, it's a simple intersect (even // if it's a tangent to the circle). if (nx==1) { result.Append(x1); return 1; } // That leaves us with two intersections, which may or may // not be grazing the circle. if (isGraze) { result.Append(x1, x2); return 1; } result.Append(x1); result.Append(x2); return 2; }
internal static uint Intersect(IntersectionResult result, ILineSegmentGeometry seg, IMultiSegmentGeometry line) { uint nx=0; IPointGeometry a = seg.Start; IPointGeometry b = seg.End; IPointGeometry[] data = line.Data; for (int i=1; i<data.Length; i++) nx += Intersect(result, a, b, data[i-1], data[i]); return nx; }
/// <summary> /// Delegate that's called whenever the index finds an object with an extent that /// overlaps the query window. /// </summary> /// <param name="item">The item to process (expected to be some sort of <c>LineFeature</c>)</param> /// <returns>True (always), indicating that the query should continue.</returns> private bool OnQueryHit(ISpatialObject item) { Debug.Assert(item is LineFeature); // Ignore if we're intersecting a line feature & the line we've found is that line LineFeature f = (LineFeature)item; if (Object.ReferenceEquals(m_Feature, f)) { return(true); } // Ignore lines that don't have any topology Topology t = f.Topology; if (t == null) { return(true); } Debug.Assert(f.IsTopological); // Ignore lines that are marked as "moved" if (f.IsMoved) { return(true); } // Intersect each divider foreach (IDivider d in t) { // Ignore divider overlaps (regarded as non-topological) if (d.IsOverlap) { continue; } // Search for intersections IntersectionResult other = new IntersectionResult(d); m_Geom.Intersect(other); if (other.IntersectCount > 0) { // Determine the context of each intersection. other.SetContext(m_Geom); // If end-to-end simple intersections are not required, weed them out. if (!m_WantEndEnd) { other.CutEndEnd(); } if (other.IntersectCount > 0) { m_Result.Add(other); } } } return(true); }
internal static uint Intersect(IntersectionResult results, IMultiSegmentGeometry line, ICircularArcGeometry arc) { uint nx=0; IPointGeometry[] segs = line.Data; IPointGeometry f = arc.First; IPointGeometry s = arc.Second; ICircleGeometry circle = arc.Circle; for (int i=1; i<segs.Length; i++) nx += Intersect(results, segs[i-1], segs[i], f, s, circle); return nx; }
abstract internal uint Intersect(IntersectionResult results);
internal static uint Intersect(IntersectionResult results, ICircleGeometry a, ICircleGeometry b) { IPointGeometry centre1 = a.Center; IPointGeometry centre2 = b.Center; double radius1 = a.Radius; double radius2 = b.Radius; // The following looks pretty similar to Geom.IntersectCircles (some tolerance values are // a bit more relaxed here). // Pull out the XY's for the 2 centres. double xc1 = centre1.X; double yc1 = centre1.Y; double xc2 = centre2.X; double yc2 = centre2.Y; // Get distance (squared) between the 2 centres. double dx = xc2 - xc1; double dy = yc2 - yc1; double distsq = dx*dx + dy*dy; // If the two circles have the same centre, check whether // the radii match (if so, we have an overlap case). if (distsq < Constants.TINY) { if (Math.Abs(radius1-radius2) < Constants.XYRES) { double xi = xc1; double yi = yc1 + radius1; results.Append(xi, yi, xi, yi); return 1; } else return 0; } // We'll need the radii squared double rsq1 = radius1 * radius1; double rsq2 = radius2 * radius2; // Now some mathematical magic I got out a book. double delrsq = rsq2 - rsq1; double sumrsq = rsq1 + rsq2; double root = 2.0*sumrsq*distsq - distsq*distsq - delrsq*delrsq; // Check for no intersection. if (root < Constants.TINY) return 0; // We have at least one intersection. double dstinv = 0.5 / distsq; double scl = 0.5 - delrsq*dstinv; double x = dx*scl + xc1; double y = dy*scl + yc1; // Is it tangential? if (root < Constants.TINY) { results.Append(x, y, 0.0); return 1; } // Figure out 2 intersections. root = dstinv * Math.Sqrt(root); double xfac = dx*root; double yfac = dy*root; results.Append(x-yfac, y+xfac, 0.0); results.Append(x+yfac, y-xfac, 0.0); return 2; }
abstract internal uint IntersectSegment(IntersectionResult results, ILineSegmentGeometry seg);
static uint ArcIntersect(IntersectionResult results, ICircularArcGeometry ab, ICircularArcGeometry pq) { // Arcs that meet exactly end to end get handled seperately. IPointGeometry a = ab.First; IPointGeometry b = ab.Second; IPointGeometry p = pq.First; IPointGeometry q = pq.Second; if (a.IsCoincident(p) || a.IsCoincident(q)) //return ArcEndIntersect(results, pq.Circle, p, q, a, b); return ArcEndIntersect(results, pq.Circle, p, q, a, b, true); if (b.IsCoincident(p) || b.IsCoincident(q)) //return ArcEndIntersect(results, pq.Circle, p, q, b, a); return ArcEndIntersect(results, pq.Circle, p, q, a, b, false); return ArcIntersect(results, pq.Circle, p, q, a, b); }
abstract internal uint IntersectMultiSegment(IntersectionResult results, IMultiSegmentGeometry line);
/// <summary> /// Intersects a pair of clockwise arcs, where one of the ends exactly coincides with the /// other arc. The two arcs are assumed to sit on different circles. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="bc">The start of the other arc.</param> /// <param name="ec">The end of the other arc.</param> /// <param name="circle">The circle that the other arc sits on.</param> /// <param name="xend">The end of THIS arc that coincides with one end of the other one.</param> /// <param name="othend">The other end of THIS arc (may or may not coincide with one end of /// the other arc).</param> /// <param name="xCircle">The circle for THIS arc</param> /// <returns>The number of intersections (1 or 2).</returns> static uint EndIntersect( IntersectionResult xsect , ICircularArcGeometry ab , ICircularArcGeometry pq , bool isFirstEndCoincident) { IPointGeometry bc = pq.First; IPointGeometry ec = pq.Second; ICircleGeometry circle = pq.Circle; IPointGeometry xend = (isFirstEndCoincident ? ab.First : ab.Second); IPointGeometry othend = (isFirstEndCoincident ? ab.Second : ab.First); ICircleGeometry xCircle = ab.Circle; // The two curves sit on different circles, so do a precise intersection of the circles. IPointGeometry c1 = xCircle.Center; IPointGeometry c2 = circle.Center; double r1 = xCircle.Radius; double r2 = circle.Radius; IPosition x1, x2; uint nx = Geom.IntersectCircles(c1, r1, c2, r2, out x1, out x2); // If we didn't get ANY intersections, that's a bit unusual // seeing how one of the ends matches. However, it's possible // due to roundoff of the end locations. So in that case, just // return a single intersection at the matching end. if (nx==0) { xsect.Append(xend); return 1; } // @devnote If we got 1 intersection (i.e. the 2 circles just // touch), you might be tempted to think that it must be close // to the matching end location. That is NOT the case if the // circles are big. // If we got 2 intersections, pick the one that's further away // than the matching end. if (nx==2 && Geom.DistanceSquared(x2, xend) > Geom.DistanceSquared(x1, xend)) x1 = x2; // That leaves us with ONE intersection with the circle ... now // confirm that it actually intersects both curves! // Does it fall in the sector defined by the clockwise curve? IPointGeometry centre = c2; Turn reft = new Turn(centre, bc); double eangle = reft.GetAngleInRadians(ec); double xangle = reft.GetAngleInRadians(x1); if (xangle > eangle) { xsect.Append(xend); return 1; } // Does it fall in the sector defined by this curve (NO tolerance allowed). PointGeometry xloc = PointGeometry.Create(x1); bool isxthis = Geom.IsInSector(xloc, c1, ab.First, ab.Second, 0.0); if (!isxthis) { xsect.Append(xend); return 1; } // Get the midpoint of the segment that connects the intersection to the matching end. IPosition midx = Position.CreateMidpoint(xend, x1); // If the midpoint does NOT graze the circle, we've got 2 distinct intersections. // 25-NOV-99: Be realistic about it (avoid meaningless sliver // polygons that are less than 0.1mm wide on the ground). Also, // the old way used 'centre' which may refer to r1 OR r2, so // you would have got the correct result only half of the time! // if ( fabs(midx.Distance(centre) - r1) > XYTOL ) { double rdiff = Geom.Distance(midx, c1) - r1; if (Math.Abs(rdiff) > 0.0001) { xsect.Append(xend); xsect.Append(x1); return 2; } // We've got a graze, but possibly one that can be ignored(!). To // understand the reasoning here, bear in mind that lines get cut // only so that network topology can be formed. To do that, 2 // orientation points are obtained for the lines incident on xend. // For curves, it's a position 5 metres along the curve (or the // total curve length if it's not that long). So if the graze is // closer than the point that will be used to get the orientation // point, we can ignore the graze, since it does not provide any // useful info. // Given that it's a graze, assume that it's ok to work out // the arc distance as if it was straight. double dsqx = Geom.DistanceSquared(xend, x1); // If it's closer than 4m (allow some leeway, seeing how we've // just done an approximation), ignore the intersection. If it's // actually between 4 and 5 metres, it shouldn't do any harm // to make a split there (although it's kind of redundant). if (dsqx < 16.0) { xsect.Append(xend); return 1; } // It's a graze. xsect.Append(xend, x1); return 1; }
abstract internal uint IntersectArc(IntersectionResult results, ICircularArcGeometry arc);
internal override uint IntersectArc(IntersectionResult results, ICircularArcGeometry arc) { return Make().IntersectArc(results, arc); }
abstract internal uint IntersectCircle(IntersectionResult results, ICircleGeometry circle);
internal override uint IntersectMultiSegment(IntersectionResult results, IMultiSegmentGeometry line) { return Make().IntersectMultiSegment(results, line); }
internal override uint Intersect(IntersectionResult results) { return(results.IntersectMultiSegment(this)); }
/// <summary> /// Intersects a line segment with the data describing a clockwise curve. /// </summary> /// <param name="result">The intersection results.</param> /// <param name="a">The start of the line segment</param> /// <param name="b">The end of the line segment</param> /// <param name="start">The start of the clockwise arc.</param> /// <param name="end">The end of the clockwise arc.</param> /// <param name="circle">The circle on which the arc lies.</param> /// <returns></returns> static uint Intersect( IntersectionResult result , IPointGeometry a , IPointGeometry b , IPointGeometry start , IPointGeometry end , ICircleGeometry circle) { // If the segment exactly meets either end of the curve, it gets handled seperately. if (a.IsCoincident(start) || a.IsCoincident(end)) return EndIntersect(result, start, end, circle, a, b); if (b.IsCoincident(start) || b.IsCoincident(end)) return EndIntersect(result, start, end, circle, b, a); // Get circle definition. IPointGeometry centre = circle.Center; double radius = circle.Radius; // Get up to 2 intersections with the circle. IPosition x1, x2; bool isGraze; uint nx = GetXCircle(a, b, centre, radius, out x1, out x2, out isGraze); // Return if no intersections with the circle. if (nx==0) return 0; // If an intersection is really close to the end of an arc, force it to be there. if (Geom.DistanceSquared(start, x1) < Constants.XYTOLSQ) x1 = start; else if (Geom.DistanceSquared(end, x1) < Constants.XYTOLSQ) x1 = end; if (nx==2) { if (Geom.DistanceSquared(start, x2) < Constants.XYTOLSQ) x2 = start; else if (Geom.DistanceSquared(end, x2) < Constants.XYTOLSQ) x2 = end; } // Determine whether the intersections fall within the sector // that's defined by the clockwise curve (grazes need a bit // more handling, so do that after we see how to do the non- // grazing case. if ( !isGraze ) { PointGeometry xloc1 = PointGeometry.Create(x1); double lintol = Constants.XYTOL / radius; // If we only got one intersect, and it's out of sector, that's us done. if (nx==1) { if (!BasicGeom.IsInSector(xloc1, centre, start, end, lintol)) return 0; result.Append(x1); return 1; } // Two intersections with the circle ... if (BasicGeom.IsInSector(xloc1, centre, start, end, lintol)) result.Append(x1); else nx--; PointGeometry xloc2 = PointGeometry.Create(x2); if (BasicGeom.IsInSector(xloc2, centre, start, end, lintol)) result.Append(x2); else nx--; return nx; } // That leaves us with the case where this segment grazes the // circle. GetXCircle is always supposed to return nx==2 in that // case (they're also supposed to be arranged in the same // direction as this segment). Debug.Assert(nx==2); // Get the clockwise angle subtended by the circular arc. Add // on a tiny bit to cover numerical comparison problems. Turn reft = new Turn(centre, start); double maxangle = reft.GetAngleInRadians(end) + Constants.TINY; // Get the clockwise angles to both intersections. double a1 = reft.GetAngleInRadians(x1); double a2 = reft.GetAngleInRadians(x2); // No graze if both are beyond the arc sector. if (a1>maxangle && a2>maxangle) return 0; // If both intersects are within sector, the only thing to watch // out for is a case where the graze apparently occupies more // than half a circle. In that case, we've actually got 2 grazes. if (a1<maxangle && a2<maxangle) { if (a2>a1 && (a2-a1)>Constants.PI) { result.Append(x1, start); result.Append(end, x2); return 2; } if (a1>a2 && (a1-a2)>Constants.PI) { result.Append(start, x2); result.Append(x1, end); return 2; } // So it's just a regular graze. result.Append(x1, x2); return 1; } // That's covered the cases where both intersects are either // both in sector, or both out. Return the intersection that's // in sector as a simple intersection (NOT a graze). // This is a cop-out. We should really try to figure out which // portion of the curve is grazing, but the logic for doing so // is surprisingly complex, being subject to a variety of special // cases. By returning the intersect as a simple intersect, I'm // hoping it covers most cases. if (a1 < maxangle) result.Append(x1); else result.Append(x2); return 1; }
/// <summary> /// Intersects this multi-segment with itself. Only SIMPLE intersections will be /// found. There are no special checks for multi-segments that graze themselves. /// </summary> /// <param name="xsect">The intersection results.</param> /// <returns>The number of self-intersections found.</returns> uint SelfIntersect(IntersectionResult xsect) { uint nx = 0; // Get an array of cumulative distances for each segment. IPointGeometry[] data = this.Data; double[] cumdist = GetCumDist(data); // Note start of initial segment (treat as the end of some imaginary line prior to the start). double xs; double ys; double xe = data[0].X; double ye = data[0].Y; // How many line segments have we got? int nseg = data.Length - 1; // Loop through each segment, intersecting it with all subsequent // segments, except for the one that immediately follows. for (int iseg = 1; iseg <= (nseg - 2); iseg++) { // The start of this segment is the end of the previous one. xs = xe; ys = ye; // Get the position of the end of the test segment xe = data[iseg].X; ye = data[iseg].Y; // Compare against subsequent segments (except the next one) for (int jseg = iseg + 2; jseg <= nseg; jseg++) { IPointGeometry start = data[jseg - 1]; IPointGeometry end = data[jseg]; double xi, yi; if (Geom.CalcIntersect(start.X, start.Y, end.X, end.Y, xs, ys, xe, ye, out xi, out yi, true) != 0) { // Define distance to the intersection on the i-segment double dx = xi - xs; double dy = yi - ys; double ilen = cumdist[iseg - 1] + Math.Sqrt(dx * dx + dy * dy); // Likewise for the j-segment dx = xi - start.X; dy = yi - start.Y; double jlen = cumdist[jseg - 1] + Math.Sqrt(dx * dx + dy * dy); // Append TWO intersections. xsect.Append(xi, yi, ilen); xsect.Append(xi, yi, jlen); nx += 2; } } } // Sort the intersections (DON'T set new sort values). xsect.Sort(false); // Return the number of intersections return(nx); }
/// <summary> /// Intersects a circle with a clockwise arc /// </summary> /// <param name="results"></param> /// <param name="c"></param> /// <param name="start"></param> /// <param name="end"></param> /// <param name="arcCircle"></param> /// <returns></returns> static uint Intersect(IntersectionResult xsect, ICircleGeometry c, IPointGeometry start, IPointGeometry end, ICircleGeometry arcCircle) { // If the circles are IDENTICAL, we've got a graze. if (CircleGeometry.IsCoincident(c, arcCircle, Constants.XYRES)) { xsect.Append(start, end); return 1; } // Intersect the 2 circles. IPosition x1, x2; uint nx = Intersect(c, arcCircle, out x1, out x2); // Return if no intersection. if (nx==0) return 0; // Remember the intersection(s) if they fall in the // curve's sector. Use a tolerance which is based on // the circle with the smaller radius (=> bigger angular // tolerance). IPointGeometry centre; double minrad; if (c.Radius < arcCircle.Radius) { minrad = c.Radius; centre = c.Center; } else { minrad = arcCircle.Radius; centre = arcCircle.Center; } // const FLOAT8 angtol = XYTOL/minrad; // const FLOAT8 angtol = 0.00002/minrad; // 20 microns double angtol = 0.002/minrad; // 2mm if (nx==1) { IPointGeometry loc = PointGeometry.Create(x1); if (Geom.IsInSector(loc, centre, start, end, angtol)) { xsect.Append(loc); return 1; } return 0; } else { // Two intersections. They are valid if they fall within the curve's sector. IPointGeometry loc1 = PointGeometry.Create(x1); IPointGeometry loc2 = PointGeometry.Create(x2); uint nok=0; if (Geom.IsInSector(loc1, centre, start, end, angtol)) { xsect.Append(loc1); nok++; } if (Geom.IsInSector(loc2, centre, start, end, angtol)) { xsect.Append(loc2); nok++; } return nok; } }
/// <summary> /// Intersect this line with another line. /// </summary> /// <param name="line">The line to intersect with (not equal to THIS line)</param> /// <param name="closeTo">The point that the intersection should be closest to. /// Specify null if you don't care. In that case, if there are multiple intersections, /// you get the intersection that is closest to one of 3 points: the start of the /// direction line, the start of the line, or the end of the line.</param> /// <param name="xsect">The position of the intersection (if any). Null if not found.</param> /// <param name="closest">The default point that is closest to the intersection. Null if /// intersection wasn't found.</param> /// <returns>True if intersection was found.</returns> internal bool Intersect( LineFeature line , PointFeature closeTo , out IPosition xsect , out PointFeature closest) { // Initialize results xsect = null; closest = null; // Don't intersect a line with itself. if (Object.ReferenceEquals(this, line)) throw new Exception("Cannot intersect a line with itself."); // Intersect this line with the other one. IntersectionResult xres = new IntersectionResult(this); uint nx = line.LineGeometry.Intersect(xres); if (nx==0) return false; // Determine which terminal point is the best. double mindsq = Double.MaxValue; if (xres.GetCloserPoint(this.StartPoint, ref mindsq, ref xsect)) closest = this.StartPoint; if (xres.GetCloserPoint(this.EndPoint, ref mindsq, ref xsect)) closest = this.EndPoint; if (xres.GetCloserPoint(line.StartPoint, ref mindsq, ref xsect)) closest = line.StartPoint; if (xres.GetCloserPoint(line.EndPoint, ref mindsq, ref xsect)) closest = line.EndPoint; // If a close-to point has been specified, that overrides // everything else (however, doing the above has the desired // effect of defining the best of the default points). In // this case, we allow an intersection that coincides with // the line being intersected. if (closeTo!=null) xres.GetClosest(closeTo, out xsect, 0.0); return (xsect!=null); }
internal static uint Intersect(IntersectionResult result, ILineSegmentGeometry seg, ICircularArcGeometry arc) { if (CircularArcGeometry.IsCircle(arc)) return Intersect(result, seg, arc.Circle); else return Intersect(result, seg.Start, seg.End, arc.First, arc.Second, arc.Circle); }
internal override uint Intersect(IntersectionResult results) { return results.IntersectSegment(this); }
internal static uint Intersect(IntersectionResult result, IMultiSegmentGeometry line1, IMultiSegmentGeometry line2) { uint nx=0; IPointGeometry[] pa = line1.Data; IPointGeometry[] pb = line2.Data; for (int i=1; i<pa.Length; i++) { IPointGeometry a = pa[i-1]; IPointGeometry b = pa[i]; for(int j=1; j<pb.Length; j++) nx += Intersect(result, a, b, pb[j-1], pb[j]); } return nx; }
internal override uint IntersectSegment(IntersectionResult results, ILineSegmentGeometry that) { return IntersectionHelper.Intersect(results, (ILineSegmentGeometry)this, that); }
internal static uint Intersect(IntersectionResult results, IMultiSegmentGeometry line, ICircleGeometry circle) { uint nx=0; IPointGeometry[] segs = line.Data; IPointGeometry c = circle.Center; double r = circle.Radius; for (int i=1; i<segs.Length; i++) nx += Intersect(results, segs[i-1], segs[i], c, r); return nx; }
/// <summary> /// Appends intersection info to this object. /// </summary> /// <param name="xsect">The intersection info to append.</param> void Append(IntersectionResult xsect) { m_Intersects.Add(xsect); }
internal static uint Intersect(IntersectionResult results, ICircularArcGeometry a, ICircleGeometry b) { if (CircularArcGeometry.IsCircle(a)) return Intersect(results, a.Circle, b); else return Intersect(results, b, a.First, a.Second, a.Circle); }
/// <summary> /// Appends intersection info to this object. /// </summary> /// <param name="xsect">The intersection info to append.</param> void Append(IntersectionResult xsect) { m_Intersects.Add(xsect); }
internal static uint Intersect(IntersectionResult results, ILineSegmentGeometry ab, ILineSegmentGeometry pq) { return Intersect(results, ab.Start, ab.End, pq.Start, pq.End); }
internal override uint IntersectMultiSegment(IntersectionResult results, IMultiSegmentGeometry that) { return(IntersectionHelper.Intersect(results, that, (ICircularArcGeometry)this)); }
/// <summary> /// Intersects a pair of clockwise arcs that sit on the same circle, and where ONE of /// the end points exactly matches. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="circle">The circle the arcs coincide with</param> /// <param name="bc1">The BC of the 1st arc</param> /// <param name="ec1">The EC of the 1st arc</param> /// <param name="bc2">The BC of the 2nd arc -- the matching end</param> /// <param name="ec2">The EC of the 2nd arc</param> /// <param name="isStartMatch">Specify <c>true</c> if <paramref name="bc2"/> matches an end /// point of the 1st arc. Specify <c>false</c> if <paramref name="ec2"/> matches an end /// point of the 1st arc.</param> /// <returns>The number of intersections (always 1).</returns> static uint ArcEndIntersect( IntersectionResult xsect , ICircleGeometry circle , IPointGeometry bc1 , IPointGeometry ec1 , IPointGeometry bc2 , IPointGeometry ec2 , bool isStartMatch) { bool bmatch = bc1.IsCoincident(bc2); bool ematch = ec1.IsCoincident(ec2); // If the two curves share the same BC or same EC if (bmatch || ematch) { // We've got some sort of graze ... // Check for total graze. if (bmatch && ematch) xsect.Append(bc1, ec1); else { // We've therefore got a partial graze. // If the length of this arc is longer than the other one, the graze is over the // length of the other one, and vice versa. Since the two curves share the same // radius and direction, the comparison just involves a comparison of the clockwise // angles subtended by the arcs. IPointGeometry centre = circle.Center; double ang1 = new Turn(centre, bc1).GetAngleInRadians(ec1); double ang2 = new Turn(centre, bc2).GetAngleInRadians(ec2); bool isThisGraze = (ang1 < ang2); if (isThisGraze) xsect.Append(bc1, ec1); else xsect.Append(bc2, ec2); } } else { // The only intersection is at the common end. if (bc1.IsCoincident(bc2) || ec1.IsCoincident(bc2)) xsect.Append(bc2); else xsect.Append(ec2); } return 1; }
internal override uint IntersectCircle(IntersectionResult results, ICircleGeometry that) { return(IntersectionHelper.Intersect(results, (ICircularArcGeometry)this, that)); }
/// <summary> /// Intersects 2 clockwise arcs that coincide with the perimeter of a circle. /// </summary> /// <param name="xsect">Intersection results.</param> /// <param name="circle">The circle the arcs coincide with</param> /// <param name="bc1">BC for 1st arc.</param> /// <param name="ec1">EC for 1st arc.</param> /// <param name="bc2">BC for 2nd arc.</param> /// <param name="ec2">EC for 2nd arc.</param> /// <returns>The number of intersections (0, 1, or 2). If non-zero, the intersections /// will be grazes.</returns> static uint ArcIntersect( IntersectionResult xsect , ICircleGeometry circle , IPointGeometry bc1 , IPointGeometry ec1 , IPointGeometry bc2 , IPointGeometry ec2) { // Define the start of the clockwise arc as a reference line, // and get the clockwise angle to it's EC. Turn reft = new Turn(circle.Center, bc1); double sector = reft.GetAngleInRadians(ec1); // Where do the BC and EC of the 2nd curve fall with respect // to the 1st curve's arc sector? double bcang = reft.GetAngleInRadians(bc2); double ecang = reft.GetAngleInRadians(ec2); if (bcang<sector) { if (ecang<bcang) { xsect.Append(bc2, ec1); xsect.Append(bc1, ec2); return 2; } if (ecang<sector) xsect.Append(bc2, ec2); else xsect.Append(bc2, ec1); return 1; } // The BC of the 2nd curve falls beyond the sector of the 1st ... // so we can't have any graze if the EC is even further on. if (ecang>bcang) return 0; // One graze ... if (ecang<sector) xsect.Append(bc1, ec2); else xsect.Append(bc1, ec1); return 1; }
internal override uint Intersect(IntersectionResult results) { return(results.IntersectArc(this)); }
/// <summary> /// Intersects a line segment with a clockwise arc, where at least one end of the /// segment exactly coincides with one end of the arc. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="bc">The start of the clockwise arc.</param> /// <param name="ec">The end of the clockwise arc.</param> /// <param name="circle">The circle on which the arc lies.</param> /// <param name="xend">The end of the segment that coincides with one end of the arc.</param> /// <param name="othend">The other end of the segment (may or may not coincide /// with one end of the arc).</param> /// <returns>The number of intersections (either 1 or 2).</returns> static uint EndIntersect( IntersectionResult xsect , IPointGeometry bc , IPointGeometry ec , ICircleGeometry circle , IPointGeometry xend , IPointGeometry othend) { // If the other end is INSIDE the circle, the matching end // location is the one and only intersection. Allow a tolerance // that is consistent with the resolution of data (3 microns). IPointGeometry centre = circle.Center; double radius = circle.Radius; double minrad = (radius-Constants.XYTOL); double minrsq = (minrad*minrad); double othdsq = Geom.DistanceSquared(othend, centre); if (othdsq < minrsq) { xsect.Append(xend); return 1; } // If the other end of the segment also coincides with either // end of the curve, the segment is a chord of the circle that // the curve lies on. However, if it's REAL close, we need to // return it as a graze ... if (othend.IsCoincident(bc) || othend.IsCoincident(ec)) { // Get the midpoint of the chord. IPosition midseg = Position.CreateMidpoint(xend, othend); // If the distance from the centre of the circle to the // midpoint is REAL close to the curve, we've got a graze. if (Geom.DistanceSquared(midseg, centre) > minrsq) { xsect.Append(xend, othend); return 1; } // Two distinct intersections. xsect.Append(xend); xsect.Append(othend); return 2; } // If the other end is within tolerance of the circle, project // it to the circle and see if it's within the curve's sector. // If not, it's not really an intersect. double maxrad = (radius+Constants.XYTOL); double maxrsq = (maxrad*maxrad); if ( othdsq < maxrsq ) { // Check if the angle to the other end is prior to the EC. // If not, it's somewhere off the curve. Turn reft = new Turn(centre, bc); if (reft.GetAngleInRadians(othend) > reft.GetAngleInRadians(ec)) { xsect.Append(xend); return 1; } // And, like above, see if the segment grazes or not ... // Get the midpoint of the chord. IPosition midseg = Position.CreateMidpoint(xend, othend); // If the distance from the centre of the circle to the // midpoint is REAL close to the curve, we've got a graze. if (Geom.DistanceSquared(midseg, centre) > minrsq) { xsect.Append(xend, othend); return 1; } // Two distinct intersections. xsect.Append(xend); xsect.Append(othend); return 2; } // That leaves us with the other end lying somewhere clearly // outside the circle. However, we could still have a graze. // Make sure the BC/EC are EXACTLY coincident with the circle (they // may have been rounded off if the curve has been intersected // with a location that's within tolerance of the circle). IPosition trueBC, trueEC; Position.GetCirclePosition(bc, centre, radius, out trueBC); Position.GetCirclePosition(ec, centre, radius, out trueEC); // As well as the end of the segment that meets the curve. IPosition trueXend = (xend.IsCoincident(bc) ? trueBC : trueEC); // Intersect the segment with the complete circle (this does // NOT return an intersection at the other end, even if it is // really close ... we took care of that above). // The intersection must lie ON the segment (not sure about this though). IPosition othvtx = othend; IPosition x1, x2; bool isTangent; uint nx = Geom.IntersectCircle(centre, radius, trueXend, othvtx, out x1, out x2, out isTangent, true); // If we got NOTHING, that's unexpected, since one end exactly coincides // with the circle. In that case, just return the matching end (NOT // projected to the true position). if (nx==0) { xsect.Append(xend); return 1; } // If we got 2 intersections, pick the one that's further away than the matching end. if (nx==2 && Geom.DistanceSquared(x2, trueXend) > Geom.DistanceSquared(x1, trueXend)) x1 = x2; // That leaves us with ONE intersection with the circle ... now // confirm that it actually intersects the arc! Turn refbc = new Turn(centre, trueBC); double eangle = refbc.GetAngleInRadians(trueEC); double xangle = refbc.GetAngleInRadians(x1); if (xangle > eangle) { xsect.Append(xend); return 1; } // Get the midpoint of the segment that connects the intersection to the true end. IPosition midx = Position.CreateMidpoint(trueXend, x1); // If the midpoint does NOT graze the circle, we've got 2 distinct intersections. // 25-NOV-99: Be realistic about it (avoid meaningless sliver polygons that are // less than 0.1mm wide on the ground). // if ( midx.DistanceSquared(centre) < minrsq ) { double rdiff = Geom.Distance(midx, centre) - radius; if (Math.Abs(rdiff) > 0.0001) { xsect.Append(xend); xsect.Append(x1); return 2; } // We've got a graze, but possibly one that can be ignored(!). To // understand the reasoning here, bear in mind that lines get cut // only so that network topology can be formed. To do that, 2 // orientation points are obtained for the lines incident on xend. // For the segment, the orientation point is the other end of the // segment. For the curve, it's a position 5 metres along the // curve (or the total curve length if it's not that long). So // if the graze is closer than the point that will be used to // get the orientation point, we can ignore the graze, since it // does not provide any useful info. // Given that it's a graze, assume that it's ok to work out // the arc distance as if it was straight. double dsqx = Geom.DistanceSquared(trueXend, x1); // If it's closer than 4m (allow some leeway, seeing how we've // just done an approximation), ignore the intersection. If it's // actually between 4 and 5 metres, it shouldn't do any harm // to make a split there (although it's kind of redundant). if (dsqx < 16.0) { xsect.Append(xend); return 1; } // It's a graze. //CeVertex vxend(xend); // wants a vertex xsect.Append(xend, x1); return 1; }
internal override uint Intersect(IntersectionResult results) { return Make().Intersect(results); }
/// <summary> /// Delegate that's called whenever the index finds an object with an extent that /// overlaps the query window. /// </summary> /// <param name="item">The item to process (expected to be some sort of <c>LineFeature</c>)</param> /// <returns>True (always), indicating that the query should continue.</returns> private bool OnQueryHit(ISpatialObject item) { Debug.Assert(item is LineFeature); // Ignore if we're intersecting a line feature & the line we've found is that line LineFeature f = (LineFeature)item; if (Object.ReferenceEquals(m_Feature, f)) return true; // Ignore lines that don't have any topology Topology t = f.Topology; if (t == null) return true; Debug.Assert(f.IsTopological); // Ignore lines that are marked as "moved" if (f.IsMoved) return true; // Intersect each divider foreach (IDivider d in t) { // Ignore divider overlaps (regarded as non-topological) if (d.IsOverlap) continue; // Search for intersections IntersectionResult other = new IntersectionResult(d); m_Geom.Intersect(other); if (other.IntersectCount>0) { // Determine the context of each intersection. other.SetContext(m_Geom); // If end-to-end simple intersections are not required, weed them out. if (!m_WantEndEnd) other.CutEndEnd(); if (other.IntersectCount>0) m_Result.Add(other); } } return true; }
internal override uint IntersectMultiSegment(IntersectionResult results, IMultiSegmentGeometry that) { return(IntersectionHelper.Intersect(results, (ILineSegmentGeometry)this, that)); }