1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT

use crate::{FillRule, PathPoint, Stroke};
use glib::translate::*;
use std::{fmt, mem};

glib::wrapper! {
    /// A [`Path`][crate::Path] describes lines and curves that are more complex
    /// than simple rectangles.
    ///
    /// Paths can used for rendering (filling or stroking) and for animations
    /// (e.g. as trajectories).
    ///
    /// [`Path`][crate::Path] is an immutable, opaque, reference-counted struct.
    /// After creation, you cannot change the types it represents. Instead,
    /// new [`Path`][crate::Path] objects have to be created. The [`PathBuilder`][crate::PathBuilder]
    /// structure is meant to help in this endeavor.
    ///
    /// Conceptually, a path consists of zero or more contours (continous, connected
    /// curves), each of which may or may not be closed. Contours are typically
    /// constructed from Bézier segments.
    ///
    /// <picture>
    ///   <source srcset="path-dark.png" media="(prefers-color-scheme: dark)">
    ///   <img alt="A Path" src="path-light.png">
    /// </picture>
    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct Path(Shared<ffi::GskPath>);

    match fn {
        ref => |ptr| ffi::gsk_path_ref(ptr),
        unref => |ptr| ffi::gsk_path_unref(ptr),
        type_ => || ffi::gsk_path_get_type(),
    }
}

impl Path {
    /// Computes the bounds of the given path.
    ///
    /// The returned bounds may be larger than necessary, because this
    /// function aims to be fast, not accurate. The bounds are guaranteed
    /// to contain the path.
    ///
    /// It is possible that the returned rectangle has 0 width and/or height.
    /// This can happen when the path only describes a point or an
    /// axis-aligned line.
    ///
    /// If the path is empty, `FALSE` is returned and @bounds are set to
    /// graphene_rect_zero(). This is different from the case where the path
    /// is a single point at the origin, where the @bounds will also be set to
    /// the zero rectangle but `TRUE` will be returned.
    ///
    /// # Returns
    ///
    /// `TRUE` if the path has bounds, `FALSE` if the path is known
    ///   to be empty and have no bounds.
    ///
    /// ## `bounds`
    /// the bounds of the given path
    #[doc(alias = "gsk_path_get_bounds")]
    #[doc(alias = "get_bounds")]
    pub fn bounds(&self) -> Option<graphene::Rect> {
        unsafe {
            let mut bounds = graphene::Rect::uninitialized();
            let ret = from_glib(ffi::gsk_path_get_bounds(
                self.to_glib_none().0,
                bounds.to_glib_none_mut().0,
            ));
            if ret {
                Some(bounds)
            } else {
                None
            }
        }
    }

    /// Computes the closest point on the path to the given point
    /// and sets the @result to it.
    ///
    /// If there is no point closer than the given threshold,
    /// `FALSE` is returned.
    /// ## `point`
    /// the point
    /// ## `threshold`
    /// maximum allowed distance
    ///
    /// # Returns
    ///
    /// `TRUE` if @point was set to the closest point
    ///   on @self, `FALSE` if no point is closer than @threshold
    ///
    /// ## `result`
    /// return location for the closest point
    ///
    /// ## `distance`
    /// return location for the distance
    #[doc(alias = "gsk_path_get_closest_point")]
    #[doc(alias = "get_closest_point")]
    pub fn closest_point(
        &self,
        point: &graphene::Point,
        threshold: f32,
    ) -> Option<(PathPoint, f32)> {
        unsafe {
            let mut result = PathPoint::uninitialized();
            let mut distance = mem::MaybeUninit::uninit();
            let ret = from_glib(ffi::gsk_path_get_closest_point(
                self.to_glib_none().0,
                point.to_glib_none().0,
                threshold,
                result.to_glib_none_mut().0,
                distance.as_mut_ptr(),
            ));
            if ret {
                Some((result, distance.assume_init()))
            } else {
                None
            }
        }
    }

    /// Gets the end point of the path.
    ///
    /// An empty path has no points, so `FALSE`
    /// is returned in this case.
    ///
    /// # Returns
    ///
    /// `TRUE` if @result was filled
    ///
    /// ## `result`
    /// return location for point
    #[doc(alias = "gsk_path_get_end_point")]
    #[doc(alias = "get_end_point")]
    pub fn end_point(&self) -> Option<PathPoint> {
        unsafe {
            let mut result = PathPoint::uninitialized();
            let ret = from_glib(ffi::gsk_path_get_end_point(
                self.to_glib_none().0,
                result.to_glib_none_mut().0,
            ));
            if ret {
                Some(result)
            } else {
                None
            }
        }
    }

    /// Gets the start point of the path.
    ///
    /// An empty path has no points, so `FALSE`
    /// is returned in this case.
    ///
    /// # Returns
    ///
    /// `TRUE` if @result was filled
    ///
    /// ## `result`
    /// return location for point
    #[doc(alias = "gsk_path_get_start_point")]
    #[doc(alias = "get_start_point")]
    pub fn start_point(&self) -> Option<PathPoint> {
        unsafe {
            let mut result = PathPoint::uninitialized();
            let ret = from_glib(ffi::gsk_path_get_start_point(
                self.to_glib_none().0,
                result.to_glib_none_mut().0,
            ));
            if ret {
                Some(result)
            } else {
                None
            }
        }
    }

