molframe_core/coords/
sample.rs1use num_traits::ToPrimitive;
4
5#[derive(Clone, Copy, Debug)]
8pub struct CoordinateSample<'a> {
9 start: &'a [[f32; 3]],
10 end: &'a [[f32; 3]],
11 weight: f32,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
16pub enum CoordinateSampleError {
17 #[error("coordinate sample requires equal-length columns")]
19 Shape,
20 #[error("coordinate sample weight must be finite and between zero and one")]
22 Weight,
23}
24
25impl<'a> CoordinateSample<'a> {
26 pub fn new(
32 start: &'a [[f32; 3]],
33 end: &'a [[f32; 3]],
34 weight: f32,
35 ) -> Result<Self, CoordinateSampleError> {
36 if start.len() != end.len() {
37 return Err(CoordinateSampleError::Shape);
38 }
39 if !weight.is_finite() || !(0.0..=1.0).contains(&weight) {
40 return Err(CoordinateSampleError::Weight);
41 }
42 Ok(Self { start, end, weight })
43 }
44
45 #[must_use]
47 pub const fn len(self) -> usize {
48 self.start.len()
49 }
50
51 #[must_use]
53 pub const fn is_empty(self) -> bool {
54 self.start.is_empty()
55 }
56
57 #[must_use]
61 pub fn position(self, row: usize) -> Option<[f32; 3]> {
62 let start = *self.start.get(row)?;
63 let end = *self.end.get(row)?;
64 if !start.iter().chain(&end).all(|value| value.is_finite()) {
65 return None;
66 }
67 if self.weight.to_bits() == 0.0f32.to_bits() || self.weight.to_bits() == (-0.0f32).to_bits()
68 {
69 return Some(start);
70 }
71 if self.weight.to_bits() == 1.0f32.to_bits() {
72 return Some(end);
73 }
74 let weight = f64::from(self.weight);
75 let mut output = [0.0; 3];
76 for axis in 0..3 {
77 output[axis] = (f64::from(start[axis]) * (1.0 - weight)
78 + f64::from(end[axis]) * weight)
79 .to_f32()?;
80 }
81 Some(output)
82 }
83}
84
85#[cfg(test)]
86#[path = "sample_tests.rs"]
87mod tests;