/// <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; } } }
/// <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! }
private static bool ArePointsOnDifferentSide(Line3D ofLine, Pnt3D point1, Pnt3D point2, double tolerance, out bool?anyPointAtLeft) { anyPointAtLeft = null; if (point1 == null || point2 == null) { return(false); } double d1 = ofLine.GetDistanceXYPerpendicularSigned(point1); if (Math.Abs(d1) < tolerance) { d1 = 0; } double d2 = ofLine.GetDistanceXYPerpendicularSigned(point2); if (Math.Abs(d2) < tolerance) { d2 = 0; } anyPointAtLeft = d1 > 0 || d2 > 0; return(d1 * d2 < 0); }
/// <summary> /// The dot product (scalar product) of u and v. /// </summary> /// <param name="u"></param> /// <param name="v"></param> /// <returns></returns> public static double DotProduct(Pnt3D u, Vector v) { Assert.AreEqual(u.Dimension, v.Dimension, "Dimensions of input are not equal"); return(DotProduct(u[0], u[1], u[2], v[0], v[1], v[2])); }
public static bool Equals3D([NotNull] Pnt3D p0, [NotNull] Pnt3D p1, double tolerance) { if (tolerance <= 0) { // Equals also if difference is non-significant return(MathUtils.AreSignificantDigitsEqual(p0.X, p1.X) && MathUtils.AreSignificantDigitsEqual(p0.Y, p1.Y) && MathUtils.AreSignificantDigitsEqual(p0.Z, p1.Z)); } return(MathUtils.IsWithinTolerance(Math.Abs(p0.X - p1.X), tolerance, MathUtils.GetDoubleSignificanceEpsilon( p0.X, p1.X)) && MathUtils.IsWithinTolerance(Math.Abs(p0.Y - p1.Y), tolerance, MathUtils.GetDoubleSignificanceEpsilon( p0.Y, p1.Y)) && MathUtils.IsWithinTolerance(Math.Abs(p0.Z - p1.Z), tolerance, MathUtils.GetDoubleSignificanceEpsilon( p0.Z, p1.Z))); }
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); }
private static Pnt3D GetNonIntersectingTargetPoint( Linestring targetRing, IEnumerable <IntersectionPoint3D> intersectionPoints) { foreach (IntersectionPoint3D intersectionPoint in intersectionPoints) { if (intersectionPoint.Type == IntersectionPointType.LinearIntersectionIntermediate) { continue; } if (intersectionPoint.Type == IntersectionPointType.LinearIntersectionStart && MathUtils.AreEqual(intersectionPoint.VirtualSourceVertex, 0)) { // Do not use the first point, there might be no proper deviation // because it is actually an intermediate intersection point in a closed ring. continue; } Pnt3D nonIntersectingTargetPnt = intersectionPoint.GetNonIntersectingTargetPoint(targetRing, 0.5); return(nonIntersectingTargetPnt); } return(null); }
private List <Line3D> FromPoints(IEnumerable <Pnt3D> points, bool updateBounds = true) { var result = new List <Line3D>(); Pnt3D p1 = null; var idx = 0; foreach (Pnt3D point in points) { if (updateBounds) { UpdateBounds(point, idx, result, p1); } if (p1 != null) { result.Add(new Line3D(p1, point)); } p1 = point; idx++; } return(result); }
public void ReplacePoint(int pointIndex, Pnt3D newPoint) { Line3D segment1 = null; Line3D segment2 = null; if (pointIndex == SegmentCount && IsClosed) { // Last point index in closed ring pointIndex = 0; } // Update EndPoint of previous segment and StartPoint of this index' segment int?previousIdx = PreviousSegmentIndex(pointIndex); if (previousIdx != null) { segment1 = PreviousSegmentInRing(pointIndex); } if (pointIndex < SegmentCount) { segment2 = _segments[pointIndex]; } segment1?.SetEndPoint(newPoint); segment2?.SetStartPoint(newPoint); // TODO: To avoid grow-only bounds changes, make sure the replaced point was not an extreme point // ... if replacedPoint.X == XMax -> re-create envelope UpdateBounds(newPoint, pointIndex, Segments, null); }
/// <summary> /// Determines the intersection point's factor of this line and the other line, if there is an /// intersection. /// </summary> /// <param name="other">The other line</param> /// <param name="thisFactor">The factor to calculate the intersection point along this line.</param> /// <param name="otherFactor">The factor to calculate the intersection point along the other line.</param> /// <returns></returns> public bool TryGetIntersectionPointFactorsXY( [NotNull] Line3D other, out double thisFactor, out double otherFactor) { thisFactor = double.NaN; otherFactor = double.NaN; // TODO: Pnt.VectorProduct is the same as the Perp Product but should probably be called VectorProductXY or PerpProduct? // The vector product is not well-defined in 2D double d = PerpProduct(DeltaX, DeltaY, other.DeltaX, other.DeltaY); if (MathUtils.AreEqual(d, 0)) { // parallel in XY plane return(false); } Pnt3D w = StartPoint - other.StartPoint; thisFactor = PerpProduct(other.DeltaX, other.DeltaY, w.X, w.Y) / d; otherFactor = PerpProduct(DeltaX, DeltaY, w.X, w.Y) / d; return(true); }
public void UpdatePoint(int pointIndex, double x, double y, double z) { var newPoint = new Pnt3D(x, y, z); // Do not update the existing point to avoid (even temporary) gaps between segments (IsClosed is called everywhere all the time...) ReplacePoint(pointIndex, newPoint); }
/// <summary> /// Determines whether the test point is completely contained (true) or /// on the boundary of the specified ring (i.e. intersecting the linestring). /// </summary> /// <param name="closedRing">The containing ring.</param> /// <param name="testPoint">The test point.</param> /// <param name="tolerance">The tolerance.</param> /// <param name="disregardingOrientation">Whether the orientation should be disregarded. /// If false, a point inside the ring is considered outside if the ring orientation is /// counter-clockwise.</param> /// <returns>Null, if the point is on the boundary, true if the point is inside the ring.</returns> public static bool?AreaContainsXY([NotNull] Linestring closedRing, [NotNull] Pnt3D testPoint, double tolerance, bool disregardingOrientation = false) { Assert.ArgumentCondition(closedRing.IsClosed, "Ring must be closed"); if (AreBoundsDisjoint(closedRing, testPoint.X, testPoint.Y, tolerance)) { return(disregardingOrientation ? false : closedRing.ClockwiseOriented == false); } // Test boundary: if (LinesContainXY(closedRing, testPoint, tolerance)) { return(null); } bool result = HasRayOddCrossingNumber(closedRing, testPoint, tolerance); if (disregardingOrientation) { return(result); } if (closedRing.ClockwiseOriented == false) { result = !result; } return(result); }
public bool IntersectsPointXY([NotNull] Pnt3D point, double tolerance) { if (!ExtentIntersects(point, tolerance, true)) { return(false); } double distanceAlong; double distanceFrom = GetDistanceXYPerpendicularSigned(point, out distanceAlong); if (Math.Abs(distanceFrom) > tolerance) { return(false); } double toleranceFactor = tolerance / Length3D; if (distanceAlong < 0 - toleranceFactor || distanceAlong > 1 + toleranceFactor) { return(false); } return(true); }
public double GetDistanceAlong(Pnt3D point, bool asRatio = false) { Pnt3D relativeToStart = point - StartPoint; double distanceRatio = DirectionVector.GetFactor(relativeToStart); return(asRatio ? distanceRatio : Length3D *distanceRatio); }
public Pnt3D VectorProduct(Pnt3D other) { return(new Pnt3D( Y * other.Z - Z * other.Y, Z * other.X - X * other.Z, X * other.Y - Y * other.X )); }
public Line3D(Pnt3D startPoint, Pnt3D endPoint) { StartPoint = startPoint; EndPoint = endPoint; // Avoid / defer the (expensive) _extent creation UpdateBounds(startPoint, endPoint); }
public IntersectionRun(IntersectionPoint3D nextIntersection, Linestring subcurve, Pnt3D includedRingStartPoint) { _includedRingStartPoint = includedRingStartPoint; NextIntersection = nextIntersection; Subcurve = subcurve; }
/// <summary> /// The perpendicular distance of the infinite line defined by the Start/End points of this /// line to the specified point. /// </summary> /// <param name="point"></param> /// <returns></returns> public double GetDistancePerpendicular(Pnt3D point) { var sp = new Line3D(StartPoint, point); Vector vectorProduct = GeomUtils.CrossProduct(DirectionVector, sp.DirectionVector); return(Math.Abs(vectorProduct.Length / DirectionVector.Length)); }
public double GetDistancePerpendicular(Pnt3D point, bool inXY) { // Could theoretically be faster with using the cross-product variant with Z=0 double distanceAlongRatio; Pnt3D pointOnLine; return(GetDistancePerpendicular(point, inXY, out distanceAlongRatio, out pointOnLine)); }
public void SetConstantZ(int startVertexIndex, int endVertexIndex, double z) { for (int i = startVertexIndex; i <= endVertexIndex; i++) { Pnt3D point = GetPoint3D(i); UpdatePoint(i, point.X, point.Y, z); } }
public bool EqualsXY(Pnt3D other, double tolerance) { if (other == null) { return(false); } return(MathUtils.AreEqual(Coordinates[0], other.X, tolerance) && MathUtils.AreEqual(Coordinates[1], other.Y, tolerance)); }
private void UpdateBounds(Pnt3D startPoint, Pnt3D endPoint) { XMin = Math.Min(startPoint.X, endPoint.X); YMin = Math.Min(startPoint.Y, endPoint.Y); ZMin = Math.Min(startPoint.Z, endPoint.Z); XMax = Math.Max(startPoint.X, endPoint.X); YMax = Math.Max(startPoint.Y, endPoint.Y); ZMax = Math.Max(startPoint.Z, endPoint.Z); }
public bool IsPotentialPseudoLinearIntersection([NotNull] Line3D sourceLine, [NotNull] Line3D targetLine, double tolerance) { // NOTE: Acute intersections can have 'pseudo-linear' intersections: // These can result in incorrect touches calculation (same as ArcObjects). // | // | // |\ // \ // \ // |\ this piece will be considered a linear intersecting if the tolerance // is smaller than the lenght of | but large enough that the bottom point // of the vertical line is within the tolerance of the \ line's interior! // // The condition is (tolerance / sin(alpha)) > dist(intersection point along source) - source start/end) // that indicates if this might be a 'pseudo-linear' intersection. // -> If the adjacent segment has a (proper) linear intersection along the same // stretch, the pseudo-linear intersection can be safely ignored. In TouchesXY() // probably all pseudo-linear intersections should be ignored?! if ((SourceStartIntersects ^ SourceEndIntersects) && (TargetStartIntersects ^ TargetEndIntersects)) { Pnt3D sourcePoint = SourceStartIntersects ? sourceLine.StartPoint : sourceLine.EndPoint; Pnt3D nonIntersectingTargetPnt = TargetStartIntersects ? targetLine.EndPoint : targetLine.StartPoint; if (SourceStartIntersects && _source1Factor > 0 && _source1Factor < 1) { double sourceEndFactor; Pnt3D touchPoint = GetLinearIntersectionEnd(sourceLine, out sourceEndFactor); return(IsBelowThreshold(sourcePoint, touchPoint, nonIntersectingTargetPnt, tolerance)); } if (SourceEndIntersects && _source2Factor > 0 && _source2Factor < 1) { double sourceInteriorFactor; Pnt3D touchPoint = GetLinearIntersectionStart(sourceLine, out sourceInteriorFactor); return(IsBelowThreshold(sourcePoint, touchPoint, nonIntersectingTargetPnt, tolerance)); } } return(false); }
private void UpdateBounds([NotNull] IPnt point, int pointIndex, [NotNull] IList <Line3D> currentSegments, [CanBeNull] Pnt3D previouslyAdded) { if (point.X < XMin) { XMin = point.X; } if (point.X > XMax) { XMax = point.X; } if (point.Y < YMin) { YMin = point.Y; RightMostBottomIndex = pointIndex; } else if (!(point.Y > YMin)) { Pnt3D currentRightMostLowestPnt; if (currentSegments.Count == RightMostBottomIndex) { if (previouslyAdded != null) { currentRightMostLowestPnt = Assert.NotNull(previouslyAdded); } else { currentRightMostLowestPnt = currentSegments[currentSegments.Count - 1].EndPoint; } } else { currentRightMostLowestPnt = currentSegments[RightMostBottomIndex].StartPoint; } if (point.X > currentRightMostLowestPnt.X) { // equally low, but more to the right RightMostBottomIndex = pointIndex; } } if (point.Y > YMax) { YMax = point.Y; } _boundingBox = null; }
public IntersectionPoint3D( [NotNull] Pnt3D point, double virtualRingVertexIndex, SegmentIntersection segmentIntersection = null, IntersectionPointType type = IntersectionPointType.Unknown) { Point = point; VirtualSourceVertex = virtualRingVertexIndex; SegmentIntersection = segmentIntersection; Type = type; }
public void ReverseOrientation() { _directionVector = null; Pnt3D tmp = StartPoint; StartPoint = EndPoint; EndPoint = tmp; UpdateDeltas(); }
public bool ContainsSourceStart(out Pnt3D startPoint) { if (_includedRingStartPoint != null) { startPoint = _includedRingStartPoint; return(true); } startPoint = null; return(false); }
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); }
public int?FindPointIdx([NotNull] Pnt3D point, bool inXY, double tolerance = double.Epsilon, bool allowIndexing = true) { if (SpatialIndex == null && allowIndexing && SegmentCount > AllowIndexingThreshold) { SpatialIndex = SpatialHashSearcher <int> .CreateSpatialSearcher(this); } if (SpatialIndex != null) { foreach (int foundSegmentIdx in SpatialIndex.Search(point, tolerance)) { Line3D segment = this[foundSegmentIdx]; bool found = inXY ? segment.StartPoint.EqualsXY(point, tolerance) : segment.StartPoint.Equals(point, tolerance); if (found) { return(foundSegmentIdx); } } } else { for (var i = 0; i < SegmentCount; i++) { bool found = inXY ? _segments[i].StartPoint.EqualsXY(point, tolerance) : _segments[i].StartPoint.Equals(point, tolerance); if (found) { return(i); } } } bool isEndPoint = inXY ? EndPoint.EqualsXY(point, tolerance) : EndPoint.Equals(point, tolerance); if (isEndPoint) { return(PointCount - 1); } return(null); }
public double GetDistanceSigned(double x, double y, double z) { if (!IsDefined) { throw new InvalidOperationException("Plane is not defined."); } var v = new Pnt3D(x, y, z); double dotProd = GeomUtils.DotProduct(v, A, B, C); return((dotProd - D) / LengthOfNormal); }