Coverage Report

Created: 2026-05-24 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/kurbo-0.13.1/src/bezpath.rs
Line
Count
Source
1
// Copyright 2018 the Kurbo Authors
2
// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4
//! Bézier paths (up to cubic).
5
6
#![allow(clippy::many_single_char_names)]
7
8
use core::iter::{Extend, FromIterator};
9
use core::mem;
10
use core::ops::{Mul, Range};
11
12
use alloc::vec::Vec;
13
14
use arrayvec::ArrayVec;
15
16
use crate::MAX_EXTREMA;
17
use crate::common::{solve_cubic, solve_quadratic};
18
use crate::{
19
    Affine, CubicBez, Line, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea,
20
    ParamCurveExtrema, ParamCurveNearest, Point, QuadBez, Rect, Shape, TranslateScale, Vec2,
21
};
22
23
#[cfg(not(feature = "std"))]
24
use crate::common::FloatFuncs;
25
26
/// A Bézier path.
27
///
28
/// These docs assume basic familiarity with Bézier curves; for an introduction,
29
/// see Pomax's wonderful [A Primer on Bézier Curves].
30
///
31
/// This path can contain lines, quadratics ([`QuadBez`]) and cubics
32
/// ([`CubicBez`]), and may contain multiple subpaths.
33
///
34
/// # Elements and Segments
35
///
36
/// A Bézier path can be represented in terms of either 'elements' ([`PathEl`])
37
/// or 'segments' ([`PathSeg`]). Elements map closely to how Béziers are
38
/// generally used in PostScript-style drawing APIs; they can be thought of as
39
/// instructions for drawing the path. Segments more directly describe the
40
/// path itself, with each segment being an independent line or curve.
41
///
42
/// These different representations are useful in different contexts.
43
/// For tasks like drawing, elements are a natural fit, but when doing
44
/// hit-testing or subdividing, we need to have access to the segments.
45
///
46
/// Conceptually, a `BezPath` contains zero or more subpaths. Each subpath
47
/// *always* begins with a `MoveTo`, then has zero or more `LineTo`, `QuadTo`,
48
/// and `CurveTo` elements, and optionally ends with a `ClosePath`.
49
///
50
/// Internally, a `BezPath` is a list of [`PathEl`]s; as such it implements
51
/// [`FromIterator<PathEl>`] and [`Extend<PathEl>`]:
52
///
53
/// ```
54
/// use kurbo::{BezPath, Rect, Shape, Vec2};
55
/// let accuracy = 0.1;
56
/// let rect = Rect::from_origin_size((0., 0.,), (10., 10.));
57
/// // these are equivalent
58
/// let path1 = rect.to_path(accuracy);
59
/// let path2: BezPath = rect.path_elements(accuracy).collect();
60
///
61
/// // extend a path with another path:
62
/// let mut path = rect.to_path(accuracy);
63
/// let shifted_rect = rect + Vec2::new(5.0, 10.0);
64
/// path.extend(shifted_rect.to_path(accuracy));
65
/// ```
66
///
67
/// You can iterate the elements of a `BezPath` with the [`iter`] method,
68
/// and the segments with the [`segments`] method:
69
///
70
/// ```
71
/// use kurbo::{BezPath, Line, PathEl, PathSeg, Point, Rect, Shape};
72
/// let accuracy = 0.1;
73
/// let rect = Rect::from_origin_size((0., 0.,), (10., 10.));
74
/// // these are equivalent
75
/// let path = rect.to_path(accuracy);
76
/// let first_el = PathEl::MoveTo(Point::ZERO);
77
/// let first_seg = PathSeg::Line(Line::new((0., 0.), (10., 0.)));
78
/// assert_eq!(path.iter().next(), Some(first_el));
79
/// assert_eq!(path.segments().next(), Some(first_seg));
80
/// ```
81
/// In addition, if you have some other type that implements
82
/// `Iterator<Item=PathEl>`, you can adapt that to an iterator of segments with
83
/// the [`segments` free function].
84
///
85
/// # Advanced functionality
86
///
87
/// In addition to the basic API, there are several useful pieces of advanced
88
/// functionality available on `BezPath`:
89
///
90
/// - [`flatten`] does Bézier flattening, converting a curve to a series of
91
///   line segments
92
/// - [`intersect_line`] computes intersections of a path with a line, useful
93
///   for things like subdividing
94
///
95
/// [A Primer on Bézier Curves]: https://pomax.github.io/bezierinfo/
96
/// [`iter`]: BezPath::iter
97
/// [`segments`]: BezPath::segments
98
/// [`flatten`]: flatten
99
/// [`intersect_line`]: PathSeg::intersect_line
100
/// [`segments` free function]: segments
101
/// [`FromIterator<PathEl>`]: std::iter::FromIterator
102
/// [`Extend<PathEl>`]: std::iter::Extend
103
#[derive(Clone, Default, Debug, PartialEq)]
104
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
105
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106
pub struct BezPath(Vec<PathEl>);
107
108
/// The element of a Bézier path.
109
///
110
/// A valid path has `MoveTo` at the beginning of each subpath.
111
#[derive(Clone, Copy, Debug, PartialEq)]
112
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
113
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114
pub enum PathEl {
115
    /// Move directly to the point without drawing anything, starting a new
116
    /// subpath.
117
    MoveTo(Point),
118
    /// Draw a line from the current location to the point.
119
    LineTo(Point),
120
    /// Draw a quadratic Bézier using the current location and the two points.
121
    ///
122
    /// The first point is the Bézier's control point, the second is its end point.
123
    QuadTo(Point, Point),
124
    /// Draw a cubic Bézier using the current location and the three points.
125
    ///
126
    /// The first two points are the Bézier's control points, the last point is its end point.
127
    CurveTo(Point, Point, Point),
128
    /// Close off the path.
129
    ClosePath,
130
}
131
132
/// A segment of a Bézier path.
133
#[derive(Clone, Copy, Debug, PartialEq)]
134
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
135
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136
pub enum PathSeg {
137
    /// A line segment.
138
    Line(Line),
139
    /// A quadratic Bézier segment.
140
    Quad(QuadBez),
141
    /// A cubic Bézier segment.
142
    Cubic(CubicBez),
143
}
144
145
/// An intersection of a [`Line`] and a [`PathSeg`].
146
///
147
/// This can be generated with the [`PathSeg::intersect_line`] method.
148
#[derive(Debug, Clone, Copy)]
149
pub struct LineIntersection {
150
    /// The 'time' that the intersection occurs, on the line.
151
    ///
152
    /// This value is in the range 0..1.
153
    pub line_t: f64,
154
155
    /// The 'time' that the intersection occurs, on the path segment.
156
    ///
157
    /// This value is nominally in the range 0..1, although it may slightly exceed
158
    /// that range at the boundaries of segments.
159
    pub segment_t: f64,
160
}
161
162
/// The minimum distance between two Bézier curves.
163
pub struct MinDistance {
164
    /// The shortest distance between any two points on the two curves.
165
    pub distance: f64,
166
    /// The position of the nearest point on the first curve, as a parameter.
167
    ///
168
    /// To resolve this to a [`Point`], use [`ParamCurve::eval`].
169
    ///
170
    /// [`ParamCurve::eval`]: crate::ParamCurve::eval
171
    pub t1: f64,
172
    /// The position of the nearest point on the second curve, as a parameter.
173
    ///
174
    /// To resolve this to a [`Point`], use [`ParamCurve::eval`].
175
    ///
176
    /// [`ParamCurve::eval`]: crate::ParamCurve::eval
177
    pub t2: f64,
178
}
179
180
impl BezPath {
181
    /// Create a new path.
182
    #[inline(always)]
183
0
    pub fn new() -> BezPath {
184
0
        BezPath::default()
185
0
    }
186
187
    /// Create a new path with the specified capacity.
188
    ///
189
    /// This can be useful if you already know how many path elements the path
190
    /// will consist of, to prevent reallocations.
191
0
    pub fn with_capacity(capacity: usize) -> BezPath {
192
0
        BezPath(Vec::with_capacity(capacity))
193
0
    }
194
195
    /// Create a path from a vector of path elements.
196
    ///
197
    /// `BezPath` also implements `FromIterator<PathEl>`, so it works with `collect`:
198
    ///
199
    /// ```
200
    /// // a very contrived example:
201
    /// use kurbo::{BezPath, PathEl};
202
    ///
203
    /// let path = BezPath::new();
204
    /// let as_vec: Vec<PathEl> = path.into_iter().collect();
205
    /// let back_to_path: BezPath = as_vec.into_iter().collect();
206
    /// ```
207
0
    pub fn from_vec(v: Vec<PathEl>) -> BezPath {
208
0
        debug_assert!(
209
0
            v.is_empty() || matches!(v.first(), Some(PathEl::MoveTo(_))),
210
0
            "BezPath must begin with MoveTo"
211
        );
212
0
        BezPath(v)
213
0
    }
214
215
    /// Removes the last [`PathEl`] from the path and returns it, or `None` if the path is empty.
216
0
    pub fn pop(&mut self) -> Option<PathEl> {
217
0
        self.0.pop()
218
0
    }
219
220
    /// Push a generic path element onto the path.
221
0
    pub fn push(&mut self, el: PathEl) {
222
0
        self.0.push(el);
223
0
        debug_assert!(
224
0
            matches!(self.0.first(), Some(PathEl::MoveTo(_))),
225
0
            "BezPath must begin with MoveTo"
226
        );
227
0
    }
228
229
    /// Push a "move to" element onto the path.
230
0
    pub fn move_to<P: Into<Point>>(&mut self, p: P) {
231
0
        self.push(PathEl::MoveTo(p.into()));
232
0
    }
233
234
    /// Push a "line to" element onto the path.
235
    ///
236
    /// Will panic with a debug assert when the path is empty and there is no
237
    /// "move to" element on the path.
238
    ///
239
    /// If `line_to` is called immediately after `close_path` then the current
240
    /// subpath starts at the initial point of the previous subpath.
241
0
    pub fn line_to<P: Into<Point>>(&mut self, p: P) {
242
0
        debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
243
0
        self.push(PathEl::LineTo(p.into()));
244
0
    }
245
246
    /// Push a "quad to" element onto the path.
247
    ///
248
    /// Will panic with a debug assert when the path is empty and there is no
249
    /// "move to" element on the path.
250
    ///
251
    /// If `quad_to` is called immediately after `close_path` then the current
252
    /// subpath starts at the initial point of the previous subpath.
253
0
    pub fn quad_to<P: Into<Point>>(&mut self, p1: P, p2: P) {
254
0
        debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
255
0
        self.push(PathEl::QuadTo(p1.into(), p2.into()));
256
0
    }
257
258
    /// Push a "curve to" element onto the path.
259
    ///
260
    /// Will panic with a debug assert when the path is empty and there is no
261
    /// "move to" element on the path.
262
    ///
263
    /// If `curve_to` is called immediately after `close_path` then the current
264
    /// subpath starts at the initial point of the previous subpath.
265
0
    pub fn curve_to<P: Into<Point>>(&mut self, p1: P, p2: P, p3: P) {
266
0
        debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
267
0
        self.push(PathEl::CurveTo(p1.into(), p2.into(), p3.into()));
268
0
    }
269
270
    /// Push a "close path" element onto the path.
271
    ///
272
    /// Will panic with a debug assert when the path is empty and there is no
273
    /// "move to" element on the path.
274
0
    pub fn close_path(&mut self) {
275
0
        debug_assert!(!self.0.is_empty(), "uninitialized subpath (missing MoveTo)");
276
0
        self.push(PathEl::ClosePath);
277
0
    }
278
279
    /// Consumes the `BezPath` and returns a vector of [`PathEl`]s.
280
    #[inline(always)]
281
0
    pub fn into_elements(self) -> Vec<PathEl> {
282
0
        self.0
283
0
    }
284
285
    /// Get the path elements.
286
    ///
287
    /// For owned elements, see [`into_elements`].
288
    ///
289
    /// [`into_elements`]: Self::into_elements
290
    #[inline(always)]
291
0
    pub fn elements(&self) -> &[PathEl] {
292
0
        &self.0
293
0
    }
294
295
    /// Get the path elements (mut version).
296
    #[inline(always)]
297
0
    pub fn elements_mut(&mut self) -> &mut [PathEl] {
298
0
        &mut self.0
299
0
    }
300
301
    /// Returns an iterator over the path's elements.
302
0
    pub fn iter(&self) -> impl Iterator<Item = PathEl> + Clone + '_ {
303
0
        self.0.iter().copied()
304
0
    }
