use crate::items::PathEvent;
#[cfg(feature = "rtti")]
use crate::rtti::*;
use auto_enums::auto_enum;
use const_field_offset::FieldOffsets;
use i_slint_core_macros::*;
#[repr(C)]
#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathMoveTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathLineTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathArcTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub radius_x: f32,
#[rtti_field]
pub radius_y: f32,
#[rtti_field]
pub x_rotation: f32,
#[rtti_field]
pub large_arc: bool,
#[rtti_field]
pub sweep: bool,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathCubicTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub control_1_x: f32,
#[rtti_field]
pub control_1_y: f32,
#[rtti_field]
pub control_2_x: f32,
#[rtti_field]
pub control_2_y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SlintElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathQuadraticTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub control_x: f32,
#[rtti_field]
pub control_y: f32,
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, derive_more::From)]
pub enum PathElement {
MoveTo(PathMoveTo),
LineTo(PathLineTo),
ArcTo(PathArcTo),
CubicTo(PathCubicTo),
QuadraticTo(PathQuadraticTo),
Close,
}
struct ToLyonPathEventIterator<'a> {
events_it: core::slice::Iter<'a, PathEvent>,
coordinates_it: core::slice::Iter<'a, lyon_path::math::Point>,
first: Option<&'a lyon_path::math::Point>,
last: Option<&'a lyon_path::math::Point>,
}
impl<'a> Iterator for ToLyonPathEventIterator<'a> {
type Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>;
fn next(&mut self) -> Option<Self::Item> {
use lyon_path::Event;
self.events_it.next().map(|event| match event {
PathEvent::Begin => Event::Begin { at: *self.coordinates_it.next().unwrap() },
PathEvent::Line => Event::Line {
from: *self.coordinates_it.next().unwrap(),
to: *self.coordinates_it.next().unwrap(),
},
PathEvent::Quadratic => Event::Quadratic {
from: *self.coordinates_it.next().unwrap(),
ctrl: *self.coordinates_it.next().unwrap(),
to: *self.coordinates_it.next().unwrap(),
},
PathEvent::Cubic => Event::Cubic {
from: *self.coordinates_it.next().unwrap(),
ctrl1: *self.coordinates_it.next().unwrap(),
ctrl2: *self.coordinates_it.next().unwrap(),
to: *self.coordinates_it.next().unwrap(),
},
PathEvent::EndOpen => {
Event::End { first: *self.first.unwrap(), last: *self.last.unwrap(), close: false }
}
PathEvent::EndClosed => {
Event::End { first: *self.first.unwrap(), last: *self.last.unwrap(), close: true }
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.events_it.size_hint()
}
}
impl<'a> ExactSizeIterator for ToLyonPathEventIterator<'a> {}
struct TransformedLyonPathIterator<EventIt> {
it: EventIt,
transform: lyon_path::math::Transform,
}
impl<
EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>,
> Iterator for TransformedLyonPathIterator<EventIt>
{
type Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>;
fn next(&mut self) -> Option<Self::Item> {
self.it.next().map(|ev| ev.transformed(&self.transform))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.it.size_hint()
}
}
impl<
EventIt: Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>>,
> ExactSizeIterator for TransformedLyonPathIterator<EventIt>
{
}
pub struct PathDataIterator {
it: LyonPathIteratorVariant,
transform: lyon_path::math::Transform,
}
enum LyonPathIteratorVariant {
FromPath(lyon_path::Path),
FromEvents(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
}
impl PathDataIterator {
#[auto_enum(Iterator)]
pub fn iter(
&self,
) -> impl Iterator<Item = lyon_path::Event<lyon_path::math::Point, lyon_path::math::Point>> + '_
{
match &self.it {
LyonPathIteratorVariant::FromPath(path) => {
TransformedLyonPathIterator { it: path.iter(), transform: self.transform }
}
LyonPathIteratorVariant::FromEvents(events, coordinates) => {
TransformedLyonPathIterator {
it: ToLyonPathEventIterator {
events_it: events.iter(),
coordinates_it: coordinates.iter(),
first: coordinates.first(),
last: coordinates.last(),
},
transform: self.transform,
}
}
}
}
pub fn fit(&mut self, width: f32, height: f32, viewbox: Option<lyon_path::math::Box2D>) {
if width > 0. || height > 0. {
let viewbox =
viewbox.unwrap_or_else(|| lyon_algorithms::aabb::bounding_box(self.iter()));
self.transform = lyon_algorithms::fit::fit_box(
&viewbox,
&lyon_path::math::Box2D::from_size(lyon_path::math::Size::new(width, height)),
lyon_algorithms::fit::FitStyle::Min,
);
}
}
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum PathData {
None,
Elements(crate::SharedVector<PathElement>),
Events(crate::SharedVector<PathEvent>, crate::SharedVector<lyon_path::math::Point>),
Commands(crate::SharedString),
}
impl Default for PathData {
fn default() -> Self {
Self::None
}
}
impl PathData {
pub fn iter(self) -> Option<PathDataIterator> {
PathDataIterator {
it: match self {
PathData::None => return None,
PathData::Elements(elements) => LyonPathIteratorVariant::FromPath(
PathData::build_path(elements.as_slice().iter()),
),
PathData::Events(events, coordinates) => {
LyonPathIteratorVariant::FromEvents(events, coordinates)
}
PathData::Commands(commands) => {
let mut builder = lyon_path::Path::builder();
let mut parser = lyon_extra::parser::PathParser::new();
match parser.parse(
&lyon_extra::parser::ParserOptions::DEFAULT,
&mut lyon_extra::parser::Source::new(commands.chars()),
&mut builder,
) {
Ok(()) => LyonPathIteratorVariant::FromPath(builder.build()),
Err(e) => {
eprintln!("Error while parsing path commands '{commands}': {e:?}");
LyonPathIteratorVariant::FromPath(Default::default())
}
}
}
},
transform: Default::default(),
}
.into()
}
fn build_path(element_it: core::slice::Iter<PathElement>) -> lyon_path::Path {
use lyon_geom::SvgArc;
use lyon_path::math::{Angle, Point, Vector};
use lyon_path::traits::SvgPathBuilder;
use lyon_path::ArcFlags;
let mut path_builder = lyon_path::Path::builder().with_svg();
for element in element_it {
match element {
PathElement::MoveTo(PathMoveTo { x, y }) => {
path_builder.move_to(Point::new(*x, *y));
}
PathElement::LineTo(PathLineTo { x, y }) => {
path_builder.line_to(Point::new(*x, *y));
}
PathElement::ArcTo(PathArcTo {
x,
y,
radius_x,
radius_y,
x_rotation,
large_arc,
sweep,
}) => {
let radii = Vector::new(*radius_x, *radius_y);
let x_rotation = Angle::degrees(*x_rotation);
let flags = ArcFlags { large_arc: *large_arc, sweep: *sweep };
let to = Point::new(*x, *y);
let svg_arc = SvgArc {
from: path_builder.current_position(),
radii,
x_rotation,
flags,
to,
};
if svg_arc.is_straight_line() {
path_builder.line_to(to);
} else {
path_builder.arc_to(radii, x_rotation, flags, to)
}
}
PathElement::CubicTo(PathCubicTo {
x,
y,
control_1_x,
control_1_y,
control_2_x,
control_2_y,
}) => {
path_builder.cubic_bezier_to(
Point::new(*control_1_x, *control_1_y),
Point::new(*control_2_x, *control_2_y),
Point::new(*x, *y),
);
}
PathElement::QuadraticTo(PathQuadraticTo { x, y, control_x, control_y }) => {
path_builder.quadratic_bezier_to(
Point::new(*control_x, *control_y),
Point::new(*x, *y),
);
}
PathElement::Close => path_builder.close(),
}
}
path_builder.build()
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::super::*;
use super::*;
#[allow(non_camel_case_types)]
type c_void = ();
#[no_mangle]
pub unsafe extern "C" fn slint_new_path_elements(
out: *mut c_void,
first_element: *const PathElement,
count: usize,
) {
let arr = crate::SharedVector::from(core::slice::from_raw_parts(first_element, count));
core::ptr::write(out as *mut crate::SharedVector<PathElement>, arr);
}
#[no_mangle]
pub unsafe extern "C" fn slint_new_path_events(
out_events: *mut c_void,
out_coordinates: *mut c_void,
first_event: *const PathEvent,
event_count: usize,
first_coordinate: *const Point,
coordinate_count: usize,
) {
let events =
crate::SharedVector::from(core::slice::from_raw_parts(first_event, event_count));
core::ptr::write(out_events as *mut crate::SharedVector<PathEvent>, events);
let coordinates = crate::SharedVector::from(core::slice::from_raw_parts(
first_coordinate,
coordinate_count,
));
core::ptr::write(out_coordinates as *mut crate::SharedVector<Point>, coordinates);
}
}