arris_math/axis.rs
1//! An axis: a point and a unit direction, the plain value a primitive or a
2//! revolve is placed by (`docs/ARCHITECTURE.md` §Operations).
3
4use crate::frame::FrameError;
5use crate::{Point3, UnitVec3, Vec3};
6
7/// A point and a unit direction. A plain value, never a handle: a
8/// cylinder is built from an `Axis` and numbers, and only the result
9/// lives in the model. The direction is unit by construction —
10/// [`Axis::new`] normalises — and that is the only invariant it carries.
11///
12/// ```
13/// use arris_math::{Axis, Point3, Vec3};
14///
15/// let a = Axis::new(Point3::new(1.0, 2.0, 3.0), Vec3::new(0.0, 0.0, 5.0)).unwrap();
16/// assert_eq!(a, Axis::z_at(Point3::new(1.0, 2.0, 3.0)));
17/// assert!(Axis::new(Point3::origin(), Vec3::zeros()).is_err());
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Axis {
22 /// A point on the axis.
23 pub origin: Point3,
24 /// The direction, unit.
25 pub direction: UnitVec3,
26}
27
28impl Axis {
29 /// The axis through `origin` along `direction`, normalised. Errors: a
30 /// non-finite input or a zero direction.
31 pub fn new(origin: Point3, direction: Vec3) -> Result<Self, FrameError> {
32 if !(origin.coords.iter().all(|c| c.is_finite()) && direction.iter().all(|c| c.is_finite()))
33 {
34 return Err(FrameError::NonFinite);
35 }
36 let direction = UnitVec3::try_new(direction, 0.0).ok_or(FrameError::ZeroAxis)?;
37 Ok(Axis { origin, direction })
38 }
39
40 /// The axis through `origin` along `+z`.
41 pub fn z_at(origin: Point3) -> Self {
42 Axis {
43 origin,
44 direction: Vec3::z_axis(),
45 }
46 }
47
48 /// The point `t` along the axis from its origin.
49 pub fn at(&self, t: f64) -> Point3 {
50 self.origin + t * self.direction.into_inner()
51 }
52}