Compare commits

...

7 Commits

Author SHA1 Message Date
aj 5949c3ca1f feat: add Delete key to remove source parts during ActionClone
Enables a "move" workflow: clone parts to a new position, then
press Delete to remove the originals. Previously Delete just
cancelled the clone action.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:21:23 -04:00
aj ef15421915 refactor: standardize fill strategy progress reporting via FillContext
Strategies and fillers previously called NestEngineBase.ReportProgress
directly, each constructing ProgressReport structs with phase, plate
number, and work area manually. Some strategies (RectBestFit) reported
nothing at all. This made progress updates inconsistent and flakey.

Add FillContext.ReportProgress(parts, description) as the single
standard method for intermediate progress. RunPipeline sets ActivePhase
before each strategy, and the context handles common fields. Lower-level
fillers (PairFiller, FillExtents, StripeFiller) now accept an
Action<List<Part>, string> callback instead of raw IProgress, removing
their coupling to NestEngineBase and ProgressReport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:21:48 -04:00
aj 943c262ad2 fix: clear part selection highlight when leaving lead-in action
ActionLeadIn.DisconnectEvents() nulled selectedLayoutPart without first
setting IsSelected = false, leaving the part permanently rendered in the
selection color (transparent blue) after switching actions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:38:56 -04:00
aj 301831e096 fix: correct Width/Length axis swap in best-fit slide offsets
BuildOffsets had Width and Length swapped after the Box axis correction
in c5943e2. Horizontal pushes used Length (X) for perpendicular sweep
and Width (Y) for push start — backwards. This caused part2 to start
inside part1's footprint, producing overlapping best-fit pairs.

Added regression test that verifies no kept best-fit pairs overlap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:26:09 -04:00
aj fce287e649 chore: regenerate NestProgressForm designer layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:34:43 -04:00
aj 7e86313d7c fix: prevent Delete key from corrupting quantity during ActionClone
ObservableList.Remove fired ItemRemoved even when the item wasn't in
the list, causing Plate to decrement Quantity.Nested for clone preview
parts that were never added — producing -1 counts. Delete in PlateView
now cancels ActionClone instead of trying to remove its preview parts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:34:25 -04:00
aj c5943e22eb fix: correct Width/Length axis mapping and add spiral center-fill
Box constructor and derived properties (Right, Top, Center, Translate, Offset)
had Width and Length swapped — Length is X axis, Width is Y axis. Corrected
across Core geometry, plate bounding box, rectangle packing, fill algorithms,
tests, and UI renderers.

