Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use SplitInternalLoops before Polygon is creation (Issue #881) #1043

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
- `IndexedPolycurve.GetSubdivisionParameters` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polyline.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polygon.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- Fix exception in `Polygon.Difference` and `Profile.Difference` when a inner loop shares edges with outer loop.


### Changed
Expand Down
179 changes: 18 additions & 161 deletions Elements/src/Geometry/Polygon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ protected override void ValidateVertices()
}

this.Vertices = Vector3.RemoveSequentialDuplicates(this.Vertices, true);
DeleteVerticesForOverlappingEdges();
Vector3.DeleteVerticesForOverlappingEdges(this.Vertices);
if (this.Vertices.Count < 3)
{
throw new ArgumentException("The polygon could not be created. At least 3 vertices are required.");
Expand Down Expand Up @@ -1494,11 +1494,7 @@ private static IList<Polygon> BooleanTwoSets(IList<Polygon> subjectPolygons, ILi
var polygons = new List<Polygon>();
foreach (List<IntPoint> path in solution)
{
var result = PolygonExtensions.ToPolygon(path, tolerance);
if (result != null)
{
polygons.Add(result);
}
polygons.AddRange(path.ToPolygon(tolerance));
}
return polygons;
}
Expand Down Expand Up @@ -1544,11 +1540,7 @@ public IList<Polygon> Difference(Polygon polygon, double tolerance = Vector3.EPS
var polygons = new List<Polygon>();
foreach (List<IntPoint> path in solution)
{
var result = PolygonExtensions.ToPolygon(path, tolerance);
if (result != null)
{
polygons.Add(result);
}
polygons.AddRange(path.ToPolygon(tolerance));
}
return polygons;
}
Expand Down Expand Up @@ -1583,14 +1575,7 @@ public IList<Polygon> Difference(IList<Polygon> difPolys, double tolerance = Vec
var polygons = new List<Polygon>();
foreach (List<IntPoint> path in solution)
{
try
{
polygons.Add(PolygonExtensions.ToPolygon(path.Distinct().ToList(), tolerance));
}
catch
{
// swallow invalid polygons
}
polygons.AddRange(path.ToPolygon(tolerance));
}
return polygons;
}
Expand Down Expand Up @@ -1620,11 +1605,7 @@ public IList<Polygon> Intersection(Polygon polygon, double tolerance = Vector3.E
var polygons = new List<Polygon>();
foreach (List<IntPoint> path in solution)
{
var result = PolygonExtensions.ToPolygon(path, tolerance);
if (result != null)
{
polygons.Add(result);
}
polygons.AddRange(path.ToPolygon(tolerance));
}
return polygons;
}
Expand All @@ -1651,7 +1632,12 @@ public Polygon Union(Polygon polygon, double tolerance = Vector3.EPSILON)
{
return null;
}
return solution.First().ToPolygon(tolerance);
var polygons = solution.First().ToPolygon(tolerance);
if (polygons.Count > 1)
{
return null;
}
return polygons.First();
}

/// <summary>
Expand Down Expand Up @@ -1694,7 +1680,12 @@ public Polygon Union(IList<Polygon> polygons, double tolerance = Vector3.EPSILON
{
return null;
}
return solution.First().Distinct().ToList().ToPolygon(tolerance);
var results = solution.First().ToPolygon(tolerance);
if (results.Count > 1)
{
return null;
}
return results.First();
}

/// <summary>
Expand All @@ -1718,7 +1709,7 @@ public IList<Polygon> XOR(Polygon polygon, double tolerance = Vector3.EPSILON)
var polygons = new List<Polygon>();
foreach (List<IntPoint> path in solution)
{
polygons.Add(PolygonExtensions.ToPolygon(path, tolerance));
polygons.AddRange(path.ToPolygon(tolerance));
}
return polygons;
}
Expand Down Expand Up @@ -2398,140 +2389,6 @@ protected override Vector3[] NormalsAtVertices()
return result;
}

/// <summary>
/// Deletes Vertices that are out on overloping Edges
/// D__________C
/// | |
/// | |
/// E|_________|B_____A
/// Vertex A will be deleted
/// </summary>
private void DeleteVerticesForOverlappingEdges()
{
if (Vertices.Count < 4)
{
return;
}

for (var i = 0; i < Vertices.Count; i++)
{
var a = Vertices[i];
var b = Vertices[(i + 1) % Vertices.Count];
var c = Vertices[(i + 2) % Vertices.Count];
bool invalid = (a - b).Unitized().Dot((b - c).Unitized()) < (Vector3.EPSILON - 1);
if (invalid)
{
Vertices.Remove(b);
i--;

if (a.IsAlmostEqualTo(c))
{
Vertices.Remove(c);
}
}
}
}

