rstmt_nrt/impls/
impl_triad_base.rs

1/*
2    Appellation: impl_triad_base <module>
3    Created At: 2025.12.20:10:56:30
4    Contrib: @FL03
5*/
6use crate::triad::TriadBase;
7
8use crate::traits::{TriadRepr, TriadReprMut, TriadType};
9use crate::types::LPR;
10use num_traits::{Float, FromPrimitive, ToPrimitive, Zero};
11use rstmt_core::{Octave, PitchMod, Transform};
12
13impl<S, T, K> TriadBase<S, K, T>
14where
15    K: TriadType,
16    S: TriadRepr<Elem = T>,
17{
18    /// Returns a new instance of the [`TriadBase`] with the given chord and kind.
19    pub fn new(chord: S, class: K) -> Self
20    where
21        T: Zero,
22    {
23        Self {
24            chord,
25            class,
26            octave: Octave::zero(),
27        }
28    }
29    /// Create a new triad from a root pitch and class
30    pub fn from_root_with_class(root: T, class: K) -> Self
31    where
32        T: Copy + FromPrimitive + Zero + PitchMod<Output = T> + core::ops::Add<Output = T>,
33    {
34        // generate the chord factors from the root and class
35        let chord = [
36            root,
37            (root + T::from_usize(class.root()).unwrap()).pmod(),
38            (root + T::from_usize(class.fifth()).unwrap()).pmod(),
39        ];
40        Self {
41            class,
42            chord: S::from_arr(chord),
43            octave: Octave::zero(),
44        }
45    }
46    #[inline]
47    /// consumes the instance to use a dynamic classification enum
48    pub fn dynamic(self) -> TriadBase<S, crate::Triads, T> {
49        TriadBase {
50            chord: self.chord,
51            class: crate::Triads::from_class(self.class),
52            octave: self.octave,
53        }
54    }
55    /// returns an immutable reference to the chord.
56    pub const fn chord(&self) -> &S {
57        &self.chord
58    }
59    /// returns a mutable reference to the chord.
60    pub const fn chord_mut(&mut self) -> &mut S {
61        &mut self.chord
62    }
63    /// returns a reference of the class of the triad.
64    pub const fn class(&self) -> &K {
65        &self.class
66    }
67    /// returns a mutable reference to the class of the triad.
68    pub const fn class_mut(&mut self) -> &mut K {
69        &mut self.class
70    }
71    /// returns a reference to the octave of the triad.
72    pub const fn octave(&self) -> &Octave<T> {
73        &self.octave
74    }
75    pub const fn octave_mut(&mut self) -> &mut Octave<T> {
76        &mut self.octave
77    }
78    /// returns a reference to the root note of the triad.
79    pub fn root(&self) -> &T
80    where
81        S: TriadRepr,
82    {
83        self.chord().root()
84    }
85    /// returns a mutable reference to the root note of the triad.
86    pub fn root_mut(&mut self) -> &mut T
87    where
88        S: TriadReprMut,
89    {
90        self.chord_mut().root_mut()
91    }
92    /// returns a reference to the third note of the triad.
93    pub fn third(&self) -> &T
94    where
95        S: TriadRepr,
96    {
97        self.chord().third()
98    }
99    /// returns a mutable reference to the third note of the triad.
100    pub fn third_mut(&mut self) -> &mut T
101    where
102        S: TriadReprMut,
103    {
104        self.chord_mut().third_mut()
105    }
106    /// returns a reference to the fifth note of the triad.
107    pub fn fifth(&self) -> &T
108    where
109        S: TriadRepr,
110    {
111        self.chord().fifth()
112    }
113    /// returns a mutable reference to the fifth note of the triad.
114    pub fn fifth_mut(&mut self) -> &mut T
115    where
116        S: TriadReprMut,
117    {
118        self.chord_mut().fifth_mut()
119    }
120    /// update the chord and returns a mutable reference to the triad
121    pub fn set_chord(&mut self, chord: S) -> &mut Self {
122        self.chord = chord;
123        self
124    }
125    /// update the class and returns a mutable reference to the triad
126    pub fn set_class(&mut self, class: K) -> &mut Self {
127        self.class = class;
128        self
129    }
130    #[inline]
131    /// consumes the current instance to create another with the given chord
132    pub fn with_chord<S2>(self, chord: S2) -> TriadBase<S2, K>
133    where
134        S2: TriadRepr<Elem = T>,
135        T: Sized,
136    {
137        TriadBase {
138            chord,
139            class: self.class,
140            octave: self.octave,
141        }
142    }
143    #[inline]
144    /// consumes the current instance to create another with the given class
145    pub fn with_class<K2>(self, class: K2) -> TriadBase<S, K2>
146    where
147        K2: TriadType,
148    {
149        TriadBase {
150            chord: self.chord,
151            class,
152            octave: self.octave,
153        }
154    }
155    #[inline]
156    pub fn with_octave(self, octave: Octave<T>) -> Self {
157        Self { octave, ..self }
158    }
159    /// consumes the triad and returns the chord and class
160    pub fn into_parts(self) -> (S, K) {
161        (self.chord, self.class)
162    }
163    /// returns true if the triad is classified as an augmented triad.
164    pub fn is_augmented(&self) -> bool {
165        self.class().is_augmented()
166    }
167    /// returns true if the triad is classified as a diminished triad.
168    pub fn is_diminished(&self) -> bool {
169        self.class().is_diminished()
170    }
171    /// returns true if the triad is classified as a major triad.
172    pub fn is_major(&self) -> bool {
173        self.class().is_major()
174    }
175    /// returns true if the triad is classified as a minor triad.
176    pub fn is_minor(&self) -> bool {
177        self.class().is_minor()
178    }
179    /// computes the centroid of the triad
180    pub fn centroid<U>(&self) -> Option<[U; 2]>
181    where
182        T: Copy + ToPrimitive,
183        U: Float + FromPrimitive + ToPrimitive + core::iter::Sum<T>,
184        S: Clone + IntoIterator<Item = T>,
185    {
186        let y = U::from(*self.octave().get())?;
187        let x = self.chord().clone().into_iter().sum::<U>() / U::from_u8(3)?;
188        Some([x, y])
189    }
190    /// returns true if the triad contains the given note
191    pub fn contains<Q>(&self, note: &Q) -> bool
192    where
193        for<'a> &'a S: IntoIterator<Item = &'a T>,
194        T: PartialEq,
195        Q: core::borrow::Borrow<T>,
196    {
197        self.chord().into_iter().any(|n| n == note.borrow())
198    }
199    #[cfg(feature = "alloc")]
200    /// returns a collection containing any tones common to both triads
201    pub fn common_tones(&self, other: &Self) -> alloc::vec::Vec<T>
202    where
203        T: Clone + PartialEq,
204        for<'a> &'a S: IntoIterator<Item = &'a T>,
205    {
206        self.chord()
207            .into_iter()
208            .filter(|n| other.contains(n))
209            .cloned()
210            .collect::<alloc::vec::Vec<_>>()
211    }
212    /// apply the given [`LPR`] transformation onto the triad, returning a new triad classified
213    /// under `Q` where `Q` and `K` are related via the `Rel` associated type. For example, if
214    /// transforming a major triad, then the resulting triad will be minor (and vice versa).
215    pub fn transform<X, Y>(self, step: X) -> Y
216    where
217        Self: Transform<X, Output = Y>,
218    {
219        <Self as Transform<X>>::transform(self, step)
220    }
221    /// apply the [`Leading`](LPR::Leading) transformation to the triad
222    pub fn leading<Y>(self) -> Y
223    where
224        Self: Transform<LPR, Output = Y>,
225    {
226        self.transform(LPR::Leading)
227    }
228    /// apply the [`Parallel`](LPR::Parallel) transformation to the triad
229    pub fn parallel<Y>(self) -> Y
230    where
231        Self: Transform<LPR, Output = Y>,
232    {
233        self.transform(LPR::Parallel)
234    }
235    /// apply the [`Relative`](LPR::Relative) transformation to the triad
236    pub fn relative<Y>(self) -> Y
237    where
238        Self: Transform<LPR, Output = Y>,
239    {
240        self.transform(LPR::Relative)
241    }
242    #[cfg(feature = "motion")]
243    /// returns a path finder for the current triad
244    pub fn path_finder(&self) -> crate::motion::PathFinder<'_, S, K, T> {
245        crate::motion::PathFinder::new(self)
246    }
247}