/// <summary> /// Computes whether the given coordinate is on the boundary of the polygon. /// </summary> /// <param name="shell">The polygon shell.</param> /// <param name="coordinate">The coordinate to check.</param> /// <param name="precisionModel">The precision model.</param> /// <returns><c>true</c> if the coordinate is on the boundary of the polygon; otherwise, <c>false</c>.</returns> /// <exception cref="System.ArgumentNullException">The shell is null.</exception> private static Boolean ComputeOnBoundary(IEnumerable <Coordinate> shell, Coordinate coordinate, PrecisionModel precisionModel) { IEnumerator <Coordinate> enumerator = shell.GetEnumerator(); if (!enumerator.MoveNext()) { return(false); } Coordinate current = enumerator.Current, next; while (enumerator.MoveNext()) { next = enumerator.Current; if (LineAlgorithms.Contains(current, next, coordinate, precisionModel)) { return(true); } current = next; } return(false); }
/// <summary> /// Removes any points in the polygon which are collinear with its nearest neighbors. /// </summary> /// <param name="polygon">The polygon to search for collinear points</param> private Polygon RemoveCollinearPoints(Polygon polygon) { if (polygon.Count <= 3) { return(polygon); } Polygon result = new Polygon(); for (int i = 0; i < polygon.Count; i++) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 current = polygon[i]; Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); // Skip adding this point to the result if it is collinear with its neighbors. if (LineAlgorithms.AreCollinear(prev, current, next)) { continue; } result.Add(current); } return(result); }
/// <summary> /// Gets the intersection type between the two given sweep line segments. /// </summary> /// <param name="x">First sweep line segment.</param> /// <param name="y">Second sweep line segment.</param> /// <returns>The type of intersection that exists between the two sweep line segments, considering the position of the sweepline.</returns> public SweepLineIntersection GetIntersection(SweepLineSegment x, SweepLineSegment y) { IList <Coordinate> intersections = LineAlgorithms.Intersection(x.LeftCoordinate, x.RightCoordinate, y.LeftCoordinate, y.RightCoordinate, _precisionModel); if (intersections.Count == 0) { return(SweepLineIntersection.NotExists); } if (intersections[0].X < _sweepLinePosition) { return(SweepLineIntersection.Passed); } if (intersections[0].X > _sweepLinePosition) { return(SweepLineIntersection.NotPassed); } return((_intersections.Contains(Tuple.Create(x, y)) || _intersections.Contains(Tuple.Create(y, x))) ? SweepLineIntersection.Passed : SweepLineIntersection.NotPassed); }
public void LineAlgorithmsCentroidTest() { // valid lines Assert.AreEqual(new Coordinate(4.5, 6.5), LineAlgorithms.Centroid(new Coordinate(2, 5), new Coordinate(7, 8))); Assert.AreEqual(new Coordinate(5, 51), LineAlgorithms.Centroid(new BasicLineString(new[] { new Coordinate(5, 2), new Coordinate(5, 100) }))); // invalid line Assert.IsFalse(LineAlgorithms.Centroid(new Coordinate(Double.NaN, 5), new Coordinate(7, 8)).IsValid); // valid line string List <Coordinate> lineString = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(1, 7) }; Assert.AreEqual(new Coordinate(1, 4), LineAlgorithms.Centroid(lineString)); // valid lines with fixed precision PrecisionModel precision = new PrecisionModel(0.001); Assert.AreEqual(new Coordinate(22000, 19000), LineAlgorithms.Centroid(new Coordinate(23000, 16000), new Coordinate(20000, 22000), precision)); Assert.AreEqual(new Coordinate(434898000, 461826000), LineAlgorithms.Centroid(new Coordinate(864325000, 96041000), new Coordinate(5470000, 827611000), precision)); // valid lines at given fixed precision Assert.AreEqual(new Coordinate(22000, 19000), LineAlgorithms.Centroid(new Coordinate(23654, 16412), new Coordinate(19530, 22009), precision)); Assert.AreEqual(new Coordinate(434898000, 461826000), LineAlgorithms.Centroid(new Coordinate(864325203, 96041202), new Coordinate(5470432, 827611534), precision)); }
/// <summary> /// Simplifies a segment of the line string. /// </summary> /// <param name="startIndex">The starting index of the segment.</param> /// <param name="endIndex">The ending index of the segment.</param> protected void SimplifySegment(Int32 startIndex, Int32 endIndex) { if (endIndex <= startIndex + 1) // the segment is a line { return; } Double maxDistance = 0; Int32 maxIndex = startIndex; // find the most distant coordinate from the line between the starting and ending coordinates for (Int32 coordinateIndex = startIndex + 1; coordinateIndex < endIndex; coordinateIndex++) { Double distance = LineAlgorithms.Distance(_source[startIndex], _source[endIndex], _source[coordinateIndex], PrecisionModel); if (distance > maxDistance) { maxIndex = coordinateIndex; maxDistance = distance; } } if (maxDistance <= _delta) // the distance is smaller than the delta, the all coordinates should be removed { return; } // recursively simplify both segments _marks[maxIndex] = true; SimplifySegment(startIndex, maxIndex); SimplifySegment(maxIndex, startIndex); }
public void Line_Side() { LineAlgorithms.SideOf(Vector2.Zero, Vector2.UnitY, Vector2.UnitX).Should().Be(1); LineAlgorithms.SideOf(Vector2.Zero, Vector2.UnitX + Vector2.UnitY, Vector2.UnitY).Should().BeApproximately(-MathF.Sqrt(0.5f), 1e-6f); }
/// <summary> /// Returns true if on is a reflex vertex. The polygon must be ordered in CCW for this to work properly. /// </summary> /// <param name="prev"></param> /// <param name="on"></param> /// <param name="next"></param> /// <returns></returns> private bool IsReflex(Microsoft.Xna.Framework.Vector2 prev, Microsoft.Xna.Framework.Vector2 on, Microsoft.Xna.Framework.Vector2 next) { // YOGESH: Added following condition of collinearity if (LineAlgorithms.AreCollinear(prev, on, next)) { return(false); } return(Right(prev, on, next)); }
public void Line_SegmentIntersectionLowSymmetry() { var result = LineAlgorithms.LineSegmentIntersection( new Vector2(704, 336), new Vector2(569, 299), new Vector2(436, 218), new Vector2(446, 357)); result.Should().NotBeNull(); Vector2X.Equals(result.IntersectionPoint, new Vector2(439.269f, 263.444f), 1e-3f).Should().BeTrue( "Intersection was not in correct position."); }
public void Line_SegmentIntersectionParallel() { var result = LineAlgorithms.LineSegmentIntersection( Vector2.UnitX, -Vector2.UnitX, Vector2.UnitX + Vector2.UnitY, -Vector2.UnitX + Vector2.UnitY); result.Should().BeNull(); }
public void Line_SegmentIntersectionHighSymmetry() { var result = LineAlgorithms.LineSegmentIntersection( Vector2.UnitX, -Vector2.UnitX, Vector2.UnitY, -Vector2.UnitY); result.Should().NotBeNull(); result.U1.Should().BeApproximately(0.5f, 0.0001f); result.U2.Should().BeApproximately(0.5f, 0.0001f); Vector2X.Equals(Vector2.Zero, result.IntersectionPoint).Should().BeTrue("Lines should intersect at origin"); }
/// <summary> /// Checks whether the given coordinate is on the shell. /// </summary> private void CheckBoundary() { IsOnBoundary = false; for (Int32 coordIndex = 0; coordIndex < _shell.Count - 1 || (coordIndex < _shell.Count && _shell[0] != _shell[_shell.Count - 1]); coordIndex++) { if (LineAlgorithms.Contains(_shell[coordIndex], _shell[(coordIndex + 1) % _shell.Count], _coordinate, PrecisionModel)) { IsOnBoundary = true; break; } } }
/// <summary> /// Registers a passed intersection point by the sweep line between the two given arguments. /// </summary> /// <remarks> /// The position of the sweep line is updated when necessary. /// </remarks> /// <param name="x">First sweep line segment.</param> /// <param name="y">Second sweep line segment.</param> public void PassIntersection(SweepLineSegment x, SweepLineSegment y) { Intersection intersection = LineAlgorithms.Intersection(x.LeftCoordinate, x.RightCoordinate, y.LeftCoordinate, y.RightCoordinate, this.precisionModel); if (intersection == null) { return; } if (intersection.Coordinate.X > this.sweepLinePosition) { this.sweepLinePosition = intersection.Coordinate.X; this.intersections.Clear(); } this.intersections.Add(Tuple.Create(x, y)); this.intersections.Add(Tuple.Create(y, x)); }
/// <summary> /// Partitions a polygon by triangles. /// </summary> public void Compute() { // source: http://www.codeproject.com/Articles/8238/Polygon-Triangulation-in-C _result = new List <Coordinate[]>(); IList <Coordinate> shell = new List <Coordinate>(_shell); IList <Coordinate> nextShell = new List <Coordinate>(shell); Coordinate[] triangle; Int32 coordinateCount = shell.Count - 1; Int32 coordinateIndex = 1; while (shell.Count != 4) { Coordinate centroid = LineAlgorithms.Centroid(shell[(shell.Count + coordinateIndex - 1) % coordinateCount], shell[(shell.Count + coordinateIndex + 1) % coordinateCount], PrecisionModel); nextShell.Remove(nextShell[coordinateIndex]); if (WindingNumberAlgorithm.IsInsidePolygon(shell, centroid, PrecisionModel) && !ShamosHoeyAlgorithm.Intersects(nextShell, PrecisionModel)) { triangle = new Coordinate[3] { shell[(coordinateCount + coordinateIndex - 1) % coordinateCount], shell[coordinateIndex], shell[(coordinateCount + coordinateIndex + 1) % coordinateCount] }; _result.Add(triangle); shell.Remove(shell[coordinateIndex]); coordinateIndex = 1; coordinateCount--; } else { coordinateIndex = (coordinateIndex + 1) % (shell.Count - 1); nextShell = new List <Coordinate>(shell); } } triangle = new Coordinate[3] { shell[0], shell[1], shell[2] }; _result.Add(triangle); _hasResult = true; }
/// <summary> /// Simplifies a segment of the line string. /// </summary> /// <param name="startIndex">The starting index of the segment.</param> /// <param name="endIndex">The ending index of the segment.</param> private void SimplifySegment(Int32 startIndex, Int32 endIndex) { if (this.Source[startIndex] == null || this.Source[endIndex] == null) { return; } if (endIndex <= startIndex + 1) // the segment is a line { return; } Double maxDistance = 0; Int32 maxIndex = startIndex; // find the most distant coordinate from the line between the starting and ending coordinates for (Int32 coordinateIndex = startIndex + 1; coordinateIndex < endIndex; coordinateIndex++) { if (this.Source[coordinateIndex] == null) { continue; } Double distance = LineAlgorithms.Distance(this.Source[startIndex], this.Source[endIndex], this.Source[coordinateIndex], this.PrecisionModel); if (distance > maxDistance) { maxIndex = coordinateIndex; maxDistance = distance; } } if (maxDistance <= this.delta) { // the distance is smaller than the delta, all coordinates should be removed return; } // recursively simplify both segments this.marks[maxIndex] = true; this.SimplifySegment(startIndex, maxIndex); this.SimplifySegment(maxIndex, startIndex); }
/// <summary> /// Partitions a polygon by triangles. /// </summary> public void Compute() { this.result = new List <Coordinate[]>(); List <Coordinate> shell = new List <Coordinate>(this.Source); List <Coordinate> nextShell = new List <Coordinate>(this.Source); Coordinate[] triangle; Int32 coordinateCount = shell.Count - 1; Int32 coordinateIndex = 1; while (shell.Count != 4) { Coordinate centroid = LineAlgorithms.Centroid(shell[(shell.Count + coordinateIndex - 1) % coordinateCount], shell[(shell.Count + coordinateIndex + 1) % coordinateCount], this.PrecisionModel); nextShell.Remove(nextShell[coordinateIndex]); if (!WindingNumberAlgorithm.InExterior(shell, centroid, this.PrecisionModel) && !ShamosHoeyAlgorithm.Intersects(nextShell, this.PrecisionModel)) { triangle = new Coordinate[3] { shell[(coordinateCount + coordinateIndex - 1) % coordinateCount], shell[coordinateIndex], shell[(coordinateCount + coordinateIndex + 1) % coordinateCount] }; this.result.Add(triangle); shell.Remove(shell[coordinateIndex]); coordinateIndex = 1; coordinateCount--; } else { coordinateIndex = (coordinateIndex + 1) % (shell.Count - 1); nextShell = new List <Coordinate>(shell); } } triangle = new Coordinate[3] { shell[0], shell[1], shell[2] }; this.result.Add(triangle); this.hasResult = true; }
public void LineAlgorithmsCoincidesTest() { // coinciding lines Coordinate firstCoordinate = new Coordinate(1, 1); CoordinateVector firstVector = new CoordinateVector(0, 2); Coordinate secondCoordinate = new Coordinate(1, 1); CoordinateVector secondVector = new CoordinateVector(0, 4); Assert.IsTrue(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); firstCoordinate = new Coordinate(2, 5); firstVector = new CoordinateVector(0, 1); secondCoordinate = new Coordinate(2, 4); secondVector = new CoordinateVector(0, 4); Assert.IsTrue(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); firstCoordinate = new Coordinate(15, 7); firstVector = new CoordinateVector(9, 5); secondCoordinate = new Coordinate(15, 7); secondVector = new CoordinateVector(9, 5); Assert.IsTrue(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); firstCoordinate = new Coordinate(2, 4); firstVector = new CoordinateVector(3, -1); secondCoordinate = new Coordinate(14, 0); secondVector = new CoordinateVector(3, -1); Assert.IsTrue(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); // not coinciding lines firstCoordinate = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondCoordinate = new Coordinate(1, 1); secondVector = new CoordinateVector(1, 0); Assert.IsFalse(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); firstCoordinate = new Coordinate(2, 5); firstVector = new CoordinateVector(0, 1); secondCoordinate = new Coordinate(2, 4); secondVector = new CoordinateVector(4, 0); Assert.IsFalse(LineAlgorithms.Coincides(firstCoordinate, firstVector, secondCoordinate, secondVector)); }
/// <summary> /// Registers a passed intersection point by the sweep line between the two given arguments. /// </summary> /// <remarks> /// The position of the sweep line is updated when necessary. /// </remarks> /// <param name="x">First sweep line segment.</param> /// <param name="y">Second sweep line segment.</param> public void PassIntersection(SweepLineSegment x, SweepLineSegment y) { IList <Coordinate> intersections = LineAlgorithms.Intersection(x.LeftCoordinate, x.RightCoordinate, y.LeftCoordinate, y.RightCoordinate, _precisionModel); if (intersections.Count == 0) { return; } if (intersections[0].X > _sweepLinePosition) { _sweepLinePosition = intersections[0].X; _intersections.Clear(); } _intersections.Add(Tuple.Create(x, y)); _intersections.Add(Tuple.Create(y, x)); }
/// <summary> /// Gets the intersection type between the two given sweep line segments. /// </summary> /// <param name="x">First sweep line segment.</param> /// <param name="y">Second sweep line segment.</param> /// <returns>The type of intersection that exists between the two sweep line segments, considering the position of the sweep line.</returns> public SweepLineIntersection GetIntersection(SweepLineSegment x, SweepLineSegment y) { Intersection intersection = LineAlgorithms.Intersection(x.LeftCoordinate, x.RightCoordinate, y.LeftCoordinate, y.RightCoordinate, this.precisionModel); if (intersection == null || intersection.Type == IntersectionType.None) { return(SweepLineIntersection.NotExists); } if (intersection.Coordinate.X < this.sweepLinePosition) { return(SweepLineIntersection.Passed); } if (intersection.Coordinate.X > this.sweepLinePosition) { return(SweepLineIntersection.NotPassed); } return((this.intersections.Contains(Tuple.Create(x, y)) || this.intersections.Contains(Tuple.Create(y, x))) ? SweepLineIntersection.Passed : SweepLineIntersection.NotPassed); }
private Microsoft.Xna.Framework.Vector2 LineSegmentPointNearestOrigin(Microsoft.Xna.Framework.Vector2 start, Microsoft.Xna.Framework.Vector2 end) { var delta = end - start; var perp = new Vector2(delta.Y, -delta.X); if (delta.LengthSquared() < Tolerance) { return(start); } var intersection = LineAlgorithms.LineSegmentIntersection( start, end, Vector2.Zero, perp); if (intersection.WithinFirstSegment) { return(intersection.IntersectionPoint); } return(start.LengthSquared() < end.LengthSquared() ? start : end); }
/// <summary> /// Processes the intersection event. /// </summary> /// <param name="intersectionEvent">The intersection event.</param> private void ProcessIntersectionEvent(IntersectionEvent intersectionEvent) { SweepLineSegment segment; Intersection intersection; /* * segment order before intersection: segmentBelow <-> segmentAbove <-> segment <-> segmentAboveAbove * segment order after intersection: segmentBelow <-> segment <-> segmentAbove <-> segmentAboveAbove */ segment = intersectionEvent.Above; SweepLineSegment segmentAbove = intersectionEvent.Below; // dandle closing intersection points when segments (partially) overlap each other if (intersectionEvent.IsClosing) { if (!this.sweepLine.IsAdjacent(segment.Edge, segmentAbove.Edge)) { this.intersections.Add(intersectionEvent.Vertex); this.edgeIndexes.Add(Tuple.Create(Math.Min(segment.Edge, segmentAbove.Edge), Math.Max(segment.Edge, segmentAbove.Edge))); } } // it is possible that the previously detected intersection point is not a real intersection, because a new segment started between them, // therefore a repeated check is necessary to carry out else if (this.sweepLine.Add(intersectionEvent)) { if (!this.sweepLine.IsAdjacent(segment.Edge, segmentAbove.Edge)) { this.intersections.Add(intersectionEvent.Vertex); this.edgeIndexes.Add(Tuple.Create(Math.Min(segment.Edge, segmentAbove.Edge), Math.Max(segment.Edge, segmentAbove.Edge))); intersection = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segmentAbove.LeftCoordinate, segmentAbove.RightCoordinate, this.PrecisionModel); if (intersection.Type == IntersectionType.Interval) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersection.End, Below = segment, Above = segmentAbove, IsClosing = true, }; this.eventQueue.Add(newIntersectionEvent); } } if (segmentAbove.Above != null) { intersection = LineAlgorithms.Intersection(segmentAbove.LeftCoordinate, segmentAbove.RightCoordinate, segmentAbove.Above.LeftCoordinate, segmentAbove.Above.RightCoordinate, this.PrecisionModel); if (intersection != null && intersection.Coordinate.X >= intersectionEvent.Vertex.X) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersection.Coordinate, Below = segmentAbove, Above = segmentAbove.Above }; if (!this.eventQueue.Contains(newIntersectionEvent)) { this.eventQueue.Add(newIntersectionEvent); } } } if (segment.Below != null) { intersection = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, this.PrecisionModel); if (intersection != null && intersection.Coordinate.X >= intersectionEvent.Vertex.X) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersection.Coordinate, Below = segment.Below, Above = segment }; if (!this.eventQueue.Contains(newIntersectionEvent)) { this.eventQueue.Add(newIntersectionEvent); } } } } }
public void LineAlgorithmsDistanceTest() { // distance of a line and a coordinate Coordinate lineStart = new Coordinate(0, 1); Coordinate lineEnd = new Coordinate(100, 1); Coordinate coordinate = new Coordinate(1, 1); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, lineEnd, coordinate)); lineStart = new Coordinate(0, 1); lineEnd = new Coordinate(100, 1); coordinate = new Coordinate(1, 7); Assert.AreEqual(6, LineAlgorithms.Distance(lineStart, lineEnd, coordinate)); lineStart = new Coordinate(5, 7); lineEnd = new Coordinate(5, 70); coordinate = new Coordinate(5, 6); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, lineEnd, coordinate)); lineStart = new Coordinate(5, 7); lineEnd = new Coordinate(5, 70); coordinate = new Coordinate(4, 7); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, lineEnd, coordinate)); lineStart = new Coordinate(0, 0); lineEnd = new Coordinate(2, -3); coordinate = new Coordinate(2, 1); Assert.AreEqual(2.22, Math.Round(LineAlgorithms.Distance(lineStart, lineEnd, coordinate), 2)); // distance of two lines lineStart = new Coordinate(0, 0); lineEnd = new Coordinate(100, 0); Coordinate secondLineStart = new Coordinate(0, 0); Coordinate secondLineEnd = new Coordinate(0, 100); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, lineEnd, secondLineStart, secondLineEnd)); lineStart = new Coordinate(0, 0); lineEnd = new Coordinate(100, 0); secondLineStart = new Coordinate(0, 1); secondLineEnd = new Coordinate(100, 1); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, lineEnd, secondLineStart, secondLineEnd)); lineStart = new Coordinate(2, 4); lineEnd = new Coordinate(5, 3); secondLineStart = new Coordinate(6, 2); secondLineEnd = new Coordinate(6, 6); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, lineEnd, secondLineStart, secondLineEnd)); lineStart = new Coordinate(1, 1); lineEnd = new Coordinate(1, 4); secondLineStart = new Coordinate(4, 2); secondLineEnd = new Coordinate(0, 2); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, lineEnd, secondLineStart, secondLineEnd)); // distance of an infinite line and a point lineStart = new Coordinate(1, 1); CoordinateVector coordinateVector = new CoordinateVector(0, 1); coordinate = new Coordinate(2, 1500); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, coordinateVector, coordinate)); lineStart = new Coordinate(1, 1); coordinateVector = new CoordinateVector(0, 1); coordinate = new Coordinate(1, 1500); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, coordinateVector, coordinate)); lineStart = new Coordinate(1, 1); coordinateVector = new CoordinateVector(0, 1); coordinate = new Coordinate(0, -100); Assert.AreEqual(1, LineAlgorithms.Distance(lineStart, coordinateVector, coordinate)); lineStart = new Coordinate(2, 4); coordinateVector = new CoordinateVector(-3, 1); coordinate = new Coordinate(14, 0); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, coordinateVector, coordinate)); lineStart = new Coordinate(2, 4); coordinateVector = new CoordinateVector(-3, 1); coordinate = new Coordinate(5, 3); Assert.AreEqual(0, LineAlgorithms.Distance(lineStart, coordinateVector, coordinate)); }
/// <summary> /// Compares two <see cref="SweepLineSegment" /> instances and returns a value indicating whether one is less than, equal to, or greater than the other. /// </summary> /// <remarks> /// The comparator applies a above-below relationship between the arguments, where a "greater" segment is above the another one. /// </remarks> /// <param name="first">The first <see cref="SweepLineSegment" /> to compare.</param> /// <param name="second">The second <see cref="SweepLineSegment" /> to compare.</param> /// <returns>A signed integer that indicates the relative values of <paramref name="first" /> and <paramref name="second" />.</returns> /// <exception cref="System.InvalidOperationException">Cannot compare non-overlapping sweep line segments.</exception> public Int32 Compare(SweepLineSegment first, SweepLineSegment second) { // Comparing non-overlapping segments is not supported. if (first.RightCoordinate.X < second.LeftCoordinate.X || first.LeftCoordinate.X > second.RightCoordinate.X) { throw new InvalidOperationException("Cannot compare non-overlapping sweep line segments."); } // The segments intersect. SweepLineIntersection intersection = GetIntersection(first, second); if (intersection != SweepLineIntersection.NotExists) { CoordinateVector xDiff = first.RightCoordinate - first.LeftCoordinate; Double xGradient = xDiff.X == 0 ? Double.MaxValue : xDiff.Y / xDiff.X; CoordinateVector yDiff = second.RightCoordinate - second.LeftCoordinate; Double yGradient = yDiff.X == 0 ? Double.MaxValue : yDiff.Y / yDiff.X; Int32 result = yGradient.CompareTo(xGradient); if (result == 0) { result = first.LeftCoordinate.X.CompareTo(second.LeftCoordinate.X); } if (result == 0) { result = second.LeftCoordinate.Y.CompareTo(first.LeftCoordinate.Y); } if (result == 0) { result = first.RightCoordinate.X.CompareTo(second.RightCoordinate.X); } if (result == 0) { result = second.RightCoordinate.Y.CompareTo(first.RightCoordinate.Y); } if (result == 0) { result = second.Edge.CompareTo(first.Edge); } if (intersection == SweepLineIntersection.Passed) { result *= -1; } return(result); } // The segments do not intersect. if (first.LeftCoordinate.X < second.LeftCoordinate.X) { Double[] verticalCollection = new[] { first.LeftCoordinate.Y, first.RightCoordinate.Y }; var verticalLineStart = new Coordinate(second.LeftCoordinate.X, verticalCollection.Min()); var verticalLineEnd = new Coordinate(second.LeftCoordinate.X, verticalCollection.Max()); IList <Coordinate> startIntersections = LineAlgorithms.Intersection(first.LeftCoordinate, first.RightCoordinate, verticalLineStart, verticalLineEnd, _precisionModel); // due to precision tolerance degeneracy we might not found the intersection return(startIntersections.Count > 0 ? startIntersections[0].Y.CompareTo(second.LeftCoordinate.Y) : ((first.LeftCoordinate.Y + first.RightCoordinate.Y) / 2.0).CompareTo(second.LeftCoordinate.Y)); } if (first.LeftCoordinate.X > second.LeftCoordinate.X) { Double[] verticalCollection = new[] { second.LeftCoordinate.Y, second.RightCoordinate.Y }; var verticalLineStart = new Coordinate(first.LeftCoordinate.X, verticalCollection.Min()); var verticalLineEnd = new Coordinate(first.LeftCoordinate.X, verticalCollection.Max()); IList <Coordinate> startIntersections = LineAlgorithms.Intersection(verticalLineStart, verticalLineEnd, second.LeftCoordinate, second.RightCoordinate, _precisionModel); return(startIntersections.Count > 0 ? first.LeftCoordinate.Y.CompareTo(startIntersections[0].Y) : first.LeftCoordinate.Y.CompareTo((second.LeftCoordinate.Y + second.RightCoordinate.Y) / 2.0)); } // first.LeftCoordinate.X == second.LeftCoordinate.X return(first.LeftCoordinate.Y.CompareTo(second.LeftCoordinate.Y)); }
public void LineAlgorithmsIntersectsTest() { // coinciding lines Coordinate firstLineStart = new Coordinate(1, 1); Coordinate firstLineEnd = new Coordinate(4, 1); Coordinate secondLineStart = new Coordinate(1, 1); Coordinate secondLineEnd = new Coordinate(4, 1); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // parallel lines firstLineStart = new Coordinate(1.3, 1.3); firstLineEnd = new Coordinate(1.3, 4.3); secondLineStart = new Coordinate(2.3, 1.3); secondLineEnd = new Coordinate(2.3, 4.3); Assert.IsFalse(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // intersecting lines firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(1, 4); secondLineStart = new Coordinate(4, 2); secondLineEnd = new Coordinate(0, 2); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(4, 4); secondLineStart = new Coordinate(1, 4); secondLineEnd = new Coordinate(4, 1); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(2, 5); secondLineEnd = new Coordinate(2, 10); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(2, 1); secondLineEnd = new Coordinate(2, 8); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(7, 7); firstLineEnd = new Coordinate(7, 7); secondLineStart = new Coordinate(7, 7); secondLineEnd = new Coordinate(7, 7); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // not intersecting lines firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(2.1, 1); secondLineEnd = new Coordinate(10, 8); Assert.IsFalse(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(1, 8.001); secondLineEnd = new Coordinate(10, 8.001); Assert.IsFalse(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // internally intersecting lines firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(3, 2); secondLineEnd = new Coordinate(2, 3); Assert.IsTrue(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(3, 2); secondLineEnd = new Coordinate(3, 2); Assert.IsTrue(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(1, 2); secondLineEnd = new Coordinate(200, 3); Assert.IsTrue(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(1, 2); secondLineEnd = new Coordinate(200, 3); Assert.IsTrue(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // not internally intersecting lines firstLineStart = new Coordinate(0, 0); firstLineEnd = new Coordinate(0, 1000.33); secondLineStart = new Coordinate(1000.33, 200); secondLineEnd = new Coordinate(0, 0); Assert.IsFalse(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(100, 1); secondLineStart = new Coordinate(1, 4); secondLineEnd = new Coordinate(1, 1); Assert.IsFalse(LineAlgorithms.InternalIntersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // intersecting infinite lines firstLineStart = new Coordinate(2, 5); CoordinateVector firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 4); CoordinateVector secondVector = new CoordinateVector(4, 0); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(2, 4); firstVector = new CoordinateVector(-3, 1); secondLineStart = new Coordinate(5, 3); secondVector = new CoordinateVector(-3, 1); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(1, 1); secondVector = new CoordinateVector(1, 0); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 2); secondVector = new CoordinateVector(0, 1); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); }
/// <summary> /// Decomposes a concave polygon into a set of convex polygons. /// </summary> /// <param name="polygon"></param> /// <returns></returns> public IReadOnlyList <Polygon> BuildConvexDecomposition(Polygon polygon) { //We force it to CCW as it is a precondition in this algorithm. polygon = ForceCounterClockWise(polygon); List <Polygon> list = new List <Polygon>(); // YOGESH : Convex Partition can not happen if there are less than 3, ie 2,1 vertices if (polygon.Count < 3) { return(list); } double d = 0.0; double lowerDist, upperDist; Microsoft.Xna.Framework.Vector2 p; Microsoft.Xna.Framework.Vector2 lowerInt = new Microsoft.Xna.Framework.Vector2(); Microsoft.Xna.Framework.Vector2 upperInt = new Microsoft.Xna.Framework.Vector2(); // intersection points int lowerIndex = 0, upperIndex = 0; Polygon lowerPoly, upperPoly; // Go thru all Verices until we find a reflex vertex i. // Extend the edges incident at i until they hit an edge // Find BEST vertex within the range, that the partitioning chord // A polygon can be broken into convex regions by eliminating all reflex vertices // Eliminating two reflex vertices with one diagonal is better than eliminating just one // A reflex vertex can only be removed if the diagonal connecting to it is within the range given by extending its neighbouring edges; // otherwise, its angle is only reduced for (int i = 0; i < polygon.Count; i++) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 on = polygon.At(i); Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); if (IsReflex(prev, on, next)) { lowerDist = double.MaxValue; upperDist = double.MaxValue; for (int j = 0; j < polygon.Count; j++) { // YOGESH: if any of j line's endpoints matches with reflex i, skip if ((i == j) || (i == NormalizeIndex(j - 1, polygon.Count)) || (i == NormalizeIndex(j + 1, polygon.Count))) { continue; // no self and prev and next, for testing } // testing incoming edge: // if line coming into i vertex (i-1 to i) has j vertex of the test-line on left // AND have j-i on right, then they will be intersecting Microsoft.Xna.Framework.Vector2 iPrev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 iSelf = polygon.At(i); Microsoft.Xna.Framework.Vector2 jSelf = polygon.At(j); Microsoft.Xna.Framework.Vector2 jPrev = polygon.At(j - 1); bool leftOK = Left(iPrev, iSelf, jSelf); bool rightOK = Right(iPrev, iSelf, jPrev); bool leftOnOK = LineAlgorithms.AreCollinear(iPrev, iSelf, jSelf); // YOGESH: cached into variables for better debugging bool rightOnOK = LineAlgorithms.AreCollinear(iPrev, iSelf, jPrev); // YOGESH: cached into variables for better debugging if (leftOnOK || rightOnOK) // YOGESH: Checked "ON" condition as well, collinearity { // lines are colinear, they can not be overlapping as polygon is simple // find closest point which is not internal to incoming line i , i -1 d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jSelf); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = jSelf; lowerIndex = j - 1; } d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jPrev); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = jPrev; lowerIndex = j; } } else if (leftOK && rightOK) // YOGESH: Intersection in-between. Bayazit had ON condition in built here, which I have taken care above. { // find the point of intersection var intersection = LineAlgorithms.LineSegmentIntersection( polygon.At(i - 1), polygon.At(i), polygon.At(j), polygon.At(j - 1)) ?.IntersectionPoint; if (intersection != null) { p = intersection.Value; // make sure it's inside the poly, if (Right(polygon.At(i + 1), polygon.At(i), p)) { d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), p); // this lower* is the point got from incoming edge into the i vertex, // lowerInt incoming edge intersection point // lowerIndex incoming edge intersection edge if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } } // testing outgoing edge: // if line outgoing from i vertex (i to i+1) has j vertex of the test-line on right // AND has j+1 on left, they they will be intersecting Microsoft.Xna.Framework.Vector2 iNext = polygon.At(i + 1); Microsoft.Xna.Framework.Vector2 jNext = polygon.At(j + 1); bool leftOKn = Left(iNext, iSelf, jNext); bool rightOKn = Right(iNext, iSelf, jSelf); bool leftOnOKn = LineAlgorithms.AreCollinear(iNext, iSelf, jNext); // YOGESH: cached into variables for better debugging bool rightOnOKn = LineAlgorithms.AreCollinear(iNext, iSelf, jSelf); if (leftOnOKn || rightOnOKn) // YOGESH: Checked "ON" condition as well, collinearity { // lines are colinear, they can not be overlapping as polygon is simple // find closest point which is not internal to incoming line i , i -1 d = Microsoft.Xna.Framework.Vector2.DistanceSquared(iSelf, jNext); // this upper* is the point got from outgoing edge into the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { // keep only the closest intersection upperDist = d; upperInt = jNext; upperIndex = j + 1; } d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), polygon.At(j)); // this upper* is the point got from outgoing edge into the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { // keep only the closest intersection upperDist = d; upperInt = jSelf; upperIndex = j; } } else if (leftOKn && rightOKn) // YOGESH: Intersection in-between. Bayazit had ON condition in built here, which I have taken care above. { var intersection = LineAlgorithms.LineSegmentIntersection( polygon.At(i + 1), polygon.At(i), polygon.At(j), polygon.At(j + 1)) ?.IntersectionPoint; if (intersection != null) { p = intersection.Value; if (Left(polygon.At(i - 1), polygon.At(i), p)) { d = Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), p); // this upper* is the point got from outgoing edge from the i vertex, // upperInt outgoing edge intersection point // upperIndex outgoing edge intersection edge if (d < upperDist) { upperDist = d; upperIndex = j; upperInt = p; } } } } } // YOGESH: If no vertices in the range, lets not choose midpoint but closet point of that segment //// if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % polygon.Count) { Microsoft.Xna.Framework.Vector2 sp = ((lowerInt + upperInt) / 2); lowerPoly = polygon.CopyRange(i, upperIndex); lowerPoly.Add(sp); upperPoly = polygon.CopyRange(lowerIndex, i); upperPoly.Add(sp); } else { //find vertex to connect to double highestScore = 0, bestIndex = lowerIndex; while (upperIndex < lowerIndex) { upperIndex += polygon.Count; } // go throuh all the vertices between the range of lower and upper for (int j = lowerIndex; j <= upperIndex; ++j) { if (CanSee(polygon, i, j)) { double score = 1 / (Microsoft.Xna.Framework.Vector2.DistanceSquared(polygon.At(i), polygon.At(j)) + 1); // if another vertex is Reflex, choosing it has highest score Microsoft.Xna.Framework.Vector2 prevj = polygon.At(j - 1); Microsoft.Xna.Framework.Vector2 onj = polygon.At(j); Microsoft.Xna.Framework.Vector2 nextj = polygon.At(j + 1); if (IsReflex(prevj, onj, nextj)) { if (RightOrOn(polygon.At(j - 1), polygon.At(j), polygon.At(i)) && LeftOrOn(polygon.At(j + 1), polygon.At(j), polygon.At(i))) { score += 3; } else { score += 2; } } else { score += 1; } if (score > highestScore) { bestIndex = j; highestScore = score; } } } // YOGESH : Pending: if there are 2 vertices as 'bestIndex', its better to disregard both and put midpoint (M case) lowerPoly = polygon.CopyRange(i, (int)bestIndex); upperPoly = polygon.CopyRange((int)bestIndex, i); } // solve smallest poly first (SAW in Bayazit's C++ code) if (lowerPoly.Count < upperPoly.Count) { list.AddRange(BuildConvexDecomposition(lowerPoly)); list.AddRange(BuildConvexDecomposition(upperPoly)); } else { list.AddRange(BuildConvexDecomposition(upperPoly)); list.AddRange(BuildConvexDecomposition(lowerPoly)); } return(list); } } // polygon is already convex if (polygon.Count > MaxPolygonVertices) { lowerPoly = polygon.CopyRange(0, polygon.Count / 2); upperPoly = polygon.CopyRange(polygon.Count / 2, 0); list.AddRange(BuildConvexDecomposition(lowerPoly)); list.AddRange(BuildConvexDecomposition(upperPoly)); } else { list.Add(polygon); } // The polygons are not guaranteed to be without collinear points. We remove // them to be sure. for (int i = 0; i < list.Count; i++) { list[i] = RemoveCollinearPoints(list[i]); } return(list); }
public void LineAlgorithmsIntersectionTest() { // coinciding lines Coordinate firstLineStart = new Coordinate(1, 1); Coordinate firstLineEnd = new Coordinate(4, 1); Coordinate secondLineStart = new Coordinate(1, 1); Coordinate secondLineEnd = new Coordinate(4, 1); IList <Coordinate> expectedResult = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(4, 1) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // lines intersecting in one point firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(1, 4); secondLineStart = new Coordinate(4, 2); secondLineEnd = new Coordinate(0, 2); expectedResult = new List <Coordinate> { new Coordinate(1, 2) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(-1, 6); firstLineEnd = new Coordinate(1, 2); secondLineStart = new Coordinate(4, 0); secondLineEnd = new Coordinate(0, 4); expectedResult = new List <Coordinate> { new Coordinate(0, 4) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 2); secondLineStart = new Coordinate(2, 2); secondLineEnd = new Coordinate(2, 2); expectedResult = new List <Coordinate> { new Coordinate(2, 2) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // lines intersecting in more than one point firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(2, 5); secondLineEnd = new Coordinate(2, 10); expectedResult = new List <Coordinate> { new Coordinate(2, 5), new Coordinate(2, 8) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(4, 1); firstLineEnd = new Coordinate(1, 4); secondLineStart = new Coordinate(3, 2); secondLineEnd = new Coordinate(2, 3); expectedResult = new List <Coordinate> { new Coordinate(3, 2), new Coordinate(2, 3) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // parallel lines firstLineStart = new Coordinate(1.3, 1.3); firstLineEnd = new Coordinate(1.3, 4.3); secondLineStart = new Coordinate(2.3, 1.3); secondLineEnd = new Coordinate(2.3, 4.3); expectedResult = new List <Coordinate> { }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // not intersecting lines firstLineStart = new Coordinate(2, 2); firstLineEnd = new Coordinate(2, 8); secondLineStart = new Coordinate(2.1, 1); secondLineEnd = new Coordinate(10, 8); expectedResult = new List <Coordinate> { }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // coinciding infinite lines firstLineStart = new Coordinate(2, 4); CoordinateVector firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 5); CoordinateVector secondVector = new CoordinateVector(0, 4); Assert.AreEqual(new Coordinate(2, 4), LineAlgorithms.Intersection(firstLineStart, firstVector, secondLineStart, secondVector)); // intersecting infinite lines firstLineStart = new Coordinate(1, 2); firstVector = new CoordinateVector(1, -2); secondLineStart = new Coordinate(4, 0); secondVector = new CoordinateVector(1, -1); Assert.AreEqual(new Coordinate(0, 4), LineAlgorithms.Intersection(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(1, 1); secondVector = new CoordinateVector(1, 0); Assert.AreEqual(new Coordinate(1, 1), LineAlgorithms.Intersection(firstLineStart, firstVector, secondLineStart, secondVector)); // not intersecting infinite lines firstLineStart = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 2); secondVector = new CoordinateVector(0, 1); Assert.IsFalse(LineAlgorithms.Intersection(firstLineStart, firstVector, secondLineStart, secondVector).IsValid); // internally intersecting lines firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(3, 2); secondLineEnd = new Coordinate(2, 3); expectedResult = new List <Coordinate> { new Coordinate(3, 2), new Coordinate(2, 3) }; Assert.AreEqual(expectedResult, LineAlgorithms.InternalIntersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(1, 2); secondLineEnd = new Coordinate(200, 2); expectedResult = new List <Coordinate> { new Coordinate(3, 2) }; Assert.AreEqual(expectedResult, LineAlgorithms.InternalIntersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(10.58, 4); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(10.58, 4); secondLineEnd = new Coordinate(4, 1); expectedResult = new List <Coordinate> { new Coordinate(10.58, 4), new Coordinate(4, 1) }; Assert.AreEqual(expectedResult, LineAlgorithms.InternalIntersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // not internally intersecting lines firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(10, 1); secondLineStart = new Coordinate(1, 4); secondLineEnd = new Coordinate(1, 1); expectedResult = new List <Coordinate> { }; Assert.AreEqual(expectedResult, LineAlgorithms.InternalIntersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // intersecting lines with fixed precision PrecisionModel precision = new PrecisionModel(0.001); firstLineStart = new Coordinate(1000, 1000); firstLineEnd = new Coordinate(1000, 4000); secondLineStart = new Coordinate(4000, 2000); secondLineEnd = new Coordinate(-2000, 1000); expectedResult = new List <Coordinate> { new Coordinate(1000, 2000) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd, precision)); secondLineEnd = new Coordinate(-2000, 0); expectedResult = new List <Coordinate> { new Coordinate(1000, 1000) }; Assert.AreEqual(expectedResult, LineAlgorithms.Intersection(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd, precision)); }
/// <summary> /// Returns true if vertex j can be seen from vertex i without any obstructions. /// </summary> /// <param name="polygon"></param> /// <param name="i"></param> /// <param name="j"></param> /// <returns></returns> private bool CanSee(Polygon polygon, int i, int j) { Microsoft.Xna.Framework.Vector2 prev = polygon.At(i - 1); Microsoft.Xna.Framework.Vector2 on = polygon.At(i); Microsoft.Xna.Framework.Vector2 next = polygon.At(i + 1); if (IsReflex(prev, on, next)) { if (LeftOrOn(polygon.At(i), polygon.At(i - 1), polygon.At(j)) && RightOrOn(polygon.At(i), polygon.At(i + 1), polygon.At(j))) { return(false); } } else { if (RightOrOn(polygon.At(i), polygon.At(i + 1), polygon.At(j)) || LeftOrOn(polygon.At(i), polygon.At(i - 1), polygon.At(j))) { return(false); } } Microsoft.Xna.Framework.Vector2 prevj = polygon.At(j - 1); Microsoft.Xna.Framework.Vector2 onj = polygon.At(j); Microsoft.Xna.Framework.Vector2 nextj = polygon.At(j + 1); if (IsReflex(prevj, onj, nextj)) { if (LeftOrOn(polygon.At(j), polygon.At(j - 1), polygon.At(i)) && RightOrOn(polygon.At(j), polygon.At(j + 1), polygon.At(i))) { return(false); } } else { if (RightOrOn(polygon.At(j), polygon.At(j + 1), polygon.At(i)) || LeftOrOn(polygon.At(j), polygon.At(j - 1), polygon.At(i))) { return(false); } } for (int k = 0; k < polygon.Count; ++k) { // YOGESH : changed from Line-Line intersection to Segment-Segment Intersection Microsoft.Xna.Framework.Vector2 p1 = polygon.At(i); Microsoft.Xna.Framework.Vector2 p2 = polygon.At(j); Microsoft.Xna.Framework.Vector2 q1 = polygon.At(k); Microsoft.Xna.Framework.Vector2 q2 = polygon.At(k + 1); // ignore incident edges if (p1.Equals(q1) || p1.Equals(q2) || p2.Equals(q1) || p2.Equals(q2)) { continue; } var intersection = LineAlgorithms.LineSegmentIntersection(p1, p2, q1, q2); if (intersection != null && intersection.WithinFirstSegment && intersection.WithinSecondSegment) { Microsoft.Xna.Framework.Vector2 intPoint = intersection.IntersectionPoint; // intPoint is not any of the j line then false, else continue. Intersection has to be interior to qualify s 'false' from here if ((!intPoint.Equals(polygon.At(k))) || (!intPoint.Equals(polygon.At(k + 1)))) { return(false); } } } return(true); }
public void LineAlgorithmsIsCollinearTest() { // collinear lines Coordinate firstLineStart = new Coordinate(1, 1); Coordinate firstLineEnd = new Coordinate(4, 1); Coordinate secondLineStart = new Coordinate(1, 1); Coordinate secondLineEnd = new Coordinate(4, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(1, 1); secondLineEnd = new Coordinate(15, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(16, 1); secondLineStart = new Coordinate(15, 1); secondLineEnd = new Coordinate(2, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(15, 1); secondLineEnd = new Coordinate(1, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(4, 1); secondLineStart = new Coordinate(4, 1); secondLineEnd = new Coordinate(-5, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(20, 1); firstLineEnd = new Coordinate(250, 1); secondLineEnd = new Coordinate(1000, 1); secondLineStart = new Coordinate(250, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(20, 1); firstLineEnd = new Coordinate(250, 1); secondLineStart = new Coordinate(259, 1); secondLineEnd = new Coordinate(1000, 1); Assert.IsTrue(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // not collinear lines firstLineStart = new Coordinate(20, 1); firstLineEnd = new Coordinate(250, 1); secondLineStart = new Coordinate(1, 100); secondLineEnd = new Coordinate(1, 1200); Assert.IsFalse(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(20, 1); firstLineEnd = new Coordinate(250, 1); secondLineStart = new Coordinate(250, 1); secondLineEnd = new Coordinate(1000, 2); Assert.IsFalse(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1.2, 1.2); firstLineEnd = new Coordinate(1.2, 7.2); secondLineStart = new Coordinate(2.2, 8.2); secondLineEnd = new Coordinate(2.2, 15.2); Assert.IsFalse(LineAlgorithms.IsCollinear(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); }
public void LineAlgorithmsIsParallelTest() { // parallel lines Coordinate firstLineStart = new Coordinate(1, 1); Coordinate firstLineEnd = new Coordinate(4, 1); Coordinate secondLineStart = new Coordinate(2, 2); Coordinate secondLineEnd = new Coordinate(4, 2); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); Assert.IsFalse(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1.2, 1.2); firstLineEnd = new Coordinate(1.2, 7.2); secondLineStart = new Coordinate(2.2, 8.2); secondLineEnd = new Coordinate(2.2, 15.2); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); Assert.IsFalse(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(1, 7); secondLineStart = new Coordinate(1, 1); secondLineEnd = new Coordinate(1, 7); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // non parallel lines firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(1, 7); secondLineStart = new Coordinate(2, 1); secondLineEnd = new Coordinate(2.2, 8); Assert.IsFalse(LineAlgorithms.IsParallel(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); firstLineStart = new Coordinate(1, 1); firstLineEnd = new Coordinate(1, 7); secondLineStart = new Coordinate(0, 2); secondLineEnd = new Coordinate(10, 2); Assert.IsFalse(LineAlgorithms.IsParallel(firstLineStart, firstLineEnd, secondLineStart, secondLineEnd)); // parallel infinite lines firstLineStart = new Coordinate(1, 1); CoordinateVector firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 2); CoordinateVector secondVector = new CoordinateVector(0, 1); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(2, 5); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 4); secondVector = new CoordinateVector(0, 4); Assert.IsTrue(LineAlgorithms.Coincides(firstLineStart, firstVector, secondLineStart, secondVector)); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(2, 4); firstVector = new CoordinateVector(-3, 1); secondLineStart = new Coordinate(5, 3); secondVector = new CoordinateVector(-3, 1); Assert.IsTrue(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); // non parallel infinite lines firstLineStart = new Coordinate(1, 1); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(1, 1); secondVector = new CoordinateVector(1, 0); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); Assert.IsFalse(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); firstLineStart = new Coordinate(2, 5); firstVector = new CoordinateVector(0, 1); secondLineStart = new Coordinate(2, 4); secondVector = new CoordinateVector(4, 0); Assert.IsTrue(LineAlgorithms.Intersects(firstLineStart, firstVector, secondLineStart, secondVector)); Assert.IsFalse(LineAlgorithms.IsParallel(firstLineStart, firstVector, secondLineStart, secondVector)); }
/// <summary> /// Computes the intersection of one or more line strings. /// </summary> public void Compute() { // source: http://geomalgorithms.com/a09-_intersect-3.html _intersections = new List <Coordinate>(); _edgeIndices = new List <Tuple <Int32, Int32> >(); Event currentEvent = _eventQueue.Next(); SweepLineSegment segment; IList <Coordinate> intersections; while (currentEvent != null) { if (currentEvent is EndPointEvent) { EndPointEvent endPointEvent = (EndPointEvent)currentEvent; switch (endPointEvent.Type) { // Left endpoint event: check for possible intersection with below and / or above segments. case EventType.Left: segment = _sweepLine.Add(endPointEvent); if (segment.Above != null) { intersections = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Above.LeftCoordinate, segment.Above.RightCoordinate, PrecisionModel); if (intersections.Count > 0) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersections[0], Below = segment, Above = segment.Above }; _eventQueue.Add(intersectionEvent); } } if (segment.Below != null) { intersections = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, PrecisionModel); if (intersections.Count > 0) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersections[0], Below = segment.Below, Above = segment }; _eventQueue.Add(intersectionEvent); } } break; // Right endpoint event: check for possible intersection of the below and above segments. case EventType.Right: segment = _sweepLine.Search(endPointEvent); if (segment != null) { if (segment.Above != null && segment.Below != null) { intersections = LineAlgorithms.Intersection(segment.Above.LeftCoordinate, segment.Above.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, PrecisionModel); if (intersections.Count > 0) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersections[0], Below = segment.Below, Above = segment.Above }; if (!_eventQueue.Contains(intersectionEvent)) { _eventQueue.Add(intersectionEvent); } } } _sweepLine.Remove(segment); } break; } } // Intersection point event: switch the two concerned segments and check for possible intersection with their below and above segments. else if (currentEvent is IntersectionEvent) { IntersectionEvent intersectionEvent = (IntersectionEvent)currentEvent; /* * Segment order before intersection: segmentBelow <-> segmentAbove <-> segment <-> segmentAboveAbove * Segment order after intersection: segmentBelow <-> segment <-> segmentAbove <-> segmentAboveAbove */ segment = intersectionEvent.Above; SweepLineSegment segmentAbove = intersectionEvent.Below; // Handle closing intersection points when segments (partially) overlap each other. if (intersectionEvent.IsClose) { if (!_sweepLine.IsAdjacent(segment.Edge, segmentAbove.Edge)) { _intersections.Add(currentEvent.Vertex); _edgeIndices.Add(Tuple.Create(Math.Min(segment.Edge, segmentAbove.Edge), Math.Max(segment.Edge, segmentAbove.Edge))); } } // It is possible that the previously detected intersection point is not a real intersection, because a new segment started between them, // therefore a repeated check is necessary to carry out. else if (_sweepLine.Add(intersectionEvent)) { if (!_sweepLine.IsAdjacent(segment.Edge, segmentAbove.Edge)) { _intersections.Add(currentEvent.Vertex); _edgeIndices.Add(Tuple.Create(Math.Min(segment.Edge, segmentAbove.Edge), Math.Max(segment.Edge, segmentAbove.Edge))); intersections = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segmentAbove.LeftCoordinate, segmentAbove.RightCoordinate, PrecisionModel); if (intersections.Count > 1 && !intersections[1].Equals(intersections[0])) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersections[1], Below = segment, Above = segmentAbove, IsClose = true, }; _eventQueue.Add(newIntersectionEvent); } } if (segmentAbove.Above != null) { intersections = LineAlgorithms.Intersection(segmentAbove.LeftCoordinate, segmentAbove.RightCoordinate, segmentAbove.Above.LeftCoordinate, segmentAbove.Above.RightCoordinate, PrecisionModel); if (intersections.Count > 0 && intersections[0].X >= intersectionEvent.Vertex.X) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersections[0], Below = segmentAbove, Above = segmentAbove.Above }; if (!_eventQueue.Contains(newIntersectionEvent)) { _eventQueue.Add(newIntersectionEvent); } } } if (segment.Below != null) { intersections = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, PrecisionModel); if (intersections.Count > 0 && intersections[0].X >= intersectionEvent.Vertex.X) { IntersectionEvent newIntersectionEvent = new IntersectionEvent { Vertex = intersections[0], Below = segment.Below, Above = segment }; if (!_eventQueue.Contains(newIntersectionEvent)) { _eventQueue.Add(newIntersectionEvent); } } } } } currentEvent = _eventQueue.Next(); } _hasResult = true; }
/// <summary> /// Processes the end point event. /// </summary> /// <param name="endPointEvent">The end point event.</param> private void ProcessEndPointEvent(EndPointEvent endPointEvent) { SweepLineSegment segment; Intersection intersection; switch (endPointEvent.Type) { // left endpoint event: check for possible intersection with below and / or above segments case EventType.Left: segment = this.sweepLine.Add(endPointEvent); if (segment.Above != null) { intersection = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Above.LeftCoordinate, segment.Above.RightCoordinate, this.PrecisionModel); if (intersection != null) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersection.Coordinate, Below = segment, Above = segment.Above }; this.eventQueue.Add(intersectionEvent); } } if (segment.Below != null) { intersection = LineAlgorithms.Intersection(segment.LeftCoordinate, segment.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, this.PrecisionModel); if (intersection != null) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersection.Coordinate, Below = segment.Below, Above = segment }; this.eventQueue.Add(intersectionEvent); } } break; // right endpoint event: check for possible intersection of the below and above segments case EventType.Right: segment = this.sweepLine.Search(endPointEvent); if (segment != null) { if (segment.Above != null && segment.Below != null) { intersection = LineAlgorithms.Intersection(segment.Above.LeftCoordinate, segment.Above.RightCoordinate, segment.Below.LeftCoordinate, segment.Below.RightCoordinate, this.PrecisionModel); if (intersection != null) { IntersectionEvent intersectionEvent = new IntersectionEvent { Vertex = intersection.Coordinate, Below = segment.Below, Above = segment.Above }; if (!this.eventQueue.Contains(intersectionEvent)) { this.eventQueue.Add(intersectionEvent); } } } this.sweepLine.Remove(segment); } break; } }