1use axiolid_core::{Interval, Scalar, Tolerance};
9
10use crate::LayeredFieldError;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum SurfaceFacing {
18 AgainstNormal,
20 WithNormal,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct SurfaceHit {
27 w: Scalar,
28 facing: SurfaceFacing,
29}
30
31impl SurfaceHit {
32 pub const fn new(w: Scalar, facing: SurfaceFacing) -> Self {
34 Self { w, facing }
35 }
36
37 pub const fn w(self) -> Scalar {
39 self.w
40 }
41
42 pub const fn facing(self) -> SurfaceFacing {
44 self.facing
45 }
46}
47
48#[derive(Debug, Clone, Default, PartialEq)]
53pub struct LayeredCell {
54 surfaces: Vec<SurfaceHit>,
55 occupancy: Vec<Interval>,
56}
57
58impl LayeredCell {
59 pub const fn empty() -> Self {
61 Self {
62 surfaces: Vec::new(),
63 occupancy: Vec::new(),
64 }
65 }
66
67 pub fn new(occupancy: Vec<Interval>) -> Result<Self, LayeredFieldError> {
69 Self::with_layers(Vec::new(), occupancy)
70 }
71
72 pub fn with_layers(
79 mut surfaces: Vec<SurfaceHit>,
80 mut occupancy: Vec<Interval>,
81 ) -> Result<Self, LayeredFieldError> {
82 if surfaces.iter().any(|hit| !hit.w.is_finite()) {
83 return Err(LayeredFieldError::InvalidInterval);
84 }
85 if occupancy
86 .iter()
87 .any(|span| !span.start.is_finite() || !span.end.is_finite() || span.start >= span.end)
88 {
89 return Err(LayeredFieldError::InvalidInterval);
90 }
91 surfaces.sort_by(|left, right| {
92 left.w
93 .total_cmp(&right.w)
94 .then_with(|| left.facing.cmp(&right.facing))
95 });
96 occupancy.sort_by(|left, right| {
97 left.start
98 .total_cmp(&right.start)
99 .then_with(|| left.end.total_cmp(&right.end))
100 });
101 if occupancy
102 .windows(2)
103 .any(|pair| pair[0].end >= pair[1].start)
104 {
105 return Err(LayeredFieldError::NonDisjointIntervals);
106 }
107 Ok(Self {
108 surfaces,
109 occupancy,
110 })
111 }
112
113 pub fn surfaces(&self) -> &[SurfaceHit] {
115 &self.surfaces
116 }
117
118 pub fn occupancy(&self) -> &[Interval] {
120 &self.occupancy
121 }
122
123 pub fn layer_count(&self) -> usize {
125 self.surfaces.len() + self.occupancy.len()
126 }
127
128 pub fn is_empty(&self) -> bool {
130 self.surfaces.is_empty() && self.occupancy.is_empty()
131 }
132
133 pub fn derive_occupancy(&self, tolerance: Tolerance) -> Result<Self, LayeredFieldError> {
140 if self.surfaces.len() % 2 != 0 {
141 return Err(LayeredFieldError::UnbalancedCrossings);
142 }
143 let mut spans = Vec::with_capacity(self.surfaces.len() / 2);
144 for pair in self.surfaces.chunks_exact(2) {
145 if pair[0].facing != SurfaceFacing::AgainstNormal
146 || pair[1].facing != SurfaceFacing::WithNormal
147 {
148 return Err(LayeredFieldError::UnbalancedCrossings);
149 }
150 if (pair[1].w - pair[0].w).abs() <= tolerance.linear() {
151 return Err(LayeredFieldError::DegenerateOccupancy);
152 }
153 spans.push(Interval::new(pair[0].w, pair[1].w));
154 }
155 Self::with_layers(self.surfaces.clone(), spans)
156 }
157
158 pub fn largest_free_span(&self, search: Interval) -> Option<Interval> {
164 let (low, high) = if search.start <= search.end {
165 (search.start, search.end)
166 } else {
167 (search.end, search.start)
168 };
169 let mut cursor = low;
170 let mut best: Option<Interval> = None;
171 for span in &self.occupancy {
172 let start = span.start.max(low);
173 let end = span.end.min(high);
174 if start >= end {
175 continue;
176 }
177 if start > cursor {
178 best = keep_longer(best, Interval::new(cursor, start));
179 }
180 cursor = cursor.max(end);
181 }
182 if cursor < high {
183 best = keep_longer(best, Interval::new(cursor, high));
184 }
185 best
186 }
187}
188
189fn keep_longer(best: Option<Interval>, candidate: Interval) -> Option<Interval> {
190 match best {
191 Some(current) if current.length() >= candidate.length() => Some(current),
192 _ => Some(candidate),
193 }
194}