305
306
    /// Iterate over the path segments.
307
0
    pub fn segments(&self) -> impl Iterator<Item = PathSeg> + Clone + '_ {
308
0
        segments(self.iter())
309
0
    }
310
311
    /// Shorten the path, keeping the first `len` elements.
312
0
    pub fn truncate(&mut self, len: usize) {
313
0
        self.0.truncate(len);
314
0
    }
315
316
    /// Get the segment at the given element index.
317
    ///
318
    /// If you need to access all segments, [`segments`] provides a better
319
    /// API. This is intended for random access of specific elements, for clients
320
    /// that require this specifically.
321
    ///
322
    /// **note**: This returns the segment that ends at the provided element
323
    /// index. In effect this means it is *1-indexed*: since no segment ends at
324
    /// the first element (which is presumed to be a `MoveTo`) `get_seg(0)` will
325
    /// always return `None`.
326
0
    pub fn get_seg(&self, ix: usize) -> Option<PathSeg> {
327
0
        if ix == 0 || ix >= self.0.len() {
328
0
            return None;
329
0
        }
330
0
        let last = match self.0[ix - 1] {
331
0
            PathEl::MoveTo(p) => p,
332
0
            PathEl::LineTo(p) => p,
333
0
            PathEl::QuadTo(_, p2) => p2,
334
0
            PathEl::CurveTo(_, _, p3) => p3,
335
0
            PathEl::ClosePath => return None,
336
        };
337
0
        match self.0[ix] {
338
0
            PathEl::LineTo(p) => Some(PathSeg::Line(Line::new(last, p))),
339
0
            PathEl::QuadTo(p1, p2) => Some(PathSeg::Quad(QuadBez::new(last, p1, p2))),
340
0
            PathEl::CurveTo(p1, p2, p3) => Some(PathSeg::Cubic(CubicBez::new(last, p1, p2, p3))),
341
0
            PathEl::ClosePath => self.0[..ix].iter().rev().find_map(|el| match *el {
342
0
                PathEl::MoveTo(start) if start != last => {
343
0
                    Some(PathSeg::Line(Line::new(last, start)))
344
                }
345
0
                _ => None,
346
0
            }),
347
0
            PathEl::MoveTo(_) => None,
348
        }
349
0
    }
350
351
    /// Returns `true` if the path contains no segments.
352
0
    pub fn is_empty(&self) -> bool {
353
0
        self.0
354
0
            .iter()
355
0
            .all(|el| matches!(el, PathEl::MoveTo(..) | PathEl::ClosePath))
356
0
    }
357
358
    /// Apply an affine transform to the path.
359
0
    pub fn apply_affine(&mut self, affine: Affine) {
360
0
        for el in self.0.iter_mut() {
361
0
            *el = affine * (*el);
362
0
        }
363
0
    }
364
365
    /// Is this path finite?
366
    #[inline]
367
0
    pub fn is_finite(&self) -> bool {
368
0
        self.0.iter().all(|v| v.is_finite())
369
0
    }
370
371
    /// Is this path NaN?
372
    #[inline]
373
0
    pub fn is_nan(&self) -> bool {
374
0
        self.0.iter().any(|v| v.is_nan())
375
0
    }
376
377
    /// Returns a rectangle that conservatively encloses the path.
378
    ///
379
    /// Unlike the `bounding_box` method, this uses control points directly
380
    /// rather than computing tight bounds for curve elements.
381
0
    pub fn control_box(&self) -> Rect {
382
0
        let mut cbox: Option<Rect> = None;
383
0
        let mut add_pts = |pts: &[Point]| {
384
0
            for pt in pts {
385
0
                cbox = match cbox {
386
0
                    Some(cbox) => Some(cbox.union_pt(*pt)),
387
0
                    _ => Some(Rect::from_points(*pt, *pt)),
388
                };
389
            }
390
0
        };
391
0
        for &el in self.elements() {
392
0
            match el {
393
0
                PathEl::MoveTo(p0) | PathEl::LineTo(p0) => add_pts(&[p0]),
394
0
                PathEl::QuadTo(p0, p1) => add_pts(&[p0, p1]),
395
0
                PathEl::CurveTo(p0, p1, p2) => add_pts(&[p0, p1, p2]),
396
0
                PathEl::ClosePath => {}
397
            }
398
        }
399
0
        cbox.unwrap_or_default()
400
0
    }
401
402
    /// Returns current position in the path, if path is not empty.
403
    ///
404
    /// This is different from calling [`PathEl::end_point`] on the last entry of [`BezPath::elements`]:
405
    /// this method handles [`PathEl::ClosePath`],
406
    /// by finding the first point of our last subpath, hence the time complexity is O(n).
407
0
    pub fn current_position(&self) -> Option<Point> {
408
0
        match self.0.last()? {
409
0
            PathEl::MoveTo(p) => Some(*p),
410
0
            PathEl::LineTo(p1) => Some(*p1),
411
0
            PathEl::QuadTo(_, p2) => Some(*p2),
412
0
            PathEl::CurveTo(_, _, p3) => Some(*p3),
413
0
            PathEl::ClosePath => self
414
0
                .elements()
415
0
                .iter()
416
0
                .rev()
417
0
                .skip(1)
418
0
                .take_while(|el| !matches!(el, PathEl::ClosePath))
419
0
                .last()
420
0
                .and_then(|el| el.end_point()),
421
        }
422
0
    }
423
424
    /// Returns an iterator over the subpaths of this path. See [Elements and Segments](#elements-and-segments) for more information.
425
0
    pub fn subpaths(&self) -> impl Iterator<Item = &[PathEl]> {
426
0
        let elements = self.elements();
427
0
        let mut i = 0;
428
429
0
        core::iter::from_fn(move || {
430
0
            if i >= elements.len() {
431
0
                return None;
432
0
            }
433
0
            let start = i;
434
0
            i += 1;
435
0
            while i < elements.len() && !matches!(elements[i], PathEl::MoveTo(_)) {
436
0
                i += 1;
437
0
            }
438
0
            Some(&elements[start..i])
439
0
        })
440
0
    }
441
442
    /// Returns a new path with the winding direction of all subpaths reversed.
443
0
    pub fn reverse_subpaths(&self) -> BezPath {
444
0
        let elements = self.elements();
445
0
        let mut start_ix = 1;
446
0
        let mut start_pt = Point::default();
447
0
        let mut reversed = BezPath(Vec::with_capacity(elements.len()));
448
        // Pending move is used to capture degenerate subpaths that should
449
        // remain in the reversed output.
450
0
        let mut pending_move = false;
451
0
        for (ix, el) in elements.iter().enumerate() {
452
0
            match el {
453
0
                PathEl::MoveTo(pt) => {
454
0
                    if pending_move {
455
0
                        reversed.push(PathEl::MoveTo(start_pt));
456
0
                    }
457
0
                    if start_ix < ix {
458
0
                        reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
459
0
                    }
460
0
                    pending_move = true;
461
0
                    start_pt = *pt;
462
0
                    start_ix = ix + 1;
463
                }
464
                PathEl::ClosePath => {
465
0
                    if start_ix <= ix {
466
0
                        reverse_subpath(start_pt, &elements[start_ix..ix], &mut reversed);
467
0
                    }
468
0
                    reversed.push(PathEl::ClosePath);
469
0
                    start_ix = ix + 1;
470
0
                    pending_move = false;
471
                }
472
0
                _ => {
473
0
                    pending_move = false;
474
0
                }
475
            }
476
        }
477
0
        if start_ix < elements.len() {
478
0
            reverse_subpath(start_pt, &elements[start_ix..], &mut reversed);
479
0
        } else if pending_move {
480
0
            reversed.push(PathEl::MoveTo(start_pt));
481
0
        }
482
0
        reversed
483
0
    }
484
}
485
486
/// Helper for reversing a subpath.
487
///
488
/// The `els` parameter must not contain any `MoveTo` or `ClosePath` elements.
489
0
fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) {
490
0
    let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt);
491
0
    reversed.push(PathEl::MoveTo(end_pt));
492
0
    for (ix, el) in els.iter().enumerate().rev() {
493
0
        let end_pt = if ix > 0 {
494
0
            els[ix - 1].end_point().unwrap()
495
        } else {
496
0
            start_pt
497
        };
498
0
        match el {
499
0
            PathEl::LineTo(_) => reversed.push(PathEl::LineTo(end_pt)),
500
0
            PathEl::QuadTo(c0, _) => reversed.push(PathEl::QuadTo(*c0, end_pt)),
501
0
            PathEl::CurveTo(c0, c1, _) => reversed.push(PathEl::CurveTo(*c1, *c0, end_pt)),
502
0
            _ => panic!("reverse_subpath expects MoveTo and ClosePath to be removed"),
503
        }
504
    }
