Allow for fitting paths into a given bounding rectangle

... by applying a transformation. This allows designing a path in some
other path design tool and then make it fit using bindings.
This commit is contained in:
Simon Hausmann 2020-07-13 15:38:56 +02:00
parent 79ba943882
commit 992f990fa8
12 changed files with 162 additions and 36 deletions

View file

@ -378,13 +378,38 @@ impl<'a> Iterator for ToLyonPathEventIterator<'a> {
impl<'a> ExactSizeIterator for ToLyonPathEventIterator<'a> {}
struct TransformedLyonPathIterator<EventIt> {
it: EventIt,
transform: lyon::math::Transform,
}
impl<EventIt: Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>>> Iterator
for TransformedLyonPathIterator<EventIt>
{
type Item = lyon::path::Event<lyon::math::Point, lyon::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::math::Point, lyon::math::Point>>>
ExactSizeIterator for TransformedLyonPathIterator<EventIt>
{
}
/// LyonPathIterator is a data structure that acts as starting point for iterating
/// through the low-level events of a path. If the path was constructed from said
/// events, then it is a very thin abstraction. If the path was created from higher-level
/// elements, then an intermediate lyon path is required/built.
pub struct LyonPathIterator<'a> {
it: LyonPathIteratorVariant<'a>,
transform: Option<lyon::math::Transform>,
}
enum LyonPathIteratorVariant<'a> {
FromPath(lyon::path::Path),
FromEvents(&'a crate::SharedArray<PathEvent>, &'a crate::SharedArray<Point>),
@ -397,17 +422,42 @@ impl<'a> LyonPathIterator<'a> {
) -> Box<dyn Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a>
{
match &self.it {
LyonPathIteratorVariant::FromPath(path) => Box::new(path.iter()),
LyonPathIteratorVariant::FromPath(path) => self.apply_transform(path.iter()),
LyonPathIteratorVariant::FromEvents(events, coordinates) => {
Box::new(ToLyonPathEventIterator {
Box::new(self.apply_transform(ToLyonPathEventIterator {
events_it: events.iter(),
coordinates_it: coordinates.iter(),
first: coordinates.first(),
last: coordinates.last(),
})
}))
}
}
}
/// This function changes the iterator to be one that applies a transformation to make the path fit into the specified bounds.
pub fn fitted(mut self, width: f32, height: f32) -> LyonPathIterator<'a> {
if width > 0. || height > 0. {
let br = lyon::algorithms::aabb::bounding_rect(self.iter());
self.transform = Some(lyon::algorithms::fit::fit_rectangle(
&br,
&Rect::from_size(Size::new(width, height)),
lyon::algorithms::fit::FitStyle::Min,
));
}
self
}
fn apply_transform(
&'a self,
event_it: impl Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a,
) -> Box<dyn Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a>
{
match self.transform {
Some(transform) => Box::new(TransformedLyonPathIterator { it: event_it, transform }),
None => Box::new(event_it),
}
}
}
#[repr(C)]
@ -443,6 +493,7 @@ impl PathData {
LyonPathIteratorVariant::FromEvents(events, coordinates)
}
},
transform: None,
}
}
@ -554,6 +605,8 @@ pub enum RenderingPrimitive {
Path {
x: f32,
y: f32,
width: f32,
height: f32,
elements: crate::PathData,
fill_color: Color,
stroke_color: Color,

View file

@ -268,6 +268,8 @@ pub use crate::abi::datastructures::TouchAreaVTable;
pub struct Path {
pub x: Property<f32>,
pub y: Property<f32>,
pub width: Property<f32>,
pub height: Property<f32>,
pub elements: Property<PathData>,
pub fill_color: Property<Color>,
pub stroke_color: Property<Color>,
@ -291,6 +293,8 @@ impl Item for Path {
RenderingPrimitive::Path {
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
width: Self::field_offsets().width.apply_pin(self).get(context),
height: Self::field_offsets().height.apply_pin(self).get(context),
elements: Self::field_offsets().elements.apply_pin(self).get(context),
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context),
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(context),

View file

@ -163,6 +163,8 @@ pub struct PathLayoutData<'a> {
pub items: Slice<'a, PathLayoutItemData<'a>>,
pub x: Coord,
pub y: Coord,
pub width: Coord,
pub height: Coord,
}
#[repr(C)]
@ -178,7 +180,7 @@ pub extern "C" fn solve_path_layout(data: &PathLayoutData) {
use lyon::geom::*;
use lyon::path::iterator::PathIterator;
let path_iter = data.elements.iter();
let path_iter = data.elements.iter().fitted(data.width, data.height);
let tolerance = 0.01;

View file

@ -557,6 +557,12 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
let component_type =
&*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription);
let resolve_prop_ref = |prop_ref: &expression_tree::Expression| {
eval::eval_expression(&prop_ref, &component_type, eval_context)
.try_into()
.unwrap_or_default()
};
for it in &component_type.original.layout_constraints.borrow().grids {
use sixtyfps_corelib::layout::*;
@ -604,21 +610,13 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
.unwrap()
};
let x = eval::eval_expression(&it.x_reference, &component_type, eval_context)
.try_into()
.unwrap_or_default();
let y = eval::eval_expression(&it.y_reference, &component_type, eval_context)
.try_into()
.unwrap_or_default();
solve_grid_layout(&GridLayoutData {
row_constraint: Slice::from(row_constraint.as_slice()),
col_constraint: Slice::from(col_constraint.as_slice()),
width: within_prop("width"),
height: within_prop("height"),
x,
y,
x: resolve_prop_ref(&it.x_reference),
y: resolve_prop_ref(&it.y_reference),
cells: Slice::from(cells.as_slice()),
});
}
@ -643,19 +641,13 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
let path_elements = eval::convert_path(&it.path, component_type, eval_context);
let x = eval::eval_expression(&it.x_reference, &component_type, eval_context)
.try_into()
.unwrap_or_default();
let y = eval::eval_expression(&it.y_reference, &component_type, eval_context)
.try_into()
.unwrap_or_default();
solve_path_layout(&PathLayoutData {
items: Slice::from(items.as_slice()),
elements: &path_elements,
x,
y,
x: resolve_prop_ref(&it.x_reference),
y: resolve_prop_ref(&it.y_reference),
width: resolve_prop_ref(&it.width_reference),
height: resolve_prop_ref(&it.height_reference),
});
}
}

View file

@ -347,6 +347,8 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
RenderingPrimitive::Path {
x: _,
y: _,
width,
height,
elements,
fill_color,
stroke_color,
@ -354,7 +356,7 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
} => {
let mut primitives = SmallVec::new();
let path_iter = elements.iter();
let path_iter = elements.iter().fitted(*width, *height);
if *fill_color != Color::TRANSPARENT {
primitives.extend(