rs_math3d/
basis.rs

1// Copyright 2020-Present (c) Raja Lehtihet & Wael El Oraiby
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice,
7// this list of conditions and the following disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its contributors
14// may be used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//! Coordinate basis and reference frame utilities.
29//!
30//! This module provides types and functions for working with coordinate systems
31//! and reference frames in 3D space. A basis represents a local coordinate system
32//! with its own origin and axis orientations.
33//!
34//! # Examples
35//!
36//! ```
37//! use rs_math3d::basis::{Basis, BasisPlane};
38//! use rs_math3d::vector::Vector3;
39//! 
40//! // Create a basis at origin with default axes
41//! let basis = Basis::<f32>::default();
42//! 
43//! // Create a basis with custom center
44//! let center = Vector3::new(10.0, 5.0, 0.0);
45//! let basis = Basis::default_with_center(&center);
46//! ```
47
48use crate::matrix::*;
49use crate::scalar::*;
50use crate::vector::*;
51use num_traits::{Zero, One};
52
53/// Represents one of the three coordinate planes.
54#[derive(Debug, Clone, Copy)]
55pub enum BasisPlane {
56    /// Plane spanned by the Y and Z axes.
57    YZ,
58    /// Plane spanned by the Z and X axes.
59    ZX,
60    /// Plane spanned by the X and Y axes.
61    XY,
62}
63
64impl BasisPlane {
65    /// Converts the plane to a numeric ID (0=YZ, 1=ZX, 2=XY).
66    pub fn to_id(&self) -> usize {
67        match self {
68            BasisPlane::YZ => 0,
69            BasisPlane::ZX => 1,
70            BasisPlane::XY => 2,
71        }
72    }
73
74    /// Creates a plane from a numeric ID.
75    ///
76    /// # Panics
77    /// Panics if id is not 0, 1, or 2.
78    pub fn of_id(id: usize) -> Self {
79        match id {
80            0 => BasisPlane::YZ,
81            1 => BasisPlane::ZX,
82            2 => BasisPlane::XY,
83            _ => panic!("invalid id"),
84        }
85    }
86}
87
88/// A 3D coordinate basis (reference frame).
89///
90/// Represents a local coordinate system with three orthogonal axes
91/// and an origin point. This is useful for transformations between
92/// different coordinate spaces.
93#[derive(Debug, Clone, Copy)]
94#[repr(C)]
95pub struct Basis<T: Scalar> {
96    /// Basis X axis.
97    pub x_axis: Vector3<T>,
98    /// Basis Y axis.
99    pub y_axis: Vector3<T>,
100    /// Basis Z axis.
101    pub z_axis: Vector3<T>,
102    /// Basis origin (center).
103    pub center: Vector3<T>,
104}
105
106impl<T: Scalar> Basis<T> {
107    /// Returns the center (origin) of the basis.
108    pub fn center(&self) -> &Vector3<T> {
109        &self.center
110    }
111
112    /// Returns a mutable reference to the center.
113    pub fn center_mut(&mut self) -> &mut Vector3<T> {
114        &mut self.center
115    }
116
117    /// Converts the basis to a 4x4 transformation matrix.
118    ///
119    /// The resulting matrix transforms from local basis space to world space:
120    /// - Columns 0-2: The basis axes (rotation/scale)
121    /// - Column 3: The center point (translation)
122    pub fn to_mat4(&self) -> Matrix4<T> {
123        Matrix4::new(
124            self.x_axis.x,
125            self.x_axis.y,
126            self.x_axis.z,
127            <T as Zero>::zero(),
128            self.y_axis.x,
129            self.y_axis.y,
130            self.y_axis.z,
131            <T as Zero>::zero(),
132            self.z_axis.x,
133            self.z_axis.y,
134            self.z_axis.z,
135            <T as Zero>::zero(),
136            self.center.x,
137            self.center.y,
138            self.center.z,
139            <T as One>::one(),
140        )
141    }
142
143    /// Creates a basis from a 4x4 transformation matrix.
144    ///
145    /// Extracts the axes from columns 0-2 and center from column 3.
146    pub fn of_mat4(mat: &Matrix4<T>) -> Self {
147        let col0 = mat.col[0];
148        let col1 = mat.col[1];
149        let col2 = mat.col[2];
150        let col3 = mat.col[3];
151        Self {
152            center: col3.xyz(),
153            x_axis: col0.xyz(),
154            y_axis: col1.xyz(),
155            z_axis: col2.xyz(),
156        }
157    }
158
159    /// Returns the default right-handed basis at the origin.
160    pub fn default() -> Self {
161        Self {
162            center: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as Zero>::zero()),
163            x_axis: Vector3::new(<T as One>::one(), <T as Zero>::zero(), <T as Zero>::zero()),
164            y_axis: Vector3::new(<T as Zero>::zero(), <T as One>::one(), <T as Zero>::zero()),
165            z_axis: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as One>::one()),
166        }
167    }
168
169    /// Returns the default right-handed basis with a custom center.
170    pub fn default_with_center(center: &Vector3<T>) -> Self {
171        Self {
172            center: *center,
173            x_axis: Vector3::new(<T as One>::one(), <T as Zero>::zero(), <T as Zero>::zero()),
174            y_axis: Vector3::new(<T as Zero>::zero(), <T as One>::one(), <T as Zero>::zero()),
175            z_axis: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as One>::one()),
176        }
177    }
178
179    /// Returns the two axes spanning the given plane.
180    pub fn plane_axis(&self, plane: BasisPlane) -> (&Vector3<T>, &Vector3<T>) {
181        match plane {
182            BasisPlane::YZ => (&self.y_axis, &self.z_axis),
183            BasisPlane::ZX => (&self.z_axis, &self.x_axis),
184            BasisPlane::XY => (&self.x_axis, &self.y_axis),
185        }
186    }
187
188    /// Returns mutable references to the two axes spanning the given plane.
189    pub fn plane_axis_mut(&mut self, plane: BasisPlane) -> (&mut Vector3<T>, &mut Vector3<T>) {
190        match plane {
191            BasisPlane::YZ => (&mut self.y_axis, &mut self.z_axis),
192            BasisPlane::ZX => (&mut self.z_axis, &mut self.x_axis),
193            BasisPlane::XY => (&mut self.x_axis, &mut self.y_axis),
194        }
195    }
196
197    /// Converts the basis axes to a 3x3 rotation matrix.
198    ///
199    /// The matrix contains only the rotation part, without translation.
200    pub fn to_mat3(&self) -> Matrix3<T> {
201        Matrix3::new(
202            self.x_axis.x,
203            self.x_axis.y,
204            self.x_axis.z,
205            self.y_axis.x,
206            self.y_axis.y,
207            self.y_axis.z,
208            self.z_axis.x,
209            self.z_axis.y,
210            self.z_axis.z,
211        )
212    }
213
214    /// Creates a basis from a center point and a 3x3 rotation matrix.
215    ///
216    /// The matrix columns become the basis axes.
217    pub fn of_center_mat3(center: &Vector3<T>, m: Matrix3<T>) -> Self {
218        Self {
219            center: *center,
220            x_axis: m.col[0],
221            y_axis: m.col[1],
222            z_axis: m.col[2],
223        }
224    }
225}