505
0
}
506
507
impl FromIterator<PathEl> for BezPath {
508
0
    fn from_iter<T: IntoIterator<Item = PathEl>>(iter: T) -> Self {
509
0
        let el_vec: Vec<_> = iter.into_iter().collect();
510
0
        BezPath::from_vec(el_vec)
511
0
    }
512
}
513
514
/// Allow iteration over references to `BezPath`.
515
///
516
/// Note: the semantics are slightly different from simply iterating over the
517
/// slice, as it returns `PathEl` items, rather than references.
518
impl<'a> IntoIterator for &'a BezPath {
519
    type Item = PathEl;
520
    type IntoIter = core::iter::Cloned<core::slice::Iter<'a, PathEl>>;
521
522
0
    fn into_iter(self) -> Self::IntoIter {
523
0
        self.elements().iter().cloned()
524
0
    }
525
}
526
527
impl IntoIterator for BezPath {
528
    type Item = PathEl;
529
    type IntoIter = alloc::vec::IntoIter<PathEl>;
530
531
0
    fn into_iter(self) -> Self::IntoIter {
532
0
        self.0.into_iter()
533
0
    }
534
}
535
536
impl Extend<PathEl> for BezPath {
537
    /// Add the items from the iterator to this path.
538
    ///
539
    /// <div class="warning">
540
    ///
541
    /// Note that if you're attempting to make a continuous path, you will generally
542
    /// want to ensure that the iterator does not contain any [`MoveTo`](PathEl::MoveTo)
543
    /// or [`ClosePath`](PathEl::ClosePath) elements.
544
    /// Note especially that many (open) [shapes](Shape) will start with a `MoveTo` if
545
    /// you use their [`path_elements`](Shape::path_elements) function.
546
    /// Some shapes have alternatives for this use case, such as [`Arc::append_iter`](crate::Arc::append_iter).
547
    ///
548
    /// </div>
549
0
    fn extend<I: IntoIterator<Item = PathEl>>(&mut self, iter: I) {
550
0
        self.0.extend(iter);
551
0
    }
Unexecuted instantiation: <kurbo::bezpath::BezPath as core::iter::traits::collect::Extend<kurbo::bezpath::PathEl>>::extend::<core::iter::adapters::skip::Skip<core::iter::adapters::copied::Copied<core::slice::iter::Iter<kurbo::bezpath::PathEl>>>>
Unexecuted instantiation: <kurbo::bezpath::BezPath as core::iter::traits::collect::Extend<kurbo::bezpath::PathEl>>::extend::<&kurbo::bezpath::BezPath>
552
}
553
554
/// Proportion of tolerance budget that goes to cubic to quadratic conversion.
555
const TO_QUAD_TOL: f64 = 0.1;
556
557
/// Flatten the path, invoking the callback repeatedly.
558
///
559
/// Flattening is the action of approximating a curve with a succession of line segments.
560
///
561
/// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 30" height="30mm" width="120mm">
562
///   <path d="M26.7 24.94l.82-11.15M44.46 5.1L33.8 7.34" fill="none" stroke="#55d400" stroke-width=".5"/>
563
///   <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"/>
564
///   <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"/>
565
///   <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"/>
566
///   <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"/>
567
///   <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"/>
568
///   <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"/>
569
///   <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"/>
570
///   <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"/>
571
///   <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"/>
572
///   <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"/>
573
///   <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"/>
574
///   <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"/>
575
///   <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"/>
576
///   <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"/>
577
///   <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"/>
578
///   <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"/>
579
///   <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"/>
580
///   <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"/>
581
///   <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"/>
582
///   <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"/>
583
///   <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"/>
584
///   <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)">
585
///     <tspan x="35.74" y="284.49" font-size="10.58">→</tspan>
586
///   </text>
587
/// </svg>
588
///
589
/// The tolerance value controls the maximum distance between the curved input
590
/// segments and their polyline approximations. (In technical terms, this is the
591
/// Hausdorff distance). The algorithm attempts to bound this distance between
592
/// by `tolerance` but this is not absolutely guaranteed. The appropriate value
593
/// depends on the use, but for antialiased rendering, a value of 0.25 has been
594
/// determined to give good results. The number of segments tends to scale as the
595
/// inverse square root of tolerance.
596
///
597
/// <svg viewBox="0 0 47.5 13.2" height="100" width="350" xmlns="http://www.w3.org/2000/svg">
598
///   <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"/>
599
///   <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"/>
600
///   <path d="M-1.94 10.46L6.2 6.08l28.32-1.4 15.17 6.74" fill="none" stroke="#000" stroke-width=".6"/>
601
///   <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"/>
602
///   <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"/>
603
///   <g fill="none" stroke="#ff7f2a" stroke-width=".26">
604
///     <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"/>
605
///   </g>
606
/// </svg>
607
///
608
/// The callback will be called in order with each element of the generated
609
/// path. Because the result is made of polylines, these will be straight-line
610
/// path elements only, no curves.
611
///
612
/// This algorithm is based on the blog post [Flattening quadratic Béziers]
613
/// but with some refinements. For one, there is a more careful approximation
614
/// at cusps. For two, the algorithm is extended to work with cubic Béziers
615
/// as well, by first subdividing into quadratics and then computing the
616
/// subdivision of each quadratic. However, as a clever trick, these quadratics
617
/// are subdivided fractionally, and their endpoints are not included.
618
///
619
/// TODO: write a paper explaining this in more detail.
620
///
621
/// [Flattening quadratic Béziers]: https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
622
0
pub fn flatten(
623
0
    path: impl IntoIterator<Item = PathEl>,
624
0
    tolerance: f64,
625
0
    mut callback: impl FnMut(PathEl),
626
0
) {
627
0
    let sqrt_tol = tolerance.sqrt();
628
0
    let mut last_pt = None;
629
0
    let mut quad_buf = Vec::new();
630
0
    for el in path {
631
0
        match el {
632
0
            PathEl::MoveTo(p) => {
633
0
                last_pt = Some(p);
634
0
                callback(PathEl::MoveTo(p));
635
0
            }
636
0
            PathEl::LineTo(p) => {
637
0
                last_pt = Some(p);
638
0
                callback(PathEl::LineTo(p));
639
0
            }
640
0
            PathEl::QuadTo(p1, p2) => {
641
0
                if let Some(p0) = last_pt {
642
0
                    let q = QuadBez::new(p0, p1, p2);
643
0
                    let params = q.estimate_subdiv(sqrt_tol);
644
0
                    let n = ((0.5 * params.val / sqrt_tol).ceil() as usize).max(1);
645
0
                    let step = 1.0 / (n as f64);
646
0
                    for i in 1..n {
647
0
                        let u = (i as f64) * step;
648
0
                        let t = q.determine_subdiv_t(&params, u);
649
0
                        let p = q.eval(t);
650
0
                        callback(PathEl::LineTo(p));
651
0
                    }
652
0
                    callback(PathEl::LineTo(p2));
653
0
                }
654
0
                last_pt = Some(p2);
655
            }
656
0
            PathEl::CurveTo(p1, p2, p3) => {
657
0
                if let Some(p0) = last_pt {
658
0
                    let c = CubicBez::new(p0, p1, p2, p3);
659
660
                    // Subdivide into quadratics, and estimate the number of
661
                    // subdivisions required for each, summing to arrive at an
662
                    // estimate for the number of subdivisions for the cubic.
663
                    // Also retain these parameters for later.
664
0
                    let iter = c.to_quads(tolerance * TO_QUAD_TOL);
665
0
                    quad_buf.clear();
666
0
                    quad_buf.reserve(iter.size_hint().0);
667
0
                    let sqrt_remain_tol = sqrt_tol * (1.0 - TO_QUAD_TOL).sqrt();
668
0
                    let mut sum = 0.0;
669
0
                    for (_, _, q) in iter {
670
0
                        let params = q.estimate_subdiv(sqrt_remain_tol);
671
0
                        sum += params.val;
672
0
                        quad_buf.push((q, params));
673
0
                    }
674
0
                    let n = ((0.5 * sum / sqrt_remain_tol).ceil() as usize).max(1);
675
676
                    // Iterate through the quadratics, outputting the points of
677
                    // subdivisions that fall within that quadratic.
678
0
                    let step = sum / (n as f64);
679
0
                    let mut i = 1;
680
0
                    let mut val_sum = 0.0;
681
0
                    for (q, params) in &quad_buf {
682
0
                        let mut target = (i as f64) * step;
683
0
                        let recip_val = params.val.recip();
684
0
                        while target < val_sum + params.val {
685
0
                            let u = (target - val_sum) * recip_val;
686
0
                            let t = q.determine_subdiv_t(params, u);
687
0
                            let p = q.eval(t);
688
0
                            callback(PathEl::LineTo(p));
689
0
                            i += 1;
690
0
                            if i == n + 1 {
691
0
                                break;
692
0
                            }
693
0
                            target = (i as f64) * step;
694
                        }
695
0
                        val_sum += params.val;
696
                    }
697
0
                    callback(PathEl::LineTo(p3));
698
0
                }
699
0
                last_pt = Some(p3);
700
            }
701
0
            PathEl::ClosePath => {
702
0
                last_pt = None;
703
0
                callback(PathEl::ClosePath);
704
0
            }
705
        }
706
    }
707
0
}
708
709
impl Mul<PathEl> for Affine {
710
    type Output = PathEl;
711
712
    // TODO: Inlining this leads to a huge performance benefit, perhaps the same should be done for
713
    // other methods.
714
    #[inline(always)]
715
0
    fn mul(self, other: PathEl) -> PathEl {
716
0
        match other {
717
0
            PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
718
0
            PathEl::LineTo(p) => PathEl::LineTo(self * p),
719
0
            PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
720
0
            PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
721
0
            PathEl::ClosePath => PathEl::ClosePath,
722
        }
723
0
    }
724
}
725
726
impl Mul<PathSeg> for Affine {
727
    type Output = PathSeg;
728
729
0
    fn mul(self, other: PathSeg) -> PathSeg {
730
0
        match other {
731
0
            PathSeg::Line(line) => PathSeg::Line(self * line),
732
0
            PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
733
0
            PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
734
        }
735
0
    }
736
}
737
738
impl Mul<BezPath> for Affine {
739
    type Output = BezPath;
740
741
0
    fn mul(self, other: BezPath) -> BezPath {
742
0
        BezPath(other.0.iter().map(|&el| self * el).collect())
743
0
    }
744
}
745
746
impl Mul<&BezPath> for Affine {
747
    type Output = BezPath;
748
749
0
    fn mul(self, other: &BezPath) -> BezPath {
750
0
        BezPath(other.0.iter().map(|&el| self * el).collect())
751
0
    }
752
}
753
754
impl Mul<PathEl> for TranslateScale {
755
    type Output = PathEl;
756
757
0
    fn mul(self, other: PathEl) -> PathEl {
758
0
        match other {
759
0
            PathEl::MoveTo(p) => PathEl::MoveTo(self * p),
760
0
            PathEl::LineTo(p) => PathEl::LineTo(self * p),
761
0
            PathEl::QuadTo(p1, p2) => PathEl::QuadTo(self * p1, self * p2),
762
0
            PathEl::CurveTo(p1, p2, p3) => PathEl::CurveTo(self * p1, self * p2, self * p3),
763
0
            PathEl::ClosePath => PathEl::ClosePath,
764
        }
765
0
    }
766
}
767
768
impl Mul<PathSeg> for TranslateScale {
769
    type Output = PathSeg;
770
771
0
    fn mul(self, other: PathSeg) -> PathSeg {
772
0
        match other {
773
0
            PathSeg::Line(line) => PathSeg::Line(self * line),
774
0
            PathSeg::Quad(quad) => PathSeg::Quad(self * quad),
775
0
            PathSeg::Cubic(cubic) => PathSeg::Cubic(self * cubic),
776
        }
777
0
    }
778
}
779
780
impl Mul<BezPath> for TranslateScale {
781
    type Output = BezPath;
782
783
0
    fn mul(self, other: BezPath) -> BezPath {
784
0
        BezPath(other.0.iter().map(|&el| self * el).collect())
785
0
    }
786
}
787
788
impl Mul<&BezPath> for TranslateScale {
789
    type Output = BezPath;
790
791
0
    fn mul(self, other: &BezPath) -> BezPath {
792
0
        BezPath(other.0.iter().map(|&el| self * el).collect())
793
0
    }
794
}
795
796
/// Close all open subpaths in a path, expressed as iterator transform.
797
0
pub(crate) fn close_subpaths<I>(elements: I) -> CloseSubpaths<I::IntoIter>
798
0
where
799
0
    I: IntoIterator<Item = PathEl>,
800
{
801
0
    CloseSubpaths {
802
0
        elements: elements.into_iter(),
803
0
        state: CloseSubpathState::Start,
804
0
    }
805
0
}
806
807
/// An iterator that closes all open subpaths.
808
///
809
/// This struct is created by the [`close_subpaths`] function.
810
#[derive(Clone)]
811
pub(crate) struct CloseSubpaths<I: Iterator<Item = PathEl>> {
812
    elements: I,
813
    state: CloseSubpathState,
814
}
815
816
#[derive(Clone)]
817
enum CloseSubpathState {
818
    Start,
819
    InSubpath,
820
    ClosedLast,
821
    PendingMoveTo(Point),
822
}
823
824
impl<I: Iterator<Item = PathEl>> Iterator for CloseSubpaths<I> {
825
    type Item = PathEl;
826
827
0
    fn next(&mut self) -> Option<PathEl> {
828
0
        match self.state {
829
            CloseSubpathState::Start => {
830
0
                let el = self.elements.next();
831
0
                if !matches!(el, Some(PathEl::ClosePath) | None) {
832
0
                    self.state = CloseSubpathState::InSubpath;
833
0
                }
834
0
                el
835
            }
836
            CloseSubpathState::InSubpath => {
837
0
                let el = self.elements.next();
838
0
                match el {
839
                    None => {
840
0
                        self.state = CloseSubpathState::ClosedLast;
841
0
                        Some(PathEl::ClosePath)
842
                    }
843
0
                    Some(PathEl::MoveTo(point)) => {
844
0
                        self.state = CloseSubpathState::PendingMoveTo(point);
845
0
                        Some(PathEl::ClosePath)
846
                    }
847
                    Some(PathEl::ClosePath) => {
848
0
                        self.state = CloseSubpathState::Start;
849
0
                        el
850
                    }
851
0
                    _ => el,
852
                }
853
            }
854
0
            CloseSubpathState::ClosedLast => None,
855
0
            CloseSubpathState::PendingMoveTo(point) => {
856
0
                self.state = CloseSubpathState::Start;
857
0
                Some(PathEl::MoveTo(point))
858
            }
859
        }
860
0
    }
861
}
862
863
/// Transform an iterator over path elements into one over path
864
/// segments.
865
///
866
/// See also [`BezPath::segments`].
867
/// This signature is a bit more general, allowing `&[PathEl]` slices
868
/// and other iterators yielding `PathEl`.
869
0
pub fn segments<I>(elements: I) -> Segments<I::IntoIter>
870
0
where
871
0
    I: IntoIterator<Item = PathEl>,
