Skip to content

Geometry_Engine: Optimisation and tweaks #3275

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

Merged
merged 17 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
132 changes: 64 additions & 68 deletions Geometry_Engine/Compute/BooleanDifference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,7 @@ public static partial class Compute
[Output("line", "The parts of the first line _not_ overlapping with the reference line.")]
public static List<Line> BooleanDifference(this Line line, Line refLine, double tolerance = Tolerance.Distance)
{
if (refLine.Length() <= tolerance)
return new List<Line> { line };

if (line.IsCollinear(refLine, tolerance))
{
List<Line> splitLine = line.SplitAtPoints(refLine.ControlPoints());

if (splitLine.Count == 3)
splitLine.RemoveAt(1);
else if (splitLine.Count == 2)
{
Point aPt = refLine.ControlPoints().Average();

if (line.Start.SquareDistance(aPt) < line.End.SquareDistance(aPt))
splitLine.RemoveAt(0);
else
splitLine.RemoveAt(1);
}
else
{
double sqTol = tolerance * tolerance;
Point aPt = splitLine[0].ControlPoints().Average();
Point aRPt = refLine.ControlPoints().Average();

if (aRPt.SquareDistance(splitLine[0]) > sqTol && aPt.SquareDistance(refLine) > sqTol)
splitLine = new List<Line> { line };
else
splitLine = new List<Line>();
}

return splitLine;
}
return new List<Line> { line };
return new List<Line> { line }.BooleanDifference(new List<Line> { refLine }, tolerance);
}

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

foreach (Line line in lines)
List<Line> result = new List<Line>();
foreach (var cluster in lines.ClusterCollinear(tolerance))
{
List<Line> splitLine = new List<Line> { line };
int k = 0;
bool split = false;
do
List<Line> toCull = new List<Line>();
for (int i = refLines.Count - 1; i >= 0; i--)
{
split = false;
for (int i = k; i < refLines.Count; i++)
if (refLines[i].IsCollinear(cluster[0]))
{
if (split)
break;

for (int j = 0; j < splitLine.Count; j++)
{
Line l = splitLine[j];
List<Line> bd = l.BooleanDifference(refLines[i], tolerance);

if (bd.Count == 0)
{
k = refLines.Count;
splitLine = new List<Line>();
split = true;
break;
}
else if (bd.Count > 1 || Math.Abs(bd[0].Length() - l.Length()) > tolerance)
{
k = i + 1;
split = true;
splitLine.RemoveAt(j);
splitLine.AddRange(bd);
break;
}
}
toCull.Add(refLines[i]);
refLines.RemoveAt(i);
}
} while (split);
}

result.AddRange(splitLine);
if (toCull.Count != 0)
result.AddRange(cluster.BooleanDifferenceCollinear(toCull, tolerance));
else
result.AddRange(cluster);
}

return result;
}

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

return result;
}



/***************************************************/
/**** Private Methods ****/
/***************************************************/

private static List<Line> BooleanDifferenceCollinear(this List<Line> lines1, List<Line> lines2, double tolerance)
{
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);
Vector dir = dirLine.Direction(tolerance);
(Point, Point) extents1 = lines1.Extents(dir, tolerance);
(Point, Point) extents2 = lines2.Extents(dir, tolerance);
Point min = (extents1.Item1 - extents2.Item1).DotProduct(dir) < 0 ? extents1.Item1 : extents2.Item1;
min = dirLine.ClosestPoint(min, true);

List<(double, double)> ranges1 = lines1.SortedDomains(min, tolerance);
List<(double, double)> ranges2 = lines2.SortedDomains(min, tolerance);

List<(double, double)> mergedRanges1 = ranges1.MergeRanges(tolerance);
List<(double, double)> mergedRanges2 = ranges2.MergeRanges(tolerance);

List<(double, double)> differenceRanges = new List<(double, double)>();
int i = 0;
foreach ((double, double) range in mergedRanges2)
{
for (; i < mergedRanges1.Count; i++)
{
if (mergedRanges1[i].Item1 - range.Item1 < -tolerance)
differenceRanges.Add((mergedRanges1[i].Item1, Math.Min(mergedRanges1[i].Item2, range.Item1)));

if (range.Item2 - mergedRanges1[i].Item2 < tolerance)
{
mergedRanges1[i] = (range.Item2, mergedRanges1[i].Item2);
break;
}
}
}

for (; i < mergedRanges1.Count; i++)
{
if (mergedRanges1[i].Item2 - mergedRanges1[i].Item1 > tolerance)
differenceRanges.Add(mergedRanges1[i]);
}

return differenceRanges.Select(x => new Line { Start = min + dir * x.Item1, End = min + dir * x.Item2 }).ToList();
}

