lumen_geometry/colon/config.rs
1//! Configuration for colon geometry generation.
2
3use super::ColonSegment;
4
5/// Configuration parameters for generating a colon curve.
6///
7/// All parameters have sensible defaults based on adult human anatomy.
8/// Use `seed` to generate procedural variations while maintaining
9/// anatomical plausibility.
10///
11/// # Example
12///
13/// ```rust
14/// use lumen_geometry::colon::ColonConfig;
15///
16/// // Default adult anatomy
17/// let config = ColonConfig::default();
18///
19/// // Procedurally varied anatomy
20/// let varied = ColonConfig {
21/// seed: Some(12345),
22/// ..Default::default()
23/// };
24///
25/// // Custom configuration
26/// let custom = ColonConfig {
27/// total_length: 120.0, // Shorter colon
28/// base_radius: 2.5, // Narrower lumen
29/// ..Default::default()
30/// };
31/// ```
32#[derive(Debug, Clone)]
33pub struct ColonConfig {
34 /// Seed for procedural variation. `None` uses default geometry.
35 ///
36 /// When set, the seed affects:
37 /// - Segment length ratios (within anatomical bounds)
38 /// - Flexure angles (within anatomical bounds)
39 /// - Radius variations along the length
40 /// - Secondary curve wobble
41 pub seed: Option<u64>,
42
43 /// Total arc length of the colon in world units.
44 ///
45 /// Adult human colon is approximately 150cm. Default is scaled
46 /// for typical game/simulation use.
47 pub total_length: f32,
48
49 /// Base lumen radius in world units.
50 ///
51 /// This is the default radius; actual radius varies by segment.
52 pub base_radius: f32,
53
54 /// Per-segment configuration overrides.
55 ///
56 /// If `None`, uses default parameters for each segment.
57 pub segment_params: Option<[SegmentParams; 8]>,
58
59 /// How much the seed can vary segment lengths.
60 ///
61 /// Value of 0.0 = no variation, 1.0 = maximum variation.
62 /// Default is 0.3 (30% variation from base lengths).
63 pub length_variation: f32,
64
65 /// How much the seed can vary flexure angles.
66 ///
67 /// Value of 0.0 = no variation, 1.0 = maximum variation.
68 /// Default is 0.2 (20% variation from base angles).
69 pub angle_variation: f32,
70
71 /// Secondary wobble amplitude for organic feel.
72 ///
73 /// Adds small-scale variation to the centerline path.
74 /// Default is 0.1 (10% of radius).
75 pub wobble_amplitude: f32,
76
77 /// Frequency of secondary wobble.
78 ///
79 /// Higher values = more frequent small bends.
80 /// Default is 8.0 (approximately 8 wobbles per segment).
81 pub wobble_frequency: f32,
82}
83
84/// Parameters for a single colon segment.
85#[derive(Debug, Clone, Copy)]
86pub struct SegmentParams {
87 /// Fraction of total length this segment occupies.
88 ///
89 /// All segment fractions should sum to 1.0.
90 pub length_fraction: f32,
91
92 /// Radius multiplier relative to base_radius.
93 ///
94 /// 1.0 = base radius, 1.2 = 20% wider, etc.
95 pub radius_scale: f32,
96
97 /// Curvature intensity for this segment.
98 ///
99 /// 0.0 = straight, 1.0 = normal curve, 2.0 = sharp bend.
100 /// Flexures typically have higher values.
101 pub curvature: f32,
102
103 /// Primary bend direction (radians).
104 ///
105 /// Determines which way this segment curves.
106 pub bend_direction: f32,
107}
108
109impl Default for ColonConfig {
110 fn default() -> Self {
111 Self {
112 seed: None,
113 total_length: 150.0,
114 base_radius: 3.0,
115 segment_params: None,
116 length_variation: 0.3,
117 angle_variation: 0.2,
118 wobble_amplitude: 0.1,
119 wobble_frequency: 8.0,
120 }
121 }
122}
123
124impl ColonConfig {
125 /// Create a new config with a specific seed.
126 pub fn with_seed(seed: u64) -> Self {
127 Self {
128 seed: Some(seed),
129 ..Default::default()
130 }
131 }
132
133 /// Get parameters for a specific segment.
134 ///
135 /// Returns custom params if set, otherwise default for that segment.
136 pub fn params_for(&self, segment: ColonSegment) -> SegmentParams {
137 if let Some(ref params) = self.segment_params {
138 params[segment.index()]
139 } else {
140 SegmentParams::default_for(segment)
141 }
142 }
143}
144
145impl SegmentParams {
146 /// Default parameters for each colon segment.
147 ///
148 /// Based on anatomical characteristics:
149 /// - Flexures are short but sharply curved
150 /// - Transverse is longest
151 /// - Cecum is widest
152 pub fn default_for(segment: ColonSegment) -> Self {
153 use std::f32::consts::PI;
154
155 match segment {
156 ColonSegment::Rectum => Self {
157 length_fraction: 0.08,
158 radius_scale: 1.0,
159 curvature: 0.2,
160 bend_direction: 0.0,
161 },
162 ColonSegment::Sigmoid => Self {
163 length_fraction: 0.12,
164 radius_scale: 0.9,
165 curvature: 1.5,
166 bend_direction: PI * 0.25, // S-curve
167 },
168 ColonSegment::Descending => Self {
169 length_fraction: 0.15,
170 radius_scale: 1.0,
171 curvature: 0.3,
172 bend_direction: PI * 0.5, // Leftward/upward
173 },
174 ColonSegment::SplenicFlexure => Self {
175 length_fraction: 0.05,
176 radius_scale: 0.95,
177 curvature: 2.5, // Sharp bend
178 bend_direction: PI,
179 },
180 ColonSegment::Transverse => Self {
181 length_fraction: 0.25,
182 radius_scale: 1.1,
183 curvature: 0.4,
184 bend_direction: PI * 1.5, // Rightward
185 },
186 ColonSegment::HepaticFlexure => Self {
187 length_fraction: 0.05,
188 radius_scale: 0.95,
189 curvature: 2.5, // Sharp bend
190 bend_direction: 0.0,
191 },
192 ColonSegment::Ascending => Self {
193 length_fraction: 0.20,
194 radius_scale: 1.05,
195 curvature: 0.3,
196 bend_direction: PI * 0.5, // Downward
197 },
198 ColonSegment::Cecum => Self {
199 length_fraction: 0.10,
200 radius_scale: 1.3, // Widest section
201 curvature: 0.5,
202 bend_direction: PI * 0.75,
203 },
204 }
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn default_length_fractions_sum_to_one() {
214 let sum: f32 = ColonSegment::ALL
215 .iter()
216 .map(|s| SegmentParams::default_for(*s).length_fraction)
217 .sum();
218 assert!(
219 (sum - 1.0).abs() < 0.001,
220 "Length fractions sum to {}, expected 1.0",
221 sum
222 );
223 }
224
225 #[test]
226 fn flexures_have_high_curvature() {
227 let splenic = SegmentParams::default_for(ColonSegment::SplenicFlexure);
228 let hepatic = SegmentParams::default_for(ColonSegment::HepaticFlexure);
229 let transverse = SegmentParams::default_for(ColonSegment::Transverse);
230
231 assert!(splenic.curvature > transverse.curvature);
232 assert!(hepatic.curvature > transverse.curvature);
233 }
234
235 #[test]
236 fn cecum_is_widest() {
237 let cecum = SegmentParams::default_for(ColonSegment::Cecum);
238 for segment in ColonSegment::ALL.iter() {
239 let params = SegmentParams::default_for(*segment);
240 assert!(
241 cecum.radius_scale >= params.radius_scale,
242 "{:?} is wider than Cecum",
243 segment
244 );
245 }
246 }
247}