872
{
873
0
    Segments {
874
0
        elements: elements.into_iter(),
875
0
        start_last: None,
876
0
    }
877
0
}
Unexecuted instantiation: kurbo::bezpath::segments::<core::iter::adapters::copied::Copied<core::slice::iter::Iter<kurbo::bezpath::PathEl>>>
Unexecuted instantiation: kurbo::bezpath::segments::<&kurbo::bezpath::BezPath>
878
879
/// An iterator that transforms path elements to path segments.
880
///
881
/// This struct is created by the [`segments`] function.
882
#[derive(Clone)]
883
pub struct Segments<I: Iterator<Item = PathEl>> {
884
    elements: I,
885
    start_last: Option<(Point, Point)>,
886
}
887
888
impl<I: Iterator<Item = PathEl>> Iterator for Segments<I> {
889
    type Item = PathSeg;
890
891
    #[inline]
892
0
    fn next(&mut self) -> Option<PathSeg> {
893
0
        for el in &mut self.elements {
894
            // We first need to check whether this is the first
895
            // path element we see to fill in the start position.
896
0
            let (start, last) = self.start_last.get_or_insert_with(|| {
897
0
                let point = match el {
898
0
                    PathEl::MoveTo(p) => p,
899
0
                    PathEl::LineTo(p) => p,
900
0
                    PathEl::QuadTo(_, p2) => p2,
901
0
                    PathEl::CurveTo(_, _, p3) => p3,
902
0
                    PathEl::ClosePath => panic!("Can't start a segment on a ClosePath"),
903
                };
904
0
                (point, point)
905
0
            });
Unexecuted instantiation: <kurbo::bezpath::Segments<core::iter::adapters::cloned::Cloned<core::slice::iter::Iter<kurbo::bezpath::PathEl>>> as core::iter::traits::iterator::Iterator>::next::{closure#0}
Unexecuted instantiation: <kurbo::bezpath::Segments<core::iter::adapters::copied::Copied<core::slice::iter::Iter<kurbo::bezpath::PathEl>>> as core::iter::traits::iterator::Iterator>::next::{closure#0}
906
907
0
            return Some(match el {
908
0
                PathEl::MoveTo(p) => {
909
0
                    *start = p;
910
0
                    *last = p;
911
0
                    continue;
912
                }
913
0
                PathEl::LineTo(p) => PathSeg::Line(Line::new(mem::replace(last, p), p)),
914
0
                PathEl::QuadTo(p1, p2) => {
915
0
                    PathSeg::Quad(QuadBez::new(mem::replace(last, p2), p1, p2))
916
                }
917
0
                PathEl::CurveTo(p1, p2, p3) => {
918
0
                    PathSeg::Cubic(CubicBez::new(mem::replace(last, p3), p1, p2, p3))
919
                }
920
                PathEl::ClosePath => {
921
0
                    if *last != *start {
922
0
                        PathSeg::Line(Line::new(mem::replace(last, *start), *start))
923
                    } else {
924
0
                        continue;
925
                    }
926
                }
927
            });
928
        }
929
930
0
        None
931
0
    }
Unexecuted instantiation: <kurbo::bezpath::Segments<core::iter::adapters::cloned::Cloned<core::slice::iter::Iter<kurbo::bezpath::PathEl>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <kurbo::bezpath::Segments<core::iter::adapters::copied::Copied<core::slice::iter::Iter<kurbo::bezpath::PathEl>>> as core::iter::traits::iterator::Iterator>::next
932
}
933
934
impl<I: Iterator<Item = PathEl>> Segments<I> {
935
    /// Here, `accuracy` specifies the accuracy for each Bézier segment. At worst,
936
    /// the total error is `accuracy` times the number of Bézier segments.
937
    // TODO: pub? Or is this subsumed by method of &[PathEl]?
938
0
    pub(crate) fn perimeter(self, accuracy: f64) -> f64 {
939
0
        self.map(|seg| seg.arclen(accuracy)).sum()
940
0
    }
941
942
    // Same
943
0
    pub(crate) fn area(self) -> f64 {
944
0
        self.map(|seg| seg.signed_area()).sum()
945
0
    }
946
947
    // Same
948
0
    pub(crate) fn winding(self, p: Point) -> i32 {
949
0
        self.map(|seg| seg.winding(p)).sum()
950
0
    }
951
952
    // Same
953
0
    pub(crate) fn bounding_box(self) -> Rect {
954
0
        let mut bbox: Option<Rect> = None;
955
0
        for seg in self {
956
0
            let seg_bb = ParamCurveExtrema::bounding_box(&seg);
957
0
            if let Some(bb) = bbox {
958
0
                bbox = Some(bb.union(seg_bb));
959
0
            } else {
960
0
                bbox = Some(seg_bb);
961
0
            }
962
        }
963
0
        bbox.unwrap_or_default()
964
0
    }
965
}
966
967
impl ParamCurve for PathSeg {
968
0
    fn eval(&self, t: f64) -> Point {
969
0
        match *self {
970
0
            PathSeg::Line(line) => line.eval(t),
971
0
            PathSeg::Quad(quad) => quad.eval(t),
972
0
            PathSeg::Cubic(cubic) => cubic.eval(t),
973
        }
974
0
    }
975
976
0
    fn subsegment(&self, range: Range<f64>) -> PathSeg {
977
0
        match *self {
978
0
            PathSeg::Line(line) => PathSeg::Line(line.subsegment(range)),
979
0
            PathSeg::Quad(quad) => PathSeg::Quad(quad.subsegment(range)),
980
0
            PathSeg::Cubic(cubic) => PathSeg::Cubic(cubic.subsegment(range)),
981
        }
982
0
    }
983
984
0
    fn start(&self) -> Point {
985
0
        match *self {
986
0
            PathSeg::Line(line) => line.start(),
987
0
            PathSeg::Quad(quad) => quad.start(),
988
0
            PathSeg::Cubic(cubic) => cubic.start(),
989
        }
990
0
    }
991
992
0
    fn end(&self) -> Point {
993
0
        match *self {
994
0
            PathSeg::Line(line) => line.end(),
995
0
            PathSeg::Quad(quad) => quad.end(),
996
0
            PathSeg::Cubic(cubic) => cubic.end(),
997
        }
998
0
    }
999
}
1000
1001
impl ParamCurveArclen for PathSeg {
1002
0
    fn arclen(&self, accuracy: f64) -> f64 {
1003
0
        match *self {
1004
0
            PathSeg::Line(line) => line.arclen(accuracy),
1005
0
            PathSeg::Quad(quad) => quad.arclen(accuracy),
1006
0
            PathSeg::Cubic(cubic) => cubic.arclen(accuracy),
1007
        }
1008
0
    }
1009
1010
0
    fn inv_arclen(&self, arclen: f64, accuracy: f64) -> f64 {
1011
0
        match *self {
1012
0
            PathSeg::Line(line) => line.inv_arclen(arclen, accuracy),
1013
0
            PathSeg::Quad(quad) => quad.inv_arclen(arclen, accuracy),
1014
0
            PathSeg::Cubic(cubic) => cubic.inv_arclen(arclen, accuracy),
1015
        }
1016
0
    }
1017
}
1018
1019
impl ParamCurveArea for PathSeg {
1020
0
    fn signed_area(&self) -> f64 {
1021
0
        match *self {
1022
0
            PathSeg::Line(line) => line.signed_area(),
1023
0
            PathSeg::Quad(quad) => quad.signed_area(),
1024
0
            PathSeg::Cubic(cubic) => cubic.signed_area(),
1025
        }
1026
0
    }
1027
}
1028
1029
impl ParamCurveNearest for PathSeg {
1030
0
    fn nearest(&self, p: Point, accuracy: f64) -> Nearest {
1031
0
        match *self {
1032
0
            PathSeg::Line(line) => line.nearest(p, accuracy),
1033
0
            PathSeg::Quad(quad) => quad.nearest(p, accuracy),
1034
0
            PathSeg::Cubic(cubic) => cubic.nearest(p, accuracy),
1035
        }
1036
0
    }
1037
}
1038
1039
impl ParamCurveExtrema for PathSeg {
1040
0
    fn extrema(&self) -> ArrayVec<f64, MAX_EXTREMA> {
1041
0
        match *self {
1042
0
            PathSeg::Line(line) => line.extrema(),
1043
0
            PathSeg::Quad(quad) => quad.extrema(),
1044
0
            PathSeg::Cubic(cubic) => cubic.extrema(),
1045
        }
1046
0
    }
1047
}
1048
1049
impl PathSeg {
1050
    /// Get the [`PathEl`] that is equivalent to discarding the segment start point.
1051
0
    pub fn as_path_el(&self) -> PathEl {
1052
0
        match self {
1053
0
            PathSeg::Line(line) => PathEl::LineTo(line.p1),
1054
0
            PathSeg::Quad(q) => PathEl::QuadTo(q.p1, q.p2),
1055
0
            PathSeg::Cubic(c) => PathEl::CurveTo(c.p1, c.p2, c.p3),
1056
        }
1057
0
    }
1058
1059
    /// Returns a new `PathSeg` describing the same path as `self`, but with
1060
    /// the points reversed.
1061
0
    pub fn reverse(&self) -> PathSeg {
1062
0
        match self {
1063
0
            PathSeg::Line(Line { p0, p1 }) => PathSeg::Line(Line::new(*p1, *p0)),
1064
0
            PathSeg::Quad(q) => PathSeg::Quad(QuadBez::new(q.p2, q.p1, q.p0)),
1065
0
            PathSeg::Cubic(c) => PathSeg::Cubic(CubicBez::new(c.p3, c.p2, c.p1, c.p0)),
1066
        }
1067
0
    }
1068
1069
    /// Convert this segment to a cubic Bézier.
1070
0
    pub fn to_cubic(&self) -> CubicBez {
1071
0
        match *self {
1072
0
            PathSeg::Line(Line { p0, p1 }) => CubicBez::new(p0, p0, p1, p1),
1073
0
            PathSeg::Cubic(c) => c,
1074
0
            PathSeg::Quad(q) => q.raise(),
1075
        }
1076
0
    }
1077
1078
    /// A single-segment "winding" number.
1079
    ///
1080
    /// Assume that `self` is monotonic in `y`, and take a ray pointing
1081
    /// left from `p`. If `self` crosses that ray, returns 1 (if we're
1082
    /// decreasing in y) or -1 (if we're increasing in y). Otherwise, returns 0.
1083
    ///
1084
    /// Handling of endpoints is a little subtle, just to make sure that
1085
    /// we correctly handle consecutive segments: we consider `self` to
1086
    /// contain the endpoint with smaller y, and not contain the endpoint
1087
    /// with larger y.
1088
0
    fn winding_inner(&self, p: Point) -> i32 {
1089
0
        let start = self.start();
1090
0
        let end = self.end();
1091
0
        let sign = if end.y > start.y {
1092
0
            if p.y < start.y || p.y >= end.y {
1093
0
                return 0;
1094
0
            }
1095
0
            -1
1096
0
        } else if end.y < start.y {
1097
0
            if p.y < end.y || p.y >= start.y {
1098
0
                return 0;
1099
0
            }
1100
0
            1
1101
        } else {
1102
0
            return 0;
1103
        };
1104
0
        match *self {
1105
0
            PathSeg::Line(_line) => {
1106
0
                if p.x < start.x.min(end.x) {
1107
0
                    return 0;
1108
0
                }
1109
0
                if p.x >= start.x.max(end.x) {
1110
0
                    return sign;
1111
0
                }
1112
                // line equation ax + by = c
1113
0
                let a = end.y - start.y;
1114
0
                let b = start.x - end.x;
1115
0
                let c = a * start.x + b * start.y;
1116
0
                if (a * p.x + b * p.y - c) * (sign as f64) <= 0.0 {
1117
0
                    sign
1118
                } else {
1119
0
                    0
1120
                }
1121
            }
1122
0
            PathSeg::Quad(quad) => {
1123
0
                let p1 = quad.p1;
1124
0
                if p.x < start.x.min(end.x).min(p1.x) {
1125
0
                    return 0;
1126
0
                }
1127
0
                if p.x >= start.x.max(end.x).max(p1.x) {
1128
0
                    return sign;
1129
0
                }
1130
0
                let t = quad.solve_monotonic_for_y(p.y);
1131
0
                let x = quad.eval(t).x;
1132
0
                if p.x >= x { sign } else { 0 }
1133
            }
1134
0
            PathSeg::Cubic(cubic) => {
1135
0
                let p1 = cubic.p1;
1136
0
                let p2 = cubic.p2;
1137
0
                if p.x < start.x.min(end.x).min(p1.x).min(p2.x) {
1138
0
                    return 0;
1139
0
                }
1140
0
                if p.x >= start.x.max(end.x).max(p1.x).max(p2.x) {
1141
0
                    return sign;
1142
0
                }
1143
0
                let t = cubic.solve_monotonic_for_y(p.y);
1144
0
                let x = cubic.eval(t).x;
1145
0
                if p.x >= x { sign } else { 0 }
1146
            }
1147
        }
1148
0
    }
1149
1150
    /// Compute the winding number contribution of a single segment.
1151
    ///
1152
    /// Cast a ray to the left and count intersections.
1153
0
    fn winding(&self, p: Point) -> i32 {
1154
0
        self.extrema_ranges()
1155
0
            .into_iter()
1156
0
            .map(|range| self.subsegment(range).winding_inner(p))
1157
0
            .sum()
1158
0
    }
1159
1160
    /// Compute intersections against a line.
1161
    ///
1162
    /// Returns a vector of the intersections. For each intersection,
1163
    /// the `t` value of the segment and line are given.
1164
    ///
1165
    /// Note: This test is designed to be inclusive of points near the endpoints
1166
    /// of the segment. This is so that testing a line against multiple
1167
    /// contiguous segments of a path will be guaranteed to catch at least one
1168
    /// of them. In such cases, use higher level logic to coalesce the hits
1169
    /// (the `t` value may be slightly outside the range of 0..1).
1170
    ///
1171
    /// # Examples
1172
    ///
1173
    /// ```
1174
    /// # use kurbo::*;
1175
    /// let seg = PathSeg::Line(Line::new((0.0, 0.0), (2.0, 0.0)));
1176
    /// let line = Line::new((1.0, 2.0), (1.0, -2.0));
1177
    /// let intersection = seg.intersect_line(line);
1178
    /// assert_eq!(intersection.len(), 1);
1179
    /// let intersection = intersection[0];
1180
    /// assert_eq!(intersection.segment_t, 0.5);
1181
    /// assert_eq!(intersection.line_t, 0.5);
1182
    ///
1183
    /// let point = seg.eval(intersection.segment_t);
1184
    /// assert_eq!(point, Point::new(1.0, 0.0));
1185
    /// ```
1186
0
    pub fn intersect_line(&self, line: Line) -> ArrayVec<LineIntersection, 3> {
1187
        const EPSILON: f64 = 1e-9;
1188
0
        let p0 = line.p0;
1189
0
        let p1 = line.p1;
1190
0
        let dx = p1.x - p0.x;
1191
0
        let dy = p1.y - p0.y;
1192
0
        let mut result = ArrayVec::new();
1193
0
        match self {
1194
0
            PathSeg::Line(l) => {
1195
0
                let det = dx * (l.p1.y - l.p0.y) - dy * (l.p1.x - l.p0.x);
1196
0
                if det.abs() < EPSILON {
1197
                    // Lines are coincident (or nearly so).
1198
0
                    return result;
1199
0
                }
1200
0
                let t = dx * (p0.y - l.p0.y) - dy * (p0.x - l.p0.x);
1201
                // t = position on self
1202
0
                let t = t / det;
1203
0
                if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1204
                    // u = position on probe line
1205
0
                    let u =
1206
0
                        (l.p0.x - p0.x) * (l.p1.y - l.p0.y) - (l.p0.y - p0.y) * (l.p1.x - l.p0.x);
1207
0
                    let u = u / det;
1208
0
                    if (0.0..=1.0).contains(&u) {
1209
0
                        result.push(LineIntersection::new(u, t));
1210
0
                    }
1211
0
                }
1212
            }
1213
0
            PathSeg::Quad(q) => {
1214
                // The basic technique here is to determine x and y as a quadratic polynomial
1215
                // as a function of t. Then plug those values into the line equation for the
1216
                // probe line (giving a sort of signed distance from the probe line) and solve
1217
                // that for t.
1218
0
                let (px0, px1, px2) = quadratic_bez_coefs(q.p0.x, q.p1.x, q.p2.x);
1219
0
                let (py0, py1, py2) = quadratic_bez_coefs(q.p0.y, q.p1.y, q.p2.y);
1220
0
                let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1221
0
                let c1 = dy * px1 - dx * py1;
1222
0
                let c2 = dy * px2 - dx * py2;
1223
0
                let invlen2 = (dx * dx + dy * dy).recip();
1224
0
                for t in solve_quadratic(c0, c1, c2) {
1225
0
                    if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1226
0
                        let x = px0 + t * px1 + t * t * px2;
1227
0
                        let y = py0 + t * py1 + t * t * py2;
1228
0
                        let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1229
0
                        if (0.0..=1.0).contains(&u) {
1230
0
                            result.push(LineIntersection::new(u, t));
1231
0
                        }
1232
0
                    }
1233
                }
1234
            }
1235
0
            PathSeg::Cubic(c) => {
1236
                // Same technique as above, but cubic polynomial.
1237
0
                let (px0, px1, px2, px3) = cubic_bez_coefs(c.p0.x, c.p1.x, c.p2.x, c.p3.x);
1238
0
                let (py0, py1, py2, py3) = cubic_bez_coefs(c.p0.y, c.p1.y, c.p2.y, c.p3.y);
1239
0
                let c0 = dy * (px0 - p0.x) - dx * (py0 - p0.y);
1240
0
                let c1 = dy * px1 - dx * py1;
1241
0
                let c2 = dy * px2 - dx * py2;
1242
0
                let c3 = dy * px3 - dx * py3;
1243
0
                let invlen2 = (dx * dx + dy * dy).recip();
1244
0
                for t in solve_cubic(c0, c1, c2, c3) {
1245
0
                    if (-EPSILON..=(1.0 + EPSILON)).contains(&t) {
1246
0
                        let x = px0 + t * px1 + t * t * px2 + t * t * t * px3;
1247
0
                        let y = py0 + t * py1 + t * t * py2 + t * t * t * py3;
1248
0
                        let u = ((x - p0.x) * dx + (y - p0.y) * dy) * invlen2;
1249
0
                        if (0.0..=1.0).contains(&u) {
1250
0
                            result.push(LineIntersection::new(u, t));
1251
0
                        }
1252
0
                    }
1253
                }
1254
            }
1255
        }
1256
0
        result
1257
0
    }
1258
1259
    /// Is this Bézier path finite?
1260
    #[inline]
1261
0
    pub fn is_finite(&self) -> bool {
1262
0
        match self {
1263
0
            PathSeg::Line(line) => line.is_finite(),
1264
0
            PathSeg::Quad(quad_bez) => quad_bez.is_finite(),
1265
0
            PathSeg::Cubic(cubic_bez) => cubic_bez.is_finite(),
1266
        }
1267
0
    }
1268
1269
    /// Is this Bézier path NaN?
1270
    #[inline]
1271
0
    pub fn is_nan(&self) -> bool {
1272
0
        match self {
1273
0
            PathSeg::Line(line) => line.is_nan(),
1274
0
            PathSeg::Quad(quad_bez) => quad_bez.is_nan(),
1275
0
            PathSeg::Cubic(cubic_bez) => cubic_bez.is_nan(),
1276
        }
1277
0
    }
1278
1279
    #[inline]
1280
0
    fn as_vec2_vec(&self) -> ArrayVec<Vec2, 4> {
1281
0
        let mut a = ArrayVec::new();
1282
0
        match self {
1283
0
            PathSeg::Line(l) => {
1284
0
                a.push(l.p0.to_vec2());
1285
0
                a.push(l.p1.to_vec2());
1286
0
            }
1287
0
            PathSeg::Quad(q) => {
1288
0
                a.push(q.p0.to_vec2());
1289
0
                a.push(q.p1.to_vec2());
1290
0
                a.push(q.p2.to_vec2());
1291
0
            }
1292
0
            PathSeg::Cubic(c) => {
1293
0
                a.push(c.p0.to_vec2());
1294
0
                a.push(c.p1.to_vec2());
1295
0
                a.push(c.p2.to_vec2());
1296
0
                a.push(c.p3.to_vec2());
1297
0
            }
1298
        }
1299
0
        a
1300
0
    }
1301
1302
    /// Minimum distance between two [`PathSeg`]s.
1303
    ///
1304
    /// Returns a tuple of the distance, the path time `t1` of the closest point
1305
    /// on the first `PathSeg`, and the path time `t2` of the closest point on the
1306
    /// second `PathSeg`.
1307
0
    pub fn min_dist(&self, other: PathSeg, accuracy: f64) -> MinDistance {
1308
0
        let (distance, t1, t2) = crate::mindist::min_dist_param(
1309
0
            &self.as_vec2_vec(),
1310
0
            &other.as_vec2_vec(),
1311
0
            (0.0, 1.0),
1312
0
            (0.0, 1.0),
1313
0
            accuracy,
1314
0
            None,
1315
0
        );
1316
0
        MinDistance {
1317
0
            distance: distance.sqrt(),
1318
0
            t1,
1319
0
            t2,
1320
0
        }
1321
0
    }
1322
1323
    /// Compute endpoint tangents of a path segment.
1324
    ///
1325
    /// This version is robust to the path segment not being a regular curve.
1326
0
    pub(crate) fn tangents(&self) -> (Vec2, Vec2) {
1327
        const EPS: f64 = 1e-12;
1328
0
        match self {
1329
0
            PathSeg::Line(l) => {
1330
0
                let d = l.p1 - l.p0;
1331
0
                (d, d)
1332
            }
1333
0
            PathSeg::Quad(q) => {
1334
0
                let d01 = q.p1 - q.p0;
1335
0
                let d0 = if d01.hypot2() > EPS { d01 } else { q.p2 - q.p0 };
1336
0
                let d12 = q.p2 - q.p1;
1337
0
                let d1 = if d12.hypot2() > EPS { d12 } else { q.p2 - q.p0 };
1338
0
                (d0, d1)
1339
            }
1340
0
            PathSeg::Cubic(c) => {
1341
0
                let d01 = c.p1 - c.p0;
1342
0
                let d0 = if d01.hypot2() > EPS {
1343
0
                    d01
1344
                } else {
1345
0
                    let d02 = c.p2 - c.p0;
1346
0
                    if d02.hypot2() > EPS { d02 } else { c.p3 - c.p0 }
1347
                };
1348
0
                let d23 = c.p3 - c.p2;
1349
0
                let d1 = if d23.hypot2() > EPS {
1350
0
                    d23
1351
                } else {
1352
0
                    let d13 = c.p3 - c.p1;
1353
0
                    if d13.hypot2() > EPS { d13 } else { c.p3 - c.p0 }
1354
                };
1355
0
                (d0, d1)
1356
            }
1357
        }
1358
0
    }
1359
}
1360
1361
impl LineIntersection {
1362
    #[inline(always)]
1363
0
    fn new(line_t: f64, segment_t: f64) -> Self {
1364
0
        LineIntersection { line_t, segment_t }
1365
0
    }
1366
1367
    /// Is this line intersection finite?
1368
    #[inline]
1369
0
    pub fn is_finite(self) -> bool {
1370
0
        self.line_t.is_finite() && self.segment_t.is_finite()
1371
0
    }
1372
1373
    /// Is this line intersection NaN?
1374
    #[inline]
1375
0
    pub fn is_nan(self) -> bool {
1376
0
        self.line_t.is_nan() || self.segment_t.is_nan()
1377
0
    }
1378
}
1379
1380
// Return polynomial coefficients given cubic Bézier coordinates.
1381
0
fn quadratic_bez_coefs(x0: f64, x1: f64, x2: f64) -> (f64, f64, f64) {
1382
0
    let p0 = x0;
1383
0
    let p1 = 2.0 * x1 - 2.0 * x0;
1384
0
    let p2 = x2 - 2.0 * x1 + x0;
1385
0
    (p0, p1, p2)
1386
0
}
1387
1388
// Return polynomial coefficients given cubic Bézier coordinates.
1389
0
fn cubic_bez_coefs(x0: f64, x1: f64, x2: f64, x3: f64) -> (f64, f64, f64, f64) {
1390
0
    let p0 = x0;
1391
0
    let p1 = 3.0 * x1 - 3.0 * x0;
1392
0
    let p2 = 3.0 * x2 - 6.0 * x1 + 3.0 * x0;
1393
0
    let p3 = x3 - 3.0 * x2 + 3.0 * x1 - x0;
1394
0
    (p0, p1, p2, p3)
1395
0
}
1396
1397
impl From<CubicBez> for PathSeg {
1398
    #[inline(always)]
1399
0
    fn from(cubic_bez: CubicBez) -> PathSeg {
1400
0
        PathSeg::Cubic(cubic_bez)
1401
0
    }
1402
}
1403
1404
impl From<Line> for PathSeg {
1405
    #[inline(always)]
1406
0
    fn from(line: Line) -> PathSeg {
1407
0
        PathSeg::Line(line)
1408
0
    }
1409
}
1410
1411
impl From<QuadBez> for PathSeg {
1412
    #[inline(always)]
1413
0
    fn from(quad_bez: QuadBez) -> PathSeg {
1414
0
        PathSeg::Quad(quad_bez)
1415
0
    }
1416
}
1417
1418
impl Shape for BezPath {
1419
    type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1420
1421
0
    fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1422
0
        self.0.iter().copied()
1423
0
    }
