skia_safe/core/
path_measure.rs

1use crate::{prelude::*, scalar, ContourMeasure, Matrix, Path, Point, Vector};
2use skia_bindings::{self as sb, SkPathMeasure};
3use std::fmt;
4
5pub type PathMeasure = Handle<SkPathMeasure>;
6
7impl NativeDrop for SkPathMeasure {
8    fn drop(&mut self) {
9        unsafe { sb::C_SkPathMeasure_destruct(self) }
10    }
11}
12
13bitflags! {
14    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15    pub struct MatrixFlags : u32 {
16        const GET_POSITION = sb::SkPathMeasure_MatrixFlags_kGetPosition_MatrixFlag as _;
17        const GET_TANGENT = sb::SkPathMeasure_MatrixFlags_kGetTangent_MatrixFlag as _;
18        const GET_POS_AND_TAN = Self::GET_POSITION.bits() | Self::GET_TANGENT.bits();
19    }
20}
21
22impl Default for MatrixFlags {
23    fn default() -> Self {
24        Self::GET_POS_AND_TAN
25    }
26}
27
28impl Default for PathMeasure {
29    fn default() -> Self {
30        Self::from_native_c(unsafe { SkPathMeasure::new() })
31    }
32}
33
34impl fmt::Debug for PathMeasure {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        f.debug_struct("PathMeasure")
37            // TODO: self must be mut
38            // .field("length", &self.length())
39            // .field("is_closed", &self.is_closed())
40            // .field("next_contour", &self.next_contour())
41            .field("current_measure", &self.current_measure())
42            .finish()
43    }
44}
45
46/// Warning: Even if you pass in a `PathMeasure` with multiple contours, most of this struct's functions, including `length` only return the value for the first contour on the path (which is why they aren't `const`). You must exhaust `PathMeasure::next_contour`.
47///
48/// ```
49/// use skia_safe::{PathMeasure, Point, Path};
50/// use std::f64::consts::PI;
51/// let mut path = Path::circle((0., 0.), 10.0, None);
52/// path.add_path(&Path::circle((100., 100.), 27.0, None), Point::default(), None);
53/// let mut measure = PathMeasure::new(&path, false, None);
54/// let mut lengths = vec![measure.length()];
55/// while measure.next_contour() {
56///     lengths.push(measure.length());
57/// }
58/// assert_eq!(*lengths.first().unwrap() as i64, (2. * PI * 10.0) as i64);
59/// assert_eq!(*lengths.get(1).unwrap() as i64, (2. * PI * 27.0) as i64);
60/// eprintln!("Circle lengths: {:?}", &lengths);
61/// ```
62impl PathMeasure {
63    pub fn new(path: &Path, force_closed: bool, res_scale: impl Into<Option<scalar>>) -> Self {
64        Self::from_native_c(unsafe {
65            SkPathMeasure::new1(path.native(), force_closed, res_scale.into().unwrap_or(1.0))
66        })
67    }
68
69    #[deprecated(since = "0.48.0", note = "Use PathMeasure::new")]
70    pub fn from_path(
71        path: &Path,
72        force_closed: bool,
73        res_scale: impl Into<Option<scalar>>,
74    ) -> Self {
75        Self::new(path, force_closed, res_scale)
76    }
77
78    pub fn set_path(&mut self, path: &Path, force_closed: bool) -> &mut Self {
79        unsafe { self.native_mut().setPath(path.native(), force_closed) }
80        self
81    }
82
83    pub fn length(&mut self) -> scalar {
84        unsafe { self.native_mut().getLength() }
85    }
86
87    // TODO: rename to get_pos_tan(), because the function expects arguments?
88    #[must_use]
89    pub fn pos_tan(&mut self, distance: scalar) -> Option<(Point, Vector)> {
90        let mut position = Point::default();
91        let mut tangent = Vector::default();
92        unsafe {
93            self.native_mut()
94                .getPosTan(distance, position.native_mut(), tangent.native_mut())
95        }
96        .if_true_some((position, tangent))
97    }
98
99    // TODO: rename to get_matrix(), because the function expects arguments?
100    #[must_use]
101    pub fn matrix(
102        &mut self,
103        distance: scalar,
104        flags: impl Into<Option<MatrixFlags>>,
105    ) -> Option<Matrix> {
106        let mut m = Matrix::default();
107        unsafe {
108            self.native_mut().getMatrix(
109                distance,
110                m.native_mut(),
111                // note: depending on the OS, different representation types are generated for MatrixFlags
112                #[allow(clippy::useless_conversion)]
113                flags.into().unwrap_or_default().bits().try_into().unwrap(),
114            )
115        }
116        .if_true_some(m)
117    }
118
119    // TODO: rename to get_segment(), because the function has arguments?
120    pub fn segment(
121        &mut self,
122        start_d: scalar,
123        stop_d: scalar,
124        start_with_move_to: bool,
125    ) -> Option<Path> {
126        let mut p = Path::default();
127        unsafe {
128            self.native_mut()
129                .getSegment(start_d, stop_d, p.native_mut(), start_with_move_to)
130        }
131        .if_true_some(p)
132    }
133
134    #[allow(clippy::wrong_self_convention)]
135    pub fn is_closed(&mut self) -> bool {
136        unsafe { self.native_mut().isClosed() }
137    }
138
139    // TODO: rename to has_next_contour()?
140    pub fn next_contour(&mut self) -> bool {
141        unsafe { self.native_mut().nextContour() }
142    }
143
144    pub fn current_measure(&self) -> &Option<ContourMeasure> {
145        ContourMeasure::from_unshared_ptr_ref(&self.native().fContour.fPtr)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use crate::{Path, PathMeasure, Point};
152
153    #[test]
154    fn current_measure() {
155        let mut path = Path::circle((0., 0.), 10.0, None);
156        path.add_path(
157            &Path::circle((100., 100.), 27.0, None),
158            Point::default(),
159            None,
160        );
161        let mut measure = PathMeasure::new(&path, false, None);
162        while measure.next_contour() {
163            eprintln!("contour: {:?}", measure.current_measure());
164        }
165        assert!(measure.current_measure().is_none());
166    }
167}