Coverage Report

Created: 2026-02-14 06:54

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