1424
1425
0
    fn to_path(&self, _tolerance: f64) -> BezPath {
1426
0
        self.clone()
1427
0
    }
1428
1429
    #[inline(always)]
1430
0
    fn into_path(self, _tolerance: f64) -> BezPath {
1431
0
        self
1432
0
    }
1433
1434
    /// Signed area.
1435
0
    fn area(&self) -> f64 {
1436
0
        self.elements().area()
1437
0
    }
1438
1439
0
    fn perimeter(&self, accuracy: f64) -> f64 {
1440
0
        self.elements().perimeter(accuracy)
1441
0
    }
1442
1443
    /// Winding number of point.
1444
0
    fn winding(&self, pt: Point) -> i32 {
1445
0
        self.elements().winding(pt)
1446
0
    }
1447
1448
0
    fn bounding_box(&self) -> Rect {
1449
0
        self.elements().bounding_box()
1450
0
    }
1451
1452
    #[inline(always)]
1453
0
    fn as_path_slice(&self) -> Option<&[PathEl]> {
1454
0
        Some(&self.0)
1455
0
    }
1456
}
1457
1458
impl PathEl {
1459
    /// Is this path element finite?
1460
    #[inline]
1461
0
    pub fn is_finite(&self) -> bool {
1462
0
        match self {
1463
0
            PathEl::MoveTo(p) => p.is_finite(),
1464
0
            PathEl::LineTo(p) => p.is_finite(),
1465
0
            PathEl::QuadTo(p, p2) => p.is_finite() && p2.is_finite(),
1466
0
            PathEl::CurveTo(p, p2, p3) => p.is_finite() && p2.is_finite() && p3.is_finite(),
1467
0
            PathEl::ClosePath => true,
1468
        }
1469
0
    }
1470
1471
    /// Is this path element NaN?
1472
    #[inline]
1473
0
    pub fn is_nan(&self) -> bool {
1474
0
        match self {
1475
0
            PathEl::MoveTo(p) => p.is_nan(),
1476
0
            PathEl::LineTo(p) => p.is_nan(),
1477
0
            PathEl::QuadTo(p, p2) => p.is_nan() || p2.is_nan(),
1478
0
            PathEl::CurveTo(p, p2, p3) => p.is_nan() || p2.is_nan() || p3.is_nan(),
1479
0
            PathEl::ClosePath => false,
1480
        }
1481
0
    }
1482
1483
    /// Get the end point of the path element, if it exists.
1484
0
    pub fn end_point(&self) -> Option<Point> {
1485
0
        match self {
1486
0
            PathEl::MoveTo(p) => Some(*p),
1487
0
            PathEl::LineTo(p1) => Some(*p1),
1488
0
            PathEl::QuadTo(_, p2) => Some(*p2),
1489
0
            PathEl::CurveTo(_, _, p3) => Some(*p3),
1490
0
            PathEl::ClosePath => None,
1491
        }
1492
0
    }
1493
}
1494
1495
/// Implements [`Shape`] for a slice of [`PathEl`], provided that the first element of the slice is
1496
/// not a `PathEl::ClosePath`. If it is, several of these functions will panic.
1497
///
1498
/// If the slice starts with `LineTo`, `QuadTo`, or `CurveTo`, it will be treated as a `MoveTo`.
1499
impl<'a> Shape for &'a [PathEl] {
1500
    type PathElementsIter<'iter>
1501
        = core::iter::Copied<core::slice::Iter<'a, PathEl>>
1502
    where
1503
        'a: 'iter;
1504
1505
    #[inline]
1506
0
    fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1507
0
        self.iter().copied()
1508
0
    }
