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::direction::Direction;
5use crate::path_iter::PathIter;
6use crate::pathkit;
7use crate::point::Point;
8use crate::rect::Rect;
9use crate::rect_corner::RectCorner;
10use crate::rrect::RRect;
11
12/// 路径的 safe 封装,对应 SkPath。Safe wrapper for SkPath.
13pub struct Path {
14    inner: pathkit::SkPath,
15}
16
17impl Path {
18    /// 创建空路径。Creates an empty path.
19    pub fn new() -> Self {
20        Self {
21            inner: unsafe { pathkit::SkPath::new() },
22        }
23    }
24
25    /// 从已有路径复制。Creates a copy of the given path.
26    pub fn from_path(other: &Path) -> Self {
27        Self {
28            inner: unsafe { pathkit::SkPath::new1(&other.inner as *const _) },
29        }
30    }
31
32    /// 清空路径。Empties the path.
33    pub fn reset(&mut self) {
34        unsafe {
35            self.inner.reset();
36        }
37    }
38
39    /// 是否为空。Returns true if the path has no points.
40    pub fn is_empty(&self) -> bool {
41        unsafe { self.inner.countPoints() == 0 }
42    }
43
44    /// 点的数量。Returns the number of points in the path.
45    pub fn count_points(&self) -> i32 {
46        unsafe { self.inner.countPoints() }
47    }
48
49    /// 动词的数量。Returns the number of verbs (move, line, quad, cubic, close).
50    pub fn count_verbs(&self) -> i32 {
51        unsafe { self.inner.countVerbs() }
52    }
53
54    /// 迭代路径中的动词与点。Iterates over path verbs and points.
55    ///
56    /// `force_close` 为 true 时,开放轮廓会生成隐式 close。
57    /// When `force_close` is true, open contours generate implicit close.
58    pub fn iter(&self, force_close: bool) -> PathIter<'_> {
59        PathIter::new(self, force_close)
60    }
61
62    /// 获取第 i 个点。Returns the point at index, or None if out of range.
63    pub fn get_point(&self, index: i32) -> Option<Point> {
64        let n = self.count_points();
65        if index >= 0 && index < n {
66            let pt = unsafe { self.inner.getPoint(index) };
67            Some(pt.into())
68        } else {
69            None
70        }
71    }
72
73    /// 计算紧密包围盒。Returns the tight axis-aligned bounding box.
74    ///
75    /// 永不失败;空路径返回 (0,0,0,0)。对复杂曲线,`pathops_tight_bounds` 可能更精确但可能返回 None。
76    /// Never fails; empty path returns (0,0,0,0). For complex curves, `pathops_tight_bounds` may be more accurate but can return None.
77    pub fn tight_bounds(&self) -> Rect {
78        let bounds = unsafe { self.inner.computeTightBounds() };
79        bounds.into()
80    }
81
82    /// 最后一段轮廓是否闭合。Returns true if the last contour ends with close().
83    pub fn is_last_contour_closed(&self) -> bool {
84        unsafe { self.inner.isLastContourClosed() }
85    }
86
87    /// 保守判断是否包含矩形。May return false for some contained rects.
88    /// Works for single convex contour paths.
89    pub fn conservatively_contains_rect(&self, rect: &Rect) -> bool {
90        let r: pathkit::SkRect = (*rect).into();
91        unsafe { self.inner.conservativelyContainsRect(&r) }
92    }
93
94    /// 是否可表示为矩形。Returns Some((rect, is_closed)) if path is a rect, None otherwise.
95    pub fn is_rect(&self) -> Option<(Rect, bool)> {
96        let mut out_rect = pathkit::SkRect {
97            fLeft: 0.0,
98            fTop: 0.0,
99            fRight: 0.0,
100            fBottom: 0.0,
101        };
102        let mut is_closed = false;
103        let mut direction = pathkit::SkPathDirection::kCW;
104        let ok = unsafe {
105            self.inner.isRect(
106                &mut out_rect as *mut _,
107                &mut is_closed as *mut _,
108                &mut direction as *mut _,
109            )
110        };
111        if ok {
112            Some((out_rect.into(), is_closed))
113        } else {
114            None
115        }
116    }
117
118    /// 是否包含点。Returns true if (x, y) is inside the filled path.
119    ///
120    /// 使用 SkPath 当前 fill type(默认 even-odd)。
121    /// Uses SkPath's current fill type (default even-odd).
122    pub fn contains(&self, x: f32, y: f32) -> bool {
123        unsafe { self.inner.contains(x, y) }
124    }
125
126    // ---------- 构建方法 / Construction methods ----------
127
128    /// 移动到 (x, y),开始新轮廓。Moves to (x, y) and starts a new contour.
129    pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
130        unsafe {
131            self.inner.moveTo(x, y);
132        }
133        self
134    }
135
136    /// 画线到 (x, y)。Adds a line from current point to (x, y).
137    ///
138    /// 需先调用 `move_to`;否则 Skia 以 (0, 0) 为隐式起点。
139    /// Requires prior `move_to`; otherwise Skia uses (0, 0) as implicit start.
140    pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
141        unsafe {
142            self.inner.lineTo(x, y);
143        }
144        self
145    }
146
147    /// 二次贝塞尔曲线。Adds a quadratic bezier (control point, end point).
148    pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> &mut Self {
149        unsafe {
150            self.inner.quadTo(x1, y1, x2, y2);
151        }
152        self
153    }
154
155    /// 三次贝塞尔曲线。Adds a cubic bezier (ctrl1, ctrl2, end point).
156    pub fn cubic_to(
157        &mut self,
158        x1: f32,
159        y1: f32,
160        x2: f32,
161        y2: f32,
162        x3: f32,
163        y3: f32,
164    ) -> &mut Self {
165        unsafe {
166            self.inner.cubicTo(x1, y1, x2, y2, x3, y3);
167        }
168        self
169    }
170
171    /// 闭合当前轮廓。Closes the current contour (line back to first point).
172    pub fn close(&mut self) -> &mut Self {
173        unsafe {
174            self.inner.close();
175        }
176        self
177    }
178
179    /// 添加矩形。Adds a rectangle as a closed contour.
180    pub fn add_rect(&mut self, rect: &Rect, dir: Direction, start: RectCorner) -> &mut Self {
181        let r: pathkit::SkRect = (*rect).into();
182        unsafe {
183            self.inner.addRect(&r, dir.into(), start.into());
184        }
185        self
186    }
187
188    /// 添加椭圆(由矩形包围)。Adds an oval (ellipse) bounded by the given rect.
189    pub fn add_oval(&mut self, rect: &Rect, dir: Direction) -> &mut Self {
190        let r: pathkit::SkRect = (*rect).into();
191        unsafe {
192            self.inner.addOval(&r, dir.into());
193        }
194        self
195    }
196
197    /// 添加圆。Adds a circle centered at (cx, cy) with given radius.
198    ///
199    /// `radius` 应 ≥ 0;负值时 Skia 行为未定义。
200    /// `radius` should be ≥ 0; negative values have undefined Skia behavior.
201    pub fn add_circle(&mut self, cx: f32, cy: f32, radius: f32, dir: Direction) -> &mut Self {
202        unsafe {
203            self.inner.addCircle(cx, cy, radius, dir.into());
204        }
205        self
206    }
207
208    /// 添加圆角矩形。Adds a rounded rectangle (rx, ry = corner radii).
209    ///
210    /// `rx`, `ry` 应 ≥ 0。Should be ≥ 0.
211    pub fn add_round_rect(
212        &mut self,
213        rect: &Rect,
214        rx: f32,
215        ry: f32,
216        dir: Direction,
217    ) -> &mut Self {
218        let r: pathkit::SkRect = (*rect).into();
219        unsafe {
220            self.inner.addRoundRect(&r, rx, ry, dir.into());
221        }
222        self
223    }
224
225    /// 添加 RRect(支持四角独立半径)。Adds RRect with per-corner radii.
226    pub fn add_rrect(&mut self, rrect: &RRect, dir: Direction) -> &mut Self {
227        unsafe {
228            self.inner.addRRect(rrect.as_raw() as *const _, dir.into());
229        }
230        self
231    }
232
233    /// 添加 RRect 并指定起始角。Adds RRect with start corner.
234    pub fn add_rrect_with_start(
235        &mut self,
236        rrect: &RRect,
237        dir: Direction,
238        start: RectCorner,
239    ) -> &mut Self {
240        unsafe {
241            self.inner.addRRect1(rrect.as_raw() as *const _, dir.into(), start.into());
242        }
243        self
244    }
245
246    /// 路径是否可表示为 RRect。Returns Some(rrect) if path is an RRect, None otherwise.
247    pub fn is_rrect(&self) -> Option<RRect> {
248        let mut out = pathkit::SkRRect {
249            fRect: pathkit::SkRect {
250                fLeft: 0.0,
251                fTop: 0.0,
252                fRight: 0.0,
253                fBottom: 0.0,
254            },
255            fRadii: [
256                pathkit::SkPoint { fX: 0.0, fY: 0.0 },
257                pathkit::SkPoint { fX: 0.0, fY: 0.0 },
258                pathkit::SkPoint { fX: 0.0, fY: 0.0 },
259                pathkit::SkPoint { fX: 0.0, fY: 0.0 },
260            ],
261            fType: 0,
262        };
263        let ok = unsafe { self.inner.isRRect(&mut out as *mut _) };
264        if ok {
265            Some(RRect::from_raw(out))
266        } else {
267            None
268        }
269    }
270
271    /// 内部 SkPath 引用(仅 crate 内使用)。Internal use only.
272    pub(crate) fn as_raw(&self) -> &pathkit::SkPath {
273        &self.inner
274    }
275
276    /// 内部 SkPath 可变引用(仅 crate 内使用)。Internal use only.
277    pub(crate) fn as_raw_mut(&mut self) -> &mut pathkit::SkPath {
278        &mut self.inner
279    }
280}
281
282impl Default for Path {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288impl Clone for Path {
289    fn clone(&self) -> Self {
290        Self::from_path(self)
291    }
292}
293
294impl std::fmt::Debug for Path {
295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296        f.debug_struct("Path")
297            .field("points", &self.count_points())
298            .field("verbs", &self.count_verbs())
299            .field("bounds", &self.tight_bounds())
300            .finish()
301    }
302}