Skip to main content

affn/transform/
compose.rs

1//! Operator composition used by [`super::Transform::then`].
2//!
3//! `next.after(previous)` builds the operator that applies `previous` first,
4//! then `next`, matching `result = next * previous`.
5
6use crate::ops::{Isometry3, Rotation3, Translation3};
7use qtty::Unit;
8
9/// Compose `Self` after `Prev`: apply `Prev` first, then `Self`.
10pub trait ComposeAfter<Prev> {
11    /// Operator type of the composed transform.
12    type Output;
13    /// Builds `Self ∘ Prev` (apply `Prev`, then `Self`).
14    fn after(self, prev: Prev) -> Self::Output;
15}
16
17// -----------------------------------------------------------------------------
18// Same-kind composition
19// -----------------------------------------------------------------------------
20
21impl ComposeAfter<Rotation3> for Rotation3 {
22    type Output = Rotation3;
23
24    #[inline]
25    fn after(self, prev: Rotation3) -> Rotation3 {
26        // self * prev — apply prev first
27        self.compose(&prev)
28    }
29}
30
31impl<U: Unit> ComposeAfter<Translation3<U>> for Translation3<U> {
32    type Output = Translation3<U>;
33
34    #[inline]
35    fn after(self, prev: Translation3<U>) -> Translation3<U> {
36        // Translations commute; compose as vector sum (self + prev).
37        self.compose(&prev)
38    }
39}
40
41impl<U: Unit> ComposeAfter<Isometry3<U>> for Isometry3<U> {
42    type Output = Isometry3<U>;
43
44    #[inline]
45    fn after(self, prev: Isometry3<U>) -> Isometry3<U> {
46        self.compose(&prev)
47    }
48}
49
50// -----------------------------------------------------------------------------
51// Rotation ↔ Translation → Isometry
52// -----------------------------------------------------------------------------
53
54impl<U: Unit> ComposeAfter<Rotation3> for Translation3<U> {
55    type Output = Isometry3<U>;
56
57    #[inline]
58    fn after(self, prev: Rotation3) -> Isometry3<U> {
59        // Translation * Rotation = (R, t)
60        Isometry3::new(prev, self)
61    }
62}
63
64impl<U: Unit> ComposeAfter<Translation3<U>> for Rotation3 {
65    type Output = Isometry3<U>;
66
67    #[inline]
68    fn after(self, prev: Translation3<U>) -> Isometry3<U> {
69        // Rotation * Translation = (R, R * t)
70        let rotated_t = self.apply_array(prev.v);
71        Isometry3::new(self, Translation3::from_array(rotated_t))
72    }
73}
74
75// -----------------------------------------------------------------------------
76// Mixing with Isometry3
77// -----------------------------------------------------------------------------
78
79impl<U: Unit> ComposeAfter<Rotation3> for Isometry3<U> {
80    type Output = Isometry3<U>;
81
82    #[inline]
83    fn after(self, prev: Rotation3) -> Isometry3<U> {
84        self.compose(&Isometry3::from_rotation(prev))
85    }
86}
87
88impl<U: Unit> ComposeAfter<Isometry3<U>> for Rotation3 {
89    type Output = Isometry3<U>;
90
91    #[inline]
92    fn after(self, prev: Isometry3<U>) -> Isometry3<U> {
93        Isometry3::from_rotation(self).compose(&prev)
94    }
95}
96
97impl<U: Unit> ComposeAfter<Translation3<U>> for Isometry3<U> {
98    type Output = Isometry3<U>;
99
100    #[inline]
101    fn after(self, prev: Translation3<U>) -> Isometry3<U> {
102        self.compose(&Isometry3::from_translation(prev))
103    }
104}
105
106impl<U: Unit> ComposeAfter<Isometry3<U>> for Translation3<U> {
107    type Output = Isometry3<U>;
108
109    #[inline]
110    fn after(self, prev: Isometry3<U>) -> Isometry3<U> {
111        Isometry3::from_translation(self).compose(&prev)
112    }
113}