1use crate::bridge::ffi;
5use crate::matrix::Matrix;
6use crate::path_fill_type::PathFillType;
7use crate::path_iter::PathIter;
8use crate::point::Point;
9use crate::rect::Rect;
10use crate::{Direction, RectCorner};
11use crate::rrect::RRect;
12use cxx::UniquePtr;
13
14pub struct Path {
17 inner: UniquePtr<ffi::Path>,
18}
19
20impl Path {
21 pub fn new() -> Self {
23 Self {
24 inner: ffi::path_new(),
25 }
26 }
27
28 pub fn from_path(other: &Path) -> Self {
30 Self {
31 inner: ffi::path_clone(other.as_cpp_ref()),
32 }
33 }
34
35 pub(crate) fn from_unique_ptr(inner: UniquePtr<ffi::Path>) -> Self {
36 Self { inner }
37 }
38
39 pub(crate) fn as_cpp_ref(&self) -> &ffi::Path {
40 self.inner.as_ref().expect("Path")
41 }
42
43 pub(crate) fn pin_cpp_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
44 self.inner.pin_mut()
45 }
46
47 pub fn reset(&mut self) {
49 ffi::path_reset(self.pin_cpp_mut());
50 }
51
52 pub fn rewind(&mut self) {
55 ffi::path_rewind(self.pin_cpp_mut());
56 }
57
58 pub fn is_empty(&self) -> bool {
61 ffi::path_count_verbs(self.as_cpp_ref()) == 0
62 }
63
64 pub fn count_points(&self) -> i32 {
66 ffi::path_count_points(self.as_cpp_ref())
67 }
68
69 pub fn count_verbs(&self) -> i32 {
71 ffi::path_count_verbs(self.as_cpp_ref())
72 }
73
74 pub fn iter(&self, force_close: bool) -> PathIter<'_> {
79 PathIter::new(self, force_close)
80 }
81
82 pub fn get_point(&self, index: i32) -> Option<Point> {
84 let n = self.count_points();
85 if index >= 0 && index < n {
86 let mut pt = ffi::Point { fX: 0.0, fY: 0.0 };
87 ffi::path_get_point(self.as_cpp_ref(), index, &mut pt);
88 Some(pt.into())
89 } else {
90 None
91 }
92 }
93
94 pub fn tight_bounds(&self) -> Rect {
99 let mut bounds = ffi::Rect {
100 fLeft: 0.0,
101 fTop: 0.0,
102 fRight: 0.0,
103 fBottom: 0.0,
104 };
105 ffi::path_compute_tight_bounds(self.as_cpp_ref(), &mut bounds);
106 bounds.into()
107 }
108
109 pub fn bounds(&self) -> Rect {
112 let mut r = ffi::Rect {
113 fLeft: 0.0,
114 fTop: 0.0,
115 fRight: 0.0,
116 fBottom: 0.0,
117 };
118 ffi::path_get_bounds(self.as_cpp_ref(), &mut r);
119 r.into()
120 }
121
122 pub fn is_finite(&self) -> bool {
125 ffi::path_is_finite(self.as_cpp_ref())
126 }
127
128 pub fn is_convex(&self) -> bool {
131 ffi::path_is_convex(self.as_cpp_ref())
132 }
133
134 pub fn is_oval(&self) -> Option<Rect> {
137 let mut r = ffi::Rect {
138 fLeft: 0.0,
139 fTop: 0.0,
140 fRight: 0.0,
141 fBottom: 0.0,
142 };
143 if ffi::path_is_oval(self.as_cpp_ref(), &mut r) {
144 Some(r.into())
145 } else {
146 None
147 }
148 }
149
150 pub fn is_line(&self) -> Option<(Point, Point)> {
153 let mut p0 = ffi::Point { fX: 0.0, fY: 0.0 };
154 let mut p1 = ffi::Point { fX: 0.0, fY: 0.0 };
155 if ffi::path_is_line(self.as_cpp_ref(), &mut p0, &mut p1) {
156 Some((p0.into(), p1.into()))
157 } else {
158 None
159 }
160 }
161
162 pub fn points(&self) -> Vec<Point> {
165 let mut v = Vec::new();
166 ffi::path_get_points_copy(self.as_cpp_ref(), &mut v);
167 v.into_iter().map(Point::from).collect()
168 }
169
170 pub fn verbs(&self) -> Vec<u8> {
173 let mut v = Vec::new();
174 ffi::path_get_verbs_copy(self.as_cpp_ref(), &mut v);
175 v
176 }
177
178 pub fn inc_reserve(&mut self, extra_pt_count: i32) {
181 ffi::path_inc_reserve(self.pin_cpp_mut(), extra_pt_count);
182 }
183
184 pub fn is_interpolatable_with(&self, other: &Path) -> bool {
187 ffi::path_is_interpolatable(self.as_cpp_ref(), other.as_cpp_ref())
188 }
189
190 pub fn try_interpolate(&self, ending: &Path, weight: f32) -> Option<Path> {
193 let mut out = Path::new();
194 let ok = ffi::path_interpolate(
195 self.as_cpp_ref(),
196 ending.as_cpp_ref(),
197 weight,
198 out.pin_cpp_mut(),
199 );
200 if ok { Some(out) } else { None }
201 }
202
203 pub fn last_pt(&self) -> Option<Point> {
206 let mut p = ffi::Point { fX: 0.0, fY: 0.0 };
207 if ffi::path_get_last_pt(self.as_cpp_ref(), &mut p) {
208 Some(p.into())
209 } else {
210 None
211 }
212 }
213
214 pub fn set_last_pt(&mut self, x: f32, y: f32) {
217 ffi::path_set_last_pt(self.pin_cpp_mut(), x, y);
218 }
219
220 pub fn segment_masks(&self) -> u32 {
223 ffi::path_segment_masks(self.as_cpp_ref())
224 }
225
226 pub fn has_multiple_contours(&self) -> bool {
229 ffi::path_has_multiple_contours(self.as_cpp_ref())
230 }
231
232 pub fn add_path_offset(&mut self, src: &Path, dx: f32, dy: f32, extend: bool) -> &mut Self {
235 ffi::path_add_path_offset(self.pin_cpp_mut(), src.as_cpp_ref(), dx, dy, extend);
236 self
237 }
238
239 pub fn reverse_add_path(&mut self, src: &Path) -> &mut Self {
242 ffi::path_reverse_add_path(self.pin_cpp_mut(), src.as_cpp_ref());
243 self
244 }
245
246 pub fn swap(&mut self, other: &mut Self) {
249 std::mem::swap(&mut self.inner, &mut other.inner);
250 }
251
252 pub fn transform(&mut self, matrix: &Matrix) {
255 ffi::path_transform(self.pin_cpp_mut(), matrix.mat.as_slice());
256 }
257
258 pub fn transformed(&self, matrix: &Matrix) -> Path {
261 let mut out = Path::new();
262 ffi::path_transform_to(self.as_cpp_ref(), matrix.mat.as_slice(), out.pin_cpp_mut());
263 out
264 }
265
266 pub fn is_last_contour_closed(&self) -> bool {
268 ffi::path_is_last_contour_closed(self.as_cpp_ref())
269 }
270
271 pub fn conservatively_contains_rect(&self, rect: &Rect) -> bool {
276 let r: ffi::Rect = (*rect).into();
277 ffi::path_conservatively_contains_rect(self.as_cpp_ref(), &r)
278 }
279
280 pub fn is_rect(&self) -> Option<(Rect, bool)> {
282 let mut out_rect = ffi::Rect {
283 fLeft: 0.0,
284 fTop: 0.0,
285 fRight: 0.0,
286 fBottom: 0.0,
287 };
288 let mut is_closed = false;
289 let mut direction = Direction::Cw;
290 let ok = ffi::path_is_rect(
291 self.as_cpp_ref(),
292 &mut out_rect,
293 &mut is_closed,
294 &mut direction,
295 );
296 if ok {
297 Some((out_rect.into(), is_closed))
298 } else {
299 None
300 }
301 }
302
303 pub fn contains(&self, x: f32, y: f32) -> bool {
308 ffi::path_contains(self.as_cpp_ref(), x, y)
309 }
310
311 pub fn fill_type(&self) -> PathFillType {
313 ffi::path_fill_type_bits(self.as_cpp_ref())
314 }
315
316 pub fn set_fill_type(&mut self, ft: PathFillType) {
318 ffi::path_set_fill_type_bits(self.pin_cpp_mut(), ft);
319 }
320
321 pub fn is_inverse_fill_type(&self) -> bool {
324 self.fill_type().is_inverse()
325 }
326
327 pub fn toggle_inverse_fill_type(&mut self) {
330 ffi::path_toggle_inverse_fill_type(self.pin_cpp_mut());
331 }
332
333 pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
337 ffi::path_move_to(self.pin_cpp_mut(), x, y);
338 self
339 }
340
341 pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
346 ffi::path_line_to(self.pin_cpp_mut(), x, y);
347 self
348 }
349
350 pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> &mut Self {
352 ffi::path_quad_to(self.pin_cpp_mut(), x1, y1, x2, y2);
353 self
354 }
355
356 pub fn cubic_to(
358 &mut self,
359 x1: f32,
360 y1: f32,
361 x2: f32,
362 y2: f32,
363 x3: f32,
364 y3: f32,
365 ) -> &mut Self {
366 ffi::path_cubic_to(self.pin_cpp_mut(), x1, y1, x2, y2, x3, y3);
367 self
368 }
369
370 pub fn conic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, w: f32) -> &mut Self {
373 ffi::path_conic_to(self.pin_cpp_mut(), x1, y1, x2, y2, w);
374 self
375 }
376
377 pub fn arc_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, radius: f32) -> &mut Self {
380 ffi::path_arc_to(self.pin_cpp_mut(), x1, y1, x2, y2, radius);
381 self
382 }
383
384 pub fn add_poly(&mut self, pts: &[Point], close: bool) -> &mut Self {
387 let ffi_pts: Vec<ffi::Point> = pts.iter().copied().map(Into::into).collect();
388 ffi::path_add_poly(self.pin_cpp_mut(), ffi_pts.as_slice(), close);
389 self
390 }
391
392 pub fn close(&mut self) -> &mut Self {
394 ffi::path_close(self.pin_cpp_mut());
395 self
396 }
397
398 pub fn add_rect(&mut self, rect: &Rect, dir: Direction, start: RectCorner) -> &mut Self {
400 let r: ffi::Rect = (*rect).into();
401 ffi::path_add_rect(self.pin_cpp_mut(), &r, dir, start);
402 self
403 }
404
405 pub fn add_oval(&mut self, rect: &Rect, dir: Direction) -> &mut Self {
407 let r: ffi::Rect = (*rect).into();
408 ffi::path_add_oval(self.pin_cpp_mut(), &r, dir);
409 self
410 }
411
412 pub fn add_oval_with_start(
415 &mut self,
416 rect: &Rect,
417 dir: Direction,
418 start: RectCorner,
419 ) -> &mut Self {
420 let r: ffi::Rect = (*rect).into();
421 ffi::path_add_oval_start(self.pin_cpp_mut(), &r, dir, start);
422 self
423 }
424
425 pub fn add_circle(&mut self, cx: f32, cy: f32, radius: f32, dir: Direction) -> &mut Self {
430 ffi::path_add_circle(self.pin_cpp_mut(), cx, cy, radius, dir);
431 self
432 }
433
434 pub fn add_round_rect(
438 &mut self,
439 rect: &Rect,
440 rx: f32,
441 ry: f32,
442 dir: Direction,
443 ) -> &mut Self {
444 let r: ffi::Rect = (*rect).into();
445 ffi::path_add_round_rect(self.pin_cpp_mut(), &r, rx, ry, dir);
446 self
447 }
448
449 pub fn add_rrect(&mut self, rrect: &RRect, dir: Direction) -> &mut Self {
451 let rr = rrect.as_ffi();
452 ffi::path_add_rrect(self.pin_cpp_mut(), &rr, dir);
453 self
454 }
455
456 pub fn add_rrect_with_start(
458 &mut self,
459 rrect: &RRect,
460 dir: Direction,
461 start: RectCorner,
462 ) -> &mut Self {
463 let rr = rrect.as_ffi();
464 ffi::path_add_rrect_start(self.pin_cpp_mut(), &rr, dir, start);
465 self
466 }
467
468 pub fn is_rrect(&self) -> Option<RRect> {
470 let mut out = ffi::RRect {
471 fRect: ffi::Rect {
472 fLeft: 0.0,
473 fTop: 0.0,
474 fRight: 0.0,
475 fBottom: 0.0,
476 },
477 fRadii: [ffi::Point { fX: 0.0, fY: 0.0 }; 4],
478 fType: ffi::RRectType::Empty,
479 };
480 ffi::rrect_new_empty(&mut out);
481 let ok = ffi::path_is_rrect(self.as_cpp_ref(), &mut out);
482 if ok {
483 Some(RRect::from_ffi(out))
484 } else {
485 None
486 }
487 }
488
489 pub(crate) fn as_raw(&self) -> &ffi::Path {
491 self.as_cpp_ref()
492 }
493
494 pub(crate) fn as_raw_pin_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
496 self.pin_cpp_mut()
497 }
498}
499
500impl Default for Path {
501 fn default() -> Self {
502 Self::new()
503 }
504}
505
506impl Clone for Path {
507 fn clone(&self) -> Self {
508 Self::from_path(self)
509 }
510}
511
512impl PartialEq for Path {
515 fn eq(&self, other: &Self) -> bool {
516 ffi::path_equals(self.as_cpp_ref(), other.as_cpp_ref())
517 }
518}
519
520impl Eq for Path {}
521
522impl std::fmt::Debug for Path {
523 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524 f.debug_struct("Path")
525 .field("fill_type", &self.fill_type())
526 .field("points", &self.count_points())
527 .field("verbs", &self.count_verbs())
528 .field("bounds", &self.bounds())
529 .finish()
530 }
531}