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); + } } }