1use crate::debug_log;
9use crate::items::PathEvent;
10#[cfg(feature = "rtti")]
11use crate::rtti::*;
12use auto_enums::auto_enum;
13use const_field_offset::FieldOffsets;
14use i_slint_core_macros::*;
15
16#[repr(C)]
17#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
18#[pin]
19pub struct PathMoveTo {
23    #[rtti_field]
24    pub x: f32,
26    #[rtti_field]
27    pub y: f32,
29}
30
31#[repr(C)]
32#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
33#[pin]
34pub struct PathLineTo {
37    #[rtti_field]
38    pub x: f32,
40    #[rtti_field]
41    pub y: f32,
43}
44
45#[repr(C)]
46#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
47#[pin]
48pub struct PathArcTo {
51    #[rtti_field]
52    pub x: f32,
54    #[rtti_field]
55    pub y: f32,
57    #[rtti_field]
58    pub radius_x: f32,
60    #[rtti_field]
61    pub radius_y: f32,
63    #[rtti_field]
64    pub x_rotation: f32,
66    #[rtti_field]
67    pub large_arc: bool,
69    #[rtti_field]
70    pub sweep: bool,
73}
74
75#[repr(C)]
76#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
77#[pin]
78pub struct PathCubicTo {
81    #[rtti_field]
82    pub x: f32,
84    #[rtti_field]
85    pub y: f32,
87    #[rtti_field]
88    pub control_1_x: f32,
90    #[rtti_field]
91    pub control_1_y: f32,
93    #[rtti_field]
94    pub control_2_x: f32,
96    #[rtti_field]
97    pub control_2_y: f32,
99}
100
101#[repr(C)]
102#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
103#[pin]
104pub struct PathQuadraticTo {
107    #[rtti_field]
108    pub x: f32,
110    #[rtti_field]
111    pub y: f32,
113    #[rtti_field]
114    pub control_x: f32,
116    #[rtti_field]
117    pub control_y: f32,
119}
120
121#[repr(C)]
122#[derive(Clone, Debug, PartialEq, derive_more::From)]
123pub enum PathElement {
125    MoveTo(PathMoveTo),
127    LineTo(PathLineTo),
129    ArcTo(PathArcTo),
131    CubicTo(PathCubicTo),
133    QuadraticTo(PathQuadraticTo),
135    Close,
137}
138
139struct ToLyonPathEventIterator<'a> {
140    events_it: core::slice::Iter<'a, PathEvent>,
141    coordinates_it: core::slice::Iter<'a, lyon_path::math::Point>,
142    first: Option<&'a lyon_path::math::Point>,
143    last: Option<&'a lyon_path::math::Point>,
144}
145
146impl Iterator for ToLyonPathEventIterator<'_> {
147    type Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>;
148    fn next(&mut self) -> Option<Self::Item> {
149        use lyon_path::Event;
150
151        self.events_it.next().map(|event| match event {
152            PathEvent::Begin => Event::Begin { at: *self.coordinates_it.next().unwrap() },
153            PathEvent::Line => Event::Line {
154                from: *self.coordinates_it.next().unwrap(),
155                to: *self.coordinates_it.next().unwrap(),
156            },
157            PathEvent::Quadratic => Event::Quadratic {
158                from: *self.coordinates_it.next().unwrap(),
159                ctrl: *self.coordinates_it.next().unwrap(),
160                to: *self.coordinates_it.next().unwrap(),
161            },
162            PathEvent::Cubic => Event::Cubic {
163                from: *self.coordinates_it.next().unwrap(),
164                ctrl1: *self.coordinates_it.next().unwrap(),
165                ctrl2: *self.coordinates_it.next().unwrap(),
166                to: *self.coordinates_it.next().unwrap(),
167            },
168            PathEvent::EndOpen => {
169                Event::End { first: *self.first.unwrap(), last: *self.last.unwrap(), close: false }
170            }
171            PathEvent::EndClosed => {
172                Event::End { first: *self.first.unwrap(), last: *self.last.unwrap(), close: true }
173            }
174        })
175    }
176
177    fn size_hint(&self) -> (usize, Option<usize>) {
178        self.events_it.size_hint()
179    }
180}
181
182impl ExactSizeIterator for ToLyonPathEventIterator<'_> {}
183
184struct TransformedLyonPathIterator<EventIt> {
185    it: EventIt,
186    transform: lyon_path::math::Transform,
187}
188
189impl<
190        EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>,
191    > Iterator for TransformedLyonPathIterator<EventIt>
192{
193    type Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>;
194    fn next(&mut self) -> Option<Self::Item> {
195        self.it.next().map(|ev| ev.transformed(&self.transform))
196    }
197
198    fn size_hint(&self) -> (usize, Option<usize>) {
199        self.it.size_hint()
200    }
201}
202
203impl<
204        EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>,
205    > ExactSizeIterator for TransformedLyonPathIterator<EventIt>
206{
207}
208
209pub struct PathDataIterator {
214    it: LyonPathIteratorVariant,
215    transform: lyon_path::math::Transform,
216}
217
218enum LyonPathIteratorVariant {
219    FromPath(lyon_path::Path),
220    FromEvents(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
221}
222
223impl PathDataIterator {
224    #[auto_enum(Iterator)]
226    pub fn iter(
227        &self,
228    ) -> impl Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>> + '_
229    {
230        match &self.it {
231            LyonPathIteratorVariant::FromPath(path) => {
232                TransformedLyonPathIterator { it: path.iter(), transform: self.transform }
233            }
234            LyonPathIteratorVariant::FromEvents(events, coordinates) => {
235                TransformedLyonPathIterator {
236                    it: ToLyonPathEventIterator {
237                        events_it: events.iter(),
238                        coordinates_it: coordinates.iter(),
239                        first: coordinates.first(),
240                        last: coordinates.last(),
241                    },
242                    transform: self.transform,
243                }
244            }
245        }
246    }
247
248    pub fn fit(&mut self, width: f32, height: f32, viewbox: Option<lyon_path::math::Box2D>) {
252        if width > 0. || height > 0. {
253            let viewbox =
254                viewbox.unwrap_or_else(|| lyon_algorithms::aabb::bounding_box(self.iter()));
255            self.transform = lyon_algorithms::fit::fit_box(
256                &viewbox,
257                &lyon_path::math::Box2D::from_size(lyon_path::math::Size::new(width, height)),
258                lyon_algorithms::fit::FitStyle::Min,
259            );
260        }
261    }
262}
263
264#[repr(C)]
265#[derive(Clone, Debug, PartialEq)]
266pub enum PathData {
269    None,
271    Elements(crate::SharedVector<PathElement>),
273    Events(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
276    Commands(crate::SharedString),
278}
279
280impl Default for PathData {
281    fn default() -> Self {
282        Self::None
283    }
284}
285
286impl PathData {
287    pub fn iter(self) -> Option<PathDataIterator> {
289        PathDataIterator {
290            it: match self {
291                PathData::None => return None,
292                PathData::Elements(elements) => LyonPathIteratorVariant::FromPath(
293                    PathData::build_path(elements.as_slice().iter()),
294                ),
295                PathData::Events(events, coordinates) => {
296                    LyonPathIteratorVariant::FromEvents(events, coordinates)
297                }
298                PathData::Commands(commands) => {
299                    let mut builder = lyon_path::Path::builder();
300                    let mut parser = lyon_extra::parser::PathParser::new();
301                    match parser.parse(
302                        &lyon_extra::parser::ParserOptions::DEFAULT,
303                        &mut lyon_extra::parser::Source::new(commands.chars()),
304                        &mut builder,
305                    ) {
306                        Ok(()) => LyonPathIteratorVariant::FromPath(builder.build()),
307                        Err(e) => {
308                            debug_log!("Error while parsing path commands '{commands}': {e:?}");
309                            LyonPathIteratorVariant::FromPath(Default::default())
310                        }
311                    }
312                }
313            },
314            transform: Default::default(),
315        }
316        .into()
317    }
318
319    fn build_path(element_it: core::slice::Iter<PathElement>) -> lyon_path::Path {
320        use lyon_geom::SvgArc;
321        use lyon_path::math::{Angle, Point, Vector};
322        use lyon_path::traits::SvgPathBuilder;
323        use lyon_path::ArcFlags;
324
325        let mut path_builder = lyon_path::Path::builder().with_svg();
326        for element in element_it {
327            match element {
328                PathElement::MoveTo(PathMoveTo { x, y }) => {
329                    path_builder.move_to(Point::new(*x, *y));
330                }
331                PathElement::LineTo(PathLineTo { x, y }) => {
332                    path_builder.line_to(Point::new(*x, *y));
333                }
334                PathElement::ArcTo(PathArcTo {
335                    x,
336                    y,
337                    radius_x,
338                    radius_y,
339                    x_rotation,
340                    large_arc,
341                    sweep,
342                }) => {
343                    let radii = Vector::new(*radius_x, *radius_y);
344                    let x_rotation = Angle::degrees(*x_rotation);
345                    let flags = ArcFlags { large_arc: *large_arc, sweep: *sweep };
346                    let to = Point::new(*x, *y);
347
348                    let svg_arc = SvgArc {
349                        from: path_builder.current_position(),
350                        radii,
351                        x_rotation,
352                        flags,
353                        to,
354                    };
355
356                    if svg_arc.is_straight_line() {
357                        path_builder.line_to(to);
358                    } else {
359                        path_builder.arc_to(radii, x_rotation, flags, to)
360                    }
361                }
362                PathElement::CubicTo(PathCubicTo {
363                    x,
364                    y,
365                    control_1_x,
366                    control_1_y,
367                    control_2_x,
368                    control_2_y,
369                }) => {
370                    path_builder.cubic_bezier_to(
371                        Point::new(*control_1_x, *control_1_y),
372                        Point::new(*control_2_x, *control_2_y),
373                        Point::new(*x, *y),
374                    );
375                }
376                PathElement::QuadraticTo(PathQuadraticTo { x, y, control_x, control_y }) => {
377                    path_builder.quadratic_bezier_to(
378                        Point::new(*control_x, *control_y),
379                        Point::new(*x, *y),
380                    );
381                }
382                PathElement::Close => path_builder.close(),
383            }
384        }
385
386        path_builder.build()
387    }
388}
389
390#[cfg(not(target_arch = "wasm32"))]
391pub(crate) mod ffi {
392    #![allow(unsafe_code)]
393
394    use super::super::*;
395    use super::*;
396
397    #[allow(non_camel_case_types)]
398    type c_void = ();
399
400    #[unsafe(no_mangle)]
401    pub unsafe extern "C" fn slint_new_path_elements(
403        out: *mut c_void,
404        first_element: *const PathElement,
405        count: usize,
406    ) {
407        let arr = crate::SharedVector::from(core::slice::from_raw_parts(first_element, count));
408        core::ptr::write(out as *mut crate::SharedVector<PathElement>, arr);
409    }
410
411    #[unsafe(no_mangle)]
412    pub unsafe extern "C" fn slint_new_path_events(
414        out_events: *mut c_void,
415        out_coordinates: *mut c_void,
416        first_event: *const PathEvent,
417        event_count: usize,
418        first_coordinate: *const Point,
419        coordinate_count: usize,
420    ) {
421        let events =
422            crate::SharedVector::from(core::slice::from_raw_parts(first_event, event_count));
423        core::ptr::write(out_events as *mut crate::SharedVector<PathEvent>, events);
424        let coordinates = crate::SharedVector::from(core::slice::from_raw_parts(
425            first_coordinate,
426            coordinate_count,
427        ));
428        core::ptr::write(out_coordinates as *mut crate::SharedVector<Point>, coordinates);
429    }
430}