Added FillSpiral with center remnant detection and recursive FillBest on
the gap between the 4 spiral quadrants. RectFill.FillBest now compares
spiral+center vs full best-fit fairly. BestCombination returns a
CombinationResult record instead of out params.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:22:55 -04:00
66 changed files with 727 additions and 435 deletions
+2 -1
View File
@@ -46,7 +46,8 @@ namespace OpenNest.Collections
public bool Remove(T item) public bool Remove(T item)
{ {
var success = items.Remove(item); var success = items.Remove(item);
ItemRemoved?.Invoke(this, new ItemRemovedEventArgs<T>(item, success)); if (success)
ItemRemoved?.Invoke(this, new ItemRemovedEventArgs<T>(item, success));
return success; return success;
} }
+6 -6
View File
@@ -50,13 +50,13 @@ namespace OpenNest
{ {
cutPosition = Position.X; cutPosition = Position.X;
lineStart = StartLimit ?? bounds.Y; lineStart = StartLimit ?? bounds.Y;
lineEnd = EndLimit ?? (bounds.Y + bounds.Length + settings.Overtravel); lineEnd = EndLimit ?? (bounds.Y + bounds.Width + settings.Overtravel);
} }
else else
{ {
cutPosition = Position.Y; cutPosition = Position.Y;
lineStart = StartLimit ?? bounds.X; lineStart = StartLimit ?? bounds.X;
lineEnd = EndLimit ?? (bounds.X + bounds.Width + settings.Overtravel); lineEnd = EndLimit ?? (bounds.X + bounds.Length + settings.Overtravel);
} }
var exclusions = new List<(double Start, double End)>(); var exclusions = new List<(double Start, double End)>();
@@ -176,13 +176,13 @@ namespace OpenNest
private (double Min, double Max) AxisBounds(Box bb, double clearance) => private (double Min, double Max) AxisBounds(Box bb, double clearance) =>
Axis == CutOffAxis.Vertical Axis == CutOffAxis.Vertical
? (bb.X - clearance, bb.X + bb.Width + clearance) ? (bb.X - clearance, bb.X + bb.Length + clearance)
: (bb.Y - clearance, bb.Y + bb.Length + clearance); : (bb.Y - clearance, bb.Y + bb.Width + clearance);
private (double Start, double End) CrossAxisBounds(Box bb, double clearance) => private (double Start, double End) CrossAxisBounds(Box bb, double clearance) =>
Axis == CutOffAxis.Vertical Axis == CutOffAxis.Vertical
? (bb.Y - clearance, bb.Y + bb.Length + clearance) ? (bb.Y - clearance, bb.Y + bb.Width + clearance)
: (bb.X - clearance, bb.X + bb.Width + clearance); : (bb.X - clearance, bb.X + bb.Length + clearance);
private Program BuildProgram(List<(double Start, double End)> segments, CutOffSettings settings) private Program BuildProgram(List<(double Start, double End)> segments, CutOffSettings settings)
{ {
+2 -2
View File
@@ -420,8 +420,8 @@ namespace OpenNest.Geometry
boundingBox.X = minX; boundingBox.X = minX;
boundingBox.Y = minY; boundingBox.Y = minY;
boundingBox.Width = maxX - minX; boundingBox.Length = maxX - minX;
boundingBox.Length = maxY - minY; boundingBox.Width = maxY - minY;
} }
public override Entity OffsetEntity(double distance, OffsetSide side) public override Entity OffsetEntity(double distance, OffsetSide side)
+2 -2
View File
@@ -12,8 +12,8 @@ namespace OpenNest.Geometry
double minX = boxes[0].X; double minX = boxes[0].X;
double minY = boxes[0].Y; double minY = boxes[0].Y;
double maxX = boxes[0].X + boxes[0].Width; double maxX = boxes[0].Right;
double maxY = boxes[0].Y + boxes[0].Length; double maxY = boxes[0].Top;
foreach (var box in boxes) foreach (var box in boxes)
{ {
+8 -8
View File
@@ -14,15 +14,15 @@ namespace OpenNest.Geometry
public Box(double x, double y, double w, double h) public Box(double x, double y, double w, double h)
{ {
Location = new Vector(x, y); Location = new Vector(x, y);
Width = w; Length = w;
Length = h; Width = h;
} }
public Vector Location; public Vector Location;
public Vector Center public Vector Center
{ {
get { return new Vector(X + Width * 0.5, Y + Length * 0.5); } get { return new Vector(X + Length * 0.5, Y + Width * 0.5); }
} }
public Size Size; public Size Size;
@@ -76,12 +76,12 @@ namespace OpenNest.Geometry
public Box Translate(double x, double y) public Box Translate(double x, double y)
{ {
return new Box(X + x, Y + y, Width, Length); return new Box(X + x, Y + y, Length, Width);
} }
public Box Translate(Vector offset) public Box Translate(Vector offset)
{ {
return new Box(X + offset.X, Y + offset.Y, Width, Length); return new Box(X + offset.X, Y + offset.Y, Length, Width);
} }
public double Left public double Left
@@ -91,12 +91,12 @@ namespace OpenNest.Geometry
public double Right public double Right
{ {
get { return X + Width; } get { return X + Length; }
} }
public double Top public double Top
{ {
get { return Y + Length; } get { return Y + Width; }
} }
public double Bottom public double Bottom
@@ -207,7 +207,7 @@ namespace OpenNest.Geometry
public Box Offset(double d) public Box Offset(double d)
{ {
return new Box(X - d, Y - d, Width + d * 2, Length + d * 2); return new Box(X - d, Y - d, Length + d * 2, Width + d * 2);
} }
public override string ToString() public override string ToString()
+4 -4
View File
@@ -9,7 +9,7 @@
var x = large.Left; var x = large.Left;
var y = small.Top; var y = small.Top;
var w = large.Width; var w = large.Length;
var h = large.Top - y; var h = large.Top - y;
return new Box(x, y, w, h); return new Box(x, y, w, h);
@@ -23,7 +23,7 @@
var x = large.Left; var x = large.Left;
var y = large.Bottom; var y = large.Bottom;
var w = small.Left - x; var w = small.Left - x;
var h = large.Length; var h = large.Width;
return new Box(x, y, w, h); return new Box(x, y, w, h);
} }
@@ -35,7 +35,7 @@
var x = large.Left; var x = large.Left;
var y = large.Bottom; var y = large.Bottom;
var w = large.Width; var w = large.Length;
var h = small.Top - y; var h = small.Top - y;
return new Box(x, y, w, h); return new Box(x, y, w, h);
@@ -49,7 +49,7 @@
var x = small.Right; var x = small.Right;
var y = large.Bottom; var y = large.Bottom;
var w = large.Right - x; var w = large.Right - x;
var h = large.Length; var h = large.Width;
return new Box(x, y, w, h); return new Box(x, y, w, h);
} }
+4 -4
View File
@@ -370,23 +370,23 @@ namespace OpenNest.Geometry
if (StartPoint.X < EndPoint.X) if (StartPoint.X < EndPoint.X)
{ {
boundingBox.X = StartPoint.X; boundingBox.X = StartPoint.X;
boundingBox.Width = EndPoint.X - StartPoint.X; boundingBox.Length = EndPoint.X - StartPoint.X;
} }
else else
{ {
boundingBox.X = EndPoint.X; boundingBox.X = EndPoint.X;
boundingBox.Width = StartPoint.X - EndPoint.X; boundingBox.Length = StartPoint.X - EndPoint.X;
} }
if (StartPoint.Y < EndPoint.Y) if (StartPoint.Y < EndPoint.Y)
{ {
boundingBox.Y = StartPoint.Y; boundingBox.Y = StartPoint.Y;
boundingBox.Length = EndPoint.Y - StartPoint.Y; boundingBox.Width = EndPoint.Y - StartPoint.Y;
} }
else else
{ {
boundingBox.Y = EndPoint.Y; boundingBox.Y = EndPoint.Y;
boundingBox.Length = StartPoint.Y - EndPoint.Y; boundingBox.Width = StartPoint.Y - EndPoint.Y;
} }
} }
+2 -2
View File
@@ -311,8 +311,8 @@ namespace OpenNest.Geometry
boundingBox.X = minX; boundingBox.X = minX;
boundingBox.Y = minY; boundingBox.Y = minY;
boundingBox.Width = maxX - minX; boundingBox.Length = maxX - minX;
boundingBox.Length = maxY - minY; boundingBox.Width = maxY - minY;
} }
public override Entity OffsetEntity(double distance, OffsetSide side) public override Entity OffsetEntity(double distance, OffsetSide side)
+1 -1
View File
@@ -277,7 +277,7 @@ namespace OpenNest
var part = new Part(BaseDrawing, Program, var part = new Part(BaseDrawing, Program,
location + offset, location + offset,
new Box(BoundingBox.X + offset.X, BoundingBox.Y + offset.Y, new Box(BoundingBox.X + offset.X, BoundingBox.Y + offset.Y,
BoundingBox.Width, BoundingBox.Length)); BoundingBox.Length, BoundingBox.Width));
return part; return part;
} }
+7 -7
View File
@@ -424,7 +424,7 @@ namespace OpenNest
{ {
var plateBox = new Box(); var plateBox = new Box();
// Convention: Size.Length = X axis (horizontal), Size.Width = Y axis (vertical) // Width = Y axis (vertical), Length = X axis (horizontal)
switch (Quadrant) switch (Quadrant)
{ {
case 1: case 1:
@@ -451,8 +451,8 @@ namespace OpenNest
return new Box(); return new Box();
} }
plateBox.Width = Size.Length; plateBox.Width = Size.Width;
plateBox.Length = Size.Width; plateBox.Length = Size.Length;
if (!includeParts) if (!includeParts)
return plateBox; return plateBox;
@@ -468,11 +468,11 @@ namespace OpenNest
? partsBox.Bottom ? partsBox.Bottom
: plateBox.Bottom; : plateBox.Bottom;
boundingBox.Width = partsBox.Right > plateBox.Right boundingBox.Length = partsBox.Right > plateBox.Right
? partsBox.Right - boundingBox.X ? partsBox.Right - boundingBox.X
: plateBox.Right - boundingBox.X; : plateBox.Right - boundingBox.X;
boundingBox.Length = partsBox.Top > plateBox.Top boundingBox.Width = partsBox.Top > plateBox.Top
? partsBox.Top - boundingBox.Y ? partsBox.Top - boundingBox.Y
: plateBox.Top - boundingBox.Y; : plateBox.Top - boundingBox.Y;
@@ -489,8 +489,8 @@ namespace OpenNest
box.X += EdgeSpacing.Left; box.X += EdgeSpacing.Left;
box.Y += EdgeSpacing.Bottom; box.Y += EdgeSpacing.Bottom;
box.Width -= EdgeSpacing.Left + EdgeSpacing.Right; box.Length -= EdgeSpacing.Left + EdgeSpacing.Right;
box.Length -= EdgeSpacing.Top + EdgeSpacing.Bottom; box.Width -= EdgeSpacing.Top + EdgeSpacing.Bottom;
return box; return box;
} }
@@ -13,8 +13,8 @@ public static class AutoSplitCalculator
var lines = new List<SplitLine>(); var lines = new List<SplitLine>();
var verticalSplits = usableWidth > 0 ? (int)System.Math.Ceiling(partBounds.Width / usableWidth) - 1 : 0; var verticalSplits = usableWidth > 0 ? (int)System.Math.Ceiling(partBounds.Length / usableWidth) - 1 : 0;
var horizontalSplits = usableHeight > 0 ? (int)System.Math.Ceiling(partBounds.Length / usableHeight) - 1 : 0; var horizontalSplits = usableHeight > 0 ? (int)System.Math.Ceiling(partBounds.Width / usableHeight) - 1 : 0;
if (verticalSplits < 0) verticalSplits = 0; if (verticalSplits < 0) verticalSplits = 0;
if (horizontalSplits < 0) horizontalSplits = 0; if (horizontalSplits < 0) horizontalSplits = 0;
@@ -34,14 +34,14 @@ public static class AutoSplitCalculator
if (verticalPieces > 1) if (verticalPieces > 1)
{ {
var spacing = partBounds.Width / verticalPieces; var spacing = partBounds.Length / verticalPieces;
for (var i = 1; i < verticalPieces; i++) for (var i = 1; i < verticalPieces; i++)
lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical)); lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical));
} }
if (horizontalPieces > 1) if (horizontalPieces > 1)
{ {
var spacing = partBounds.Length / horizontalPieces; var spacing = partBounds.Width / horizontalPieces;
for (var i = 1; i < horizontalPieces; i++) for (var i = 1; i < horizontalPieces; i++)
lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal)); lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal));
} }
@@ -89,16 +89,18 @@ namespace OpenNest.Engine.BestFit
if (isHorizontalPush) if (isHorizontalPush)
{ {
perpMin = -(bbox2.Length + spacing); // Perpendicular sweep along Y → Width; push extent along X → Length
perpMax = bbox1.Length + bbox2.Length + spacing;
pushStartOffset = bbox1.Width + bbox2.Width + spacing * 2;
}
else
{
perpMin = -(bbox2.Width + spacing); perpMin = -(bbox2.Width + spacing);
perpMax = bbox1.Width + bbox2.Width + spacing; perpMax = bbox1.Width + bbox2.Width + spacing;
pushStartOffset = bbox1.Length + bbox2.Length + spacing * 2; pushStartOffset = bbox1.Length + bbox2.Length + spacing * 2;
} }
else
{
// Perpendicular sweep along X → Length; push extent along Y → Width
perpMin = -(bbox2.Length + spacing);
perpMax = bbox1.Length + bbox2.Length + spacing;
pushStartOffset = bbox1.Width + bbox2.Width + spacing * 2;
}
var alignedStart = System.Math.Ceiling(perpMin / stepSize) * stepSize; var alignedStart = System.Math.Ceiling(perpMin / stepSize) * stepSize;
+2 -1
View File
@@ -203,7 +203,7 @@ namespace OpenNest
if (newWidth >= workArea.Width && newLength >= workArea.Length) if (newWidth >= workArea.Width && newLength >= workArea.Length)
return workArea; return workArea;
return new Box(workArea.X, workArea.Y, newWidth, newLength); return new Box(workArea.X, workArea.Y, newLength, newWidth);
} }
private List<Part> RunFillPipeline(NestItem item, Box workArea, private List<Part> RunFillPipeline(NestItem item, Box workArea,
@@ -295,6 +295,7 @@ namespace OpenNest
foreach (var strategy in FillStrategyRegistry.Strategies) foreach (var strategy in FillStrategyRegistry.Strategies)
{ {
context.Token.ThrowIfCancellationRequested(); context.Token.ThrowIfCancellationRequested();
context.ActivePhase = strategy.Phase;
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = strategy.Fill(context); var result = strategy.Fill(context);
+6 -4
View File
@@ -2,13 +2,15 @@
namespace OpenNest namespace OpenNest
{ {
internal record CombinationResult(bool Found, int Count1, int Count2);
internal static class BestCombination internal static class BestCombination
{ {
public static bool FindFrom2(double length1, double length2, double overallLength, out int count1, out int count2) public static CombinationResult FindFrom2(double length1, double length2, double overallLength)
{ {
overallLength += Tolerance.Epsilon; overallLength += Tolerance.Epsilon;
count1 = 0; var count1 = 0;
count2 = 0; var count2 = 0;
var maxCount1 = (int)System.Math.Floor(overallLength / length1); var maxCount1 = (int)System.Math.Floor(overallLength / length1);
var bestRemnant = overallLength + 1; var bestRemnant = overallLength + 1;
@@ -30,7 +32,7 @@ namespace OpenNest
break; break;
} }
return count1 > 0 || count2 > 0; return new CombinationResult(count1 > 0 || count2 > 0, count1, count2);
} }
} }
} }
+8 -31
View File
@@ -24,10 +24,8 @@ namespace OpenNest.Engine.Fill
} }
public List<Part> Fill(Drawing drawing, double rotationAngle = 0, public List<Part> Fill(Drawing drawing, double rotationAngle = 0,
int plateNumber = 0,
CancellationToken token = default, CancellationToken token = default,
IProgress<NestProgress> progress = null, Action<List<Part>, string> reportProgress = null)
List<Engine.BestFit.BestFitResult> bestFits = null)
{ {
var pair = BuildPair(drawing, rotationAngle); var pair = BuildPair(drawing, rotationAngle);
if (pair == null) if (pair == null)
@@ -37,14 +35,7 @@ namespace OpenNest.Engine.Fill
if (column.Count == 0) if (column.Count == 0)
return new List<Part>(); return new List<Part>();
NestEngineBase.ReportProgress(progress, new ProgressReport reportProgress?.Invoke(column, $"Extents: initial column {column.Count} parts");
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = column,
WorkArea = workArea,
Description = $"Extents: initial column {column.Count} parts",
});
var adjusted = AdjustColumn(pair.Value, column, token); var adjusted = AdjustColumn(pair.Value, column, token);
@@ -56,25 +47,11 @@ namespace OpenNest.Engine.Fill
adjusted = column; adjusted = column;
} }
NestEngineBase.ReportProgress(progress, new ProgressReport reportProgress?.Invoke(adjusted, $"Extents: column {adjusted.Count} parts");
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = adjusted,
WorkArea = workArea,
Description = $"Extents: column {adjusted.Count} parts",
});
var result = RepeatColumns(adjusted, token); var result = RepeatColumns(adjusted, token);
NestEngineBase.ReportProgress(progress, new ProgressReport reportProgress?.Invoke(result, $"Extents: {result.Count} parts total");
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = result,
WorkArea = workArea,
Description = $"Extents: {result.Count} parts total",
});
return result; return result;
} }
@@ -96,7 +73,7 @@ namespace OpenNest.Engine.Fill
var boundary2 = new PartBoundary(part2, halfSpacing); var boundary2 = new PartBoundary(part2, halfSpacing);
// Position part2 to the right of part1 at bounding box width distance. // Position part2 to the right of part1 at bounding box width distance.
var startOffset = part1.BoundingBox.Width + part2.BoundingBox.Width + partSpacing; var startOffset = part1.BoundingBox.Length + part2.BoundingBox.Length + partSpacing;
part2.Offset(startOffset, 0); part2.Offset(startOffset, 0);
part2.UpdateBounds(); part2.UpdateBounds();
@@ -135,7 +112,7 @@ namespace OpenNest.Engine.Fill
// Compute vertical copy distance using bounding boxes as starting point, // Compute vertical copy distance using bounding boxes as starting point,
// then slide down to find true geometry distance. // then slide down to find true geometry distance.
var pairHeight = pair.Bbox.Length; var pairHeight = pair.Bbox.Width;
var testOffset = new Vector(0, pairHeight); var testOffset = new Vector(0, pairHeight);
// Create test parts for slide distance measurement. // Create test parts for slide distance measurement.
@@ -218,7 +195,7 @@ namespace OpenNest.Engine.Fill
private List<Part> AdjustColumn(PartPair pair, List<Part> column, CancellationToken token) private List<Part> AdjustColumn(PartPair pair, List<Part> column, CancellationToken token)
{ {
var originalPairWidth = pair.Bbox.Width; var originalPairWidth = pair.Bbox.Length;
for (var iteration = 0; iteration < MaxIterations; iteration++) for (var iteration = 0; iteration < MaxIterations; iteration++)
{ {
@@ -294,7 +271,7 @@ namespace OpenNest.Engine.Fill
// Check if the pair got wider. // Check if the pair got wider.
var newBbox = PairBbox(p1, p2); var newBbox = PairBbox(p1, p2);
if (newBbox.Width > originalPairWidth + Tolerance.Epsilon) if (newBbox.Length > originalPairWidth + Tolerance.Epsilon)
return null; return null;
return AnchorToWorkArea(p1, p2); return AnchorToWorkArea(p1, p2);
+2 -2
View File
@@ -11,7 +11,7 @@ namespace OpenNest.Engine.Fill
public FillLinear(Box workArea, double partSpacing) public FillLinear(Box workArea, double partSpacing)
{ {
PartSpacing = partSpacing; PartSpacing = partSpacing;
WorkArea = new Box(workArea.X, workArea.Y, workArea.Width, workArea.Length); WorkArea = new Box(workArea.X, workArea.Y, workArea.Length, workArea.Width);
} }
public Box WorkArea { get; } public Box WorkArea { get; }
@@ -41,7 +41,7 @@ namespace OpenNest.Engine.Fill
private static double GetDimension(Box box, NestDirection direction) private static double GetDimension(Box box, NestDirection direction)
{ {
return direction == NestDirection.Horizontal ? box.Width : box.Length; return direction == NestDirection.Horizontal ? box.Length : box.Width;
} }
private static double GetStart(Box box, NestDirection direction) private static double GetStart(Box box, NestDirection direction)
+11 -18
View File
@@ -45,9 +45,8 @@ namespace OpenNest.Engine.Fill
} }
public PairFillResult Fill(NestItem item, Box workArea, public PairFillResult Fill(NestItem item, Box workArea,
int plateNumber = 0,
CancellationToken token = default, CancellationToken token = default,
IProgress<NestProgress> progress = null) Action<List<Part>, string> reportProgress = null)
{ {
var bestFits = BestFitCache.GetOrCompute( var bestFits = BestFitCache.GetOrCompute(
item.Drawing, plateSize.Length, plateSize.Width, partSpacing); item.Drawing, plateSize.Length, plateSize.Width, partSpacing);
@@ -58,7 +57,7 @@ namespace OpenNest.Engine.Fill
var targetCount = item.Quantity > 0 ? item.Quantity : 0; var targetCount = item.Quantity > 0 ? item.Quantity : 0;
var parts = EvaluateCandidates(candidates, item.Drawing, workArea, targetCount, var parts = EvaluateCandidates(candidates, item.Drawing, workArea, targetCount,
plateNumber, token, progress); token, reportProgress);
return new PairFillResult { Parts = parts, BestFits = bestFits }; return new PairFillResult { Parts = parts, BestFits = bestFits };
} }
@@ -66,7 +65,7 @@ namespace OpenNest.Engine.Fill
private List<Part> EvaluateCandidates( private List<Part> EvaluateCandidates(
List<BestFitResult> candidates, Drawing drawing, List<BestFitResult> candidates, Drawing drawing,
Box workArea, int targetCount, Box workArea, int targetCount,
int plateNumber, CancellationToken token, IProgress<NestProgress> progress) CancellationToken token, Action<List<Part>, string> reportProgress)
{ {
List<Part> best = null; List<Part> best = null;
var sinceImproved = 0; var sinceImproved = 0;
@@ -112,14 +111,8 @@ namespace OpenNest.Engine.Fill
sinceImproved++; sinceImproved++;
} }
NestEngineBase.ReportProgress(progress, new ProgressReport reportProgress?.Invoke(best,
{ $"Pairs: {batchStart + j + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts");
Phase = NestPhase.Pairs,
PlateNumber = plateNumber,
Parts = best,
WorkArea = workArea,
Description = $"Pairs: {batchStart + j + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts",
});
} }
if (batchEnd >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit) if (batchEnd >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
@@ -175,8 +168,8 @@ namespace OpenNest.Engine.Fill
var newTop = remaining.Max(p => p.BoundingBox.Top); var newTop = remaining.Max(p => p.BoundingBox.Top);
return new Box(workArea.X, workArea.Y, return new Box(workArea.X, workArea.Y,
workArea.Width, workArea.Length,
System.Math.Min(newTop - workArea.Y, workArea.Length)); System.Math.Min(newTop - workArea.Y, workArea.Width));
} }
private List<Part> EvaluateCandidate(BestFitResult candidate, Drawing drawing, private List<Part> EvaluateCandidate(BestFitResult candidate, Drawing drawing,
@@ -271,8 +264,8 @@ namespace OpenNest.Engine.Fill
var topHeight = System.Math.Max(0, workArea.Top - gridBox.Top); var topHeight = System.Math.Max(0, workArea.Top - gridBox.Top);
var rightWidth = System.Math.Max(0, workArea.Right - gridBox.Right); var rightWidth = System.Math.Max(0, workArea.Right - gridBox.Right);
var topArea = workArea.Width * topHeight; var topArea = workArea.Length * topHeight;
var rightArea = rightWidth * System.Math.Min(gridBox.Top - workArea.Y, workArea.Length); var rightArea = rightWidth * System.Math.Min(gridBox.Top - workArea.Y, workArea.Width);
var remnantArea = topArea + rightArea; var remnantArea = topArea + rightArea;
return (int)(remnantArea * maxUtilization / partArea) + 1; return (int)(remnantArea * maxUtilization / partArea) + 1;
@@ -292,7 +285,7 @@ namespace OpenNest.Engine.Fill
var topLength = workArea.Top - topY; var topLength = workArea.Top - topY;
if (topLength >= minDim) if (topLength >= minDim)
{ {
var topBox = new Box(workArea.X, topY, workArea.Width, topLength); var topBox = new Box(workArea.X, topY, workArea.Length, topLength);
var parts = FillRemnantBox(drawing, topBox, token); var parts = FillRemnantBox(drawing, topBox, token);
if (parts != null && parts.Count > (bestRemnant?.Count ?? 0)) if (parts != null && parts.Count > (bestRemnant?.Count ?? 0))
bestRemnant = parts; bestRemnant = parts;
@@ -303,7 +296,7 @@ namespace OpenNest.Engine.Fill
var rightWidth = workArea.Right - rightX; var rightWidth = workArea.Right - rightX;
if (rightWidth >= minDim) if (rightWidth >= minDim)
{ {
var rightBox = new Box(rightX, workArea.Y, rightWidth, workArea.Length); var rightBox = new Box(rightX, workArea.Y, rightWidth, workArea.Width);
var parts = FillRemnantBox(drawing, rightBox, token); var parts = FillRemnantBox(drawing, rightBox, token);
if (parts != null && parts.Count > (bestRemnant?.Count ?? 0)) if (parts != null && parts.Count > (bestRemnant?.Count ?? 0))
bestRemnant = parts; bestRemnant = parts;
+1 -1
View File
@@ -24,7 +24,7 @@ namespace OpenNest.Engine.Fill
public PartBoundary(Part part, double spacing) public PartBoundary(Part part, double spacing)
{ {
var entities = ConvertProgram.ToGeometry(part.Program) var entities = ConvertProgram.ToGeometry(part.Program)
.Where(e => e.Layer != SpecialLayers.Rapid) .Where(e => e.Layer == SpecialLayers.Cut)
.ToList(); .ToList();
var definedShape = new ShapeProfile(entities); var definedShape = new ShapeProfile(entities);
+7 -7
View File
@@ -13,15 +13,15 @@ namespace OpenNest.Engine.Fill
var cellBox = cell.GetBoundingBox(); var cellBox = cell.GetBoundingBox();
var halfSpacing = partSpacing / 2; var halfSpacing = partSpacing / 2;
var cellWidth = cellBox.Width + partSpacing; var cellW = cellBox.Width + partSpacing;
var cellHeight = cellBox.Length + partSpacing; var cellL = cellBox.Length + partSpacing;
if (cellWidth <= 0 || cellHeight <= 0) if (cellW <= 0 || cellL <= 0)
return new List<Part>(); return new List<Part>();
// Size.Width = X-axis, Size.Length = Y-axis // Width = Y axis, Length = X axis
var cols = (int)System.Math.Floor(plateSize.Width / cellWidth); var cols = (int)System.Math.Floor(plateSize.Length / cellL);
var rows = (int)System.Math.Floor(plateSize.Length / cellHeight); var rows = (int)System.Math.Floor(plateSize.Width / cellW);
if (cols <= 0 || rows <= 0) if (cols <= 0 || rows <= 0)
return new List<Part>(); return new List<Part>();
@@ -37,7 +37,7 @@ namespace OpenNest.Engine.Fill
{ {
for (var col = 0; col < cols; col++) for (var col = 0; col < cols; col++)
{ {
var tileOffset = baseOffset + new Vector(col * cellWidth, row * cellHeight); var tileOffset = baseOffset + new Vector(col * cellL, row * cellW);
foreach (var part in cell) foreach (var part in cell)
{ {
+2 -2
View File
@@ -304,10 +304,10 @@ namespace OpenNest.Engine.Fill
// Edge extensions (priority 1). // Edge extensions (priority 1).
if (remnant.Right > envelope.Right + eps) if (remnant.Right > envelope.Right + eps)
TryAdd(results, envelope.Right, remnant.Bottom, remnant.Right - envelope.Right, remnant.Length, 1, minDim); TryAdd(results, envelope.Right, remnant.Bottom, remnant.Right - envelope.Right, remnant.Width, 1, minDim);
if (remnant.Left < envelope.Left - eps) if (remnant.Left < envelope.Left - eps)
TryAdd(results, remnant.Left, remnant.Bottom, envelope.Left - remnant.Left, remnant.Length, 1, minDim); TryAdd(results, remnant.Left, remnant.Bottom, envelope.Left - remnant.Left, remnant.Width, 1, minDim);
if (remnant.Top > envelope.Top + eps) if (remnant.Top > envelope.Top + eps)
TryAdd(results, innerLeft, envelope.Top, innerRight - innerLeft, remnant.Top - envelope.Top, 1, minDim); TryAdd(results, innerLeft, envelope.Top, innerRight - innerLeft, remnant.Top - envelope.Top, 1, minDim);
+11 -17
View File
@@ -95,14 +95,8 @@ public class StripeFiller
} }
} }
NestEngineBase.ReportProgress(_context.Progress, new ProgressReport _context.ReportProgress(bestParts,
{ $"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts");
Phase = NestPhase.Custom,
PlateNumber = _context.PlateNumber,
Parts = bestParts,
WorkArea = workArea,
Description = $"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts",
});
} }
return bestParts ?? new List<Part>(); return bestParts ?? new List<Part>();
@@ -201,8 +195,8 @@ public class StripeFiller
private static Box MakeStripeBox(Box workArea, double perpDim, NestDirection primaryAxis) private static Box MakeStripeBox(Box workArea, double perpDim, NestDirection primaryAxis)
{ {
return primaryAxis == NestDirection.Horizontal return primaryAxis == NestDirection.Horizontal
? new Box(workArea.X, workArea.Y, workArea.Width, perpDim) ? new Box(workArea.X, workArea.Y, workArea.Length, perpDim)
: new Box(workArea.X, workArea.Y, perpDim, workArea.Length); : new Box(workArea.X, workArea.Y, perpDim, workArea.Width);
} }
private List<Part> FillRemnant(List<Part> gridParts, NestDirection primaryAxis) private List<Part> FillRemnant(List<Part> gridParts, NestDirection primaryAxis)
@@ -224,7 +218,7 @@ public class StripeFiller
var remnantLength = workArea.Top - remnantY; var remnantLength = workArea.Top - remnantY;
if (remnantLength < minDim) if (remnantLength < minDim)
return null; return null;
remnantBox = new Box(workArea.X, remnantY, workArea.Width, remnantLength); remnantBox = new Box(workArea.X, remnantY, workArea.Length, remnantLength);
} }
else else
{ {
@@ -232,7 +226,7 @@ public class StripeFiller
var remnantWidth = workArea.Right - remnantX; var remnantWidth = workArea.Right - remnantX;
if (remnantWidth < minDim) if (remnantWidth < minDim)
return null; return null;
remnantBox = new Box(remnantX, workArea.Y, remnantWidth, workArea.Length); remnantBox = new Box(remnantX, workArea.Y, remnantWidth, workArea.Width);
} }
Debug.WriteLine($"[StripeFiller] Remnant box: {remnantBox.Width:F2}x{remnantBox.Length:F2}"); Debug.WriteLine($"[StripeFiller] Remnant box: {remnantBox.Width:F2}x{remnantBox.Length:F2}");
@@ -324,7 +318,7 @@ public class StripeFiller
{ {
var box = FillHelpers.BuildRotatedPattern(patternParts, 0).BoundingBox; var box = FillHelpers.BuildRotatedPattern(patternParts, 0).BoundingBox;
var span0 = GetDimension(box, axis); var span0 = GetDimension(box, axis);
var perpSpan0 = axis == NestDirection.Horizontal ? box.Length : box.Width; var perpSpan0 = axis == NestDirection.Horizontal ? box.Width : box.Length;
if (span0 <= perpSpan0) if (span0 <= perpSpan0)
return 0; return 0;
@@ -388,7 +382,7 @@ public class StripeFiller
var rotated = FillHelpers.BuildRotatedPattern(patternParts, currentAngle); var rotated = FillHelpers.BuildRotatedPattern(patternParts, currentAngle);
var pairSpan = GetDimension(rotated.BoundingBox, axis); var pairSpan = GetDimension(rotated.BoundingBox, axis);
var perpDim = axis == NestDirection.Horizontal var perpDim = axis == NestDirection.Horizontal
? rotated.BoundingBox.Length : rotated.BoundingBox.Width; ? rotated.BoundingBox.Width : rotated.BoundingBox.Length;
if (pairSpan + spacing <= 0) if (pairSpan + spacing <= 0)
break; break;
@@ -472,13 +466,13 @@ public class StripeFiller
{ {
var rotated = FillHelpers.BuildRotatedPattern(patternParts, angle); var rotated = FillHelpers.BuildRotatedPattern(patternParts, angle);
return axis == NestDirection.Horizontal return axis == NestDirection.Horizontal
? rotated.BoundingBox.Width ? rotated.BoundingBox.Length
: rotated.BoundingBox.Length; : rotated.BoundingBox.Width;
} }
private static double GetDimension(Box box, NestDirection axis) private static double GetDimension(Box box, NestDirection axis)
{ {
return axis == NestDirection.Horizontal ? box.Width : box.Length; return axis == NestDirection.Horizontal ? box.Length : box.Width;
} }
private static bool HasOverlappingParts(List<Part> parts) => private static bool HasOverlappingParts(List<Part> parts) =>
+1 -1
View File
@@ -38,7 +38,7 @@ namespace OpenNest
var bb = item.Drawing.Program.BoundingBox(); var bb = item.Drawing.Program.BoundingBox();
var cos = System.Math.Abs(System.Math.Cos(angle)); var cos = System.Math.Abs(System.Math.Cos(angle));
var sin = System.Math.Abs(System.Math.Sin(angle)); var sin = System.Math.Abs(System.Math.Sin(angle));
return bb.Length * cos + bb.Width * sin; return bb.Width * cos + bb.Length * sin;
} }
} }
} }
+3 -3
View File
@@ -47,7 +47,7 @@ namespace OpenNest.Engine.ML
{ {
Area = drawing.Area, Area = drawing.Area,
Convexity = drawing.Area / (hullArea > 0 ? hullArea : 1.0), Convexity = drawing.Area / (hullArea > 0 ? hullArea : 1.0),
AspectRatio = bb.Width / (bb.Length > 0 ? bb.Length : 1.0), AspectRatio = bb.Length / (bb.Width > 0 ? bb.Width : 1.0),
BoundingBoxFill = drawing.Area / (bb.Area() > 0 ? bb.Area() : 1.0), BoundingBoxFill = drawing.Area / (bb.Area() > 0 ? bb.Area() : 1.0),
VertexCount = polygon.Vertices.Count, VertexCount = polygon.Vertices.Count,
Bitmask = GenerateBitmask(polygon, 32) Bitmask = GenerateBitmask(polygon, 32)
@@ -72,8 +72,8 @@ namespace OpenNest.Engine.ML
for (int x = 0; x < size; x++) for (int x = 0; x < size; x++)
{ {
// Map grid coordinate (0..size) to bounding box coordinate // Map grid coordinate (0..size) to bounding box coordinate
var px = bb.Left + (x + 0.5) * (bb.Width / size); var px = bb.Left + (x + 0.5) * (bb.Length / size);
var py = bb.Bottom + (y + 0.5) * (bb.Length / size); var py = bb.Bottom + (y + 0.5) * (bb.Width / size);
if (polygon.ContainsPoint(new Vector(px, py))) if (polygon.ContainsPoint(new Vector(px, py)))
{ {
@@ -29,11 +29,15 @@ namespace OpenNest.RectanglePacking
Bin.Items.AddRange(bin1.Items); Bin.Items.AddRange(bin1.Items);
else else
Bin.Items.AddRange(bin2.Items); Bin.Items.AddRange(bin2.Items);
} }
public override void Fill(Item item, int maxCount) public override void Fill(Item item, int maxCount)
{ {
throw new NotImplementedException(); Fill(item);
if (Bin.Items.Count > maxCount)
Bin.Items.RemoveRange(maxCount, Bin.Items.Count - maxCount);
} }
private Bin BestFitHorizontal(Item item) => BestFitAxis(item, horizontal: true); private Bin BestFitHorizontal(Item item) => BestFitAxis(item, horizontal: true);
@@ -44,14 +48,18 @@ namespace OpenNest.RectanglePacking
{ {
var bin = Bin.Clone() as Bin; var bin = Bin.Clone() as Bin;
var primarySize = horizontal ? item.Width : item.Length; var primarySize = horizontal ? item.Length : item.Width;
var secondarySize = horizontal ? item.Length : item.Width; var secondarySize = horizontal ? item.Width : item.Length;
var binPrimary = horizontal ? bin.Width : Bin.Length; var binPrimary = horizontal ? bin.Length : Bin.Width;
var binSecondary = horizontal ? bin.Length : Bin.Width; var binSecondary = horizontal ? bin.Width : Bin.Length;
if (!BestCombination.FindFrom2(primarySize, secondarySize, binPrimary, out var normalPrimary, out var rotatePrimary)) var combo = BestCombination.FindFrom2(primarySize, secondarySize, binPrimary);
if (!combo.Found)
return bin; return bin;
var normalPrimary = combo.Count1;
var rotatePrimary = combo.Count2;
var normalSecondary = (int)System.Math.Floor((binSecondary + Tolerance.Epsilon) / secondarySize); var normalSecondary = (int)System.Math.Floor((binSecondary + Tolerance.Epsilon) / secondarySize);
var rotateSecondary = (int)System.Math.Floor((binSecondary + Tolerance.Epsilon) / primarySize); var rotateSecondary = (int)System.Math.Floor((binSecondary + Tolerance.Epsilon) / primarySize);
@@ -67,9 +75,9 @@ namespace OpenNest.RectanglePacking
bin.Items.AddRange(FillGrid(item, normalRows, normalCols, int.MaxValue)); bin.Items.AddRange(FillGrid(item, normalRows, normalCols, int.MaxValue));
if (horizontal) if (horizontal)
item.Location.X += item.Width * normalPrimary; item.Location.X += item.Length * normalPrimary;
else else
item.Location.Y += item.Length * normalPrimary; item.Location.Y += item.Width * normalPrimary;
item.Rotate(); item.Rotate();
@@ -27,8 +27,8 @@ namespace OpenNest.RectanglePacking
{ {
for (var j = 0; j < innerCount; j++) for (var j = 0; j < innerCount; j++)
{ {
var x = (columnMajor ? i : j) * item.Width + item.X; var x = (columnMajor ? i : j) * item.Length + item.X;
var y = (columnMajor ? j : i) * item.Length + item.Y; var y = (columnMajor ? j : i) * item.Width + item.Y;
var clone = item.Clone() as Item; var clone = item.Clone() as Item;
clone.Location = new Vector(x, y); clone.Location = new Vector(x, y);
@@ -14,16 +14,16 @@ namespace OpenNest.RectanglePacking
public override void Fill(Item item) public override void Fill(Item item)
{ {
var ycount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length); var ycount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width);
var xcount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width); var xcount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length);
for (int i = 0; i < xcount; i++) for (int i = 0; i < xcount; i++)
{ {
var x = item.Width * i + Bin.X; var x = item.Length * i + Bin.X;
for (int j = 0; j < ycount; j++) for (int j = 0; j < ycount; j++)
{ {
var y = item.Length * j + Bin.Y; var y = item.Width * j + Bin.Y;
var addedItem = item.Clone() as Item; var addedItem = item.Clone() as Item;
addedItem.Location = new Vector(x, y); addedItem.Location = new Vector(x, y);
@@ -35,8 +35,8 @@ namespace OpenNest.RectanglePacking
public override void Fill(Item item, int maxCount) public override void Fill(Item item, int maxCount)
{ {
var ycount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length); var ycount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width);
var xcount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width); var xcount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length);
var count = ycount * xcount; var count = ycount * xcount;
if (count <= maxCount) if (count <= maxCount)
@@ -0,0 +1,83 @@
using OpenNest.Geometry;
using OpenNest.Math;
namespace OpenNest.RectanglePacking
{
internal class FillSpiral : FillEngine
{
public Box CenterRemnant { get; private set; }
public FillSpiral(Bin bin)
: base(bin)
{
}
public override void Fill(Item item)
{
Fill(item, int.MaxValue);
}
public override void Fill(Item item, int maxCount)
{
if (item == null) return;
// Width = Y axis, Length = X axis
var comboY = BestCombination.FindFrom2(item.Width, item.Length, Bin.Width);
var comboX = BestCombination.FindFrom2(item.Length, item.Width, Bin.Length);
if (!comboY.Found || !comboX.Found)
return;
var q14size = new Size(
item.Width * comboY.Count1,
item.Length * comboX.Count1);
var q23size = new Size(
item.Length * comboY.Count2,
item.Width * comboX.Count2);
if ((q14size.Width > q23size.Width && q14size.Length > q23size.Length) ||
(q23size.Width > q14size.Width && q23size.Length > q14size.Length))
return; // cant do an efficient spiral fill
// Q1: normal orientation at bin origin
item.Location = Bin.Location;
var q1 = FillGrid(item, comboY.Count1, comboX.Count1, maxCount);
Bin.Items.AddRange(q1);
// Q2: rotated, above Q1
item.Rotate();
item.Location = new Vector(Bin.X, Bin.Y + q14size.Width);
var q2 = FillGrid(item, comboY.Count2, comboX.Count2, maxCount - Bin.Items.Count);
Bin.Items.AddRange(q2);
// Q3: rotated, right of Q1
item.Location = new Vector(Bin.X + q14size.Length, Bin.Y);
var q3 = FillGrid(item, comboY.Count2, comboX.Count2, maxCount - Bin.Items.Count);
Bin.Items.AddRange(q3);
// Q4: normal orientation, diagonal from Q1
item.Rotate();
item.Location = new Vector(
Bin.X + q23size.Length,
Bin.Y + q23size.Width);
var q4 = FillGrid(item, comboY.Count1, comboX.Count1, maxCount);
Bin.Items.AddRange(q4);
// Compute center remnant — the rectangular gap between the 4 quadrants
// Only valid when all 4 quadrants have items; otherwise the "center"
// overlaps an occupied quadrant and recursion never terminates.
var centerW = System.Math.Abs(q14size.Length - q23size.Length);
var centerH = System.Math.Abs(q14size.Width - q23size.Width);
if (comboY.Count1 > 0 && comboY.Count2 > 0 && comboX.Count1 > 0 && comboX.Count2 > 0
&& centerW > Tolerance.Epsilon && centerH > Tolerance.Epsilon)
{
CenterRemnant = new Box(
Bin.X + System.Math.Min(q14size.Length, q23size.Length),
Bin.Y + System.Math.Min(q14size.Width, q23size.Width),
centerW,
centerH);
}
}
}
}
+2 -2
View File
@@ -37,8 +37,8 @@ namespace OpenNest.RectanglePacking
double minX = items[0].X; double minX = items[0].X;
double minY = items[0].Y; double minY = items[0].Y;
double maxX = items[0].X + items[0].Width; double maxX = items[0].Right;
double maxY = items[0].Y + items[0].Length; double maxY = items[0].Top;
foreach (var box in items) foreach (var box in items)
{ {
@@ -16,11 +16,11 @@ namespace OpenNest.RectanglePacking
public override void Pack(List<Item> items) public override void Pack(List<Item> items)
{ {
items = items.OrderBy(i => -i.Length).ToList(); items = items.OrderBy(i => -i.Width).ToList();
foreach (var item in items) foreach (var item in items)
{ {
if (item.Length > Bin.Length) if (item.Width > Bin.Width)
continue; continue;
var level = FindLevel(item); var level = FindLevel(item);
@@ -36,10 +36,10 @@ namespace OpenNest.RectanglePacking
{ {
foreach (var level in levels) foreach (var level in levels)
{ {
if (level.Height < item.Length) if (level.Height < item.Width)
continue; continue;
if (level.RemainingWidth < item.Width) if (level.RemainingLength < item.Length)
continue; continue;
return level; return level;
@@ -58,12 +58,12 @@ namespace OpenNest.RectanglePacking
var remaining = Bin.Top - y; var remaining = Bin.Top - y;
if (remaining < item.Length) if (remaining < item.Width)
return null; return null;
var level = new Level(Bin); var level = new Level(Bin);
level.Y = y; level.Y = y;
level.Height = item.Length; level.Height = item.Width;
levels.Add(level); levels.Add(level);
@@ -93,9 +93,9 @@ namespace OpenNest.RectanglePacking
set { NextItemLocation.Y = value; } set { NextItemLocation.Y = value; }
} }
public double Width public double LevelLength
{ {
get { return Parent.Width; } get { return Parent.Length; }
} }
public double Height { get; set; } public double Height { get; set; }
@@ -105,9 +105,9 @@ namespace OpenNest.RectanglePacking
get { return Y + Height; } get { return Y + Height; }
} }
public double RemainingWidth public double RemainingLength
{ {
get { return X + Width - NextItemLocation.X; } get { return X + LevelLength - NextItemLocation.X; }
} }
public void AddItem(Item item) public void AddItem(Item item)
@@ -115,7 +115,7 @@ namespace OpenNest.RectanglePacking
item.Location = NextItemLocation; item.Location = NextItemLocation;
Parent.Items.Add(item); Parent.Items.Add(item);
NextItemLocation = new Vector(NextItemLocation.X + item.Width, NextItemLocation.Y); NextItemLocation = new Vector(NextItemLocation.X + item.Length, NextItemLocation.Y);
} }
} }
} }
@@ -0,0 +1,44 @@
using OpenNest.Geometry;
namespace OpenNest.RectanglePacking
{
internal static class RectFill
{
public static void FillBest(Bin bin, Item item, int maxCount = int.MaxValue)
{
var spiralBin = bin.Clone() as Bin;
var spiral = new FillSpiral(spiralBin);
spiral.Fill(item, maxCount);
// Recursively fill the center remnant of the spiral
if (spiralBin.Items.Count > 0 && spiral.CenterRemnant != null)
{
var center = spiral.CenterRemnant;
var fitsNormal = item.Length <= center.Length && item.Width <= center.Width;
var fitsRotated = item.Width <= center.Length && item.Length <= center.Width;
if (fitsNormal || fitsRotated)
{
var remaining = maxCount - spiralBin.Items.Count;
FillBest(center.Location, center.Size, spiralBin, item, remaining);
}
}
var bestFitBin = bin.Clone() as Bin;
new FillBestFit(bestFitBin).Fill(item, maxCount);
var winner = spiralBin.Items.Count >= bestFitBin.Items.Count ? spiralBin : bestFitBin;
bin.Items.AddRange(winner.Items);
}
public static void FillBest(Vector location, Size size, Bin target, Item item, int maxCount)
{
if (size.Width <= 0 || size.Length <= 0 || maxCount <= 0)
return;
var bin = new Bin { Location = location, Size = size };
FillBest(bin, item, maxCount);
target.Items.AddRange(bin.Items);
}
}
}
@@ -24,8 +24,8 @@ namespace OpenNest.Engine.Strategies
return FillHelpers.BestOverAngles(context, angles, return FillHelpers.BestOverAngles(context, angles,
angle => filler.Fill(context.Item.Drawing, angle, angle => filler.Fill(context.Item.Drawing, angle,
context.PlateNumber, context.Token, context.Progress), context.Token, context.ReportProgress),
NestPhase.Extents, "Extents"); "Extents");
} }
} }
} }
+17
View File
@@ -23,9 +23,26 @@ namespace OpenNest.Engine.Strategies
/// <summary>For progress reporting only; comparisons use Policy.Comparer.</summary> /// <summary>For progress reporting only; comparisons use Policy.Comparer.</summary>
public FillScore CurrentBestScore { get; set; } public FillScore CurrentBestScore { get; set; }
public NestPhase WinnerPhase { get; set; } public NestPhase WinnerPhase { get; set; }
public NestPhase ActivePhase { get; set; }
public List<PhaseResult> PhaseResults { get; } = new(); public List<PhaseResult> PhaseResults { get; } = new();
public List<AngleResult> AngleResults { get; } = new(); public List<AngleResult> AngleResults { get; } = new();
public Dictionary<string, object> SharedState { get; } = new(); public Dictionary<string, object> SharedState { get; } = new();
/// <summary>
/// Standard progress reporting for strategies and fillers. Reports intermediate
/// results using the current ActivePhase, PlateNumber, and WorkArea.
/// </summary>
public void ReportProgress(List<Part> parts, string description)
{
NestEngineBase.ReportProgress(Progress, new ProgressReport
{
Phase = ActivePhase,
PlateNumber = PlateNumber,
Parts = parts,
WorkArea = WorkArea,
Description = description,
});
}
} }
} }
+3 -10
View File
@@ -113,13 +113,12 @@ namespace OpenNest.Engine.Strategies
/// <summary> /// <summary>
/// Sweeps a list of angles, calling fillAtAngle for each, and returns /// Sweeps a list of angles, calling fillAtAngle for each, and returns
/// the best result according to the context's comparer. Handles /// the best result according to the context's comparer. Handles
/// cancellation and progress reporting. /// cancellation and progress reporting via context.ReportProgress.
/// </summary> /// </summary>
public static List<Part> BestOverAngles( public static List<Part> BestOverAngles(
FillContext context, FillContext context,
IReadOnlyList<double> angles, IReadOnlyList<double> angles,
Func<double, List<Part>> fillAtAngle, Func<double, List<Part>> fillAtAngle,
NestPhase phase,
string phaseLabel) string phaseLabel)
{ {
var workArea = context.WorkArea; var workArea = context.WorkArea;
@@ -140,14 +139,8 @@ namespace OpenNest.Engine.Strategies
best = result; best = result;
} }
NestEngineBase.ReportProgress(context.Progress, new ProgressReport context.ReportProgress(best,
{ $"{phaseLabel}: {i + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts");
Phase = phase,
PlateNumber = context.PlateNumber,
Parts = best,
WorkArea = workArea,
Description = $"{phaseLabel}: {i + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts",
});
} }
return best ?? new List<Part>(); return best ?? new List<Part>();
@@ -40,7 +40,7 @@ namespace OpenNest.Engine.Strategies
return result; return result;
}, },
NestPhase.Linear, "Linear"); "Linear");
} }
} }
} }
@@ -30,7 +30,7 @@ namespace OpenNest.Engine.Strategies
var dedup = GridDedup.GetOrCreate(context.SharedState); var dedup = GridDedup.GetOrCreate(context.SharedState);
var filler = new PairFiller(context.Plate, comparer, dedup); var filler = new PairFiller(context.Plate, comparer, dedup);
var result = filler.Fill(context.Item, context.WorkArea, var result = filler.Fill(context.Item, context.WorkArea,
context.PlateNumber, context.Token, context.Progress); context.Token, context.ReportProgress);
context.SharedState["BestFits"] = result.BestFits; context.SharedState["BestFits"] = result.BestFits;
@@ -14,8 +14,7 @@ namespace OpenNest.Engine.Strategies
var binItem = BinConverter.ToItem(context.Item, context.Plate.PartSpacing); var binItem = BinConverter.ToItem(context.Item, context.Plate.PartSpacing);
var bin = BinConverter.CreateBin(context.WorkArea, context.Plate.PartSpacing); var bin = BinConverter.CreateBin(context.WorkArea, context.Plate.PartSpacing);
var engine = new FillBestFit(bin); RectFill.FillBest(bin, binItem);
engine.Fill(binItem);
return BinConverter.ToParts(bin, new List<NestItem> { context.Item }); return BinConverter.ToParts(bin, new List<NestItem> { context.Item });
} }
+1 -1
View File
@@ -36,7 +36,7 @@ namespace OpenNest
var bb = item.Drawing.Program.BoundingBox(); var bb = item.Drawing.Program.BoundingBox();
var cos = System.Math.Abs(System.Math.Cos(angle)); var cos = System.Math.Abs(System.Math.Cos(angle));
var sin = System.Math.Abs(System.Math.Sin(angle)); var sin = System.Math.Abs(System.Math.Sin(angle));
return bb.Width * cos + bb.Length * sin; return bb.Length * cos + bb.Width * sin;
} }
} }
} }
@@ -0,0 +1,70 @@
using OpenNest.CNC;
using OpenNest.Converters;
using OpenNest.Engine.BestFit;
using OpenNest.Geometry;
using OpenNest.IO;
using Xunit.Abstractions;
namespace OpenNest.Tests.BestFit;
public class BestFitOverlapTests
{
private const string DxfPath = @"C:\Users\AJ\Desktop\Templates\4526 A14 PT16.dxf";
private readonly ITestOutputHelper _output;
public BestFitOverlapTests(ITestOutputHelper output) => _output = output;
private static Drawing MakeRoundedRect(double w = 7.25, double h = 3.31, double r = 0.5)
{
var pgm = new Program();
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
pgm.Codes.Add(new LinearMove(new Vector(w - r, 0)));
pgm.Codes.Add(new ArcMove(new Vector(w, r), new Vector(w - r, r), RotationType.CW));
pgm.Codes.Add(new LinearMove(new Vector(w, h - r)));
pgm.Codes.Add(new ArcMove(new Vector(w - r, h), new Vector(w - r, h - r), RotationType.CW));
pgm.Codes.Add(new LinearMove(new Vector(0, h)));
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
return new Drawing("rounded-rect", pgm);
}
private static Drawing ImportDxf()
{
if (!File.Exists(DxfPath))
return null;
var importer = new DxfImporter();
importer.GetGeometry(DxfPath, out var geometry);
var pgm = ConvertGeometry.ToProgram(geometry);
return new Drawing("PT16", pgm);
}
[Fact]
public void KeptPairs_NoOverlap()
{
var drawing = ImportDxf() ?? MakeRoundedRect();
var bbox = drawing.Program.BoundingBox();
_output.WriteLine($"Drawing: {drawing.Name}, bbox={bbox.Length:F2} x {bbox.Width:F2}");
var finder = new BestFitFinder(120, 60);
var results = finder.FindBestFits(drawing);
var kept = results.Where(r => r.Keep).ToList();
_output.WriteLine($"Total results: {results.Count}, Kept: {kept.Count}");
var overlapping = 0;
foreach (var result in kept)
{
var parts = result.BuildParts(drawing);
if (parts[0].Intersects(parts[1], out var pts))
{
overlapping++;
_output.WriteLine($" OVERLAP #{overlapping}: Test {result.Candidate.TestNumber} " +
$"Part2Rot={OpenNest.Math.Angle.ToDegrees(result.Candidate.Part2Rotation):F1}° " +
$"collision pts={pts.Count}");
}
}
Assert.Equal(0, overlapping);
}
}
+2 -2
View File
@@ -49,7 +49,7 @@ public class NestProgressTests
var parts = new List<Part> var parts = new List<Part>
{ {
TestHelpers.MakePartAt(0, 0, 5), TestHelpers.MakePartAt(0, 0, 5),
TestHelpers.MakePartAt(10, 0, 5), TestHelpers.MakePartAt(0, 10, 5),
}; };
var progress = new NestProgress { BestParts = parts }; var progress = new NestProgress { BestParts = parts };
Assert.Equal(15, progress.NestedWidth, precision: 4); Assert.Equal(15, progress.NestedWidth, precision: 4);
@@ -61,7 +61,7 @@ public class NestProgressTests
var parts = new List<Part> var parts = new List<Part>
{ {
TestHelpers.MakePartAt(0, 0, 5), TestHelpers.MakePartAt(0, 0, 5),
TestHelpers.MakePartAt(0, 10, 5), TestHelpers.MakePartAt(10, 0, 5),
}; };
var progress = new NestProgress { BestParts = parts }; var progress = new NestProgress { BestParts = parts };
Assert.Equal(15, progress.NestedLength, precision: 4); Assert.Equal(15, progress.NestedLength, precision: 4);
+50 -50
View File
@@ -6,61 +6,61 @@ public class BestCombinationTests
public void BothFit_FindsZeroRemnant() public void BothFit_FindsZeroRemnant()
{ {
// 100 = 0*30 + 5*20 (algorithm iterates from countLength1=0, finds zero remnant first) // 100 = 0*30 + 5*20 (algorithm iterates from countLength1=0, finds zero remnant first)
var result = BestCombination.FindFrom2(30, 20, 100, out var c1, out var c2); var result = BestCombination.FindFrom2(30, 20, 100);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0.0, 100.0 - (c1 * 30.0 + c2 * 20.0), 5); Assert.Equal(0.0, 100.0 - (result.Count1 * 30.0 + result.Count2 * 20.0), 5);
} }
[Fact] [Fact]
public void OnlyLength1Fits_ReturnsMaxCount1() public void OnlyLength1Fits_ReturnsMaxCount1()
{ {
var result = BestCombination.FindFrom2(10, 200, 50, out var c1, out var c2); var result = BestCombination.FindFrom2(10, 200, 50);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(5, c1); Assert.Equal(5, result.Count1);
Assert.Equal(0, c2); Assert.Equal(0, result.Count2);
} }
[Fact] [Fact]
public void OnlyLength2Fits_ReturnsMaxCount2() public void OnlyLength2Fits_ReturnsMaxCount2()
{ {
var result = BestCombination.FindFrom2(200, 10, 50, out var c1, out var c2); var result = BestCombination.FindFrom2(200, 10, 50);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0, c1); Assert.Equal(0, result.Count1);
Assert.Equal(5, c2); Assert.Equal(5, result.Count2);
} }
[Fact] [Fact]
public void NeitherFits_ReturnsFalse() public void NeitherFits_ReturnsFalse()
{ {
var result = BestCombination.FindFrom2(100, 200, 50, out var c1, out var c2); var result = BestCombination.FindFrom2(100, 200, 50);
Assert.False(result); Assert.False(result.Found);
Assert.Equal(0, c1); Assert.Equal(0, result.Count1);
Assert.Equal(0, c2); Assert.Equal(0, result.Count2);
} }
[Fact] [Fact]
public void Length1FillsExactly_ZeroRemnant() public void Length1FillsExactly_ZeroRemnant()
{ {
var result = BestCombination.FindFrom2(25, 10, 100, out var c1, out var c2); var result = BestCombination.FindFrom2(25, 10, 100);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0.0, 100.0 - (c1 * 25.0 + c2 * 10.0), 5); Assert.Equal(0.0, 100.0 - (result.Count1 * 25.0 + result.Count2 * 10.0), 5);
} }
[Fact] [Fact]
public void MixMinimizesRemnant() public void MixMinimizesRemnant()
{ {
// 7 and 3 into 20: best is 2*7 + 2*3 = 20 (zero remnant) // 7 and 3 into 20: best is 2*7 + 2*3 = 20 (zero remnant)
var result = BestCombination.FindFrom2(7, 3, 20, out var c1, out var c2); var result = BestCombination.FindFrom2(7, 3, 20);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(2, c1); Assert.Equal(2, result.Count1);
Assert.Equal(2, c2); Assert.Equal(2, result.Count2);
Assert.True(c1 * 7 + c2 * 3 <= 20); Assert.True(result.Count1 * 7 + result.Count2 * 3 <= 20);
} }
[Fact] [Fact]
@@ -68,28 +68,28 @@ public class BestCombinationTests
{ {
// 6 and 5 into 17: // 6 and 5 into 17:
// all length1: 2*6=12, remnant=5 -> actually 2*6+1*5=17 perfect // all length1: 2*6=12, remnant=5 -> actually 2*6+1*5=17 perfect
var result = BestCombination.FindFrom2(6, 5, 17, out var c1, out var c2); var result = BestCombination.FindFrom2(6, 5, 17);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0.0, 17.0 - (c1 * 6.0 + c2 * 5.0), 5); Assert.Equal(0.0, 17.0 - (result.Count1 * 6.0 + result.Count2 * 5.0), 5);
} }
[Fact] [Fact]
public void EqualLengths_FillsWithLength1() public void EqualLengths_FillsWithLength1()
{ {
var result = BestCombination.FindFrom2(10, 10, 50, out var c1, out var c2); var result = BestCombination.FindFrom2(10, 10, 50);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(5, c1 + c2); Assert.Equal(5, result.Count1 + result.Count2);
} }
[Fact] [Fact]
public void SmallLengths_LargeOverall() public void SmallLengths_LargeOverall()
{ {
var result = BestCombination.FindFrom2(3, 7, 100, out var c1, out var c2); var result = BestCombination.FindFrom2(3, 7, 100);
Assert.True(result); Assert.True(result.Found);
var used = c1 * 3.0 + c2 * 7.0; var used = result.Count1 * 3.0 + result.Count2 * 7.0;
Assert.True(used <= 100); Assert.True(used <= 100);
Assert.True(100 - used < 3); // remnant less than smallest piece Assert.True(100 - used < 3); // remnant less than smallest piece
} }
@@ -100,41 +100,41 @@ public class BestCombinationTests
// length1=9, length2=5, overall=10: // length1=9, length2=5, overall=10:
// length1 alone: 1*9=9 remnant=1 // length1 alone: 1*9=9 remnant=1
// length2 alone: 2*5=10 remnant=0 // length2 alone: 2*5=10 remnant=0
var result = BestCombination.FindFrom2(9, 5, 10, out var c1, out var c2); var result = BestCombination.FindFrom2(9, 5, 10);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0, c1); Assert.Equal(0, result.Count1);
Assert.Equal(2, c2); Assert.Equal(2, result.Count2);
} }
[Fact] [Fact]
public void FractionalLengths_WorkCorrectly() public void FractionalLengths_WorkCorrectly()
{ {
var result = BestCombination.FindFrom2(2.5, 3.5, 12, out var c1, out var c2); var result = BestCombination.FindFrom2(2.5, 3.5, 12);
Assert.True(result); Assert.True(result.Found);
var used = c1 * 2.5 + c2 * 3.5; var used = result.Count1 * 2.5 + result.Count2 * 3.5;
Assert.True(used <= 12.0 + 0.001); Assert.True(used <= 12.0 + 0.001);
} }
[Fact] [Fact]
public void OverallExactlyOneOfEach() public void OverallExactlyOneOfEach()
{ {
var result = BestCombination.FindFrom2(40, 60, 100, out var c1, out var c2); var result = BestCombination.FindFrom2(40, 60, 100);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(1, c1); Assert.Equal(1, result.Count1);
Assert.Equal(1, c2); Assert.Equal(1, result.Count2);
} }
[Fact] [Fact]
public void OverallSmallerThanEither_ReturnsFalse() public void OverallSmallerThanEither_ReturnsFalse()
{ {
var result = BestCombination.FindFrom2(10, 20, 5, out var c1, out var c2); var result = BestCombination.FindFrom2(10, 20, 5);
Assert.False(result); Assert.False(result.Found);
Assert.Equal(0, c1); Assert.Equal(0, result.Count1);
Assert.Equal(0, c2); Assert.Equal(0, result.Count2);
} }
[Fact] [Fact]
@@ -142,9 +142,9 @@ public class BestCombinationTests
{ {
// 4 and 6 into 24: 0*4+4*6=24 or 3*4+2*6=24 or 6*4+0*6=24 // 4 and 6 into 24: 0*4+4*6=24 or 3*4+2*6=24 or 6*4+0*6=24
// Algorithm iterates from 0 length1 upward, finds zero remnant and breaks // Algorithm iterates from 0 length1 upward, finds zero remnant and breaks
var result = BestCombination.FindFrom2(4, 6, 24, out var c1, out var c2); var result = BestCombination.FindFrom2(4, 6, 24);
Assert.True(result); Assert.True(result.Found);
Assert.Equal(0.0, 24.0 - (c1 * 4.0 + c2 * 6.0), 5); Assert.Equal(0.0, 24.0 - (result.Count1 * 4.0 + result.Count2 * 6.0), 5);
} }
} }
+4 -4
View File
@@ -30,8 +30,8 @@ public class PatternTilerTests
foreach (var part in result) foreach (var part in result)
{ {
Assert.True(part.BoundingBox.Right <= plateSize.Width + 0.001); Assert.True(part.BoundingBox.Right <= plateSize.Length + 0.001);
Assert.True(part.BoundingBox.Top <= plateSize.Length + 0.001); Assert.True(part.BoundingBox.Top <= plateSize.Width + 0.001);
Assert.True(part.BoundingBox.Left >= -0.001); Assert.True(part.BoundingBox.Left >= -0.001);
Assert.True(part.BoundingBox.Bottom >= -0.001); Assert.True(part.BoundingBox.Bottom >= -0.001);
} }
@@ -87,8 +87,8 @@ public class PatternTilerTests
var maxRight = result.Max(p => p.BoundingBox.Right); var maxRight = result.Max(p => p.BoundingBox.Right);
var maxTop = result.Max(p => p.BoundingBox.Top); var maxTop = result.Max(p => p.BoundingBox.Top);
Assert.True(maxRight <= 50.001); Assert.True(maxRight <= 10.001);
Assert.True(maxTop <= 10.001); Assert.True(maxTop <= 50.001);
} }
[Fact] [Fact]
+15 -15
View File
@@ -108,8 +108,8 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants(); var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r => var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1 && r.Length >= 19.9 && r.Length <= 20.1 &&
r.Length >= 99.9); r.Width >= 99.9);
Assert.NotNull(gap); Assert.NotNull(gap);
} }
@@ -146,8 +146,8 @@ public class RemnantFinderTests
// Should find the 80x100 strip on the left // Should find the 80x100 strip on the left
var left = remnants.FirstOrDefault(r => var left = remnants.FirstOrDefault(r =>
r.Width >= 79.9 && r.Width <= 80.1 && r.Length >= 79.9 && r.Length <= 80.1 &&
r.Length >= 99.9); r.Width >= 99.9);
Assert.NotNull(left); Assert.NotNull(left);
} }
@@ -186,7 +186,7 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants(); var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r => var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1); r.Length >= 19.9 && r.Length <= 20.1);
Assert.NotNull(gap); Assert.NotNull(gap);
} }
@@ -202,7 +202,7 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants(); var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r => var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1); r.Length >= 19.9 && r.Length <= 20.1);
Assert.NotNull(gap); Assert.NotNull(gap);
} }
@@ -280,9 +280,9 @@ public class RemnantFinderTests
finder.AddObstacle(new Box(0, 47, 21, 6)); finder.AddObstacle(new Box(0, 47, 21, 6));
var remnants = finder.FindRemnants(); var remnants = finder.FindRemnants();
var above = remnants.FirstOrDefault(r => r.Bottom >= 53 - 0.1 && r.Width > 50); var above = remnants.FirstOrDefault(r => r.Bottom >= 53 - 0.1 && r.Length > 50);
var below = remnants.FirstOrDefault(r => r.Top <= 47 + 0.1 && r.Width > 50); var below = remnants.FirstOrDefault(r => r.Top <= 47 + 0.1 && r.Length > 50);
var right = remnants.FirstOrDefault(r => r.Left >= 21 - 0.1 && r.Length > 50); var right = remnants.FirstOrDefault(r => r.Left >= 21 - 0.1 && r.Width > 50);
Assert.NotNull(above); Assert.NotNull(above);
Assert.NotNull(below); Assert.NotNull(below);
@@ -312,10 +312,10 @@ public class RemnantFinderTests
var topGap = tiered.FirstOrDefault(t => var topGap = tiered.FirstOrDefault(t =>
t.Box.Bottom > 50 && t.Box.Bottom < 55 && t.Box.Bottom > 50 && t.Box.Bottom < 55 &&
t.Box.Left < 1 && t.Box.Left < 1 &&
t.Box.Width > 100 && t.Box.Length > 100 &&
t.Box.Length > 5); t.Box.Width > 5);
Assert.True(topGap.Box.Width > 0, "Expected remnant above main grid"); Assert.True(topGap.Box.Length > 0, "Expected remnant above main grid");
} }
[Fact] [Fact]
@@ -361,11 +361,11 @@ public class RemnantFinderTests
// The gap at x < 106 from y=53.14 to y=59.8 should be found. // The gap at x < 106 from y=53.14 to y=59.8 should be found.
Assert.True(remnants.Count > 0, "Should find gap above main grid"); Assert.True(remnants.Count > 0, "Should find gap above main grid");
var topRemnant = remnants.FirstOrDefault(r => r.Length >= 5.375 && r.Width > 50); var topRemnant = remnants.FirstOrDefault(r => r.Width >= 5.375 && r.Length > 50);
Assert.NotNull(topRemnant); Assert.NotNull(topRemnant);
// Verify dimensions are close to the expected ~104 x 6.6 gap. // Verify dimensions are close to the expected ~104 x 6.6 gap.
Assert.True(topRemnant.Width > 100, $"Expected width > 100, got {topRemnant.Width:F1}"); Assert.True(topRemnant.Length > 100, $"Expected length > 100, got {topRemnant.Length:F1}");
Assert.True(topRemnant.Length > 6, $"Expected length > 6, got {topRemnant.Length:F1}"); Assert.True(topRemnant.Width > 6, $"Expected width > 6, got {topRemnant.Width:F1}");
} }
} }
@@ -123,7 +123,7 @@ public class PolygonHelperTests
var rotated = PolygonHelper.RotatePolygon(polygon, Angle.HalfPI); var rotated = PolygonHelper.RotatePolygon(polygon, Angle.HalfPI);
rotated.UpdateBounds(); rotated.UpdateBounds();
Assert.True(System.Math.Abs(rotated.BoundingBox.Width - 10) < 0.1); Assert.True(System.Math.Abs(rotated.BoundingBox.Length - 10) < 0.1);
Assert.True(System.Math.Abs(rotated.BoundingBox.Length - 20) < 0.1); Assert.True(System.Math.Abs(rotated.BoundingBox.Width - 20) < 0.1);
} }
} }
@@ -11,8 +11,8 @@ public class IsoscelesTriangleShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01); Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(8, bbox.Length, 0.01); Assert.Equal(8, bbox.Width, 0.01);
} }
[Fact] [Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class LShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01); Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(20, bbox.Length, 0.01); Assert.Equal(20, bbox.Width, 0.01);
} }
[Fact] [Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class RectangleShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01); Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(5, bbox.Length, 0.01); Assert.Equal(5, bbox.Width, 0.01);
} }
[Fact] [Fact]
@@ -11,8 +11,8 @@ public class RightTriangleShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(12, bbox.Width, 0.01); Assert.Equal(12, bbox.Length, 0.01);
Assert.Equal(8, bbox.Length, 0.01); Assert.Equal(8, bbox.Width, 0.01);
} }
[Fact] [Fact]
@@ -11,8 +11,8 @@ public class RoundedRectangleShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(20, bbox.Width, 0.1); Assert.Equal(20, bbox.Length, 0.1);
Assert.Equal(10, bbox.Length, 0.1); Assert.Equal(10, bbox.Width, 0.1);
} }
[Fact] [Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class TShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(12, bbox.Width, 0.01); Assert.Equal(12, bbox.Length, 0.01);
Assert.Equal(18, bbox.Length, 0.01); Assert.Equal(18, bbox.Width, 0.01);
} }
[Fact] [Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class TrapezoidShapeTests
var drawing = shape.GetDrawing(); var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox(); var bbox = drawing.Program.BoundingBox();
Assert.Equal(20, bbox.Width, 0.01); Assert.Equal(20, bbox.Length, 0.01);
Assert.Equal(8, bbox.Length, 0.01); Assert.Equal(8, bbox.Width, 0.01);
} }
[Fact] [Fact]
@@ -161,11 +161,11 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox(); var bb2 = results[1].Program.BoundingBox();
// Piece lengths should sum to original length // Piece lengths should sum to original length
Assert.Equal(100.0, bb1.Width + bb2.Width, 1); Assert.Equal(100.0, bb1.Length + bb2.Length, 1);
// Both pieces should have the same width as the original // Both pieces should have the same width as the original
Assert.Equal(100.0, bb1.Length, 1); Assert.Equal(100.0, bb1.Width, 1);
Assert.Equal(100.0, bb2.Length, 1); Assert.Equal(100.0, bb2.Width, 1);
} }
[Fact] [Fact]
@@ -183,11 +183,11 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox(); var bb2 = results[1].Program.BoundingBox();
// Piece widths should sum to original width // Piece widths should sum to original width
Assert.Equal(100.0, bb1.Length + bb2.Length, 1); Assert.Equal(100.0, bb1.Width + bb2.Width, 1);
// Both pieces should have the same length as the original // Both pieces should have the same length as the original
Assert.Equal(100.0, bb1.Width, 1); Assert.Equal(100.0, bb1.Length, 1);
Assert.Equal(100.0, bb2.Width, 1); Assert.Equal(100.0, bb2.Length, 1);
} }
[Fact] [Fact]
@@ -287,8 +287,8 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox(); var bb2 = results[1].Program.BoundingBox();
// Left piece should be 30 long, right piece should be 70 long // Left piece should be 30 long, right piece should be 70 long
Assert.Equal(30.0, bb1.Width, 1); Assert.Equal(30.0, bb1.Length, 1);
Assert.Equal(70.0, bb2.Width, 1); Assert.Equal(70.0, bb2.Length, 1);
} }
[Fact] [Fact]
@@ -37,8 +37,8 @@ public class StripeFillerTests
Drawing drawing, double spacing) Drawing drawing, double spacing)
{ {
var bb = drawing.Program.BoundingBox(); var bb = drawing.Program.BoundingBox();
var w = bb.Width; var w = bb.Length;
var h = bb.Length; var h = bb.Width;
var candidate = new PairCandidate var candidate = new PairCandidate
{ {
@@ -85,7 +85,7 @@ public class StripeFillerTests
pattern.Parts, 22.0, NestDirection.Horizontal); pattern.Parts, 22.0, NestDirection.Horizontal);
var rotated = FillHelpers.BuildRotatedPattern(pattern.Parts, angle); var rotated = FillHelpers.BuildRotatedPattern(pattern.Parts, angle);
var span = rotated.BoundingBox.Width; var span = rotated.BoundingBox.Length;
Assert.True(System.Math.Abs(span - 22.0) < 0.5, Assert.True(System.Math.Abs(span - 22.0) < 0.5,
$"Expected span ~22, got {span:F2} at {OpenNest.Math.Angle.ToDegrees(angle):F1}°"); $"Expected span ~22, got {span:F2} at {OpenNest.Math.Angle.ToDegrees(angle):F1}°");
} }
+14 -2
View File
@@ -12,6 +12,7 @@ namespace OpenNest.Actions
public class ActionClone : Action public class ActionClone : Action
{ {
private readonly List<LayoutPart> parts; private readonly List<LayoutPart> parts;
private readonly List<Part> sourceParts;
private double lastScale; private double lastScale;
@@ -28,6 +29,7 @@ namespace OpenNest.Actions
plateView.MouseDown += plateView_MouseDown; plateView.MouseDown += plateView_MouseDown;
plateView.Paint += plateView_Paint; plateView.Paint += plateView_Paint;
sourceParts = partsToClone;
parts = new List<LayoutPart>(); parts = new List<LayoutPart>();
lastScale = double.NaN; lastScale = double.NaN;
@@ -61,6 +63,16 @@ namespace OpenNest.Actions
Apply(); Apply();
break; break;
case Keys.Delete:
foreach (var part in sourceParts)
plateView.Plate.Parts.Remove(part);
if (plateView.Plate.CutOffs.Count > 0)
plateView.Plate.RegenerateCutOffs(plateView.CutOffSettings);
plateView.Invalidate();
break;
case Keys.F: case Keys.F:
if ((Control.ModifierKeys & Keys.Control) == Keys.Control) if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
Fill(); Fill();
@@ -198,9 +210,9 @@ namespace OpenNest.Actions
Box cutoffBox; Box cutoffBox;
if (cutoff.Axis == CutOffAxis.Vertical) if (cutoff.Axis == CutOffAxis.Vertical)
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Length); cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Width);
else else
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Width, 0); cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Length, 0);
boxes.Add(cutoffBox.Offset(plate.PartSpacing)); boxes.Add(cutoffBox.Offset(plate.PartSpacing));
} }
+3
View File
@@ -65,6 +65,9 @@ namespace OpenNest.Actions
contextMenu?.Dispose(); contextMenu?.Dispose();
contextMenu = null; contextMenu = null;
if (selectedLayoutPart != null)
selectedLayoutPart.IsSelected = false;
selectedLayoutPart = null; selectedLayoutPart = null;
selectedPart = null; selectedPart = null;
profile = null; profile = null;
+4 -4
View File
@@ -92,8 +92,8 @@ namespace OpenNest.Actions
var location = plateView.PointWorldToGraph(SelectedArea.Location); var location = plateView.PointWorldToGraph(SelectedArea.Location);
var size = new SizeF( var size = new SizeF(
plateView.LengthWorldToGui(SelectedArea.Width), plateView.LengthWorldToGui(SelectedArea.Length),
plateView.LengthWorldToGui(SelectedArea.Length)); plateView.LengthWorldToGui(SelectedArea.Width));
var rect = new System.Drawing.RectangleF(location.X, location.Y - size.Height, size.Width, size.Height); var rect = new System.Drawing.RectangleF(location.X, location.Y - size.Height, size.Width, size.Height);
@@ -176,9 +176,9 @@ namespace OpenNest.Actions
Box cutoffBox; Box cutoffBox;
if (cutoff.Axis == CutOffAxis.Vertical) if (cutoff.Axis == CutOffAxis.Vertical)
cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Length); cutoffBox = new Box(cutoff.Position.X, plateBounds.Y, 0, plateBounds.Width);
else else
cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Width, 0); cutoffBox = new Box(plateBounds.X, cutoff.Position.Y, plateBounds.Length, 0);
boxes.Add(cutoffBox.Offset(plateView.Plate.PartSpacing)); boxes.Add(cutoffBox.Offset(plateView.Plate.PartSpacing));
} }
+1 -1
View File
@@ -206,7 +206,7 @@ namespace OpenNest.Controls
public virtual void ZoomToArea(Box box, bool redraw = true) public virtual void ZoomToArea(Box box, bool redraw = true)
{ {
ZoomToArea(box.X, box.Y, box.Width, box.Length, redraw); ZoomToArea(box.X, box.Y, box.Length, box.Width, redraw);
} }
public virtual void ZoomToArea(double x, double y, double width, double height, bool redraw = true) public virtual void ZoomToArea(double x, double y, double width, double height, bool redraw = true)
+7 -7
View File
@@ -190,8 +190,8 @@ namespace OpenNest.Controls
var rect = new RectangleF var rect = new RectangleF
{ {
Location = view.PointWorldToGraph(workArea.Location), Location = view.PointWorldToGraph(workArea.Location),
Width = view.LengthWorldToGui(workArea.Width), Width = view.LengthWorldToGui(workArea.Length),
Height = view.LengthWorldToGui(workArea.Length) Height = view.LengthWorldToGui(workArea.Width)
}; };
rect.Y -= rect.Height; rect.Y -= rect.Height;
@@ -226,8 +226,8 @@ namespace OpenNest.Controls
{ {
var box = remnants[i]; var box = remnants[i];
var loc = view.PointWorldToGraph(box.Location); var loc = view.PointWorldToGraph(box.Location);
var w = view.LengthWorldToGui(box.Width); var w = view.LengthWorldToGui(box.Length);
var h = view.LengthWorldToGui(box.Length); var h = view.LengthWorldToGui(box.Width);
var rect = new RectangleF(loc.X, loc.Y - h, w, h); var rect = new RectangleF(loc.X, loc.Y - h, w, h);
var priority = view.DebugRemnantPriorities != null && i < view.DebugRemnantPriorities.Count var priority = view.DebugRemnantPriorities != null && i < view.DebugRemnantPriorities.Count
@@ -355,7 +355,7 @@ namespace OpenNest.Controls
var location = part.Location; var location = part.Location;
var pt1 = view.PointWorldToGraph(location); var pt1 = view.PointWorldToGraph(location);
var pt2 = view.PointWorldToGraph(new Vector( var pt2 = view.PointWorldToGraph(new Vector(
location.X + box.Width, location.Y + box.Length)); location.X + box.Length, location.Y + box.Width));
using var warnPen = new Pen(Color.FromArgb(180, 255, 140, 0), 2f); using var warnPen = new Pen(Color.FromArgb(180, 255, 140, 0), 2f);
g.DrawRectangle(warnPen, pt1.X, pt2.Y, g.DrawRectangle(warnPen, pt1.X, pt2.Y,
System.Math.Abs(pt2.X - pt1.X), System.Math.Abs(pt2.Y - pt1.Y)); System.Math.Abs(pt2.X - pt1.X), System.Math.Abs(pt2.Y - pt1.Y));
@@ -542,8 +542,8 @@ namespace OpenNest.Controls
var rect = new RectangleF var rect = new RectangleF
{ {
Location = view.PointWorldToGraph(box.Location), Location = view.PointWorldToGraph(box.Location),
Width = view.LengthWorldToGui(box.Width), Width = view.LengthWorldToGui(box.Length),
Height = view.LengthWorldToGui(box.Length) Height = view.LengthWorldToGui(box.Width)
}; };
g.DrawRectangle(view.ColorScheme.BoundingBoxPen, rect.X, rect.Y - rect.Height, rect.Width, rect.Height); g.DrawRectangle(view.ColorScheme.BoundingBoxPen, rect.X, rect.Y - rect.Height, rect.Width, rect.Height);
+1
View File
@@ -849,6 +849,7 @@ namespace OpenNest.Controls
Invalidate(); Invalidate();
} }
private void redrawTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) private void redrawTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{ {
Invalidate(); Invalidate();
+2 -2
View File
@@ -173,13 +173,13 @@ namespace OpenNest.Forms
return; return;
} }
if (LeftSpacing + RightSpacing >= size.Width) if (LeftSpacing + RightSpacing >= size.Length)
{ {
applyButton.Enabled = false; applyButton.Enabled = false;
return; return;
} }
if (TopSpacing + BottomSpacing >= size.Length) if (TopSpacing + BottomSpacing >= size.Width)
{ {
applyButton.Enabled = false; applyButton.Enabled = false;
return; return;
+2 -2
View File
@@ -131,13 +131,13 @@ namespace OpenNest.Forms
return; return;
} }
if (LeftSpacing + RightSpacing >= size.Width) if (LeftSpacing + RightSpacing >= size.Length)
{ {
applyButton.Enabled = false; applyButton.Enabled = false;
return; return;
} }
if (TopSpacing + BottomSpacing >= size.Length) if (TopSpacing + BottomSpacing >= size.Width)
{ {
applyButton.Enabled = false; applyButton.Enabled = false;
return; return;
+162 -110
View File
@@ -17,6 +17,11 @@ namespace OpenNest.Forms
private void InitializeComponent() private void InitializeComponent()
{ {
ColorScheme colorScheme1 = new ColorScheme();
CutOffSettings cutOffSettings1 = new CutOffSettings();
Plate plate1 = new Plate();
Collections.ObservableList<CutOff> observableList_11 = new Collections.ObservableList<CutOff>();
Collections.ObservableList<Part> observableList_12 = new Collections.ObservableList<Part>();
phaseStepper = new OpenNest.Controls.PhaseStepperControl(); phaseStepper = new OpenNest.Controls.PhaseStepperControl();
resultsPanel = new System.Windows.Forms.Panel(); resultsPanel = new System.Windows.Forms.Panel();
resultsTable = new System.Windows.Forms.TableLayoutPanel(); resultsTable = new System.Windows.Forms.TableLayoutPanel();
@@ -42,57 +47,23 @@ namespace OpenNest.Forms
stopButton = new System.Windows.Forms.Button(); stopButton = new System.Windows.Forms.Button();
acceptButton = new System.Windows.Forms.Button(); acceptButton = new System.Windows.Forms.Button();
splitContainer = new System.Windows.Forms.SplitContainer(); splitContainer = new System.Windows.Forms.SplitContainer();
statsPanel = new System.Windows.Forms.Panel();
previewPlateView = new OpenNest.Controls.PlateView(); previewPlateView = new OpenNest.Controls.PlateView();
((System.ComponentModel.ISupportInitialize)splitContainer).BeginInit(); statsPanel = new System.Windows.Forms.Panel();
splitContainer.Panel1.SuspendLayout();
splitContainer.Panel2.SuspendLayout();
splitContainer.SuspendLayout();
statsPanel.SuspendLayout();
resultsPanel.SuspendLayout(); resultsPanel.SuspendLayout();
resultsTable.SuspendLayout(); resultsTable.SuspendLayout();
densityPanel.SuspendLayout(); densityPanel.SuspendLayout();
statusPanel.SuspendLayout(); statusPanel.SuspendLayout();
statusTable.SuspendLayout(); statusTable.SuspendLayout();
buttonPanel.SuspendLayout(); buttonPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitContainer).BeginInit();
splitContainer.Panel1.SuspendLayout();
splitContainer.Panel2.SuspendLayout();
splitContainer.SuspendLayout();
statsPanel.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// splitContainer
//
splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
splitContainer.Location = new System.Drawing.Point(0, 0);
splitContainer.Name = "splitContainer";
splitContainer.Panel1.Controls.Add(previewPlateView);
splitContainer.Panel2.Controls.Add(statsPanel);
splitContainer.Size = new System.Drawing.Size(750, 420);
splitContainer.SplitterDistance = 480;
splitContainer.TabIndex = 0;
//
// previewPlateView
//
previewPlateView.AllowDrop = false;
previewPlateView.Dock = System.Windows.Forms.DockStyle.Fill;
previewPlateView.Location = new System.Drawing.Point(0, 0);
previewPlateView.Name = "previewPlateView";
previewPlateView.Size = new System.Drawing.Size(480, 420);
previewPlateView.TabIndex = 0;
//
// statsPanel
//
statsPanel.AutoScroll = true;
statsPanel.Controls.Add(buttonPanel);
statsPanel.Controls.Add(statusPanel);
statsPanel.Controls.Add(resultsPanel);
statsPanel.Controls.Add(phaseStepper);
statsPanel.Dock = System.Windows.Forms.DockStyle.Fill;
statsPanel.Location = new System.Drawing.Point(0, 0);
statsPanel.Name = "statsPanel";
statsPanel.Size = new System.Drawing.Size(266, 420);
statsPanel.TabIndex = 0;
//
// phaseStepper // phaseStepper
// //
phaseStepper.ActivePhase = null; phaseStepper.ActivePhase = null;
phaseStepper.Dock = System.Windows.Forms.DockStyle.Top; phaseStepper.Dock = System.Windows.Forms.DockStyle.Top;
phaseStepper.IsComplete = false; phaseStepper.IsComplete = false;
@@ -100,9 +71,9 @@ namespace OpenNest.Forms
phaseStepper.Name = "phaseStepper"; phaseStepper.Name = "phaseStepper";
phaseStepper.Size = new System.Drawing.Size(266, 60); phaseStepper.Size = new System.Drawing.Size(266, 60);
phaseStepper.TabIndex = 0; phaseStepper.TabIndex = 0;
// //
// resultsPanel // resultsPanel
// //
resultsPanel.BackColor = System.Drawing.Color.White; resultsPanel.BackColor = System.Drawing.Color.White;
resultsPanel.Controls.Add(resultsTable); resultsPanel.Controls.Add(resultsTable);
resultsPanel.Controls.Add(resultsHeader); resultsPanel.Controls.Add(resultsHeader);
@@ -113,9 +84,9 @@ namespace OpenNest.Forms
resultsPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10); resultsPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
resultsPanel.Size = new System.Drawing.Size(266, 120); resultsPanel.Size = new System.Drawing.Size(266, 120);
resultsPanel.TabIndex = 1; resultsPanel.TabIndex = 1;
// //
// resultsTable // resultsTable
// //
resultsTable.AutoSize = true; resultsTable.AutoSize = true;
resultsTable.ColumnCount = 2; resultsTable.ColumnCount = 2;
resultsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F)); resultsTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F));
@@ -135,9 +106,9 @@ namespace OpenNest.Forms
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle()); resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
resultsTable.Size = new System.Drawing.Size(238, 69); resultsTable.Size = new System.Drawing.Size(238, 69);
resultsTable.TabIndex = 1; resultsTable.TabIndex = 1;
// //
// partsLabel // partsLabel
// //
partsLabel.AutoSize = true; partsLabel.AutoSize = true;
partsLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); partsLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
partsLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); partsLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -147,20 +118,20 @@ namespace OpenNest.Forms
partsLabel.Size = new System.Drawing.Size(43, 17); partsLabel.Size = new System.Drawing.Size(43, 17);
partsLabel.TabIndex = 0; partsLabel.TabIndex = 0;
partsLabel.Text = "Parts:"; partsLabel.Text = "Parts:";
// //
// partsValue // partsValue
// //
partsValue.AutoSize = true; partsValue.AutoSize = true;
partsValue.Font = new System.Drawing.Font("Consolas", 9.75F); partsValue.Font = new System.Drawing.Font("Consolas", 9.75F);
partsValue.Location = new System.Drawing.Point(90, 3); partsValue.Location = new System.Drawing.Point(90, 3);
partsValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); partsValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
partsValue.Name = "partsValue"; partsValue.Name = "partsValue";
partsValue.Size = new System.Drawing.Size(13, 15); partsValue.Size = new System.Drawing.Size(14, 15);
partsValue.TabIndex = 1; partsValue.TabIndex = 1;
partsValue.Text = "\u2014"; partsValue.Text = "";
// //
// densityLabel // densityLabel
// //
densityLabel.AutoSize = true; densityLabel.AutoSize = true;
densityLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); densityLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
densityLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); densityLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -170,41 +141,41 @@ namespace OpenNest.Forms
densityLabel.Size = new System.Drawing.Size(59, 17); densityLabel.Size = new System.Drawing.Size(59, 17);
densityLabel.TabIndex = 2; densityLabel.TabIndex = 2;
densityLabel.Text = "Density:"; densityLabel.Text = "Density:";
// //
// densityPanel // densityPanel
// //
densityPanel.AutoSize = true; densityPanel.AutoSize = true;
densityPanel.Controls.Add(densityValue); densityPanel.Controls.Add(densityValue);
densityPanel.Controls.Add(densityBar); densityPanel.Controls.Add(densityBar);
densityPanel.Location = new System.Drawing.Point(90, 23); densityPanel.Location = new System.Drawing.Point(90, 23);
densityPanel.Margin = new System.Windows.Forms.Padding(0); densityPanel.Margin = new System.Windows.Forms.Padding(0);
densityPanel.Name = "densityPanel"; densityPanel.Name = "densityPanel";
densityPanel.Size = new System.Drawing.Size(148, 21); densityPanel.Size = new System.Drawing.Size(149, 21);
densityPanel.TabIndex = 3; densityPanel.TabIndex = 3;
densityPanel.WrapContents = false; densityPanel.WrapContents = false;
// //
// densityValue // densityValue
// //
densityValue.AutoSize = true; densityValue.AutoSize = true;
densityValue.Font = new System.Drawing.Font("Consolas", 9.75F); densityValue.Font = new System.Drawing.Font("Consolas", 9.75F);
densityValue.Location = new System.Drawing.Point(0, 3); densityValue.Location = new System.Drawing.Point(0, 3);
densityValue.Margin = new System.Windows.Forms.Padding(0, 3, 8, 3); densityValue.Margin = new System.Windows.Forms.Padding(0, 3, 8, 3);
densityValue.Name = "densityValue"; densityValue.Name = "densityValue";
densityValue.Size = new System.Drawing.Size(13, 15); densityValue.Size = new System.Drawing.Size(14, 15);
densityValue.TabIndex = 0; densityValue.TabIndex = 0;
densityValue.Text = "\u2014"; densityValue.Text = "";
// //
// densityBar // densityBar
// //
densityBar.Location = new System.Drawing.Point(21, 5); densityBar.Location = new System.Drawing.Point(22, 5);
densityBar.Margin = new System.Windows.Forms.Padding(0, 5, 0, 0); densityBar.Margin = new System.Windows.Forms.Padding(0, 5, 0, 0);
densityBar.Name = "densityBar"; densityBar.Name = "densityBar";
densityBar.Size = new System.Drawing.Size(127, 8); densityBar.Size = new System.Drawing.Size(127, 8);
densityBar.TabIndex = 1; densityBar.TabIndex = 1;
densityBar.Value = 0D; densityBar.Value = 0D;
// //
// nestedAreaLabel // nestedAreaLabel
// //
nestedAreaLabel.AutoSize = true; nestedAreaLabel.AutoSize = true;
nestedAreaLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); nestedAreaLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
nestedAreaLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); nestedAreaLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -214,20 +185,20 @@ namespace OpenNest.Forms
nestedAreaLabel.Size = new System.Drawing.Size(55, 17); nestedAreaLabel.Size = new System.Drawing.Size(55, 17);
nestedAreaLabel.TabIndex = 4; nestedAreaLabel.TabIndex = 4;
nestedAreaLabel.Text = "Nested:"; nestedAreaLabel.Text = "Nested:";
// //
// nestedAreaValue // nestedAreaValue
// //
nestedAreaValue.AutoSize = true; nestedAreaValue.AutoSize = true;
nestedAreaValue.Font = new System.Drawing.Font("Consolas", 9.75F); nestedAreaValue.Font = new System.Drawing.Font("Consolas", 9.75F);
nestedAreaValue.Location = new System.Drawing.Point(90, 49); nestedAreaValue.Location = new System.Drawing.Point(90, 49);
nestedAreaValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); nestedAreaValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
nestedAreaValue.Name = "nestedAreaValue"; nestedAreaValue.Name = "nestedAreaValue";
nestedAreaValue.Size = new System.Drawing.Size(13, 15); nestedAreaValue.Size = new System.Drawing.Size(14, 15);
nestedAreaValue.TabIndex = 5; nestedAreaValue.TabIndex = 5;
nestedAreaValue.Text = "\u2014"; nestedAreaValue.Text = "";
// //
// resultsHeader // resultsHeader
// //
resultsHeader.AutoSize = true; resultsHeader.AutoSize = true;
resultsHeader.Dock = System.Windows.Forms.DockStyle.Top; resultsHeader.Dock = System.Windows.Forms.DockStyle.Top;
resultsHeader.Font = new System.Drawing.Font("Segoe UI", 10.5F, System.Drawing.FontStyle.Bold); resultsHeader.Font = new System.Drawing.Font("Segoe UI", 10.5F, System.Drawing.FontStyle.Bold);
@@ -238,9 +209,9 @@ namespace OpenNest.Forms
resultsHeader.Size = new System.Drawing.Size(65, 23); resultsHeader.Size = new System.Drawing.Size(65, 23);
resultsHeader.TabIndex = 0; resultsHeader.TabIndex = 0;
resultsHeader.Text = "RESULTS"; resultsHeader.Text = "RESULTS";
// //
// statusPanel // statusPanel
// //
statusPanel.BackColor = System.Drawing.Color.White; statusPanel.BackColor = System.Drawing.Color.White;
statusPanel.Controls.Add(statusTable); statusPanel.Controls.Add(statusTable);
statusPanel.Controls.Add(statusHeader); statusPanel.Controls.Add(statusHeader);
@@ -250,9 +221,9 @@ namespace OpenNest.Forms
statusPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10); statusPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
statusPanel.Size = new System.Drawing.Size(266, 115); statusPanel.Size = new System.Drawing.Size(266, 115);
statusPanel.TabIndex = 2; statusPanel.TabIndex = 2;
// //
// statusTable // statusTable
// //
statusTable.AutoSize = true; statusTable.AutoSize = true;
statusTable.ColumnCount = 2; statusTable.ColumnCount = 2;
statusTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F)); statusTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F));
@@ -272,9 +243,9 @@ namespace OpenNest.Forms
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle()); statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
statusTable.Size = new System.Drawing.Size(238, 69); statusTable.Size = new System.Drawing.Size(238, 69);
statusTable.TabIndex = 1; statusTable.TabIndex = 1;
// //
// plateLabel // plateLabel
// //
plateLabel.AutoSize = true; plateLabel.AutoSize = true;
plateLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); plateLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
plateLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); plateLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -284,20 +255,20 @@ namespace OpenNest.Forms
plateLabel.Size = new System.Drawing.Size(43, 17); plateLabel.Size = new System.Drawing.Size(43, 17);
plateLabel.TabIndex = 0; plateLabel.TabIndex = 0;
plateLabel.Text = "Plate:"; plateLabel.Text = "Plate:";
// //
// plateValue // plateValue
// //
plateValue.AutoSize = true; plateValue.AutoSize = true;
plateValue.Font = new System.Drawing.Font("Consolas", 9.75F); plateValue.Font = new System.Drawing.Font("Consolas", 9.75F);
plateValue.Location = new System.Drawing.Point(90, 3); plateValue.Location = new System.Drawing.Point(90, 3);
plateValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); plateValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
plateValue.Name = "plateValue"; plateValue.Name = "plateValue";
plateValue.Size = new System.Drawing.Size(13, 15); plateValue.Size = new System.Drawing.Size(14, 15);
plateValue.TabIndex = 1; plateValue.TabIndex = 1;
plateValue.Text = "\u2014"; plateValue.Text = "";
// //
// elapsedLabel // elapsedLabel
// //
elapsedLabel.AutoSize = true; elapsedLabel.AutoSize = true;
elapsedLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); elapsedLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
elapsedLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); elapsedLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -307,9 +278,9 @@ namespace OpenNest.Forms
elapsedLabel.Size = new System.Drawing.Size(59, 17); elapsedLabel.Size = new System.Drawing.Size(59, 17);
elapsedLabel.TabIndex = 2; elapsedLabel.TabIndex = 2;
elapsedLabel.Text = "Elapsed:"; elapsedLabel.Text = "Elapsed:";
// //
// elapsedValue // elapsedValue
// //
elapsedValue.AutoSize = true; elapsedValue.AutoSize = true;
elapsedValue.Font = new System.Drawing.Font("Consolas", 9.75F); elapsedValue.Font = new System.Drawing.Font("Consolas", 9.75F);
elapsedValue.Location = new System.Drawing.Point(90, 26); elapsedValue.Location = new System.Drawing.Point(90, 26);
@@ -318,9 +289,9 @@ namespace OpenNest.Forms
elapsedValue.Size = new System.Drawing.Size(35, 15); elapsedValue.Size = new System.Drawing.Size(35, 15);
elapsedValue.TabIndex = 3; elapsedValue.TabIndex = 3;
elapsedValue.Text = "0:00"; elapsedValue.Text = "0:00";
// //
// descriptionLabel // descriptionLabel
// //
descriptionLabel.AutoSize = true; descriptionLabel.AutoSize = true;
descriptionLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold); descriptionLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
descriptionLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51); descriptionLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
@@ -330,20 +301,20 @@ namespace OpenNest.Forms
descriptionLabel.Size = new System.Drawing.Size(49, 17); descriptionLabel.Size = new System.Drawing.Size(49, 17);
descriptionLabel.TabIndex = 4; descriptionLabel.TabIndex = 4;
descriptionLabel.Text = "Detail:"; descriptionLabel.Text = "Detail:";
// //
// descriptionValue // descriptionValue
// //
descriptionValue.AutoSize = true; descriptionValue.AutoSize = true;
descriptionValue.Font = new System.Drawing.Font("Segoe UI", 9.75F); descriptionValue.Font = new System.Drawing.Font("Segoe UI", 9.75F);
descriptionValue.Location = new System.Drawing.Point(90, 49); descriptionValue.Location = new System.Drawing.Point(90, 49);
descriptionValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); descriptionValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
descriptionValue.Name = "descriptionValue"; descriptionValue.Name = "descriptionValue";
descriptionValue.Size = new System.Drawing.Size(20, 17); descriptionValue.Size = new System.Drawing.Size(21, 17);
descriptionValue.TabIndex = 5; descriptionValue.TabIndex = 5;
descriptionValue.Text = "\u2014"; descriptionValue.Text = "";
// //
// statusHeader // statusHeader
// //
statusHeader.AutoSize = true; statusHeader.AutoSize = true;
statusHeader.Dock = System.Windows.Forms.DockStyle.Top; statusHeader.Dock = System.Windows.Forms.DockStyle.Top;
statusHeader.Font = new System.Drawing.Font("Segoe UI", 10.5F, System.Drawing.FontStyle.Bold); statusHeader.Font = new System.Drawing.Font("Segoe UI", 10.5F, System.Drawing.FontStyle.Bold);
@@ -354,9 +325,9 @@ namespace OpenNest.Forms
statusHeader.Size = new System.Drawing.Size(59, 23); statusHeader.Size = new System.Drawing.Size(59, 23);
statusHeader.TabIndex = 0; statusHeader.TabIndex = 0;
statusHeader.Text = "STATUS"; statusHeader.Text = "STATUS";
// //
// buttonPanel // buttonPanel
// //
buttonPanel.AutoSize = true; buttonPanel.AutoSize = true;
buttonPanel.Controls.Add(stopButton); buttonPanel.Controls.Add(stopButton);
buttonPanel.Controls.Add(acceptButton); buttonPanel.Controls.Add(acceptButton);
@@ -367,9 +338,9 @@ namespace OpenNest.Forms
buttonPanel.Padding = new System.Windows.Forms.Padding(9, 6, 9, 6); buttonPanel.Padding = new System.Windows.Forms.Padding(9, 6, 9, 6);
buttonPanel.Size = new System.Drawing.Size(266, 45); buttonPanel.Size = new System.Drawing.Size(266, 45);
buttonPanel.TabIndex = 3; buttonPanel.TabIndex = 3;
// //
// stopButton // stopButton
// //
stopButton.Enabled = false; stopButton.Enabled = false;
stopButton.Font = new System.Drawing.Font("Segoe UI", 9.75F); stopButton.Font = new System.Drawing.Font("Segoe UI", 9.75F);
stopButton.Location = new System.Drawing.Point(155, 9); stopButton.Location = new System.Drawing.Point(155, 9);
@@ -380,12 +351,12 @@ namespace OpenNest.Forms
stopButton.Text = "Stop"; stopButton.Text = "Stop";
stopButton.UseVisualStyleBackColor = true; stopButton.UseVisualStyleBackColor = true;
stopButton.Click += StopButton_Click; stopButton.Click += StopButton_Click;
// //
// acceptButton // acceptButton
// //
acceptButton.Enabled = false; acceptButton.Enabled = false;
acceptButton.Font = new System.Drawing.Font("Segoe UI", 9.75F); acceptButton.Font = new System.Drawing.Font("Segoe UI", 9.75F);
acceptButton.Location = new System.Drawing.Point(56, 9); acceptButton.Location = new System.Drawing.Point(62, 9);
acceptButton.Margin = new System.Windows.Forms.Padding(6, 3, 0, 3); acceptButton.Margin = new System.Windows.Forms.Padding(6, 3, 0, 3);
acceptButton.Name = "acceptButton"; acceptButton.Name = "acceptButton";
acceptButton.Size = new System.Drawing.Size(93, 27); acceptButton.Size = new System.Drawing.Size(93, 27);
@@ -393,12 +364,93 @@ namespace OpenNest.Forms
acceptButton.Text = "Accept"; acceptButton.Text = "Accept";
acceptButton.UseVisualStyleBackColor = true; acceptButton.UseVisualStyleBackColor = true;
acceptButton.Click += AcceptButton_Click; acceptButton.Click += AcceptButton_Click;
// //
// splitContainer
//
splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
splitContainer.Location = new System.Drawing.Point(0, 0);
splitContainer.Name = "splitContainer";
//
// splitContainer.Panel1
//
splitContainer.Panel1.Controls.Add(previewPlateView);
//
// splitContainer.Panel2
//
splitContainer.Panel2.Controls.Add(statsPanel);
splitContainer.Size = new System.Drawing.Size(626, 341);
splitContainer.SplitterDistance = 356;
splitContainer.TabIndex = 0;
//
// previewPlateView
//
previewPlateView.ActiveWorkArea = null;
previewPlateView.AllowPan = true;
previewPlateView.AllowSelect = true;
previewPlateView.AllowZoom = true;
previewPlateView.BackColor = System.Drawing.Color.DarkGray;
colorScheme1.BackgroundColor = System.Drawing.Color.DarkGray;
colorScheme1.BoundingBoxColor = System.Drawing.Color.FromArgb(128, 128, 255);
colorScheme1.EdgeSpacingColor = System.Drawing.Color.FromArgb(180, 180, 180);
colorScheme1.LayoutFillColor = System.Drawing.Color.WhiteSmoke;
colorScheme1.LayoutOutlineColor = System.Drawing.Color.Gray;
colorScheme1.OriginColor = System.Drawing.Color.Gray;
colorScheme1.PreviewPartColor = System.Drawing.Color.FromArgb(255, 140, 0);
colorScheme1.RapidColor = System.Drawing.Color.DodgerBlue;
previewPlateView.ColorScheme = colorScheme1;
cutOffSettings1.CutDirection = CutDirection.AwayFromOrigin;
cutOffSettings1.MinSegmentLength = 0.05D;
cutOffSettings1.Overtravel = 0D;
cutOffSettings1.PartClearance = 0.02D;
previewPlateView.CutOffSettings = cutOffSettings1;
previewPlateView.DebugRemnantPriorities = null;
previewPlateView.DebugRemnants = null;
previewPlateView.Dock = System.Windows.Forms.DockStyle.Fill;
previewPlateView.DrawBounds = true;
previewPlateView.DrawCutDirection = false;
previewPlateView.DrawOffset = false;
previewPlateView.DrawOrigin = true;
previewPlateView.DrawPiercePoints = false;
previewPlateView.DrawRapid = false;
previewPlateView.FillParts = true;
previewPlateView.Location = new System.Drawing.Point(0, 0);
previewPlateView.Name = "previewPlateView";
previewPlateView.OffsetIncrementDistance = 10D;
previewPlateView.OffsetTolerance = 0.001D;
plate1.CutOffs = observableList_11;
plate1.CuttingParameters = null;
plate1.GrainAngle = 0D;
plate1.Parts = observableList_12;
plate1.PartSpacing = 0D;
plate1.Quadrant = 1;
plate1.Quantity = 0;
previewPlateView.Plate = plate1;
previewPlateView.RotateIncrementAngle = 10D;
previewPlateView.SelectedCutOff = null;
previewPlateView.ShowBendLines = false;
previewPlateView.Size = new System.Drawing.Size(356, 341);
previewPlateView.Status = "Select";
previewPlateView.TabIndex = 0;
//
// statsPanel
//
statsPanel.AutoScroll = true;
statsPanel.Controls.Add(buttonPanel);
statsPanel.Controls.Add(statusPanel);
statsPanel.Controls.Add(resultsPanel);
statsPanel.Controls.Add(phaseStepper);
statsPanel.Dock = System.Windows.Forms.DockStyle.Fill;
statsPanel.Location = new System.Drawing.Point(0, 0);
statsPanel.Name = "statsPanel";
statsPanel.Size = new System.Drawing.Size(266, 341);
statsPanel.TabIndex = 0;
//
// NestProgressForm // NestProgressForm
// //
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(750, 420); ClientSize = new System.Drawing.Size(626, 341);
Controls.Add(splitContainer); Controls.Add(splitContainer);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
MaximizeBox = false; MaximizeBox = false;
@@ -408,12 +460,6 @@ namespace OpenNest.Forms
ShowInTaskbar = false; ShowInTaskbar = false;
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
Text = "Nesting Progress"; Text = "Nesting Progress";
splitContainer.Panel1.ResumeLayout(false);
splitContainer.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)splitContainer).EndInit();
splitContainer.ResumeLayout(false);
statsPanel.ResumeLayout(false);
statsPanel.PerformLayout();
resultsPanel.ResumeLayout(false); resultsPanel.ResumeLayout(false);
resultsPanel.PerformLayout(); resultsPanel.PerformLayout();
resultsTable.ResumeLayout(false); resultsTable.ResumeLayout(false);
@@ -425,6 +471,12 @@ namespace OpenNest.Forms
statusTable.ResumeLayout(false); statusTable.ResumeLayout(false);
statusTable.PerformLayout(); statusTable.PerformLayout();
buttonPanel.ResumeLayout(false); buttonPanel.ResumeLayout(false);
splitContainer.Panel1.ResumeLayout(false);
splitContainer.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)splitContainer).EndInit();
splitContainer.ResumeLayout(false);
statsPanel.ResumeLayout(false);
statsPanel.PerformLayout();
ResumeLayout(false); ResumeLayout(false);
} }
+18
View File
@@ -42,6 +42,7 @@
this.saveButton = new System.Windows.Forms.Button(); this.saveButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button(); this.cancelButton = new System.Windows.Forms.Button();
this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); this.bottomPanel1 = new OpenNest.Controls.BottomPanel();
this.strategyGrid = new System.Windows.Forms.DataGridView();
this.strategyGroupBox = new System.Windows.Forms.GroupBox(); this.strategyGroupBox = new System.Windows.Forms.GroupBox();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
@@ -212,8 +213,24 @@
this.bottomPanel1.Size = new System.Drawing.Size(708, 50); this.bottomPanel1.Size = new System.Drawing.Size(708, 50);
this.bottomPanel1.TabIndex = 1; this.bottomPanel1.TabIndex = 1;
// //
// strategyGrid
//
this.strategyGrid.AllowUserToAddRows = false;
this.strategyGrid.AllowUserToDeleteRows = false;
this.strategyGrid.AllowUserToResizeRows = false;
this.strategyGrid.BackgroundColor = System.Drawing.SystemColors.Window;
this.strategyGrid.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.strategyGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.strategyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
this.strategyGrid.Location = new System.Drawing.Point(3, 18);
this.strategyGrid.Name = "strategyGrid";
this.strategyGrid.RowHeadersVisible = false;
this.strategyGrid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.strategyGrid.TabIndex = 0;
//
// strategyGroupBox // strategyGroupBox
// //
this.strategyGroupBox.Controls.Add(this.strategyGrid);
this.strategyGroupBox.Location = new System.Drawing.Point(12, 178); this.strategyGroupBox.Location = new System.Drawing.Point(12, 178);
this.strategyGroupBox.Name = "strategyGroupBox"; this.strategyGroupBox.Name = "strategyGroupBox";
this.strategyGroupBox.Size = new System.Drawing.Size(684, 180); this.strategyGroupBox.Size = new System.Drawing.Size(684, 180);
@@ -263,6 +280,7 @@
private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button1;
private System.Windows.Forms.DataGridView strategyGrid;
private System.Windows.Forms.GroupBox strategyGroupBox; private System.Windows.Forms.GroupBox strategyGroupBox;
} }
} }
+45 -23
View File
@@ -1,4 +1,4 @@
using OpenNest.Engine.Strategies; using OpenNest.Engine.Strategies;
using OpenNest.Properties; using OpenNest.Properties;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -9,12 +9,10 @@ namespace OpenNest.Forms
{ {
public partial class OptionsForm : Form public partial class OptionsForm : Form
{ {
private readonly List<CheckBox> _strategyCheckBoxes = new();
public OptionsForm() public OptionsForm()
{ {
InitializeComponent(); InitializeComponent();
BuildStrategyCheckBoxes(); BuildStrategyGrid();
} }
protected override void OnLoad(EventArgs e) protected override void OnLoad(EventArgs e)
@@ -23,23 +21,44 @@ namespace OpenNest.Forms
LoadSettings(); LoadSettings();
} }
private void BuildStrategyCheckBoxes() private void BuildStrategyGrid()
{ {
var strategies = FillStrategyRegistry.AllStrategies; strategyGrid.AutoGenerateColumns = false;
var y = 20;
foreach (var strategy in strategies) strategyGrid.Columns.Add(new DataGridViewCheckBoxColumn
{ {
var cb = new CheckBox Name = "Enabled",
{ HeaderText = "",
Text = strategy.Name, Width = 30,
Tag = strategy.Name, });
AutoSize = true,
Location = new System.Drawing.Point(10, y), strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
}; {
strategyGroupBox.Controls.Add(cb); Name = "Name",
_strategyCheckBoxes.Add(cb); HeaderText = "Strategy",
y += 24; ReadOnly = true,
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
});
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "Phase",
HeaderText = "Phase",
ReadOnly = true,
Width = 100,
});
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "Order",
HeaderText = "Order",
ReadOnly = true,
Width = 55,
});
foreach (var strategy in FillStrategyRegistry.AllStrategies)
{
strategyGrid.Rows.Add(true, strategy.Name, strategy.Phase, strategy.Order);
} }
} }
@@ -51,8 +70,8 @@ namespace OpenNest.Forms
numericUpDown2.Value = (decimal)Settings.Default.ImportSplinePrecision; numericUpDown2.Value = (decimal)Settings.Default.ImportSplinePrecision;
var disabledNames = ParseDisabledStrategies(Settings.Default.DisabledStrategies); var disabledNames = ParseDisabledStrategies(Settings.Default.DisabledStrategies);
foreach (var cb in _strategyCheckBoxes) foreach (DataGridViewRow row in strategyGrid.Rows)
cb.Checked = !disabledNames.Contains((string)cb.Tag); row.Cells["Enabled"].Value = !disabledNames.Contains((string)row.Cells["Name"].Value);
} }
private void SaveSettings() private void SaveSettings()
@@ -62,9 +81,12 @@ namespace OpenNest.Forms
Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value; Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value;
Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value; Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value;
var disabledNames = _strategyCheckBoxes var disabledNames = new List<string>();
.Where(cb => !cb.Checked) foreach (DataGridViewRow row in strategyGrid.Rows)
.Select(cb => (string)cb.Tag); {
if (row.Cells["Enabled"].Value is false)
disabledNames.Add((string)row.Cells["Name"].Value);
}
Settings.Default.DisabledStrategies = string.Join(",", disabledNames); Settings.Default.DisabledStrategies = string.Join(",", disabledNames);
Settings.Default.Save(); Settings.Default.Save();
+2 -2
View File
@@ -112,8 +112,8 @@ public partial class SimplifierViewerForm : Form
var padded = new Box( var padded = new Box(
candidate.BoundingBox.X - tol * 2, candidate.BoundingBox.X - tol * 2,
candidate.BoundingBox.Y - tol * 2, candidate.BoundingBox.Y - tol * 2,
candidate.BoundingBox.Width + tol * 4, candidate.BoundingBox.Length + tol * 4,
candidate.BoundingBox.Length + tol * 4); candidate.BoundingBox.Width + tol * 4);
entityView.ZoomToArea(padded); entityView.ZoomToArea(padded);
} }
+2 -2
View File
@@ -78,7 +78,7 @@ public partial class SplitDrawingForm : Form
var usable = plateW - 2 * spacing - overhang; var usable = plateW - 2 * spacing - overhang;
if (usable > 0) if (usable > 0)
{ {
var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1; var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1;
for (var i = 1; i <= splits; i++) for (var i = 1; i <= splits; i++)
_splitLines.Add(new SplitLine(_drawingBounds.X + usable * i, CutOffAxis.Vertical)); _splitLines.Add(new SplitLine(_drawingBounds.X + usable * i, CutOffAxis.Vertical));
} }
@@ -88,7 +88,7 @@ public partial class SplitDrawingForm : Form
var usable = plateH - 2 * spacing - overhang; var usable = plateH - 2 * spacing - overhang;
if (usable > 0) if (usable > 0)
{ {
var splits = (int)System.Math.Ceiling(_drawingBounds.Length / usable) - 1; var splits = (int)System.Math.Ceiling(_drawingBounds.Width / usable) - 1;
for (var i = 1; i <= splits; i++) for (var i = 1; i <= splits; i++)
_splitLines.Add(new SplitLine(_drawingBounds.Y + usable * i, CutOffAxis.Horizontal)); _splitLines.Add(new SplitLine(_drawingBounds.Y + usable * i, CutOffAxis.Horizontal));
} }
+1 -1
View File
@@ -128,7 +128,7 @@ namespace OpenNest
if (shapes.Count == 0) if (shapes.Count == 0)
{ {
var bbox = BasePart.BaseDrawing.Program.BoundingBox(); var bbox = BasePart.BaseDrawing.Program.BoundingBox();
return new Vector(bbox.Location.X + bbox.Width / 2, bbox.Location.Y + bbox.Length / 2); return new Vector(bbox.Location.X + bbox.Length / 2, bbox.Location.Y + bbox.Width / 2);
} }
var profile = new ShapeProfile(nonRapid); var profile = new ShapeProfile(nonRapid);