2 Commits
Author SHA1 Message Date
ajandClaude Opus 4.6 f208569e72 fix: improve CadConverter sidebar layout and bend line visibility
Replace sidebar Panel+Splitter with SplitContainer for resizable file
list / filter panel. Sort file list alphabetically on insert. Widen bend
line dash spacing and draw ETCH layer entities on top of bend lines so
etch marks are visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:51:21 -04:00
ajandClaude Opus 4.6 1ffe904892 fix: exclude BEND layer entities from DXF geometry import
BEND layer lines were being imported as cut geometry alongside the
separate Bend object detection, causing duplicate dark lines in nests.
Skip BEND layer entities in DxfImporter since bend detection reads
directly from the raw CadDocument.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:17:33 -04:00
4 changed files with 203 additions and 131 deletions
+10
View File
@@ -24,6 +24,11 @@ namespace OpenNest.IO
foreach (var entity in doc.Entities) foreach (var entity in doc.Entities)
{ {
// Skip bend line entities — they are converted to Bend objects
// separately via bend detection, not cut geometry.
if (IsBendLayer(entity.Layer?.Name))
continue;
switch (entity) switch (entity)
{ {
case ACadSharp.Entities.Line line: case ACadSharp.Entities.Line line:
@@ -131,5 +136,10 @@ namespace OpenNest.IO
return success; return success;
} }
private static bool IsBendLayer(string layerName)
{
return string.Equals(layerName, "BEND", System.StringComparison.OrdinalIgnoreCase);
}
} }
} }
+13 -2
View File
@@ -48,12 +48,20 @@ namespace OpenNest.Controls
foreach (var entity in Entities) foreach (var entity in Entities)
{ {
if (IsEtchLayer(entity.Layer)) continue;
var pen = GetEntityPen(entity.Color); var pen = GetEntityPen(entity.Color);
DrawEntity(e.Graphics, entity, pen); DrawEntity(e.Graphics, entity, pen);
} }
DrawBendLines(e.Graphics); DrawBendLines(e.Graphics);
foreach (var entity in Entities)
{
if (!IsEtchLayer(entity.Layer)) continue;
var pen = GetEntityPen(entity.Color);
DrawEntity(e.Graphics, entity, pen);
}
#if DRAW_OFFSET #if DRAW_OFFSET
var offsetShape = new Shape(); var offsetShape = new Shape();
@@ -138,6 +146,9 @@ namespace OpenNest.Controls
penCache.Clear(); penCache.Clear();
} }
private static bool IsEtchLayer(Layer layer) =>
string.Equals(layer?.Name, "ETCH", System.StringComparison.OrdinalIgnoreCase);
private void DrawBendLines(Graphics g) private void DrawBendLines(Graphics g)
{ {
if (Bends == null || Bends.Count == 0) if (Bends == null || Bends.Count == 0)
@@ -145,11 +156,11 @@ namespace OpenNest.Controls
using var bendPen = new Pen(Color.Yellow, 1.5f) using var bendPen = new Pen(Color.Yellow, 1.5f)
{ {
DashStyle = DashStyle.Dash DashPattern = new float[] { 8, 6 }
}; };
using var selectedPen = new Pen(Color.Cyan, 2.5f) using var selectedPen = new Pen(Color.Cyan, 2.5f)
{ {
DashStyle = DashStyle.Dash DashPattern = new float[] { 8, 6 }
}; };
for (var i = 0; i < Bends.Count; i++) for (var i = 0; i < Bends.Count; i++)
+9 -1
View File
@@ -62,12 +62,20 @@ namespace OpenNest.Controls
public void AddItem(FileListItem item) public void AddItem(FileListItem item)
{ {
items.Add(item); var index = items.BinarySearch(item, Comparer<FileListItem>.Create(
(a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)));
if (index < 0) index = ~index;
items.Insert(index, item);
if (items.Count == 1) if (items.Count == 1)
{ {
selectedIndex = 0; selectedIndex = 0;
SelectedIndexChanged?.Invoke(this, selectedIndex); SelectedIndexChanged?.Invoke(this, selectedIndex);
} }
else if (selectedIndex >= 0 && index <= selectedIndex)
{
selectedIndex++;
}
Invalidate(); Invalidate();
} }
+135 -92
View File
@@ -15,10 +15,9 @@ namespace OpenNest.Forms
private void InitializeComponent() private void InitializeComponent()
{ {
sidebarPanel = new System.Windows.Forms.Panel(); sidebarSplit = new System.Windows.Forms.SplitContainer();
fileList = new OpenNest.Controls.FileListControl(); fileList = new OpenNest.Controls.FileListControl();
filterPanel = new OpenNest.Controls.FilterPanel(); filterPanel = new OpenNest.Controls.FilterPanel();
splitterSidebar = new System.Windows.Forms.Splitter();
entityView1 = new OpenNest.Controls.EntityView(); entityView1 = new OpenNest.Controls.EntityView();
detailBar = new System.Windows.Forms.FlowLayoutPanel(); detailBar = new System.Windows.Forms.FlowLayoutPanel();
lblQty = new System.Windows.Forms.Label(); lblQty = new System.Windows.Forms.Label();
@@ -33,204 +32,248 @@ namespace OpenNest.Forms
bottomPanel1 = new OpenNest.Controls.BottomPanel(); bottomPanel1 = new OpenNest.Controls.BottomPanel();
cancelButton = new System.Windows.Forms.Button(); cancelButton = new System.Windows.Forms.Button();
acceptButton = new System.Windows.Forms.Button(); acceptButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)sidebarSplit).BeginInit();
sidebarSplit.Panel1.SuspendLayout();
sidebarSplit.Panel2.SuspendLayout();
sidebarSplit.SuspendLayout();
detailBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)numQuantity).BeginInit(); ((System.ComponentModel.ISupportInitialize)numQuantity).BeginInit();
sidebarPanel.SuspendLayout();
bottomPanel1.SuspendLayout(); bottomPanel1.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// sidebarPanel (Left dock — contains file list + filter panel) // sidebarSplit
// //
sidebarPanel.Dock = System.Windows.Forms.DockStyle.Left; sidebarSplit.Dock = System.Windows.Forms.DockStyle.Left;
sidebarPanel.Name = "sidebarPanel"; sidebarSplit.Location = new System.Drawing.Point(0, 0);
sidebarPanel.Size = new System.Drawing.Size(260, 670); sidebarSplit.Name = "sidebarSplit";
sidebarPanel.Controls.Add(filterPanel); sidebarSplit.Orientation = System.Windows.Forms.Orientation.Horizontal;
sidebarPanel.Controls.Add(fileList);
// //
// fileList (Top of sidebar) // sidebarSplit.Panel1
//
sidebarSplit.Panel1.Controls.Add(fileList);
//
// sidebarSplit.Panel2
//
sidebarSplit.Panel2.Controls.Add(filterPanel);
sidebarSplit.Size = new System.Drawing.Size(260, 670);
sidebarSplit.SplitterDistance = 300;
sidebarSplit.SplitterWidth = 5;
sidebarSplit.TabIndex = 2;
//
// fileList
// //
fileList.Dock = System.Windows.Forms.DockStyle.Top;
fileList.AllowDrop = true; fileList.AllowDrop = true;
fileList.BackColor = System.Drawing.Color.White;
fileList.Dock = System.Windows.Forms.DockStyle.Fill;
fileList.Font = new System.Drawing.Font("Segoe UI", 9F);
fileList.Location = new System.Drawing.Point(0, 0);
fileList.Name = "fileList"; fileList.Name = "fileList";
fileList.Size = new System.Drawing.Size(260, 300); fileList.Size = new System.Drawing.Size(260, 300);
fileList.TabIndex = 0;
// //
// filterPanel (Fill remainder of sidebar) // filterPanel
// //
filterPanel.AutoScroll = true;
filterPanel.BackColor = System.Drawing.Color.White;
filterPanel.Dock = System.Windows.Forms.DockStyle.Fill; filterPanel.Dock = System.Windows.Forms.DockStyle.Fill;
filterPanel.Location = new System.Drawing.Point(0, 0);
filterPanel.Name = "filterPanel"; filterPanel.Name = "filterPanel";
filterPanel.Size = new System.Drawing.Size(260, 370); filterPanel.Size = new System.Drawing.Size(260, 365);
filterPanel.TabIndex = 0;
// //
// splitterSidebar (between sidebar and preview) // entityView1
//
splitterSidebar.Location = new System.Drawing.Point(260, 0);
splitterSidebar.Name = "splitterSidebar";
splitterSidebar.Size = new System.Drawing.Size(3, 670);
splitterSidebar.TabStop = false;
//
// entityView1 (Fill — main preview area)
// //
entityView1.BackColor = System.Drawing.Color.FromArgb(33, 40, 48); entityView1.BackColor = System.Drawing.Color.FromArgb(33, 40, 48);
entityView1.Cursor = System.Windows.Forms.Cursors.Cross; entityView1.Cursor = System.Windows.Forms.Cursors.Cross;
entityView1.Dock = System.Windows.Forms.DockStyle.Fill; entityView1.Dock = System.Windows.Forms.DockStyle.Fill;
entityView1.Location = new System.Drawing.Point(260, 0);
entityView1.Name = "entityView1"; entityView1.Name = "entityView1";
entityView1.Size = new System.Drawing.Size(761, 634); entityView1.Size = new System.Drawing.Size(764, 634);
entityView1.TabIndex = 0;
// //
// detailBar (Bottom of preview area) // detailBar
// //
detailBar.Dock = System.Windows.Forms.DockStyle.Bottom;
detailBar.Name = "detailBar";
detailBar.Size = new System.Drawing.Size(761, 36);
detailBar.BackColor = System.Drawing.Color.FromArgb(245, 245, 245); detailBar.BackColor = System.Drawing.Color.FromArgb(245, 245, 245);
detailBar.Controls.Add(lblQty);
detailBar.Controls.Add(numQuantity);
detailBar.Controls.Add(lblCust);
detailBar.Controls.Add(txtCustomer);
detailBar.Controls.Add(lblDimensions);
detailBar.Controls.Add(lblEntityCount);
detailBar.Controls.Add(btnSplit);
detailBar.Controls.Add(lblDetect);
detailBar.Controls.Add(cboBendDetector);
detailBar.Dock = System.Windows.Forms.DockStyle.Bottom;
detailBar.Location = new System.Drawing.Point(260, 634);
detailBar.Name = "detailBar";
detailBar.Padding = new System.Windows.Forms.Padding(4, 6, 4, 4); detailBar.Padding = new System.Windows.Forms.Padding(4, 6, 4, 4);
detailBar.Size = new System.Drawing.Size(764, 36);
detailBar.TabIndex = 1;
detailBar.WrapContents = false; detailBar.WrapContents = false;
// //
// lblQty // lblQty
// //
lblQty.Text = "Qty:";
lblQty.AutoSize = true; lblQty.AutoSize = true;
lblQty.Font = new System.Drawing.Font("Segoe UI", 9f); lblQty.Font = new System.Drawing.Font("Segoe UI", 9F);
lblQty.Location = new System.Drawing.Point(6, 9);
lblQty.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0); lblQty.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0);
lblQty.Name = "lblQty";
lblQty.Size = new System.Drawing.Size(29, 15);
lblQty.TabIndex = 0;
lblQty.Text = "Qty:";
// //
// numQuantity // numQuantity
// //
numQuantity.Size = new System.Drawing.Size(50, 24); numQuantity.Font = new System.Drawing.Font("Segoe UI", 9F);
numQuantity.Minimum = 1; numQuantity.Location = new System.Drawing.Point(37, 6);
numQuantity.Maximum = 9999;
numQuantity.Value = 1;
numQuantity.Font = new System.Drawing.Font("Segoe UI", 9f);
numQuantity.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0); numQuantity.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0);
numQuantity.Maximum = new decimal(new int[] { 9999, 0, 0, 0 });
numQuantity.Minimum = new decimal(new int[] { 1, 0, 0, 0 });
numQuantity.Name = "numQuantity";
numQuantity.Size = new System.Drawing.Size(50, 23);
numQuantity.TabIndex = 1;
numQuantity.Value = new decimal(new int[] { 1, 0, 0, 0 });
// //
// lblCust // lblCust
// //
lblCust.Text = "Customer:";
lblCust.AutoSize = true; lblCust.AutoSize = true;
lblCust.Font = new System.Drawing.Font("Segoe UI", 9f); lblCust.Font = new System.Drawing.Font("Segoe UI", 9F);
lblCust.Location = new System.Drawing.Point(97, 9);
lblCust.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0); lblCust.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0);
lblCust.Name = "lblCust";
lblCust.Size = new System.Drawing.Size(62, 15);
lblCust.TabIndex = 2;
lblCust.Text = "Customer:";
// //
// txtCustomer // txtCustomer
// //
txtCustomer.Size = new System.Drawing.Size(100, 24);
txtCustomer.Font = new System.Drawing.Font("Segoe UI", 9f);
txtCustomer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; txtCustomer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
txtCustomer.Font = new System.Drawing.Font("Segoe UI", 9F);
txtCustomer.Location = new System.Drawing.Point(161, 6);
txtCustomer.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0); txtCustomer.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0);
txtCustomer.Name = "txtCustomer";
txtCustomer.Size = new System.Drawing.Size(100, 23);
txtCustomer.TabIndex = 3;
// //
// lblDimensions // lblDimensions
// //
lblDimensions.AutoSize = true; lblDimensions.AutoSize = true;
lblDimensions.Font = new System.Drawing.Font("Segoe UI", 9f); lblDimensions.Font = new System.Drawing.Font("Segoe UI", 9F);
lblDimensions.ForeColor = System.Drawing.Color.Gray; lblDimensions.ForeColor = System.Drawing.Color.Gray;
lblDimensions.Location = new System.Drawing.Point(271, 9);
lblDimensions.Margin = new System.Windows.Forms.Padding(2, 3, 8, 0); lblDimensions.Margin = new System.Windows.Forms.Padding(2, 3, 8, 0);
lblDimensions.Name = "lblDimensions";
lblDimensions.Size = new System.Drawing.Size(0, 15);
lblDimensions.TabIndex = 4;
// //
// lblEntityCount // lblEntityCount
// //
lblEntityCount.AutoSize = true; lblEntityCount.AutoSize = true;
lblEntityCount.Font = new System.Drawing.Font("Segoe UI", 9f); lblEntityCount.Font = new System.Drawing.Font("Segoe UI", 9F);
lblEntityCount.ForeColor = System.Drawing.Color.Gray; lblEntityCount.ForeColor = System.Drawing.Color.Gray;
lblEntityCount.Location = new System.Drawing.Point(281, 9);
lblEntityCount.Margin = new System.Windows.Forms.Padding(2, 3, 8, 0); lblEntityCount.Margin = new System.Windows.Forms.Padding(2, 3, 8, 0);
lblEntityCount.Name = "lblEntityCount";
lblEntityCount.Size = new System.Drawing.Size(0, 15);
lblEntityCount.TabIndex = 5;
// //
// btnSplit // btnSplit
// //
btnSplit.Text = "Split...";
btnSplit.Size = new System.Drawing.Size(60, 24);
btnSplit.FlatStyle = System.Windows.Forms.FlatStyle.Flat; btnSplit.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
btnSplit.Font = new System.Drawing.Font("Segoe UI", 9f); btnSplit.Font = new System.Drawing.Font("Segoe UI", 9F);
btnSplit.Location = new System.Drawing.Point(291, 6);
btnSplit.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0); btnSplit.Margin = new System.Windows.Forms.Padding(2, 0, 8, 0);
btnSplit.Name = "btnSplit";
btnSplit.Size = new System.Drawing.Size(60, 24);
btnSplit.TabIndex = 6;
btnSplit.Text = "Split...";
// //
// lblDetect // lblDetect
// //
lblDetect.Text = "Bends:";
lblDetect.AutoSize = true; lblDetect.AutoSize = true;
lblDetect.Font = new System.Drawing.Font("Segoe UI", 9f); lblDetect.Font = new System.Drawing.Font("Segoe UI", 9F);
lblDetect.Location = new System.Drawing.Point(361, 9);
lblDetect.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0); lblDetect.Margin = new System.Windows.Forms.Padding(2, 3, 0, 0);
lblDetect.Name = "lblDetect";
lblDetect.Size = new System.Drawing.Size(42, 15);
lblDetect.TabIndex = 7;
lblDetect.Text = "Bends:";
// //
// cboBendDetector // cboBendDetector
// //
cboBendDetector.Size = new System.Drawing.Size(90, 24);
cboBendDetector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; cboBendDetector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
cboBendDetector.Font = new System.Drawing.Font("Segoe UI", 9f); cboBendDetector.Font = new System.Drawing.Font("Segoe UI", 9F);
cboBendDetector.Location = new System.Drawing.Point(405, 6);
cboBendDetector.Margin = new System.Windows.Forms.Padding(2, 0, 0, 0); cboBendDetector.Margin = new System.Windows.Forms.Padding(2, 0, 0, 0);
cboBendDetector.Name = "cboBendDetector";
detailBar.Controls.AddRange(new System.Windows.Forms.Control[] { cboBendDetector.Size = new System.Drawing.Size(90, 23);
lblQty, numQuantity, lblCust, txtCustomer, cboBendDetector.TabIndex = 8;
lblDimensions, lblEntityCount, btnSplit,
lblDetect, cboBendDetector
});
// //
// bottomPanel1 // bottomPanel1
// //
bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
bottomPanel1.Name = "bottomPanel1";
bottomPanel1.Size = new System.Drawing.Size(1024, 50);
bottomPanel1.Controls.Add(cancelButton); bottomPanel1.Controls.Add(cancelButton);
bottomPanel1.Controls.Add(acceptButton); bottomPanel1.Controls.Add(acceptButton);
bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
bottomPanel1.Location = new System.Drawing.Point(0, 670);
bottomPanel1.Name = "bottomPanel1";
bottomPanel1.Size = new System.Drawing.Size(1024, 50);
bottomPanel1.TabIndex = 3;
// //
// cancelButton // cancelButton
// //
cancelButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; cancelButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
cancelButton.Location = new System.Drawing.Point(922, 10);
cancelButton.Size = new System.Drawing.Size(90, 28);
cancelButton.Text = "Cancel";
cancelButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; cancelButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
cancelButton.Font = new System.Drawing.Font("Segoe UI", 9f); cancelButton.Font = new System.Drawing.Font("Segoe UI", 9F);
cancelButton.Location = new System.Drawing.Point(922, 10);
cancelButton.Name = "cancelButton";
cancelButton.Size = new System.Drawing.Size(90, 28);
cancelButton.TabIndex = 0;
cancelButton.Text = "Cancel";
// //
// acceptButton // acceptButton
// //
acceptButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; acceptButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK;
acceptButton.Location = new System.Drawing.Point(826, 10);
acceptButton.Size = new System.Drawing.Size(90, 28);
acceptButton.Text = "Accept";
acceptButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; acceptButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
acceptButton.Font = new System.Drawing.Font("Segoe UI", 9f); acceptButton.Font = new System.Drawing.Font("Segoe UI", 9F);
acceptButton.Location = new System.Drawing.Point(826, 10);
acceptButton.Name = "acceptButton";
acceptButton.Size = new System.Drawing.Size(90, 28);
acceptButton.TabIndex = 1;
acceptButton.Text = "Accept";
// //
// CadConverterForm // CadConverterForm
// Add order: Fill last so it gets remaining space
// //
AllowDrop = true;
AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
ClientSize = new System.Drawing.Size(1024, 720); ClientSize = new System.Drawing.Size(1024, 720);
Controls.Add(entityView1); Controls.Add(entityView1);
Controls.Add(detailBar); Controls.Add(detailBar);
Controls.Add(splitterSidebar); Controls.Add(sidebarSplit);
Controls.Add(sidebarPanel);
Controls.Add(bottomPanel1); Controls.Add(bottomPanel1);
Font = new System.Drawing.Font("Segoe UI", 9f); Font = new System.Drawing.Font("Segoe UI", 9F);
MinimizeBox = false; MinimizeBox = false;
Name = "CadConverterForm";
ShowIcon = false; ShowIcon = false;
ShowInTaskbar = false; ShowInTaskbar = false;
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
Text = "CAD Converter"; Text = "CAD Converter";
AllowDrop = true; WindowState = System.Windows.Forms.FormWindowState.Maximized;
sidebarSplit.Panel1.ResumeLayout(false);
sidebarSplit.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)sidebarSplit).EndInit();
sidebarSplit.ResumeLayout(false);
detailBar.ResumeLayout(false);
detailBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)numQuantity).EndInit(); ((System.ComponentModel.ISupportInitialize)numQuantity).EndInit();
sidebarPanel.ResumeLayout(false);
bottomPanel1.ResumeLayout(false); bottomPanel1.ResumeLayout(false);
ResumeLayout(false); ResumeLayout(false);
} }
#endregion #endregion
private System.Windows.Forms.Panel sidebarPanel; private System.Windows.Forms.SplitContainer sidebarSplit;
private System.Windows.Forms.Splitter splitterSidebar;
private Controls.FileListControl fileList; private Controls.FileListControl fileList;
private Controls.FilterPanel filterPanel; private Controls.FilterPanel filterPanel;
private Controls.EntityView entityView1; private Controls.EntityView entityView1;