/// <summary>
/// A Polygon can't have self intersections, but points can still lay on other lines.
/// This leads to hidden voids embedded in the perimeter.
/// This function checks if any points are on another line of the loop and splits into distinct loops if found.
/// </summary>
/// <returns>List of simple polygons</returns>
internal List<Polygon> SplitInternalLoops()
{
List<List<Vector3>> polygonPresets = new List<List<Vector3>>();

//Store accumulated vertices and lines between them.
List<Vector3> loopVertices = new List<Vector3>();
List<Line> openLoop = new List<Line>();

//Check if a point lay on active open loop lines.
foreach (var v in Vertices)
{
bool intersectionFound = false;
for (int i = 0; i < openLoop.Count; i++)
{
if (openLoop[i].PointOnLine(v) && v.DistanceTo(openLoop[i]) < Vector3.EPSILON)
{
//Remove points and lines from intersection points to this.
var vertices = loopVertices.Skip(i + 1).ToList();
loopVertices.RemoveRange(i + 1, vertices.Count);
openLoop.RemoveRange(i + 1, vertices.Count - 1);
//Cut intersected line and add this point to open loop.
loopVertices.Add(v);
openLoop[i] = new Line(openLoop[i].Start, v);

//Loop can possibly be just two points connected forth and back.
//Filter it early.
vertices.Add(v);
if (vertices.Count > 2)
{
polygonPresets.Add(vertices);
}
intersectionFound = true;
break;
}
}

//Then check if line (this plus last points) intersects with any accumulated points (going backward)
if (!intersectionFound)
{
Line segment = loopVertices.Any() ? new Line(loopVertices.Last(), v) : null;
for (int i = loopVertices.Count - 1; i >= 0; i--)
{
//Last point is already part of the line.
if (i == loopVertices.Count)
{
continue;
}

if (segment.PointOnLine(loopVertices[i]) && loopVertices[i].DistanceTo(segment) < Vector3.EPSILON)
{
var vertices = loopVertices.Skip(i).ToList();
segment = new Line(loopVertices[i], segment.End);

loopVertices.RemoveRange(i + 1, vertices.Count - 1);
openLoop.RemoveRange(i, vertices.Count - 1);

if (vertices.Count > 2)
{
polygonPresets.Add(vertices);
}
}
}

//If no intersection found just add point and line to open loop.
loopVertices.Add(v);
if (segment != null)
{
openLoop.Add(segment);
}
}
}

//Leftover points form last loop if it has enough points.
if (loopVertices.Count > 2)
{
polygonPresets.Add(loopVertices);
}

List<Polygon> polygons = new List<Polygon>();
foreach (var preset in polygonPresets)
{
try
{
//Polygon constructor cleanup removes any excess vertices and segments.
//This can lead to, again, having too little vertices for valid polygon.
var loop = new Polygon(preset);
polygons.Add(loop);
}
catch
{
//Just ignore polygons that failed check due to having less than 3 points.
}
}
return polygons;
}
}
}
28 changes: 27 additions & 1 deletion Elements/src/Geometry/PolygonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal static List<IntPoint> ToClipperPath(this Polygon p, double tolerance =
/// <param name="p"></param>
/// <param name="tolerance">Optional tolerance value. Be sure to use the same tolerance value as you used when converting to Clipper path.</param>
/// <returns></returns>
internal static Polygon ToPolygon(this List<IntPoint> p, double tolerance = Vector3.EPSILON)
internal static List<Polygon> ToPolygon(this List<IntPoint> p, double tolerance = Vector3.EPSILON)
{
var scale = Math.Round(1.0 / tolerance);
var converted = new Vector3[p.Count];
Expand All @@ -43,6 +43,32 @@ internal static Polygon ToPolygon(this List<IntPoint> p, double tolerance = Vect
var v = p[i];
converted[i] = new Vector3(v.X / scale, v.Y / scale);
}

var polygons = new List<Polygon>();
var cleaned = Vector3.RemoveSequentialDuplicates(converted, true);
Vector3.DeleteVerticesForOverlappingEdges(cleaned);
if (cleaned.Count < 3)
{
return polygons;
}

//Single perimeter can be split not only into one simple perimeter and several voids,
//but also as several independent simple perimeters.
var loops = Vector3.SplitInternalLoops(cleaned);

foreach (var loop in loops)
{
var polygon = ToPolygon(loop);
if (polygon != null)
{
polygons.Add(polygon);
}
}
return polygons;
}

private static Polygon ToPolygon(List<Vector3> converted)
{
try
{
return new Polygon(converted);
Expand Down
8 changes: 4 additions & 4 deletions Elements/src/Geometry/Polyline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,12 +453,12 @@ public virtual Polygon[] Offset(double offset, EndType endType, double tolerance
co.AddPath(path, JoinType.jtMiter, clEndType);
co.Execute(ref solution, offset * clipperScale); // important, scale also used here

var result = new Polygon[solution.Count];
for (var i = 0; i < result.Length; i++)
var result = new List<Polygon>();
foreach (List<IntPoint> s in solution)
{
result[i] = solution[i].ToPolygon(tolerance);
result.AddRange(s.ToPolygon(tolerance));
}
return result;
return result.ToArray();
}

/// <summary>
Expand Down
Loading
Loading