wassily_core/
points.rs

1//! # Point Utilities
2//!
3//! Functions and types for dealing with 2D and 3D points, including mathematical
4//! operations, transformations, and utility functions for generative art.
5//!
6//! ## Key Functions
7//!
8//! - [`pt()`]: Create 2D points from any numeric type
9//! - [`pt3()`]: Create 3D points from any numeric type
10//! - [`center()`]: Find the center point of a rectangle
11//! - Linear interpolation between points
12//! - Distance calculations between points
13//!
14//! ## Mathematical Constants
15//!
16//! Common mathematical constants are re-exported for convenience:
17//! - [`PI`]: π (3.14159...)
18//! - [`TAU`]: 2π (6.28318...)
19//! - [`HALF_PI`]: π/2 (1.57079...)
20//!
21//! ## Example
22//!
23//! ```no_run
24//! use wassily_core::*;
25//! 
26//! // Create points from different numeric types
27//! let p1 = pt(100, 200);      // From integers
28//! let p2 = pt(150.5, 250.5);  // From floats
29//! 
30//! // Calculate center of a canvas
31//! let center_point = center(400, 300);
32//! 
33//! // Linear interpolation
34//! let midpoint = lerp(p1, p2, 0.5);
35//! ```
36use num_complex::Complex32;
37use num_traits::AsPrimitive;
38use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
39use tiny_skia::Point;
40
41pub use std::f32::consts::FRAC_PI_2 as HALF_PI;
42pub use std::f32::consts::PI;
43pub use std::f32::consts::TAU;
44
45/// Create a `Point` from x and y coordinates.
46pub fn pt<S, T>(x: S, y: T) -> Point
47where
48    S: AsPrimitive<f32>,
49    T: AsPrimitive<f32>,
50{
51    Point::from_xy(x.as_(), y.as_())
52}
53
54pub fn pt3<S, T, U>(x: S, y: T, z: U) -> Point3
55where
56    S: AsPrimitive<f32>,
57    T: AsPrimitive<f32>,
58    U: AsPrimitive<f32>,
59{
60    Point3 {
61        x: x.as_(),
62        y: y.as_(),
63        z: z.as_(),
64    }
65}
66
67pub fn polar<S, T>(theta: S, r: T) -> Point
68where
69    S: AsPrimitive<f32>,
70    T: AsPrimitive<f32>,
71{
72    Point::from_xy(r.as_() * theta.as_().cos(), r.as_() * theta.as_().sin())
73}
74
75pub fn complex(p: Point) -> Complex32 {
76    Complex32::new(p.x, p.y)
77}
78
79pub fn center<S, T>(width: S, height: T) -> Point
80where
81    S: AsPrimitive<f32>,
82    T: AsPrimitive<f32>,
83{
84    Point::from_xy(width.as_() / 2.0, height.as_() / 2.0)
85}
86
87/// A trait to unify the interface of tiny-skia `Point` and wassily `Point3`.
88pub trait Algebra: Copy {
89    const ZERO: Self;
90
91    /// Multiply all coordinates by a scalar.
92    fn scale(self, k: f32) -> Self;
93
94    /// Linear interpolation between two points.
95    fn lerp(self, other: Self, t: f32) -> Self;
96
97    /// The square of the magnitude of the vector.
98    fn mag2(self) -> f32;
99
100    /// The distance squared between two points.
101    fn dist2(self, other: Self) -> f32;
102
103    /// The dot product of two vectors.
104    fn dot(self, other: Self) -> f32;
105
106    /// The magnitude of the vector.
107    fn mag(self) -> f32 {
108        self.mag2().sqrt()
109    }
110
111    /// Normalize the vector.
112    fn normalize(self) -> Self {
113        self.scale(1.0 / self.mag())
114    }
115
116    /// The average of two vectors.
117    fn average(self, other: Self) -> Self {
118        self.lerp(other, 0.5)
119    }
120
121    /// Distance between two points.
122    fn dist(self, other: Self) -> f32 {
123        self.dist2(other).sqrt()
124    }
125}
126
127impl Algebra for Point {
128    const ZERO: Self = Point { x: 0.0, y: 0.0 };
129
130    fn mag2(self) -> f32 {
131        self.x * self.x + self.y * self.y
132    }
133
134    fn scale(self, k: f32) -> Self {
135        Point::from_xy(k * self.x, k * self.y)
136    }
137
138    fn lerp(self, other: Self, t: f32) -> Self {
139        let x = self.x * (1.0 - t) + t * other.x;
140        let y = self.y * (1.0 - t) + t * other.y;
141        Self::from_xy(x, y)
142    }
143
144    fn dist2(self, other: Self) -> f32 {
145        pt(self.x - other.x, self.y - other.y).mag2()
146    }
147
148    fn dot(self, other: Self) -> f32 {
149        self.x * other.x + self.y * other.y
150    }
151}
152
153/// Spherical coordinates of a `Point3`.
154#[derive(Clone, Copy, Debug)]
155pub struct Spherical {
156    pub phi: f32,
157    pub theta: f32,
158    pub radius: f32,
159}
160
161impl Spherical {
162    pub fn new(phi: f32, theta: f32, radius: f32) -> Self {
163        Self { phi, theta, radius }
164    }
165}
166
167/// A point in 3D space.
168#[derive(Clone, Copy, Debug)]
169pub struct Point3 {
170    pub x: f32,
171    pub y: f32,
172    pub z: f32,
173}
174
175impl Algebra for Point3 {
176    const ZERO: Self = Point3 {
177        x: 0.0,
178        y: 0.0,
179        z: 0.0,
180    };
181
182    fn scale(self, k: f32) -> Self {
183        self * k
184    }
185
186    fn lerp(self, other: Self, t: f32) -> Self {
187        let x = self.x * (1.0 - t) + t * other.x;
188        let y = self.y * (1.0 - t) + t * other.y;
189        let z = self.z * (1.0 - t) + t * other.z;
190        Point3 { x, y, z }
191    }
192
193    fn mag2(self) -> f32 {
194        self.dot(self)
195    }
196
197    fn dist2(self, other: Self) -> f32 {
198        pt3(self.x - other.x, self.y - other.y, self.z - other.z).mag2()
199    }
200
201    fn dot(self, other: Self) -> f32 {
202        self.x * other.x + self.y * other.y + self.z * other.z
203    }
204}
205
206impl Point3 {
207    pub fn new(x: f32, y: f32, z: f32) -> Self {
208        Self { x, y, z }
209    }
210
211    /// Conert from cartesian to Spherical coordinates.
212    pub fn to_spherical(&self, center: Point3) -> Spherical {
213        let x = self.x - center.x;
214        let y = self.y - center.y;
215        let z = self.z - center.z;
216        let radius = (x * x + y * y + z * z).sqrt();
217        let theta = (z / radius).acos();
218        let phi = y.atan2(x);
219        Spherical { phi, theta, radius }
220    }
221
222    /// Rotate around the x-axis.
223    pub fn rotate_x(&self, theta: f32) -> Self {
224        let x = self.x;
225        let y = self.y * theta.cos() - self.z * theta.sin();
226        let z = self.y * theta.sin() + self.z * theta.cos();
227        Self { x, y, z }
228    }
229
230    /// Rotate around the y-axis.
231    pub fn rotate_y(&self, theta: f32) -> Self {
232        let x = self.x * theta.cos() + self.z * theta.sin();
233        let y = self.y;
234        let z = -self.x * theta.sin() + self.z * theta.cos();
235        Self { x, y, z }
236    }
237
238    /// Rotate around the z-axis.
239    pub fn rotate_z(&self, theta: f32) -> Self {
240        let x = self.x * theta.cos() - self.y * theta.sin();
241        let y = self.x * theta.sin() + self.y * theta.cos();
242        let z = self.z;
243        Self { x, y, z }
244    }
245}
246
247impl Sub for Point3 {
248    type Output = Point3;
249
250    fn sub(self, rhs: Self) -> Self::Output {
251        Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
252    }
253}
254
255impl SubAssign for Point3 {
256    fn sub_assign(&mut self, rhs: Self) {
257        *self = *self - rhs;
258    }
259}
260
261impl Add for Point3 {
262    type Output = Point3;
263
264    fn add(self, rhs: Self) -> Self::Output {
265        Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
266    }
267}
268
269impl AddAssign for Point3 {
270    fn add_assign(&mut self, rhs: Self) {
271        *self = *self + rhs;
272    }
273}
274
275impl Mul<f32> for Point3 {
276    type Output = Point3;
277
278    fn mul(self, rhs: f32) -> Self::Output {
279        Point3::new(self.x * rhs, self.y * rhs, self.z * rhs)
280    }
281}
282
283impl MulAssign<f32> for Point3 {
284    fn mul_assign(&mut self, rhs: f32) {
285        *self = *self * rhs;
286    }
287}
288
289impl Div<f32> for Point3 {
290    type Output = Point3;
291
292    fn div(self, rhs: f32) -> Self::Output {
293        Point3::new(self.x / rhs, self.y / rhs, self.z / rhs)
294    }
295}
296
297impl DivAssign<f32> for Point3 {
298    fn div_assign(&mut self, rhs: f32) {
299        *self = *self / rhs;
300    }
301}
302
303impl Mul<Point3> for f32 {
304    type Output = Point3;
305
306    fn mul(self, rhs: Point3) -> Self::Output {
307        rhs * self
308    }
309}
310
311impl Div<Point3> for f32 {
312    type Output = Point3;
313
314    fn div(self, rhs: Point3) -> Self::Output {
315        rhs / self
316    }
317}