lumen_geometry/colon/
segments.rs1use crate::AnatomicalSegment;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum ColonSegment {
18 Rectum,
20 Sigmoid,
22 Descending,
24 SplenicFlexure,
26 Transverse,
28 HepaticFlexure,
30 Ascending,
32 Cecum,
34}
35
36impl ColonSegment {
37 pub const ALL: [ColonSegment; 8] = [
39 ColonSegment::Rectum,
40 ColonSegment::Sigmoid,
41 ColonSegment::Descending,
42 ColonSegment::SplenicFlexure,
43 ColonSegment::Transverse,
44 ColonSegment::HepaticFlexure,
45 ColonSegment::Ascending,
46 ColonSegment::Cecum,
47 ];
48
49 pub const DEFAULT_BOUNDARIES: [f32; 9] = [
54 0.00, 0.08, 0.20, 0.35, 0.40, 0.65, 0.70, 0.90, 1.00, ];
64
65 pub fn at_t(t: f32) -> Self {
70 let t = t.clamp(0.0, 1.0);
71 for (i, segment) in Self::ALL.iter().enumerate() {
72 if t < Self::DEFAULT_BOUNDARIES[i + 1] {
73 return *segment;
74 }
75 }
76 ColonSegment::Cecum
77 }
78
79 pub fn index(&self) -> usize {
81 match self {
82 ColonSegment::Rectum => 0,
83 ColonSegment::Sigmoid => 1,
84 ColonSegment::Descending => 2,
85 ColonSegment::SplenicFlexure => 3,
86 ColonSegment::Transverse => 4,
87 ColonSegment::HepaticFlexure => 5,
88 ColonSegment::Ascending => 6,
89 ColonSegment::Cecum => 7,
90 }
91 }
92
93 pub fn is_flexure(&self) -> bool {
95 matches!(
96 self,
97 ColonSegment::SplenicFlexure | ColonSegment::HepaticFlexure
98 )
99 }
100}
101
102impl AnatomicalSegment for ColonSegment {
103 fn name(&self) -> &'static str {
104 match self {
105 ColonSegment::Rectum => "Rectum",
106 ColonSegment::Sigmoid => "Sigmoid",
107 ColonSegment::Descending => "Descending",
108 ColonSegment::SplenicFlexure => "Splenic Flexure",
109 ColonSegment::Transverse => "Transverse",
110 ColonSegment::HepaticFlexure => "Hepatic Flexure",
111 ColonSegment::Ascending => "Ascending",
112 ColonSegment::Cecum => "Cecum",
113 }
114 }
115
116 fn t_range(&self) -> (f32, f32) {
117 let idx = self.index();
118 (
119 ColonSegment::DEFAULT_BOUNDARIES[idx],
120 ColonSegment::DEFAULT_BOUNDARIES[idx + 1],
121 )
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn segment_at_t_boundaries() {
131 assert_eq!(ColonSegment::at_t(0.0), ColonSegment::Rectum);
132 assert_eq!(ColonSegment::at_t(0.07), ColonSegment::Rectum);
133 assert_eq!(ColonSegment::at_t(0.08), ColonSegment::Sigmoid);
134 assert_eq!(ColonSegment::at_t(0.5), ColonSegment::Transverse);
135 assert_eq!(ColonSegment::at_t(1.0), ColonSegment::Cecum);
136 }
137
138 #[test]
139 fn segment_at_t_clamping() {
140 assert_eq!(ColonSegment::at_t(-0.5), ColonSegment::Rectum);
141 assert_eq!(ColonSegment::at_t(1.5), ColonSegment::Cecum);
142 }
143
144 #[test]
145 fn all_segments_cover_full_range() {
146 for i in 0..8 {
148 let (start, end) = ColonSegment::ALL[i].t_range();
149 assert_eq!(start, ColonSegment::DEFAULT_BOUNDARIES[i]);
150 assert_eq!(end, ColonSegment::DEFAULT_BOUNDARIES[i + 1]);
151 }
152 }
153
154 #[test]
155 fn flexures_identified() {
156 assert!(!ColonSegment::Rectum.is_flexure());
157 assert!(!ColonSegment::Transverse.is_flexure());
158 assert!(ColonSegment::SplenicFlexure.is_flexure());
159 assert!(ColonSegment::HepaticFlexure.is_flexure());
160 }
161}