Skip to main content

rscad_csg_bsp/
affine.rs

1use nalgebra::{Affine3, Rotation3, Unit, Vector3};
2use num_traits::Float;
3
4use crate::Number;
5
6/// Extension trait for building `Affine3` transforms via chaining.
7///
8/// Transforms compose left-to-right: `.scale(2.0).rotate_y(45.0)` scales first,
9/// then rotates.
10///
11/// All angles are in **degrees**.
12pub trait Affine3Ext<T: Number> {
13    /// Uniform scale by `factor` on all axes.
14    fn scale(self, factor: T) -> Self;
15
16    /// Per-axis scale.
17    fn scale_xyz(self, factors: Vector3<T>) -> Self;
18
19    /// Rotate around the X axis by `angle_deg` degrees.
20    fn rotate_x(self, angle_deg: T) -> Self;
21
22    /// Rotate around the Y axis by `angle_deg` degrees.
23    fn rotate_y(self, angle_deg: T) -> Self;
24
25    /// Rotate around the Z axis by `angle_deg` degrees.
26    fn rotate_z(self, angle_deg: T) -> Self;
27
28    /// Rotate around an arbitrary `axis` by `angle_deg` degrees.
29    fn rotate_axis(self, axis: Unit<Vector3<T>>, angle_deg: T) -> Self;
30
31    /// Translate by `offset`.
32    fn translate(self, offset: Vector3<T>) -> Self;
33}
34
35impl<T: Number> Affine3Ext<T> for Affine3<T> {
36    fn scale(self, factor: T) -> Self {
37        self.scale_xyz(Vector3::new(factor, factor, factor))
38    }
39
40    fn scale_xyz(self, factors: Vector3<T>) -> Self {
41        let m = nalgebra::Matrix4::new_nonuniform_scaling(&factors);
42        Affine3::from_matrix_unchecked(m) * self
43    }
44
45    fn rotate_x(self, angle_deg: T) -> Self {
46        self.rotate_axis(Unit::new_normalize(Vector3::x()), angle_deg)
47    }
48
49    fn rotate_y(self, angle_deg: T) -> Self {
50        self.rotate_axis(Unit::new_normalize(Vector3::y()), angle_deg)
51    }
52
53    fn rotate_z(self, angle_deg: T) -> Self {
54        self.rotate_axis(Unit::new_normalize(Vector3::z()), angle_deg)
55    }
56
57    fn rotate_axis(self, axis: Unit<Vector3<T>>, angle_deg: T) -> Self {
58        let rot = Rotation3::from_axis_angle(&axis, Float::to_radians(angle_deg));
59        Affine3::from_matrix_unchecked(rot.to_homogeneous()) * self
60    }
61
62    fn translate(self, offset: Vector3<T>) -> Self {
63        let m = nalgebra::Matrix4::new_translation(&offset);
64        Affine3::from_matrix_unchecked(m) * self
65    }
66}