1509
1510
0
    fn to_path(&self, _tolerance: f64) -> BezPath {
1511
0
        BezPath::from_vec(self.to_vec())
1512
0
    }
1513
1514
    /// Signed area.
1515
0
    fn area(&self) -> f64 {
1516
0
        segments(self.iter().copied()).area()
1517
0
    }
1518
1519
0
    fn perimeter(&self, accuracy: f64) -> f64 {
1520
0
        segments(self.iter().copied()).perimeter(accuracy)
1521
0
    }
1522
1523
    /// Winding number of point.
1524
0
    fn winding(&self, pt: Point) -> i32 {
1525
0
        segments(self.iter().copied()).winding(pt)
1526
0
    }
1527
1528
0
    fn bounding_box(&self) -> Rect {
1529
0
        segments(self.iter().copied()).bounding_box()
1530
0
    }
1531
1532
    #[inline(always)]
1533
0
    fn as_path_slice(&self) -> Option<&[PathEl]> {
1534
0
        Some(self)
1535
0
    }
1536
}
1537
1538
/// Implements [`Shape`] for an array of [`PathEl`], provided that the first element of the array is
1539
/// not a `PathEl::ClosePath`. If it is, several of these functions will panic.
1540
///
1541
/// If the array starts with `LineTo`, `QuadTo`, or `CurveTo`, it will be treated as a `MoveTo`.
1542
impl<const N: usize> Shape for [PathEl; N] {
1543
    type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;
1544
1545
    #[inline]
1546
0
    fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
1547
0
        self.iter().copied()
1548
0
    }
1549
1550
0
    fn to_path(&self, _tolerance: f64) -> BezPath {
1551
0
        BezPath::from_vec(self.to_vec())
1552
0
    }
1553
1554
    /// Signed area.
1555
0
    fn area(&self) -> f64 {
1556
0
        segments(self.iter().copied()).area()
1557
0
    }
1558
1559
0
    fn perimeter(&self, accuracy: f64) -> f64 {
1560
0
        segments(self.iter().copied()).perimeter(accuracy)
1561
0
    }
1562
1563
    /// Winding number of point.
1564
0
    fn winding(&self, pt: Point) -> i32 {
1565
0
        segments(self.iter().copied()).winding(pt)
1566
0
    }
1567
1568
0
    fn bounding_box(&self) -> Rect {
1569
0
        segments(self.iter().copied()).bounding_box()
1570
0
    }
1571
1572
    #[inline(always)]
1573
0
    fn as_path_slice(&self) -> Option<&[PathEl]> {
1574
0
        Some(self)
1575
0
    }
1576
}
1577
1578
/// An iterator for path segments.
1579
pub struct PathSegIter {
1580
    seg: PathSeg,
1581
    ix: usize,
1582
}
1583
1584
impl Shape for PathSeg {
1585
    type PathElementsIter<'iter> = PathSegIter;
1586
1587
    #[inline(always)]
1588
0
    fn path_elements(&self, _tolerance: f64) -> PathSegIter {
1589
0
        PathSegIter { seg: *self, ix: 0 }
1590
0
    }
1591
1592
    /// The area under the curve.
1593
    ///
1594
    /// We could just return `0`, but this seems more useful.
1595
0
    fn area(&self) -> f64 {
1596
0
        self.signed_area()
1597
0
    }
1598
1599
    #[inline]
1600
0
    fn perimeter(&self, accuracy: f64) -> f64 {
1601
0
        self.arclen(accuracy)
1602
0
    }
1603
1604
    #[inline(always)]
1605
0
    fn winding(&self, _pt: Point) -> i32 {
1606
0
        0
1607
0
    }
1608
1609
    #[inline]
1610
0
    fn bounding_box(&self) -> Rect {
1611
0
        ParamCurveExtrema::bounding_box(self)
1612
0
    }
1613
1614
0
    fn as_line(&self) -> Option<Line> {
1615
0
        if let PathSeg::Line(line) = self {
1616
0
            Some(*line)
1617
        } else {
1618
0
            None
1619
        }
1620
0
    }
1621
}
1622
1623
impl Iterator for PathSegIter {
1624
    type Item = PathEl;
1625
1626
0
    fn next(&mut self) -> Option<PathEl> {
1627
0
        self.ix += 1;
1628
0
        match (self.ix, self.seg) {
1629
            // yes I could do some fancy bindings thing here but... :shrug:
1630
0
            (1, PathSeg::Line(seg)) => Some(PathEl::MoveTo(seg.p0)),
1631
0
            (1, PathSeg::Quad(seg)) => Some(PathEl::MoveTo(seg.p0)),
1632
0
            (1, PathSeg::Cubic(seg)) => Some(PathEl::MoveTo(seg.p0)),
1633
0
            (2, PathSeg::Line(seg)) => Some(PathEl::LineTo(seg.p1)),
1634
0
            (2, PathSeg::Quad(seg)) => Some(PathEl::QuadTo(seg.p1, seg.p2)),
1635
0
            (2, PathSeg::Cubic(seg)) => Some(PathEl::CurveTo(seg.p1, seg.p2, seg.p3)),
1636
0
            _ => None,
1637
        }
1638
0
    }
1639
}
1640
1641
#[cfg(test)]
1642
mod tests {
1643
    use crate::{Circle, DEFAULT_ACCURACY};
1644
1645
    use super::*;
1646
1647
    fn assert_approx_eq(x: f64, y: f64) {
1648
        assert!((x - y).abs() < 1e-8, "{x} != {y}");
1649
    }
1650
1651
    #[test]
1652
    #[should_panic(expected = "uninitialized subpath")]
1653
    fn test_elements_to_segments_starts_on_closepath() {
1654
        let mut path = BezPath::new();
1655
        path.close_path();
1656
        path.segments().next();
1657
    }
1658
1659
    #[test]
1660
    fn test_elements_to_segments_closepath_refers_to_last_moveto() {
1661
        let mut path = BezPath::new();
1662
        path.move_to((5.0, 5.0));
1663
        path.line_to((15.0, 15.0));
1664
        path.move_to((10.0, 10.0));
1665
        path.line_to((15.0, 15.0));
1666
        path.close_path();
1667
        assert_eq!(
1668
            path.segments().collect::<Vec<_>>().last(),
1669
            Some(&Line::new((15.0, 15.0), (10.0, 10.0)).into()),
1670
        );
1671
    }
1672
1673
    #[test]
1674
    #[should_panic(expected = "uninitialized subpath")]
1675
    fn test_must_not_start_on_quad() {
1676
        let mut path = BezPath::new();
1677
        path.quad_to((5.0, 5.0), (10.0, 10.0));
1678
        path.line_to((15.0, 15.0));
1679
        path.close_path();
1680
    }
1681
1682
    #[test]
1683
    fn test_intersect_line() {
1684
        let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1685
        let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1686
        let intersection = PathSeg::Line(h_line).intersect_line(v_line)[0];
1687
        assert_approx_eq(intersection.segment_t, 0.1);
1688
        assert_approx_eq(intersection.line_t, 0.5);
1689
1690
        let v_line = Line::new((-10.0, -10.0), (-10.0, 10.0));
1691
        assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1692
1693
        let v_line = Line::new((10.0, 10.0), (10.0, 20.0));
1694
        assert!(PathSeg::Line(h_line).intersect_line(v_line).is_empty());
1695
    }
1696
1697
    #[test]
1698
    fn test_intersect_qad() {
1699
        let q = QuadBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -10.0));
1700
        let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1701
        assert_eq!(PathSeg::Quad(q).intersect_line(v_line).len(), 1);
1702
        let intersection = PathSeg::Quad(q).intersect_line(v_line)[0];
1703
        assert_approx_eq(intersection.segment_t, 0.5);
1704
        assert_approx_eq(intersection.line_t, 0.75);
1705
1706
        let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1707
        assert_eq!(PathSeg::Quad(q).intersect_line(h_line).len(), 2);
1708
    }
1709
1710
    #[test]
1711
    fn test_intersect_cubic() {
1712
        let c = CubicBez::new((0.0, -10.0), (10.0, 20.0), (20.0, -20.0), (30.0, 10.0));
1713
        let v_line = Line::new((10.0, -10.0), (10.0, 10.0));
1714
        assert_eq!(PathSeg::Cubic(c).intersect_line(v_line).len(), 1);
1715
        let intersection = PathSeg::Cubic(c).intersect_line(v_line)[0];
1716
        assert_approx_eq(intersection.segment_t, 0.333333333);
1717
        assert_approx_eq(intersection.line_t, 0.592592592);
1718
1719
        let h_line = Line::new((0.0, 0.0), (100.0, 0.0));
1720
        assert_eq!(PathSeg::Cubic(c).intersect_line(h_line).len(), 3);
1721
    }
1722
1723
    #[test]
1724
    fn test_contains() {
1725
        let mut path = BezPath::new();
1726
        path.move_to((0.0, 0.0));
1727
        path.line_to((1.0, 1.0));
1728
        path.line_to((2.0, 0.0));
1729
        path.close_path();
1730
        assert_eq!(path.winding(Point::new(1.0, 0.5)), -1);
1731
        assert!(path.contains(Point::new(1.0, 0.5)));
1732
    }
