Skip to main content

path_kit/
path.rs

1//! 路径类型,由 move/line/quad/cubic/close 组成的 2D 轮廓。
2//! Path type representing 2D contours built from move, line, quad, cubic, and close verbs.
3
4use 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
13/// 路径的 safe 封装,底层为 C++ `pk::SkPath`。
14/// Safe wrapper around `pk::SkPath`.
15pub struct Path {
16    inner: UniquePtr<ffi::Path>,
17}
18
19impl Path {
20    /// 创建空路径。Creates an empty path.
21    pub fn new() -> Self {
22        Self {
23            inner: ffi::path_new(),
24        }
25    }
26
27    /// 从已有路径复制。Creates a copy of the given path.
28    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    /// 清空路径。Empties the path.
47    pub fn reset(&mut self) {
48        ffi::path_reset(self.pin_cpp_mut());
49    }
50
51    /// 是否为空。Returns true if the path has no points.
52    pub fn is_empty(&self) -> bool {
53        ffi::path_count_points(self.as_cpp_ref()) == 0
54    }
55
56    /// 点的数量。Returns the number of points in the path.
57    pub fn count_points(&self) -> i32 {
58        ffi::path_count_points(self.as_cpp_ref())
59    }
60
61    /// 动词的数量。Returns the number of verbs (move, line, quad, cubic, close).
62    pub fn count_verbs(&self) -> i32 {
63        ffi::path_count_verbs(self.as_cpp_ref())
64    }
65
66    /// 迭代路径中的动词与点。Iterates over path verbs and points.
67    ///
68    /// `force_close` 为 true 时,开放轮廓会生成隐式 close。
69    /// When `force_close` is true, open contours generate implicit close.
70    pub fn iter(&self, force_close: bool) -> PathIter<'_> {
71        PathIter::new(self, force_close)
72    }
73
74    /// 获取第 i 个点。Returns the point at index, or None if out of range.
75    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    /// 计算紧密包围盒。Returns the tight axis-aligned bounding box.
87    ///
88    /// 永不失败;空路径返回 (0,0,0,0)。对复杂曲线,`pathops_tight_bounds` 可能更精确但可能返回 None。
89    /// Never fails; empty path returns (0,0,0,0). For complex curves, `pathops_tight_bounds` may be more accurate but can return None.
90    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    /// 最后一段轮廓是否闭合。Returns true if the last contour ends with close().
102    pub fn is_last_contour_closed(&self) -> bool {
103        ffi::path_is_last_contour_closed(self.as_cpp_ref())
104    }
105
106    /// 保守判断是否包含矩形(可能将部分内含矩形判为 false)。
107    /// Conservatively tests rect containment; may return false for some contained rects.
108    ///
109    /// 适用于单段凸轮廓路径。Works for single convex contour paths.
110    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    /// 是否可表示为矩形。Returns Some((rect, is_closed)) if path is a rect, None otherwise.
116    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    /// 是否包含点。Returns true if (x, y) is inside the filled path.
139    ///
140    /// 使用当前 [`fill_type`](Self::fill_type)(新建路径默认为 [`PathFillType::Winding`])。
141    /// Uses current [`fill_type`](Self::fill_type) (new paths default to [`PathFillType::Winding`]).
142    pub fn contains(&self, x: f32, y: f32) -> bool {
143        ffi::path_contains(self.as_cpp_ref(), x, y)
144    }
145
146    /// 当前填充规则。Current fill rule.
147    pub fn fill_type(&self) -> PathFillType {
148        ffi::path_fill_type_bits(self.as_cpp_ref())
149    }
150
151    /// 设置填充规则。Sets fill rule.
152    pub fn set_fill_type(&mut self, ft: PathFillType) {
153        ffi::path_set_fill_type_bits(self.pin_cpp_mut(), ft);
154    }
155
156    /// 是否为反色填充(`InverseWinding` / `InverseEvenOdd`)。
157    /// True if fill type is inverse winding or inverse even-odd.
158    pub fn is_inverse_fill_type(&self) -> bool {
159        self.fill_type().is_inverse()
160    }
161
162    /// 在「普通 / 反色」之间切换(`Winding` ↔ `InverseWinding`,`EvenOdd` ↔ `InverseEvenOdd`)。
163    /// Toggles between normal and inverse fill (winding/even-odd pairs).
164    pub fn toggle_inverse_fill_type(&mut self) {
165        ffi::path_toggle_inverse_fill_type(self.pin_cpp_mut());
166    }
167
168    // ---------- 构建方法 / Construction methods ----------
169
170    /// 移动到 (x, y),开始新轮廓。Moves to (x, y) and starts a new contour.
171    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    /// 画线到 (x, y)。Adds a line from current point to (x, y).
177    ///
178    /// 需先调用 `move_to`;否则 Skia 以 (0, 0) 为隐式起点。
179    /// Requires prior `move_to`; otherwise Skia uses (0, 0) as implicit start.
180    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    /// 二次贝塞尔曲线。Adds a quadratic bezier (control point, end point).
186    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    /// 三次贝塞尔曲线。Adds a cubic bezier (ctrl1, ctrl2, end point).
192    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    /// 闭合当前轮廓。Closes the current contour (line back to first point).
206    pub fn close(&mut self) -> &mut Self {
207        ffi::path_close(self.pin_cpp_mut());
208        self
209    }
210
211    /// 添加矩形。Adds a rectangle as a closed contour.
212    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    /// 添加椭圆(由矩形包围)。Adds an oval (ellipse) bounded by the given rect.
219    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    /// 添加圆。Adds a circle centered at (cx, cy) with given radius.
226    ///
227    /// `radius` 应 ≥ 0;负值时 Skia 行为未定义。
228    /// `radius` should be ≥ 0; negative values have undefined Skia behavior.
229    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    /// 添加圆角矩形。Adds a rounded rectangle (rx, ry = corner radii).
235    ///
236    /// `rx`, `ry` 应 ≥ 0。Should be ≥ 0.
237    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    /// 添加 RRect(支持四角独立半径)。Adds RRect with per-corner radii.
250    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    /// 添加 RRect 并指定起始角。Adds RRect with start corner.
257    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    /// 路径是否可表示为 RRect。Returns Some(rrect) if path is an RRect, None otherwise.
269    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    /// 内部 `ffi::Path` 引用(仅 crate 内使用)。Internal use only.
290    pub(crate) fn as_raw(&self) -> &ffi::Path {
291        self.as_cpp_ref()
292    }
293
294    /// 内部 `ffi::Path` 可变 Pin(仅 crate 内使用)。Internal use only.
295    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}