diff --git a/CutList/Forms/ResultsForm.Designer.cs b/CutList/Forms/ResultsForm.Designer.cs index af422ff..9c654d5 100644 --- a/CutList/Forms/ResultsForm.Designer.cs +++ b/CutList/Forms/ResultsForm.Designer.cs @@ -31,6 +31,7 @@ this.components = new System.ComponentModel.Container(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.countDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.spacingDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.lengthDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.usedLengthDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -68,6 +69,7 @@ this.dataGridView1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single; this.dataGridView1.ColumnHeadersHeight = 30; this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.countDataGridViewTextBoxColumn, this.spacingDataGridViewTextBoxColumn, this.lengthDataGridViewTextBoxColumn, this.usedLengthDataGridViewTextBoxColumn, @@ -83,9 +85,16 @@ this.dataGridView1.Size = new System.Drawing.Size(994, 253); this.dataGridView1.TabIndex = 0; this.dataGridView1.RowEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_RowEnter); - // + // + // countDataGridViewTextBoxColumn + // + this.countDataGridViewTextBoxColumn.DataPropertyName = "Count"; + this.countDataGridViewTextBoxColumn.HeaderText = "Count"; + this.countDataGridViewTextBoxColumn.Name = "countDataGridViewTextBoxColumn"; + this.countDataGridViewTextBoxColumn.Width = 60; + // // spacingDataGridViewTextBoxColumn - // + // this.spacingDataGridViewTextBoxColumn.DataPropertyName = "Spacing"; this.spacingDataGridViewTextBoxColumn.HeaderText = "Spacing"; this.spacingDataGridViewTextBoxColumn.Name = "spacingDataGridViewTextBoxColumn"; @@ -121,8 +130,8 @@ this.utilizationDataGridViewTextBoxColumn.ReadOnly = true; // // binBindingSource - // - this.binBindingSource.DataSource = typeof(SawCut.Bin); + // + this.binBindingSource.DataSource = typeof(SawCut.BinGroup); // // splitContainer1 // @@ -260,6 +269,7 @@ private System.Windows.Forms.BindingSource binBindingSource; private System.Windows.Forms.SplitContainer splitContainer1; private System.Windows.Forms.BindingSource uIItemBindingSource; + private System.Windows.Forms.DataGridViewTextBoxColumn countDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn spacingDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn lengthDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn usedLengthDataGridViewTextBoxColumn; diff --git a/CutList/Forms/ResultsForm.cs b/CutList/Forms/ResultsForm.cs index afdfb4b..0cfdda9 100644 --- a/CutList/Forms/ResultsForm.cs +++ b/CutList/Forms/ResultsForm.cs @@ -11,6 +11,7 @@ namespace CutList.Forms public partial class ResultsForm : Form { private string filename; + private List _originalBins = new List(); public ResultsForm(string filename) { @@ -23,21 +24,27 @@ namespace CutList.Forms private void dataGridView1_RowEnter(object sender, DataGridViewCellEventArgs e) { - var selectedBin = dataGridView1.Rows[e.RowIndex].DataBoundItem as Bin; + var selectedGroup = dataGridView1.Rows[e.RowIndex].DataBoundItem as BinGroup; - if (selectedBin == null) + if (selectedGroup == null) return; - binLayoutView1.Bin = selectedBin; + var representativeBin = selectedGroup.RepresentativeBin; + binLayoutView1.Bin = representativeBin; binLayoutView1.Invalidate(); - dataGridView2.DataSource = selectedBin.Items; + dataGridView2.DataSource = representativeBin.Items; } public List Bins { - get { return dataGridView1.DataSource as List; } - set { dataGridView1.DataSource = value; } + get { return _originalBins; } + set + { + _originalBins = value; + var groupedBins = BinGroupingHelper.GroupIdenticalBins(value); + dataGridView1.DataSource = groupedBins; + } } private void saveToolStripMenuItem_Click(object sender, EventArgs e) @@ -47,7 +54,7 @@ namespace CutList.Forms public void Save(string filepath) { - var writer = new BinFileSaver(Bins); + var writer = new BinFileSaver(_originalBins); writer.SaveBinsToFile(filepath); } diff --git a/SawCut/BinComparer.cs b/SawCut/BinComparer.cs new file mode 100644 index 0000000..37fc8bc --- /dev/null +++ b/SawCut/BinComparer.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SawCut +{ + /// + /// Compares bins to determine if they are identical (same items in same order). + /// + public class BinComparer : IEqualityComparer + { + public bool Equals(Bin x, Bin y) + { + if (ReferenceEquals(x, y)) return true; + if (x == null || y == null) return false; + + // Check basic properties + if (Math.Abs(x.Length - y.Length) > 0.0001) return false; + if (Math.Abs(x.Spacing - y.Spacing) > 0.0001) return false; + + // Check item count + if (x.Items.Count != y.Items.Count) return false; + + // Check each item in order + for (int i = 0; i < x.Items.Count; i++) + { + var itemX = x.Items[i]; + var itemY = y.Items[i]; + + if (itemX.Name != itemY.Name) return false; + if (Math.Abs(itemX.Length - itemY.Length) > 0.0001) return false; + } + + return true; + } + + public int GetHashCode(Bin bin) + { + if (bin == null) return 0; + + unchecked + { + int hash = 17; + hash = hash * 23 + bin.Length.GetHashCode(); + hash = hash * 23 + bin.Spacing.GetHashCode(); + hash = hash * 23 + bin.Items.Count.GetHashCode(); + + // Include first and last item in hash for better distribution + if (bin.Items.Count > 0) + { + var firstItem = bin.Items[0]; + hash = hash * 23 + (firstItem.Name?.GetHashCode() ?? 0); + hash = hash * 23 + firstItem.Length.GetHashCode(); + + if (bin.Items.Count > 1) + { + var lastItem = bin.Items[bin.Items.Count - 1]; + hash = hash * 23 + (lastItem.Name?.GetHashCode() ?? 0); + hash = hash * 23 + lastItem.Length.GetHashCode(); + } + } + + return hash; + } + } + } + + /// + /// Helper methods for grouping bins. + /// + public static class BinGroupingHelper + { + /// + /// Groups identical bins together and returns a list of BinGroup objects. + /// + public static List GroupIdenticalBins(List bins) + { + if (bins == null || bins.Count == 0) + return new List(); + + var comparer = new BinComparer(); + var groups = new List(); + var processed = new HashSet(); + + for (int i = 0; i < bins.Count; i++) + { + if (processed.Contains(i)) + continue; + + var currentBin = bins[i]; + int count = 1; + + // Find all identical bins + for (int j = i + 1; j < bins.Count; j++) + { + if (processed.Contains(j)) + continue; + + if (comparer.Equals(currentBin, bins[j])) + { + count++; + processed.Add(j); + } + } + + processed.Add(i); + groups.Add(new BinGroup(currentBin, count)); + } + + return groups; + } + } +} diff --git a/SawCut/BinGroup.cs b/SawCut/BinGroup.cs new file mode 100644 index 0000000..ec115e7 --- /dev/null +++ b/SawCut/BinGroup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SawCut +{ + /// + /// Represents a group of identical bins (bins with the same items in the same order). + /// Used for displaying consolidated results. + /// + public class BinGroup + { + public BinGroup(Bin representativeBin, int count) + { + if (representativeBin == null) + throw new ArgumentNullException(nameof(representativeBin)); + + if (count <= 0) + throw new ArgumentException("Count must be greater than zero", nameof(count)); + + RepresentativeBin = representativeBin; + Count = count; + } + + /// + /// A representative bin from this group (all bins in the group are identical). + /// + public Bin RepresentativeBin { get; } + + /// + /// The number of identical bins in this group. + /// + public int Count { get; } + + // Properties that delegate to the representative bin for data binding + public double Spacing => RepresentativeBin.Spacing; + public double Length => RepresentativeBin.Length; + public double UsedLength => RepresentativeBin.UsedLength; + public double RemainingLength => RepresentativeBin.RemainingLength; + public double Utilization => RepresentativeBin.Utilization; + public IReadOnlyList Items => RepresentativeBin.Items; + + public override string ToString() + { + if (Count == 1) + return RepresentativeBin.ToString(); + + return $"{RepresentativeBin} (x{Count})"; + } + } +}