experimental SVG-based video rendering engine made for music videos. React to MIDI or arbitrary signals from your DAW through "probe" VSTs
1use num::FromPrimitive;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 Point::{Center, Corner},
6 Region,
7};
8
9use super::Angle;
10
11#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
12#[cfg_attr(feature = "web", serde(tag = "type", content = "data"))]
13pub enum Point {
14 Corner(usize, usize),
15 Center(usize, usize),
16}
17
18impl Default for Point {
19 fn default() -> Self {
20 Self::Corner(0, 0)
21 }
22}
23
24impl Point {
25 pub fn coords(&self, cell_size: usize) -> (f32, f32) {
26 let (x, y) = self.xy::<f32>();
27 let cell = cell_size as f32;
28
29 match self {
30 Point::Corner(..) => (x * cell, y * cell),
31 Point::Center(..) => (x * cell + cell / 2.0, y * cell + cell / 2.0),
32 }
33 }
34
35 pub fn as_centered(&self) -> Self {
36 match self {
37 Point::Corner(x, y) => Point::Center(*x, *y),
38 Point::Center(_, _) => *self,
39 }
40 }
41
42 pub fn as_corner(&self) -> Self {
43 match self {
44 Point::Center(x, y) => Point::Corner(*x, *y),
45 Point::Corner(_, _) => *self,
46 }
47 }
48
49 pub fn region(&self) -> Region {
50 Region::from((self.clone(), self.clone()))
51 }
52
53 pub fn with(&self, x: usize, y: usize) -> Self {
54 match self {
55 Point::Corner(..) => Point::Corner(x, y),
56 Point::Center(..) => Point::Center(x, y),
57 }
58 }
59
60 pub fn set(&mut self, x: usize, y: usize) {
61 *self = self.with(x, y);
62 }
63
64 pub fn set_x(&mut self, x: usize) {
65 self.set(x, self.y());
66 }
67
68 pub fn with_x(&self, x: usize) -> Self {
69 self.with(x, self.y())
70 }
71
72 pub fn increment_x(&mut self, by: isize) {
73 self.set_x(self.x().saturating_add_signed(by));
74 }
75
76 pub fn set_y(&mut self, y: usize) {
77 self.set(self.x(), y);
78 }
79
80 pub fn increment_y(&mut self, by: isize) {
81 self.set_y(self.y().saturating_add_signed(by));
82 }
83
84 pub fn with_y(&self, y: usize) -> Self {
85 self.with(self.x(), y)
86 }
87
88 pub fn xy<N: FromPrimitive>(&self) -> (N, N) {
89 let (x, y) = match self {
90 &Point::Corner(x, y) => (x, y),
91 &Point::Center(x, y) => (x, y),
92 };
93
94 (N::from_usize(x).unwrap(), N::from_usize(y).unwrap())
95 }
96
97 pub fn x(&self) -> usize {
98 self.xy().0
99 }
100
101 pub fn y(&self) -> usize {
102 self.xy().1
103 }
104
105 pub fn translated(&self, dx: i32, dy: i32) -> Self {
106 Self::from((
107 (self.x() as i32 + dx) as usize,
108 (self.y() as i32 + dy) as usize,
109 ))
110 }
111
112 pub fn translated_by(&self, point: Point) -> Self {
113 Self::from((self.x() + point.x(), self.y() + point.y()))
114 }
115
116 pub fn translate(&mut self, dx: i32, dy: i32) {
117 *self = Self::from((
118 (self.x() as i32 + dx) as usize,
119 (self.y() as i32 + dy) as usize,
120 ))
121 }
122
123 /// get SVG coordinates of the cell's center instead of its origin (top-left)
124 #[deprecated = "Use a CenterPoint instead (WIP)"]
125 pub fn center_coords(&self, cell_size: usize) -> (f32, f32) {
126 let (x, y) = self.coords(cell_size);
127 (x + cell_size as f32 / 2.0, y + cell_size as f32 / 2.0)
128 }
129
130 pub fn distance_to(&self, other: &Point) -> (usize, usize) {
131 (
132 self.x().abs_diff(other.x()) + 1,
133 self.y().abs_diff(other.y()) + 1,
134 )
135 }
136
137 pub fn rotated(&self, around: &Point, angle: Angle) -> Self {
138 let (dx, dy) = (
139 self.x() as f32 - around.x() as f32,
140 self.y() as f32 - around.y() as f32,
141 );
142
143 let (cos, sin) = angle.cos_sin();
144 let new_x = dx * cos - dy * sin;
145 let new_y = dx * sin + dy * cos;
146
147 Self::from((
148 (new_x + around.x() as f32) as usize,
149 (new_y + around.y() as f32) as usize,
150 ))
151 }
152}
153
154impl From<(usize, usize)> for Point {
155 fn from(value: (usize, usize)) -> Self {
156 Self::Corner(value.0, value.1)
157 }
158}
159
160impl From<(&usize, &usize)> for Point {
161 fn from(value: (&usize, &usize)) -> Self {
162 Self::Corner(*value.0, *value.1)
163 }
164}
165
166impl From<(i32, i32)> for Point {
167 fn from(value: (i32, i32)) -> Self {
168 Self::Corner(value.0 as usize, value.1 as usize)
169 }
170}
171
172impl PartialEq<(usize, usize)> for Point {
173 fn eq(&self, other: &(usize, usize)) -> bool {
174 self.xy() == other.clone()
175 }
176}
177
178impl Eq for Point {}
179
180impl std::fmt::Display for Point {
181 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
182 match self {
183 Point::Corner(x, y) => write!(f, "({x}, {y})"),
184 Point::Center(x, y) => write!(f, "[{x}, {y}]"),
185 }
186 }
187}
188
189impl std::fmt::Debug for Point {
190 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
191 match self {
192 Point::Corner(x, y) => write!(f, "({x}, {y})"),
193 Point::Center(x, y) => write!(f, "[{x}, {y}]"),
194 }
195 }
196}
197
198impl std::ops::Sub for Point {
199 type Output = (isize, isize);
200
201 fn sub(self, rhs: Point) -> Self::Output {
202 match (self, rhs) {
203 (Corner(..), Corner(..)) => {}
204 (Center(..), Center(..)) => {}
205 _ => panic!("Cannot subtract CornerPoint and CenterPoint"),
206 }
207
208 let (x1, y1) = self.xy::<isize>();
209 let (x2, y2) = rhs.xy::<isize>();
210
211 (x1 - x2, y1 - y2)
212 }
213}
214
215pub trait Norm {
216 fn norm(&self) -> f32;
217}
218
219impl Norm for (usize, usize) {
220 fn norm(&self) -> f32 {
221 ((self.0 * self.0 + self.1 * self.1) as f32).sqrt()
222 }
223}