Compare commits
5 Commits
59fec2f98d
...
a801a05e49
| Author | SHA1 | Date | |
|---|---|---|---|
| a801a05e49 | |||
| da77cc9270 | |||
| 27f0685058 | |||
| 53988acefc | |||
| a8d90be2ea |
@@ -1,6 +1,9 @@
|
|||||||
|
using OpenNest.Engine;
|
||||||
|
using OpenNest.Converters;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using OpenNest.Math;
|
using OpenNest.Math;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace OpenNest.Engine.BestFit
|
namespace OpenNest.Engine.BestFit
|
||||||
{
|
{
|
||||||
@@ -54,6 +57,68 @@ namespace OpenNest.Engine.BestFit
|
|||||||
|
|
||||||
return new List<Part> { part1, part2 };
|
return new List<Part> { part1, part2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Part> BuildCanonicalParts()
|
||||||
|
{
|
||||||
|
return NormalizeToCutOrigin(BuildParts(Candidate.Drawing));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Part> BuildSourceParts(Drawing drawing)
|
||||||
|
{
|
||||||
|
var parts = BuildCanonicalParts();
|
||||||
|
var sourceAngle = drawing?.Source?.Angle ?? 0.0;
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.Count; i++)
|
||||||
|
{
|
||||||
|
var p = parts[i];
|
||||||
|
var rebound = Part.CreateAtOrigin(drawing, p.Rotation);
|
||||||
|
var delta = p.BoundingBox.Location - rebound.BoundingBox.Location;
|
||||||
|
rebound.Offset(delta);
|
||||||
|
rebound.UpdateBounds();
|
||||||
|
parts[i] = rebound;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NormalizeToCutOrigin(CanonicalFrame.FromCanonical(parts, sourceAngle));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box GetCutBounds(List<Part> parts)
|
||||||
|
{
|
||||||
|
return GetCutBoundingBox(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Part> NormalizeToCutOrigin(List<Part> parts)
|
||||||
|
{
|
||||||
|
if (parts == null || parts.Count == 0)
|
||||||
|
return parts;
|
||||||
|
|
||||||
|
var bounds = GetCutBoundingBox(parts);
|
||||||
|
var offset = new Vector(-bounds.Left, -bounds.Bottom);
|
||||||
|
|
||||||
|
foreach (var part in parts)
|
||||||
|
part.Offset(offset);
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Box GetCutBoundingBox(List<Part> parts)
|
||||||
|
{
|
||||||
|
var entities = new List<IBoundable>();
|
||||||
|
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
var partEntities = ConvertProgram.ToGeometry(part.Program)
|
||||||
|
.Where(e => e.Layer != SpecialLayers.Rapid)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var entity in partEntities)
|
||||||
|
{
|
||||||
|
entity.Offset(part.Location);
|
||||||
|
entities.Add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities.GetBoundingBox();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BestFitSortField
|
public enum BestFitSortField
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Fill
|
namespace OpenNest.Engine.Fill
|
||||||
{
|
{
|
||||||
@@ -14,7 +15,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
public static double Push(List<Part> movingParts, Plate plate, PushDirection direction)
|
public static double Push(List<Part> movingParts, Plate plate, PushDirection direction)
|
||||||
{
|
{
|
||||||
var obstacleParts = plate.Parts
|
var obstacleParts = plate.Parts
|
||||||
.Where(p => !movingParts.Contains(p))
|
.Where(p => !movingParts.Contains(p) && !IntersectsAny(p, movingParts))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return Push(movingParts, obstacleParts, plate.WorkArea(), plate.PartSpacing, direction);
|
return Push(movingParts, obstacleParts, plate.WorkArea(), plate.PartSpacing, direction);
|
||||||
@@ -26,7 +27,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
public static double Push(List<Part> movingParts, Plate plate, double angle)
|
public static double Push(List<Part> movingParts, Plate plate, double angle)
|
||||||
{
|
{
|
||||||
var obstacleParts = plate.Parts
|
var obstacleParts = plate.Parts
|
||||||
.Where(p => !movingParts.Contains(p))
|
.Where(p => !movingParts.Contains(p) && !IntersectsAny(p, movingParts))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var direction = new Vector(System.Math.Cos(angle), System.Math.Sin(angle));
|
var direction = new Vector(System.Math.Cos(angle), System.Math.Sin(angle));
|
||||||
@@ -99,6 +100,13 @@ namespace OpenNest.Engine.Fill
|
|||||||
: PartGeometry.GetPerimeterEntities(obstacleParts[i]);
|
: PartGeometry.GetPerimeterEntities(obstacleParts[i]);
|
||||||
|
|
||||||
var d = SpatialQuery.DirectionalDistance(movingEntities, obstacleEntities[i], direction);
|
var d = SpatialQuery.DirectionalDistance(movingEntities, obstacleEntities[i], direction);
|
||||||
|
if (d <= Tolerance.Epsilon
|
||||||
|
&& partSpacing <= Tolerance.Epsilon
|
||||||
|
&& CanNudgeWithoutOverlap(moving, obstacleParts[i], direction))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (d < distance)
|
if (d < distance)
|
||||||
distance = d;
|
distance = d;
|
||||||
}
|
}
|
||||||
@@ -115,6 +123,31 @@ namespace OpenNest.Engine.Fill
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IntersectsAny(Part candidate, List<Part> parts)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < parts.Count; i++)
|
||||||
|
{
|
||||||
|
if (candidate.Intersects(parts[i], out _))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CanNudgeWithoutOverlap(Part moving, Part obstacle, Vector direction)
|
||||||
|
{
|
||||||
|
var nudge = direction * (Tolerance.Epsilon * 10);
|
||||||
|
|
||||||
|
moving.Offset(nudge);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return !moving.Intersects(obstacle, out _);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
moving.Offset(-nudge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static double Push(List<Part> movingParts, List<Part> obstacleParts,
|
public static double Push(List<Part> movingParts, List<Part> obstacleParts,
|
||||||
Box workArea, double partSpacing, PushDirection direction)
|
Box workArea, double partSpacing, PushDirection direction)
|
||||||
{
|
{
|
||||||
@@ -130,7 +163,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
public static double PushBoundingBox(List<Part> movingParts, Plate plate, PushDirection direction)
|
public static double PushBoundingBox(List<Part> movingParts, Plate plate, PushDirection direction)
|
||||||
{
|
{
|
||||||
var obstacleParts = plate.Parts
|
var obstacleParts = plate.Parts
|
||||||
.Where(p => !movingParts.Contains(p))
|
.Where(p => !movingParts.Contains(p) && !IntersectsAny(p, movingParts))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return PushBoundingBox(movingParts, obstacleParts, plate.WorkArea(), plate.PartSpacing, direction);
|
return PushBoundingBox(movingParts, obstacleParts, plate.WorkArea(), plate.PartSpacing, direction);
|
||||||
|
|||||||
+33
-2
@@ -65,6 +65,36 @@ namespace OpenNest.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Entity> GetGeometry(string path, Func<string, bool> layerFilter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var reader = new DxfReader(path);
|
||||||
|
var doc = reader.Read();
|
||||||
|
return ConvertEntities(doc, layerFilter);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex.Message);
|
||||||
|
return new List<Entity>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Entity> GetGeometry(Stream stream, Func<string, bool> layerFilter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var reader = new DxfReader(stream);
|
||||||
|
var doc = reader.Read();
|
||||||
|
return ConvertEntities(doc, layerFilter);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex.Message);
|
||||||
|
return new List<Entity>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Export
|
#region Export
|
||||||
@@ -128,16 +158,17 @@ namespace OpenNest.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Entity> ConvertEntities(CadDocument doc)
|
private static List<Entity> ConvertEntities(CadDocument doc, Func<string, bool> layerFilter = null)
|
||||||
{
|
{
|
||||||
var entities = new List<Entity>();
|
var entities = new List<Entity>();
|
||||||
var lines = new List<Line>();
|
var lines = new List<Line>();
|
||||||
var arcs = new List<Arc>();
|
var arcs = new List<Arc>();
|
||||||
var circles = new List<Circle>();
|
var circles = new List<Circle>();
|
||||||
|
var filter = layerFilter ?? IsNonCutLayer;
|
||||||
|
|
||||||
foreach (var entity in doc.Entities)
|
foreach (var entity in doc.Entities)
|
||||||
{
|
{
|
||||||
if (IsNonCutLayer(entity.Layer?.Name))
|
if (filter(entity.Layer?.Name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (entity)
|
switch (entity)
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using OpenNest.Engine;
|
||||||
|
using OpenNest.Engine.BestFit;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Math;
|
||||||
|
using OpenNest.Shapes;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests.BestFit;
|
||||||
|
|
||||||
|
public class BestFitResultFrameTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BuildCanonicalParts_NonAxisAlignedPairNormalizesActualBounds()
|
||||||
|
{
|
||||||
|
var drawing = new TShape { Width = 10, Height = 8 }.GetDrawing();
|
||||||
|
var canonical = CanonicalFrame.AsCanonicalCopy(drawing);
|
||||||
|
|
||||||
|
var result = EvaluateOffsetPair(canonical, new Vector(40, 30));
|
||||||
|
|
||||||
|
Assert.True(IsNonAxisAligned(result.OptimalRotation),
|
||||||
|
$"Expected a non-axis-aligned result, got {Angle.ToDegrees(result.OptimalRotation):F2} degrees.");
|
||||||
|
|
||||||
|
var parts = result.BuildCanonicalParts();
|
||||||
|
var bounds = result.GetCutBounds(parts);
|
||||||
|
|
||||||
|
Assert.Equal(0, bounds.Left, 3);
|
||||||
|
Assert.Equal(0, bounds.Bottom, 3);
|
||||||
|
Assert.Equal(result.BoundingWidth, bounds.Length, 2);
|
||||||
|
Assert.Equal(result.BoundingHeight, bounds.Width, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildSourceParts_RebindsCanonicalResultToRotatedSourceDrawing()
|
||||||
|
{
|
||||||
|
var drawing = new TShape { Width = 10, Height = 8 }.GetDrawing();
|
||||||
|
drawing.Program.Rotate(Angle.ToRadians(30), drawing.Program.BoundingBox().Center);
|
||||||
|
drawing.RecomputeCanonicalAngle();
|
||||||
|
|
||||||
|
var canonical = CanonicalFrame.AsCanonicalCopy(drawing);
|
||||||
|
var result = EvaluateOffsetPair(canonical, new Vector(40, 30));
|
||||||
|
|
||||||
|
var parts = result.BuildSourceParts(drawing);
|
||||||
|
var bounds = result.GetCutBounds(parts);
|
||||||
|
|
||||||
|
Assert.All(parts, p => Assert.Same(drawing, p.BaseDrawing));
|
||||||
|
Assert.Equal(0, bounds.Left, 3);
|
||||||
|
Assert.Equal(0, bounds.Bottom, 3);
|
||||||
|
Assert.False(parts[0].Intersects(parts[1], out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BestFitResult EvaluateOffsetPair(Drawing drawing, Vector offset)
|
||||||
|
{
|
||||||
|
var candidate = new PairCandidate
|
||||||
|
{
|
||||||
|
Drawing = drawing,
|
||||||
|
Part1Rotation = 0,
|
||||||
|
Part2Rotation = System.Math.PI,
|
||||||
|
Part2Offset = offset,
|
||||||
|
Spacing = 0.25
|
||||||
|
};
|
||||||
|
|
||||||
|
return new PairEvaluator().Evaluate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNonAxisAligned(double angle)
|
||||||
|
{
|
||||||
|
var normalized = Angle.NormalizeRad(angle);
|
||||||
|
var nearestQuadrant = Angle.HalfPI * System.Math.Round(normalized / Angle.HalfPI);
|
||||||
|
var delta = System.Math.Abs(normalized - nearestQuadrant);
|
||||||
|
delta = System.Math.Min(delta, Angle.HalfPI - delta);
|
||||||
|
return delta > Angle.ToRadians(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,6 +97,33 @@ namespace OpenNest.Tests.Fill
|
|||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Drawing MakeTriangleDrawing(params Vector[] points)
|
||||||
|
{
|
||||||
|
var pgm = new OpenNest.CNC.Program();
|
||||||
|
pgm.Codes.Add(new OpenNest.CNC.RapidMove(points[0]));
|
||||||
|
|
||||||
|
for (var i = 1; i < points.Length; i++)
|
||||||
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(points[i]));
|
||||||
|
|
||||||
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(points[0]));
|
||||||
|
return new Drawing("triangle", pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Part MakeTrianglePart(params Vector[] points)
|
||||||
|
{
|
||||||
|
var part = new Part(MakeTriangleDrawing(points));
|
||||||
|
part.UpdateBounds();
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Part MakeTrianglePart(double x, double y, params Vector[] points)
|
||||||
|
{
|
||||||
|
var part = MakeTrianglePart(points);
|
||||||
|
part.Location = new Vector(x, y);
|
||||||
|
part.UpdateBounds();
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Push_Left_MovesPartTowardEdge()
|
public void Push_Left_MovesPartTowardEdge()
|
||||||
{
|
{
|
||||||
@@ -171,6 +198,86 @@ namespace OpenNest.Tests.Fill
|
|||||||
Assert.NotEqual(distNoSpacing, distWithSpacing);
|
Assert.NotEqual(distNoSpacing, distWithSpacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Push_Up_AllowsSharedDiagonalEdgeToSeparate()
|
||||||
|
{
|
||||||
|
var workArea = new Box(0, 0, 20, 20);
|
||||||
|
var obstacle = MakeTrianglePart(
|
||||||
|
new Vector(0, 0),
|
||||||
|
new Vector(10, 0),
|
||||||
|
new Vector(0, 10));
|
||||||
|
var movingPart = MakeTrianglePart(
|
||||||
|
new Vector(0, 10),
|
||||||
|
new Vector(10, 0),
|
||||||
|
new Vector(10, 10));
|
||||||
|
|
||||||
|
var distance = Compactor.Push(
|
||||||
|
new List<Part> { movingPart },
|
||||||
|
new List<Part> { obstacle },
|
||||||
|
workArea,
|
||||||
|
0,
|
||||||
|
PushDirection.Up);
|
||||||
|
|
||||||
|
Assert.True(distance > 0);
|
||||||
|
Assert.True(movingPart.BoundingBox.Top > 19.9);
|
||||||
|
Assert.False(movingPart.Intersects(obstacle, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Push_Up_MovesAfterRightTriangleIsPushedLeftIntoSharedEdge()
|
||||||
|
{
|
||||||
|
var workArea = new Box(0, 0, 24, 24);
|
||||||
|
var leftTriangle = MakeTrianglePart(
|
||||||
|
2, 2,
|
||||||
|
new Vector(0, 0),
|
||||||
|
new Vector(8, 0),
|
||||||
|
new Vector(4, 10));
|
||||||
|
var rightTriangle = MakeTrianglePart(
|
||||||
|
14, 4,
|
||||||
|
new Vector(0, 10),
|
||||||
|
new Vector(8, 10),
|
||||||
|
new Vector(4, 0));
|
||||||
|
|
||||||
|
var moving = new List<Part> { rightTriangle };
|
||||||
|
var obstacles = new List<Part> { leftTriangle };
|
||||||
|
|
||||||
|
var leftDistance = Compactor.Push(moving, obstacles, workArea, 0, PushDirection.Left);
|
||||||
|
var yBeforePushUp = rightTriangle.Location.Y;
|
||||||
|
var bottomBeforePushUp = rightTriangle.BoundingBox.Bottom;
|
||||||
|
|
||||||
|
var upDistance = Compactor.Push(moving, obstacles, workArea, 0, PushDirection.Up);
|
||||||
|
|
||||||
|
Assert.True(leftDistance > 0);
|
||||||
|
Assert.True(upDistance > 0);
|
||||||
|
Assert.True(rightTriangle.Location.Y > yBeforePushUp);
|
||||||
|
Assert.True(rightTriangle.BoundingBox.Bottom > bottomBeforePushUp);
|
||||||
|
Assert.False(rightTriangle.Intersects(leftTriangle, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Push_Left_BlocksWhenSharedDiagonalEdgeWouldOverlap()
|
||||||
|
{
|
||||||
|
var workArea = new Box(0, 0, 20, 20);
|
||||||
|
var obstacle = MakeTrianglePart(
|
||||||
|
new Vector(0, 0),
|
||||||
|
new Vector(10, 0),
|
||||||
|
new Vector(0, 10));
|
||||||
|
var movingPart = MakeTrianglePart(
|
||||||
|
new Vector(0, 10),
|
||||||
|
new Vector(10, 0),
|
||||||
|
new Vector(10, 10));
|
||||||
|
|
||||||
|
var distance = Compactor.Push(
|
||||||
|
new List<Part> { movingPart },
|
||||||
|
new List<Part> { obstacle },
|
||||||
|
workArea,
|
||||||
|
0,
|
||||||
|
PushDirection.Left);
|
||||||
|
|
||||||
|
Assert.Equal(0, distance);
|
||||||
|
Assert.Equal(0, movingPart.BoundingBox.Left);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Push_AngleLeft_MovesPartTowardEdge()
|
public void Push_AngleLeft_MovesPartTowardEdge()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace OpenNest.Forms
|
|||||||
|
|
||||||
public BestFitResult SelectedResult { get; private set; }
|
public BestFitResult SelectedResult { get; private set; }
|
||||||
public Drawing SelectedDrawing => activeDrawing;
|
public Drawing SelectedDrawing => activeDrawing;
|
||||||
|
public List<Part> SelectedParts { get; private set; }
|
||||||
|
|
||||||
public BestFitViewerForm(DrawingCollection drawings, Plate plate, Units units = Units.Inches)
|
public BestFitViewerForm(DrawingCollection drawings, Plate plate, Units units = Units.Inches)
|
||||||
{
|
{
|
||||||
@@ -318,12 +319,12 @@ namespace OpenNest.Forms
|
|||||||
var cell = new BestFitCell(colorScheme);
|
var cell = new BestFitCell(colorScheme);
|
||||||
cell.PartColor = partColor;
|
cell.PartColor = partColor;
|
||||||
cell.Dock = DockStyle.Fill;
|
cell.Dock = DockStyle.Fill;
|
||||||
|
|
||||||
|
var parts = result.BuildCanonicalParts();
|
||||||
cell.Plate.Size = new Geometry.Size(
|
cell.Plate.Size = new Geometry.Size(
|
||||||
result.BoundingHeight,
|
result.BoundingHeight,
|
||||||
result.BoundingWidth);
|
result.BoundingWidth);
|
||||||
|
|
||||||
var parts = result.BuildParts(drawing);
|
|
||||||
|
|
||||||
foreach (var part in parts)
|
foreach (var part in parts)
|
||||||
cell.Plate.Parts.Add(part);
|
cell.Plate.Parts.Add(part);
|
||||||
|
|
||||||
@@ -332,6 +333,7 @@ namespace OpenNest.Forms
|
|||||||
cell.DoubleClick += (sender, e) =>
|
cell.DoubleClick += (sender, e) =>
|
||||||
{
|
{
|
||||||
SelectedResult = result;
|
SelectedResult = result;
|
||||||
|
SelectedParts = result.BuildSourceParts(drawing);
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -686,7 +686,8 @@ namespace OpenNest.Forms
|
|||||||
{
|
{
|
||||||
if (form.ShowDialog(this) == DialogResult.OK && form.SelectedResult != null)
|
if (form.ShowDialog(this) == DialogResult.OK && form.SelectedResult != null)
|
||||||
{
|
{
|
||||||
var parts = form.SelectedResult.BuildParts(form.SelectedDrawing);
|
var parts = form.SelectedParts
|
||||||
|
?? form.SelectedResult.BuildSourceParts(form.SelectedDrawing);
|
||||||
activeForm.PlateView.SetAction(typeof(ActionClone), parts);
|
activeForm.PlateView.SetAction(typeof(ActionClone), parts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user