/***************************************************/
}
}
Expand Down
131 changes: 90 additions & 41 deletions Geometry_Engine/Compute/BooleanIntersection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,29 +77,8 @@ public static BoundingBox BooleanIntersection(this List<BoundingBox> boxes, doub
[Output("intersection", "The line corresponding to the overlap of the two provided lines or null if no overlap could be found.")]
public static Line BooleanIntersection(this Line line, Line refLine, double tolerance = Tolerance.Distance)
{
if (line == null || line.Length() <= tolerance || refLine.Length() <= tolerance)
return null;

if (line.IsCollinear(refLine, tolerance))
{
List<Line> splitLine = line.SplitAtPoints(refLine.ControlPoints(), tolerance);
if (splitLine.Count == 3)
return splitLine[1];
else if (splitLine.Count == 2)
{
Point aPt = refLine.ControlPoints().Average();
return line.Start.SquareDistance(aPt) < line.End.SquareDistance(aPt) ? splitLine[0] : splitLine[1];
}
else
{
double sqTol = tolerance * tolerance;
Point aPt = splitLine[0].ControlPoints().Average();
Point aRPt = refLine.ControlPoints().Average();
return aRPt.SquareDistance(splitLine[0]) > sqTol && aPt.SquareDistance(refLine) > sqTol ? null : line;
}
}

return null;
List<Line> result = new List<Line> { line }.BooleanIntersection(new List<Line> { refLine }, tolerance);
return result.Count == 0 ? null : result[0];
}

/***************************************************/
Expand All @@ -111,15 +90,38 @@ public static Line BooleanIntersection(this Line line, Line refLine, double tole
[Output("intersection", "The collection of lines corresponding to the overlaps of the first line and any of the reference lines.")]
public static List<Line> BooleanIntersection(this Line line, List<Line> refLines, double tolerance = Tolerance.Distance)
{
List<Line> result = new List<Line>();
if (line.Length() <= tolerance)
return result;
return new List<Line> { line }.BooleanIntersection(refLines, tolerance);
}

/***************************************************/


foreach (Line l in refLines.BooleanUnion(tolerance))
[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.")]
[Input("lines", "First line to intersect.")]
[Input("refLines", "A collection of reference lines to intersect with the first line.")]
[Input("tolerance", "Tolerance to be used in the method.", typeof(Length))]
[Output("intersection", "The collection of lines corresponding to the overlaps of the first line and any of the reference lines.")]
public static List<Line> BooleanIntersection(this List<Line> lines, List<Line> refLines, double tolerance = Tolerance.Distance)
{
double sqTol = tolerance * tolerance;
lines = lines.Where(x => x != null && x.SquareLength() > sqTol).ToList();
List<Line> refLeft = refLines.Where(x => x != null && x.SquareLength() > sqTol).ToList();

List<Line> result = new List<Line>();
foreach (var cluster in lines.ClusterCollinear(tolerance))
{
Line intersection = line.BooleanIntersection(l, tolerance);
if (intersection != null)
result.Add(intersection);
List<Line> toIntersect = new List<Line>();
for (int i = refLines.Count - 1; i >= 0; i--)
{
if (refLines[i].IsCollinear(cluster[0]))
{
toIntersect.Add(refLines[i]);
refLines.RemoveAt(i);
}
}

if (toIntersect.Count != 0)
result.AddRange(cluster.BooleanIntersectionCollinear(toIntersect, tolerance));
}

return result;
Expand All @@ -133,19 +135,14 @@ public static List<Line> BooleanIntersection(this Line line, List<Line> refLines
[Output("intersection", "The line corresponding to the overlap all of the provided lines.")]
public static Line BooleanIntersection(this List<Line> lines, double tolerance = Tolerance.Distance)
{
if (lines[0].Length() <= tolerance)
if (lines.IsCollinear(tolerance))
return lines.BooleanIntersectionCollinear(tolerance);
else
return null;

Line result = lines[0];
for (int i = 1; i < lines.Count; i++)
{
result = result.BooleanIntersection(lines[i], tolerance);
}

return result;
}



/***************************************************/
/**** public Methods - Regions ****/
/***************************************************/
Expand Down Expand Up @@ -410,7 +407,8 @@ public static List<PolyCurve> BooleanIntersection(this IEnumerable<ICurve> regio
return result;
}

/**************************************************

/***************************************************/
/*** Private methods ***/
/***************************************************/

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

/***************************************************/
/***************************************************/

private static List<Line> BooleanIntersectionCollinear(this List<Line> lines, List<Line> lines2, double tolerance)
{
Vector dir = lines[0].Direction();
(Point, Point) extents1 = lines.Extents(dir, tolerance);
(Point, Point) extents2 = lines2.Extents(dir, tolerance);
Point min = (extents1.Item1 - extents2.Item1).DotProduct(dir) < 0 ? extents1.Item1 : extents2.Item1;

List<(double, double)> ranges1 = lines.SortedDomains(min, tolerance);
List<(double, double)> ranges2 = lines2.SortedDomains(min, tolerance);

List<(double, double)> mergedRanges1 = ranges1.MergeRanges(tolerance);
List<(double, double)> mergedRanges2 = ranges2.MergeRanges(tolerance);

List<(double, double)> intersectedRanges = new List<(double, double)>();
int i = 0;
foreach ((double, double) range in mergedRanges1)
{
for (; i < mergedRanges2.Count; i++)
{
if (range.Item1 - mergedRanges2[i].Item2 < -tolerance && range.Item2 - mergedRanges2[i].Item1 > tolerance)
intersectedRanges.Add((Math.Max(range.Item1, mergedRanges2[i].Item1), Math.Min(range.Item2, mergedRanges2[i].Item2)));

if (range.Item2 - mergedRanges2[i].Item2 < tolerance)
break;
}
}

return intersectedRanges.Select(x => new Line { Start = min + dir * x.Item1, End = min + dir * x.Item2 }).ToList();
}

/***************************************************/

private static Line BooleanIntersectionCollinear(this List<Line> lines, double tolerance)
{
Line dirLine = lines.Select(x => x.Start).Union(lines.Select(x => x.End)).FitLine(tolerance);
Vector dir = dirLine.Direction(tolerance);
(Point, Point) extents = lines.Extents(dir, tolerance);
Point min = dirLine.ClosestPoint(extents.Item1, true);

List<(double, double)> ranges = lines.SortedDomains(min, tolerance);
double maxStart = ranges.Select(x => x.Item1).OrderByDescending(x => x).First();
double minEnd = ranges.Select(x => x.Item2).OrderBy(x => x).First();

if (minEnd - maxStart > tolerance)
return new Line { Start = min + dir * maxStart, End = min + dir * minEnd };
else
return null;
}

/***************************************************/

}
}
Expand Down
Loading