ressa_path/
lib.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7//! A [tiny-skia](https://github.com/RazrFalcon/tiny-skia) Bezier path implementation.
8//!
9//! Provides a memory-efficient Bezier path container, path builder, path stroker and path dasher.
10//!
11//! Also provides some basic geometry types, but they will be moved to an external crate eventually.
12//!
13//! Note that all types use single precision floats (`f32`), just like [Skia](https://skia.org/).
14
15#![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
60/// An integer length that is guarantee to be > 0
61type LengthU32 = core::num::NonZeroU32;
62
63/// A point.
64///
65/// Doesn't guarantee to be finite.
66#[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    /// Creates a new `Point`.
83    pub fn from_xy(x: f32, y: f32) -> Self {
84        Point { x, y }
85    }
86
87    /// Creates a new `Point` from `f32x2`.
88    pub fn from_f32x2(r: f32x2) -> Self {
89        Point::from_xy(r.x(), r.y())
90    }
91
92    /// Converts a `Point` into a `f32x2`.
93    pub fn to_f32x2(&self) -> f32x2 {
94        f32x2::new(self.x, self.y)
95    }
96
97    /// Creates a point at 0x0 position.
98    pub fn zero() -> Self {
99        Point { x: 0.0, y: 0.0 }
100    }
101
102    /// Returns true if x and y are both zero.
103    pub fn is_zero(&self) -> bool {
104        self.x == 0.0 && self.y == 0.0
105    }
106
107    /// Returns true if both x and y are measurable values.
108    ///
109    /// Both values are other than infinities and NaN.
110    pub fn is_finite(&self) -> bool {
111        (self.x * self.y).is_finite()
112    }
113
114    /// Checks that two `Point`s are almost equal.
115    pub(crate) fn almost_equal(&self, other: Point) -> bool {
116        !(*self - other).can_normalize()
117    }
118
119    /// Checks that two `Point`s are almost equal using the specified tolerance.
120    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    /// Scales (fX, fY) so that length() returns one, while preserving ratio of fX to fY,
126    /// if possible.
127    ///
128    /// If prior length is nearly zero, sets vector to (0, 0) and returns
129    /// false; otherwise returns true.
130    pub fn normalize(&mut self) -> bool {
131        self.set_length_from(self.x, self.y, 1.0)
132    }
133
134    /// Sets vector to (x, y) scaled so length() returns one, and so that (x, y)
135    /// is proportional to (x, y).
136    ///
137    /// If (x, y) length is nearly zero, sets vector to (0, 0) and returns false;
138    /// otherwise returns true.
139    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    /// Returns the Euclidean distance from origin.
148    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    /// Scales vector so that distanceToOrigin() returns length, if possible.
160    ///
161    /// If former length is nearly zero, sets vector to (0, 0) and return false;
162    /// otherwise returns true.
163    pub fn set_length(&mut self, length: f32) -> bool {
164        self.set_length_from(self.x, self.y, length)
165    }
166
167    /// Sets vector to (x, y) scaled to length, if possible.
168    ///
169    /// If former length is nearly zero, sets vector to (0, 0) and return false;
170    /// otherwise returns true.
171    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    /// Returns the Euclidean distance from origin.
176    pub fn distance(&self, other: Point) -> f32 {
177        (*self - other).length()
178    }
179
180    /// Returns the dot product of two points.
181    pub fn dot(&self, other: Point) -> f32 {
182        self.x * other.x + self.y * other.y
183    }
184
185    /// Returns the cross product of vector and vec.
186    ///
187    /// Vector and vec form three-dimensional vectors with z-axis value equal to zero.
188    /// The cross product is a three-dimensional vector with x-axis and y-axis values
189    /// equal to zero. The cross product z-axis component is returned.
190    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    /// Scales Point in-place by scale.
205    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
229// We have to worry about 2 tricky conditions:
230// 1. underflow of mag2 (compared against nearlyzero^2)
231// 2. overflow of mag2 (compared w/ isfinite)
232//
233// If we underflow, we return false. If we overflow, we compute again using
234// doubles, which is much slower (3x in a desktop test) but will not overflow.
235fn 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    // our mag2 step overflowed to infinity, so use doubles instead.
243    // much slower, but needed when x or y are very large, other wise we
244    // divide by inf. and return (0,0) vector.
245    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    // check if we're not finite, or we're zero-length
253    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/// An integer size.
329///
330/// # Guarantees
331///
332/// - Width and height are positive and non-zero.
333#[derive(Copy, Clone, PartialEq, Debug)]
334pub struct IntSize {
335    width: LengthU32,
336    height: LengthU32,
337}
338
339impl IntSize {
340    /// Creates a new `IntSize` from width and height.
341    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    /// Returns width.
349    pub fn width(&self) -> u32 {
350        self.width.get()
351    }
352
353    /// Returns height.
354    pub fn height(&self) -> u32 {
355        self.height.get()
356    }
357
358    /// Converts the current size into a `IntRect` at a provided position.
359    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    /// Converts the current size into a `IntRect` at a provided position.
364    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}