2024-09-01 13:10:19 +02:00
|
|
|
|
#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
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Geometric region used for boolean operations calculations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class Region
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2024-09-02 10:08:52 +02:00
|
|
|
|
/// Edges belonging to this polygon.
|
2024-09-01 13:10:19 +02:00
|
|
|
|
/// </summary>
|
2024-09-02 10:08:52 +02:00
|
|
|
|
private List<SweepEdge> Edges { get; set; }
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
2024-09-04 09:04:07 +02:00
|
|
|
|
private List<Contour> Contours { get; set; }
|
|
|
|
|
|
2024-09-01 13:10:19 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Subdivide my edges, set their inOut tags and validate their non-colinearity.
|
|
|
|
|
/// </summary>
|
2024-09-02 10:08:52 +02:00
|
|
|
|
private void SetEdges(List<SweepVertex> vertices)
|
2024-09-01 13:10:19 +02:00
|
|
|
|
{
|
|
|
|
|
// Lexico-graphically sort my vertices.
|
2024-09-02 10:08:52 +02:00
|
|
|
|
vertices.Sort();
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
// Sweepline to keep track of edges.
|
|
|
|
|
SweepLine sweepLine = new SweepLine();
|
|
|
|
|
|
2024-09-02 10:08:52 +02:00
|
|
|
|
// Processed edges will be moved here.
|
|
|
|
|
List<SweepEdge> processed = new List<SweepEdge>();
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
// Go through all the sorted vertices, test for intersections when appropriate and,
|
|
|
|
|
// if needed, subdivide the edges.
|
2024-09-02 10:08:52 +02:00
|
|
|
|
while (vertices.Count != 0)
|
2024-09-01 13:10:19 +02:00
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
SweepVertex acVx = vertices[0];
|
|
|
|
|
vertices.RemoveAt(0);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
if (acVx.IsLeft())
|
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
sweepLine.Add(acVx.Edge, true);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
SweepEdge prevEdge = sweepLine.PrevEdge(acVx.Edge);
|
|
|
|
|
SweepEdge nextEdge = sweepLine.NextEdge(acVx.Edge);
|
|
|
|
|
|
|
|
|
|
acVx.Edge.SetTransitionInside(prevEdge);
|
|
|
|
|
|
|
|
|
|
if (prevEdge != null)
|
|
|
|
|
{
|
|
|
|
|
List<SweepVertex> newVertices = acVx.Edge.TrySubdivideBy(prevEdge);
|
|
|
|
|
|
|
|
|
|
foreach (SweepVertex vertex in newVertices)
|
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
Utils.InsertVertexSorted(vertices, vertex);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nextEdge != null)
|
|
|
|
|
{
|
|
|
|
|
List<SweepVertex> newVertices = acVx.Edge.TrySubdivideBy(nextEdge);
|
|
|
|
|
|
|
|
|
|
foreach (SweepVertex vertex in newVertices)
|
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
Utils.InsertVertexSorted(vertices, vertex);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SweepEdge prevEdge = sweepLine.PrevEdge(acVx.Edge);
|
|
|
|
|
SweepEdge nextEdge = sweepLine.NextEdge(acVx.Edge);
|
|
|
|
|
|
|
|
|
|
sweepLine.Remove(acVx.Edge);
|
2024-09-02 10:08:52 +02:00
|
|
|
|
processed.Add(acVx.Edge);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
if (prevEdge != null && nextEdge != null)
|
|
|
|
|
{
|
|
|
|
|
List<SweepVertex> newVertices = prevEdge.TrySubdivideBy(nextEdge);
|
|
|
|
|
|
|
|
|
|
foreach (SweepVertex vertex in newVertices)
|
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
Utils.InsertVertexSorted(vertices, vertex);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-02 10:08:52 +02:00
|
|
|
|
|
|
|
|
|
Edges = processed;
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 09:04:07 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Find the index of an un-processed vertex with the same position as the one on specified index.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pos">Index of the current vertex.</param>
|
|
|
|
|
/// <param name="vertices">List of vertices.</param>
|
|
|
|
|
/// <param name="processed">Mask for processed vertices.</param>
|
|
|
|
|
/// <returns>Index of an unprocessed neighbour with the same position.</returns>
|
|
|
|
|
private int NextPos(int pos, List<SweepVertex> 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).");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compute my closed contours.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ComputeContours()
|
|
|
|
|
{
|
|
|
|
|
// Gather all vertices of the region.
|
|
|
|
|
List<SweepVertex> vertices = new List<SweepVertex>();
|
|
|
|
|
foreach (SweepEdge edge in Edges)
|
|
|
|
|
{
|
|
|
|
|
vertices.Add(edge.LeftVertex);
|
|
|
|
|
vertices.Add(edge.RightVertex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lexico-graphically sort the vertices.
|
|
|
|
|
vertices.Sort();
|
|
|
|
|
|
|
|
|
|
// Sweepline to keep track of edges.
|
|
|
|
|
SweepLine sweepLine = new SweepLine();
|
|
|
|
|
|
|
|
|
|
bool[] processed = new bool[vertices.Count];
|
|
|
|
|
List<Contour> contours = new List<Contour>();
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < vertices.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
SweepVertex acVx = vertices[i];
|
|
|
|
|
|
|
|
|
|
if (acVx.IsLeft())
|
|
|
|
|
{
|
|
|
|
|
sweepLine.Add(acVx.Edge, false);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sweepLine.Remove(acVx.Edge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed[i])
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
processed[i] = true;
|
|
|
|
|
|
|
|
|
|
// We should be guaranteed to always hit left endpoint leading us on a counter clockwise path.
|
|
|
|
|
Contour contour = new Contour();
|
|
|
|
|
contours.Add(contour);
|
|
|
|
|
|
|
|
|
|
Point2d target = acVx.Point;
|
|
|
|
|
|
|
|
|
|
int pos = i;
|
|
|
|
|
while (!Utils.DoubleEquals(vertices[pos].OtherVertex().Point.GetDistanceTo(target), 0))
|
|
|
|
|
{
|
|
|
|
|
int otherPos = vertices.IndexOf(vertices[pos].OtherVertex());
|
|
|
|
|
pos = NextPos(otherPos, vertices, processed);
|
|
|
|
|
|
|
|
|
|
contour.Vertices.Add(vertices[pos].Point);
|
|
|
|
|
processed[pos] = true;
|
|
|
|
|
|
|
|
|
|
processed[otherPos] = true;
|
|
|
|
|
}
|
|
|
|
|
processed[vertices.IndexOf(vertices[pos].OtherVertex())] = true;
|
|
|
|
|
}
|
|
|
|
|
Contours = contours;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-01 13:10:19 +02:00
|
|
|
|
/// <summary>
|
2024-09-02 10:08:52 +02:00
|
|
|
|
/// Get a copy of the regions edges list.
|
2024-09-01 13:10:19 +02:00
|
|
|
|
/// </summary>
|
2024-09-02 10:08:52 +02:00
|
|
|
|
/// <returns>Copy of the regions edges list.</returns>
|
|
|
|
|
internal List<SweepEdge> GetEdgeCopies()
|
2024-09-01 13:10:19 +02:00
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
List<SweepEdge> copies = new List<SweepEdge>();
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
2024-09-02 10:08:52 +02:00
|
|
|
|
foreach (SweepEdge edge in Edges)
|
2024-09-01 13:10:19 +02:00
|
|
|
|
{
|
2024-09-02 10:08:52 +02:00
|
|
|
|
copies.Add((SweepEdge)edge.Clone());
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-02 10:08:52 +02:00
|
|
|
|
return copies;
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 09:04:07 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get a copy of the contours of the polygon.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>List of copies of the regions contours.</returns>
|
|
|
|
|
public List<Contour> GetContourCopies()
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-01 13:10:19 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Instantiate a region with edges defined by a list of given line segments.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="segments"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Region FromSegments(List<LineSegment2d> segments)
|
|
|
|
|
{
|
|
|
|
|
Region region = new Region();
|
|
|
|
|
|
2024-09-02 10:08:52 +02:00
|
|
|
|
List<SweepVertex> vertices = new List<SweepVertex>();
|
|
|
|
|
|
2024-09-01 13:10:19 +02:00
|
|
|
|
foreach(LineSegment2d segment in segments)
|
|
|
|
|
{
|
|
|
|
|
SweepEdge newEdge = new SweepEdge(segment.StartPoint, segment.EndPoint);
|
2024-09-02 10:08:52 +02:00
|
|
|
|
vertices.Add(newEdge.LeftVertex);
|
|
|
|
|
vertices.Add(newEdge.RightVertex);
|
2024-09-01 13:10:19 +02:00
|
|
|
|
newEdge.ParentRegion = region;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-02 10:08:52 +02:00
|
|
|
|
region.SetEdges(vertices);
|
2024-09-04 09:04:07 +02:00
|
|
|
|
region.ComputeContours();
|
2024-09-01 13:10:19 +02:00
|
|
|
|
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
internal void Draw()
|
|
|
|
|
{
|
2024-09-04 09:04:07 +02:00
|
|
|
|
foreach (Contour contour in Contours)
|
2024-09-01 13:10:19 +02:00
|
|
|
|
{
|
2024-09-04 09:04:07 +02:00
|
|
|
|
contour.Draw();
|
2024-09-01 13:10:19 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|