diff --git a/CutList/Tool.cs b/CutList/Tool.cs
index fde034b..c3bef52 100644
--- a/CutList/Tool.cs
+++ b/CutList/Tool.cs
@@ -1,16 +1,109 @@
-namespace CutList
+using System;
+
+namespace CutList
{
+ ///
+ /// Represents a cutting tool with its kerf (blade width).
+ /// Enforces business rules for valid tools.
+ ///
public class Tool
{
- public string Name { get; set; }
+ private string _name;
+ private double _kerf;
- public double Kerf { get; set; }
+ public Tool(string name, double kerf, bool allowUserToChange = false)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentException("Tool name cannot be empty", nameof(name));
+
+ if (kerf < 0)
+ throw new ArgumentException("Kerf cannot be negative", nameof(kerf));
+
+ _name = name;
+ _kerf = kerf;
+ AllowUserToChange = allowUserToChange;
+ }
+
+ ///
+ /// Parameterless constructor for serialization only.
+ /// Use the parameterized constructor for creating valid instances.
+ ///
+ public Tool()
+ {
+ }
+
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentException("Tool name cannot be empty", nameof(value));
+ _name = value;
+ }
+ }
+
+ public double Kerf
+ {
+ get => _kerf;
+ set
+ {
+ if (value < 0)
+ throw new ArgumentException("Kerf cannot be negative", nameof(value));
+ _kerf = value;
+ }
+ }
public bool AllowUserToChange { get; set; }
+ ///
+ /// Calculates the total material wasted for a given number of cuts.
+ ///
+ public double CalculateTotalWaste(int numberOfCuts)
+ {
+ if (numberOfCuts < 0)
+ throw new ArgumentException("Number of cuts cannot be negative", nameof(numberOfCuts));
+
+ return Kerf * numberOfCuts;
+ }
+
+ ///
+ /// Checks if this tool is compatible with the given material thickness.
+ /// Some tools may have minimum/maximum thickness requirements.
+ ///
+ public bool IsCompatibleWithThickness(double thickness)
+ {
+ // For now, all tools are compatible with any thickness
+ // This can be extended based on specific tool requirements
+ return thickness > 0;
+ }
+
public override string ToString()
{
return Name;
}
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Tool other)
+ {
+ return Name == other.Name &&
+ Math.Abs(Kerf - other.Kerf) < 0.0001 &&
+ AllowUserToChange == other.AllowUserToChange;
+ }
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ hash = hash * 23 + (Name?.GetHashCode() ?? 0);
+ hash = hash * 23 + Kerf.GetHashCode();
+ hash = hash * 23 + AllowUserToChange.GetHashCode();
+ return hash;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/SawCut/BinItem.cs b/SawCut/BinItem.cs
index 03bea6f..1ec90ed 100644
--- a/SawCut/BinItem.cs
+++ b/SawCut/BinItem.cs
@@ -1,9 +1,97 @@
-namespace SawCut
+using System;
+
+namespace SawCut
{
+ ///
+ /// Represents an item to be placed in a bin.
+ /// Enforces business rules for valid items.
+ ///
public class BinItem
{
- public string Name { get; set; }
+ private string _name;
+ private double _length;
- public double Length { get; set; }
+ public BinItem(string name, double length)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentException("Item name cannot be empty", nameof(name));
+
+ if (length <= 0)
+ throw new ArgumentException("Item length must be greater than zero", nameof(length));
+
+ _name = name;
+ _length = length;
+ }
+
+ ///
+ /// Parameterless constructor for serialization only.
+ /// Use the parameterized constructor for creating valid instances.
+ ///
+ public BinItem()
+ {
+ }
+
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentException("Item name cannot be empty", nameof(value));
+ _name = value;
+ }
+ }
+
+ public double Length
+ {
+ get => _length;
+ set
+ {
+ if (value <= 0)
+ throw new ArgumentException("Item length must be greater than zero", nameof(value));
+ _length = value;
+ }
+ }
+
+ ///
+ /// Checks if this item can fit in the given available length.
+ ///
+ public bool CanFitIn(double availableLength)
+ {
+ return Length <= availableLength;
+ }
+
+ ///
+ /// Checks if this item can fit in the given available length with spacing.
+ ///
+ public bool CanFitInWithSpacing(double availableLength, double spacing)
+ {
+ return Length + spacing <= availableLength;
+ }
+
+ public override string ToString()
+ {
+ return $"{Name} ({Length}\")";
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is BinItem other)
+ {
+ return Name == other.Name && Length.IsEqualTo(other.Length);
+ }
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ hash = hash * 23 + (Name?.GetHashCode() ?? 0);
+ hash = hash * 23 + Length.GetHashCode();
+ return hash;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/SawCut/MultiBin.cs b/SawCut/MultiBin.cs
index 7a6040d..2e60387 100644
--- a/SawCut/MultiBin.cs
+++ b/SawCut/MultiBin.cs
@@ -1,14 +1,125 @@
-namespace SawCut
+using System;
+
+namespace SawCut
{
+ ///
+ /// Represents a type of bin with quantity and priority.
+ /// Enforces business rules for valid bin configurations.
+ ///
public class MultiBin
{
- public int Quantity { get; set; } = 1;
+ private int _quantity;
+ private double _length;
+ private int _priority;
- public double Length { get; set; }
+ public MultiBin(double length, int quantity = 1, int priority = 25)
+ {
+ if (length <= 0)
+ throw new ArgumentException("Bin length must be greater than zero", nameof(length));
+
+ if (quantity < -1 || quantity == 0)
+ throw new ArgumentException("Quantity must be positive or -1 for unlimited", nameof(quantity));
+
+ _length = length;
+ _quantity = quantity;
+ _priority = priority;
+ }
///
- /// Lower value priority with be used first. Default to 25
+ /// Parameterless constructor for serialization only.
+ /// Use the parameterized constructor for creating valid instances.
///
- public int Priority { get; set; } = 25;
+ public MultiBin()
+ {
+ _quantity = 1;
+ _priority = 25;
+ }
+
+ ///
+ /// Quantity of bins available. Use -1 for unlimited bins.
+ ///
+ public int Quantity
+ {
+ get => _quantity;
+ set
+ {
+ if (value < -1 || value == 0)
+ throw new ArgumentException("Quantity must be positive or -1 for unlimited", nameof(value));
+ _quantity = value;
+ }
+ }
+
+ public double Length
+ {
+ get => _length;
+ set
+ {
+ if (value <= 0)
+ throw new ArgumentException("Bin length must be greater than zero", nameof(value));
+ _length = value;
+ }
+ }
+
+ ///
+ /// Lower value priority will be used first. Default is 25.
+ ///
+ public int Priority
+ {
+ get => _priority;
+ set => _priority = value;
+ }
+
+ ///
+ /// Checks if this bin type has unlimited quantity.
+ ///
+ public bool IsUnlimited => Quantity == -1;
+
+ ///
+ /// Checks if an item of the given length can fit in this bin.
+ ///
+ public bool CanFitItem(double itemLength)
+ {
+ return itemLength <= Length && itemLength > 0;
+ }
+
+ ///
+ /// Calculates the waste for a single bin if the given length is used.
+ ///
+ public double CalculateWaste(double usedLength)
+ {
+ if (usedLength < 0 || usedLength > Length)
+ throw new ArgumentException("Used length must be between 0 and bin length", nameof(usedLength));
+
+ return Length - usedLength;
+ }
+
+ public override string ToString()
+ {
+ var quantityStr = IsUnlimited ? "Unlimited" : Quantity.ToString();
+ return $"Bin {Length}\" (Qty: {quantityStr}, Priority: {Priority})";
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is MultiBin other)
+ {
+ return Quantity == other.Quantity &&
+ Length.IsEqualTo(other.Length) &&
+ Priority == other.Priority;
+ }
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ hash = hash * 23 + Quantity.GetHashCode();
+ hash = hash * 23 + Length.GetHashCode();
+ hash = hash * 23 + Priority.GetHashCode();
+ return hash;
+ }
+ }
}
}
\ No newline at end of file