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)
{
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;
}
+6 -6
View File
@@ -50,13 +50,13 @@ namespace OpenNest
{
cutPosition = Position.X;
lineStart = StartLimit ?? bounds.Y;
lineEnd = EndLimit ?? (bounds.Y + bounds.Length + settings.Overtravel);
lineEnd = EndLimit ?? (bounds.Y + bounds.Width + settings.Overtravel);
}
else
{
cutPosition = Position.Y;
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)>();
@@ -176,13 +176,13 @@ namespace OpenNest
private (double Min, double Max) AxisBounds(Box bb, double clearance) =>
Axis == CutOffAxis.Vertical
? (bb.X - clearance, bb.X + bb.Width + clearance)
: (bb.Y - clearance, bb.Y + bb.Length + clearance);
? (bb.X - clearance, bb.X + bb.Length + clearance)
: (bb.Y - clearance, bb.Y + bb.Width + clearance);
private (double Start, double End) CrossAxisBounds(Box bb, double clearance) =>
Axis == CutOffAxis.Vertical
? (bb.Y - clearance, bb.Y + bb.Length + clearance)
: (bb.X - clearance, bb.X + bb.Width + clearance);
? (bb.Y - clearance, bb.Y + bb.Width + clearance)
: (bb.X - clearance, bb.X + bb.Length + clearance);
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.Y = minY;
boundingBox.Width = maxX - minX;
boundingBox.Length = maxY - minY;
boundingBox.Length = maxX - minX;
boundingBox.Width = maxY - minY;
}
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 minY = boxes[0].Y;
double maxX = boxes[0].X + boxes[0].Width;
double maxY = boxes[0].Y + boxes[0].Length;
double maxX = boxes[0].Right;
double maxY = boxes[0].Top;
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)
{
Location = new Vector(x, y);
Width = w;
Length = h;
Length = w;
Width = h;
}
public Vector Location;
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;
@@ -76,12 +76,12 @@ namespace OpenNest.Geometry
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)
{
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
@@ -91,12 +91,12 @@ namespace OpenNest.Geometry
public double Right
{
get { return X + Width; }
get { return X + Length; }
}
public double Top
{
get { return Y + Length; }
get { return Y + Width; }
}
public double Bottom
@@ -207,7 +207,7 @@ namespace OpenNest.Geometry
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()
+4 -4
View File
@@ -9,7 +9,7 @@
var x = large.Left;
var y = small.Top;
var w = large.Width;
var w = large.Length;
var h = large.Top - y;
return new Box(x, y, w, h);
@@ -23,7 +23,7 @@
var x = large.Left;
var y = large.Bottom;
var w = small.Left - x;
var h = large.Length;
var h = large.Width;
return new Box(x, y, w, h);
}
@@ -35,7 +35,7 @@
var x = large.Left;
var y = large.Bottom;
var w = large.Width;
var w = large.Length;
var h = small.Top - y;
return new Box(x, y, w, h);
@@ -49,7 +49,7 @@
var x = small.Right;
var y = large.Bottom;
var w = large.Right - x;
var h = large.Length;
var h = large.Width;
return new Box(x, y, w, h);
}
+4 -4
View File
@@ -370,23 +370,23 @@ namespace OpenNest.Geometry
if (StartPoint.X < EndPoint.X)
{
boundingBox.X = StartPoint.X;
boundingBox.Width = EndPoint.X - StartPoint.X;
boundingBox.Length = EndPoint.X - StartPoint.X;
}
else
{
boundingBox.X = EndPoint.X;
boundingBox.Width = StartPoint.X - EndPoint.X;
boundingBox.Length = StartPoint.X - EndPoint.X;
}
if (StartPoint.Y < EndPoint.Y)
{
boundingBox.Y = StartPoint.Y;
boundingBox.Length = EndPoint.Y - StartPoint.Y;
boundingBox.Width = EndPoint.Y - StartPoint.Y;
}
else
{
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.Y = minY;
boundingBox.Width = maxX - minX;
boundingBox.Length = maxY - minY;
boundingBox.Length = maxX - minX;
boundingBox.Width = maxY - minY;
}
public override Entity OffsetEntity(double distance, OffsetSide side)
+1 -1
View File
@@ -277,7 +277,7 @@ namespace OpenNest
var part = new Part(BaseDrawing, Program,
location + offset,
new Box(BoundingBox.X + offset.X, BoundingBox.Y + offset.Y,
BoundingBox.Width, BoundingBox.Length));
BoundingBox.Length, BoundingBox.Width));
return part;
}
+7 -7
View File
@@ -424,7 +424,7 @@ namespace OpenNest
{
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)
{
case 1:
@@ -451,8 +451,8 @@ namespace OpenNest
return new Box();
}
plateBox.Width = Size.Length;
plateBox.Length = Size.Width;
plateBox.Width = Size.Width;
plateBox.Length = Size.Length;
if (!includeParts)
return plateBox;
@@ -468,11 +468,11 @@ namespace OpenNest
? partsBox.Bottom
: plateBox.Bottom;
boundingBox.Width = partsBox.Right > plateBox.Right
boundingBox.Length = partsBox.Right > plateBox.Right
? partsBox.Right - boundingBox.X
: plateBox.Right - boundingBox.X;
boundingBox.Length = partsBox.Top > plateBox.Top
boundingBox.Width = partsBox.Top > plateBox.Top
? partsBox.Top - boundingBox.Y
: plateBox.Top - boundingBox.Y;
@@ -489,8 +489,8 @@ namespace OpenNest
box.X += EdgeSpacing.Left;
box.Y += EdgeSpacing.Bottom;
box.Width -= EdgeSpacing.Left + EdgeSpacing.Right;
box.Length -= EdgeSpacing.Top + EdgeSpacing.Bottom;
box.Length -= EdgeSpacing.Left + EdgeSpacing.Right;
box.Width -= EdgeSpacing.Top + EdgeSpacing.Bottom;
return box;
}
@@ -13,8 +13,8 @@ public static class AutoSplitCalculator
var lines = new List<SplitLine>();
var verticalSplits = usableWidth > 0 ? (int)System.Math.Ceiling(partBounds.Width / usableWidth) - 1 : 0;
var horizontalSplits = usableHeight > 0 ? (int)System.Math.Ceiling(partBounds.Length / usableHeight) - 1 : 0;
var verticalSplits = usableWidth > 0 ? (int)System.Math.Ceiling(partBounds.Length / usableWidth) - 1 : 0;
var horizontalSplits = usableHeight > 0 ? (int)System.Math.Ceiling(partBounds.Width / usableHeight) - 1 : 0;
if (verticalSplits < 0) verticalSplits = 0;
if (horizontalSplits < 0) horizontalSplits = 0;
@@ -34,14 +34,14 @@ public static class AutoSplitCalculator
if (verticalPieces > 1)
{
var spacing = partBounds.Width / verticalPieces;
var spacing = partBounds.Length / verticalPieces;
for (var i = 1; i < verticalPieces; i++)
lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical));
}
if (horizontalPieces > 1)
{
var spacing = partBounds.Length / horizontalPieces;
var spacing = partBounds.Width / horizontalPieces;
for (var i = 1; i < horizontalPieces; i++)
lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal));
}
@@ -89,16 +89,18 @@ namespace OpenNest.Engine.BestFit
if (isHorizontalPush)
{
perpMin = -(bbox2.Length + spacing);
perpMax = bbox1.Length + bbox2.Length + spacing;
pushStartOffset = bbox1.Width + bbox2.Width + spacing * 2;
}
else
{
// Perpendicular sweep along Y → Width; push extent along X → Length
perpMin = -(bbox2.Width + spacing);
perpMax = bbox1.Width + bbox2.Width + spacing;
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;
+2 -1
View File
@@ -203,7 +203,7 @@ namespace OpenNest
if (newWidth >= workArea.Width && newLength >= workArea.Length)
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,
@@ -295,6 +295,7 @@ namespace OpenNest
foreach (var strategy in FillStrategyRegistry.Strategies)
{
context.Token.ThrowIfCancellationRequested();
context.ActivePhase = strategy.Phase;
var sw = Stopwatch.StartNew();
var result = strategy.Fill(context);
+6 -4
View File
@@ -2,13 +2,15 @@
namespace OpenNest
{
internal record CombinationResult(bool Found, int Count1, int Count2);
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;
count1 = 0;
count2 = 0;
var count1 = 0;
var count2 = 0;
var maxCount1 = (int)System.Math.Floor(overallLength / length1);
var bestRemnant = overallLength + 1;
@@ -30,7 +32,7 @@ namespace OpenNest
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,
int plateNumber = 0,
CancellationToken token = default,
IProgress<NestProgress> progress = null,
List<Engine.BestFit.BestFitResult> bestFits = null)
Action<List<Part>, string> reportProgress = null)
{
var pair = BuildPair(drawing, rotationAngle);
if (pair == null)
@@ -37,14 +35,7 @@ namespace OpenNest.Engine.Fill
if (column.Count == 0)
return new List<Part>();
NestEngineBase.ReportProgress(progress, new ProgressReport
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = column,
WorkArea = workArea,
Description = $"Extents: initial column {column.Count} parts",
});
reportProgress?.Invoke(column, $"Extents: initial column {column.Count} parts");
var adjusted = AdjustColumn(pair.Value, column, token);
@@ -56,25 +47,11 @@ namespace OpenNest.Engine.Fill
adjusted = column;
}
NestEngineBase.ReportProgress(progress, new ProgressReport
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = adjusted,
WorkArea = workArea,
Description = $"Extents: column {adjusted.Count} parts",
});
reportProgress?.Invoke(adjusted, $"Extents: column {adjusted.Count} parts");
var result = RepeatColumns(adjusted, token);
NestEngineBase.ReportProgress(progress, new ProgressReport
{
Phase = NestPhase.Extents,
PlateNumber = plateNumber,
Parts = result,
WorkArea = workArea,
Description = $"Extents: {result.Count} parts total",
});
reportProgress?.Invoke(result, $"Extents: {result.Count} parts total");
return result;
}
@@ -96,7 +73,7 @@ namespace OpenNest.Engine.Fill
var boundary2 = new PartBoundary(part2, halfSpacing);
// 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.UpdateBounds();
@@ -135,7 +112,7 @@ namespace OpenNest.Engine.Fill
// Compute vertical copy distance using bounding boxes as starting point,
// then slide down to find true geometry distance.
var pairHeight = pair.Bbox.Length;
var pairHeight = pair.Bbox.Width;
var testOffset = new Vector(0, pairHeight);
// 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)
{
var originalPairWidth = pair.Bbox.Width;
var originalPairWidth = pair.Bbox.Length;
for (var iteration = 0; iteration < MaxIterations; iteration++)
{
@@ -294,7 +271,7 @@ namespace OpenNest.Engine.Fill
// Check if the pair got wider.
var newBbox = PairBbox(p1, p2);
if (newBbox.Width > originalPairWidth + Tolerance.Epsilon)
if (newBbox.Length > originalPairWidth + Tolerance.Epsilon)
return null;
return AnchorToWorkArea(p1, p2);
+2 -2
View File
@@ -11,7 +11,7 @@ namespace OpenNest.Engine.Fill
public FillLinear(Box workArea, double 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; }
@@ -41,7 +41,7 @@ namespace OpenNest.Engine.Fill
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)
+11 -18
View File
@@ -45,9 +45,8 @@ namespace OpenNest.Engine.Fill
}
public PairFillResult Fill(NestItem item, Box workArea,
int plateNumber = 0,
CancellationToken token = default,
IProgress<NestProgress> progress = null)
Action<List<Part>, string> reportProgress = null)
{
var bestFits = BestFitCache.GetOrCompute(
item.Drawing, plateSize.Length, plateSize.Width, partSpacing);
@@ -58,7 +57,7 @@ namespace OpenNest.Engine.Fill
var targetCount = item.Quantity > 0 ? item.Quantity : 0;
var parts = EvaluateCandidates(candidates, item.Drawing, workArea, targetCount,
plateNumber, token, progress);
token, reportProgress);
return new PairFillResult { Parts = parts, BestFits = bestFits };
}
@@ -66,7 +65,7 @@ namespace OpenNest.Engine.Fill
private List<Part> EvaluateCandidates(
List<BestFitResult> candidates, Drawing drawing,
Box workArea, int targetCount,
int plateNumber, CancellationToken token, IProgress<NestProgress> progress)
CancellationToken token, Action<List<Part>, string> reportProgress)
{
List<Part> best = null;
var sinceImproved = 0;
@@ -112,14 +111,8 @@ namespace OpenNest.Engine.Fill
sinceImproved++;
}
NestEngineBase.ReportProgress(progress, new ProgressReport
{
Phase = NestPhase.Pairs,
PlateNumber = plateNumber,
Parts = best,
WorkArea = workArea,
Description = $"Pairs: {batchStart + j + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts",
});
reportProgress?.Invoke(best,
$"Pairs: {batchStart + j + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts");
}
if (batchEnd >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
@@ -175,8 +168,8 @@ namespace OpenNest.Engine.Fill
var newTop = remaining.Max(p => p.BoundingBox.Top);
return new Box(workArea.X, workArea.Y,
workArea.Width,
System.Math.Min(newTop - workArea.Y, workArea.Length));
workArea.Length,
System.Math.Min(newTop - workArea.Y, workArea.Width));
}
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 rightWidth = System.Math.Max(0, workArea.Right - gridBox.Right);
var topArea = workArea.Width * topHeight;
var rightArea = rightWidth * System.Math.Min(gridBox.Top - workArea.Y, workArea.Length);
var topArea = workArea.Length * topHeight;
var rightArea = rightWidth * System.Math.Min(gridBox.Top - workArea.Y, workArea.Width);
var remnantArea = topArea + rightArea;
return (int)(remnantArea * maxUtilization / partArea) + 1;
@@ -292,7 +285,7 @@ namespace OpenNest.Engine.Fill
var topLength = workArea.Top - topY;
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);
if (parts != null && parts.Count > (bestRemnant?.Count ?? 0))
bestRemnant = parts;
@@ -303,7 +296,7 @@ namespace OpenNest.Engine.Fill
var rightWidth = workArea.Right - rightX;
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);
if (parts != null && parts.Count > (bestRemnant?.Count ?? 0))
bestRemnant = parts;
+1 -1
View File
@@ -24,7 +24,7 @@ namespace OpenNest.Engine.Fill
public PartBoundary(Part part, double spacing)
{
var entities = ConvertProgram.ToGeometry(part.Program)
.Where(e => e.Layer != SpecialLayers.Rapid)
.Where(e => e.Layer == SpecialLayers.Cut)
.ToList();
var definedShape = new ShapeProfile(entities);
+7 -7
View File
@@ -13,15 +13,15 @@ namespace OpenNest.Engine.Fill
var cellBox = cell.GetBoundingBox();
var halfSpacing = partSpacing / 2;
var cellWidth = cellBox.Width + partSpacing;
var cellHeight = cellBox.Length + partSpacing;
var cellW = cellBox.Width + partSpacing;
var cellL = cellBox.Length + partSpacing;
if (cellWidth <= 0 || cellHeight <= 0)
if (cellW <= 0 || cellL <= 0)
return new List<Part>();
// Size.Width = X-axis, Size.Length = Y-axis
var cols = (int)System.Math.Floor(plateSize.Width / cellWidth);
var rows = (int)System.Math.Floor(plateSize.Length / cellHeight);
// Width = Y axis, Length = X axis
var cols = (int)System.Math.Floor(plateSize.Length / cellL);
var rows = (int)System.Math.Floor(plateSize.Width / cellW);
if (cols <= 0 || rows <= 0)
return new List<Part>();
@@ -37,7 +37,7 @@ namespace OpenNest.Engine.Fill
{
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)
{
+2 -2
View File
@@ -304,10 +304,10 @@ namespace OpenNest.Engine.Fill
// Edge extensions (priority 1).
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)
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)
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
{
Phase = NestPhase.Custom,
PlateNumber = _context.PlateNumber,
Parts = bestParts,
WorkArea = workArea,
Description = $"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts",
});
_context.ReportProgress(bestParts,
$"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts");
}
return bestParts ?? new List<Part>();
@@ -201,8 +195,8 @@ public class StripeFiller
private static Box MakeStripeBox(Box workArea, double perpDim, NestDirection primaryAxis)
{
return primaryAxis == NestDirection.Horizontal
? new Box(workArea.X, workArea.Y, workArea.Width, perpDim)
: new Box(workArea.X, workArea.Y, perpDim, workArea.Length);
? new Box(workArea.X, workArea.Y, workArea.Length, perpDim)
: new Box(workArea.X, workArea.Y, perpDim, workArea.Width);
}
private List<Part> FillRemnant(List<Part> gridParts, NestDirection primaryAxis)
@@ -224,7 +218,7 @@ public class StripeFiller
var remnantLength = workArea.Top - remnantY;
if (remnantLength < minDim)
return null;
remnantBox = new Box(workArea.X, remnantY, workArea.Width, remnantLength);
remnantBox = new Box(workArea.X, remnantY, workArea.Length, remnantLength);
}
else
{
@@ -232,7 +226,7 @@ public class StripeFiller
var remnantWidth = workArea.Right - remnantX;
if (remnantWidth < minDim)
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}");
@@ -324,7 +318,7 @@ public class StripeFiller
{
var box = FillHelpers.BuildRotatedPattern(patternParts, 0).BoundingBox;
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)
return 0;
@@ -388,7 +382,7 @@ public class StripeFiller
var rotated = FillHelpers.BuildRotatedPattern(patternParts, currentAngle);
var pairSpan = GetDimension(rotated.BoundingBox, axis);
var perpDim = axis == NestDirection.Horizontal
? rotated.BoundingBox.Length : rotated.BoundingBox.Width;
? rotated.BoundingBox.Width : rotated.BoundingBox.Length;
if (pairSpan + spacing <= 0)
break;
@@ -472,13 +466,13 @@ public class StripeFiller
{
var rotated = FillHelpers.BuildRotatedPattern(patternParts, angle);
return axis == NestDirection.Horizontal
? rotated.BoundingBox.Width
: rotated.BoundingBox.Length;
? rotated.BoundingBox.Length
: rotated.BoundingBox.Width;
}
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) =>
+1 -1
View File
@@ -38,7 +38,7 @@ namespace OpenNest
var bb = item.Drawing.Program.BoundingBox();
var cos = System.Math.Abs(System.Math.Cos(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,
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),
VertexCount = polygon.Vertices.Count,
Bitmask = GenerateBitmask(polygon, 32)
@@ -72,8 +72,8 @@ namespace OpenNest.Engine.ML
for (int x = 0; x < size; x++)
{
// Map grid coordinate (0..size) to bounding box coordinate
var px = bb.Left + (x + 0.5) * (bb.Width / size);
var py = bb.Bottom + (y + 0.5) * (bb.Length / size);
var px = bb.Left + (x + 0.5) * (bb.Length / size);
var py = bb.Bottom + (y + 0.5) * (bb.Width / size);
if (polygon.ContainsPoint(new Vector(px, py)))
{
@@ -29,11 +29,15 @@ namespace OpenNest.RectanglePacking
Bin.Items.AddRange(bin1.Items);
else
Bin.Items.AddRange(bin2.Items);
}
}
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);
@@ -44,14 +48,18 @@ namespace OpenNest.RectanglePacking
{
var bin = Bin.Clone() as Bin;
var primarySize = horizontal ? item.Width : item.Length;
var secondarySize = horizontal ? item.Length : item.Width;
var binPrimary = horizontal ? bin.Width : Bin.Length;
var binSecondary = horizontal ? bin.Length : Bin.Width;
var primarySize = horizontal ? item.Length : item.Width;
var secondarySize = horizontal ? item.Width : item.Length;
var binPrimary = 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;
var normalPrimary = combo.Count1;
var rotatePrimary = combo.Count2;
var normalSecondary = (int)System.Math.Floor((binSecondary + Tolerance.Epsilon) / secondarySize);
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));
if (horizontal)
item.Location.X += item.Width * normalPrimary;
item.Location.X += item.Length * normalPrimary;
else
item.Location.Y += item.Length * normalPrimary;
item.Location.Y += item.Width * normalPrimary;
item.Rotate();
@@ -27,8 +27,8 @@ namespace OpenNest.RectanglePacking
{
for (var j = 0; j < innerCount; j++)
{
var x = (columnMajor ? i : j) * item.Width + item.X;
var y = (columnMajor ? j : i) * item.Length + item.Y;
var x = (columnMajor ? i : j) * item.Length + item.X;
var y = (columnMajor ? j : i) * item.Width + item.Y;
var clone = item.Clone() as Item;
clone.Location = new Vector(x, y);
@@ -14,16 +14,16 @@ namespace OpenNest.RectanglePacking
public override void Fill(Item item)
{
var ycount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length);
var xcount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width);
var ycount = (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++)
{
var x = item.Width * i + Bin.X;
var x = item.Length * i + Bin.X;
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;
addedItem.Location = new Vector(x, y);
@@ -35,8 +35,8 @@ namespace OpenNest.RectanglePacking
public override void Fill(Item item, int maxCount)
{
var ycount = (int)System.Math.Floor((Bin.Length + Tolerance.Epsilon) / item.Length);
var xcount = (int)System.Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width);
var ycount = (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;
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 minY = items[0].Y;
double maxX = items[0].X + items[0].Width;
double maxY = items[0].Y + items[0].Length;
double maxX = items[0].Right;
double maxY = items[0].Top;
foreach (var box in items)
{
@@ -16,11 +16,11 @@ namespace OpenNest.RectanglePacking
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)
{
if (item.Length > Bin.Length)
if (item.Width > Bin.Width)
continue;
var level = FindLevel(item);
@@ -36,10 +36,10 @@ namespace OpenNest.RectanglePacking
{
foreach (var level in levels)
{
if (level.Height < item.Length)
if (level.Height < item.Width)
continue;
if (level.RemainingWidth < item.Width)
if (level.RemainingLength < item.Length)
continue;
return level;
@@ -58,12 +58,12 @@ namespace OpenNest.RectanglePacking
var remaining = Bin.Top - y;
if (remaining < item.Length)
if (remaining < item.Width)
return null;
var level = new Level(Bin);
level.Y = y;
level.Height = item.Length;
level.Height = item.Width;
levels.Add(level);
@@ -93,9 +93,9 @@ namespace OpenNest.RectanglePacking
set { NextItemLocation.Y = value; }
}
public double Width
public double LevelLength
{
get { return Parent.Width; }
get { return Parent.Length; }
}
public double Height { get; set; }
@@ -105,9 +105,9 @@ namespace OpenNest.RectanglePacking
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)
@@ -115,7 +115,7 @@ namespace OpenNest.RectanglePacking
item.Location = NextItemLocation;
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,
angle => filler.Fill(context.Item.Drawing, angle,
context.PlateNumber, context.Token, context.Progress),
NestPhase.Extents, "Extents");
context.Token, context.ReportProgress),
"Extents");
}
}
}
+17
View File
@@ -23,9 +23,26 @@ namespace OpenNest.Engine.Strategies
/// <summary>For progress reporting only; comparisons use Policy.Comparer.</summary>
public FillScore CurrentBestScore { get; set; }
public NestPhase WinnerPhase { get; set; }
public NestPhase ActivePhase { get; set; }
public List<PhaseResult> PhaseResults { get; } = new();
public List<AngleResult> AngleResults { 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>
/// Sweeps a list of angles, calling fillAtAngle for each, and returns
/// the best result according to the context's comparer. Handles
/// cancellation and progress reporting.
/// cancellation and progress reporting via context.ReportProgress.
/// </summary>
public static List<Part> BestOverAngles(
FillContext context,
IReadOnlyList<double> angles,
Func<double, List<Part>> fillAtAngle,
NestPhase phase,
string phaseLabel)
{
var workArea = context.WorkArea;
@@ -140,14 +139,8 @@ namespace OpenNest.Engine.Strategies
best = result;
}
NestEngineBase.ReportProgress(context.Progress, new ProgressReport
{
Phase = phase,
PlateNumber = context.PlateNumber,
Parts = best,
WorkArea = workArea,
Description = $"{phaseLabel}: {i + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts",
});
context.ReportProgress(best,
$"{phaseLabel}: {i + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts");
}
return best ?? new List<Part>();
@@ -40,7 +40,7 @@ namespace OpenNest.Engine.Strategies
return result;
},
NestPhase.Linear, "Linear");
"Linear");
}
}
}
@@ -30,7 +30,7 @@ namespace OpenNest.Engine.Strategies
var dedup = GridDedup.GetOrCreate(context.SharedState);
var filler = new PairFiller(context.Plate, comparer, dedup);
var result = filler.Fill(context.Item, context.WorkArea,
context.PlateNumber, context.Token, context.Progress);
context.Token, context.ReportProgress);
context.SharedState["BestFits"] = result.BestFits;
@@ -14,8 +14,7 @@ namespace OpenNest.Engine.Strategies
var binItem = BinConverter.ToItem(context.Item, context.Plate.PartSpacing);
var bin = BinConverter.CreateBin(context.WorkArea, context.Plate.PartSpacing);
var engine = new FillBestFit(bin);
engine.Fill(binItem);
RectFill.FillBest(bin, binItem);
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 cos = System.Math.Abs(System.Math.Cos(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>
{
TestHelpers.MakePartAt(0, 0, 5),
TestHelpers.MakePartAt(10, 0, 5),
TestHelpers.MakePartAt(0, 10, 5),
};
var progress = new NestProgress { BestParts = parts };
Assert.Equal(15, progress.NestedWidth, precision: 4);
@@ -61,7 +61,7 @@ public class NestProgressTests
var parts = new List<Part>
{
TestHelpers.MakePartAt(0, 0, 5),
TestHelpers.MakePartAt(0, 10, 5),
TestHelpers.MakePartAt(10, 0, 5),
};
var progress = new NestProgress { BestParts = parts };
Assert.Equal(15, progress.NestedLength, precision: 4);
+50 -50
View File
@@ -6,61 +6,61 @@ public class BestCombinationTests
public void BothFit_FindsZeroRemnant()
{
// 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.Equal(0.0, 100.0 - (c1 * 30.0 + c2 * 20.0), 5);
Assert.True(result.Found);
Assert.Equal(0.0, 100.0 - (result.Count1 * 30.0 + result.Count2 * 20.0), 5);
}
[Fact]
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.Equal(5, c1);
Assert.Equal(0, c2);
Assert.True(result.Found);
Assert.Equal(5, result.Count1);
Assert.Equal(0, result.Count2);
}
[Fact]
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.Equal(0, c1);
Assert.Equal(5, c2);
Assert.True(result.Found);
Assert.Equal(0, result.Count1);
Assert.Equal(5, result.Count2);
}
[Fact]
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.Equal(0, c1);
Assert.Equal(0, c2);
Assert.False(result.Found);
Assert.Equal(0, result.Count1);
Assert.Equal(0, result.Count2);
}
[Fact]
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.Equal(0.0, 100.0 - (c1 * 25.0 + c2 * 10.0), 5);
Assert.True(result.Found);
Assert.Equal(0.0, 100.0 - (result.Count1 * 25.0 + result.Count2 * 10.0), 5);
}
[Fact]
public void MixMinimizesRemnant()
{
// 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.Equal(2, c1);
Assert.Equal(2, c2);
Assert.True(c1 * 7 + c2 * 3 <= 20);
Assert.True(result.Found);
Assert.Equal(2, result.Count1);
Assert.Equal(2, result.Count2);
Assert.True(result.Count1 * 7 + result.Count2 * 3 <= 20);
}
[Fact]
@@ -68,28 +68,28 @@ public class BestCombinationTests
{
// 6 and 5 into 17:
// 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.Equal(0.0, 17.0 - (c1 * 6.0 + c2 * 5.0), 5);
Assert.True(result.Found);
Assert.Equal(0.0, 17.0 - (result.Count1 * 6.0 + result.Count2 * 5.0), 5);
}
[Fact]
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.Equal(5, c1 + c2);
Assert.True(result.Found);
Assert.Equal(5, result.Count1 + result.Count2);
}
[Fact]
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);
var used = c1 * 3.0 + c2 * 7.0;
Assert.True(result.Found);
var used = result.Count1 * 3.0 + result.Count2 * 7.0;
Assert.True(used <= 100);
Assert.True(100 - used < 3); // remnant less than smallest piece
}
@@ -100,41 +100,41 @@ public class BestCombinationTests
// length1=9, length2=5, overall=10:
// length1 alone: 1*9=9 remnant=1
// 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.Equal(0, c1);
Assert.Equal(2, c2);
Assert.True(result.Found);
Assert.Equal(0, result.Count1);
Assert.Equal(2, result.Count2);
}
[Fact]
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);
var used = c1 * 2.5 + c2 * 3.5;
Assert.True(result.Found);
var used = result.Count1 * 2.5 + result.Count2 * 3.5;
Assert.True(used <= 12.0 + 0.001);
}
[Fact]
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.Equal(1, c1);
Assert.Equal(1, c2);
Assert.True(result.Found);
Assert.Equal(1, result.Count1);
Assert.Equal(1, result.Count2);
}
[Fact]
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.Equal(0, c1);
Assert.Equal(0, c2);
Assert.False(result.Found);
Assert.Equal(0, result.Count1);
Assert.Equal(0, result.Count2);
}
[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
// 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.Equal(0.0, 24.0 - (c1 * 4.0 + c2 * 6.0), 5);
Assert.True(result.Found);
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)
{
Assert.True(part.BoundingBox.Right <= plateSize.Width + 0.001);
Assert.True(part.BoundingBox.Top <= plateSize.Length + 0.001);
Assert.True(part.BoundingBox.Right <= plateSize.Length + 0.001);
Assert.True(part.BoundingBox.Top <= plateSize.Width + 0.001);
Assert.True(part.BoundingBox.Left >= -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 maxTop = result.Max(p => p.BoundingBox.Top);
Assert.True(maxRight <= 50.001);
Assert.True(maxTop <= 10.001);
Assert.True(maxRight <= 10.001);
Assert.True(maxTop <= 50.001);
}
[Fact]
+15 -15
View File
@@ -108,8 +108,8 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1 &&
r.Length >= 99.9);
r.Length >= 19.9 && r.Length <= 20.1 &&
r.Width >= 99.9);
Assert.NotNull(gap);
}
@@ -146,8 +146,8 @@ public class RemnantFinderTests
// Should find the 80x100 strip on the left
var left = remnants.FirstOrDefault(r =>
r.Width >= 79.9 && r.Width <= 80.1 &&
r.Length >= 99.9);
r.Length >= 79.9 && r.Length <= 80.1 &&
r.Width >= 99.9);
Assert.NotNull(left);
}
@@ -186,7 +186,7 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1);
r.Length >= 19.9 && r.Length <= 20.1);
Assert.NotNull(gap);
}
@@ -202,7 +202,7 @@ public class RemnantFinderTests
var remnants = finder.FindRemnants();
var gap = remnants.FirstOrDefault(r =>
r.Width >= 19.9 && r.Width <= 20.1);
r.Length >= 19.9 && r.Length <= 20.1);
Assert.NotNull(gap);
}
@@ -280,9 +280,9 @@ public class RemnantFinderTests
finder.AddObstacle(new Box(0, 47, 21, 6));
var remnants = finder.FindRemnants();
var above = remnants.FirstOrDefault(r => r.Bottom >= 53 - 0.1 && r.Width > 50);
var below = remnants.FirstOrDefault(r => r.Top <= 47 + 0.1 && r.Width > 50);
var right = remnants.FirstOrDefault(r => r.Left >= 21 - 0.1 && r.Length > 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.Length > 50);
var right = remnants.FirstOrDefault(r => r.Left >= 21 - 0.1 && r.Width > 50);
Assert.NotNull(above);
Assert.NotNull(below);
@@ -312,10 +312,10 @@ public class RemnantFinderTests
var topGap = tiered.FirstOrDefault(t =>
t.Box.Bottom > 50 && t.Box.Bottom < 55 &&
t.Box.Left < 1 &&
t.Box.Width > 100 &&
t.Box.Length > 5);
t.Box.Length > 100 &&
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]
@@ -361,11 +361,11 @@ public class RemnantFinderTests
// 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");
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);
// 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 > 6, $"Expected length > 6, got {topRemnant.Length:F1}");
Assert.True(topRemnant.Length > 100, $"Expected length > 100, 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);
rotated.UpdateBounds();
Assert.True(System.Math.Abs(rotated.BoundingBox.Width - 10) < 0.1);
Assert.True(System.Math.Abs(rotated.BoundingBox.Length - 20) < 0.1);
Assert.True(System.Math.Abs(rotated.BoundingBox.Length - 10) < 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 bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01);
Assert.Equal(8, bbox.Length, 0.01);
Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(8, bbox.Width, 0.01);
}
[Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class LShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01);
Assert.Equal(20, bbox.Length, 0.01);
Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(20, bbox.Width, 0.01);
}
[Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class RectangleShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(10, bbox.Width, 0.01);
Assert.Equal(5, bbox.Length, 0.01);
Assert.Equal(10, bbox.Length, 0.01);
Assert.Equal(5, bbox.Width, 0.01);
}
[Fact]
@@ -11,8 +11,8 @@ public class RightTriangleShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(12, bbox.Width, 0.01);
Assert.Equal(8, bbox.Length, 0.01);
Assert.Equal(12, bbox.Length, 0.01);
Assert.Equal(8, bbox.Width, 0.01);
}
[Fact]
@@ -11,8 +11,8 @@ public class RoundedRectangleShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(20, bbox.Width, 0.1);
Assert.Equal(10, bbox.Length, 0.1);
Assert.Equal(20, bbox.Length, 0.1);
Assert.Equal(10, bbox.Width, 0.1);
}
[Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class TShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(12, bbox.Width, 0.01);
Assert.Equal(18, bbox.Length, 0.01);
Assert.Equal(12, bbox.Length, 0.01);
Assert.Equal(18, bbox.Width, 0.01);
}
[Fact]
+2 -2
View File
@@ -11,8 +11,8 @@ public class TrapezoidShapeTests
var drawing = shape.GetDrawing();
var bbox = drawing.Program.BoundingBox();
Assert.Equal(20, bbox.Width, 0.01);
Assert.Equal(8, bbox.Length, 0.01);
Assert.Equal(20, bbox.Length, 0.01);
Assert.Equal(8, bbox.Width, 0.01);
}
[Fact]
@@ -161,11 +161,11 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox();
// 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
Assert.Equal(100.0, bb1.Length, 1);
Assert.Equal(100.0, bb2.Length, 1);
Assert.Equal(100.0, bb1.Width, 1);
Assert.Equal(100.0, bb2.Width, 1);
}
[Fact]
@@ -183,11 +183,11 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox();
// 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
Assert.Equal(100.0, bb1.Width, 1);
Assert.Equal(100.0, bb2.Width, 1);
Assert.Equal(100.0, bb1.Length, 1);
Assert.Equal(100.0, bb2.Length, 1);
}
[Fact]
@@ -287,8 +287,8 @@ public class DrawingSplitterTests
var bb2 = results[1].Program.BoundingBox();
// Left piece should be 30 long, right piece should be 70 long
Assert.Equal(30.0, bb1.Width, 1);
Assert.Equal(70.0, bb2.Width, 1);
Assert.Equal(30.0, bb1.Length, 1);
Assert.Equal(70.0, bb2.Length, 1);
}
[Fact]
@@ -37,8 +37,8 @@ public class StripeFillerTests
Drawing drawing, double spacing)
{
var bb = drawing.Program.BoundingBox();
var w = bb.Width;
var h = bb.Length;
var w = bb.Length;
var h = bb.Width;
var candidate = new PairCandidate
{
@@ -85,7 +85,7 @@ public class StripeFillerTests
pattern.Parts, 22.0, NestDirection.Horizontal);
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,
$"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
{
private readonly List<LayoutPart> parts;
private readonly List<Part> sourceParts;
private double lastScale;
@@ -28,6 +29,7 @@ namespace OpenNest.Actions
plateView.MouseDown += plateView_MouseDown;
plateView.Paint += plateView_Paint;
sourceParts = partsToClone;
parts = new List<LayoutPart>();
lastScale = double.NaN;
@@ -61,6 +63,16 @@ namespace OpenNest.Actions
Apply();
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:
if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
Fill();
@@ -198,9 +210,9 @@ namespace OpenNest.Actions
Box cutoffBox;
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
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));
}
+3
View File
@@ -65,6 +65,9 @@ namespace OpenNest.Actions
contextMenu?.Dispose();
contextMenu = null;
if (selectedLayoutPart != null)
selectedLayoutPart.IsSelected = false;
selectedLayoutPart = null;
selectedPart = null;
profile = null;
+4 -4
View File
@@ -92,8 +92,8 @@ namespace OpenNest.Actions
var location = plateView.PointWorldToGraph(SelectedArea.Location);
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);
@@ -176,9 +176,9 @@ namespace OpenNest.Actions
Box cutoffBox;
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
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));
}
+1 -1
View File
@@ -206,7 +206,7 @@ namespace OpenNest.Controls
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)
+7 -7
View File
@@ -190,8 +190,8 @@ namespace OpenNest.Controls
var rect = new RectangleF
{
Location = view.PointWorldToGraph(workArea.Location),
Width = view.LengthWorldToGui(workArea.Width),
Height = view.LengthWorldToGui(workArea.Length)
Width = view.LengthWorldToGui(workArea.Length),
Height = view.LengthWorldToGui(workArea.Width)
};
rect.Y -= rect.Height;
@@ -226,8 +226,8 @@ namespace OpenNest.Controls
{
var box = remnants[i];
var loc = view.PointWorldToGraph(box.Location);
var w = view.LengthWorldToGui(box.Width);
var h = view.LengthWorldToGui(box.Length);
var w = view.LengthWorldToGui(box.Length);
var h = view.LengthWorldToGui(box.Width);
var rect = new RectangleF(loc.X, loc.Y - h, w, h);
var priority = view.DebugRemnantPriorities != null && i < view.DebugRemnantPriorities.Count
@@ -355,7 +355,7 @@ namespace OpenNest.Controls
var location = part.Location;
var pt1 = view.PointWorldToGraph(location);
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);
g.DrawRectangle(warnPen, pt1.X, pt2.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
{
Location = view.PointWorldToGraph(box.Location),
Width = view.LengthWorldToGui(box.Width),
Height = view.LengthWorldToGui(box.Length)
Width = view.LengthWorldToGui(box.Length),
Height = view.LengthWorldToGui(box.Width)
};
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();
}
private void redrawTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Invalidate();
+2 -2
View File
@@ -173,13 +173,13 @@ namespace OpenNest.Forms
return;
}
if (LeftSpacing + RightSpacing >= size.Width)
if (LeftSpacing + RightSpacing >= size.Length)
{
applyButton.Enabled = false;
return;
}
if (TopSpacing + BottomSpacing >= size.Length)
if (TopSpacing + BottomSpacing >= size.Width)
{
applyButton.Enabled = false;
return;
+2 -2
View File
@@ -131,13 +131,13 @@ namespace OpenNest.Forms
return;
}
if (LeftSpacing + RightSpacing >= size.Width)
if (LeftSpacing + RightSpacing >= size.Length)
{
applyButton.Enabled = false;
return;
}
if (TopSpacing + BottomSpacing >= size.Length)
if (TopSpacing + BottomSpacing >= size.Width)
{
applyButton.Enabled = false;
return;
+162 -110
View File
@@ -17,6 +17,11 @@ namespace OpenNest.Forms
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();
resultsPanel = new System.Windows.Forms.Panel();
resultsTable = new System.Windows.Forms.TableLayoutPanel();
@@ -42,57 +47,23 @@ namespace OpenNest.Forms
stopButton = new System.Windows.Forms.Button();
acceptButton = new System.Windows.Forms.Button();
splitContainer = new System.Windows.Forms.SplitContainer();
statsPanel = new System.Windows.Forms.Panel();
previewPlateView = new OpenNest.Controls.PlateView();
((System.ComponentModel.ISupportInitialize)splitContainer).BeginInit();
splitContainer.Panel1.SuspendLayout();
splitContainer.Panel2.SuspendLayout();
splitContainer.SuspendLayout();
statsPanel.SuspendLayout();
statsPanel = new System.Windows.Forms.Panel();
resultsPanel.SuspendLayout();
resultsTable.SuspendLayout();
densityPanel.SuspendLayout();
statusPanel.SuspendLayout();
statusTable.SuspendLayout();
buttonPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitContainer).BeginInit();
splitContainer.Panel1.SuspendLayout();
splitContainer.Panel2.SuspendLayout();
splitContainer.SuspendLayout();
statsPanel.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.ActivePhase = null;
phaseStepper.Dock = System.Windows.Forms.DockStyle.Top;
phaseStepper.IsComplete = false;
@@ -100,9 +71,9 @@ namespace OpenNest.Forms
phaseStepper.Name = "phaseStepper";
phaseStepper.Size = new System.Drawing.Size(266, 60);
phaseStepper.TabIndex = 0;
//
//
// resultsPanel
//
//
resultsPanel.BackColor = System.Drawing.Color.White;
resultsPanel.Controls.Add(resultsTable);
resultsPanel.Controls.Add(resultsHeader);
@@ -113,9 +84,9 @@ namespace OpenNest.Forms
resultsPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
resultsPanel.Size = new System.Drawing.Size(266, 120);
resultsPanel.TabIndex = 1;
//
//
// resultsTable
//
//
resultsTable.AutoSize = true;
resultsTable.ColumnCount = 2;
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.Size = new System.Drawing.Size(238, 69);
resultsTable.TabIndex = 1;
//
//
// partsLabel
//
//
partsLabel.AutoSize = true;
partsLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 0;
partsLabel.Text = "Parts:";
//
//
// partsValue
//
//
partsValue.AutoSize = true;
partsValue.Font = new System.Drawing.Font("Consolas", 9.75F);
partsValue.Location = new System.Drawing.Point(90, 3);
partsValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
partsValue.Name = "partsValue";
partsValue.Size = new System.Drawing.Size(13, 15);
partsValue.Size = new System.Drawing.Size(14, 15);
partsValue.TabIndex = 1;
partsValue.Text = "\u2014";
//
partsValue.Text = "";
//
// densityLabel
//
//
densityLabel.AutoSize = true;
densityLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 2;
densityLabel.Text = "Density:";
//
//
// densityPanel
//
//
densityPanel.AutoSize = true;
densityPanel.Controls.Add(densityValue);
densityPanel.Controls.Add(densityBar);
densityPanel.Location = new System.Drawing.Point(90, 23);
densityPanel.Margin = new System.Windows.Forms.Padding(0);
densityPanel.Name = "densityPanel";
densityPanel.Size = new System.Drawing.Size(148, 21);
densityPanel.Size = new System.Drawing.Size(149, 21);
densityPanel.TabIndex = 3;
densityPanel.WrapContents = false;
//
//
// densityValue
//
//
densityValue.AutoSize = true;
densityValue.Font = new System.Drawing.Font("Consolas", 9.75F);
densityValue.Location = new System.Drawing.Point(0, 3);
densityValue.Margin = new System.Windows.Forms.Padding(0, 3, 8, 3);
densityValue.Name = "densityValue";
densityValue.Size = new System.Drawing.Size(13, 15);
densityValue.Size = new System.Drawing.Size(14, 15);
densityValue.TabIndex = 0;
densityValue.Text = "\u2014";
//
densityValue.Text = "";
//
// 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.Name = "densityBar";
densityBar.Size = new System.Drawing.Size(127, 8);
densityBar.TabIndex = 1;
densityBar.Value = 0D;
//
//
// nestedAreaLabel
//
//
nestedAreaLabel.AutoSize = true;
nestedAreaLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 4;
nestedAreaLabel.Text = "Nested:";
//
//
// nestedAreaValue
//
//
nestedAreaValue.AutoSize = true;
nestedAreaValue.Font = new System.Drawing.Font("Consolas", 9.75F);
nestedAreaValue.Location = new System.Drawing.Point(90, 49);
nestedAreaValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
nestedAreaValue.Name = "nestedAreaValue";
nestedAreaValue.Size = new System.Drawing.Size(13, 15);
nestedAreaValue.Size = new System.Drawing.Size(14, 15);
nestedAreaValue.TabIndex = 5;
nestedAreaValue.Text = "\u2014";
//
nestedAreaValue.Text = "";
//
// resultsHeader
//
//
resultsHeader.AutoSize = true;
resultsHeader.Dock = System.Windows.Forms.DockStyle.Top;
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.TabIndex = 0;
resultsHeader.Text = "RESULTS";
//
//
// statusPanel
//
//
statusPanel.BackColor = System.Drawing.Color.White;
statusPanel.Controls.Add(statusTable);
statusPanel.Controls.Add(statusHeader);
@@ -250,9 +221,9 @@ namespace OpenNest.Forms
statusPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
statusPanel.Size = new System.Drawing.Size(266, 115);
statusPanel.TabIndex = 2;
//
//
// statusTable
//
//
statusTable.AutoSize = true;
statusTable.ColumnCount = 2;
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.Size = new System.Drawing.Size(238, 69);
statusTable.TabIndex = 1;
//
//
// plateLabel
//
//
plateLabel.AutoSize = true;
plateLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 0;
plateLabel.Text = "Plate:";
//
//
// plateValue
//
//
plateValue.AutoSize = true;
plateValue.Font = new System.Drawing.Font("Consolas", 9.75F);
plateValue.Location = new System.Drawing.Point(90, 3);
plateValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
plateValue.Name = "plateValue";
plateValue.Size = new System.Drawing.Size(13, 15);
plateValue.Size = new System.Drawing.Size(14, 15);
plateValue.TabIndex = 1;
plateValue.Text = "\u2014";
//
plateValue.Text = "";
//
// elapsedLabel
//
//
elapsedLabel.AutoSize = true;
elapsedLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 2;
elapsedLabel.Text = "Elapsed:";
//
//
// elapsedValue
//
//
elapsedValue.AutoSize = true;
elapsedValue.Font = new System.Drawing.Font("Consolas", 9.75F);
elapsedValue.Location = new System.Drawing.Point(90, 26);
@@ -318,9 +289,9 @@ namespace OpenNest.Forms
elapsedValue.Size = new System.Drawing.Size(35, 15);
elapsedValue.TabIndex = 3;
elapsedValue.Text = "0:00";
//
//
// descriptionLabel
//
//
descriptionLabel.AutoSize = true;
descriptionLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
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.TabIndex = 4;
descriptionLabel.Text = "Detail:";
//
//
// descriptionValue
//
//
descriptionValue.AutoSize = true;
descriptionValue.Font = new System.Drawing.Font("Segoe UI", 9.75F);
descriptionValue.Location = new System.Drawing.Point(90, 49);
descriptionValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
descriptionValue.Name = "descriptionValue";
descriptionValue.Size = new System.Drawing.Size(20, 17);
descriptionValue.Size = new System.Drawing.Size(21, 17);
descriptionValue.TabIndex = 5;
descriptionValue.Text = "\u2014";
//
descriptionValue.Text = "";
//
// statusHeader
//
//
statusHeader.AutoSize = true;
statusHeader.Dock = System.Windows.Forms.DockStyle.Top;
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.TabIndex = 0;
statusHeader.Text = "STATUS";
//
//
// buttonPanel
//
//
buttonPanel.AutoSize = true;
buttonPanel.Controls.Add(stopButton);
buttonPanel.Controls.Add(acceptButton);
@@ -367,9 +338,9 @@ namespace OpenNest.Forms
buttonPanel.Padding = new System.Windows.Forms.Padding(9, 6, 9, 6);
buttonPanel.Size = new System.Drawing.Size(266, 45);
buttonPanel.TabIndex = 3;
//
//
// stopButton
//
//
stopButton.Enabled = false;
stopButton.Font = new System.Drawing.Font("Segoe UI", 9.75F);
stopButton.Location = new System.Drawing.Point(155, 9);
@@ -380,12 +351,12 @@ namespace OpenNest.Forms
stopButton.Text = "Stop";
stopButton.UseVisualStyleBackColor = true;
stopButton.Click += StopButton_Click;
//
//
// acceptButton
//
//
acceptButton.Enabled = false;
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.Name = "acceptButton";
acceptButton.Size = new System.Drawing.Size(93, 27);
@@ -393,12 +364,93 @@ namespace OpenNest.Forms
acceptButton.Text = "Accept";
acceptButton.UseVisualStyleBackColor = true;
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
//
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(750, 420);
ClientSize = new System.Drawing.Size(626, 341);
Controls.Add(splitContainer);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
MaximizeBox = false;
@@ -408,12 +460,6 @@ namespace OpenNest.Forms
ShowInTaskbar = false;
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
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.PerformLayout();
resultsTable.ResumeLayout(false);
@@ -425,6 +471,12 @@ namespace OpenNest.Forms
statusTable.ResumeLayout(false);
statusTable.PerformLayout();
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);
}
+18
View File
@@ -42,6 +42,7 @@
this.saveButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.bottomPanel1 = new OpenNest.Controls.BottomPanel();
this.strategyGrid = new System.Windows.Forms.DataGridView();
this.strategyGroupBox = new System.Windows.Forms.GroupBox();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.tableLayoutPanel1.SuspendLayout();
@@ -212,8 +213,24 @@
this.bottomPanel1.Size = new System.Drawing.Size(708, 50);
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
//
this.strategyGroupBox.Controls.Add(this.strategyGrid);
this.strategyGroupBox.Location = new System.Drawing.Point(12, 178);
this.strategyGroupBox.Name = "strategyGroupBox";
this.strategyGroupBox.Size = new System.Drawing.Size(684, 180);
@@ -263,6 +280,7 @@
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.DataGridView strategyGrid;
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 System;
using System.Collections.Generic;
@@ -9,12 +9,10 @@ namespace OpenNest.Forms
{
public partial class OptionsForm : Form
{
private readonly List<CheckBox> _strategyCheckBoxes = new();
public OptionsForm()
{
InitializeComponent();
BuildStrategyCheckBoxes();
BuildStrategyGrid();
}
protected override void OnLoad(EventArgs e)
@@ -23,23 +21,44 @@ namespace OpenNest.Forms
LoadSettings();
}
private void BuildStrategyCheckBoxes()
private void BuildStrategyGrid()
{
var strategies = FillStrategyRegistry.AllStrategies;
var y = 20;
strategyGrid.AutoGenerateColumns = false;
foreach (var strategy in strategies)
strategyGrid.Columns.Add(new DataGridViewCheckBoxColumn
{
var cb = new CheckBox
{
Text = strategy.Name,
Tag = strategy.Name,
AutoSize = true,
Location = new System.Drawing.Point(10, y),
};
strategyGroupBox.Controls.Add(cb);
_strategyCheckBoxes.Add(cb);
y += 24;
Name = "Enabled",
HeaderText = "",
Width = 30,
});
strategyGrid.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "Name",
HeaderText = "Strategy",
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;
var disabledNames = ParseDisabledStrategies(Settings.Default.DisabledStrategies);
foreach (var cb in _strategyCheckBoxes)
cb.Checked = !disabledNames.Contains((string)cb.Tag);
foreach (DataGridViewRow row in strategyGrid.Rows)
row.Cells["Enabled"].Value = !disabledNames.Contains((string)row.Cells["Name"].Value);
}
private void SaveSettings()
@@ -62,9 +81,12 @@ namespace OpenNest.Forms
Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value;
Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value;
var disabledNames = _strategyCheckBoxes
.Where(cb => !cb.Checked)
.Select(cb => (string)cb.Tag);
var disabledNames = new List<string>();
foreach (DataGridViewRow row in strategyGrid.Rows)
{
if (row.Cells["Enabled"].Value is false)
disabledNames.Add((string)row.Cells["Name"].Value);
}
Settings.Default.DisabledStrategies = string.Join(",", disabledNames);
Settings.Default.Save();
+2 -2
View File
@@ -112,8 +112,8 @@ public partial class SimplifierViewerForm : Form
var padded = new Box(
candidate.BoundingBox.X - 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);
}
+2 -2
View File
@@ -78,7 +78,7 @@ public partial class SplitDrawingForm : Form
var usable = plateW - 2 * spacing - overhang;
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++)
_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;
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++)
_splitLines.Add(new SplitLine(_drawingBounds.Y + usable * i, CutOffAxis.Horizontal));
}
+1 -1
View File
@@ -128,7 +128,7 @@ namespace OpenNest
if (shapes.Count == 0)
{
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);