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(¢er);
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    YZ,
57    ZX,
58    XY,
59}
60
61impl BasisPlane {
62    /// Converts the plane to a numeric ID (0=YZ, 1=ZX, 2=XY).
63    pub fn to_id(&self) -> usize {
64        match self {
65            BasisPlane::YZ => 0,
66            BasisPlane::ZX => 1,
67            BasisPlane::XY => 2,
68        }
69    }
70
71    /// Creates a plane from a numeric ID.
72    ///
73    /// # Panics
74    /// Panics if id is not 0, 1, or 2.
75    pub fn of_id(id: usize) -> Self {
76        match id {
77            0 => BasisPlane::YZ,
78            1 => BasisPlane::ZX,
79            2 => BasisPlane::XY,
80            _ => panic!("invalid id"),
81        }
82    }
83}
84
85/// A 3D coordinate basis (reference frame).
86///
87/// Represents a local coordinate system with three orthogonal axes
88/// and an origin point. This is useful for transformations between
89/// different coordinate spaces.
90#[derive(Debug, Clone, Copy)]
91#[repr(C)]
92pub struct Basis<T: Scalar> {
93    pub x_axis: Vector3<T>,
94    pub y_axis: Vector3<T>,
95    pub z_axis: Vector3<T>,
96    pub center: Vector3<T>,
97}
98
99impl<T: Scalar> Basis<T> {
100    /// Returns the center (origin) of the basis.
101    pub fn center(&self) -> &Vector3<T> {
102        &self.center
103    }
104
105    /// Returns a mutable reference to the center.
106    pub fn center_mut(&mut self) -> &mut Vector3<T> {
107        &mut self.center
108    }
109
110    /// Converts the basis to a 4x4 transformation matrix.
111    ///
112    /// The resulting matrix transforms from local basis space to world space:
113    /// - Columns 0-2: The basis axes (rotation/scale)
114    /// - Column 3: The center point (translation)
115    pub fn to_mat4(&self) -> Matrix4<T> {
116        Matrix4::new(
117            self.x_axis.x,
118            self.x_axis.y,
119            self.x_axis.z,
120            <T as Zero>::zero(),
121            self.y_axis.x,
122            self.y_axis.y,
123            self.y_axis.z,
124            <T as Zero>::zero(),
125            self.z_axis.x,
126            self.z_axis.y,
127            self.z_axis.z,
128            <T as Zero>::zero(),
129            self.center.x,
130            self.center.y,
131            self.center.z,
132            <T as One>::one(),
133        )
134    }
135
136    /// Creates a basis from a 4x4 transformation matrix.
137    ///
138    /// Extracts the axes from columns 0-2 and center from column 3.
139    pub fn of_mat4(mat: &Matrix4<T>) -> Self {
140        let col0 = mat.col[0];
141        let col1 = mat.col[1];
142        let col2 = mat.col[2];
143        let col3 = mat.col[3];
144        Self {
145            center: col3.xyz(),
146            x_axis: col0.xyz(),
147            y_axis: col1.xyz(),
148            z_axis: col2.xyz(),
149        }
150    }
151
152    pub fn default() -> Self {
153        Self {
154            center: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as Zero>::zero()),
155            x_axis: Vector3::new(<T as One>::one(), <T as Zero>::zero(), <T as Zero>::zero()),
156            y_axis: Vector3::new(<T as Zero>::zero(), <T as One>::one(), <T as Zero>::zero()),
157            z_axis: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as One>::one()),
158        }
159    }
160
161    pub fn default_with_center(center: &Vector3<T>) -> Self {
162        Self {
163            center: *center,
164            x_axis: Vector3::new(<T as One>::one(), <T as Zero>::zero(), <T as Zero>::zero()),
165            y_axis: Vector3::new(<T as Zero>::zero(), <T as One>::one(), <T as Zero>::zero()),
166            z_axis: Vector3::new(<T as Zero>::zero(), <T as Zero>::zero(), <T as One>::one()),
167        }
168    }
169
170    pub fn plane_axis(&self, plane: BasisPlane) -> (&Vector3<T>, &Vector3<T>) {
171        match plane {
172            BasisPlane::YZ => (&self.y_axis, &self.z_axis),
173            BasisPlane::ZX => (&self.z_axis, &self.x_axis),
174            BasisPlane::XY => (&self.x_axis, &self.y_axis),
175        }
176    }
177
178    pub fn plane_axis_mut(&mut self, plane: BasisPlane) -> (&mut Vector3<T>, &mut Vector3<T>) {
179        match plane {
180            BasisPlane::YZ => (&mut self.y_axis, &mut self.z_axis),
181            BasisPlane::ZX => (&mut self.z_axis, &mut self.x_axis),
182            BasisPlane::XY => (&mut self.x_axis, &mut self.y_axis),
183        }
184    }
185
186    /// Converts the basis axes to a 3x3 rotation matrix.
187    ///
188    /// The matrix contains only the rotation part, without translation.
189    pub fn to_mat3(&self) -> Matrix3<T> {
190        Matrix3::new(
191            self.x_axis.x,
192            self.x_axis.y,
193            self.x_axis.z,
194            self.y_axis.x,
195            self.y_axis.y,
196            self.y_axis.z,
197            self.z_axis.x,
198            self.z_axis.y,
199            self.z_axis.z,
200        )
201    }
202
203    /// Creates a basis from a center point and a 3x3 rotation matrix.
204    ///
205    /// The matrix columns become the basis axes.
206    pub fn of_center_mat3(center: &Vector3<T>, m: Matrix3<T>) -> Self {
207        Self {
208            center: *center,
209            x_axis: m.col[0],
210            y_axis: m.col[1],
211            z_axis: m.col[2],
212        }
213    }
214}