1#![no_std]
16#![warn(missing_docs)]
17#![warn(missing_copy_implementations)]
18#![warn(missing_debug_implementations)]
19#![allow(clippy::approx_constant)]
20#![allow(clippy::collapsible_if)]
21#![allow(clippy::eq_op)]
22#![allow(clippy::excessive_precision)]
23#![allow(clippy::identity_op)]
24#![allow(clippy::manual_range_contains)]
25#![allow(clippy::neg_cmp_op_on_partial_ord)]
26#![allow(clippy::too_many_arguments)]
27#![allow(clippy::upper_case_acronyms)]
28#![allow(clippy::wrong_self_convention)]
29
30#[cfg(not(any(feature = "std", feature = "no-std-float")))]
31compile_error!("You have to activate either the `std` or the `no-std-float` feature.");
32
33#[cfg(feature = "std")]
34extern crate std;
35
36extern crate alloc;
37
38mod dash;
39mod f32x2_t;
40mod f32x4_t;
41mod floating_point;
42mod path;
43mod path_builder;
44pub mod path_geometry;
45mod rect;
46mod scalar;
47mod stroker;
48mod transform;
49
50pub use dash::StrokeDash;
51pub use f32x2_t::f32x2;
52pub use floating_point::*;
53pub use path::*;
54pub use path_builder::*;
55pub use rect::*;
56pub use scalar::*;
57pub use stroker::*;
58pub use transform::*;
59
60type LengthU32 = core::num::NonZeroU32;
62
63#[allow(missing_docs)]
67#[repr(C)]
68#[derive(Copy, Clone, PartialEq, Default, Debug)]
69pub struct Point {
70 pub x: f32,
71 pub y: f32,
72}
73
74impl From<(f32, f32)> for Point {
75 #[inline]
76 fn from(v: (f32, f32)) -> Self {
77 Point { x: v.0, y: v.1 }
78 }
79}
80
81impl Point {
82 pub fn from_xy(x: f32, y: f32) -> Self {
84 Point { x, y }
85 }
86
87 pub fn from_f32x2(r: f32x2) -> Self {
89 Point::from_xy(r.x(), r.y())
90 }
91
92 pub fn to_f32x2(&self) -> f32x2 {
94 f32x2::new(self.x, self.y)
95 }
96
97 pub fn zero() -> Self {
99 Point { x: 0.0, y: 0.0 }
100 }
101
102 pub fn is_zero(&self) -> bool {
104 self.x == 0.0 && self.y == 0.0
105 }
106
107 pub fn is_finite(&self) -> bool {
111 (self.x * self.y).is_finite()
112 }
113
114 pub(crate) fn almost_equal(&self, other: Point) -> bool {
116 !(*self - other).can_normalize()
117 }
118
119 pub(crate) fn equals_within_tolerance(&self, other: Point, tolerance: f32) -> bool {
121 (self.x - other.x).is_nearly_zero_within_tolerance(tolerance)
122 && (self.y - other.y).is_nearly_zero_within_tolerance(tolerance)
123 }
124
125 pub fn normalize(&mut self) -> bool {
131 self.set_length_from(self.x, self.y, 1.0)
132 }
133
134 pub fn set_normalize(&mut self, x: f32, y: f32) -> bool {
140 self.set_length_from(x, y, 1.0)
141 }
142
143 pub(crate) fn can_normalize(&self) -> bool {
144 self.x.is_finite() && self.y.is_finite() && (self.x != 0.0 || self.y != 0.0)
145 }
146
147 pub fn length(&self) -> f32 {
149 let mag2 = self.x * self.x + self.y * self.y;
150 if mag2.is_finite() {
151 mag2.sqrt()
152 } else {
153 let xx = f64::from(self.x);
154 let yy = f64::from(self.y);
155 (xx * xx + yy * yy).sqrt() as f32
156 }
157 }
158
159 pub fn set_length(&mut self, length: f32) -> bool {
164 self.set_length_from(self.x, self.y, length)
165 }
166
167 pub fn set_length_from(&mut self, x: f32, y: f32, length: f32) -> bool {
172 set_point_length(self, x, y, length, &mut None)
173 }
174
175 pub fn distance(&self, other: Point) -> f32 {
177 (*self - other).length()
178 }
179
180 pub fn dot(&self, other: Point) -> f32 {
182 self.x * other.x + self.y * other.y
183 }
184
185 pub fn cross(&self, other: Point) -> f32 {
191 self.x * other.y - self.y * other.x
192 }
193
194 pub(crate) fn distance_to_sqd(&self, pt: Point) -> f32 {
195 let dx = self.x - pt.x;
196 let dy = self.y - pt.y;
197 dx * dx + dy * dy
198 }
199
200 pub(crate) fn length_sqd(&self) -> f32 {
201 self.dot(*self)
202 }
203
204 pub fn scale(&mut self, scale: f32) {
206 self.x *= scale;
207 self.y *= scale;
208 }
209
210 pub(crate) fn scaled(&self, scale: f32) -> Self {
211 Point::from_xy(self.x * scale, self.y * scale)
212 }
213
214 pub(crate) fn swap_coords(&mut self) {
215 core::mem::swap(&mut self.x, &mut self.y);
216 }
217
218 pub(crate) fn rotate_cw(&mut self) {
219 self.swap_coords();
220 self.x = -self.x;
221 }
222
223 pub(crate) fn rotate_ccw(&mut self) {
224 self.swap_coords();
225 self.y = -self.y;
226 }
227}
228
229fn set_point_length(
236 pt: &mut Point,
237 mut x: f32,
238 mut y: f32,
239 length: f32,
240 orig_length: &mut Option<f32>,
241) -> bool {
242 let xx = x as f64;
246 let yy = y as f64;
247 let dmag = (xx * xx + yy * yy).sqrt();
248 let dscale = length as f64 / dmag;
249 x *= dscale as f32;
250 y *= dscale as f32;
251
252 if !x.is_finite() || !y.is_finite() || (x == 0.0 && y == 0.0) {
254 *pt = Point::zero();
255 return false;
256 }
257
258 let mut mag = 0.0;
259 if orig_length.is_some() {
260 mag = dmag as f32;
261 }
262
263 *pt = Point::from_xy(x, y);
264
265 if orig_length.is_some() {
266 *orig_length = Some(mag);
267 }
268
269 true
270}
271
272impl core::ops::Neg for Point {
273 type Output = Point;
274
275 fn neg(self) -> Self::Output {
276 Point {
277 x: -self.x,
278 y: -self.y,
279 }
280 }
281}
282
283impl core::ops::Add for Point {
284 type Output = Point;
285
286 fn add(self, other: Point) -> Self::Output {
287 Point::from_xy(self.x + other.x, self.y + other.y)
288 }
289}
290
291impl core::ops::AddAssign for Point {
292 fn add_assign(&mut self, other: Point) {
293 self.x += other.x;
294 self.y += other.y;
295 }
296}
297
298impl core::ops::Sub for Point {
299 type Output = Point;
300
301 fn sub(self, other: Point) -> Self::Output {
302 Point::from_xy(self.x - other.x, self.y - other.y)
303 }
304}
305
306impl core::ops::SubAssign for Point {
307 fn sub_assign(&mut self, other: Point) {
308 self.x -= other.x;
309 self.y -= other.y;
310 }
311}
312
313impl core::ops::Mul for Point {
314 type Output = Point;
315
316 fn mul(self, other: Point) -> Self::Output {
317 Point::from_xy(self.x * other.x, self.y * other.y)
318 }
319}
320
321impl core::ops::MulAssign for Point {
322 fn mul_assign(&mut self, other: Point) {
323 self.x *= other.x;
324 self.y *= other.y;
325 }
326}
327
328#[derive(Copy, Clone, PartialEq, Debug)]
334pub struct IntSize {
335 width: LengthU32,
336 height: LengthU32,
337}
338
339impl IntSize {
340 pub fn from_wh(width: u32, height: u32) -> Option<Self> {
342 Some(IntSize {
343 width: LengthU32::new(width)?,
344 height: LengthU32::new(height)?,
345 })
346 }
347
348 pub fn width(&self) -> u32 {
350 self.width.get()
351 }
352
353 pub fn height(&self) -> u32 {
355 self.height.get()
356 }
357
358 pub fn to_int_rect(&self, x: i32, y: i32) -> IntRect {
360 IntRect::from_xywh(x, y, self.width.get(), self.height.get()).unwrap()
361 }
362
363 pub fn to_screen_int_rect(&self, x: u32, y: u32) -> ScreenIntRect {
365 ScreenIntRect::from_xywh_safe(x, y, self.width, self.height)
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn int_size_tests() {
375 assert_eq!(IntSize::from_wh(0, 0), None);
376 assert_eq!(IntSize::from_wh(1, 0), None);
377 assert_eq!(IntSize::from_wh(0, 1), None);
378
379 let size = IntSize::from_wh(3, 4).unwrap();
380 assert_eq!(
381 size.to_int_rect(1, 2),
382 IntRect::from_xywh(1, 2, 3, 4).unwrap()
383 );
384 assert_eq!(
385 size.to_screen_int_rect(1, 2),
386 ScreenIntRect::from_xywh(1, 2, 3, 4).unwrap()
387 );
388 }
389}