1733
1734
    // get_seg(i) should produce the same results as path_segments().nth(i - 1).
1735
    #[test]
1736
    fn test_get_seg() {
1737
        let circle = Circle::new((10.0, 10.0), 2.0).to_path(DEFAULT_ACCURACY);
1738
        let segments = circle.path_segments(DEFAULT_ACCURACY).collect::<Vec<_>>();
1739
        let get_segs = (1..usize::MAX)
1740
            .map_while(|i| circle.get_seg(i))
1741
            .collect::<Vec<_>>();
1742
        assert_eq!(segments, get_segs);
1743
    }
1744
1745
    #[test]
1746
    fn test_control_box() {
1747
        // a sort of map ping looking thing drawn with a single cubic
1748
        // cbox is wildly different than tight box
1749
        let path = BezPath::from_svg("M200,300 C50,50 350,50 200,300").unwrap();
1750
        assert_eq!(Rect::new(50.0, 50.0, 350.0, 300.0), path.control_box());
1751
        assert!(path.control_box().area() > path.bounding_box().area());
1752
    }
1753
1754
    #[test]
1755
    fn test_subpaths() {
1756
        let path = BezPath::from_svg("M10,10 L0,10 L0,0 L10,0 Z M100,100 M30,0 Q35,10,40,0 L30,0")
1757
            .unwrap();
1758
        assert_eq!(
1759
            vec![
1760
                BezPath::from_svg("M10,10 L0,10 L0,0 L10,0 Z").unwrap(),
1761
                BezPath::from_svg("M100,100").unwrap(),
1762
                BezPath::from_svg("M30,0 Q35,10,40,0 L30,0").unwrap(),
1763
            ],
1764
            path.subpaths()
1765
                .map(|sp| BezPath::from_vec(sp.to_vec()))
1766
                .collect::<Vec<_>>()
1767
        );
1768
    }
1769
1770
    #[test]
1771
    fn test_reverse_unclosed() {
1772
        let path = BezPath::from_svg("M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60").unwrap();
1773
        let reversed = path.reverse_subpaths();
1774
        assert_eq!(
1775
            "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10",
1776
            reversed.to_svg()
1777
        );
1778
    }
1779
1780
    #[test]
1781
    fn test_reverse_closed_triangle() {
1782
        let path = BezPath::from_svg("M100,100 L150,200 L50,200 Z").unwrap();
1783
        let reversed = path.reverse_subpaths();
1784
        assert_eq!("M50,200 L150,200 L100,100 Z", reversed.to_svg());
1785
    }
1786
1787
    #[test]
1788
    fn test_reverse_closed_shape() {
1789
        let path = BezPath::from_svg(
1790
            "M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z",
1791
        )
1792
        .unwrap();
1793
        let reversed = path.reverse_subpaths();
1794
        assert_eq!(
1795
            "M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z",
1796
            reversed.to_svg()
1797
        );
1798
    }
1799
1800
    #[test]
1801
    fn test_reverse_multiple_subpaths() {
1802
        let svg = "M10,10 Q40,40 60,10 L100,10 C125,10 150,50 125,60 M100,100 L150,200 L50,200 Z M125,100 Q200,150 175,300 C150,150 50,150 25,300 Q0,150 75,100 L100,50 Z";
1803
        let expected_svg = "M125,60 C150,50 125,10 100,10 L60,10 Q40,40 10,10 M50,200 L150,200 L100,100 Z M100,50 L75,100 Q0,150 25,300 C50,150 150,150 175,300 Q200,150 125,100 Z";
1804
        let path = BezPath::from_svg(svg).unwrap();
1805
        let reversed = path.reverse_subpaths();
1806
        assert_eq!(expected_svg, reversed.to_svg());
1807
    }
1808
1809
    // https://github.com/fonttools/fonttools/blob/bf265ce49e0cae6f032420a4c80c31d8e16285b8/Tests/pens/reverseContourPen_test.py#L7
1810
    #[test]
1811
    fn test_reverse_lines() {
1812
        let mut path = BezPath::new();
1813
        path.move_to((0.0, 0.0));
1814
        path.line_to((1.0, 1.0));
1815
        path.line_to((2.0, 2.0));
1816
        path.line_to((3.0, 3.0));
1817
        path.close_path();
1818
        let rev = path.reverse_subpaths();
1819
        assert_eq!("M3,3 L2,2 L1,1 L0,0 Z", rev.to_svg());
1820
    }
1821
1822
    #[test]
1823
    fn test_reverse_multiple_moves() {
1824
        reverse_test_helper(
1825
            vec![
1826
                PathEl::MoveTo((2.0, 2.0).into()),
1827
                PathEl::MoveTo((3.0, 3.0).into()),
1828
                PathEl::ClosePath,
1829
                PathEl::MoveTo((4.0, 4.0).into()),
1830
            ],
1831
            vec![
1832
                PathEl::MoveTo((2.0, 2.0).into()),
1833
                PathEl::MoveTo((3.0, 3.0).into()),
1834
                PathEl::ClosePath,
1835
                PathEl::MoveTo((4.0, 4.0).into()),
1836
            ],
1837
        );
1838
    }
1839
1840
    // The following are direct port of fonttools'
1841
    // reverseContourPen_test.py::test_reverse_pen, adapted to rust, excluding
1842
    // test cases that don't apply because we don't implement
1843
    // outputImpliedClosingLine=False.
1844
    // https://github.com/fonttools/fonttools/blob/85c80be/Tests/pens/reverseContourPen_test.py#L6-L467
1845
1846
    #[test]
1847
    fn test_reverse_closed_last_line_not_on_move() {
1848
        reverse_test_helper(
1849
            vec![
1850
                PathEl::MoveTo((0.0, 0.0).into()),
1851
                PathEl::LineTo((1.0, 1.0).into()),
1852
                PathEl::LineTo((2.0, 2.0).into()),
1853
                PathEl::LineTo((3.0, 3.0).into()),
1854
                PathEl::ClosePath,
1855
            ],
1856
            vec![
1857
                PathEl::MoveTo((3.0, 3.0).into()),
1858
                PathEl::LineTo((2.0, 2.0).into()),
1859
                PathEl::LineTo((1.0, 1.0).into()),
1860
                PathEl::LineTo((0.0, 0.0).into()), // closing line NOT implied
1861
                PathEl::ClosePath,
1862
            ],
1863
        );
1864
    }
1865
1866
    #[test]
1867
    fn test_reverse_closed_last_line_overlaps_move() {
1868
        reverse_test_helper(
1869
            vec![
1870
                PathEl::MoveTo((0.0, 0.0).into()),
1871
                PathEl::LineTo((1.0, 1.0).into()),
1872
                PathEl::LineTo((2.0, 2.0).into()),
1873
                PathEl::LineTo((0.0, 0.0).into()),
1874
                PathEl::ClosePath,
1875
            ],
1876
            vec![
1877
                PathEl::MoveTo((0.0, 0.0).into()),
1878
                PathEl::LineTo((2.0, 2.0).into()),
1879
                PathEl::LineTo((1.0, 1.0).into()),
1880
                PathEl::LineTo((0.0, 0.0).into()), // closing line NOT implied
1881
                PathEl::ClosePath,
1882
            ],
1883
        );
1884
    }
1885
1886
    #[test]
1887
    fn test_reverse_closed_duplicate_line_following_move() {
1888
        reverse_test_helper(
1889
            vec![
1890
                PathEl::MoveTo((0.0, 0.0).into()),
1891
                PathEl::LineTo((0.0, 0.0).into()),
1892
                PathEl::LineTo((1.0, 1.0).into()),
1893
                PathEl::LineTo((2.0, 2.0).into()),
1894
                PathEl::ClosePath,
1895
            ],
1896
            vec![
1897
                PathEl::MoveTo((2.0, 2.0).into()),
1898
                PathEl::LineTo((1.0, 1.0).into()),
1899
                PathEl::LineTo((0.0, 0.0).into()), // duplicate line retained
1900
                PathEl::LineTo((0.0, 0.0).into()),
1901
                PathEl::ClosePath,
1902
            ],
1903
        );
1904
    }
1905
1906
    #[test]
1907
    fn test_reverse_closed_two_lines() {
1908
        reverse_test_helper(
1909
            vec![
1910
                PathEl::MoveTo((0.0, 0.0).into()),
1911
                PathEl::LineTo((1.0, 1.0).into()),
1912
                PathEl::ClosePath,
1913
            ],
1914
            vec![
1915
                PathEl::MoveTo((1.0, 1.0).into()),
1916
                PathEl::LineTo((0.0, 0.0).into()), // closing line NOT implied
1917
                PathEl::ClosePath,
1918
            ],
1919
        );
1920
    }
1921
1922
    #[test]
1923
    fn test_reverse_closed_last_curve_overlaps_move() {
1924
        reverse_test_helper(
1925
            vec![
1926
                PathEl::MoveTo((0.0, 0.0).into()),
1927
                PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1928
                PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (0.0, 0.0).into()),
1929
                PathEl::ClosePath,
1930
            ],
1931
            vec![
1932
                PathEl::MoveTo((0.0, 0.0).into()), // no extra lineTo added here
1933
                PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1934
                PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1935
                PathEl::ClosePath,
1936
            ],
1937
        );
1938
    }
1939
1940
    #[test]
1941
    fn test_reverse_closed_last_curve_not_on_move() {
1942
        reverse_test_helper(
1943
            vec![
1944
                PathEl::MoveTo((0.0, 0.0).into()),
1945
                PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
1946
                PathEl::CurveTo((4.0, 4.0).into(), (5.0, 5.0).into(), (6.0, 6.0).into()),
1947
                PathEl::ClosePath,
1948
            ],
1949
            vec![
1950
                PathEl::MoveTo((6.0, 6.0).into()), // the previously implied line
1951
                PathEl::CurveTo((5.0, 5.0).into(), (4.0, 4.0).into(), (3.0, 3.0).into()),
1952
                PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
1953
                PathEl::ClosePath,
1954
            ],
1955
        );
1956
    }
1957
1958
    #[test]
1959
    fn test_reverse_closed_line_curve_line() {
1960
        reverse_test_helper(
1961
            vec![
1962
                PathEl::MoveTo((0.0, 0.0).into()),
1963
                PathEl::LineTo((1.0, 1.0).into()), // this line...
1964
                PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
1965
                PathEl::CurveTo((5.0, 5.0).into(), (6.0, 6.0).into(), (7.0, 7.0).into()),
1966
                PathEl::ClosePath,
1967
            ],
1968
            vec![
1969
                PathEl::MoveTo((7.0, 7.0).into()),
1970
                PathEl::CurveTo((6.0, 6.0).into(), (5.0, 5.0).into(), (4.0, 4.0).into()),
1971
                PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
1972
                PathEl::LineTo((0.0, 0.0).into()), // ... does NOT become implied
1973
                PathEl::ClosePath,
1974
            ],
1975
        );
1976
    }
1977
1978
    #[test]
1979
    fn test_reverse_closed_last_quad_overlaps_move() {
1980
        reverse_test_helper(
1981
            vec![
1982
                PathEl::MoveTo((0.0, 0.0).into()),
1983
                PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
1984
                PathEl::QuadTo((3.0, 3.0).into(), (0.0, 0.0).into()),
1985
                PathEl::ClosePath,
1986
            ],
1987
            vec![
1988
                PathEl::MoveTo((0.0, 0.0).into()), // no extra lineTo added here
1989
                PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
1990
                PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
1991
                PathEl::ClosePath,
1992
            ],
1993
        );
1994
    }
1995
1996
    #[test]
1997
    fn test_reverse_closed_last_quad_not_on_move() {
1998
        reverse_test_helper(
1999
            vec![
2000
                PathEl::MoveTo((0.0, 0.0).into()),
2001
                PathEl::QuadTo((1.0, 1.0).into(), (2.0, 2.0).into()),
2002
                PathEl::QuadTo((3.0, 3.0).into(), (4.0, 4.0).into()),
2003
                PathEl::ClosePath,
2004
            ],
2005
            vec![
2006
                PathEl::MoveTo((4.0, 4.0).into()), // the previously implied line
2007
                PathEl::QuadTo((3.0, 3.0).into(), (2.0, 2.0).into()),
2008
                PathEl::QuadTo((1.0, 1.0).into(), (0.0, 0.0).into()),
2009
                PathEl::ClosePath,
2010
            ],
2011
        );
2012
    }
2013
2014
    #[test]
