/// <summary> /// Checks if a position is coincident with a line segment /// </summary> /// <param name="p">The position to test</param> /// <param name="start">The start of the segment.</param> /// <param name="end">The end of the segment.</param> /// <param name="tolsq">The tolerance (squared) to use. Default is XYTOLSQ.</param> /// <returns>True if the test position lies somewhere along the segment.</returns> public static bool IsCoincidentWith(IPointGeometry p, IPointGeometry start, IPointGeometry end, double tolsq) { // Check whether there is exact coincidence at either end. if (p.IsCoincident(start) || p.IsCoincident(end)) { return(true); } // Get the distance squared of a perpendicular dropped from // the test position to the segment (or the closest end if the // perpendicular does not fall ON the segment). return(BasicGeom.DistanceSquared(p.X, p.Y, start.X, start.Y, end.X, end.Y) < tolsq); }
/// <summary> /// Checks whether any intersections occur at positions that do not coincide /// with the end points of a line. /// </summary> /// <param name="line">The line to check. This should be the same line that /// was supplied to the <c>IntersectionFinder</c> constructor that created THIS results /// object (doing otherwise doesn't make any sense).</param> /// <returns>TRUE if any intersection does not coincide with the end points /// of the specified line.</returns> internal bool IsSplitOn(ILineGeometry line) { // Get the locations of the line end points. IPointGeometry start = line.Start; IPointGeometry end = line.End; // Go through each intersection, looking for one that does // not correspond to the line ends. foreach (IntersectionData d in m_Data) { IPointGeometry loc1 = new PointGeometry(d.P1); if (!start.IsCoincident(loc1) && !end.IsCoincident(loc1)) { return(true); } if (d.IsGraze) { /* * Huh? This was the original, but it always ended up returning true. * * IPointGeometry loc2 = PointGeometry.New(d.P2); * if (!start.IsCoincident(loc2) && !end.IsCoincident(loc2)) * return true; * * return true; */ return(true); } } return(false); }
/// <summary> /// Returns the context code for a simple intersection with a line. /// </summary> /// <param name="loc">The location of the intersection.</param> /// <param name="line">The line to compare with.</param> /// <returns>The context code.</returns> static IntersectionType GetContext(IPointGeometry loc, ILineGeometry line) { IntersectionType context = 0; if (loc.IsCoincident(line.Start)) { context |= IntersectionType.TouchStart; } if (loc.IsCoincident(line.End)) { context |= IntersectionType.TouchEnd; } if (context == 0) { context = IntersectionType.TouchOther; } return(context); }
/// <summary> /// Constructor for a grazing intersection. If the supplied positions are /// actually closer than the coordinate resolution (1 micron), a simple /// intersection will be defined. /// </summary> /// <param name="p1">The 1st intersection.</param> /// <param name="p2">The 2nd intersection.</param> IntersectionData(IPointGeometry p1, IPointGeometry p2) { m_X1 = p1; if (!p1.IsCoincident(p2)) { m_X2 = p2; } m_SortValue = 0.0; m_Context1 = 0; m_Context2 = 0; }
/// <summary> /// Determines which side of a divider a horizontal line segment lies on. /// Used in point in polygon. /// </summary> /// <param name="id">The divider to process</param> /// <param name="s">Start of horizontal segment.</param> /// <param name="e">End of segment.</param> /// <param name="od">The divider the side refers to. This usually refers to /// the supplied divider, but may refer to another divider in a situation where the /// horizontal segment passes directly through one end of <paramref name="d"/>.</param> /// <returns>Side.Left if the line is to the left of the divider, Side.Right /// if line to the right, Side.On if the end of the line is coincident with /// the divider, Side.Unknown if an error arose.</returns> /// <remarks> /// Rather than passing in 2 PointGeometry objects, it would be better to pass /// in a HorizontalRay object, since 2 arbitrary positions are not guaranteed /// to be horizontal. /// </remarks> internal static Side GetSide(IDivider id, IPointGeometry s, IPointGeometry e, out IDivider od) { // If the end of the horizontal segment hits the start or // the end of the specified divider, we have a situation where the divider // may not be the divider which is adjacent to the segment. In // that case, use special code to determine the side code. // Otherwise just convert the supplied divider into a line, and get the // side code by looking for a vertex which has a different // northing from that of the horizontal segment. Debug.Assert(s.Easting.Microns <= e.Easting.Microns); double d = e.Easting.Meters - s.Easting.Meters; Debug.Assert(d>=0.0); HorizontalRay hseg = new HorizontalRay(s, d); if (e.IsCoincident(id.From) || e.IsCoincident(id.To)) // e both times! return hseg.GetSide(id, e.IsCoincident(id.From), out od); else { od = id; return od.LineGeometry.GetSide(hseg); } }
/// <summary> /// Executes the move operation. /// </summary> /// <param name="to">The revised position of the reference point.</param> internal void Execute(PointGeometry to) { // Remember the old and new positions. IPointGeometry txtPos = m_Label.Position; IPointGeometry polPos = m_Label.GetPolPosition(); if (txtPos.IsCoincident(polPos)) { m_OldPosition = null; } else { m_OldPosition = PointGeometry.Create(polPos); } m_NewPosition = to; // Move the reference position (and rework any enclosing polygon) m_Label.SetPolPosition(m_NewPosition); // Peform standard completion steps Complete(); }
internal override void MouseMove(IPosition p) { PointFeature oldCurrentPoint = m_CurrentPoint; CadastralMapModel map = CadastralMapModel.Current; EditingController ec = EditingController.Current; ILength size = new Length(ec.Project.Settings.PointHeight * 0.5); m_CurrentPoint = (map.QueryClosest(p, size, SpatialType.Point) as PointFeature); if (m_Start == null) { // If the mouse is over a different point, ensure the old point is erased if (oldCurrentPoint != null && !Object.ReferenceEquals(oldCurrentPoint, m_CurrentPoint)) { ErasePainting(); } return; } if (m_CurrentPoint == null) { m_End = PointGeometry.Create(p); } else { m_End = m_CurrentPoint; } if (m_End.IsCoincident(m_Start)) { m_End = null; return; } ErasePainting(); }
/// <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; }
/// <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; }
/// <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 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 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> /// Checks if a position is coincident with a line segment /// </summary> /// <param name="p">The position to test</param> /// <param name="start">The start of the segment.</param> /// <param name="end">The end of the segment.</param> /// <param name="tolsq">The tolerance (squared) to use. Default is XYTOLSQ.</param> /// <returns>True if the test position lies somewhere along the segment.</returns> public static bool IsCoincidentWith(IPointGeometry p, IPointGeometry start, IPointGeometry end, double tolsq) { // Check whether there is exact coincidence at either end. if (p.IsCoincident(start) || p.IsCoincident(end)) return true; // Get the distance squared of a perpendicular dropped from // the test position to the segment (or the closest end if the // perpendicular does not fall ON the segment). return (BasicGeom.DistanceSquared(p.X, p.Y, start.X, start.Y, end.X, end.Y) < tolsq); }