private static void AssignInteriorRings([NotNull] IEnumerable <Linestring> interiorRings, [NotNull] IList <RingGroup> leftPolys, [NotNull] IList <RingGroup> rightPolys, [NotNull] IList <RingGroup> bothSidePolys, [NotNull] MultiLinestring unCutParts, double tolerance) { foreach (Linestring unCutIsland in interiorRings) { // Assuming no conflicts (island within island) between un-cut islands because // they all come from the same source. int assignmentCount = 0; assignmentCount += AssignInteriorRing(unCutIsland, rightPolys, tolerance); assignmentCount += AssignInteriorRing(unCutIsland, leftPolys, tolerance); assignmentCount += AssignInteriorRing(unCutIsland, bothSidePolys, tolerance); if (assignmentCount == 0) { unCutParts.AddLinestring(unCutIsland); } Assert.True(assignmentCount < 2, "Multiple inner ring assignments!"); } }
public MultipleRingNavigator([NotNull] MultiLinestring sourceRings, [NotNull] MultiLinestring targets, double tolerance, bool allowTargetTargetIntersections = false) : base(sourceRings, targets, tolerance) { Assert.ArgumentCondition(sourceRings.IsClosed, "Source rings must be closed."); // TODO: Implement target-self-intersecting navigation (vertical, spaghetti, multipatch rings, // and especially simplified self-intersecting lines) // TODO: Implement Reshape and avoid phantom points _allowTargetTargetIntersections = allowTargetTargetIntersections; }
private static void AssignInteriorRings([NotNull] IEnumerable <Linestring> interiorRings, [NotNull] ICollection <RingGroup> polygons, [NotNull] MultiLinestring unassignedParts, double tolerance) { foreach (Linestring interiorRing in interiorRings) { // Assuming no conflicts (island within island) between un-cut islands because // they all come from the same source. int assignmentCount = AssignInteriorRing(interiorRing, polygons, tolerance); if (assignmentCount == 0) { unassignedParts.AddLinestring(interiorRing); } Assert.True(assignmentCount < 2, "Multiple inner ring assignments!"); } }
private static int GetContainingRingIndex([NotNull] MultiLinestring polygon, [NotNull] Linestring containedRing, double tolerance, out bool ringsAreEqual, out IntersectionPoint3D touchPoint) { IList <IntersectionPoint3D> intersectionPoints = GeomTopoOpUtils.GetIntersectionPoints(polygon, containedRing, tolerance); ringsAreEqual = false; touchPoint = null; // Equal to outer ring -> removes outer ring in original // or equal to inner ring -> ignore cookie cutter if (intersectionPoints.Count == 2 && intersectionPoints[0].SourcePartIndex == intersectionPoints[1].SourcePartIndex && intersectionPoints[0].Point.Equals(intersectionPoints[1].Point) && intersectionPoints[0].Type == IntersectionPointType.LinearIntersectionStart && intersectionPoints[1].Type == IntersectionPointType.LinearIntersectionEnd) { ringsAreEqual = true; return(intersectionPoints[0].SourcePartIndex); } var outerRingIntersections = intersectionPoints .Where(i => polygon.GetLinestring(i.SourcePartIndex).ClockwiseOriented == true) .ToList(); // Touching outer ring in one point -> boundary loop in original if (outerRingIntersections.Count > 0) { Assert.True(outerRingIntersections.Count < 2, "Unexpected number of touching points."); touchPoint = outerRingIntersections[0]; return(touchPoint.SourcePartIndex); } // Inside an inner ring -> ignore cookie cutter for (int i = 0; i < polygon.PartCount; i++) { Linestring ring = polygon.GetLinestring(i); if (ring.ClockwiseOriented == true) { continue; } int currentIdx = i; bool?areaContainsXY = GeomRelationUtils.AreaContainsXY( ring, containedRing, intersectionPoints.Where(ip => ip.SourcePartIndex == currentIdx), tolerance, true); if (areaContainsXY == true) { return(i); } } // Inside an outer ring but not an inner ring: Add as island for (int i = 0; i < polygon.PartCount; i++) { Linestring ring = polygon.GetLinestring(i); if (ring.ClockwiseOriented == false) { continue; } if (GeomRelationUtils.AreaContainsXY(ring, containedRing.StartPoint, tolerance) == true) { return(i); } } return(-1); }
/// <summary> /// Cuts the source ring using the target and returns separate lists /// of result rings on the left/right side of the cut line. /// </summary> /// <param name="leftPolys">Result polygons on the left side of the cut line.</param> /// <param name="rightPolys">Result polygons on the right side of the cut line.</param> /// <param name="clipPolys"></param> /// <param name="undefinedSidePolys"></param> /// <param name="unCutParts"></param> /// <returns>Whether the cut operation was successful or not.</returns> public bool CutXY([NotNull] out IList <RingGroup> leftPolys, [NotNull] out IList <RingGroup> rightPolys, [NotNull] out IList <RingGroup> clipPolys, [NotNull] out IList <MultiLinestring> undefinedSidePolys, [NotNull] out MultiLinestring unCutParts) { Assert.ArgumentCondition(_subcurveNavigator.Source.IsClosed, "source must be closed."); // Based on Weiler–Atherton clipping algorithm, added specific logic for // linear intersections, un-closed target lines and multi-parts. // Potential enhancements: Do not insert phantom points! IList <Linestring> rightRings = GetRightSideRings(); IList <Linestring> leftRings = GetLeftSideRings(); IList <Linestring> duplicates = new List <Linestring>(); if (!_subcurveNavigator.Target.IsClosed && _subcurveNavigator.AreIntersectionPointsNonSequential()) { // Cut backs result in duplicates which are both on the left and the right! duplicates = RemoveDuplicateRings(leftRings, rightRings); } // Assign the cut inner rings (anti-clockwise) to un-cut outer rings... var unCutOuterRings = _subcurveNavigator.GetNonIntersectedSourceRings() .Where(r => r.ClockwiseOriented != false) .ToList(); rightPolys = AssignToResultRingGroups(rightRings, unCutOuterRings); leftPolys = AssignToResultRingGroups(leftRings, unCutOuterRings); IList <RingGroup> bothSidePolys = AssignToResultRingGroups(duplicates, unCutOuterRings); unCutParts = unCutOuterRings.Count == 1 ? (MultiLinestring) new RingGroup(unCutOuterRings[0]) : new MultiPolycurve(unCutOuterRings); // Assign the remaining interior rings; AssignInteriorRings(rightRings, leftPolys, rightPolys, bothSidePolys, unCutParts, _subcurveNavigator.Tolerance); AssignInteriorRings(leftRings, leftPolys, rightPolys, bothSidePolys, unCutParts, _subcurveNavigator.Tolerance); AssignInteriorRings(duplicates, leftPolys, rightPolys, bothSidePolys, unCutParts, _subcurveNavigator.Tolerance); // Assign the inner rings from the original var unCutIslands = _subcurveNavigator.GetNonIntersectedSourceRings() .Where(r => r.ClockwiseOriented == false); AssignInteriorRings(unCutIslands, leftPolys, rightPolys, bothSidePolys, unCutParts, _subcurveNavigator.Tolerance); // Assign closed cut lines completely contained by an outer ring (and not by an inner ring) var unusedCutRings = _subcurveNavigator.GetNonIntersectedTargets().Where(t => t.IsClosed); undefinedSidePolys = bothSidePolys.Cast <MultiLinestring>().ToList(); clipPolys = new List <RingGroup>(); foreach (Linestring unusedCutRing in unusedCutRings) { MultiLinestring updatedUnCut; RingGroup cookie; if (!unCutParts.IsEmpty && TryCutCookie(unusedCutRing, unCutParts, out updatedUnCut, out cookie)) { unCutParts = MultiPolycurve.CreateEmpty(); undefinedSidePolys.Add(updatedUnCut); clipPolys.Add(cookie); continue; } RingGroup updatedCut; if (TryCutCookie(unusedCutRing, leftPolys, out updatedCut, out cookie)) { clipPolys.Add(cookie); continue; } if (TryCutCookie(unusedCutRing, rightPolys, out updatedCut, out cookie)) { clipPolys.Add(cookie); continue; } if (TryCutCookie(unusedCutRing, bothSidePolys, out updatedCut, out cookie)) { clipPolys.Add(cookie); } } return((rightPolys.Count > 0 && leftPolys.Count > 0) || undefinedSidePolys.Count > 1 || clipPolys.Count > 0); }