From 6d1a3f5e2c9bdb3cedb5295a2ee4727b251487d6 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 24 Mar 2026 20:36:20 -0400 Subject: [PATCH] feat: rebuild CadConverterForm with sidebar+preview layout Replace the old DataGridView+TabControl layout with a sidebar containing FileListControl and FilterPanel on the left, and EntityView with a detail bar on the right. Adds drag-and-drop support, thread-safe parallel file import, bend detection integration, and split-to-DXF workflow. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest/Forms/CadConverterForm.Designer.cs | 371 +++++------- OpenNest/Forms/CadConverterForm.cs | 603 ++++++++++---------- 2 files changed, 438 insertions(+), 536 deletions(-) diff --git a/OpenNest/Forms/CadConverterForm.Designer.cs b/OpenNest/Forms/CadConverterForm.Designer.cs index 19f0e74..da87131 100644 --- a/OpenNest/Forms/CadConverterForm.Designer.cs +++ b/OpenNest/Forms/CadConverterForm.Designer.cs @@ -1,298 +1,185 @@ -namespace OpenNest.Forms +namespace OpenNest.Forms { partial class CadConverterForm { - /// - /// Required designer variable. - /// private System.ComponentModel.IContainer components = null; - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) - { components.Dispose(); - } base.Dispose(disposing); } #region Windows Form Designer generated code - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// private void InitializeComponent() { - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); - splitContainer1 = new System.Windows.Forms.SplitContainer(); - dataGridView1 = new System.Windows.Forms.DataGridView(); - splitContainer2 = new System.Windows.Forms.SplitContainer(); - tabControl1 = new System.Windows.Forms.TabControl(); - tabPage1 = new System.Windows.Forms.TabPage(); - checkedListBox1 = new System.Windows.Forms.CheckedListBox(); - tabPage2 = new System.Windows.Forms.TabPage(); - checkedListBox2 = new System.Windows.Forms.CheckedListBox(); - tabPage3 = new System.Windows.Forms.TabPage(); - checkedListBox3 = new System.Windows.Forms.CheckedListBox(); + mainSplitter = new System.Windows.Forms.SplitContainer(); + sidebarSplitter = new System.Windows.Forms.SplitContainer(); + fileList = new OpenNest.Controls.FileListControl(); + filterPanel = new OpenNest.Controls.FilterPanel(); + rightPanel = new System.Windows.Forms.Panel(); entityView1 = new OpenNest.Controls.EntityView(); + detailBar = new System.Windows.Forms.Panel(); + lblDimensions = new System.Windows.Forms.Label(); + lblEntityCount = new System.Windows.Forms.Label(); + numQuantity = new System.Windows.Forms.NumericUpDown(); + txtCustomer = new System.Windows.Forms.TextBox(); + btnSplit = new System.Windows.Forms.Button(); + cboBendDetector = new System.Windows.Forms.ComboBox(); + lblQty = new System.Windows.Forms.Label(); + lblCust = new System.Windows.Forms.Label(); + lblDetect = new System.Windows.Forms.Label(); bottomPanel1 = new OpenNest.Controls.BottomPanel(); cancelButton = new System.Windows.Forms.Button(); acceptButton = new System.Windows.Forms.Button(); - ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); - splitContainer1.Panel1.SuspendLayout(); - splitContainer1.Panel2.SuspendLayout(); - splitContainer1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit(); - ((System.ComponentModel.ISupportInitialize)splitContainer2).BeginInit(); - splitContainer2.Panel1.SuspendLayout(); - splitContainer2.Panel2.SuspendLayout(); - splitContainer2.SuspendLayout(); - tabControl1.SuspendLayout(); - tabPage1.SuspendLayout(); - tabPage2.SuspendLayout(); - tabPage3.SuspendLayout(); + + ((System.ComponentModel.ISupportInitialize)mainSplitter).BeginInit(); + mainSplitter.Panel1.SuspendLayout(); + mainSplitter.Panel2.SuspendLayout(); + mainSplitter.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)sidebarSplitter).BeginInit(); + sidebarSplitter.Panel1.SuspendLayout(); + sidebarSplitter.Panel2.SuspendLayout(); + sidebarSplitter.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)numQuantity).BeginInit(); bottomPanel1.SuspendLayout(); SuspendLayout(); - // - // splitContainer1 - // - splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; - splitContainer1.Location = new System.Drawing.Point(0, 0); - splitContainer1.Name = "splitContainer1"; - splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer1.Panel1 - // - splitContainer1.Panel1.Controls.Add(dataGridView1); - // - // splitContainer1.Panel2 - // - splitContainer1.Panel2.Controls.Add(splitContainer2); - splitContainer1.Size = new System.Drawing.Size(928, 643); - splitContainer1.SplitterDistance = 302; - splitContainer1.TabIndex = 0; - // - // dataGridView1 - // - dataGridView1.AllowUserToAddRows = false; - dataGridView1.AllowUserToResizeRows = false; - dataGridView1.BackgroundColor = System.Drawing.Color.White; - dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None; - dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); - dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.FromArgb(255, 255, 192); - dataGridViewCellStyle1.SelectionForeColor = System.Drawing.Color.Black; - dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False; - dataGridView1.DefaultCellStyle = dataGridViewCellStyle1; - dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; - dataGridView1.GridColor = System.Drawing.Color.Gainsboro; - dataGridView1.Location = new System.Drawing.Point(0, 0); - dataGridView1.MultiSelect = false; - dataGridView1.Name = "dataGridView1"; - dataGridView1.RowHeadersVisible = false; - dataGridView1.RowTemplate.Height = 26; - dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - dataGridView1.Size = new System.Drawing.Size(928, 302); - dataGridView1.TabIndex = 0; - dataGridView1.DataBindingComplete += dataGridView1_DataBindingComplete; - dataGridView1.SelectionChanged += dataGridView1_SelectionChanged; - // - // splitContainer2 - // - splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; - splitContainer2.Location = new System.Drawing.Point(0, 0); - splitContainer2.Name = "splitContainer2"; - // - // splitContainer2.Panel1 - // - splitContainer2.Panel1.Controls.Add(tabControl1); - // - // splitContainer2.Panel2 - // - splitContainer2.Panel2.Controls.Add(entityView1); - splitContainer2.Size = new System.Drawing.Size(928, 337); - splitContainer2.SplitterDistance = 309; - splitContainer2.TabIndex = 0; - // - // tabControl1 - // - tabControl1.Controls.Add(tabPage1); - tabControl1.Controls.Add(tabPage2); - tabControl1.Controls.Add(tabPage3); - tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; - tabControl1.Location = new System.Drawing.Point(0, 0); - tabControl1.Name = "tabControl1"; - tabControl1.SelectedIndex = 0; - tabControl1.Size = new System.Drawing.Size(309, 337); - tabControl1.TabIndex = 0; - // - // tabPage1 - // - tabPage1.Controls.Add(checkedListBox1); - tabPage1.Location = new System.Drawing.Point(4, 25); - tabPage1.Name = "tabPage1"; - tabPage1.Padding = new System.Windows.Forms.Padding(3); - tabPage1.Size = new System.Drawing.Size(301, 308); - tabPage1.TabIndex = 0; - tabPage1.Text = "Layers"; - tabPage1.UseVisualStyleBackColor = true; - // - // checkedListBox1 - // - checkedListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; - checkedListBox1.CheckOnClick = true; - checkedListBox1.Dock = System.Windows.Forms.DockStyle.Fill; - checkedListBox1.FormattingEnabled = true; - checkedListBox1.Location = new System.Drawing.Point(3, 3); - checkedListBox1.Name = "checkedListBox1"; - checkedListBox1.Size = new System.Drawing.Size(295, 302); - checkedListBox1.TabIndex = 0; - checkedListBox1.SelectedIndexChanged += checkedListBox1_SelectedIndexChanged; - // - // tabPage2 - // - tabPage2.Controls.Add(checkedListBox2); - tabPage2.Location = new System.Drawing.Point(4, 24); - tabPage2.Name = "tabPage2"; - tabPage2.Padding = new System.Windows.Forms.Padding(3); - tabPage2.Size = new System.Drawing.Size(301, 309); - tabPage2.TabIndex = 1; - tabPage2.Text = "Colors"; - tabPage2.UseVisualStyleBackColor = true; - // - // checkedListBox2 - // - checkedListBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; - checkedListBox2.CheckOnClick = true; - checkedListBox2.Dock = System.Windows.Forms.DockStyle.Fill; - checkedListBox2.FormattingEnabled = true; - checkedListBox2.Location = new System.Drawing.Point(3, 3); - checkedListBox2.Name = "checkedListBox2"; - checkedListBox2.Size = new System.Drawing.Size(295, 303); - checkedListBox2.TabIndex = 1; - checkedListBox2.DrawItem += checkedListBox2_DrawItem; - checkedListBox2.SelectedIndexChanged += checkedListBox2_SelectedIndexChanged; - // - // tabPage3 - // - tabPage3.Controls.Add(checkedListBox3); - tabPage3.Location = new System.Drawing.Point(4, 24); - tabPage3.Name = "tabPage3"; - tabPage3.Padding = new System.Windows.Forms.Padding(3); - tabPage3.Size = new System.Drawing.Size(301, 309); - tabPage3.TabIndex = 2; - tabPage3.Text = "Line Types"; - tabPage3.UseVisualStyleBackColor = true; - // - // checkedListBox3 - // - checkedListBox3.BorderStyle = System.Windows.Forms.BorderStyle.None; - checkedListBox3.CheckOnClick = true; - checkedListBox3.Dock = System.Windows.Forms.DockStyle.Fill; - checkedListBox3.FormattingEnabled = true; - checkedListBox3.Location = new System.Drawing.Point(3, 3); - checkedListBox3.Name = "checkedListBox3"; - checkedListBox3.Size = new System.Drawing.Size(295, 303); - checkedListBox3.TabIndex = 2; - checkedListBox3.SelectedIndexChanged += checkedListBox3_SelectedIndexChanged; - // + + // mainSplitter (sidebar | preview) + mainSplitter.Dock = System.Windows.Forms.DockStyle.Fill; + mainSplitter.SplitterDistance = 260; + mainSplitter.SplitterWidth = 3; + mainSplitter.Panel1.Controls.Add(sidebarSplitter); + mainSplitter.Panel2.Controls.Add(rightPanel); + + // sidebarSplitter (file list | filter panel) + sidebarSplitter.Dock = System.Windows.Forms.DockStyle.Fill; + sidebarSplitter.Orientation = System.Windows.Forms.Orientation.Horizontal; + sidebarSplitter.SplitterDistance = 280; + sidebarSplitter.SplitterWidth = 3; + sidebarSplitter.Panel1.Controls.Add(fileList); + sidebarSplitter.Panel2.Controls.Add(filterPanel); + + // fileList + fileList.Dock = System.Windows.Forms.DockStyle.Fill; + fileList.AllowDrop = true; + + // filterPanel + filterPanel.Dock = System.Windows.Forms.DockStyle.Fill; + + // rightPanel + rightPanel.Dock = System.Windows.Forms.DockStyle.Fill; + rightPanel.Controls.Add(entityView1); + rightPanel.Controls.Add(detailBar); + // entityView1 - // entityView1.BackColor = System.Drawing.Color.FromArgb(33, 40, 48); entityView1.Cursor = System.Windows.Forms.Cursors.Cross; entityView1.Dock = System.Windows.Forms.DockStyle.Fill; - entityView1.Location = new System.Drawing.Point(0, 0); - entityView1.Name = "entityView1"; - entityView1.Size = new System.Drawing.Size(615, 337); - entityView1.TabIndex = 0; - entityView1.Text = "entityView1"; - // + + // detailBar + detailBar.Dock = System.Windows.Forms.DockStyle.Bottom; + detailBar.Height = 36; + detailBar.BackColor = System.Drawing.Color.FromArgb(245, 245, 245); + detailBar.Padding = new System.Windows.Forms.Padding(6, 4, 6, 4); + + lblQty = new System.Windows.Forms.Label { Text = "Qty:", AutoSize = true, Location = new System.Drawing.Point(6, 9), Font = new System.Drawing.Font("Segoe UI", 9f) }; + numQuantity.Location = new System.Drawing.Point(35, 5); + numQuantity.Size = new System.Drawing.Size(50, 24); + numQuantity.Minimum = 1; + numQuantity.Maximum = 9999; + numQuantity.Value = 1; + numQuantity.Font = new System.Drawing.Font("Segoe UI", 9f); + + lblCust = new System.Windows.Forms.Label { Text = "Customer:", AutoSize = true, Location = new System.Drawing.Point(95, 9), Font = new System.Drawing.Font("Segoe UI", 9f) }; + txtCustomer.Location = new System.Drawing.Point(165, 5); + txtCustomer.Size = new System.Drawing.Size(120, 24); + txtCustomer.Font = new System.Drawing.Font("Segoe UI", 9f); + txtCustomer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + + lblDimensions = new System.Windows.Forms.Label { AutoSize = true, Location = new System.Drawing.Point(300, 9), Font = new System.Drawing.Font("Segoe UI", 9f), ForeColor = System.Drawing.Color.Gray }; + lblEntityCount = new System.Windows.Forms.Label { AutoSize = true, Location = new System.Drawing.Point(420, 9), Font = new System.Drawing.Font("Segoe UI", 9f), ForeColor = System.Drawing.Color.Gray }; + + btnSplit = new System.Windows.Forms.Button { Text = "Split...", Location = new System.Drawing.Point(520, 4), Size = new System.Drawing.Size(60, 28), FlatStyle = System.Windows.Forms.FlatStyle.Flat, Font = new System.Drawing.Font("Segoe UI", 9f) }; + + lblDetect = new System.Windows.Forms.Label { Text = "Bends:", AutoSize = true, Location = new System.Drawing.Point(590, 9), Font = new System.Drawing.Font("Segoe UI", 9f) }; + cboBendDetector.Location = new System.Drawing.Point(638, 5); + cboBendDetector.Size = new System.Drawing.Size(100, 24); + cboBendDetector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + cboBendDetector.Font = new System.Drawing.Font("Segoe UI", 9f); + + detailBar.Controls.AddRange(new System.Windows.Forms.Control[] { + lblQty, numQuantity, lblCust, txtCustomer, + lblDimensions, lblEntityCount, btnSplit, + lblDetect, cboBendDetector + }); + // bottomPanel1 - // + bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + bottomPanel1.Height = 50; bottomPanel1.Controls.Add(cancelButton); bottomPanel1.Controls.Add(acceptButton); - bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; - bottomPanel1.Location = new System.Drawing.Point(0, 643); - bottomPanel1.Name = "bottomPanel1"; - bottomPanel1.Size = new System.Drawing.Size(928, 50); - bottomPanel1.TabIndex = 1; - // - // cancelButton - // + cancelButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; cancelButton.Location = new System.Drawing.Point(826, 10); - cancelButton.Name = "cancelButton"; cancelButton.Size = new System.Drawing.Size(90, 28); - cancelButton.TabIndex = 1; cancelButton.Text = "Cancel"; - cancelButton.UseVisualStyleBackColor = true; - // - // acceptButton - // + cancelButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + cancelButton.Font = new System.Drawing.Font("Segoe UI", 9f); + acceptButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; acceptButton.Location = new System.Drawing.Point(730, 10); - acceptButton.Name = "acceptButton"; acceptButton.Size = new System.Drawing.Size(90, 28); - acceptButton.TabIndex = 0; acceptButton.Text = "Accept"; - acceptButton.UseVisualStyleBackColor = true; - // + acceptButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + acceptButton.Font = new System.Drawing.Font("Segoe UI", 9f); + // CadConverterForm - // AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; - ClientSize = new System.Drawing.Size(928, 693); - Controls.Add(splitContainer1); + ClientSize = new System.Drawing.Size(1024, 720); + Controls.Add(mainSplitter); Controls.Add(bottomPanel1); - Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + Font = new System.Drawing.Font("Segoe UI", 9f); MinimizeBox = false; - Name = "CadConverterForm"; ShowIcon = false; ShowInTaskbar = false; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; Text = "CAD Converter"; - splitContainer1.Panel1.ResumeLayout(false); - splitContainer1.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)splitContainer1).EndInit(); - splitContainer1.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit(); - splitContainer2.Panel1.ResumeLayout(false); - splitContainer2.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)splitContainer2).EndInit(); - splitContainer2.ResumeLayout(false); - tabControl1.ResumeLayout(false); - tabPage1.ResumeLayout(false); - tabPage2.ResumeLayout(false); - tabPage3.ResumeLayout(false); + AllowDrop = true; + + ((System.ComponentModel.ISupportInitialize)mainSplitter).EndInit(); + mainSplitter.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)sidebarSplitter).EndInit(); + sidebarSplitter.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)numQuantity).EndInit(); bottomPanel1.ResumeLayout(false); ResumeLayout(false); - } #endregion + private System.Windows.Forms.SplitContainer mainSplitter; + private System.Windows.Forms.SplitContainer sidebarSplitter; + private Controls.FileListControl fileList; + private Controls.FilterPanel filterPanel; + private System.Windows.Forms.Panel rightPanel; + private Controls.EntityView entityView1; + private System.Windows.Forms.Panel detailBar; + private System.Windows.Forms.Label lblDimensions; + private System.Windows.Forms.Label lblEntityCount; + private System.Windows.Forms.NumericUpDown numQuantity; + private System.Windows.Forms.TextBox txtCustomer; + private System.Windows.Forms.Button btnSplit; + private System.Windows.Forms.ComboBox cboBendDetector; + private System.Windows.Forms.Label lblQty; + private System.Windows.Forms.Label lblCust; + private System.Windows.Forms.Label lblDetect; + private Controls.BottomPanel bottomPanel1; private System.Windows.Forms.Button acceptButton; private System.Windows.Forms.Button cancelButton; - private Controls.BottomPanel bottomPanel1; - private System.Windows.Forms.SplitContainer splitContainer1; - private System.Windows.Forms.DataGridView dataGridView1; - private System.Windows.Forms.SplitContainer splitContainer2; - private System.Windows.Forms.TabControl tabControl1; - private System.Windows.Forms.TabPage tabPage1; - private System.Windows.Forms.TabPage tabPage2; - private Controls.EntityView entityView1; - private System.Windows.Forms.CheckedListBox checkedListBox1; - private System.Windows.Forms.TabPage tabPage3; - private System.Windows.Forms.CheckedListBox checkedListBox2; - private System.Windows.Forms.CheckedListBox checkedListBox3; } -} \ No newline at end of file +} diff --git a/OpenNest/Forms/CadConverterForm.cs b/OpenNest/Forms/CadConverterForm.cs index c5434bf..48fc4d0 100644 --- a/OpenNest/Forms/CadConverterForm.cs +++ b/OpenNest/Forms/CadConverterForm.cs @@ -1,10 +1,13 @@ -using OpenNest.CNC; +using OpenNest.Bending; +using OpenNest.CNC; +using OpenNest.Controls; using OpenNest.Converters; using OpenNest.Geometry; using OpenNest.IO; +using OpenNest.IO.Bending; using OpenNest.Properties; +using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; @@ -16,206 +19,203 @@ namespace OpenNest.Forms { public partial class CadConverterForm : Form { + private static int colorIndex; + public CadConverterForm() { InitializeComponent(); - Items = new BindingList(); - dataGridView1.DataSource = Items; - dataGridView1.DataError += dataGridView1_DataError; + fileList.SelectedIndexChanged += OnFileSelected; + filterPanel.FilterChanged += OnFilterChanged; + filterPanel.BendLineSelected += OnBendLineSelected; + filterPanel.BendLineRemoved += OnBendLineRemoved; + btnSplit.Click += OnSplitClicked; + numQuantity.ValueChanged += OnQuantityChanged; + txtCustomer.TextChanged += OnCustomerChanged; + cboBendDetector.SelectedIndexChanged += OnBendDetectorChanged; - var splitColumn = new DataGridViewButtonColumn - { - Name = "Split", - HeaderText = "", - Text = "Split", - UseColumnTextForButtonValue = true, - Width = 50 - }; - dataGridView1.Columns.Add(splitColumn); - dataGridView1.CellContentClick += OnCellContentClick; + // Populate bend detector dropdown + cboBendDetector.Items.Add("Auto"); + foreach (var detector in BendDetectorRegistry.Detectors) + cboBendDetector.Items.Add(detector.Name); + cboBendDetector.SelectedIndex = 0; + + // Drag & drop + AllowDrop = true; + DragEnter += OnDragEnter; + DragDrop += OnDragDrop; } - private BindingList Items { get; set; } + private FileListItem CurrentItem => fileList.SelectedItem; - private void SetRotation(Shape shape, RotationType rotation) + #region File Import + + public void AddFile(string file) => AddFile(file, 0, null); + + private void AddFile(string file, int detectorIndex, string detectorName) { try { - var dir = shape.ToPolygon(3).RotationDirection(); + var importer = new DxfImporter(); + importer.SplinePrecision = Settings.Default.ImportSplinePrecision; + var result = importer.Import(file); - if (dir != rotation) - shape.Reverse(); - } - catch { } - } + if (result.Entities.Count == 0) + return; - private void LoadItem(CadConverterItem item) - { - entityView1.Entities.Clear(); - entityView1.Entities.AddRange(item.Entities); - entityView1.ZoomToFit(); + // Compute bounds + var bounds = result.Entities.GetBoundingBox(); - item.Entities.ForEach(e => e.IsVisible = true); - - // Layers - checkedListBox1.Items.Clear(); - - var layers = item.Entities - .Where(e => e.Layer != null) - .Select(e => e.Layer.Name) - .ToList() - .Distinct(); - - foreach (var layer in layers) - checkedListBox1.Items.Add(layer, true); - - // Colors - checkedListBox2.Items.Clear(); - - var colors = item.Entities - .Select(e => e.Color.ToArgb()) - .Distinct() - .Select(argb => new ColorItem(Color.FromArgb(argb))); - - foreach (var color in colors) - checkedListBox2.Items.Add(color, false); - - // Line Types - checkedListBox3.Items.Clear(); - - var lineTypes = item.Entities - .Select(e => e.LineTypeName ?? "Continuous") - .Distinct(); - - foreach (var lineType in lineTypes) - checkedListBox3.Items.Add(lineType, false); - } - - private static int colorIndex; - - private static Color GetNextColor() - { - var color = ColorScheme.PartColors[colorIndex % ColorScheme.PartColors.Length]; - colorIndex++; - return color; - } - - public List GetDrawings() - { - var drawings = new List(); - - foreach (var item in Items) - { - if (item.SplitDrawings != null && item.SplitDrawings.Count > 0) + // Detect bends (detectorIndex/Name captured on UI thread) + var bends = new List(); + if (result.Document != null) { - foreach (var splitDrawing in item.SplitDrawings) - splitDrawing.Color = GetNextColor(); - drawings.AddRange(item.SplitDrawings); - continue; + bends = detectorIndex == 0 + ? BendDetectorRegistry.AutoDetect(result.Document) + : BendDetectorRegistry.GetByName(detectorName) + ?.DetectBends(result.Document) + ?? new List(); } - var entities = item.Entities.Where(e => e.Layer.IsVisible && e.IsVisible).ToList(); - - if (entities.Count == 0) - continue; - - var drawing = new Drawing(item.Name); - drawing.Color = GetNextColor(); - drawing.Customer = item.Customer; - drawing.Source.Path = item.Path; - drawing.Quantity.Required = item.Quantity; - - var shape = new ShapeProfile(entities); - - SetRotation(shape.Perimeter, RotationType.CW); - - foreach (var cutout in shape.Cutouts) - SetRotation(cutout, RotationType.CCW); - - entities = new List(); - entities.AddRange(shape.Perimeter.Entities); - - shape.Cutouts.ForEach(cutout => entities.AddRange(cutout.Entities)); - - var pgm = ConvertGeometry.ToProgram(entities); - var firstCode = pgm[0]; - - if (firstCode.Type == CodeType.RapidMove) - { - var rapid = (RapidMove)firstCode; - - drawing.Source.Offset = rapid.EndPoint; - - pgm.Offset(-rapid.EndPoint); - pgm.Codes.RemoveAt(0); - } - - drawing.Program = pgm; - drawings.Add(drawing); - - Thread.Sleep(20); - } - - return drawings; - } - - private CadConverterItem CurrentItem - { - get - { - return dataGridView1.SelectedRows.Count != 0 - ? Items[dataGridView1.SelectedRows[0].Index] - : null; - } - } - - public void AddFile(string file) - { - var importer = new DxfImporter(); - importer.SplinePrecision = Settings.Default.ImportSplinePrecision; - - var entities = new List(); - - if (!importer.GetGeometry(file, out entities)) - { - MessageBox.Show("Failed to import file \"" + file + "\""); - return; - } - - lock (Items) - { - Items.Add(new CadConverterItem + var item = new FileListItem { Name = Path.GetFileNameWithoutExtension(file), - Entities = entities, + Entities = result.Entities, Path = file, Quantity = 1, - Customer = string.Empty - }); + Customer = string.Empty, + Bends = bends, + Bounds = bounds, + EntityCount = result.Entities.Count + }; + + if (InvokeRequired) + BeginInvoke((Action)(() => fileList.AddItem(item))); + else + fileList.AddItem(item); + } + catch (Exception ex) + { + MessageBox.Show($"Error importing \"{file}\": {ex.Message}"); } } public void AddFiles(IEnumerable files) { - Parallel.ForEach(files, AddFile); + var fileArray = files.ToArray(); + // Capture UI state on main thread before entering parallel loop + var detectorIndex = cboBendDetector.SelectedIndex; + var detectorName = cboBendDetector.SelectedItem?.ToString(); + + System.Threading.Tasks.Task.Run(() => + { + Parallel.ForEach(fileArray, file => AddFile(file, detectorIndex, detectorName)); + }); } - private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) + #endregion + + #region Event Handlers + + private void OnFileSelected(object sender, int index) { - MessageBox.Show(e.Exception.Message); + var item = CurrentItem; + if (item == null) + { + ClearDetailBar(); + return; + } + + LoadItem(item); } - private void OnCellContentClick(object sender, DataGridViewCellEventArgs e) + private void LoadItem(FileListItem item) { - if (e.RowIndex < 0) return; - if (dataGridView1.Columns[e.ColumnIndex].Name != "Split") return; + entityView1.ClearPenCache(); + entityView1.Entities.Clear(); + entityView1.Entities.AddRange(item.Entities); + + item.Entities.ForEach(e => e.IsVisible = true); + if (item.Entities.Any(e => e.Layer != null)) + item.Entities.ForEach(e => e.Layer.IsVisible = true); + + filterPanel.LoadItem(item.Entities, item.Bends); + + numQuantity.Value = item.Quantity; + txtCustomer.Text = item.Customer ?? ""; + + var bounds = item.Bounds; + lblDimensions.Text = bounds != null + ? $"{bounds.Width:0.#} x {bounds.Length:0.#}" + : ""; + lblEntityCount.Text = $"{item.EntityCount} entities"; + + entityView1.ZoomToFit(); + } + + private void ClearDetailBar() + { + numQuantity.Value = 1; + txtCustomer.Text = ""; + lblDimensions.Text = ""; + lblEntityCount.Text = ""; + entityView1.Entities.Clear(); + entityView1.Invalidate(); + } + + private void OnFilterChanged(object sender, EventArgs e) + { + var item = CurrentItem; + if (item == null) return; + + filterPanel.ApplyFilters(item.Entities); + entityView1.Invalidate(); + } + + private void OnBendLineSelected(object sender, int index) + { + // TODO: Highlight bend line in EntityView + entityView1.Invalidate(); + } + + private void OnBendLineRemoved(object sender, int index) + { + var item = CurrentItem; + if (item == null || index < 0 || index >= item.Bends.Count) return; + + item.Bends.RemoveAt(index); + filterPanel.LoadItem(item.Entities, item.Bends); + entityView1.Invalidate(); + } + + private void OnQuantityChanged(object sender, EventArgs e) + { + var item = CurrentItem; + if (item != null) + item.Quantity = (int)numQuantity.Value; + } + + private void OnCustomerChanged(object sender, EventArgs e) + { + var item = CurrentItem; + if (item != null) + item.Customer = txtCustomer.Text; + } + + private void OnBendDetectorChanged(object sender, EventArgs e) + { + // Re-run bend detection on current item if it has a document + // For now, bend detection only runs at import time + } + + private void OnSplitClicked(object sender, EventArgs e) + { + var item = CurrentItem; + if (item == null) return; - var item = Items[e.RowIndex]; var entities = item.Entities.Where(en => en.Layer.IsVisible && en.IsVisible).ToList(); if (entities.Count == 0) return; - // Build a temporary drawing from the item's entities (same logic as GetDrawings) var shape = new ShapeProfile(entities); SetRotation(shape.Perimeter, RotationType.CW); foreach (var cutout in shape.Cutouts) @@ -236,153 +236,168 @@ namespace OpenNest.Forms var drawing = new Drawing(item.Name, pgm); using var form = new SplitDrawingForm(drawing); - if (form.ShowDialog(this) == DialogResult.OK && form.ResultDrawings?.Count > 1) + if (form.ShowDialog(this) != DialogResult.OK || form.ResultDrawings?.Count <= 1) + return; + + // Write split DXF files and re-import + var sourceDir = Path.GetDirectoryName(item.Path); + var baseName = Path.GetFileNameWithoutExtension(item.Path); + var writableDir = Directory.Exists(sourceDir) && IsDirectoryWritable(sourceDir) + ? sourceDir + : Path.GetTempPath(); + + var index = fileList.SelectedIndex; + var newItems = new List(); + + for (var i = 0; i < form.ResultDrawings.Count; i++) { - item.SplitDrawings = form.ResultDrawings; - dataGridView1.InvalidateRow(e.RowIndex); + var splitName = $"{baseName}_split{i + 1}.dxf"; + var splitPath = GetUniquePath(Path.Combine(writableDir, splitName)); + + // TODO: Use SplitDxfWriter when implemented + // For now, export via DxfExporter + var exporter = new DxfExporter(); + using var stream = new FileStream(splitPath, FileMode.Create); + exporter.ExportProgram(form.ResultDrawings[i].Program, stream); + + newItems.Add(splitPath); + } + + // Remove original and add split files + fileList.RemoveAt(index); + foreach (var path in newItems) + AddFile(path); + + if (writableDir != sourceDir) + MessageBox.Show($"Split files written to: {writableDir}", "Split Output", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void OnDragEnter(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effect = DragDropEffects.Copy; + } + + private void OnDragDrop(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + var files = (string[])e.Data.GetData(DataFormats.FileDrop); + var dxfFiles = files.Where(f => + f.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)).ToArray(); + if (dxfFiles.Length > 0) + AddFiles(dxfFiles); } } - private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) - { - dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); - } - - private void dataGridView1_SelectionChanged(object sender, System.EventArgs e) - { - var currentItem = CurrentItem; - - if (currentItem != null) - LoadItem(currentItem); - } - - #region Colors - - private static Color[] Colors = new Color[] - { - Color.FromArgb(160, 255, 255), - Color.FromArgb(160, 255, 160), - Color.FromArgb(160, 160, 255), - Color.FromArgb(255, 255, 160), - Color.FromArgb(255, 160, 255), - Color.FromArgb(255, 160, 160), - - Color.FromArgb(200, 255, 255), - Color.FromArgb(200, 255, 200), - Color.FromArgb(200, 200, 255), - Color.FromArgb(255, 255, 200), - Color.FromArgb(255, 200, 255), - Color.FromArgb(255, 200, 200), - }; - #endregion - private void checkedListBox1_SelectedIndexChanged(object sender, System.EventArgs e) - { - var index = checkedListBox1.SelectedIndex; - var layerName = checkedListBox1.Items[index].ToString(); - var isVisible = checkedListBox1.CheckedItems.Contains(layerName); + #region Output - CurrentItem.Entities.ForEach(entity => + public List GetDrawings() + { + var drawings = new List(); + + foreach (var item in fileList.Items) { - if (entity.Layer.Name == layerName) - entity.Layer.IsVisible = isVisible; - }); + var entities = item.Entities.Where(e => e.Layer.IsVisible && e.IsVisible).ToList(); - entityView1.Invalidate(); - } + if (entities.Count == 0) + continue; - private void checkedListBox2_SelectedIndexChanged(object sender, System.EventArgs e) - { - UpdateEntityVisibility(); - } + var drawing = new Drawing(item.Name); + drawing.Color = GetNextColor(); + drawing.Customer = item.Customer; + drawing.Source.Path = item.Path; + drawing.Quantity.Required = item.Quantity; - private void checkedListBox3_SelectedIndexChanged(object sender, System.EventArgs e) - { - UpdateEntityVisibility(); - } + // Copy bends + if (item.Bends != null) + drawing.Bends.AddRange(item.Bends); - private void UpdateEntityVisibility() - { - var item = CurrentItem; - if (item == null) return; + var shape = new ShapeProfile(entities); - var checkedColors = new HashSet(); - for (var i = 0; i < checkedListBox2.Items.Count; i++) - { - if (checkedListBox2.GetItemChecked(i)) - checkedColors.Add(((ColorItem)checkedListBox2.Items[i]).Argb); + SetRotation(shape.Perimeter, RotationType.CW); + + foreach (var cutout in shape.Cutouts) + SetRotation(cutout, RotationType.CCW); + + entities = new List(); + entities.AddRange(shape.Perimeter.Entities); + shape.Cutouts.ForEach(cutout => entities.AddRange(cutout.Entities)); + + var pgm = ConvertGeometry.ToProgram(entities); + var firstCode = pgm[0]; + + if (firstCode.Type == CodeType.RapidMove) + { + var rapid = (RapidMove)firstCode; + drawing.Source.Offset = rapid.EndPoint; + pgm.Offset(-rapid.EndPoint); + pgm.Codes.RemoveAt(0); + } + + drawing.Program = pgm; + drawings.Add(drawing); + + Thread.Sleep(20); } - var checkedLineTypes = new HashSet(); - for (var i = 0; i < checkedListBox3.Items.Count; i++) + return drawings; + } + + #endregion + + #region Helpers + + private static void SetRotation(Shape shape, RotationType rotation) + { + try { - if (checkedListBox3.GetItemChecked(i)) - checkedLineTypes.Add(checkedListBox3.Items[i].ToString()); + var dir = shape.ToPolygon(3).RotationDirection(); + if (dir != rotation) shape.Reverse(); + } + catch { } + } + + private static Color GetNextColor() + { + var color = ColorScheme.PartColors[colorIndex % ColorScheme.PartColors.Length]; + colorIndex++; + return color; + } + + private static bool IsDirectoryWritable(string path) + { + try + { + var testFile = Path.Combine(path, $".writetest_{Guid.NewGuid()}"); + File.WriteAllText(testFile, ""); + File.Delete(testFile); + return true; + } + catch { return false; } + } + + private static string GetUniquePath(string path) + { + if (!File.Exists(path)) return path; + + var dir = Path.GetDirectoryName(path); + var name = Path.GetFileNameWithoutExtension(path); + var ext = Path.GetExtension(path); + var counter = 2; + + while (File.Exists(path)) + { + path = Path.Combine(dir, $"{name}_{counter}{ext}"); + counter++; } - item.Entities.ForEach(entity => - { - entity.IsVisible = !checkedColors.Contains(entity.Color.ToArgb()) - && !checkedLineTypes.Contains(entity.LineTypeName ?? "Continuous"); - }); - - entityView1.Invalidate(); + return path; } - private void checkedListBox2_DrawItem(object sender, DrawItemEventArgs e) - { - if (e.Index < 0) return; - - e.DrawBackground(); - - var colorItem = (ColorItem)checkedListBox2.Items[e.Index]; - var swatchRect = new Rectangle(e.Bounds.Left + 20, e.Bounds.Top + 2, 16, e.Bounds.Height - 4); - - using (var brush = new SolidBrush(colorItem.Color)) - e.Graphics.FillRectangle(brush, swatchRect); - - e.Graphics.DrawRectangle(Pens.Gray, swatchRect); - - var textRect = new Rectangle(swatchRect.Right + 4, e.Bounds.Top, e.Bounds.Width - swatchRect.Right - 4, e.Bounds.Height); - TextRenderer.DrawText(e.Graphics, colorItem.ToString(), e.Font, textRect, e.ForeColor, TextFormatFlags.VerticalCenter); - - e.DrawFocusRectangle(); - } + #endregion } - - class CadConverterItem - { - public string Name { get; set; } - - public string Customer { get; set; } - - public int Quantity { get; set; } - - [ReadOnly(true)] - public string Path { get; set; } - - [Browsable(false)] - public List Entities { get; set; } - - [Browsable(false)] - public List SplitDrawings { get; set; } - } - - class ColorItem - { - public int Argb { get; } - public Color Color { get; } - - public ColorItem(Color color) - { - Color = color; - Argb = color.ToArgb(); - } - - public override string ToString() => $"RGB({Color.R}, {Color.G}, {Color.B})"; - public override bool Equals(object obj) => obj is ColorItem other && Argb == other.Argb; - public override int GetHashCode() => Argb; - } - }