Skip to main content

void_graph/graph/
geometry.rs

1//! 2D geometry primitives for graph rendering.
2//!
3//! This module is adapted from [Serie](https://github.com/lusingander/serie),
4//! a TUI Git graph visualizer by lusingander. Used under MIT license.
5
6use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
7
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct Point {
10    pub x: f64,
11    pub y: f64,
12}
13
14impl Point {
15    pub fn new(x: f64, y: f64) -> Self {
16        Self { x, y }
17    }
18
19    pub fn is_inside_polygon(&self, vertices: &[Point]) -> bool {
20        if vertices.len() < 3 {
21            return false;
22        }
23
24        let signs = vertices
25            .iter()
26            .zip(vertices.iter().cycle().skip(1))
27            .map(|(&a, &b)| {
28                let edge_vector = b - a;
29                let point_vector = *self - a;
30                edge_vector.cross(point_vector).signum()
31            })
32            .collect::<Vec<_>>();
33
34        let first_sign = signs[0];
35        if first_sign == 0.0 {
36            return true;
37        }
38
39        signs.iter().all(|&s| s == 0.0 || s == first_sign)
40    }
41}
42
43impl Add<Vector> for Point {
44    type Output = Point;
45
46    fn add(self, rhs: Vector) -> Self::Output {
47        Point {
48            x: self.x + rhs.x,
49            y: self.y + rhs.y,
50        }
51    }
52}
53
54impl Sub for Point {
55    type Output = Vector;
56
57    fn sub(self, rhs: Point) -> Self::Output {
58        Vector {
59            x: self.x - rhs.x,
60            y: self.y - rhs.y,
61        }
62    }
63}
64
65impl Sub<Vector> for Point {
66    type Output = Point;
67
68    fn sub(self, rhs: Vector) -> Self::Output {
69        Point {
70            x: self.x - rhs.x,
71            y: self.y - rhs.y,
72        }
73    }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq)]
77pub struct Vector {
78    pub x: f64,
79    pub y: f64,
80}
81
82impl Vector {
83    pub fn new(x: f64, y: f64) -> Self {
84        Self { x, y }
85    }
86
87    pub fn length(&self) -> f64 {
88        (self.x.powi(2) + self.y.powi(2)).sqrt()
89    }
90
91    pub fn zero() -> Self {
92        Self { x: 0.0, y: 0.0 }
93    }
94
95    pub fn normalize(&self) -> Vector {
96        let len = self.length();
97        if len == 0.0 {
98            Vector::zero()
99        } else {
100            *self / len
101        }
102    }
103
104    pub fn perpendicular(&self) -> Vector {
105        Vector::new(-self.y, self.x)
106    }
107
108    pub fn dot(&self, other: Vector) -> f64 {
109        self.x * other.x + self.y * other.y
110    }
111
112    pub fn cross(&self, other: Vector) -> f64 {
113        self.x * other.y - self.y * other.x
114    }
115}
116
117impl Add for Vector {
118    type Output = Vector;
119
120    fn add(self, rhs: Vector) -> Self::Output {
121        Vector {
122            x: self.x + rhs.x,
123            y: self.y + rhs.y,
124        }
125    }
126}
127
128impl AddAssign for Vector {
129    fn add_assign(&mut self, rhs: Self) {
130        self.x += rhs.x;
131        self.y += rhs.y;
132    }
133}
134
135impl Sub for Vector {
136    type Output = Vector;
137
138    fn sub(self, rhs: Vector) -> Self::Output {
139        Vector {
140            x: self.x - rhs.x,
141            y: self.y - rhs.y,
142        }
143    }
144}
145
146impl SubAssign for Vector {
147    fn sub_assign(&mut self, rhs: Self) {
148        self.x -= rhs.x;
149        self.y -= rhs.y;
150    }
151}
152
153impl Mul<f64> for Vector {
154    type Output = Vector;
155
156    fn mul(self, rhs: f64) -> Self::Output {
157        Vector {
158            x: self.x * rhs,
159            y: self.y * rhs,
160        }
161    }
162}
163
164impl Mul<Vector> for f64 {
165    type Output = Vector;
166
167    fn mul(self, rhs: Vector) -> Self::Output {
168        rhs * self
169    }
170}
171
172impl Div<f64> for Vector {
173    type Output = Vector;
174
175    fn div(self, rhs: f64) -> Self::Output {
176        Vector {
177            x: self.x / rhs,
178            y: self.y / rhs,
179        }
180    }
181}
182
183pub fn bounding_box_u32(points: &[Point]) -> (u32, u32, u32, u32) {
184    let (mut min_x, mut min_y) = (f64::INFINITY, f64::INFINITY);
185    let (mut max_x, mut max_y) = (f64::NEG_INFINITY, f64::NEG_INFINITY);
186
187    for point in points {
188        if point.x < min_x {
189            min_x = point.x;
190        }
191        if point.y < min_y {
192            min_y = point.y;
193        }
194        if point.x > max_x {
195            max_x = point.x;
196        }
197        if point.y > max_y {
198            max_y = point.y;
199        }
200    }
201
202    (min_x as u32, min_y as u32, max_x as u32, max_y as u32)
203}