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