Skip to main content

use_projective/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4fn valid_homogeneous_coordinates(coordinates: &[f64]) -> bool {
5    !coordinates.is_empty()
6        && coordinates.iter().all(|coordinate| coordinate.is_finite())
7        && coordinates.iter().any(|coordinate| *coordinate != 0.0)
8}
9
10/// A projective point represented by homogeneous coordinates.
11#[derive(Debug, Clone, PartialEq)]
12pub struct ProjectivePoint {
13    coordinates: Vec<f64>,
14}
15
16impl ProjectivePoint {
17    /// Creates a projective point from nonzero finite homogeneous coordinates.
18    #[must_use]
19    pub fn new(coordinates: Vec<f64>) -> Option<Self> {
20        if valid_homogeneous_coordinates(&coordinates) {
21            Some(Self { coordinates })
22        } else {
23            None
24        }
25    }
26
27    /// Returns the homogeneous coordinates.
28    #[must_use]
29    pub fn coordinates(&self) -> &[f64] {
30        &self.coordinates
31    }
32
33    /// Returns the projective dimension.
34    #[must_use]
35    pub fn dimension(&self) -> usize {
36        self.coordinates.len() - 1
37    }
38}
39
40/// A projective line represented by homogeneous coefficients.
41#[derive(Debug, Clone, PartialEq)]
42pub struct ProjectiveLine {
43    coefficients: Vec<f64>,
44}
45
46impl ProjectiveLine {
47    /// Creates a projective line from nonzero finite homogeneous coefficients.
48    #[must_use]
49    pub fn new(coefficients: Vec<f64>) -> Option<Self> {
50        if valid_homogeneous_coordinates(&coefficients) {
51            Some(Self { coefficients })
52        } else {
53            None
54        }
55    }
56
57    /// Returns the line coefficients.
58    #[must_use]
59    pub fn coefficients(&self) -> &[f64] {
60        &self.coefficients
61    }
62}
63
64/// A projective plane descriptor.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct ProjectivePlane {
67    order: Option<usize>,
68}
69
70impl ProjectivePlane {
71    /// Creates a projective plane descriptor with optional finite order.
72    #[must_use]
73    pub const fn new(order: Option<usize>) -> Self {
74        Self { order }
75    }
76
77    /// Returns the finite order, when known.
78    #[must_use]
79    pub const fn order(self) -> Option<usize> {
80        self.order
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::{ProjectiveLine, ProjectivePlane, ProjectivePoint};
87
88    #[test]
89    fn validates_projective_coordinates() {
90        let point = ProjectivePoint::new(vec![1.0, 2.0, 1.0]).expect("valid point");
91        let line = ProjectiveLine::new(vec![1.0, -1.0, 0.0]).expect("valid line");
92        let plane = ProjectivePlane::new(Some(2));
93
94        assert_eq!(point.dimension(), 2);
95        assert_eq!(point.coordinates(), &[1.0, 2.0, 1.0]);
96        assert_eq!(line.coefficients(), &[1.0, -1.0, 0.0]);
97        assert_eq!(plane.order(), Some(2));
98        assert_eq!(ProjectivePoint::new(vec![0.0, 0.0]), None);
99    }
100}