skia_safe/core/
path_measure.rs

1use crate::{prelude::*, scalar, ContourMeasure, Matrix, Path, PathBuilder, 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        .then_some((position, tangent))
97    }
98
99    #[deprecated(since = "0.88.0", note = "Use get_matrix()")]
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        .then_some(m)
117    }
118
119    #[must_use]
120    pub fn get_matrix(
121        &mut self,
122        distance: scalar,
123        matrix: &mut Matrix,
124        flags: impl Into<Option<MatrixFlags>>,
125    ) -> bool {
126        unsafe {
127            self.native_mut().getMatrix(
128                distance,
129                matrix.native_mut(),
130                // note: depending on the OS, different representation types are generated for MatrixFlags
131                #[allow(clippy::useless_conversion)]
132                flags.into().unwrap_or_default().bits().try_into().unwrap(),
133            )
134        }
135    }
136
137    #[deprecated(since = "0.88.0", note = "Use get_segment()")]
138    pub fn segment(
139        &mut self,
140        start_d: scalar,
141        stop_d: scalar,
142        start_with_move_to: bool,
143    ) -> Option<Path> {
144        let mut p = PathBuilder::default();
145        unsafe {
146            self.native_mut()
147                .getSegment(start_d, stop_d, p.native_mut(), start_with_move_to)
148        }
149        .then_some(p.detach())
150    }
151
152    pub fn get_segment(
153        &mut self,
154        start_d: scalar,
155        stop_d: scalar,
156        dst: &mut PathBuilder,
157        start_with_move_to: bool,
158    ) -> bool {
159        unsafe {
160            self.native_mut()
161                .getSegment(start_d, stop_d, dst.native_mut(), start_with_move_to)
162        }
163    }
164
165    #[allow(clippy::wrong_self_convention)]
166    pub fn is_closed(&mut self) -> bool {
167        unsafe { self.native_mut().isClosed() }
168    }
169
170    // TODO: rename to has_next_contour()?
171    pub fn next_contour(&mut self) -> bool {
172        unsafe { self.native_mut().nextContour() }
173    }
174
175    pub fn current_measure(&self) -> &Option<ContourMeasure> {
176        ContourMeasure::from_unshared_ptr_ref(&self.native().fContour.fPtr)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use crate::{Path, PathMeasure, Point};
183
184    #[test]
185    fn current_measure() {
186        let mut path = Path::circle((0., 0.), 10.0, None);
187        path.add_path(
188            &Path::circle((100., 100.), 27.0, None),
189            Point::default(),
190            None,
191        );
192        let mut measure = PathMeasure::new(&path, false, None);
193        while measure.next_contour() {
194            eprintln!("contour: {:?}", measure.current_measure());
195        }
196        assert!(measure.current_measure().is_none());
197    }
198}