Skip to content

Commit 5b19bd4

Browse files
author
Fraser Greenroyd
authored
Geometry_Engine: Optimisation and OutlinesFromLines (#3275)
2 parents 88112c5 + 36062e4 commit 5b19bd4

File tree

8 files changed

+469
-234
lines changed

8 files changed

+469
-234
lines changed

Geometry_Engine/Compute/BooleanDifference.cs

Lines changed: 64 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -44,39 +44,7 @@ public static partial class Compute
4444
[Output("line", "The parts of the first line _not_ overlapping with the reference line.")]
4545
public static List<Line> BooleanDifference(this Line line, Line refLine, double tolerance = Tolerance.Distance)
4646
{
47-
if (refLine.Length() <= tolerance)
48-
return new List<Line> { line };
49-
50-
if (line.IsCollinear(refLine, tolerance))
51-
{
52-
List<Line> splitLine = line.SplitAtPoints(refLine.ControlPoints());
53-
54-
if (splitLine.Count == 3)
55-
splitLine.RemoveAt(1);
56-
else if (splitLine.Count == 2)
57-
{
58-
Point aPt = refLine.ControlPoints().Average();
59-
60-
if (line.Start.SquareDistance(aPt) < line.End.SquareDistance(aPt))
61-
splitLine.RemoveAt(0);
62-
else
63-
splitLine.RemoveAt(1);
64-
}
65-
else
66-
{
67-
double sqTol = tolerance * tolerance;
68-
Point aPt = splitLine[0].ControlPoints().Average();
69-
Point aRPt = refLine.ControlPoints().Average();
70-
71-
if (aRPt.SquareDistance(splitLine[0]) > sqTol && aPt.SquareDistance(refLine) > sqTol)
72-
splitLine = new List<Line> { line };
73-
else
74-
splitLine = new List<Line>();
75-
}
76-
77-
return splitLine;
78-
}
79-
return new List<Line> { line };
47+
return new List<Line> { line }.BooleanDifference(new List<Line> { refLine }, tolerance);
8048
}
8149

8250
/***************************************************/
@@ -88,47 +56,29 @@ public static List<Line> BooleanDifference(this Line line, Line refLine, double
8856
[Output("lines", "The parts of the first lines _not_ overlapping with the reference lines.")]
8957
public static List<Line> BooleanDifference(this List<Line> lines, List<Line> refLines, double tolerance = Tolerance.Distance)
9058
{
91-
List<Line> result = new List<Line>();
59+
double sqTol = tolerance * tolerance;
60+
lines = lines.Where(x => x != null && x.SquareLength() > sqTol).ToList();
61+
List<Line> refLeft = refLines.Where(x => x != null && x.SquareLength() > sqTol).ToList();
9262

93-
foreach (Line line in lines)
63+
List<Line> result = new List<Line>();
64+
foreach (var cluster in lines.ClusterCollinear(tolerance))
9465
{
95-
List<Line> splitLine = new List<Line> { line };
96-
int k = 0;
97-
bool split = false;
98-
do
66+
List<Line> toCull = new List<Line>();
67+
for (int i = refLines.Count - 1; i >= 0; i--)
9968
{
100-
split = false;
101-
for (int i = k; i < refLines.Count; i++)
69+
if (refLines[i].IsCollinear(cluster[0]))
10270
{
103-
if (split)
104-
break;
105-
106-
for (int j = 0; j < splitLine.Count; j++)
107-
{
108-
Line l = splitLine[j];
109-
List<Line> bd = l.BooleanDifference(refLines[i], tolerance);
110-
111-
if (bd.Count == 0)
112-
{
113-
k = refLines.Count;
114-
splitLine = new List<Line>();
115-
split = true;
116-
break;
117-
}
118-
else if (bd.Count > 1 || Math.Abs(bd[0].Length() - l.Length()) > tolerance)
119-
{
120-
k = i + 1;
121-
split = true;
122-
splitLine.RemoveAt(j);
123-
splitLine.AddRange(bd);
124-
break;
125-
}
126-
}
71+
toCull.Add(refLines[i]);
72+
refLines.RemoveAt(i);
12773
}
128-
} while (split);
74+
}
12975

130-
result.AddRange(splitLine);
76+
if (toCull.Count != 0)
77+
result.AddRange(cluster.BooleanDifferenceCollinear(toCull, tolerance));
78+
else
79+
result.AddRange(cluster);
13180
}
81+
13282
return result;
13383
}
13484

@@ -458,7 +408,53 @@ public static List<PolyCurve> BooleanDifference(this ICurve region, IEnumerable<
458408

459409
return result;
460410
}
461-
411+
412+
413+
/***************************************************/
414+
/**** Private Methods ****/
415+
/***************************************************/
416+
417+
private static List<Line> BooleanDifferenceCollinear(this List<Line> lines1, List<Line> lines2, double tolerance)
418+
{
419+
Line dirLine = lines1.Select(x => x.Start).Union(lines1.Select(x => x.End)).Union(lines2.Select(x => x.Start)).Union(lines2.Select(x => x.End)).FitLine(tolerance);
420+
Vector dir = dirLine.Direction(tolerance);
421+
(Point, Point) extents1 = lines1.Extents(dir, tolerance);
422+
(Point, Point) extents2 = lines2.Extents(dir, tolerance);
423+
Point min = (extents1.Item1 - extents2.Item1).DotProduct(dir) < 0 ? extents1.Item1 : extents2.Item1;
424+
min = dirLine.ClosestPoint(min, true);
425+
426+
List<(double, double)> ranges1 = lines1.SortedDomains(min, tolerance);
427+
List<(double, double)> ranges2 = lines2.SortedDomains(min, tolerance);
428+
429+
List<(double, double)> mergedRanges1 = ranges1.MergeRanges(tolerance);
430+
List<(double, double)> mergedRanges2 = ranges2.MergeRanges(tolerance);
431+
432+
List<(double, double)> differenceRanges = new List<(double, double)>();
433+
int i = 0;
434+
foreach ((double, double) range in mergedRanges2)
435+
{
436+
for (; i < mergedRanges1.Count; i++)
437+
{
438+
if (mergedRanges1[i].Item1 - range.Item1 < -tolerance)
439+
differenceRanges.Add((mergedRanges1[i].Item1, Math.Min(mergedRanges1[i].Item2, range.Item1)));
440+
441+
if (range.Item2 - mergedRanges1[i].Item2 < tolerance)
442+
{
443+
mergedRanges1[i] = (range.Item2, mergedRanges1[i].Item2);
444+
break;
445+
}
446+
}
447+
}
448+
449+
for (; i < mergedRanges1.Count; i++)
450+
{
451+
if (mergedRanges1[i].Item2 - mergedRanges1[i].Item1 > tolerance)
452+
differenceRanges.Add(mergedRanges1[i]);
453+
}
454+
455+
return differenceRanges.Select(x => new Line { Start = min + dir * x.Item1, End = min + dir * x.Item2 }).ToList();
456+
}
457+
462458
/***************************************************/
463459
}
464460
}

