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 size;
48mod stroker;
49mod transform;
50
51pub use dash::StrokeDash;
52pub use f32x2_t::f32x2;
53pub use floating_point::*;
54pub use path::*;
55pub use path_builder::*;
56pub use rect::*;
57pub use scalar::*;
58pub use size::*;
59pub use stroker::*;
60pub use transform::*;
61
62/// An integer length that is guarantee to be > 0
63type LengthU32 = core::num::NonZeroU32;
64
65/// A point.
66///
67/// Doesn't guarantee to be finite.
68#[allow(missing_docs)]
69#[repr(C)]
70#[derive(Copy, Clone, PartialEq, Default, Debug)]
71pub struct Point {
72 pub x: f32,
73 pub y: f32,
74}
75
76impl From<(f32, f32)> for Point {
77 #[inline]
78 fn from(v: (f32, f32)) -> Self {
79 Point { x: v.0, y: v.1 }
80 }
81}
82
83impl Point {
84 /// Creates a new `Point`.
85 pub fn from_xy(x: f32, y: f32) -> Self {
86 Point { x, y }
87 }
88
89 /// Creates a new `Point` from `f32x2`.
90 pub fn from_f32x2(r: f32x2) -> Self {
91 Point::from_xy(r.x(), r.y())
92 }
93
94 /// Converts a `Point` into a `f32x2`.
95 pub fn to_f32x2(&self) -> f32x2 {
96 f32x2::new(self.x, self.y)
97 }
98
99 /// Creates a point at 0x0 position.
100 pub fn zero() -> Self {
101 Point { x: 0.0, y: 0.0 }
102 }
103
104 /// Returns true if x and y are both zero.
105 pub fn is_zero(&self) -> bool {
106 self.x == 0.0 && self.y == 0.0
107 }
108
109 /// Returns true if both x and y are measurable values.
110 ///
111 /// Both values are other than infinities and NaN.
112 pub fn is_finite(&self) -> bool {
113 (self.x * self.y).is_finite()
114 }
115
116 /// Checks that two `Point`s are almost equal.
117 pub(crate) fn almost_equal(&self, other: Point) -> bool {
118 !(*self - other).can_normalize()
119 }
120
121 /// Checks that two `Point`s are almost equal using the specified tolerance.
122 pub(crate) fn equals_within_tolerance(&self, other: Point, tolerance: f32) -> bool {
123 (self.x - other.x).is_nearly_zero_within_tolerance(tolerance)
124 && (self.y - other.y).is_nearly_zero_within_tolerance(tolerance)
125 }
126
127 /// Scales (fX, fY) so that length() returns one, while preserving ratio of fX to fY,
128 /// if possible.
129 ///
130 /// If prior length is nearly zero, sets vector to (0, 0) and returns
131 /// false; otherwise returns true.
132 pub fn normalize(&mut self) -> bool {
133 self.set_length_from(self.x, self.y, 1.0)
134 }
135
136 /// Sets vector to (x, y) scaled so length() returns one, and so that (x, y)
137 /// is proportional to (x, y).
138 ///
139 /// If (x, y) length is nearly zero, sets vector to (0, 0) and returns false;
140 /// otherwise returns true.
141 pub fn set_normalize(&mut self, x: f32, y: f32) -> bool {
142 self.set_length_from(x, y, 1.0)
143 }
144
145 pub(crate) fn can_normalize(&self) -> bool {
146 self.x.is_finite() && self.y.is_finite() && (self.x != 0.0 || self.y != 0.0)
147 }
148
149 /// Returns the Euclidean distance from origin.
150 pub fn length(&self) -> f32 {
151 let mag2 = self.x * self.x + self.y * self.y;
152 if mag2.is_finite() {
153 mag2.sqrt()
154 } else {
155 let xx = f64::from(self.x);
156 let yy = f64::from(self.y);
157 (xx * xx + yy * yy).sqrt() as f32
158 }
159 }
160
161 /// Scales vector so that distanceToOrigin() returns length, if possible.
162 ///
163 /// If former length is nearly zero, sets vector to (0, 0) and return false;
164 /// otherwise returns true.
165 pub fn set_length(&mut self, length: f32) -> bool {
166 self.set_length_from(self.x, self.y, length)
167 }
168
169 /// Sets vector to (x, y) scaled to length, if possible.
170 ///
171 /// If former length is nearly zero, sets vector to (0, 0) and return false;
172 /// otherwise returns true.
173 pub fn set_length_from(&mut self, x: f32, y: f32, length: f32) -> bool {
174 set_point_length(self, x, y, length, &mut None)
175 }
176
177 /// Returns the Euclidean distance from origin.
178 pub fn distance(&self, other: Point) -> f32 {
179 (*self - other).length()
180 }
181
182 /// Returns the dot product of two points.
183 pub fn dot(&self, other: Point) -> f32 {
184 self.x * other.x + self.y * other.y
185 }
186
187 /// Returns the cross product of vector and vec.
188 ///
189 /// Vector and vec form three-dimensional vectors with z-axis value equal to zero.
190 /// The cross product is a three-dimensional vector with x-axis and y-axis values
191 /// equal to zero. The cross product z-axis component is returned.
192 pub fn cross(&self, other: Point) -> f32 {
193 self.x * other.y - self.y * other.x
194 }
195
196 pub(crate) fn distance_to_sqd(&self, pt: Point) -> f32 {
197 let dx = self.x - pt.x;
198 let dy = self.y - pt.y;
199 dx * dx + dy * dy
200 }
201
202 pub(crate) fn length_sqd(&self) -> f32 {
203 self.dot(*self)
204 }
205
206 /// Scales Point in-place by scale.
207 pub fn scale(&mut self, scale: f32) {
208 self.x *= scale;
209 self.y *= scale;
210 }
211
212 pub(crate) fn scaled(&self, scale: f32) -> Self {
213 Point::from_xy(self.x * scale, self.y * scale)
214 }
215
216 pub(crate) fn swap_coords(&mut self) {
217 core::mem::swap(&mut self.x, &mut self.y);
218 }
219
220 pub(crate) fn rotate_cw(&mut self) {
221 self.swap_coords();
222 self.x = -self.x;
223 }
224
225 pub(crate) fn rotate_ccw(&mut self) {
226 self.swap_coords();
227 self.y = -self.y;
228 }
229}
230
231// We have to worry about 2 tricky conditions:
232// 1. underflow of mag2 (compared against nearlyzero^2)
233// 2. overflow of mag2 (compared w/ isfinite)
234//
235// If we underflow, we return false. If we overflow, we compute again using
236// doubles, which is much slower (3x in a desktop test) but will not overflow.
237fn set_point_length(
238 pt: &mut Point,
239 mut x: f32,
240 mut y: f32,
241 length: f32,
242 orig_length: &mut Option<f32>,
243) -> bool {
244 // our mag2 step overflowed to infinity, so use doubles instead.
245 // much slower, but needed when x or y are very large, other wise we
246 // divide by inf. and return (0,0) vector.
247 let xx = x as f64;
248 let yy = y as f64;
249 let dmag = (xx * xx + yy * yy).sqrt();
250 let dscale = length as f64 / dmag;
251 x *= dscale as f32;
252 y *= dscale as f32;
253
254 // check if we're not finite, or we're zero-length
255 if !x.is_finite() || !y.is_finite() || (x == 0.0 && y == 0.0) {
256 *pt = Point::zero();
257 return false;
258 }
259
260 let mut mag = 0.0;
261 if orig_length.is_some() {
262 mag = dmag as f32;
263 }
264
265 *pt = Point::from_xy(x, y);
266
267 if orig_length.is_some() {
268 *orig_length = Some(mag);
269 }
270
271 true
272}
273
274impl core::ops::Neg for Point {
275 type Output = Point;
276
277 fn neg(self) -> Self::Output {
278 Point {
279 x: -self.x,
280 y: -self.y,
281 }
282 }
283}
284
285impl core::ops::Add for Point {
286 type Output = Point;
287
288 fn add(self, other: Point) -> Self::Output {
289 Point::from_xy(self.x + other.x, self.y + other.y)
290 }
291}
292
293impl core::ops::AddAssign for Point {
294 fn add_assign(&mut self, other: Point) {
295 self.x += other.x;
296 self.y += other.y;
297 }
298}
299
300impl core::ops::Sub for Point {
301 type Output = Point;
302
303 fn sub(self, other: Point) -> Self::Output {
304 Point::from_xy(self.x - other.x, self.y - other.y)
305 }
306}
307
308impl core::ops::SubAssign for Point {
309 fn sub_assign(&mut self, other: Point) {
310 self.x -= other.x;
311 self.y -= other.y;
312 }
313}
314
315impl core::ops::Mul for Point {
316 type Output = Point;
317
318 fn mul(self, other: Point) -> Self::Output {
319 Point::from_xy(self.x * other.x, self.y * other.y)
320 }
321}
322
323impl core::ops::MulAssign for Point {
324 fn mul_assign(&mut self, other: Point) {
325 self.x *= other.x;
326 self.y *= other.y;
327 }
328}
329