1 | use crate::{prelude::*, scalar, ContourMeasure, Matrix, Path, Point, Vector}; |
2 | use skia_bindings::{self as sb, SkPathMeasure}; |
3 | use std::fmt; |
4 | |
5 | pub type PathMeasure = Handle<SkPathMeasure>; |
6 | |
7 | impl NativeDrop for SkPathMeasure { |
8 | fn drop(&mut self) { |
9 | unsafe { sb::C_SkPathMeasure_destruct(self) } |
10 | } |
11 | } |
12 | |
13 | bitflags! { |
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 | |
22 | impl Default for MatrixFlags { |
23 | fn default() -> Self { |
24 | Self::GET_POS_AND_TAN |
25 | } |
26 | } |
27 | |
28 | impl Default for PathMeasure { |
29 | fn default() -> Self { |
30 | Self::from_native_c(unsafe { SkPathMeasure::new() }) |
31 | } |
32 | } |
33 | |
34 | impl fmt::Debug for PathMeasure { |
35 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
36 | f&mut DebugStruct<'_, '_>.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(name:"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 | /// ``` |
62 | impl 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)] |
150 | mod 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 | } |
168 | |