diff --git a/OpenNest.IO/CadImporter.cs b/OpenNest.IO/CadImporter.cs
index 7ce0d27..bbfda92 100644
--- a/OpenNest.IO/CadImporter.cs
+++ b/OpenNest.IO/CadImporter.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using OpenNest.Bending;
+using OpenNest.Converters;
using OpenNest.Geometry;
using OpenNest.IO.Bending;
@@ -45,5 +47,78 @@ namespace OpenNest.IO
Name = options.Name ?? Path.GetFileNameWithoutExtension(path),
};
}
+
+ ///
+ /// Build a fully-populated from an import result plus
+ /// the caller's current entity and bend state. UI callers pass the currently
+ /// visible subset; headless callers pass the full lists.
+ ///
+ /// The produced drawing has:
+ /// - Program generated from the visible entities, with its first rapid moved
+ /// to the origin and the pierce location stored in Source.Offset
+ /// - SourceEntities containing all non-bend-source entities from the result
+ /// - SuppressedEntityIds containing entities whose layer or IsVisible is false
+ /// - Bends copied from the provided list
+ /// - Customer, Quantity, Source.Path from options / result
+ ///
+ /// Import result from .
+ ///
+ /// Entities to build the program from. Typically the currently visible subset.
+ ///
+ /// Bends to attach to the drawing.
+ /// Required quantity.
+ /// Customer name, or null.
+ ///
+ /// When non-null, replaces the generated program (used by the UI to honor
+ /// in-place G-code edits). Source.Offset is still populated from the
+ /// generated program so round-trips stay consistent.
+ ///
+ public static Drawing BuildDrawing(
+ CadImportResult result,
+ IEnumerable entities,
+ IEnumerable bends,
+ int quantity,
+ string customer,
+ OpenNest.CNC.Program editedProgram)
+ {
+ var visible = entities as IList ?? new List(entities);
+ var bendList = bends as IList ?? new List(bends);
+
+ var normalized = ShapeProfile.NormalizeEntities(visible);
+ var pgm = ConvertGeometry.ToProgram(normalized);
+
+ var offset = Vector.Zero;
+ if (pgm != null && pgm.Codes.Count > 0 && pgm[0].Type == OpenNest.CNC.CodeType.RapidMove)
+ {
+ var rapid = (OpenNest.CNC.RapidMove)pgm[0];
+ offset = rapid.EndPoint;
+ pgm.Offset(-offset);
+ }
+
+ var drawing = new Drawing(result.Name)
+ {
+ Color = Drawing.GetNextColor(),
+ Customer = customer,
+ };
+ drawing.Source.Path = result.SourcePath;
+ drawing.Source.Offset = offset;
+ drawing.Quantity.Required = quantity;
+ drawing.Bends.AddRange(bendList);
+ drawing.Program = editedProgram ?? pgm;
+
+ var bendSources = new HashSet(
+ bendList.Where(b => b.SourceEntity != null).Select(b => b.SourceEntity));
+
+ drawing.SourceEntities = result.Entities
+ .Where(e => !bendSources.Contains(e))
+ .ToList();
+
+ drawing.SuppressedEntityIds = new HashSet(
+ drawing.SourceEntities
+ .Where(e => !(e.Layer != null && e.Layer.IsVisible && e.IsVisible))
+ .Select(e => e.Id));
+
+ return drawing;
+ }
}
}
diff --git a/OpenNest.Tests/IO/CadImporterTests.cs b/OpenNest.Tests/IO/CadImporterTests.cs
index b265d4e..c7d55d2 100644
--- a/OpenNest.Tests/IO/CadImporterTests.cs
+++ b/OpenNest.Tests/IO/CadImporterTests.cs
@@ -38,5 +38,75 @@ namespace OpenNest.Tests.IO
Assert.Equal("custom", result.Name);
}
+
+ [Fact]
+ public void BuildDrawing_ProducesDrawingWithProgramAndMetadata()
+ {
+ var result = CadImporter.Import(TestDxf);
+
+ var drawing = CadImporter.BuildDrawing(
+ result,
+ result.Entities,
+ result.Bends,
+ quantity: 5,
+ customer: "ACME",
+ editedProgram: null);
+
+ Assert.NotNull(drawing);
+ Assert.Equal("4526 A14 PT11", drawing.Name);
+ Assert.Equal("ACME", drawing.Customer);
+ Assert.Equal(5, drawing.Quantity.Required);
+ Assert.Equal(TestDxf, drawing.Source.Path);
+ Assert.NotNull(drawing.Program);
+ Assert.NotEmpty(drawing.Program.Codes);
+ Assert.NotNull(drawing.SourceEntities);
+ Assert.NotEmpty(drawing.SourceEntities);
+ }
+
+ [Fact]
+ public void BuildDrawing_ExtractsFirstRapidAsSourceOffset()
+ {
+ var result = CadImporter.Import(TestDxf);
+
+ var drawing = CadImporter.BuildDrawing(result, result.Entities, result.Bends,
+ quantity: 1, customer: null, editedProgram: null);
+
+ Assert.NotNull(drawing.Source.Offset);
+ // After offset extraction, the program's first rapid must start at origin.
+ var firstRapid = (OpenNest.CNC.RapidMove)drawing.Program.Codes[0];
+ Assert.Equal(0, firstRapid.EndPoint.X, 6);
+ Assert.Equal(0, firstRapid.EndPoint.Y, 6);
+ }
+
+ [Fact]
+ public void BuildDrawing_WhenEntityHidden_TracksSuppressedId()
+ {
+ var result = CadImporter.Import(TestDxf);
+ // Suppress the first non-bend-source entity
+ var bendSources = result.Bends
+ .Where(b => b.SourceEntity != null)
+ .Select(b => b.SourceEntity)
+ .ToHashSet();
+ var hidden = result.Entities.First(e => !bendSources.Contains(e));
+ hidden.IsVisible = false;
+
+ var drawing = CadImporter.BuildDrawing(result, result.Entities, result.Bends,
+ quantity: 1, customer: null, editedProgram: null);
+
+ Assert.Contains(hidden.Id, drawing.SuppressedEntityIds);
+ }
+
+ [Fact]
+ public void BuildDrawing_WhenEditedProgramProvided_UsesEditedProgram()
+ {
+ var result = CadImporter.Import(TestDxf);
+ var edited = new OpenNest.CNC.Program();
+ edited.MoveTo(new OpenNest.Geometry.Vector(0, 0));
+
+ var drawing = CadImporter.BuildDrawing(result, result.Entities, result.Bends,
+ quantity: 1, customer: null, editedProgram: edited);
+
+ Assert.Same(edited, drawing.Program);
+ }
}
}