#if ACAD24 using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Colors; using AcAp = Autodesk.AutoCAD.ApplicationServices; #elif BCAD using Bricscad.EditorInput; using Teigha.Geometry; using Teigha.DatabaseServices; using Teigha.Colors; #endif using System; using System.Collections.Generic; namespace Boprs { /// /// Geometric region used for boolean operations calculations. /// public class Region { /// /// Edges belonging to this polygon. /// private List Edges { get; set; } private List Contours { get; set; } /// /// Subdivide my edges, set their inOut tags and validate their non-colinearity. /// private void SetEdges(List vertices) { // Lexico-graphically sort my vertices. vertices.Sort(); // Sweepline to keep track of edges. SweepLine sweepLine = new SweepLine(); // Processed edges will be moved here. List processed = new List(); // Go through all the sorted vertices, test for intersections when appropriate and, // if needed, subdivide the edges. while (vertices.Count != 0) { SweepVertex acVx = vertices[0]; vertices.RemoveAt(0); if (acVx.IsLeft()) { sweepLine.Add(acVx.Edge, true); SweepEdge prevEdge = sweepLine.PrevEdge(acVx.Edge); SweepEdge nextEdge = sweepLine.NextEdge(acVx.Edge); acVx.Edge.SetTransitionInside(prevEdge); if (prevEdge != null) { List newVertices = acVx.Edge.TrySubdivideBy(prevEdge); foreach (SweepVertex vertex in newVertices) { Utils.InsertVertexSorted(vertices, vertex); } } if (nextEdge != null) { List newVertices = acVx.Edge.TrySubdivideBy(nextEdge); foreach (SweepVertex vertex in newVertices) { Utils.InsertVertexSorted(vertices, vertex); } } } else { SweepEdge prevEdge = sweepLine.PrevEdge(acVx.Edge); SweepEdge nextEdge = sweepLine.NextEdge(acVx.Edge); sweepLine.Remove(acVx.Edge); processed.Add(acVx.Edge); if (prevEdge != null && nextEdge != null) { List newVertices = prevEdge.TrySubdivideBy(nextEdge); foreach (SweepVertex vertex in newVertices) { Utils.InsertVertexSorted(vertices, vertex); } } } } Edges = processed; } /// /// Find the index of an un-processed vertex with the same position as the one on specified index. /// /// Index of the current vertex. /// List of vertices. /// Mask for processed vertices. /// Index of an unprocessed neighbour with the same position. private int NextPos(int pos, List vertices, bool[] processed) { int newPos = pos + 1; while(newPos < vertices.Count && Utils.DoubleEquals(vertices[pos].Point.GetDistanceTo(vertices[newPos].Point), 0)) { if (!processed[newPos]) { return newPos; } else { newPos++; } } newPos = pos - 1; while (newPos >= 0 && Utils.DoubleEquals(vertices[pos].Point.GetDistanceTo(vertices[newPos].Point), 0)) { if (!processed[newPos]) { return newPos; } else { newPos--; } } throw new Exception("Hit contour dead end (non-connected edge)."); } /// /// Compute my closed contours. /// private void ComputeContours() { // Gather all vertices of the region. List vertices = new List(); foreach (SweepEdge edge in Edges) { vertices.Add(edge.LeftVertex); vertices.Add(edge.RightVertex); } // Lexico-graphically sort the vertices. vertices.Sort(); bool[] processed = new bool[vertices.Count]; List contours = new List(); for(int i = 0; i < vertices.Count; i++) { // If we already traversed this vertex, skip it. if (processed[i]) { continue; } // Otherwise this is an edge on an indiscovered contour, create it. Contour contour = new Contour(); contours.Add(contour); // acVx should be guaranteed to be the lowest and leftest vertex of the new polygon. // Thus it cannot be vertical. // if it is transition inside, it is a positive contour and should wind counter-clockwise, // if it is transition outside, it is a negative contour and should wind clockwise. SweepVertex acVx = vertices[i]; bool shouldBeCW = !acVx.Edge.TransitionInside; // Traverse connected edges, and mark taversed vertices as processed, // until we loop back to the first discovered vertex on the contour. int pos = i; processed[i] = true; Point2d target = acVx.Point; contour.Vertices.Add(vertices[pos].Point); acVx.Edge.ParentContour = contour; while (!Utils.DoubleEquals(vertices[pos].OtherVertex().Point.GetDistanceTo(target), 0)) { int otherPos = vertices.IndexOf(vertices[pos].OtherVertex()); pos = NextPos(otherPos, vertices, processed); processed[pos] = true; processed[otherPos] = true; vertices[pos].Edge.ParentContour = contour; contour.Vertices.Add(vertices[pos].Point); } processed[vertices.IndexOf(vertices[pos].OtherVertex())] = true; // If the contour is a hole and winds ccw, or positive and winds cw, flip it. if (shouldBeCW != contour.IsClockwise()) { contour.Reverse(); } } Contours = contours; } /// /// Get a copy of the regions edges list. /// /// Copy of the regions edges list. internal List GetEdgeCopies() { List copies = new List(); foreach (SweepEdge edge in Edges) { copies.Add((SweepEdge)edge.Clone()); } return copies; } /// /// Get a copy of the contours of the polygon. /// /// List of copies of the regions contours. public List GetContourCopies() { List copies = new List(); foreach (Contour contour in Contours) { copies.Add((Contour)contour.Clone()); } return copies; } /// /// Instantiate a region with edges defined by a list of given line segments. /// /// /// public static Region FromSegments(List segments) { Region region = new Region(); List vertices = new List(); foreach(LineSegment2d segment in segments) { SweepEdge newEdge = new SweepEdge(segment.StartPoint, segment.EndPoint); vertices.Add(newEdge.LeftVertex); vertices.Add(newEdge.RightVertex); newEdge.ParentRegion = region; } region.SetEdges(vertices); region.ComputeContours(); return region; } } }