1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_coordinate::GeometryError;
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct Hyperplane {
9 coefficients: Vec<f64>,
10 offset: f64,
11}
12
13impl Hyperplane {
14 pub fn try_new(coefficients: Vec<f64>, offset: f64) -> Result<Self, GeometryError> {
21 if coefficients.is_empty() || coefficients.iter().all(|value| *value == 0.0) {
22 return Err(GeometryError::ZeroDirectionVector);
23 }
24
25 for value in &coefficients {
26 if !value.is_finite() {
27 return Err(GeometryError::non_finite_component(
28 "Hyperplane",
29 "coefficient",
30 *value,
31 ));
32 }
33 }
34
35 if !offset.is_finite() {
36 return Err(GeometryError::non_finite_component(
37 "Hyperplane",
38 "offset",
39 offset,
40 ));
41 }
42
43 Ok(Self {
44 coefficients,
45 offset,
46 })
47 }
48
49 #[must_use]
51 pub fn dimension(&self) -> usize {
52 self.coefficients.len()
53 }
54
55 #[must_use]
57 pub fn coefficients(&self) -> &[f64] {
58 &self.coefficients
59 }
60
61 #[must_use]
63 pub const fn offset(&self) -> f64 {
64 self.offset
65 }
66
67 #[must_use]
69 pub fn evaluate(&self, point: &[f64]) -> Option<f64> {
70 if point.len() != self.coefficients.len() {
71 return None;
72 }
73
74 Some(
75 self.coefficients
76 .iter()
77 .zip(point)
78 .map(|(coefficient, coordinate)| coefficient * coordinate)
79 .sum::<f64>()
80 + self.offset,
81 )
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::Hyperplane;
88 use use_coordinate::GeometryError;
89
90 #[test]
91 fn evaluates_hyperplanes() {
92 let hyperplane = Hyperplane::try_new(vec![1.0, 0.0, 0.0], -2.0).expect("valid hyperplane");
93
94 assert_eq!(hyperplane.dimension(), 3);
95 assert_eq!(hyperplane.coefficients(), &[1.0, 0.0, 0.0]);
96 assert_eq!(hyperplane.offset(), -2.0);
97 assert_eq!(hyperplane.evaluate(&[2.0, 4.0, 5.0]), Some(0.0));
98 assert_eq!(hyperplane.evaluate(&[2.0, 4.0]), None);
99 }
100
101 #[test]
102 fn rejects_empty_or_zero_coefficients() {
103 assert_eq!(
104 Hyperplane::try_new(Vec::new(), 0.0),
105 Err(GeometryError::ZeroDirectionVector)
106 );
107 assert_eq!(
108 Hyperplane::try_new(vec![0.0, 0.0], 0.0),
109 Err(GeometryError::ZeroDirectionVector)
110 );
111 }
112}