/ bin / app / src / gfx / linalg.rs
linalg.rs
  1  /* This file is part of DarkFi (https://dark.fi)
  2   *
  3   * Copyright (C) 2020-2025 Dyne.org foundation
  4   *
  5   * This program is free software: you can redistribute it and/or modify
  6   * it under the terms of the GNU Affero General Public License as
  7   * published by the Free Software Foundation, either version 3 of the
  8   * License, or (at your option) any later version.
  9   *
 10   * This program is distributed in the hope that it will be useful,
 11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13   * GNU Affero General Public License for more details.
 14   *
 15   * You should have received a copy of the GNU Affero General Public License
 16   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17   */
 18  
 19  use async_trait::async_trait;
 20  use darkfi_serial::{SerialDecodable, SerialEncodable};
 21  use std::ops::{Add, AddAssign, Div, DivAssign, Mul, Sub, SubAssign};
 22  
 23  #[derive(Clone, Copy, Debug, SerialEncodable, SerialDecodable)]
 24  pub struct Dimension {
 25      pub w: f32,
 26      pub h: f32,
 27  }
 28  
 29  impl Dimension {
 30      pub fn contains(&self, other: &Dimension) -> bool {
 31          other.w <= self.w && other.h <= self.h
 32      }
 33  }
 34  
 35  impl From<[f32; 2]> for Dimension {
 36      fn from(dim: [f32; 2]) -> Self {
 37          Self { w: dim[0], h: dim[1] }
 38      }
 39  }
 40  
 41  impl Mul<f32> for Dimension {
 42      type Output = Dimension;
 43  
 44      fn mul(self, scale: f32) -> Self::Output {
 45          Self { w: self.w * scale, h: self.h * scale }
 46      }
 47  }
 48  
 49  impl Div<f32> for Dimension {
 50      type Output = Dimension;
 51  
 52      fn div(self, scale: f32) -> Self::Output {
 53          Self { w: self.w / scale, h: self.h / scale }
 54      }
 55  }
 56  
 57  #[derive(Clone, Copy, Default, SerialEncodable, SerialDecodable)]
 58  pub struct Point {
 59      pub x: f32,
 60      pub y: f32,
 61  }
 62  
 63  impl Point {
 64      pub const fn new(x: f32, y: f32) -> Self {
 65          Self { x, y }
 66      }
 67  
 68      pub fn zero() -> Self {
 69          Self { x: 0., y: 0. }
 70      }
 71  
 72      pub fn unpack(&self) -> (f32, f32) {
 73          (self.x, self.y)
 74      }
 75  
 76      pub fn as_arr(&self) -> [f32; 2] {
 77          [self.x, self.y]
 78      }
 79  
 80      pub fn offset(&self, off_x: f32, off_y: f32) -> Self {
 81          Self { x: self.x + off_x, y: self.y + off_y }
 82      }
 83  
 84      pub fn to_rect(&self, w: f32, h: f32) -> Rectangle {
 85          Rectangle { x: self.x, y: self.y, w, h }
 86      }
 87  
 88      pub fn dist_sq(&self, other: Point) -> f32 {
 89          (self.x - other.x).powi(2) + (self.y - other.y).powi(2)
 90      }
 91      pub fn dist(&self, other: Point) -> f32 {
 92          self.dist_sq(other).sqrt()
 93      }
 94  
 95      pub fn normalize(&mut self) {
 96          let scale = self.dist(Point::zero());
 97          self.x /= scale;
 98          self.y /= scale;
 99          assert!((self.dist(Point::zero()) - 1.) < f32::EPSILON);
100      }
101  
102      /// Counterclockwise perp vector (with -y up)
103      pub fn perp_left(&self) -> Point {
104          Point::new(self.y, -self.x)
105      }
106      /// Clockwise perp vector (with +y down)
107      pub fn perp_right(&self) -> Point {
108          Point::new(-self.y, self.x)
109      }
110  }
111  
112  impl From<[f32; 2]> for Point {
113      fn from(pos: [f32; 2]) -> Self {
114          Self { x: pos[0], y: pos[1] }
115      }
116  }
117  
118  impl std::fmt::Debug for Point {
119      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120          write!(f, "({}, {})", self.x, self.y)
121      }
122  }
123  
124  impl Add for Point {
125      type Output = Self;
126  
127      fn add(self, other: Self) -> Self::Output {
128          Self { x: self.x + other.x, y: self.y + other.y }
129      }
130  }
131  
132  impl Sub for Point {
133      type Output = Self;
134  
135      fn sub(self, other: Self) -> Self::Output {
136          Self { x: self.x - other.x, y: self.y - other.y }
137      }
138  }
139  
140  impl AddAssign for Point {
141      fn add_assign(&mut self, other: Self) {
142          *self = Self { x: self.x + other.x, y: self.y + other.y };
143      }
144  }
145  
146  impl SubAssign for Point {
147      fn sub_assign(&mut self, other: Self) {
148          *self = Self { x: self.x - other.x, y: self.y - other.y };
149      }
150  }
151  
152  impl Mul<f32> for Point {
153      type Output = Self;
154  
155      fn mul(self, scale: f32) -> Self {
156          Point::new(scale * self.x, scale * self.y)
157      }
158  }
159  
160  #[derive(Clone, Copy, SerialEncodable, SerialDecodable)]
161  pub struct Rectangle {
162      pub x: f32,
163      pub y: f32,
164      pub w: f32,
165      pub h: f32,
166  }
167  
168  impl Rectangle {
169      pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
170          Self { x, y, w, h }
171      }
172  
173      pub fn zero() -> Self {
174          Self { x: 0., y: 0., w: 0., h: 0. }
175      }
176  
177      /// Use from() instead
178      #[deprecated]
179      pub fn from_array(arr: [f32; 4]) -> Self {
180          Self { x: arr[0], y: arr[1], w: arr[2], h: arr[3] }
181      }
182  
183      pub fn clip(&self, other: &Self) -> Option<Self> {
184          if other.x + other.w < self.x ||
185              other.x > self.x + self.w ||
186              other.y + other.h < self.y ||
187              other.y > self.y + self.h
188          {
189              return None
190          }
191  
192          let mut clipped = other.clone();
193          if clipped.x < self.x {
194              clipped.x = self.x;
195              clipped.w = other.x + other.w - clipped.x;
196          }
197          if clipped.y < self.y {
198              clipped.y = self.y;
199              clipped.h = other.y + other.h - clipped.y;
200          }
201          if clipped.x + clipped.w > self.x + self.w {
202              clipped.w = self.x + self.w - clipped.x;
203          }
204          if clipped.y + clipped.h > self.y + self.h {
205              clipped.h = self.y + self.h - clipped.y;
206          }
207          Some(clipped)
208      }
209  
210      pub fn with_zero_pos(&self) -> Self {
211          Self::new(0., 0., self.w, self.h)
212      }
213  
214      pub fn clip_point(&self, mut point: Point) -> Point {
215          if point.x < self.x {
216              point.x = self.x;
217          }
218          if point.y < self.y {
219              point.y = self.y;
220          }
221          if point.x > self.x + self.w {
222              point.x = self.x + self.w;
223          }
224          if point.y > self.y + self.h {
225              point.y = self.y + self.h;
226          }
227          point
228      }
229  
230      pub fn contains(&self, point: Point) -> bool {
231          self.x <= point.x &&
232              point.x <= self.x + self.w &&
233              self.y <= point.y &&
234              point.y <= self.y + self.h
235      }
236  
237      pub fn rhs(&self) -> f32 {
238          self.x + self.w
239      }
240      pub fn bhs(&self) -> f32 {
241          self.y + self.h
242      }
243  
244      pub fn pos(&self) -> Point {
245          Point { x: self.x, y: self.y }
246      }
247      pub fn corner(&self) -> Point {
248          Point { x: self.x + self.w, y: self.y + self.h }
249      }
250      pub fn center(&self) -> Point {
251          Point { x: self.x + self.w / 2., y: self.y + self.h / 2. }
252      }
253      pub fn top_right(&self) -> Point {
254          Point { x: self.rhs(), y: self.y }
255      }
256      pub fn bot_left(&self) -> Point {
257          Point { x: self.x, y: self.y + self.h }
258      }
259  
260      pub fn dim(&self) -> Dimension {
261          Dimension { w: self.w, h: self.h }
262      }
263  
264      #[deprecated]
265      pub fn top_left(&self) -> Point {
266          Point { x: self.x, y: self.y }
267      }
268      #[deprecated]
269      pub fn bottom_right(&self) -> Point {
270          Point { x: self.x + self.w, y: self.y + self.h }
271      }
272  
273      pub fn includes(&self, child: &Self) -> bool {
274          self.contains(child.pos()) && self.contains(child.corner())
275      }
276  }
277  
278  impl From<[f32; 4]> for Rectangle {
279      fn from(rect: [f32; 4]) -> Self {
280          Self { x: rect[0], y: rect[1], w: rect[2], h: rect[3] }
281      }
282  }
283  
284  impl Add<Point> for Rectangle {
285      type Output = Rectangle;
286  
287      fn add(self, other: Point) -> Self::Output {
288          Self { x: self.x + other.x, y: self.y + other.y, w: self.w, h: self.h }
289      }
290  }
291  
292  impl Sub<Point> for Rectangle {
293      type Output = Rectangle;
294  
295      fn sub(self, other: Point) -> Self::Output {
296          Self { x: self.x - other.x, y: self.y - other.y, w: self.w, h: self.h }
297      }
298  }
299  
300  impl Mul<f32> for Rectangle {
301      type Output = Rectangle;
302  
303      fn mul(self, scale: f32) -> Self::Output {
304          Self { x: self.x * scale, y: self.y * scale, w: self.w * scale, h: self.h * scale }
305      }
306  }
307  
308  impl Div<f32> for Rectangle {
309      type Output = Rectangle;
310  
311      fn div(self, scale: f32) -> Self::Output {
312          Self { x: self.x / scale, y: self.y / scale, w: self.w / scale, h: self.h / scale }
313      }
314  }
315  
316  impl DivAssign<f32> for Rectangle {
317      fn div_assign(&mut self, scale: f32) {
318          self.x /= scale;
319          self.y /= scale;
320          self.w /= scale;
321          self.h /= scale;
322      }
323  }
324  
325  impl std::fmt::Debug for Rectangle {
326      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327          write!(f, "({}, {}, {}, {})", self.x, self.y, self.w, self.h)
328      }
329  }
330  
331  impl From<parley::BoundingBox> for Rectangle {
332      fn from(rect: parley::BoundingBox) -> Self {
333          Self::new(rect.x0 as f32, rect.y0 as f32, rect.width() as f32, rect.height() as f32)
334      }
335  }