1use crate::debug_log;
9use crate::items::{ImageFit, 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<EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>>
190 Iterator for TransformedLyonPathIterator<EventIt>
191{
192 type Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>;
193 fn next(&mut self) -> Option<Self::Item> {
194 self.it.next().map(|ev| ev.transformed(&self.transform))
195 }
196
197 fn size_hint(&self) -> (usize, Option<usize>) {
198 self.it.size_hint()
199 }
200}
201
202impl<EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>>
203 ExactSizeIterator for TransformedLyonPathIterator<EventIt>
204{
205}
206
207pub struct PathDataIterator {
212 it: LyonPathIteratorVariant,
213 transform: lyon_path::math::Transform,
214}
215
216enum LyonPathIteratorVariant {
217 FromPath(lyon_path::Path),
218 FromEvents(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
219}
220
221impl PathDataIterator {
222 #[auto_enum(Iterator)]
224 pub fn iter(
225 &self,
226 ) -> impl Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>> + '_
227 {
228 match &self.it {
229 LyonPathIteratorVariant::FromPath(path) => {
230 TransformedLyonPathIterator { it: path.iter(), transform: self.transform }
231 }
232 LyonPathIteratorVariant::FromEvents(events, coordinates) => {
233 TransformedLyonPathIterator {
234 it: ToLyonPathEventIterator {
235 events_it: events.iter(),
236 coordinates_it: coordinates.iter(),
237 first: coordinates.first(),
238 last: coordinates.last(),
239 },
240 transform: self.transform,
241 }
242 }
243 }
244 }
245
246 pub fn fit(
250 &mut self,
251 width: f32,
252 height: f32,
253 viewbox: Option<lyon_path::math::Box2D>,
254 style: ImageFit,
255 ) {
256 if width > 0. || height > 0. {
257 let fit_style = match style {
258 ImageFit::Contain => lyon_algorithms::fit::FitStyle::Min,
259 ImageFit::Cover => lyon_algorithms::fit::FitStyle::Max,
260 ImageFit::Fill => lyon_algorithms::fit::FitStyle::Stretch,
261 ImageFit::Preserve => return,
262 };
263 let viewbox =
264 viewbox.unwrap_or_else(|| lyon_algorithms::aabb::bounding_box(self.iter()));
265 self.transform = lyon_algorithms::fit::fit_box(
266 &viewbox,
267 &lyon_path::math::Box2D::from_size(lyon_path::math::Size::new(width, height)),
268 fit_style,
269 );
270 }
271 }
272}
273
274#[repr(C)]
275#[derive(Clone, Debug, PartialEq)]
276#[derive(Default)]
279pub enum PathData {
280 #[default]
282 None,
283 Elements(crate::SharedVector<PathElement>),
285 Events(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
288 Commands(crate::SharedString),
290}
291
292impl PathData {
293 pub fn iter(self) -> Option<PathDataIterator> {
295 PathDataIterator {
296 it: match self {
297 PathData::None => return None,
298 PathData::Elements(elements) => LyonPathIteratorVariant::FromPath(
299 PathData::build_path(elements.as_slice().iter()),
300 ),
301 PathData::Events(events, coordinates) => {
302 LyonPathIteratorVariant::FromEvents(events, coordinates)
303 }
304 PathData::Commands(commands) => {
305 let mut builder = lyon_path::Path::builder();
306 let mut parser = lyon_extra::parser::PathParser::new();
307 match parser.parse(
308 &lyon_extra::parser::ParserOptions::DEFAULT,
309 &mut lyon_extra::parser::Source::new(commands.chars()),
310 &mut builder,
311 ) {
312 Ok(()) => LyonPathIteratorVariant::FromPath(builder.build()),
313 Err(e) => {
314 debug_log!("Error while parsing path commands '{commands}': {e:?}");
315 LyonPathIteratorVariant::FromPath(Default::default())
316 }
317 }
318 }
319 },
320 transform: Default::default(),
321 }
322 .into()
323 }
324
325 fn build_path(element_it: core::slice::Iter<PathElement>) -> lyon_path::Path {
326 use lyon_geom::SvgArc;
327 use lyon_path::ArcFlags;
328 use lyon_path::math::{Angle, Point, Vector};
329 use lyon_path::traits::SvgPathBuilder;
330
331 let mut path_builder = lyon_path::Path::builder().with_svg();
332 for element in element_it {
333 match element {
334 PathElement::MoveTo(PathMoveTo { x, y }) => {
335 path_builder.move_to(Point::new(*x, *y));
336 }
337 PathElement::LineTo(PathLineTo { x, y }) => {
338 path_builder.line_to(Point::new(*x, *y));
339 }
340 PathElement::ArcTo(PathArcTo {
341 x,
342 y,
343 radius_x,
344 radius_y,
345 x_rotation,
346 large_arc,
347 sweep,
348 }) => {
349 let radii = Vector::new(*radius_x, *radius_y);
350 let x_rotation = Angle::degrees(*x_rotation);
351 let flags = ArcFlags { large_arc: *large_arc, sweep: *sweep };
352 let to = Point::new(*x, *y);
353
354 let svg_arc = SvgArc {
355 from: path_builder.current_position(),
356 radii,
357 x_rotation,
358 flags,
359 to,
360 };
361
362 if svg_arc.is_straight_line() {
363 path_builder.line_to(to);
364 } else {
365 path_builder.arc_to(radii, x_rotation, flags, to)
366 }
367 }
368 PathElement::CubicTo(PathCubicTo {
369 x,
370 y,
371 control_1_x,
372 control_1_y,
373 control_2_x,
374 control_2_y,
375 }) => {
376 path_builder.cubic_bezier_to(
377 Point::new(*control_1_x, *control_1_y),
378 Point::new(*control_2_x, *control_2_y),
379 Point::new(*x, *y),
380 );
381 }
382 PathElement::QuadraticTo(PathQuadraticTo { x, y, control_x, control_y }) => {
383 path_builder.quadratic_bezier_to(
384 Point::new(*control_x, *control_y),
385 Point::new(*x, *y),
386 );
387 }
388 PathElement::Close => path_builder.close(),
389 }
390 }
391
392 path_builder.build()
393 }
394}
395
396#[cfg(not(target_arch = "wasm32"))]
397pub(crate) mod ffi {
398 #![allow(unsafe_code)]
399
400 use super::super::*;
401 use super::*;
402 use core::ffi::c_void;
403
404 #[unsafe(no_mangle)]
405 pub unsafe extern "C" fn slint_new_path_elements(
407 out: *mut c_void,
408 first_element: *const PathElement,
409 count: usize,
410 ) {
411 let arr =
412 crate::SharedVector::from(unsafe { core::slice::from_raw_parts(first_element, count) });
413 unsafe { core::ptr::write(out as *mut crate::SharedVector<PathElement>, arr) };
414 }
415
416 #[unsafe(no_mangle)]
417 pub unsafe extern "C" fn slint_new_path_events(
419 out_events: *mut c_void,
420 out_coordinates: *mut c_void,
421 first_event: *const PathEvent,
422 event_count: usize,
423 first_coordinate: *const Point,
424 coordinate_count: usize,
425 ) {
426 let events = crate::SharedVector::from(unsafe {
427 core::slice::from_raw_parts(first_event, event_count)
428 });
429 unsafe { core::ptr::write(out_events as *mut crate::SharedVector<PathEvent>, events) };
430 let coordinates = crate::SharedVector::from(unsafe {
431 core::slice::from_raw_parts(first_coordinate, coordinate_count)
432 });
433 unsafe {
434 core::ptr::write(out_coordinates as *mut crate::SharedVector<Point>, coordinates)
435 };
436 }
437}