/// <summary> /// Returns the points from the target that deviate from the source, i.e. that are on neither of the sourceLines. /// </summary> /// <param name="targetSegments"></param> /// <param name="sourceLine1"></param> /// <param name="sourceLine2"></param> /// <param name="xyTolerance"></param> /// <param name="before"></param> /// <param name="after"></param> private void GetNonIntersectingTargetPoints(ISegmentList targetSegments, Line3D sourceLine1, Line3D sourceLine2, double xyTolerance, [CanBeNull] out Pnt3D before, [CanBeNull] out Pnt3D after) { Line3D thisTargetSegment = targetSegments[TargetIndex]; before = null; after = null; double?targetStartIntersectionFactor = GetTargetStartIntersectionFactor(); double?targetEndIntersectionFactor = GetTargetEndIntersectionFactor(); if (targetStartIntersectionFactor == null && targetEndIntersectionFactor == null) { // The target interior intersects the source start before = thisTargetSegment.StartPoint; after = thisTargetSegment.EndPoint; } else if (targetStartIntersectionFactor != null) { Line3D previousTargetSegment = targetSegments.PreviousSegment(TargetIndex); if (previousTargetSegment != null) { before = previousTargetSegment.StartPoint; after = thisTargetSegment.EndPoint; } } else // targetEndIntersectionFactor != null { Line3D nextTargetSegment = targetSegments.NextSegment(TargetIndex); if (nextTargetSegment != null) { before = thisTargetSegment.StartPoint; after = nextTargetSegment.EndPoint; } } if (before != null) { if (sourceLine1.IntersectsPointXY(before, xyTolerance) || sourceLine2.IntersectsPointXY(before, xyTolerance)) { before = null; } } if (after != null) { if (sourceLine1.IntersectsPointXY(after, xyTolerance) || sourceLine2.IntersectsPointXY(after, xyTolerance)) { after = null; } } }
private static void ClassifyIntersection([NotNull] ISegmentList source, [NotNull] ISegmentList target, [NotNull] IntersectionPoint3D intersectionPoint, out bool isInbound, out bool isOutbound) { Assert.False(intersectionPoint.Type == IntersectionPointType.Unknown, "Cannot classify unknown intersection type."); if (intersectionPoint.Type == IntersectionPointType.LinearIntersectionIntermediate) { isInbound = false; isOutbound = false; return; } int?previousTargetSegment = intersectionPoint.GetNonIntersectingTargetSegmentIndex(target, false); int?nextTargetSegment = intersectionPoint.GetNonIntersectingTargetSegmentIndex(target, true); Pnt3D previousPntAlongTarget = previousTargetSegment == null ? null : target[previousTargetSegment.Value].StartPoint; Pnt3D nextPntAlongTarget = nextTargetSegment == null ? null : target[nextTargetSegment.Value].EndPoint; isInbound = nextPntAlongTarget != null && intersectionPoint.IsOnTheRightSide(source, nextPntAlongTarget, true); isOutbound = previousPntAlongTarget != null && intersectionPoint.IsOnTheRightSide(source, previousPntAlongTarget, true); }
protected void ClassifyIntersections( [NotNull] ISegmentList source, [NotNull] ISegmentList target, [NotNull] out IList <IntersectionPoint3D> intersectionsInboundTarget, [NotNull] out IList <IntersectionPoint3D> intersectionsOutboundTarget) { intersectionsInboundTarget = new List <IntersectionPoint3D>(); intersectionsOutboundTarget = new List <IntersectionPoint3D>(); foreach (IntersectionPoint3D intersectionPoint3D in IntersectionsAlongSource) { bool isInbound, isOutbound; ClassifyIntersection(source, target, intersectionPoint3D, out isInbound, out isOutbound); // In-bound takes precedence because if the target is both inbound and outbound (i.e. touching from inside) // the resulting part is on the left of the cut line which is consistent with other in-bound intersections. if (isInbound) { intersectionsInboundTarget.Add(intersectionPoint3D); } else if (isOutbound) { intersectionsOutboundTarget.Add(intersectionPoint3D); } } if (!target.IsClosed) { // Remove dangles that cannot cut and would lead to duplicate result rings RemoveDeadEndIntersections(intersectionsInboundTarget, intersectionsOutboundTarget); } }
/// <summary> /// Determines whether the specified closed polycurve contains (including the boundary) the /// specified test geometry. This method ignores the orientation. /// </summary> /// <param name="closedPolycurve"></param> /// <param name="targetSegments"></param> /// <param name="tolerance"></param> /// <param name="knownIntersections"></param> /// <returns></returns> public static bool PolycurveContainsXY( [NotNull] ISegmentList closedPolycurve, [NotNull] Linestring targetSegments, double tolerance, IEnumerable <SegmentIntersection> knownIntersections = null) { if (AreBoundsDisjoint(closedPolycurve, targetSegments, tolerance)) { return(false); } if (knownIntersections == null) { knownIntersections = SegmentIntersectionUtils.GetSegmentIntersectionsXY( closedPolycurve, targetSegments, tolerance); } // TODO: GeomUtils.IEnumerable<IntersectionPoint3D> GetIntersectionPointsWithDeviation() for better performance var intersectionPoints = GeomTopoOpUtils.GetIntersectionPoints(closedPolycurve, targetSegments, tolerance, knownIntersections, false); Pnt3D nonIntersectingTargetPnt = GetNonIntersectingTargetPoint(targetSegments, intersectionPoints); IEnumerable <Pnt3D> checkPoints = nonIntersectingTargetPnt != null ? new[] { nonIntersectingTargetPnt } : targetSegments.GetPoints(); return(checkPoints.All(p => PolycurveContainsXY(closedPolycurve, p, tolerance))); // The check points are assumed not to be on the boundary! }
/// <summary> /// Gets the segment intersections between the specified path and multiline-string. /// </summary> /// <param name="segmentList1"></param> /// <param name="segmentList2"></param> /// <param name="tolerance"></param> /// <param name="optimizeLinearIntersections">If true, an optimiziation that improves the /// performance when linear intersections are present. If a segmentList1 segment is equal /// to a linestring2 segment it will directly be used. Other intersections will be missed. /// </param> /// <returns>The segment intersections with the index of the cut part of multiLinestring2</returns> public static IEnumerable <SegmentIntersection> GetSegmentIntersectionsXY( [NotNull] ISegmentList segmentList1, [NotNull] ISegmentList segmentList2, double tolerance, bool optimizeLinearIntersections = false) { if (GeomRelationUtils.AreBoundsDisjoint(segmentList1, segmentList2, tolerance)) { yield break; } SegmentIntersection previousLinearIntersection = null; for (var lineIdx = 0; lineIdx < segmentList1.SegmentCount; lineIdx++) { Line3D line = segmentList1.GetSegment(lineIdx); foreach (SegmentIntersection result in GetSegmentIntersectionsXY( lineIdx, line, segmentList2, tolerance, previousLinearIntersection)) { if (optimizeLinearIntersections && result.SegmentsAreEqualInXy) { previousLinearIntersection = result; } yield return(result); } } }
/// <summary> /// Creates the IntersectionPoint3D of type <see cref="IntersectionPointType.LinearIntersectionEnd"/> /// that corresponds to the second intersection along the source segment. /// </summary> /// <param name="intersection">The intersection</param> /// <param name="sourceSegments"></param> /// <param name="targetSegments"></param> /// <returns></returns> public static IntersectionPoint3D GetLinearIntersectionEnd( [NotNull] SegmentIntersection intersection, [NotNull] ISegmentList sourceSegments, [NotNull] ISegmentList targetSegments) { Line3D sourceSegment = sourceSegments[intersection.SourceIndex]; double endFactor; Pnt3D toPoint = intersection.GetLinearIntersectionEnd(sourceSegment, out endFactor); int sourcePartIdx; int sourceSegmentIndex = sourceSegments.GetLocalSegmentIndex( intersection.SourceIndex, out sourcePartIdx); IntersectionPoint3D result = new IntersectionPoint3D( toPoint, sourceSegmentIndex + endFactor, intersection) { Type = IntersectionPointType.LinearIntersectionEnd, SourcePartIndex = sourcePartIdx }; int targetPartIdx; result.VirtualTargetVertex = CalculateVirtualTargetVertex( targetSegments, result.Type, intersection, out targetPartIdx); result.TargetPartIndex = targetPartIdx; return(result); }
private static bool IsTargetRingNullPoint( [NotNull] SegmentIntersection thisLinearIntersection, [CanBeNull] SegmentIntersection previousLinearIntersection, ISegmentList targetSegments) { if (previousLinearIntersection == null) { return(false); } if (thisLinearIntersection.TargetStartIntersects && targetSegments.IsFirstSegmentInPart(thisLinearIntersection.TargetIndex) && targetSegments.IsLastSegmentInPart(previousLinearIntersection.TargetIndex) && targetSegments.IsClosed) { return(true); } // The linear intersections can be inverted, i.e. travelling backwards along target: if (thisLinearIntersection.TargetStartIntersects && // TargetEndIntersects?? targetSegments.IsLastSegmentInPart(thisLinearIntersection.TargetIndex) && targetSegments.IsFirstSegmentInPart(previousLinearIntersection.TargetIndex) && targetSegments.IsClosed) { return(true); } return(false); }
private static IEnumerable <SegmentIntersection> GetSegmentIntersectionsXY( int sourceLineIdx, [NotNull] Line3D sourceLine, [NotNull] ISegmentList segmentList, double tolerance, [CanBeNull] SegmentIntersection previousLinearIntersection) { if (previousLinearIntersection != null) { // speculation: this segment also runs along the next / previous target SegmentIntersection continuousIntersection = TryGetContinuousLinearIntersection( sourceLineIdx, sourceLine, segmentList, previousLinearIntersection, tolerance); if (continuousIntersection != null) { yield return(continuousIntersection); yield break; } } IEnumerable <KeyValuePair <int, Line3D> > segmentsByIndex = segmentList.FindSegments(sourceLine, tolerance); foreach (SegmentIntersection intersection in IntersectLineWithLinestringXY( sourceLine, sourceLineIdx, segmentsByIndex, tolerance)) { yield return(intersection); } }
private bool TargetCrossesBetweenSourceSegments([CanBeNull] Line3D sourceLine1, [CanBeNull] Line3D sourceLine2, [NotNull] ISegmentList targetSegments, double tolerance, out bool?targetDeviatesToLeft) { targetDeviatesToLeft = null; if (sourceLine1 == null || sourceLine2 == null) { return(false); } Pnt3D targetBefore; Pnt3D targetAfter; GetNonIntersectingTargetPoints(targetSegments, sourceLine1, sourceLine2, tolerance, out targetBefore, out targetAfter); if (targetBefore == null && targetAfter == null) { return(false); } bool isRightTurn = sourceLine1.IsLeftXY(sourceLine2.EndPoint) < 0; if (targetBefore != null && targetAfter != null) { bool beforeIsRight = IsRightOfVertex(isRightTurn, sourceLine1, sourceLine2, targetBefore); bool afterIsRight = IsRightOfVertex(isRightTurn, sourceLine1, sourceLine2, targetAfter); if (beforeIsRight != afterIsRight) { return(true); } targetDeviatesToLeft = !beforeIsRight; } else { if (targetBefore != null) { targetDeviatesToLeft = !IsRightOfVertex(isRightTurn, sourceLine1, sourceLine2, targetBefore); } else { targetDeviatesToLeft = !IsRightOfVertex(isRightTurn, sourceLine1, sourceLine2, targetAfter); } } return(false); }
protected SubcurveNavigator(ISegmentList source, ISegmentList target, double tolerance) { Source = source; Target = target; Tolerance = tolerance; }
private static IEnumerable <SegmentIntersection> FilterIntersections( [NotNull] IEnumerable <SegmentIntersection> intersectionsForSourceSegment, [NotNull] ISegmentList source, [NotNull] IDictionary <int, HashSet <double> > usedIntersectionFactorsByPart) { foreach (SegmentIntersection intersection in intersectionsForSourceSegment) { var filter = false; if (!intersection.HasLinearIntersection && intersection.SingleInteriorIntersectionFactor == null) { double intersectionFactor; if (intersection.SourceStartIntersects) { intersectionFactor = 0; } else if (intersection.SourceEndIntersects) { intersectionFactor = 1; } else if (intersection.TargetStartIntersects) { intersectionFactor = Assert.NotNull(intersection.TargetStartFactor).Value; } else { intersectionFactor = Assert.NotNull(intersection.TargetEndFactor).Value; } int partIndex; int localSegmentIndex = source.GetLocalSegmentIndex(intersection.SourceIndex, out partIndex); double intersectionFactorPartGlobal = localSegmentIndex + intersectionFactor; // TODO: Extract class IntersectionFactors which encapsulates all the // Contains, Add etc. methods, probably even the filtering if (ContainsIntersection(usedIntersectionFactorsByPart, partIndex, intersectionFactorPartGlobal)) { filter = true; } else { AddIntersectionFactor(intersectionFactorPartGlobal, partIndex, usedIntersectionFactorsByPart, source); } } if (!filter) { yield return(intersection); } } }
private static void CollectIntersection( [NotNull] SegmentIntersection intersection, [NotNull] ISegmentList source, [NotNull] IDictionary <int, HashSet <double> > allLinearIntersectionFactors, [NotNull] ICollection <SegmentIntersection> intersectionsForCurrentSourceSegment) { // Collect segments for current index in list, unless they are clearly not needed // (and would need to be filtered by a later linear intersection if added) bool isLinear = intersection.HasLinearIntersection; if (isLinear) { int partIndex; int localSegmentIndex = source.GetLocalSegmentIndex(intersection.SourceIndex, out partIndex); double linearIntersectionStartFactor = localSegmentIndex + intersection.GetLinearIntersectionStartFactor(true); double linearIntersectionEndFactor = localSegmentIndex + intersection.GetLinearIntersectionEndFactor(true); if (intersection.IsSourceZeroLength2D && ContainsIntersection(allLinearIntersectionFactors, partIndex, linearIntersectionEndFactor)) { // avoid double linear segments if the source segment is vertical return; } AddIntersectionFactor(linearIntersectionStartFactor, partIndex, allLinearIntersectionFactors, source); AddIntersectionFactor(linearIntersectionEndFactor, partIndex, allLinearIntersectionFactors, source); } if (!isLinear && intersection.SourceStartIntersects && source.IsFirstSegmentInPart(intersection.SourceIndex) && source.IsClosed) { // will be reported again at the end return; } if (!isLinear && intersection.SourceEndIntersects && !source.IsLastSegmentInPart(intersection.SourceIndex)) { // will be reported again at next segment return; } intersectionsForCurrentSourceSegment.Add(intersection); }
public Line3D TryGetIntersectionLine([NotNull] ISegmentList source) { if (!HasLinearIntersection) { return(null); } Line3D sourceSegment = source[SourceIndex]; return(TryGetIntersectionLine(sourceSegment)); }
public bool IsOnTheRightSide([NotNull] ISegmentList source, [NotNull] Pnt3D testPoint, bool disregardOrientation = false) { Assert.True(source.IsClosed, "Source must be closed ring(s)"); Linestring sourceRing = source.GetPart(SourcePartIndex); double sourceRatio; int sourceSegmentIdx = GetLocalSourceIntersectionSegmentIdx(sourceRing, out sourceRatio); Line3D sourceSegment = sourceRing[sourceSegmentIdx]; if (sourceRatio > 0 && sourceRatio < 1) { // The intersection is on the source segment's interior return(sourceSegment.IsLeftXY(testPoint) < 0); } Line3D previousSegment, nextSegment; // Intersection at source vertex 0 or 1 -> get the 2 adjacent segments // ReSharper disable once CompareOfFloatsByEqualityOperator if (sourceRatio == 0) { previousSegment = sourceRing.PreviousSegmentInRing(sourceSegmentIdx, true); nextSegment = SegmentIntersection.IsSourceZeroLength2D ? sourceRing.NextSegmentInRing(sourceSegmentIdx, true) : sourceSegment; } else // sourceRatio == 1 { previousSegment = SegmentIntersection.IsSourceZeroLength2D ? sourceRing.PreviousSegmentInRing( sourceSegmentIdx, true) : sourceSegment; nextSegment = sourceRing.NextSegmentInRing(sourceSegmentIdx, true); } bool result = GeomTopoOpUtils.IsOnTheRightSide(previousSegment.StartPoint, Point, nextSegment.EndPoint, testPoint); if (!disregardOrientation && sourceRing.ClockwiseOriented == false) { result = !result; } return(result); }
private static IEnumerable <Linestring> GetUnused(ISegmentList linestrings, HashSet <int> usedIndexes) { for (int i = 0; i < linestrings.PartCount; i++) { if (usedIndexes.Contains(i)) { continue; } Linestring cutLine = linestrings.GetPart(i); yield return(cutLine); } }
public static bool LinesContainXY([NotNull] ISegmentList segments, [NotNull] Pnt3D testPoint, double tolerance) { foreach (KeyValuePair <int, Line3D> segmentsAroundPoint in segments.FindSegments(testPoint, tolerance)) { Line3D segment = segmentsAroundPoint.Value; if (segment.IntersectsPointXY(testPoint, tolerance)) { return(true); } } return(false); }
/// <summary> /// Determines whether the test point is completely contained (true) or /// on the boundary of the specified rings (e.e. intersecting the boundary). /// </summary> /// <param name="closedRings">The containing rings.</param> /// <param name="testPoint"></param> /// <param name="tolerance"></param> /// <returns>Null, if the point is on the boundary, true if the point is inside the ring.</returns> public static bool?AreaContainsXY([NotNull] ISegmentList closedRings, [NotNull] Pnt3D testPoint, double tolerance) { Assert.ArgumentCondition(closedRings.IsClosed, "Rings must be closed"); if (AreBoundsDisjoint(closedRings, testPoint.X, testPoint.Y, tolerance)) { return(false); } // Test boundary: if (LinesContainXY(closedRings, testPoint, tolerance)) { return(null); } return(HasRayOddCrossingNumber(closedRings, testPoint, tolerance)); }
GetFilteredIntersectionsOrderedAlongSourceSegments( [NotNull] IEnumerable <SegmentIntersection> intersections, [NotNull] ISegmentList source) { var intersectionsForCurrentSourceSegment = new List <SegmentIntersection>(3); var allLinearIntersectionFactors = new Dictionary <int, HashSet <double> >(); int currentIndex = -1; foreach (SegmentIntersection intersection in intersections) { if (intersection.SourceIndex != currentIndex) { currentIndex = intersection.SourceIndex; foreach ( SegmentIntersection collectedIntersection in FilterIntersections(intersectionsForCurrentSourceSegment, source, allLinearIntersectionFactors) .OrderBy(i => i.GetFirstIntersectionAlongSource())) { yield return(collectedIntersection); } intersectionsForCurrentSourceSegment.Clear(); } CollectIntersection(intersection, source, allLinearIntersectionFactors, intersectionsForCurrentSourceSegment); } foreach ( SegmentIntersection collectedIntersection in FilterIntersections(intersectionsForCurrentSourceSegment, source, allLinearIntersectionFactors) .OrderBy(i => i.GetFirstIntersectionAlongSource())) { yield return(collectedIntersection); } }
private static void AddIntersectionFactor( double localIntersectionFactor, int partIndex, [NotNull] IDictionary <int, HashSet <double> > usedIntersectionFactorsByPart, [NotNull] ISegmentList source) { Linestring part = source.GetPart(partIndex); if (IsRingStartOrEnd(part, localIntersectionFactor)) { // add both start and end point AddIntersectionFactor(0, partIndex, usedIntersectionFactorsByPart); AddIntersectionFactor(part.SegmentCount, partIndex, usedIntersectionFactorsByPart); } else { AddIntersectionFactor(localIntersectionFactor, partIndex, usedIntersectionFactorsByPart); } }
/// <summary> /// Determines whether the specified closed polycurve contains (including the boundary) the /// specified test point. This method ignores the orientation. /// </summary> /// <param name="closedPolycurve"></param> /// <param name="testPoint"></param> /// <param name="tolerance"></param> /// <returns></returns> public static bool PolycurveContainsXY([NotNull] ISegmentList closedPolycurve, [NotNull] Pnt3D testPoint, double tolerance) { if (AreBoundsDisjoint(closedPolycurve, testPoint.X, testPoint.Y, tolerance)) { return(false); } // Test boundary: if (LinesContainXY(closedPolycurve, testPoint, tolerance)) { return(true); } if (!closedPolycurve.IsClosed) { return(false); } return(HasRayOddCrossingNumber(closedPolycurve, testPoint, tolerance)); }
private static SegmentIntersection TryGetContinuousLinearIntersection( int lineIdx, [NotNull] Line3D line1, [NotNull] ISegmentList segmentList2, [CanBeNull] SegmentIntersection previous, double tolerance) { if (previous == null || previous.SourceIndex + 1 != lineIdx) { // We jumped over some source segments return(null); } int?targetIdx = previous.LinearIntersectionInOppositeDirection ? segmentList2.PreviousSegmentIndex(previous.TargetIndex) : segmentList2.NextSegmentIndex(previous.TargetIndex); if (targetIdx == null) { return(null); } Line3D targetSegment = segmentList2.GetSegment(targetIdx.Value); if (targetSegment.EqualsXY(line1, tolerance)) { SegmentIntersection resultIntersection = SegmentIntersection.CreateCoincidenceIntersectionXY( lineIdx, targetIdx.Value, previous.LinearIntersectionInOppositeDirection); return(resultIntersection); } return(null); }
/// <summary> /// Returns source rings disjoint from target and target rings disjoint from source. /// </summary> /// <param name="ringPredicate"></param> /// <returns></returns> private IEnumerable <Linestring> GetRingsDisjointFromOtherPoly( Predicate <Linestring> ringPredicate) { var result = new List <Linestring>(); // W.r.t. AreaContainsXY returning null: if the start point is on the boundary there // are duplicate rings which need to be identified elsewhere. ISegmentList target = _subcurveNavigator.Target; foreach (Linestring ring in _subcurveNavigator.GetNonIntersectedSourceRings() .Where(r => ringPredicate(r))) { bool?contains = GeomRelationUtils.AreaContainsXY( target, ring.StartPoint, _subcurveNavigator.Tolerance); if (contains == false) { // disjoint result.Add(ring); } } ISegmentList source = _subcurveNavigator.Source; foreach (Linestring ring in _subcurveNavigator.GetNonIntersectedTargets() .Where(r => ringPredicate(r))) { bool?contains = GeomRelationUtils.AreaContainsXY( source, ring.StartPoint, _subcurveNavigator.Tolerance); if (contains == false) { result.Add(ring); } } return(result); }
/// <summary> /// Returns all self intersections except the intersection between consecutive segments. /// </summary> /// <param name="sourceLineGlobalIdx"></param> /// <param name="sourceLine"></param> /// <param name="containingSegmentList"></param> /// <param name="tolerance"></param> /// <returns></returns> public static IEnumerable <SegmentIntersection> GetRelevantSelfIntersectionsXY( int sourceLineGlobalIdx, [NotNull] Line3D sourceLine, [NotNull] ISegmentList containingSegmentList, double tolerance) { // a predicate, i.e. i != sourceGlobalIdx && i != sourceGlobalIdx - 1 && i != sourceGlobalIdx + 1 Predicate <int> predicate = i => i != sourceLineGlobalIdx; IEnumerable <KeyValuePair <int, Line3D> > segmentsByGlobalIdx = containingSegmentList.FindSegments(sourceLine, tolerance, true, predicate); foreach (SegmentIntersection intersection in IntersectLineWithLinestringXY( sourceLine, sourceLineGlobalIdx, segmentsByGlobalIdx, tolerance)) { int?nextSegmentIndex = containingSegmentList.NextSegmentIndex(intersection.SourceIndex); if (nextSegmentIndex == intersection.TargetIndex && !intersection.HasLinearIntersection) { continue; } int?previousSegmentIndex = containingSegmentList.PreviousSegmentIndex(intersection.SourceIndex); if (previousSegmentIndex == intersection.TargetIndex && !intersection.HasLinearIntersection) { continue; } yield return(intersection); } }
private static double CalculateVirtualTargetVertex(ISegmentList targetSegments, IntersectionPointType intersectionType, SegmentIntersection segmentIntersection, out int targetPartIndex) { int targetSegmentIndex = targetSegments.GetLocalSegmentIndex( segmentIntersection.TargetIndex, out targetPartIndex); double result; switch (intersectionType) { case IntersectionPointType.LinearIntersectionStart: result = targetSegmentIndex + segmentIntersection.GetRatioAlongTargetLinearStart(); break; case IntersectionPointType.LinearIntersectionEnd: case IntersectionPointType.LinearIntersectionIntermediate: result = targetSegmentIndex + segmentIntersection.GetRatioAlongTargetLinearEnd(); break; case IntersectionPointType.Crossing: case IntersectionPointType.TouchingInPoint: result = targetSegmentIndex + segmentIntersection.GetIntersectionPointFactorAlongTarget(); break; default: throw new InvalidOperationException( $"Unsupported type :{intersectionType}"); } return(result); }
/// <summary> /// Determines whether the horizontal ray from the test point to XMin crosses /// the specified segments an odd number of times. /// NOTE: For test points exactly on the boundary the result depends on whether /// the the point is on the right or the left boundary! /// </summary> /// <param name="segments"></param> /// <param name="testPoint"></param> /// <param name="tolerance"></param> /// <returns></returns> private static bool HasRayOddCrossingNumber([NotNull] ISegmentList segments, [NotNull] IPnt testPoint, double tolerance) { bool result = false; // Get the intersecting segments along the horizontal line from XMin to the testPoint IEnumerable <KeyValuePair <int, Line3D> > intersectingSegments = segments.FindSegments(segments.XMin, testPoint.Y, testPoint.X, testPoint.Y, tolerance); foreach (KeyValuePair <int, Line3D> path2Segment in intersectingSegments.OrderBy( kvp => kvp.Key)) { Line3D segment = path2Segment.Value; Pnt3D previous = segment.StartPoint; Pnt3D vertex = segment.EndPoint; if (vertex.Y < testPoint.Y && previous.Y >= testPoint.Y || // downward crossing previous.Y < testPoint.Y && vertex.Y >= testPoint.Y) // upward crossing { double dX = previous.X - vertex.X; double dY = previous.Y - vertex.Y; double y = testPoint.Y - vertex.Y; if (vertex.X + y * dX / dY < testPoint.X) { result = !result; } } } return(result); }
public int?GetNonIntersectingTargetSegmentIndex( [NotNull] ISegmentList target, bool forwardAlongTarget) { if (SegmentIntersection.HasLinearIntersection) { if (Type == IntersectionPointType.LinearIntersectionIntermediate) { return(null); } // search the first non-intersecting point at the and of the linear intersection bool deviationIsBackward = Type == IntersectionPointType.LinearIntersectionStart; if (SegmentIntersection.LinearIntersectionInOppositeDirection) { deviationIsBackward = !deviationIsBackward; } if ((deviationIsBackward && forwardAlongTarget) || (!deviationIsBackward && !forwardAlongTarget)) { return(null); } // get the first non-intersecting point after the linear intersection, check its side: if (SegmentIntersection.TargetStartIntersects && SegmentIntersection.TargetEndIntersects) { // Target is within source (or equal to source) return(forwardAlongTarget ? target.NextSegmentIndex(SegmentIntersection.TargetIndex) : target.PreviousSegmentIndex(SegmentIntersection.TargetIndex)); } if (!SegmentIntersection.TargetStartIntersects && !SegmentIntersection.TargetEndIntersects) { // Source is completely within target return(SegmentIntersection.TargetIndex); } // Either the source start or the source end intersects but not both // The same must be the case for the target Assert.True( SegmentIntersection.TargetEndIntersects ^ SegmentIntersection.TargetStartIntersects, "Either target start or end is expected to intersect"); } if (SegmentIntersection.TargetStartIntersects) { return(forwardAlongTarget ? SegmentIntersection.TargetIndex : target.PreviousSegmentIndex(SegmentIntersection.TargetIndex)); } if (SegmentIntersection.TargetEndIntersects) { return(forwardAlongTarget ? target.NextSegmentIndex(SegmentIntersection.TargetIndex) : SegmentIntersection.TargetIndex); } // By now all linear cases should have been handled Assert.False(SegmentIntersection.HasLinearIntersection, "Not all linear cases were handled."); return(SegmentIntersection.TargetIndex); }
/// <summary> /// Determines whether the two paths cross (i.e. the intersection is a point) in this intersection. /// </summary> /// <param name="sourceSegments"></param> /// <param name="targetSegments"></param> /// <param name="tolerance"></param> /// <param name="targetDeviatesToLeft">If there is no crossing: Whether the target segment deviates /// to the left of the source.</param> /// <returns></returns> public bool IsCrossingInPoint([NotNull] ISegmentList sourceSegments, [NotNull] ISegmentList targetSegments, double tolerance, out bool?targetDeviatesToLeft) { targetDeviatesToLeft = null; if (HasLinearIntersection) { return(false); } if (SingleInteriorIntersectionFactor != null) { // interior/interior targetDeviatesToLeft = true; return(true); } Line3D sourceLine = sourceSegments[SourceIndex]; // Target start/end on source interior: check previous/next target segments if (TargetStartIsOnSourceInterior) { Line3D thisTargetSegment = targetSegments[TargetIndex]; Line3D previousTargetSegment = targetSegments.PreviousSegment(TargetIndex); if (previousTargetSegment == null) { return(false); } if (ArePointsOnDifferentSide(sourceLine, previousTargetSegment.StartPoint, thisTargetSegment.EndPoint, tolerance, out targetDeviatesToLeft)) { return(true); } } if (TargetEndIsOnSourceInterior) { // check the sides of the target segment and the next target segment Line3D thisTargetSegment = targetSegments[TargetIndex]; Line3D nextTargetSegment = targetSegments.NextSegment(TargetIndex); if (nextTargetSegment == null) { return(false); } if (ArePointsOnDifferentSide(sourceLine, thisTargetSegment.StartPoint, nextTargetSegment.EndPoint, tolerance, out targetDeviatesToLeft)) { return(true); } } if (SourceStartIntersects) { Line3D previousSource = sourceSegments.PreviousSegment(SourceIndex, true); return(TargetCrossesBetweenSourceSegments(previousSource, sourceLine, targetSegments, tolerance, out targetDeviatesToLeft)); } if (SourceEndIntersects) { Line3D nextSource = sourceSegments.NextSegment(SourceIndex, true); return(TargetCrossesBetweenSourceSegments(sourceLine, nextSource, targetSegments, tolerance, out targetDeviatesToLeft)); } return(false); }
public static IntersectionPoint3D CreateSingleIntersectionPoint( [NotNull] SegmentIntersection intersection, [NotNull] ISegmentList sourceSegments, [NotNull] ISegmentList targetSegments, double tolerance) { Line3D sourceSegment = sourceSegments.GetSegment(intersection.SourceIndex); double?targetIntersectionFactorOnSource = intersection.GetIntersectionPointFactorAlongSource(); int sourcePartIdx; int sourceSegmentIndex = sourceSegments.GetLocalSegmentIndex( intersection.SourceIndex, out sourcePartIdx); Pnt3D point; double sourceIndex; // ReSharper disable once CompareOfFloatsByEqualityOperator if (targetIntersectionFactorOnSource == 0) { point = sourceSegment.StartPoint; sourceIndex = sourceSegmentIndex; } // ReSharper disable once CompareOfFloatsByEqualityOperator else if (targetIntersectionFactorOnSource == 1) { point = sourceSegment.EndPoint; sourceIndex = sourceSegmentIndex + 1; } else { point = sourceSegment.GetPointAlong( targetIntersectionFactorOnSource.Value, true); sourceIndex = sourceSegmentIndex + targetIntersectionFactorOnSource.Value; } IntersectionPoint3D result = new IntersectionPoint3D(point, sourceIndex, intersection) { SourcePartIndex = sourcePartIdx }; bool?targetDeviatesToLeft; result.Type = intersection.IsCrossingInPoint( sourceSegments, targetSegments, tolerance, out targetDeviatesToLeft) ? IntersectionPointType.Crossing : IntersectionPointType.TouchingInPoint; result.TargetDeviatesToLeftOfSource = targetDeviatesToLeft; int targetPartIdx; result.VirtualTargetVertex = CalculateVirtualTargetVertex( targetSegments, result.Type, intersection, out targetPartIdx); result.TargetPartIndex = targetPartIdx; return(result); }
/// <summary> /// Collects the intersection points from the sorted and filtered intersections. /// </summary> /// <param name="sortedRelevantIntersections"></param> /// <param name="sourceSegments"></param> /// <param name="targetSegments"></param> /// <param name="tolerance"></param> /// <param name="includeLinearIntersectionIntermediatePoints"> /// Includes all intermediate points along linear intersections. /// NOTE: They will not be ordered along the source segments when compared /// to other intersection point types.</param> /// <returns></returns> public static IList <IntersectionPoint3D> CollectIntersectionPoints( [NotNull] IEnumerable <SegmentIntersection> sortedRelevantIntersections, [NotNull] ISegmentList sourceSegments, [NotNull] ISegmentList targetSegments, double tolerance, bool includeLinearIntersectionIntermediatePoints = false) { var result = new List <IntersectionPoint3D>(); IntersectionPoint3D startPoint = null; IntersectionPoint3D previousLinearEnd = null; foreach (SegmentIntersection intersection in sortedRelevantIntersections) { if (!intersection.HasLinearIntersection) { // emit previous linear stretch (to maintain order along source) TryAddLinearIntersectionStretch(startPoint, previousLinearEnd, result); startPoint = null; previousLinearEnd = null; result.Add( IntersectionPoint3D.CreateSingleIntersectionPoint( intersection, sourceSegments, targetSegments, tolerance)); continue; } IntersectionPoint3D fromPoint = IntersectionPoint3D.GetLinearIntersectionStart( intersection, sourceSegments, targetSegments); IntersectionPoint3D toPoint = IntersectionPoint3D.GetLinearIntersectionEnd( intersection, sourceSegments, targetSegments); if (previousLinearEnd == null) { startPoint = fromPoint; } // For symmetry reasons, also the target ring start/end should break linear intersections. // Otherwise the result is different in XY depending on the order of the arguments. bool isTargetRingNullPoint = IsTargetRingNullPoint( fromPoint.SegmentIntersection, previousLinearEnd?.SegmentIntersection, targetSegments); if (previousLinearEnd == null || !isTargetRingNullPoint && previousLinearEnd.Point.Equals(fromPoint.Point)) { // first, or connected to previous -> continue: if (previousLinearEnd != null && includeLinearIntersectionIntermediatePoints) { previousLinearEnd.Type = IntersectionPointType.LinearIntersectionIntermediate; result.Add(previousLinearEnd); } previousLinearEnd = toPoint; } else { // emit TryAddLinearIntersectionStretch(startPoint, previousLinearEnd, result); // re-start with current from/to startPoint = fromPoint; // isTargetRingNullPoint ? fromPoint : null; previousLinearEnd = toPoint; // isTargetRingNullPoint ? toPoint : null; } } TryAddLinearIntersectionStretch(startPoint, previousLinearEnd, result); return(result); }