1use crate::bridge::ffi;
5use crate::path_fill_type::PathFillType;
6use crate::path_iter::PathIter;
7use crate::point::Point;
8use crate::rect::Rect;
9use crate::{Direction, RectCorner};
10use crate::rrect::RRect;
11use cxx::UniquePtr;
12
13pub struct Path {
16 inner: UniquePtr<ffi::Path>,
17}
18
19impl Path {
20 pub fn new() -> Self {
22 Self {
23 inner: ffi::path_new(),
24 }
25 }
26
27 pub fn from_path(other: &Path) -> Self {
29 Self {
30 inner: ffi::path_clone(other.as_cpp_ref()),
31 }
32 }
33
34 pub(crate) fn from_unique_ptr(inner: UniquePtr<ffi::Path>) -> Self {
35 Self { inner }
36 }
37
38 pub(crate) fn as_cpp_ref(&self) -> &ffi::Path {
39 self.inner.as_ref().expect("Path")
40 }
41
42 pub(crate) fn pin_cpp_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
43 self.inner.pin_mut()
44 }
45
46 pub fn reset(&mut self) {
48 ffi::path_reset(self.pin_cpp_mut());
49 }
50
51 pub fn is_empty(&self) -> bool {
53 ffi::path_count_points(self.as_cpp_ref()) == 0
54 }
55
56 pub fn count_points(&self) -> i32 {
58 ffi::path_count_points(self.as_cpp_ref())
59 }
60
61 pub fn count_verbs(&self) -> i32 {
63 ffi::path_count_verbs(self.as_cpp_ref())
64 }
65
66 pub fn iter(&self, force_close: bool) -> PathIter<'_> {
71 PathIter::new(self, force_close)
72 }
73
74 pub fn get_point(&self, index: i32) -> Option<Point> {
76 let n = self.count_points();
77 if index >= 0 && index < n {
78 let mut pt = ffi::Point { fX: 0.0, fY: 0.0 };
79 ffi::path_get_point(self.as_cpp_ref(), index, &mut pt);
80 Some(pt.into())
81 } else {
82 None
83 }
84 }
85
86 pub fn tight_bounds(&self) -> Rect {
91 let mut bounds = ffi::Rect {
92 fLeft: 0.0,
93 fTop: 0.0,
94 fRight: 0.0,
95 fBottom: 0.0,
96 };
97 ffi::path_compute_tight_bounds(self.as_cpp_ref(), &mut bounds);
98 bounds.into()
99 }
100
101 pub fn is_last_contour_closed(&self) -> bool {
103 ffi::path_is_last_contour_closed(self.as_cpp_ref())
104 }
105
106 pub fn conservatively_contains_rect(&self, rect: &Rect) -> bool {
111 let r: ffi::Rect = (*rect).into();
112 ffi::path_conservatively_contains_rect(self.as_cpp_ref(), &r)
113 }
114
115 pub fn is_rect(&self) -> Option<(Rect, bool)> {
117 let mut out_rect = ffi::Rect {
118 fLeft: 0.0,
119 fTop: 0.0,
120 fRight: 0.0,
121 fBottom: 0.0,
122 };
123 let mut is_closed = false;
124 let mut direction = Direction::Cw;
125 let ok = ffi::path_is_rect(
126 self.as_cpp_ref(),
127 &mut out_rect,
128 &mut is_closed,
129 &mut direction,
130 );
131 if ok {
132 Some((out_rect.into(), is_closed))
133 } else {
134 None
135 }
136 }
137
138 pub fn contains(&self, x: f32, y: f32) -> bool {
143 ffi::path_contains(self.as_cpp_ref(), x, y)
144 }
145
146 pub fn fill_type(&self) -> PathFillType {
148 ffi::path_fill_type_bits(self.as_cpp_ref())
149 }
150
151 pub fn set_fill_type(&mut self, ft: PathFillType) {
153 ffi::path_set_fill_type_bits(self.pin_cpp_mut(), ft);
154 }
155
156 pub fn is_inverse_fill_type(&self) -> bool {
159 self.fill_type().is_inverse()
160 }
161
162 pub fn toggle_inverse_fill_type(&mut self) {
165 ffi::path_toggle_inverse_fill_type(self.pin_cpp_mut());
166 }
167
168 pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
172 ffi::path_move_to(self.pin_cpp_mut(), x, y);
173 self
174 }
175
176 pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
181 ffi::path_line_to(self.pin_cpp_mut(), x, y);
182 self
183 }
184
185 pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> &mut Self {
187 ffi::path_quad_to(self.pin_cpp_mut(), x1, y1, x2, y2);
188 self
189 }
190
191 pub fn cubic_to(
193 &mut self,
194 x1: f32,
195 y1: f32,
196 x2: f32,
197 y2: f32,
198 x3: f32,
199 y3: f32,
200 ) -> &mut Self {
201 ffi::path_cubic_to(self.pin_cpp_mut(), x1, y1, x2, y2, x3, y3);
202 self
203 }
204
205 pub fn close(&mut self) -> &mut Self {
207 ffi::path_close(self.pin_cpp_mut());
208 self
209 }
210
211 pub fn add_rect(&mut self, rect: &Rect, dir: Direction, start: RectCorner) -> &mut Self {
213 let r: ffi::Rect = (*rect).into();
214 ffi::path_add_rect(self.pin_cpp_mut(), &r, dir, start);
215 self
216 }
217
218 pub fn add_oval(&mut self, rect: &Rect, dir: Direction) -> &mut Self {
220 let r: ffi::Rect = (*rect).into();
221 ffi::path_add_oval(self.pin_cpp_mut(), &r, dir);
222 self
223 }
224
225 pub fn add_circle(&mut self, cx: f32, cy: f32, radius: f32, dir: Direction) -> &mut Self {
230 ffi::path_add_circle(self.pin_cpp_mut(), cx, cy, radius, dir);
231 self
232 }
233
234 pub fn add_round_rect(
238 &mut self,
239 rect: &Rect,
240 rx: f32,
241 ry: f32,
242 dir: Direction,
243 ) -> &mut Self {
244 let r: ffi::Rect = (*rect).into();
245 ffi::path_add_round_rect(self.pin_cpp_mut(), &r, rx, ry, dir);
246 self
247 }
248
249 pub fn add_rrect(&mut self, rrect: &RRect, dir: Direction) -> &mut Self {
251 let rr = rrect.as_ffi();
252 ffi::path_add_rrect(self.pin_cpp_mut(), &rr, dir);
253 self
254 }
255
256 pub fn add_rrect_with_start(
258 &mut self,
259 rrect: &RRect,
260 dir: Direction,
261 start: RectCorner,
262 ) -> &mut Self {
263 let rr = rrect.as_ffi();
264 ffi::path_add_rrect_start(self.pin_cpp_mut(), &rr, dir, start);
265 self
266 }
267
268 pub fn is_rrect(&self) -> Option<RRect> {
270 let mut out = ffi::RRect {
271 fRect: ffi::Rect {
272 fLeft: 0.0,
273 fTop: 0.0,
274 fRight: 0.0,
275 fBottom: 0.0,
276 },
277 fRadii: [ffi::Point { fX: 0.0, fY: 0.0 }; 4],
278 fType: ffi::RRectType::Empty,
279 };
280 ffi::rrect_new_empty(&mut out);
281 let ok = ffi::path_is_rrect(self.as_cpp_ref(), &mut out);
282 if ok {
283 Some(RRect::from_ffi(out))
284 } else {
285 None
286 }
287 }
288
289 pub(crate) fn as_raw(&self) -> &ffi::Path {
291 self.as_cpp_ref()
292 }
293
294 pub(crate) fn as_raw_pin_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
296 self.pin_cpp_mut()
297 }
298}
299
300impl Default for Path {
301 fn default() -> Self {
302 Self::new()
303 }
304}
305
306impl Clone for Path {
307 fn clone(&self) -> Self {
308 Self::from_path(self)
309 }
310}
311
312impl std::fmt::Debug for Path {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 f.debug_struct("Path")
315 .field("fill_type", &self.fill_type())
316 .field("points", &self.count_points())
317 .field("verbs", &self.count_verbs())
318 .field("bounds", &self.tight_bounds())
319 .finish()
320 }
321}