Geometry_Engine/Compute/BooleanIntersection.cs

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,8 @@ public static BoundingBox BooleanIntersection(this List<BoundingBox> boxes, doub
7777
[Output("intersection", "The line corresponding to the overlap of the two provided lines or null if no overlap could be found.")]
7878
public static Line BooleanIntersection(this Line line, Line refLine, double tolerance = Tolerance.Distance)
7979
{
80-
if (line == null || line.Length() <= tolerance || refLine.Length() <= tolerance)
81-
return null;
82-
83-
if (line.IsCollinear(refLine, tolerance))
84-
{
85-
List<Line> splitLine = line.SplitAtPoints(refLine.ControlPoints(), tolerance);
86-
if (splitLine.Count == 3)
87-
return splitLine[1];
88-
else if (splitLine.Count == 2)
89-
{
90-
Point aPt = refLine.ControlPoints().Average();
91-
return line.Start.SquareDistance(aPt) < line.End.SquareDistance(aPt) ? splitLine[0] : splitLine[1];
92-
}
93-
else
94-
{
95-
double sqTol = tolerance * tolerance;
96-
Point aPt = splitLine[0].ControlPoints().Average();
97-
Point aRPt = refLine.ControlPoints().Average();
98-
return aRPt.SquareDistance(splitLine[0]) > sqTol && aPt.SquareDistance(refLine) > sqTol ? null : line;
99-
}
100-
}
101-
102-
return null;
80+
List<Line> result = new List<Line> { line }.BooleanIntersection(new List<Line> { refLine }, tolerance);
81+
return result.Count == 0 ? null : result[0];
10382
}
10483

10584
/***************************************************/
@@ -111,15 +90,38 @@ public static Line BooleanIntersection(this Line line, Line refLine, double tole
11190
[Output("intersection", "The collection of lines corresponding to the overlaps of the first line and any of the reference lines.")]
11291
public static List<Line> BooleanIntersection(this Line line, List<Line> refLines, double tolerance = Tolerance.Distance)
11392
{
114-
List<Line> result = new List<Line>();
115-
if (line.Length() <= tolerance)
116-
return result;
93+
return new List<Line> { line }.BooleanIntersection(refLines, tolerance);
94+
}
95+
96+
/***************************************************/
97+
11798

118-
foreach (Line l in refLines.BooleanUnion(tolerance))
99+
[Description("Computes the boolean intersection of two collections of lines, e.g. all overlaps of the first set of lines with the reference lines and returns a new collection of lines matching the overlap.")]
100+
[Input("lines", "First list of lines to intersect.")]
101+
[Input("refLines", "A list of reference lines to intersect with the first list of lines.")]
102+
[Input("tolerance", "Tolerance to be used in the method.", typeof(Length))]
103+
[Output("intersection", "The list of lines corresponding to the overlaps of the first list of lines and any of the reference lines.")]
104+
public static List<Line> BooleanIntersection(this List<Line> lines, List<Line> refLines, double tolerance = Tolerance.Distance)
105+
{
106+
double sqTol = tolerance * tolerance;
107+
lines = lines.Where(x => x != null && x.SquareLength() > sqTol).ToList();
108+
List<Line> refLeft = refLines.Where(x => x != null && x.SquareLength() > sqTol).ToList();
109+
110+
List<Line> result = new List<Line>();
111+
foreach (var cluster in lines.ClusterCollinear(tolerance))
119112
{
120-
Line intersection = line.BooleanIntersection(l, tolerance);
121-
if (intersection != null)
122-
result.Add(intersection);
113+
List<Line> toIntersect = new List<Line>();
114+
for (int i = refLines.Count - 1; i >= 0; i--)
115+
{
116+
if (refLines[i].IsCollinear(cluster[0]))
117+
{
118+
toIntersect.Add(refLines[i]);
119+
refLines.RemoveAt(i);
120+
}
121+
}
122+
123+
if (toIntersect.Count != 0)
124+
result.AddRange(cluster.BooleanIntersectionCollinear(toIntersect, tolerance));
123125
}
124126

125127
return result;
@@ -128,24 +130,19 @@ public static List<Line> BooleanIntersection(this Line line, List<Line> refLines
128130
/***************************************************/
129131

130132
[Description("Computes the boolean intersection of a collection of lines, e.g. the overlap of all the provided lines and returns this overlap as a new line.")]
131-
[Input("lines", "The collection of lines to intersect.")]
133+
[Input("lines", "The list of lines to intersect.")]
132134
[Input("tolerance", "Tolerance to be used in the method.", typeof(Length))]
133135
[Output("intersection", "The line corresponding to the overlap all of the provided lines.")]
134136
public static Line BooleanIntersection(this List<Line> lines, double tolerance = Tolerance.Distance)
135137
{
136-
if (lines[0].Length() <= tolerance)
138+
if (lines.IsCollinear(tolerance))
139+
return lines.BooleanIntersectionCollinear(tolerance);
140+
else
137141
return null;
138-
139-
Line result = lines[0];
140-
for (int i = 1; i < lines.Count; i++)
141-
{
142-
result = result.BooleanIntersection(lines[i], tolerance);
143-
}
144-
145-
return result;
146142
}
147143

148144

145+
149146
/***************************************************/
150147
/**** public Methods - Regions ****/
151148
/***************************************************/
@@ -410,7 +407,8 @@ public static List<PolyCurve> BooleanIntersection(this IEnumerable<ICurve> regio
410407
return result;
411408
}
412409

413-
/**************************************************
410+
411+
/***************************************************/
414412
/*** Private methods ***/
415413
/***************************************************/
416414

@@ -432,7 +430,58 @@ private static bool IsSimilarSegment(this ICurve curve, ICurve refCurve, double
432430
return false;
433431
}
434432

435-
/***************************************************/
433+
/***************************************************/
434+
435+
private static List<Line> BooleanIntersectionCollinear(this List<Line> lines, List<Line> lines2, double tolerance)
436+
{
437+
Vector dir = lines[0].Direction();
438+
(Point, Point) extents1 = lines.Extents(dir, tolerance);
439+
(Point, Point) extents2 = lines2.Extents(dir, tolerance);
440+
Point min = (extents1.Item1 - extents2.Item1).DotProduct(dir) < 0 ? extents1.Item1 : extents2.Item1;
441+
442+
List<(double, double)> ranges1 = lines.SortedDomains(min, tolerance);
443+
List<(double, double)> ranges2 = lines2.SortedDomains(min, tolerance);
444+
445+
List<(double, double)> mergedRanges1 = ranges1.MergeRanges(tolerance);
446+
List<(double, double)> mergedRanges2 = ranges2.MergeRanges(tolerance);
447+
448+
List<(double, double)> intersectedRanges = new List<(double, double)>();
449+
int i = 0;
450+
foreach ((double, double) range in mergedRanges1)
451+
{
452+
for (; i < mergedRanges2.Count; i++)
453+
{
454+
if (range.Item1 - mergedRanges2[i].Item2 < -tolerance && range.Item2 - mergedRanges2[i].Item1 > tolerance)
455+
intersectedRanges.Add((Math.Max(range.Item1, mergedRanges2[i].Item1), Math.Min(range.Item2, mergedRanges2[i].Item2)));
456+
457+
if (range.Item2 - mergedRanges2[i].Item2 < tolerance)
458+
break;
459+
}
460+
}
461+
462+
return intersectedRanges.Select(x => new Line { Start = min + dir * x.Item1, End = min + dir * x.Item2 }).ToList();
463+
}
464+
465+
/***************************************************/
466+
467+
private static Line BooleanIntersectionCollinear(this List<Line> lines, double tolerance)
468+
{
469+
Line dirLine = lines.Select(x => x.Start).Union(lines.Select(x => x.End)).FitLine(tolerance);
470+
Vector dir = dirLine.Direction(tolerance);
471+
(Point, Point) extents = lines.Extents(dir, tolerance);
472+
Point min = dirLine.ClosestPoint(extents.Item1, true);
473+
474+
List<(double, double)> ranges = lines.SortedDomains(min, tolerance);
475+
double maxStart = ranges.Select(x => x.Item1).OrderByDescending(x => x).First();
476+
double minEnd = ranges.Select(x => x.Item2).OrderBy(x => x).First();
477+
478+
if (minEnd - maxStart > tolerance)
479+
return new Line { Start = min + dir * maxStart, End = min + dir * minEnd };
480+
else
481+
return null;
482+
}
483+
484+
/***************************************************/
436485

437486
}
438487
}

0 commit comments

Comments
 (0)