1 | #![doc (html_logo_url = "https://nical.github.io/lyon-doc/lyon-logo.svg" )] |
2 | #![deny (bare_trait_objects)] |
3 | #![deny (unconditional_recursion)] |
4 | #![allow (clippy::excessive_precision)] |
5 | #![allow (clippy::let_and_return)] |
6 | #![allow (clippy::many_single_char_names)] |
7 | #![no_std ] |
8 | |
9 | //! Simple 2D geometric primitives on top of euclid. |
10 | //! |
11 | //! This crate is reexported in [lyon](https://docs.rs/lyon/). |
12 | //! |
13 | //! # Overview. |
14 | //! |
15 | //! This crate implements some of the maths to work with: |
16 | //! |
17 | //! - lines and line segments, |
18 | //! - quadratic and cubic bézier curves, |
19 | //! - elliptic arcs, |
20 | //! - triangles. |
21 | //! |
22 | //! # Flattening |
23 | //! |
24 | //! Flattening is the action of approximating a curve with a succession of line segments. |
25 | //! |
26 | //! <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 30" height="30mm" width="120mm"> |
27 | //! <path d="M26.7 24.94l.82-11.15M44.46 5.1L33.8 7.34" fill="none" stroke="#55d400" stroke-width=".5"/> |
28 | //! <path d="M26.7 24.94c.97-11.13 7.17-17.6 17.76-19.84M75.27 24.94l1.13-5.5 2.67-5.48 4-4.42L88 6.7l5.02-1.6" fill="none" stroke="#000"/> |
29 | //! <path d="M77.57 19.37a1.1 1.1 0 0 1-1.08 1.08 1.1 1.1 0 0 1-1.1-1.08 1.1 1.1 0 0 1 1.08-1.1 1.1 1.1 0 0 1 1.1 1.1" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
30 | //! <path d="M77.57 19.37a1.1 1.1 0 0 1-1.08 1.08 1.1 1.1 0 0 1-1.1-1.08 1.1 1.1 0 0 1 1.08-1.1 1.1 1.1 0 0 1 1.1 1.1" color="#000" fill="#fff"/> |
31 | //! <path d="M80.22 13.93a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.08 1.1 1.1 0 0 1 1.08 1.08" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
32 | //! <path d="M80.22 13.93a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.08 1.1 1.1 0 0 1 1.08 1.08" color="#000" fill="#fff"/> |
33 | //! <path d="M84.08 9.55a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.1-1.1 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
34 | //! <path d="M84.08 9.55a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.1-1.1 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="#fff"/> |
35 | //! <path d="M89.1 6.66a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.08-1.08 1.1 1.1 0 0 1 1.1 1.08" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
36 | //! <path d="M89.1 6.66a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.08-1.08 1.1 1.1 0 0 1 1.1 1.08" color="#000" fill="#fff"/> |
37 | //! <path d="M94.4 5a1.1 1.1 0 0 1-1.1 1.1A1.1 1.1 0 0 1 92.23 5a1.1 1.1 0 0 1 1.08-1.08A1.1 1.1 0 0 1 94.4 5" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
38 | //! <path d="M94.4 5a1.1 1.1 0 0 1-1.1 1.1A1.1 1.1 0 0 1 92.23 5a1.1 1.1 0 0 1 1.08-1.08A1.1 1.1 0 0 1 94.4 5" color="#000" fill="#fff"/> |
39 | //! <path d="M76.44 25.13a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
40 | //! <path d="M76.44 25.13a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="#fff"/> |
41 | //! <path d="M27.78 24.9a1.1 1.1 0 0 1-1.08 1.08 1.1 1.1 0 0 1-1.1-1.08 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
42 | //! <path d="M27.78 24.9a1.1 1.1 0 0 1-1.08 1.08 1.1 1.1 0 0 1-1.1-1.08 1.1 1.1 0 0 1 1.1-1.1 1.1 1.1 0 0 1 1.08 1.1" color="#000" fill="#fff"/> |
43 | //! <path d="M45.4 5.14a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.1-1.1 1.1 1.1 0 0 1 1.1-1.08 1.1 1.1 0 0 1 1.1 1.08" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
44 | //! <path d="M45.4 5.14a1.1 1.1 0 0 1-1.08 1.1 1.1 1.1 0 0 1-1.1-1.1 1.1 1.1 0 0 1 1.1-1.08 1.1 1.1 0 0 1 1.1 1.08" color="#000" fill="#fff"/> |
45 | //! <path d="M28.67 13.8a1.1 1.1 0 0 1-1.1 1.08 1.1 1.1 0 0 1-1.08-1.08 1.1 1.1 0 0 1 1.08-1.1 1.1 1.1 0 0 1 1.1 1.1" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
46 | //! <path d="M28.67 13.8a1.1 1.1 0 0 1-1.1 1.08 1.1 1.1 0 0 1-1.08-1.08 1.1 1.1 0 0 1 1.08-1.1 1.1 1.1 0 0 1 1.1 1.1" color="#000" fill="#fff"/> |
47 | //! <path d="M35 7.32a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.1A1.1 1.1 0 0 1 35 7.33" color="#000" fill="none" stroke="#030303" stroke-linecap="round" stroke-opacity=".5"/> |
48 | //! <path d="M35 7.32a1.1 1.1 0 0 1-1.1 1.1 1.1 1.1 0 0 1-1.08-1.1 1.1 1.1 0 0 1 1.1-1.1A1.1 1.1 0 0 1 35 7.33" color="#000" fill="#fff"/> |
49 | //! <text style="line-height:6.61458302px" x="35.74" y="284.49" font-size="5.29" font-family="Sans" letter-spacing="0" word-spacing="0" fill="#b3b3b3" stroke-width=".26" transform="translate(19.595 -267)"> |
50 | //! <tspan x="35.74" y="284.49" font-size="10.58">→</tspan> |
51 | //! </text> |
52 | //! </svg> |
53 | //! |
54 | //! The tolerance threshold taken as input by the flattening algorithms corresponds |
55 | //! to the maximum distance between the curve and its linear approximation. |
56 | //! The smaller the tolerance is, the more precise the approximation and the more segments |
57 | //! are generated. This value is typically chosen in function of the zoom level. |
58 | //! |
59 | //! <svg viewBox="0 0 47.5 13.2" height="100" width="350" xmlns="http://www.w3.org/2000/svg"> |
60 | //! <path d="M-2.44 9.53c16.27-8.5 39.68-7.93 52.13 1.9" fill="none" stroke="#dde9af" stroke-width="4.6"/> |
61 | //! <path d="M-1.97 9.3C14.28 1.03 37.36 1.7 49.7 11.4" fill="none" stroke="#00d400" stroke-width=".57" stroke-linecap="round" stroke-dasharray="4.6, 2.291434"/> |
62 | //! <path d="M-1.94 10.46L6.2 6.08l28.32-1.4 15.17 6.74" fill="none" stroke="#000" stroke-width=".6"/> |
63 | //! <path d="M6.83 6.57a.9.9 0 0 1-1.25.15.9.9 0 0 1-.15-1.25.9.9 0 0 1 1.25-.15.9.9 0 0 1 .15 1.25" color="#000" stroke="#000" stroke-width=".57" stroke-linecap="round" stroke-opacity=".5"/> |
64 | //! <path d="M35.35 5.3a.9.9 0 0 1-1.25.15.9.9 0 0 1-.15-1.25.9.9 0 0 1 1.25-.15.9.9 0 0 1 .15 1.24" color="#000" stroke="#000" stroke-width=".6" stroke-opacity=".5"/> |
65 | //! <g fill="none" stroke="#ff7f2a" stroke-width=".26"> |
66 | //! <path d="M20.4 3.8l.1 1.83M19.9 4.28l.48-.56.57.52M21.02 5.18l-.5.56-.6-.53" stroke-width=".2978872"/> |
67 | //! </g> |
68 | //! </svg> |
69 | //! |
70 | //! The figure above shows a close up on a curve (the dotted line) and its linear |
71 | //! approximation (the black segments). The tolerance threshold is represented by |
72 | //! the light green area and the orange arrow. |
73 | //! |
74 | |
75 | //#![allow(needless_return)] // clippy |
76 | |
77 | #[cfg (any(test, feature = "std" ))] |
78 | extern crate std; |
79 | |
80 | // Reexport dependencies. |
81 | pub use arrayvec; |
82 | pub use euclid; |
83 | |
84 | #[cfg (feature = "serialization" )] |
85 | #[macro_use ] |
86 | pub extern crate serde; |
87 | |
88 | #[macro_use ] |
89 | mod segment; |
90 | pub mod arc; |
91 | pub mod cubic_bezier; |
92 | mod cubic_bezier_intersections; |
93 | mod line; |
94 | pub mod quadratic_bezier; |
95 | mod triangle; |
96 | pub mod utils; |
97 | |
98 | #[doc (inline)] |
99 | pub use crate::arc::{Arc, ArcFlags, SvgArc}; |
100 | #[doc (inline)] |
101 | pub use crate::cubic_bezier::CubicBezierSegment; |
102 | #[doc (inline)] |
103 | pub use crate::line::{Line, LineEquation, LineSegment}; |
104 | #[doc (inline)] |
105 | pub use crate::quadratic_bezier::QuadraticBezierSegment; |
106 | #[doc (inline)] |
107 | pub use crate::segment::Segment; |
108 | #[doc (inline)] |
109 | pub use crate::triangle::Triangle; |
110 | |
111 | pub use crate::scalar::Scalar; |
112 | |
113 | mod scalar { |
114 | pub(crate) use euclid::Trig; |
115 | pub(crate) use num_traits::cast::cast; |
116 | pub(crate) use num_traits::{Float, FloatConst, NumCast}; |
117 | |
118 | use core::fmt::{Debug, Display}; |
119 | use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign}; |
120 | |
121 | pub trait Scalar: |
122 | Float |
123 | + NumCast |
124 | + FloatConst |
125 | + Sized |
126 | + Display |
127 | + Debug |
128 | + Trig |
129 | + AddAssign |
130 | + SubAssign |
131 | + MulAssign |
132 | + DivAssign |
133 | { |
134 | const HALF: Self; |
135 | const ZERO: Self; |
136 | const ONE: Self; |
137 | const TWO: Self; |
138 | const THREE: Self; |
139 | const FOUR: Self; |
140 | const FIVE: Self; |
141 | const SIX: Self; |
142 | const SEVEN: Self; |
143 | const EIGHT: Self; |
144 | const NINE: Self; |
145 | const TEN: Self; |
146 | |
147 | const MIN: Self; |
148 | const MAX: Self; |
149 | |
150 | const EPSILON: Self; |
151 | const DIV_EPSILON: Self = Self::EPSILON; |
152 | |
153 | /// Epsilon constants are usually not a good way to deal with float precision. |
154 | /// Float precision depends on the magnitude of the values and so should appropriate |
155 | /// epsilons. |
156 | fn epsilon_for(_reference: Self) -> Self { |
157 | Self::EPSILON |
158 | } |
159 | |
160 | fn value(v: f32) -> Self; |
161 | } |
162 | |
163 | impl Scalar for f32 { |
164 | const HALF: Self = 0.5; |
165 | const ZERO: Self = 0.0; |
166 | const ONE: Self = 1.0; |
167 | const TWO: Self = 2.0; |
168 | const THREE: Self = 3.0; |
169 | const FOUR: Self = 4.0; |
170 | const FIVE: Self = 5.0; |
171 | const SIX: Self = 6.0; |
172 | const SEVEN: Self = 7.0; |
173 | const EIGHT: Self = 8.0; |
174 | const NINE: Self = 9.0; |
175 | const TEN: Self = 10.0; |
176 | |
177 | const MIN: Self = f32::MIN; |
178 | const MAX: Self = f32::MAX; |
179 | |
180 | const EPSILON: Self = 1e-4; |
181 | |
182 | fn epsilon_for(reference: Self) -> Self { |
183 | // The thresholds are chosen by looking at the table at |
184 | // https://blog.demofox.org/2017/11/21/floating-point-precision/ plus a bit |
185 | // of trial and error. They might change in the future. |
186 | // TODO: instead of casting to an integer, could look at the exponent directly. |
187 | let magnitude = reference.abs() as i32; |
188 | match magnitude { |
189 | 0..=7 => 1e-5, |
190 | 8..=1023 => 1e-3, |
191 | 1024..=4095 => 1e-2, |
192 | 5096..=65535 => 1e-1, |
193 | 65536..=8_388_607 => 0.5, |
194 | _ => 1.0, |
195 | } |
196 | } |
197 | |
198 | #[inline ] |
199 | fn value(v: f32) -> Self { |
200 | v |
201 | } |
202 | } |
203 | |
204 | // Epsilon constants are usually not a good way to deal with float precision. |
205 | // Float precision depends on the magnitude of the values and so should appropriate |
206 | // epsilons. This function addresses this somewhat empirically. |
207 | impl Scalar for f64 { |
208 | const HALF: Self = 0.5; |
209 | const ZERO: Self = 0.0; |
210 | const ONE: Self = 1.0; |
211 | const TWO: Self = 2.0; |
212 | const THREE: Self = 3.0; |
213 | const FOUR: Self = 4.0; |
214 | const FIVE: Self = 5.0; |
215 | const SIX: Self = 6.0; |
216 | const SEVEN: Self = 7.0; |
217 | const EIGHT: Self = 8.0; |
218 | const NINE: Self = 9.0; |
219 | const TEN: Self = 10.0; |
220 | |
221 | const MIN: Self = f64::MIN; |
222 | const MAX: Self = f64::MAX; |
223 | |
224 | const EPSILON: Self = 1e-8; |
225 | |
226 | fn epsilon_for(reference: Self) -> Self { |
227 | let magnitude = reference.abs() as i64; |
228 | match magnitude { |
229 | 0..=65_535 => 1e-8, |
230 | 65_536..=8_388_607 => 1e-5, |
231 | 8_388_608..=4_294_967_295 => 1e-3, |
232 | _ => 1e-1, |
233 | } |
234 | } |
235 | |
236 | #[inline ] |
237 | fn value(v: f32) -> Self { |
238 | v as f64 |
239 | } |
240 | } |
241 | } |
242 | |
243 | /// Alias for `euclid::default::Point2D`. |
244 | pub use euclid::default::Point2D as Point; |
245 | |
246 | /// Alias for `euclid::default::Vector2D`. |
247 | pub use euclid::default::Vector2D as Vector; |
248 | |
249 | /// Alias for `euclid::default::Size2D`. |
250 | pub use euclid::default::Size2D as Size; |
251 | |
252 | /// Alias for `euclid::default::Box2D` |
253 | pub use euclid::default::Box2D; |
254 | |
255 | /// Alias for `euclid::default::Transform2D` |
256 | pub type Transform<S> = euclid::default::Transform2D<S>; |
257 | |
258 | /// Alias for `euclid::default::Rotation2D` |
259 | pub type Rotation<S> = euclid::default::Rotation2D<S>; |
260 | |
261 | /// Alias for `euclid::default::Translation2D` |
262 | pub type Translation<S> = euclid::Translation2D<S, euclid::UnknownUnit, euclid::UnknownUnit>; |
263 | |
264 | /// Alias for `euclid::default::Scale` |
265 | pub use euclid::default::Scale; |
266 | |
267 | /// An angle in radians. |
268 | pub use euclid::Angle; |
269 | |
270 | /// Shorthand for `Vector::new(x, y)`. |
271 | #[inline ] |
272 | pub fn vector<S>(x: S, y: S) -> Vector<S> { |
273 | Vector::new(x, y) |
274 | } |
275 | |
276 | /// Shorthand for `Point::new(x, y)`. |
277 | #[inline ] |
278 | pub fn point<S>(x: S, y: S) -> Point<S> { |
279 | Point::new(x, y) |
280 | } |
281 | |
282 | /// Shorthand for `Size::new(x, y)`. |
283 | #[inline ] |
284 | pub fn size<S>(w: S, h: S) -> Size<S> { |
285 | Size::new(width:w, height:h) |
286 | } |
287 | |
288 | pub mod traits { |
289 | pub use crate::segment::Segment; |
290 | |
291 | use crate::{Point, Rotation, Scalar, Scale, Transform, Translation, Vector}; |
292 | |
293 | pub trait Transformation<S> { |
294 | fn transform_point(&self, p: Point<S>) -> Point<S>; |
295 | fn transform_vector(&self, v: Vector<S>) -> Vector<S>; |
296 | } |
297 | |
298 | impl<S: Scalar> Transformation<S> for Transform<S> { |
299 | fn transform_point(&self, p: Point<S>) -> Point<S> { |
300 | self.transform_point(p) |
301 | } |
302 | |
303 | fn transform_vector(&self, v: Vector<S>) -> Vector<S> { |
304 | self.transform_vector(v) |
305 | } |
306 | } |
307 | |
308 | impl<S: Scalar> Transformation<S> for Rotation<S> { |
309 | fn transform_point(&self, p: Point<S>) -> Point<S> { |
310 | self.transform_point(p) |
311 | } |
312 | |
313 | fn transform_vector(&self, v: Vector<S>) -> Vector<S> { |
314 | self.transform_vector(v) |
315 | } |
316 | } |
317 | |
318 | impl<S: Scalar> Transformation<S> for Translation<S> { |
319 | fn transform_point(&self, p: Point<S>) -> Point<S> { |
320 | self.transform_point(p) |
321 | } |
322 | |
323 | fn transform_vector(&self, v: Vector<S>) -> Vector<S> { |
324 | v |
325 | } |
326 | } |
327 | |
328 | impl<S: Scalar> Transformation<S> for Scale<S> { |
329 | fn transform_point(&self, p: Point<S>) -> Point<S> { |
330 | (*self).transform_point(p) |
331 | } |
332 | |
333 | fn transform_vector(&self, v: Vector<S>) -> Vector<S> { |
334 | (*self).transform_vector(v) |
335 | } |
336 | } |
337 | |
338 | // Automatically implement Transformation for all &Transformation. |
339 | impl<'l, S: Scalar, T: Transformation<S>> Transformation<S> for &'l T { |
340 | #[inline ] |
341 | fn transform_point(&self, p: Point<S>) -> Point<S> { |
342 | (*self).transform_point(p) |
343 | } |
344 | |
345 | #[inline ] |
346 | fn transform_vector(&self, v: Vector<S>) -> Vector<S> { |
347 | (*self).transform_vector(v) |
348 | } |
349 | } |
350 | } |
351 | |