eryon_surface/points/
point.rs1use super::PointKind;
6use num_traits::{FromPrimitive, Num};
7use rstmt::Octave;
8use rstmt::nrt::{Triad, Triads};
9
10fn adjust_coordinates<T>(coords: [T; 3], triad: &Triad) -> [T; 3]
12where
13 T: Copy + PartialOrd + FromPrimitive + Num,
14{
15 let class = triad.class();
17 match class {
19 Triads::Major => {
20 coords
22 }
23 Triads::Minor => {
24 let third_weight_reduction = T::from_f32(0.1).unwrap();
26 [
27 coords[0],
28 coords[1] - third_weight_reduction,
29 coords[2] + third_weight_reduction,
30 ]
31 }
32 Triads::Diminished => {
33 let fifth_weight_reduction = T::from_f32(0.15).unwrap();
35 [
36 coords[0],
37 coords[1] + fifth_weight_reduction,
38 coords[2] - fifth_weight_reduction,
39 ]
40 }
41 Triads::Augmented => {
42 let fifth_weight_increase = T::from_f32(0.1).unwrap();
44 let total = T::one() - fifth_weight_increase;
45 let factor = total / (coords[0] + coords[1]);
46 [
47 coords[0] * factor,
48 coords[1] * factor,
49 coords[2] + fifth_weight_increase,
50 ]
51 } }
53}
54
55#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
57#[cfg_attr(
58 feature = "serde",
59 derive(serde_derive::Deserialize, serde_derive::Serialize)
60)]
61pub struct Point<T = f32> {
62 pub(crate) coordinates: [T; 3],
64 pub(crate) kind: PointKind,
66 pub(crate) modulus: Octave,
68 pub(crate) weight: T,
70}
71
72impl<T> Point<T> {
73 pub fn new(mut u: T, mut v: T, modulus: Octave<isize>, kind: PointKind) -> Self
74 where
75 T: Copy + PartialOrd + num_traits::Num,
76 {
77 let sum = u + v;
79 if sum > T::one() {
80 u = u / sum;
82 v = v / sum;
83 }
84 let w = T::one() - u - v;
85
86 Self {
87 coordinates: [u, v, w],
88 kind,
89 modulus,
90 weight: T::one(),
91 }
92 }
93 pub fn from_triad(kind: PointKind, triad: &Triad) -> Self
95 where
96 T: num_traits::Float + num_traits::FromPrimitive,
97 {
98 let base_coords = kind.default_coordinates::<T>();
99 let adjusted_coords = adjust_coordinates(base_coords, triad);
100
101 Self {
102 coordinates: adjusted_coords,
103 modulus: triad.octave(),
104 kind,
105 weight: T::one(),
106 }
107 }
108 pub const fn coordinates(&self) -> &[T; 3] {
110 &self.coordinates
111 }
112 pub fn coordinates_mut(&mut self) -> &mut [T; 3] {
114 &mut self.coordinates
115 }
116 pub const fn modulus(&self) -> &Octave {
118 &self.modulus
119 }
120 pub fn modulus_mut(&mut self) -> &mut Octave {
122 &mut self.modulus
123 }
124 pub fn kind(&self) -> PointKind {
126 self.kind
127 }
128 pub const fn weight(&self) -> &T {
130 &self.weight
131 }
132 pub fn weight_mut(&mut self) -> &mut T {
134 &mut self.weight
135 }
136 pub fn set_coordinates(&mut self, u: T, v: T)
138 where
139 T: Copy + PartialOrd + num_traits::Num,
140 {
141 let sum = u + v;
142 if sum > T::one() {
143 self.coordinates[0] = u / sum;
144 self.coordinates[1] = v / sum;
145 } else {
146 self.coordinates[0] = u;
147 self.coordinates[1] = v;
148 }
149 self.coordinates[2] = T::one() - self.coordinates[0] - self.coordinates[1];
150 }
151 pub fn set_kind(&mut self, kind: PointKind) {
153 self.kind = kind;
154 }
155 pub fn set_weight(&mut self, weight: T) {
157 self.weight = weight;
158 }
159 pub fn with_coordinates(self, u: T, v: T) -> Self
161 where
162 T: Copy + PartialOrd + num_traits::NumOps<T> + num_traits::One,
163 {
164 let sum = u + v;
165 let (u, v) = if sum > T::one() {
166 (u / sum, v / sum)
167 } else {
168 (u, v)
169 };
170 let w = T::one() - u - v;
171 Self {
172 coordinates: [u, v, w],
173 ..self
174 }
175 }
176 pub fn with_kind(self, kind: PointKind) -> Self {
178 Self { kind, ..self }
179 }
180 pub fn with_modulus(self, modulus: Octave) -> Self {
182 Self { modulus, ..self }
183 }
184 pub fn with_weight(self, weight: T) -> Self {
186 Self { weight, ..self }
187 }
188 pub fn validate(&self) -> bool
190 where
191 T: num_traits::Float,
192 {
193 let sum = self.coordinates.iter().fold(T::zero(), |a, &b| a + b);
194 (sum - T::one()).abs() < T::from(1e-6).unwrap()
195 }
196}
197
198impl<T> core::ops::Index<usize> for Point<T> {
199 type Output = T;
200
201 fn index(&self, index: usize) -> &Self::Output {
202 &self.coordinates[index % 3]
203 }
204}
205
206impl<T> core::ops::IndexMut<usize> for Point<T> {
207 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
208 &mut self.coordinates[index % 3]
209 }
210}