| 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 | |