/// <summary> /// Orders the vertices of the polygon in the x-direction. /// </summary> /// <param name="polygon">The polygon.</param> /// <returns>Vertex2D[].</returns> public static Vertex2D[] SortVerticesByYValue(this IEnumerable <Polygon> polygons) { var yStrands = new List <Vertex2D[]>(); var numVertices = 0; foreach (var polygon in polygons) { numVertices += polygon.Vertices.Count; foreach (var monoBox in polygon.PartitionIntoMonotoneBoxes(MonotonicityChange.Y)) { var newStrand = new List <Vertex2D>(); var vertex = monoBox.Vertex1; while (vertex != monoBox.Vertex2) { newStrand.Add(vertex); vertex = vertex.StartLine.ToPoint; } if (!monoBox.YInPositiveMonotonicity) { newStrand.Reverse(); } yStrands.Add(newStrand.ToArray()); } } var sortedVertices = new Vertex2D[numVertices]; var i = 0; foreach (var vertex in CombineYSortedVerticesIntoOneCollection(yStrands)) { sortedVertices[i++] = vertex; } return(sortedVertices); }
/// <summary> /// Orders the vertices of the polygon in the x-direction. /// </summary> /// <param name="polygon">The polygon.</param> /// <returns>Vertex2D[].</returns> internal static Vertex2D[] SortVerticesByXValue(this Polygon polygon) { return(polygon.Vertices.OrderBy(v => v, new VertexSortedByXFirst()).ToArray()); var xStrands = new List <Vertex2D[]>(); foreach (var monoBox in polygon.PartitionIntoMonotoneBoxes(MonotonicityChange.X)) { var newStrand = new List <Vertex2D>(); var vertex = monoBox.Vertex1; while (vertex != monoBox.Vertex2) { newStrand.Add(vertex); vertex = vertex.StartLine.ToPoint; } if (!monoBox.XInPositiveMonotonicity) { newStrand.Reverse(); } xStrands.Add(newStrand.ToArray()); } var numVertices = polygon.Vertices.Count; var sortedVertices = new Vertex2D[numVertices]; var i = 0; foreach (var vertex in CombineXSortedVerticesIntoOneCollection(xStrands)) { sortedVertices[i++] = vertex; } return(sortedVertices); }
/// <summary> /// Gets the other point that makes up this line. /// </summary> /// <param name="point"></param> /// <returns></returns> public Vertex2D OtherPoint(Vertex2D point) { if (point == FromPoint) { return(ToPoint); } return(point == ToPoint ? FromPoint : null); }
/// <summary> /// Sets to and from nodes as well as slope and intercept of line. /// </summary> /// <param name="fromNode">From node.</param> /// <param name="toNode">To node.</param> internal PolygonEdge(Vertex2D fromNode, Vertex2D toNode) { FromPoint = fromNode; ToPoint = toNode; XMax = (FromPoint.X > ToPoint.X) ? FromPoint.X : ToPoint.X; XMin = (FromPoint.X < ToPoint.X) ? FromPoint.X : ToPoint.X; YMax = (FromPoint.Y > ToPoint.Y) ? FromPoint.Y : ToPoint.Y; YMin = (FromPoint.Y < ToPoint.Y) ? FromPoint.Y : ToPoint.Y; }
/// <summary> /// Makes the vertices. /// </summary> private void MakeVertices() { foreach (var polygon in AllPolygons) { var numPoints = polygon._path.Count; var pointsArray = new Vertex2D[numPoints]; for (int i = 0; i < numPoints; i++) { pointsArray[i] = new Vertex2D(polygon._path[i], i, Index); } polygon._vertices = pointsArray.ToList(); } }
public MonotoneBox(Vertex2D vertex1, Vertex2D vertex2, MonotonicityChange lowMonoChange, MonotonicityChange hiMonoChange, bool xInPositiveMonotonicity, bool yInPositiveMonotonicity) : this() { this.Vertex1 = vertex1; this.Vertex2 = vertex2; this.LowChange = lowMonoChange; this.HiChange = hiMonoChange; XInPositiveMonotonicity = xInPositiveMonotonicity; YInPositiveMonotonicity = yInPositiveMonotonicity; Left = Math.Min(vertex1.X, vertex2.X); Right = Math.Max(vertex1.X, vertex2.X); Bottom = Math.Min(vertex1.Y, vertex2.Y); Top = Math.Max(vertex1.Y, vertex2.Y); }
public static IEnumerable <Polygon> CreateXMonotonePolygons(this Polygon polygon) { polygon.MakePolygonEdgesIfNonExistent(); var connections = FindInternalDiagonalsForMonotone(polygon); foreach (var edge in polygon.Edges) { AddNewConnection(connections, edge.FromPoint, edge.ToPoint); } foreach (var edge in polygon.InnerPolygons.SelectMany(p => p.Edges)) { AddNewConnection(connections, edge.FromPoint, edge.ToPoint); } while (connections.Any()) { var startingConnectionKVP = connections.First(); var start = startingConnectionKVP.Key; var newVertices = new List <Vertex2D> { start }; var current = start; var nextConnections = startingConnectionKVP.Value; Vertex2D next = nextConnections[0]; while (next != start) { newVertices.Add(next); RemoveConnection(connections, current, next); current = next; nextConnections = connections[current]; if (nextConnections.Count == 1) { next = nextConnections[0]; } else { next = ChooseTightestLeftTurn(nextConnections, current, current.Coordinates - newVertices[^ 2].Coordinates);
/// <summary>Gets the monotonicity change.</summary> /// <param name="vertex">The vertex.</param> /// <param name="tolerance">The tolerance.</param> /// <returns>MonotonicityChange.</returns> /// <exception cref="ArgumentException"> /// vertex does not conenct to polygon edges. Be sure to invoke" + /// " MakePolygonEdgesIfNonExistent on parent polygon before this calling this method. - vertex /// </exception> public static MonotonicityChange GetMonotonicityChange(this Vertex2D vertex, double tolerance) { if (vertex.EndLine == null) { throw new ArgumentException("vertex does not connect to polygon edges. Be sure to invoke" + " MakePolygonEdgesIfNonExistent on parent polygon before this calling this method.", nameof(vertex)); } var xPrev = vertex.EndLine.Vector.X; var yPrev = vertex.EndLine.Vector.Y; if (xPrev.IsNegligible() && yPrev.IsNegligible()) { return(MonotonicityChange.SameAsPrevious); } double xNext, yNext; var neighborVertex = vertex.StartLine.ToPoint; do { xNext = neighborVertex.EndLine.Vector.X; yNext = neighborVertex.EndLine.Vector.Y; neighborVertex = neighborVertex.StartLine.ToPoint; }while (xNext.IsNegligible(tolerance) && yNext.IsNegligible(tolerance)); //at this point one or both of the x&y deltas are non-negligible. /**** I wish this were enough (the next 6 lines) but it leads to problems * var xProduct = xNext * xPrev; * var yProduct = yNext * yPrev; * if (xProduct > 0 && yProduct > 0) return MonotonicityChange.Neither; * if (xProduct < 0 && yProduct < 0) return MonotonicityChange.Both; * if (xProduct > 0) return MonotonicityChange.Y; * return MonotonicityChange.X; ****/ // first check the cases where all four numbers are not negligible var xChangesDir = (xPrev.IsNegativeNonNegligible(tolerance) && xNext.IsPositiveNonNegligible(tolerance)) || (xPrev.IsPositiveNonNegligible(tolerance) && xNext.IsNegativeNonNegligible(tolerance)); var yChangesDir = (yPrev.IsNegativeNonNegligible(tolerance) && yNext.IsPositiveNonNegligible(tolerance)) || (yPrev.IsPositiveNonNegligible(tolerance) && yNext.IsNegativeNonNegligible(tolerance)); if (xChangesDir && yChangesDir) { return(MonotonicityChange.Both); } var xSameDir = (xPrev.IsNegativeNonNegligible(tolerance) && xNext.IsNegativeNonNegligible(tolerance)) || (xPrev.IsPositiveNonNegligible(tolerance) && xNext.IsPositiveNonNegligible(tolerance)); if (yChangesDir && xSameDir) { return(MonotonicityChange.Y); } var ySameDir = (yPrev.IsNegativeNonNegligible(tolerance) && yNext.IsNegativeNonNegligible(tolerance)) || (yPrev.IsPositiveNonNegligible(tolerance) && yNext.IsPositiveNonNegligible(tolerance)); if (xChangesDir && ySameDir) { return(MonotonicityChange.X); } if (xSameDir && ySameDir) { return(MonotonicityChange.Neither); } // if at this point then one or more values in the vectors is zero/negligible since the above booleans were // defined with this restriction if (xPrev.IsNegligible(tolerance) && xNext.IsNegligible(tolerance)) // then line is vertical { return((Math.Sign(yPrev) == Math.Sign(yNext)) ? MonotonicityChange.Neither : MonotonicityChange.Y); } // eww, the latter in that return is problematic at it would be a knife edge. but that's not this functions job to police if (yPrev.IsNegligible(tolerance) && yNext.IsNegligible(tolerance)) // then line is horizontal { return((Math.Sign(xPrev) == Math.Sign(xNext)) ? MonotonicityChange.Neither : MonotonicityChange.X); } // at this point, we've checked that 1) no vector is zero, 2) all are nonnegligible, // 3) either both x's or both y's are negligible. neighborVertex = vertex; if (xPrev.IsNegligible(tolerance)) //we know that yPrev != 0 (given first condition) and we know xNext != 0 (given // the condition before the last). it's possible that yNext is zero, but it doesn't affect the approach { do { neighborVertex = neighborVertex.EndLine.FromPoint; xPrev = neighborVertex.EndLine.Vector.X; } while (xPrev.IsNegligible(tolerance)); xChangesDir = Math.Sign(xPrev) != Math.Sign(xNext); } else if (xNext.IsNegligible(tolerance)) { do { neighborVertex = neighborVertex.StartLine.ToPoint; xNext = neighborVertex.EndLine.Vector.X; } while (xNext.IsNegligible(tolerance)); xChangesDir = Math.Sign(xPrev) != Math.Sign(xNext); } neighborVertex = vertex; if (yPrev.IsNegligible(tolerance)) { do { neighborVertex = neighborVertex.EndLine.FromPoint; yPrev = neighborVertex.EndLine.Vector.Y; } while (yPrev.IsNegligible(tolerance)); yChangesDir = (Math.Sign(yPrev) != Math.Sign(yNext)); } else if (yNext.IsNegligible(tolerance)) { do { neighborVertex = neighborVertex.StartLine.ToPoint; yNext = neighborVertex.EndLine.Vector.Y; } while (yNext.IsNegligible(tolerance)); yChangesDir = Math.Sign(yPrev) != Math.Sign(yNext); } if (xChangesDir && yChangesDir) { return(MonotonicityChange.Both); } if (xChangesDir) { return(MonotonicityChange.X); } if (yChangesDir) { return(MonotonicityChange.Y); } return(MonotonicityChange.Neither); }
/// <summary> /// Partitions the into monotone boxes. /// </summary> /// <param name="polygon">The polygon.</param> /// <param name="divideAt">The divide at.</param> /// <returns>System.Collections.Generic.IEnumerable<TVGL.TwoDimensional.MonotoneBox>.</returns> /// <exception cref="MonotoneBox">polygon.Vertices[0], polygon.Vertices[1], MonotonicityChange.Both, MonotonicityChange.Both, true, true</exception> public static IEnumerable <MonotoneBox> PartitionIntoMonotoneBoxes(this Polygon polygon, MonotonicityChange divideAt = MonotonicityChange.Both) { polygon.MakePolygonEdgesIfNonExistent(); var numPoints = polygon.Vertices.Count; if (numPoints <= 1) { yield return (new MonotoneBox(polygon.Vertices[0], polygon.Vertices[0], MonotonicityChange.Both, MonotonicityChange.Both, true, true)); yield break; } if (numPoints <= 2) { yield return (new MonotoneBox(polygon.Vertices[0], polygon.Vertices[1], MonotonicityChange.Both, MonotonicityChange.Both, true, true)); yield break; } var initBoxIndex = -1; var initBoxMonoChange = MonotonicityChange.Neither; Vertex2D beginBoxVertex = null; var beginBoxMonoChange = MonotonicityChange.Neither; var tolerance = polygon.GetToleranceForPolygon(); var i = 0; while (i % numPoints != initBoxIndex) { var vertex = polygon.Vertices[i % numPoints]; var monoChange = GetMonotonicityChange(vertex, tolerance); if (monoChange == MonotonicityChange.SameAsPrevious || monoChange == MonotonicityChange.Neither) { continue; } if (monoChange == MonotonicityChange.Both || (monoChange == MonotonicityChange.X && (divideAt == MonotonicityChange.X || divideAt == MonotonicityChange.Both)) || (monoChange == MonotonicityChange.Y && (divideAt == MonotonicityChange.Y || divideAt == MonotonicityChange.Both))) { if (initBoxIndex < 0) { initBoxIndex = i; beginBoxVertex = vertex; initBoxMonoChange = beginBoxMonoChange = monoChange; } else { yield return(new MonotoneBox(beginBoxVertex, vertex, beginBoxMonoChange, monoChange, vertex.X >= beginBoxVertex.X, vertex.Y >= beginBoxVertex.Y)); beginBoxVertex = vertex; beginBoxMonoChange = monoChange; } } i++; } var lastVertex = polygon.Vertices[initBoxIndex]; yield return(new MonotoneBox(beginBoxVertex, lastVertex, beginBoxMonoChange, initBoxMonoChange, lastVertex.X - beginBoxVertex.X >= 0, lastVertex.Y - beginBoxVertex.Y >= 0)); }