2015
    fn test_reverse_closed_line_quad_line() {
2016
        reverse_test_helper(
2017
            vec![
2018
                PathEl::MoveTo((0.0, 0.0).into()),
2019
                PathEl::LineTo((1.0, 1.0).into()), // this line...
2020
                PathEl::QuadTo((2.0, 2.0).into(), (3.0, 3.0).into()),
2021
                PathEl::ClosePath,
2022
            ],
2023
            vec![
2024
                PathEl::MoveTo((3.0, 3.0).into()),
2025
                PathEl::QuadTo((2.0, 2.0).into(), (1.0, 1.0).into()),
2026
                PathEl::LineTo((0.0, 0.0).into()), // ... does NOT become implied
2027
                PathEl::ClosePath,
2028
            ],
2029
        );
2030
    }
2031
2032
    #[test]
2033
    fn test_reverse_empty() {
2034
        reverse_test_helper(vec![], vec![]);
2035
    }
2036
2037
    #[test]
2038
    fn test_reverse_single_point() {
2039
        reverse_test_helper(
2040
            vec![PathEl::MoveTo((0.0, 0.0).into())],
2041
            vec![PathEl::MoveTo((0.0, 0.0).into())],
2042
        );
2043
    }
2044
2045
    #[test]
2046
    fn test_reverse_single_point_closed() {
2047
        reverse_test_helper(
2048
            vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
2049
            vec![PathEl::MoveTo((0.0, 0.0).into()), PathEl::ClosePath],
2050
        );
2051
    }
2052
2053
    #[test]
2054
    fn test_reverse_single_line_open() {
2055
        reverse_test_helper(
2056
            vec![
2057
                PathEl::MoveTo((0.0, 0.0).into()),
2058
                PathEl::LineTo((1.0, 1.0).into()),
2059
            ],
2060
            vec![
2061
                PathEl::MoveTo((1.0, 1.0).into()),
2062
                PathEl::LineTo((0.0, 0.0).into()),
2063
            ],
2064
        );
2065
    }
2066
2067
    #[test]
2068
    fn test_reverse_single_curve_open() {
2069
        reverse_test_helper(
2070
            vec![
2071
                PathEl::MoveTo((0.0, 0.0).into()),
2072
                PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
2073
            ],
2074
            vec![
2075
                PathEl::MoveTo((3.0, 3.0).into()),
2076
                PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
2077
            ],
2078
        );
2079
    }
2080
2081
    #[test]
2082
    fn test_reverse_curve_line_open() {
2083
        reverse_test_helper(
2084
            vec![
2085
                PathEl::MoveTo((0.0, 0.0).into()),
2086
                PathEl::CurveTo((1.0, 1.0).into(), (2.0, 2.0).into(), (3.0, 3.0).into()),
2087
                PathEl::LineTo((4.0, 4.0).into()),
2088
            ],
2089
            vec![
2090
                PathEl::MoveTo((4.0, 4.0).into()),
2091
                PathEl::LineTo((3.0, 3.0).into()),
2092
                PathEl::CurveTo((2.0, 2.0).into(), (1.0, 1.0).into(), (0.0, 0.0).into()),
2093
            ],
2094
        );
2095
    }
2096
2097
    #[test]
2098
    fn test_reverse_line_curve_open() {
2099
        reverse_test_helper(
2100
            vec![
2101
                PathEl::MoveTo((0.0, 0.0).into()),
2102
                PathEl::LineTo((1.0, 1.0).into()),
2103
                PathEl::CurveTo((2.0, 2.0).into(), (3.0, 3.0).into(), (4.0, 4.0).into()),
2104
            ],
2105
            vec![
2106
                PathEl::MoveTo((4.0, 4.0).into()),
2107
                PathEl::CurveTo((3.0, 3.0).into(), (2.0, 2.0).into(), (1.0, 1.0).into()),
2108
                PathEl::LineTo((0.0, 0.0).into()),
2109
            ],
2110
        );
2111
    }
2112
2113
    #[test]
2114
    fn test_reverse_duplicate_point_after_move() {
2115
        // Test case from: https://github.com/googlei18n/cu2qu/issues/51#issue-179370514
2116
        // Simplified to only use atomic PathEl::QuadTo (no QuadSplines).
2117
        reverse_test_helper(
2118
            vec![
2119
                PathEl::MoveTo((848.0, 348.0).into()),
2120
                PathEl::LineTo((848.0, 348.0).into()),
2121
                PathEl::QuadTo((848.0, 526.0).into(), (449.0, 704.0).into()),
2122
                PathEl::QuadTo((848.0, 171.0).into(), (848.0, 348.0).into()),
2123
                PathEl::ClosePath,
2124
            ],
2125
            vec![
2126
                PathEl::MoveTo((848.0, 348.0).into()),
2127
                PathEl::QuadTo((848.0, 171.0).into(), (449.0, 704.0).into()),
2128
                PathEl::QuadTo((848.0, 526.0).into(), (848.0, 348.0).into()),
2129
                PathEl::LineTo((848.0, 348.0).into()),
2130
                PathEl::ClosePath,
2131
            ],
2132
        );
2133
    }
2134
2135
    #[test]
2136
    fn test_reverse_duplicate_point_at_end() {
2137
        // Test case from: https://github.com/googlefonts/fontmake/issues/572
2138
        reverse_test_helper(
2139
            vec![
2140
                PathEl::MoveTo((0.0, 651.0).into()),
2141
                PathEl::LineTo((0.0, 101.0).into()),
2142
                PathEl::LineTo((0.0, 101.0).into()),
2143
                PathEl::LineTo((0.0, 651.0).into()),
2144
                PathEl::LineTo((0.0, 651.0).into()),
2145
                PathEl::ClosePath,
2146
            ],
2147
            vec![
2148
                PathEl::MoveTo((0.0, 651.0).into()),
2149
                PathEl::LineTo((0.0, 651.0).into()),
2150
                PathEl::LineTo((0.0, 101.0).into()),
2151
                PathEl::LineTo((0.0, 101.0).into()),
2152
                PathEl::LineTo((0.0, 651.0).into()),
2153
                PathEl::ClosePath,
2154
            ],
2155
        );
2156
    }
2157
2158
    fn reverse_test_helper(contour: Vec<PathEl>, expected: Vec<PathEl>) {
2159
        assert_eq!(BezPath(contour).reverse_subpaths().0, expected);
2160
    }
2161
2162
    #[test]
2163
    fn test_rect_segments() {
2164
        // Ensure that `from_path_segments` does not insert spurious MoveTos in
2165
        // the middle of a path.
2166
        let x0 = 25.189500810000002;
2167
        let x1 = 568.18950081;
2168
        let y0 = -105.0;
2169
        let y1 = 176.0;
2170
        let r = Rect::from_points((x0, y0), (x1, y1));
2171
2172
        let path0 = r.into_path(0.0);
2173
        assert!(
2174
            path0
2175
                .elements()
2176
                .iter()
2177
                .skip(1)
2178
                .all(|el| !matches!(el, PathEl::MoveTo(_)))
2179
        );
2180
2181
        let path1 = BezPath::from_path_segments(path0.segments());
2182
        assert!(
2183
            path1
2184
                .elements()
2185
                .iter()
2186
                .skip(1)
2187
                .all(|el| !matches!(el, PathEl::MoveTo(_)))
2188
        );
2189
    }
2190
2191
    #[test]
2192
    fn test_current_position() {
2193
        let mut path = BezPath::new();
2194
        assert_eq!(path.current_position(), None);
2195
        path.move_to((0., 0.));
2196
        assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2197
        path.line_to((10., 10.));
2198
        assert_eq!(path.current_position(), Some(Point::new(10., 10.)));
2199
        path.line_to((10., 0.));
2200
        assert_eq!(path.current_position(), Some(Point::new(10., 0.)));
2201
        path.close_path();
2202
        assert_eq!(path.current_position(), Some(Point::new(0., 0.)));
2203
2204
        path.close_path();
2205
        assert_eq!(path.current_position(), None);
2206
2207
        path.move_to((0., 10.));
2208
        assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2209
        path.close_path();
2210
        assert_eq!(path.current_position(), Some(Point::new(0., 10.)));
2211
        path.close_path();
2212
        assert_eq!(path.current_position(), None);
2213
    }
2214
2215
    // Regression test for #531
2216
    //
2217
    // When testing the winding number of a point whose y coordinate is very
2218
    // close (or equal to) the y coordinate of a segment's start or end, we need
2219
    // to be consistent about how we treat it. In particular, in #531 we noticed
2220
    // that the point was within the y range of a monotonic segment, but because
2221
    // of rounding errors the cubic solver disagreed.
2222
    #[test]
2223
    fn winding_endpoints() {
2224
        let bez = BezPath::from_vec(vec![
2225
            PathEl::MoveTo((200.0, 410.0).into()),
2226
            PathEl::CurveTo(
2227
                (139.0, 410.0).into(),
2228
                (90.0, 360.8772277832031).into(),
2229
                (90.0, 300.0).into(),
2230
            ),
2231
            PathEl::CurveTo(
2232
                (90.0, 239.0).into(),
2233
                (139.0, 190.0).into(),
2234
                (200.0, 190.0).into(),
2235
            ),
2236
            PathEl::CurveTo(
2237
                (150.0, 210.0).into(),
2238
                (110.0, 250.0).into(),
2239
                (110.0, 300.0).into(),
2240
            ),
2241
            PathEl::CurveTo(
2242
                (110.0, 349.0).into(),
2243
                (150.0, 390.0).into(),
2244
                (200.0, 390.0).into(),
2245
            ),
2246
            PathEl::ClosePath,
2247
        ]);
2248
2249
        assert!(bez.contains((100.0, 300.1).into()));
2250
        assert!(bez.contains((100.0, 299.9).into()));
2251
        assert!(bez.contains((100.0, 300.0).into()));
2252
    }
2253
2254
    #[test]
2255
    fn check_close_subpaths() {
2256
        let mut bez = BezPath::new();
2257
        bez.move_to((10.0, 10.0));
2258
        bez.line_to((100.0, 20.0));
2259
        bez.line_to((60.0, 100.0));
2260
        let elements = close_subpaths(&bez).collect::<Vec<_>>();
2261
        bez.close_path();
2262
        assert_eq!(&elements, bez.elements());
2263
2264
        // Test implicit closepath in middle of path
2265
        let mut bez2 = BezPath::new();
2266
        bez2.move_to((10.0, 10.0));
2267
        bez2.line_to((100.0, 20.0));
2268
        bez2.line_to((60.0, 100.0));
2269
        bez2.move_to((110.0, 10.0));
2270
        bez2.line_to((200.0, 20.0));
2271
        bez2.line_to((160.0, 100.0));
2272
        let elements2 = close_subpaths(&bez2).collect::<Vec<_>>();
2273
        let mut bez3 = BezPath::new();
2274
        bez3.move_to((10.0, 10.0));
2275
        bez3.line_to((100.0, 20.0));
2276
        bez3.line_to((60.0, 100.0));
2277
        bez3.close_path();
2278
        bez3.move_to((110.0, 10.0));
2279
        bez3.line_to((200.0, 20.0));
2280
        bez3.line_to((160.0, 100.0));
2281
        bez3.close_path();
2282
        assert_eq!(&elements2, bez3.elements());
2283
    }
2284
2285
    #[test]
2286
    fn close_subpaths_does_not_duplicate_existing_closepath() {
2287
        let path = BezPath::from_vec(vec![
2288
            PathEl::MoveTo((0.0, 0.0).into()),
2289
            PathEl::LineTo((10.0, 0.0).into()),
2290
            PathEl::LineTo((10.0, 10.0).into()),
2291
            PathEl::ClosePath,
2292
            PathEl::MoveTo((20.0, 0.0).into()),
2293
            PathEl::LineTo((30.0, 0.0).into()),
2294
        ]);
2295
2296
        let closed = close_subpaths(path.iter()).collect::<Vec<_>>();
2297
2298
        assert_eq!(
2299
            closed,
2300
            vec![
2301
                PathEl::MoveTo((0.0, 0.0).into()),
2302
                PathEl::LineTo((10.0, 0.0).into()),
2303
                PathEl::LineTo((10.0, 10.0).into()),
2304
                PathEl::ClosePath,
2305
                PathEl::MoveTo((20.0, 0.0).into()),
2306
                PathEl::LineTo((30.0, 0.0).into()),
2307
                PathEl::ClosePath,
2308
            ]
2309
        );
2310
    }
2311
}