Skip to main content

u_nesting_cutting/
config.rs

1//! Configuration for cutting path optimization.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use crate::bridge::BridgeConfig;
7use crate::leadin::LeadInConfig;
8use crate::thermal::ThermalConfig;
9
10/// Configuration parameters for cutting path optimization.
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct CuttingConfig {
14    /// Kerf width (cutting tool width) in the same units as geometry coordinates.
15    /// Used for kerf compensation. Set to 0.0 to disable.
16    pub kerf_width: f64,
17
18    /// Weight factor for pierce count in the cost function.
19    /// Cost = total_rapid_distance + pierce_weight * pierce_count
20    pub pierce_weight: f64,
21
22    /// Maximum number of 2-opt improvement iterations.
23    /// Set to 0 to use only the nearest-neighbor solution.
24    pub max_2opt_iterations: usize,
25
26    /// Machine rapid traverse speed (mm/s or units/s).
27    /// Used only for time estimation, not for optimization.
28    pub rapid_speed: f64,
29
30    /// Machine cutting speed (mm/s or units/s).
31    /// Used only for time estimation, not for optimization.
32    pub cut_speed: f64,
33
34    /// Default cut direction for exterior contours.
35    pub exterior_direction: CutDirectionPreference,
36
37    /// Default cut direction for interior contours (holes).
38    pub interior_direction: CutDirectionPreference,
39
40    /// Home position for the cutting head (start/end point).
41    /// Default is (0.0, 0.0).
42    pub home_position: (f64, f64),
43
44    /// Number of candidate pierce points to evaluate per contour.
45    /// Higher values give better pierce point selection but slower optimization.
46    /// Default: 1 (use nearest point on contour to previous endpoint).
47    pub pierce_candidates: usize,
48
49    /// Tolerance for geometric comparisons.
50    pub tolerance: f64,
51
52    /// Lead-in/lead-out configuration.
53    pub lead_in: LeadInConfig,
54
55    /// Bridge/tab (micro-joint) configuration.
56    pub bridge: BridgeConfig,
57
58    /// Thermal model configuration.
59    pub thermal: ThermalConfig,
60}
61
62/// Preference for cutting direction.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65pub enum CutDirectionPreference {
66    /// Counter-clockwise (conventional for exterior contours).
67    Ccw,
68    /// Clockwise (conventional for interior/hole contours).
69    Cw,
70    /// Automatically determine based on contour type.
71    Auto,
72}
73
74impl Default for CuttingConfig {
75    fn default() -> Self {
76        Self {
77            kerf_width: 0.0,
78            pierce_weight: 10.0,
79            max_2opt_iterations: 1000,
80            rapid_speed: 1000.0,
81            cut_speed: 100.0,
82            exterior_direction: CutDirectionPreference::Auto,
83            interior_direction: CutDirectionPreference::Auto,
84            home_position: (0.0, 0.0),
85            pierce_candidates: 1,
86            tolerance: 1e-6,
87            lead_in: LeadInConfig::default(),
88            bridge: BridgeConfig::default(),
89            thermal: ThermalConfig::default(),
90        }
91    }
92}
93
94impl CuttingConfig {
95    /// Creates a new default configuration.
96    pub fn new() -> Self {
97        Self::default()
98    }
99
100    /// Sets the kerf width.
101    pub fn with_kerf_width(mut self, width: f64) -> Self {
102        self.kerf_width = width;
103        self
104    }
105
106    /// Sets the pierce weight factor.
107    pub fn with_pierce_weight(mut self, weight: f64) -> Self {
108        self.pierce_weight = weight;
109        self
110    }
111
112    /// Sets the maximum 2-opt iterations.
113    pub fn with_max_2opt_iterations(mut self, iterations: usize) -> Self {
114        self.max_2opt_iterations = iterations;
115        self
116    }
117
118    /// Sets the home position.
119    pub fn with_home_position(mut self, x: f64, y: f64) -> Self {
120        self.home_position = (x, y);
121        self
122    }
123
124    /// Sets the number of pierce candidates per contour.
125    pub fn with_pierce_candidates(mut self, candidates: usize) -> Self {
126        self.pierce_candidates = candidates.max(1);
127        self
128    }
129
130    /// Sets the lead-in/lead-out configuration.
131    pub fn with_lead_in(mut self, lead_in: LeadInConfig) -> Self {
132        self.lead_in = lead_in;
133        self
134    }
135
136    /// Sets the bridge/tab configuration.
137    pub fn with_bridge(mut self, bridge: BridgeConfig) -> Self {
138        self.bridge = bridge;
139        self
140    }
141
142    /// Sets the thermal model configuration.
143    pub fn with_thermal(mut self, thermal: ThermalConfig) -> Self {
144        self.thermal = thermal;
145        self
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_default_config() {
155        let config = CuttingConfig::default();
156        assert_eq!(config.kerf_width, 0.0);
157        assert_eq!(config.pierce_weight, 10.0);
158        assert_eq!(config.max_2opt_iterations, 1000);
159        assert_eq!(config.home_position, (0.0, 0.0));
160    }
161
162    #[test]
163    fn test_builder() {
164        let config = CuttingConfig::new()
165            .with_kerf_width(0.5)
166            .with_pierce_weight(20.0)
167            .with_home_position(10.0, 10.0);
168
169        assert_eq!(config.kerf_width, 0.5);
170        assert_eq!(config.pierce_weight, 20.0);
171        assert_eq!(config.home_position, (10.0, 10.0));
172    }
173
174    #[test]
175    fn test_pierce_candidates_minimum() {
176        let config = CuttingConfig::new().with_pierce_candidates(0);
177        assert_eq!(config.pierce_candidates, 1);
178    }
179}