    /// Computes the bounds for stroking the given path with the
    /// parameters in @stroke.
    ///
    /// The returned bounds may be larger than necessary, because this
    /// function aims to be fast, not accurate. The bounds are guaranteed
    /// to contain the area affected by the stroke, including protrusions
    /// like miters.
    /// ## `stroke`
    /// stroke parameters
    ///
    /// # Returns
    ///
    /// `TRUE` if the path has bounds, `FALSE` if the path is known
    ///   to be empty and have no bounds.
    ///
    /// ## `bounds`
    /// the bounds to fill in
    #[doc(alias = "gsk_path_get_stroke_bounds")]
    #[doc(alias = "get_stroke_bounds")]
    pub fn stroke_bounds(&self, stroke: &Stroke) -> Option<graphene::Rect> {
        unsafe {
            let mut bounds = graphene::Rect::uninitialized();
            let ret = from_glib(ffi::gsk_path_get_stroke_bounds(
                self.to_glib_none().0,
                stroke.to_glib_none().0,
                bounds.to_glib_none_mut().0,
            ));
            if ret {
                Some(bounds)
            } else {
                None
            }
        }
    }

    /// Returns whether the given point is inside the area
    /// that would be affected if the path was filled according
    /// to @fill_rule.
    ///
    /// Note that this function assumes that filling a contour
    /// implicitly closes it.
    /// ## `point`
    /// the point to test
    /// ## `fill_rule`
    /// the fill rule to follow
    ///
    /// # Returns
    ///
    /// `TRUE` if @point is inside
    #[doc(alias = "gsk_path_in_fill")]
    pub fn in_fill(&self, point: &graphene::Point, fill_rule: FillRule) -> bool {
        unsafe {
            from_glib(ffi::gsk_path_in_fill(
                self.to_glib_none().0,
                point.to_glib_none().0,
                fill_rule.into_glib(),
            ))
        }
    }

    /// Returns if the path represents a single closed
    /// contour.
    ///
    /// # Returns
    ///
    /// `TRUE` if the path is closed
    #[doc(alias = "gsk_path_is_closed")]
    pub fn is_closed(&self) -> bool {
        unsafe { from_glib(ffi::gsk_path_is_closed(self.to_glib_none().0)) }
    }

    /// Checks if the path is empty, i.e. contains no lines or curves.
    ///
    /// # Returns
    ///
    /// `TRUE` if the path is empty
    #[doc(alias = "gsk_path_is_empty")]
    pub fn is_empty(&self) -> bool {
        unsafe { from_glib(ffi::gsk_path_is_empty(self.to_glib_none().0)) }
    }

    /// Appends the given @path to the given cairo context for drawing
    /// with Cairo.
    ///
    /// This may cause some suboptimal conversions to be performed as
    /// Cairo does not support all features of [`Path`][crate::Path].
    ///
    /// This function does not clear the existing Cairo path. Call
    /// cairo_new_path() if you want this.
    /// ## `cr`
    /// a cairo context
    #[doc(alias = "gsk_path_to_cairo")]
    pub fn to_cairo(&self, cr: &cairo::Context) {
        unsafe {
            ffi::gsk_path_to_cairo(self.to_glib_none().0, mut_override(cr.to_glib_none().0));
        }
    }

    /// Converts the path into a string that is suitable for printing.
    ///
    /// You can use this function in a debugger to get a quick overview
    /// of the path.
    ///
    /// This is a wrapper around `Gsk::Path::print()`, see that function
    /// for details.
    ///
    /// # Returns
    ///
    /// A new string for @self
    #[doc(alias = "gsk_path_to_string")]
    #[doc(alias = "to_string")]
    pub fn to_str(&self) -> glib::GString {
        unsafe { from_glib_full(ffi::gsk_path_to_string(self.to_glib_none().0)) }
    }

    /// This is a convenience function that constructs a [`Path`][crate::Path]
    /// from a serialized form.
    ///
    /// The string is expected to be in (a superset of)
    /// [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
    /// as e.g. produced by [`to_str()`][Self::to_str()].
    ///
    /// A high-level summary of the syntax:
    ///
    /// - `M x y` Move to `(x, y)`
    /// - `L x y` Add a line from the current point to `(x, y)`
    /// - `Q x1 y1 x2 y2` Add a quadratic Bézier from the current point to `(x2, y2)`, with control point `(x1, y1)`
    /// - `C x1 y1 x2 y2 x3 y3` Add a cubic Bézier from the current point to `(x3, y3)`, with control points `(x1, y1)` and `(x2, y2)`
    /// - `Z` Close the contour by drawing a line back to the start point
    /// - `H x` Add a horizontal line from the current point to the given x value
    /// - `V y` Add a vertical line from the current point to the given y value
    /// - `T x2 y2` Add a quadratic Bézier, using the reflection of the previous segments' control point as control point
    /// - `S x2 y2 x3 y3` Add a cubic Bézier, using the reflection of the previous segments' second control point as first control point
    /// - `A rx ry r l s x y` Add an elliptical arc from the current point to `(x, y)` with radii rx and ry. See the SVG documentation for how the other parameters influence the arc.
    /// - `O x1 y1 x2 y2 w` Add a rational quadratic Bézier from the current point to `(x2, y2)` with control point `(x1, y1)` and weight `w`.
    ///
    /// All the commands have lowercase variants that interpret coordinates
    /// relative to the current point.
    ///
    /// The `O` command is an extension that is not supported in SVG.
    /// ## `string`
    /// a string
    ///
    /// # Returns
    ///
    /// a new [`Path`][crate::Path], or `NULL` if @string could not be parsed
    #[doc(alias = "gsk_path_parse")]
    pub fn parse(string: &str) -> Result<Path, glib::BoolError> {
        assert_initialized_main_thread!();
        unsafe {
            Option::<_>::from_glib_full(ffi::gsk_path_parse(string.to_glib_none().0))
                .ok_or_else(|| glib::bool_error!("Can't parse Path"))
        }
    }
}

impl fmt::Display for Path {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_str())
    }
}