diff --git a/OpenNest.Core/Geometry/Line.cs b/OpenNest.Core/Geometry/Line.cs index ba1300e..5ee18ff 100644 --- a/OpenNest.Core/Geometry/Line.cs +++ b/OpenNest.Core/Geometry/Line.cs @@ -6,8 +6,8 @@ namespace OpenNest.Geometry { public class Line : Entity { - private Vector pt1; - private Vector pt2; + internal Vector pt1; + internal Vector pt2; public Line() { diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs index be5b0d3..190b8f8 100644 --- a/OpenNest.Core/Helper.cs +++ b/OpenNest.Core/Helper.cs @@ -861,22 +861,25 @@ namespace OpenNest /// private static double RayEdgeDistance(Vector vertex, Line edge, PushDirection direction) { - var p1 = edge.StartPoint; - var p2 = edge.EndPoint; + var p1x = edge.pt1.X; + var p1y = edge.pt1.Y; + var p2x = edge.pt2.X; + var p2y = edge.pt2.Y; switch (direction) { case PushDirection.Left: { // Ray goes in -X direction. Need non-horizontal edge. - if (p1.Y.IsEqualTo(p2.Y)) + var dy = p2y - p1y; + if (dy > -Tolerance.Epsilon && dy < Tolerance.Epsilon) return double.MaxValue; // horizontal edge, parallel to ray - var t = (vertex.Y - p1.Y) / (p2.Y - p1.Y); + var t = (vertex.Y - p1y) / dy; if (t < -Tolerance.Epsilon || t > 1.0 + Tolerance.Epsilon) return double.MaxValue; - var ix = p1.X + t * (p2.X - p1.X); + var ix = p1x + t * (p2x - p1x); var dist = vertex.X - ix; // positive if edge is to the left if (dist > Tolerance.Epsilon) return dist; if (dist >= -Tolerance.Epsilon) return 0; // touching @@ -885,14 +888,15 @@ namespace OpenNest case PushDirection.Right: { - if (p1.Y.IsEqualTo(p2.Y)) + var dy = p2y - p1y; + if (dy > -Tolerance.Epsilon && dy < Tolerance.Epsilon) return double.MaxValue; - var t = (vertex.Y - p1.Y) / (p2.Y - p1.Y); + var t = (vertex.Y - p1y) / dy; if (t < -Tolerance.Epsilon || t > 1.0 + Tolerance.Epsilon) return double.MaxValue; - var ix = p1.X + t * (p2.X - p1.X); + var ix = p1x + t * (p2x - p1x); var dist = ix - vertex.X; if (dist > Tolerance.Epsilon) return dist; if (dist >= -Tolerance.Epsilon) return 0; // touching @@ -902,14 +906,15 @@ namespace OpenNest case PushDirection.Down: { // Ray goes in -Y direction. Need non-vertical edge. - if (p1.X.IsEqualTo(p2.X)) + var dx = p2x - p1x; + if (dx > -Tolerance.Epsilon && dx < Tolerance.Epsilon) return double.MaxValue; // vertical edge, parallel to ray - var t = (vertex.X - p1.X) / (p2.X - p1.X); + var t = (vertex.X - p1x) / dx; if (t < -Tolerance.Epsilon || t > 1.0 + Tolerance.Epsilon) return double.MaxValue; - var iy = p1.Y + t * (p2.Y - p1.Y); + var iy = p1y + t * (p2y - p1y); var dist = vertex.Y - iy; if (dist > Tolerance.Epsilon) return dist; if (dist >= -Tolerance.Epsilon) return 0; // touching @@ -918,14 +923,15 @@ namespace OpenNest case PushDirection.Up: { - if (p1.X.IsEqualTo(p2.X)) + var dx = p2x - p1x; + if (dx > -Tolerance.Epsilon && dx < Tolerance.Epsilon) return double.MaxValue; - var t = (vertex.X - p1.X) / (p2.X - p1.X); + var t = (vertex.X - p1x) / dx; if (t < -Tolerance.Epsilon || t > 1.0 + Tolerance.Epsilon) return double.MaxValue; - var iy = p1.Y + t * (p2.Y - p1.Y); + var iy = p1y + t * (p2y - p1y); var dist = iy - vertex.Y; if (dist > Tolerance.Epsilon) return dist; if (dist >= -Tolerance.Epsilon) return 0; // touching @@ -949,14 +955,15 @@ namespace OpenNest // Case 1: Each moving vertex → each stationary edge for (int i = 0; i < movingLines.Count; i++) { - var movingLine = movingLines[i]; + var movingStart = movingLines[i].pt1; + var movingEnd = movingLines[i].pt2; for (int j = 0; j < stationaryLines.Count; j++) { - var d = RayEdgeDistance(movingLine.StartPoint, stationaryLines[j], direction); + var d = RayEdgeDistance(movingStart, stationaryLines[j], direction); if (d < minDist) minDist = d; - d = RayEdgeDistance(movingLine.EndPoint, stationaryLines[j], direction); + d = RayEdgeDistance(movingEnd, stationaryLines[j], direction); if (d < minDist) minDist = d; } } @@ -966,14 +973,15 @@ namespace OpenNest for (int i = 0; i < stationaryLines.Count; i++) { - var stationaryLine = stationaryLines[i]; + var stationaryStart = stationaryLines[i].pt1; + var stationaryEnd = stationaryLines[i].pt2; for (int j = 0; j < movingLines.Count; j++) { - var d = RayEdgeDistance(stationaryLine.StartPoint, movingLines[j], opposite); + var d = RayEdgeDistance(stationaryStart, movingLines[j], opposite); if (d < minDist) minDist = d; - d = RayEdgeDistance(stationaryLine.EndPoint, movingLines[j], opposite); + d = RayEdgeDistance(stationaryEnd, movingLines[j], opposite); if (d < minDist) minDist = d; } } diff --git a/OpenNest.Engine/NestEngine.cs b/OpenNest.Engine/NestEngine.cs index 454dbe4..2b740b5 100644 --- a/OpenNest.Engine/NestEngine.cs +++ b/OpenNest.Engine/NestEngine.cs @@ -273,8 +273,19 @@ namespace OpenNest for (var i = 0; i < parts.Count; i++) { + var box1 = parts[i].BoundingBox; + for (var j = i + 1; j < parts.Count; j++) { + var box2 = parts[j].BoundingBox; + + // Fast bounding box rejection — if boxes don't overlap, + // the parts can't intersect. Eliminates nearly all pairs + // in grid layouts. + if (box1.Right < box2.Left || box2.Right < box1.Left || + box1.Top < box2.Bottom || box2.Top < box1.Bottom) + continue; + List pts; if (parts[i].Intersects(parts[j], out pts)) diff --git a/OpenNest/Actions/ActionClone.cs b/OpenNest/Actions/ActionClone.cs index 5879048..f3cd60d 100644 --- a/OpenNest/Actions/ActionClone.cs +++ b/OpenNest/Actions/ActionClone.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Windows.Forms; using OpenNest.Controls; @@ -170,6 +171,8 @@ namespace OpenNest.Actions private void Fill() { + var sw = Stopwatch.StartNew(); + var plate = plateView.Plate; var engine = new NestEngine(plate); var groupParts = parts.Select(p => p.BasePart).ToList(); @@ -179,6 +182,8 @@ namespace OpenNest.Actions if (plate.Parts.Count == 0) { engine.Fill(groupParts); + sw.Stop(); + plateView.Status = $"Fill: {plate.Parts.Count} parts in {sw.ElapsedMilliseconds} ms"; return; } @@ -197,7 +202,10 @@ namespace OpenNest.Actions if (bestArea == Box.Empty) return; + var before = plate.Parts.Count; engine.Fill(groupParts, bestArea); + sw.Stop(); + plateView.Status = $"Fill: {plate.Parts.Count - before} parts in {sw.ElapsedMilliseconds} ms"; } } } diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs index b554bf7..fd91a16 100644 --- a/OpenNest/Controls/PlateView.cs +++ b/OpenNest/Controls/PlateView.cs @@ -139,7 +139,7 @@ namespace OpenNest.Controls public string Status { get { return status; } - protected set + set { status = value;