rstmt_nrt/impls/
impl_triad_repr.rs

1/*
2    Appellation: impl_triad_repr <module>
3    Created At: 2025.12.20:11:04:03
4    Contrib: @FL03
5*/
6use crate::triad::TriadBase;
7
8use crate::traits::TriadRepr;
9use crate::types::{LPR, Triads};
10use num_traits::{Float, FromPrimitive, Num, ToPrimitive, Zero};
11use rstmt_core::traits::{PitchMod, Transform};
12use rstmt_core::{Augmented, Diminished, Major, Minor};
13
14impl<S, T> TriadBase<S, Augmented, T>
15where
16    S: TriadRepr<Elem = T>,
17{
18    /// returns a new instance of the [`TriadBase`] with the given chord and kind as an
19    /// augmented triad.
20    pub fn augmented(root: T) -> Self
21    where
22        T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
23    {
24        TriadBase::from_root_with_class(root, Augmented)
25    }
26}
27
28impl<S, T> TriadBase<S, Diminished, T>
29where
30    S: TriadRepr<Elem = T>,
31{
32    /// returns a new instance of the [`TriadBase`] with the given chord and kind as a
33    /// diminished triad.
34    pub fn diminished(root: T) -> Self
35    where
36        T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
37    {
38        TriadBase::from_root_with_class(root, Diminished)
39    }
40}
41
42impl<S, T> TriadBase<S, Major, T>
43where
44    S: TriadRepr<Elem = T>,
45{
46    /// returns a new instance of the [`TriadBase`] with the given chord and kind as a major
47    /// triad.
48    pub fn major(root: T) -> Self
49    where
50        T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
51    {
52        TriadBase::from_root_with_class(root, Major)
53    }
54}
55
56impl<S, T> TriadBase<S, Minor, T>
57where
58    S: TriadRepr<Elem = T>,
59{
60    /// returns a new instance of the [`TriadBase`] with the given chord and kind as a minor
61    /// triad.
62    pub fn minor(root: T) -> Self
63    where
64        T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
65    {
66        TriadBase::from_root_with_class(root, Minor)
67    }
68}
69
70impl<T> TriadBase<[T; 3], Triads, T>
71where
72    T: Copy + ToPrimitive + FromPrimitive + PitchMod<Output = T> + core::ops::Add<Output = T>,
73{
74    // /// creates a new augmented triad from the given root
75    // pub fn augmented(root: T) -> Self {
76    //     Self::from_root_with_class(root, TriadClass::Augmented)
77    // }
78    // /// creates a new diminished triad from the given root
79    // pub fn diminished(root: T) -> Self
80    // where
81    //     T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
82    // {
83    //     Self::from_root_with_class(root, TriadClass::Diminished)
84    // }
85    // /// Create a new major triad from the given root
86    // pub fn major(root: T) -> Self
87    // where
88    //     T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
89    // {
90    //     Self::from_root_with_class(root, TriadClass::Major)
91    // }
92    // /// creates a new minor triad from the given root
93    // pub fn minor(root: T) -> Self
94    // where
95    //     T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
96    // {
97    //     Self::from_root_with_class(root, TriadClass::Minor)
98    // }
99    /// return the barycentric coordinates of the given note w.r.t the current triad
100    pub fn barycentric<N, U>(&self, p: N) -> [U; 3]
101    where
102        T: Copy + ToPrimitive,
103        N: rstmt_core::IntoAspn,
104        U: Float + FromPrimitive,
105    {
106        let note = p.into_aspn();
107        let px = U::from_usize(note.class().pmod()).unwrap();
108        let py = U::from_isize(*note.octave()).unwrap();
109        let y = U::from(*self.octave).unwrap();
110        let [v0, v1, v2] = self.chord().map(|n| U::from(n).unwrap());
111
112        let d00 = v0 * v0 + y * y;
113        let d01 = v0 * v1 + y * y;
114        let d11 = v1 * v1 + y * y;
115        let d20 = v2 * px + y * py;
116        let d21 = v2 * v1 + y * y;
117
118        let denom = d00 * d11 - d01 * d01;
119        let a = (d11 * d20 - d01 * d21) / denom;
120        let b = (d00 * d21 - d01 * d20) / denom;
121        let c = U::one() - a - b;
122        [a, b, c]
123    }
124    /// returns true if the pitches within the triad match its classification
125    pub fn is_valid(&self) -> bool
126    where
127        T: Copy
128            + PartialEq
129            + FromPrimitive
130            + ToPrimitive
131            + PitchMod<Output = T>
132            + core::ops::Sub<Output = T>,
133    {
134        self.class().validate(self.chord())
135    }
136    /// returns some [`LPR`] transformation, iff they are within a single _step_ of one another
137    /// otherwise, returns [`None`](Option::None).
138    pub fn is_neighbor(&self, other: &Self) -> Option<LPR>
139    where
140        Self: Transform<LPR, Output = Self>,
141        T: PartialEq,
142    {
143        LPR::iter().find(|&dirac| self.transform(dirac) == *other)
144    }
145}
146impl<T> TriadBase<[T; 3], Triads, T>
147where
148    T: Copy + ToPrimitive + FromPrimitive + Num + PitchMod<Output = T>,
149{
150    // TODO: create a `Walk` iterator using the given transformations and
151    /// apply a series of transformations to a triad
152    pub fn walk<I>(&self, path: I) -> Self
153    where
154        I: IntoIterator<Item = LPR>,
155    {
156        path.into_iter()
157            .fold(*self, |triad, dirac| dirac.apply(triad))
158    }
159    /// apply a chain of transformations to a triad in-place
160    pub fn walk_inplace<I>(&mut self, path: I)
161    where
162        I: IntoIterator<Item = LPR>,
163    {
164        *self = self.walk(path);
165    }
166}
167
168impl TriadBase<[usize; 3], Triads> {
169    /// apply a single transformation to a triad in-place, mutating the current instance
170    pub fn transform_inplace(&mut self, transform: LPR) {
171        *self = self.transform(transform);
172    }
173}