|
|
|
|
@@ -1,12 +1,12 @@
|
|
|
|
|
use super::misc::CentroidType;
|
|
|
|
|
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
|
|
|
|
use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData};
|
|
|
|
|
use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable};
|
|
|
|
|
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, SeedValue};
|
|
|
|
|
use crate::renderer::GraphicElementRendered;
|
|
|
|
|
use crate::transform::{Footprint, Transform, TransformMut};
|
|
|
|
|
use crate::vector::style::LineJoin;
|
|
|
|
|
use crate::vector::PointDomain;
|
|
|
|
|
use crate::{Color, GraphicElement, GraphicGroup};
|
|
|
|
|
use crate::{Color, GraphicElement, GraphicGroup, GraphicGroupTable};
|
|
|
|
|
|
|
|
|
|
use bezier_rs::{Cap, Join, Subpath, SubpathTValue, TValue};
|
|
|
|
|
use glam::{DAffine2, DVec2};
|
|
|
|
|
@@ -18,21 +18,27 @@ trait VectorIterMut {
|
|
|
|
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = (&mut VectorData, DAffine2)>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl VectorIterMut for GraphicGroup {
|
|
|
|
|
impl VectorIterMut for GraphicGroupTable {
|
|
|
|
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = (&mut VectorData, DAffine2)> {
|
|
|
|
|
let parent_transform = self.transform;
|
|
|
|
|
// Grab only the direct children (perhaps unintuitive?)
|
|
|
|
|
self.iter_mut().filter_map(|(element, _)| element.as_vector_data_mut()).map(move |vector| {
|
|
|
|
|
let transform = parent_transform * vector.transform;
|
|
|
|
|
(vector, transform)
|
|
|
|
|
let instance = self.one_item_mut();
|
|
|
|
|
|
|
|
|
|
let parent_transform = instance.transform;
|
|
|
|
|
|
|
|
|
|
// Grab only the direct children
|
|
|
|
|
instance.iter_mut().filter_map(|(element, _)| element.as_vector_data_mut()).map(move |vector_data| {
|
|
|
|
|
let vector_data = vector_data.one_item_mut();
|
|
|
|
|
let transform = parent_transform * vector_data.transform;
|
|
|
|
|
(vector_data, transform)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl VectorIterMut for VectorData {
|
|
|
|
|
impl VectorIterMut for VectorDataTable {
|
|
|
|
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = (&mut VectorData, DAffine2)> {
|
|
|
|
|
let transform = self.transform;
|
|
|
|
|
std::iter::once((self, transform))
|
|
|
|
|
self.instances_mut().map(|instance| {
|
|
|
|
|
let transform = instance.transform;
|
|
|
|
|
(instance, transform)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -45,10 +51,10 @@ async fn assign_colors<F: 'n + Send, T: VectorIterMut>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
#[widget(ParsedWidgetOverride::Hidden)]
|
|
|
|
|
vector_group: impl Node<F, Output = T>,
|
|
|
|
|
@@ -60,13 +66,14 @@ async fn assign_colors<F: 'n + Send, T: VectorIterMut>(
|
|
|
|
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")] seed: SeedValue,
|
|
|
|
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")] repeat_every: u32,
|
|
|
|
|
) -> T {
|
|
|
|
|
let mut input = vector_group.eval(footprint).await;
|
|
|
|
|
let length = input.vector_iter_mut().count();
|
|
|
|
|
let mut vector_group = vector_group.eval(footprint).await;
|
|
|
|
|
|
|
|
|
|
let length = vector_group.vector_iter_mut().count();
|
|
|
|
|
let gradient = if reverse { gradient.reversed() } else { gradient };
|
|
|
|
|
|
|
|
|
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
|
|
|
|
|
|
|
|
|
for (i, (vector_data, _)) in input.vector_iter_mut().enumerate() {
|
|
|
|
|
for (i, (vector_data, _)) in vector_group.vector_iter_mut().enumerate() {
|
|
|
|
|
let factor = match randomize {
|
|
|
|
|
true => rng.gen::<f64>(),
|
|
|
|
|
false => match repeat_every {
|
|
|
|
|
@@ -87,7 +94,8 @@ async fn assign_colors<F: 'n + Send, T: VectorIterMut>(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
input
|
|
|
|
|
|
|
|
|
|
vector_group
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
|
|
|
|
@@ -112,22 +120,22 @@ async fn fill<F: 'n + Send, FillTy: Into<Fill> + 'n + Send, TargetTy: VectorIter
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = TargetTy>,
|
|
|
|
|
#[implementations(
|
|
|
|
|
@@ -176,14 +184,14 @@ async fn stroke<F: 'n + Send, ColorTy: Into<Option<Color>> + 'n + Send, TargetTy
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = TargetTy>,
|
|
|
|
|
#[implementations(
|
|
|
|
|
@@ -234,10 +242,10 @@ async fn repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + Transform
|
|
|
|
|
footprint: F,
|
|
|
|
|
// TODO: Implement other GraphicElementRendered types.
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
instance: impl Node<F, Output = I>,
|
|
|
|
|
#[default(100., 100.)]
|
|
|
|
|
@@ -245,7 +253,7 @@ async fn repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + Transform
|
|
|
|
|
direction: DVec2,
|
|
|
|
|
angle: Angle,
|
|
|
|
|
#[default(4)] instances: IntegerCount,
|
|
|
|
|
) -> GraphicGroup {
|
|
|
|
|
) -> GraphicGroupTable {
|
|
|
|
|
let instance = instance.eval(footprint).await;
|
|
|
|
|
let first_vector_transform = instance.transform();
|
|
|
|
|
|
|
|
|
|
@@ -253,10 +261,10 @@ async fn repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + Transform
|
|
|
|
|
let instances = instances.max(1);
|
|
|
|
|
let total = (instances - 1) as f64;
|
|
|
|
|
|
|
|
|
|
let mut result = GraphicGroup::EMPTY;
|
|
|
|
|
let mut result = GraphicGroup::default();
|
|
|
|
|
|
|
|
|
|
let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY) else {
|
|
|
|
|
return result;
|
|
|
|
|
return GraphicGroupTable::new(result);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let center = (bounding_box[0] + bounding_box[1]) / 2.;
|
|
|
|
|
@@ -273,7 +281,7 @@ async fn repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + Transform
|
|
|
|
|
result.push((new_instance, None));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
GraphicGroupTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -287,24 +295,24 @@ async fn circular_repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + T
|
|
|
|
|
footprint: F,
|
|
|
|
|
// TODO: Implement other GraphicElementRendered types.
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
instance: impl Node<F, Output = I>,
|
|
|
|
|
angle_offset: Angle,
|
|
|
|
|
#[default(5)] radius: f64,
|
|
|
|
|
#[default(5)] instances: IntegerCount,
|
|
|
|
|
) -> GraphicGroup {
|
|
|
|
|
) -> GraphicGroupTable {
|
|
|
|
|
let instance = instance.eval(footprint).await;
|
|
|
|
|
let first_vector_transform = instance.transform();
|
|
|
|
|
let instances = instances.max(1);
|
|
|
|
|
|
|
|
|
|
let mut result = GraphicGroup::EMPTY;
|
|
|
|
|
let mut result = GraphicGroup::default();
|
|
|
|
|
|
|
|
|
|
let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY) else {
|
|
|
|
|
return result;
|
|
|
|
|
return GraphicGroupTable::new(result);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let center = (bounding_box[0] + bounding_box[1]) / 2.;
|
|
|
|
|
@@ -322,7 +330,7 @@ async fn circular_repeat<F: 'n + Send + Copy, I: 'n + GraphicElementRendered + T
|
|
|
|
|
result.push((new_instance, None));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
GraphicGroupTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -334,17 +342,17 @@ async fn copy_to_points<F: 'n + Send + Copy, I: GraphicElementRendered + ConcatE
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
points: impl Node<F, Output = VectorData>,
|
|
|
|
|
points: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[expose]
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
instance: impl Node<F, Output = I>,
|
|
|
|
|
#[default(1)] random_scale_min: f64,
|
|
|
|
|
@@ -353,9 +361,12 @@ async fn copy_to_points<F: 'n + Send + Copy, I: GraphicElementRendered + ConcatE
|
|
|
|
|
random_scale_seed: SeedValue,
|
|
|
|
|
random_rotation: Angle,
|
|
|
|
|
random_rotation_seed: SeedValue,
|
|
|
|
|
) -> GraphicGroup {
|
|
|
|
|
) -> GraphicGroupTable {
|
|
|
|
|
let points = points.eval(footprint).await;
|
|
|
|
|
let points = points.one_item();
|
|
|
|
|
|
|
|
|
|
let instance = instance.eval(footprint).await;
|
|
|
|
|
|
|
|
|
|
let instance_transform = instance.transform();
|
|
|
|
|
|
|
|
|
|
let random_scale_difference = random_scale_max - random_scale_min;
|
|
|
|
|
@@ -406,7 +417,7 @@ async fn copy_to_points<F: 'n + Send + Copy, I: GraphicElementRendered + ConcatE
|
|
|
|
|
result.push((new_instance, None));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
GraphicGroupTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -417,18 +428,20 @@ async fn bounding_box<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
let bounding_box = vector_data.bounding_box_with_transform(vector_data.transform).unwrap();
|
|
|
|
|
let mut result = VectorData::from_subpath(Subpath::new_rect(bounding_box[0], bounding_box[1]));
|
|
|
|
|
result.style = vector_data.style.clone();
|
|
|
|
|
result.style.set_stroke_transform(DAffine2::IDENTITY);
|
|
|
|
|
result
|
|
|
|
|
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector), properties("offset_path_properties"))]
|
|
|
|
|
@@ -439,23 +452,23 @@ async fn offset_path<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
distance: f64,
|
|
|
|
|
line_join: LineJoin,
|
|
|
|
|
#[default(4.)] miter_limit: f64,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
let subpaths = vector_data.stroke_bezier_paths();
|
|
|
|
|
let mut result = VectorData::empty();
|
|
|
|
|
result.style = vector_data.style.clone();
|
|
|
|
|
result.style.set_stroke_transform(DAffine2::IDENTITY);
|
|
|
|
|
|
|
|
|
|
// Perform operation on all subpaths in this shape.
|
|
|
|
|
for mut subpath in subpaths {
|
|
|
|
|
for mut subpath in vector_data.stroke_bezier_paths() {
|
|
|
|
|
subpath.apply_transform(vector_data.transform);
|
|
|
|
|
|
|
|
|
|
// Taking the existing stroke data and passing it to Bezier-rs to generate new paths.
|
|
|
|
|
@@ -472,7 +485,7 @@ async fn offset_path<F: 'n + Send>(
|
|
|
|
|
result.append_subpath(subpath_out, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -483,14 +496,17 @@ async fn solidify_stroke<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
let transform = &vector_data.transform;
|
|
|
|
|
let style = &vector_data.style;
|
|
|
|
|
|
|
|
|
|
let VectorData { transform, style, .. } = &vector_data;
|
|
|
|
|
let subpaths = vector_data.stroke_bezier_paths();
|
|
|
|
|
let mut result = VectorData::empty();
|
|
|
|
|
|
|
|
|
|
@@ -531,7 +547,7 @@ async fn solidify_stroke<F: 'n + Send>(
|
|
|
|
|
result.style.set_stroke(Stroke::default());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -542,12 +558,14 @@ async fn flatten_vector_elements<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> GraphicGroup,
|
|
|
|
|
Footprint -> GraphicGroup,
|
|
|
|
|
() -> GraphicGroupTable,
|
|
|
|
|
Footprint -> GraphicGroupTable,
|
|
|
|
|
)]
|
|
|
|
|
graphic_group_input: impl Node<F, Output = GraphicGroup>,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
graphic_group_input: impl Node<F, Output = GraphicGroupTable>,
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let graphic_group = graphic_group_input.eval(footprint).await;
|
|
|
|
|
let graphic_group = graphic_group.one_item();
|
|
|
|
|
|
|
|
|
|
// A node based solution to support passing through vector data could be a network node with a cache node connected to
|
|
|
|
|
// a flatten vector elements connected to an if else node, another connection from the cache directly
|
|
|
|
|
// To the if else node, and another connection from the cache to a matches type node connected to the if else node.
|
|
|
|
|
@@ -555,9 +573,12 @@ async fn flatten_vector_elements<F: 'n + Send>(
|
|
|
|
|
for (element, reference) in graphic_group.iter() {
|
|
|
|
|
match element {
|
|
|
|
|
GraphicElement::VectorData(vector_data) => {
|
|
|
|
|
result.concat(vector_data, current_transform, reference.map(|node_id| node_id.0).unwrap_or_default());
|
|
|
|
|
for instance in vector_data.instances() {
|
|
|
|
|
result.concat(instance, current_transform, reference.map(|node_id| node_id.0).unwrap_or_default());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
GraphicElement::GraphicGroup(graphic_group) => {
|
|
|
|
|
let graphic_group = graphic_group.one_item();
|
|
|
|
|
concat_group(graphic_group, current_transform * graphic_group.transform, result);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
@@ -566,25 +587,29 @@ async fn flatten_vector_elements<F: 'n + Send>(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut result = VectorData::empty();
|
|
|
|
|
concat_group(&graphic_group, DAffine2::IDENTITY, &mut result);
|
|
|
|
|
concat_group(graphic_group, DAffine2::IDENTITY, &mut result);
|
|
|
|
|
// TODO: This leads to incorrect stroke widths when flattening groups with different transforms.
|
|
|
|
|
result.style.set_stroke_transform(DAffine2::IDENTITY);
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait ConcatElement {
|
|
|
|
|
fn concat(&mut self, other: &Self, transform: DAffine2, node_id: u64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ConcatElement for GraphicGroup {
|
|
|
|
|
impl ConcatElement for GraphicGroupTable {
|
|
|
|
|
fn concat(&mut self, other: &Self, transform: DAffine2, _node_id: u64) {
|
|
|
|
|
let own = self.one_item_mut();
|
|
|
|
|
let other = other.one_item();
|
|
|
|
|
|
|
|
|
|
// TODO: Decide if we want to keep this behavior whereby the layers are flattened
|
|
|
|
|
for (mut element, footprint_mapping) in other.iter().cloned() {
|
|
|
|
|
*element.transform_mut() = transform * element.transform() * other.transform();
|
|
|
|
|
self.push((element, footprint_mapping));
|
|
|
|
|
own.push((element, footprint_mapping));
|
|
|
|
|
}
|
|
|
|
|
self.alpha_blending = other.alpha_blending;
|
|
|
|
|
|
|
|
|
|
own.alpha_blending = other.alpha_blending;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -596,10 +621,10 @@ async fn sample_points<F: 'n + Send + Copy>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
spacing: f64,
|
|
|
|
|
start_offset: f64,
|
|
|
|
|
stop_offset: f64,
|
|
|
|
|
@@ -609,9 +634,10 @@ async fn sample_points<F: 'n + Send + Copy>(
|
|
|
|
|
Footprint -> Vec<f64>,
|
|
|
|
|
)]
|
|
|
|
|
subpath_segment_lengths: impl Node<F, Output = Vec<f64>>,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
// Evaluate vector data and subpath segment lengths asynchronously.
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
let subpath_segment_lengths = subpath_segment_lengths.eval(footprint).await;
|
|
|
|
|
|
|
|
|
|
// Create an iterator over the bezier segments with enumeration and peeking capability.
|
|
|
|
|
@@ -753,7 +779,7 @@ async fn sample_points<F: 'n + Send + Copy>(
|
|
|
|
|
result.style.set_stroke_transform(vector_data.transform);
|
|
|
|
|
|
|
|
|
|
// Return the resulting vector data with newly generated points and segments.
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category(""), path(graphene_core::vector))]
|
|
|
|
|
@@ -764,22 +790,23 @@ async fn poisson_disk_points<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[default(10.)]
|
|
|
|
|
#[min(0.01)]
|
|
|
|
|
separation_disk_diameter: f64,
|
|
|
|
|
seed: SeedValue,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
|
|
|
|
let mut result = VectorData::empty();
|
|
|
|
|
|
|
|
|
|
if separation_disk_diameter <= 0.01 {
|
|
|
|
|
return result;
|
|
|
|
|
return VectorDataTable::new(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for mut subpath in vector_data.stroke_bezier_paths() {
|
|
|
|
|
@@ -812,7 +839,7 @@ async fn poisson_disk_points<F: 'n + Send>(
|
|
|
|
|
result.style = vector_data.style.clone();
|
|
|
|
|
result.style.set_stroke_transform(DAffine2::IDENTITY);
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category(""), path(graphene_core::vector))]
|
|
|
|
|
@@ -823,12 +850,13 @@ async fn subpath_segment_lengths<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
) -> Vec<f64> {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
vector_data
|
|
|
|
|
.segment_bezier_iter()
|
|
|
|
|
@@ -844,17 +872,18 @@ async fn splines_from_points<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
// Evaluate the vector data within the given footprint.
|
|
|
|
|
let mut vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let vector_data = vector_data.one_item_mut();
|
|
|
|
|
|
|
|
|
|
// Exit early if there are no points to generate splines from.
|
|
|
|
|
if vector_data.point_domain.positions().is_empty() {
|
|
|
|
|
return vector_data;
|
|
|
|
|
return VectorDataTable::new(vector_data.clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut segment_domain = SegmentDomain::default();
|
|
|
|
|
@@ -887,7 +916,7 @@ async fn splines_from_points<F: 'n + Send>(
|
|
|
|
|
}
|
|
|
|
|
vector_data.segment_domain = segment_domain;
|
|
|
|
|
|
|
|
|
|
vector_data
|
|
|
|
|
VectorDataTable::new(vector_data.clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -898,14 +927,15 @@ async fn jitter_points<F: 'n + Send>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
vector_data: impl Node<F, Output = VectorData>,
|
|
|
|
|
vector_data: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[default(5.)] amount: f64,
|
|
|
|
|
seed: SeedValue,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
let mut vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let vector_data = vector_data.eval(footprint).await;
|
|
|
|
|
let mut vector_data = vector_data.one_item().clone();
|
|
|
|
|
|
|
|
|
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
|
|
|
|
|
|
|
|
|
@@ -949,7 +979,7 @@ async fn jitter_points<F: 'n + Send>(
|
|
|
|
|
vector_data.transform = DAffine2::IDENTITY;
|
|
|
|
|
vector_data.style.set_stroke_transform(DAffine2::IDENTITY);
|
|
|
|
|
|
|
|
|
|
vector_data
|
|
|
|
|
VectorDataTable::new(vector_data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
@@ -960,23 +990,26 @@ async fn morph<F: 'n + Send + Copy>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
source: impl Node<F, Output = VectorData>,
|
|
|
|
|
source: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[expose]
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
target: impl Node<F, Output = VectorData>,
|
|
|
|
|
target: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[range((0., 1.))]
|
|
|
|
|
#[default(0.5)]
|
|
|
|
|
time: Fraction,
|
|
|
|
|
#[min(0.)] start_index: IntegerCount,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let source = source.eval(footprint).await;
|
|
|
|
|
let source = source.one_item();
|
|
|
|
|
let target = target.eval(footprint).await;
|
|
|
|
|
let target = target.one_item();
|
|
|
|
|
|
|
|
|
|
let mut result = VectorData::empty();
|
|
|
|
|
|
|
|
|
|
let time = time.clamp(0., 1.);
|
|
|
|
|
@@ -1055,7 +1088,7 @@ async fn morph<F: 'n + Send + Copy>(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn bevel_algorithm(mut vector_data: VectorData, distance: f64) -> VectorData {
|
|
|
|
|
@@ -1173,18 +1206,24 @@ async fn bevel<F: 'n + Send + Copy>(
|
|
|
|
|
)]
|
|
|
|
|
footprint: F,
|
|
|
|
|
#[implementations(
|
|
|
|
|
() -> VectorData,
|
|
|
|
|
Footprint -> VectorData,
|
|
|
|
|
() -> VectorDataTable,
|
|
|
|
|
Footprint -> VectorDataTable,
|
|
|
|
|
)]
|
|
|
|
|
source: impl Node<F, Output = VectorData>,
|
|
|
|
|
source: impl Node<F, Output = VectorDataTable>,
|
|
|
|
|
#[default(10.)] distance: Length,
|
|
|
|
|
) -> VectorData {
|
|
|
|
|
bevel_algorithm(source.eval(footprint).await, distance)
|
|
|
|
|
) -> VectorDataTable {
|
|
|
|
|
let source = source.eval(footprint).await;
|
|
|
|
|
let source = source.one_item();
|
|
|
|
|
|
|
|
|
|
let result = bevel_algorithm(source.clone(), distance);
|
|
|
|
|
|
|
|
|
|
VectorDataTable::new(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
async fn area(_: (), vector_data: impl Node<Footprint, Output = VectorData>) -> f64 {
|
|
|
|
|
async fn area(_: (), vector_data: impl Node<Footprint, Output = VectorDataTable>) -> f64 {
|
|
|
|
|
let vector_data = vector_data.eval(Footprint::default()).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
let mut area = 0.;
|
|
|
|
|
let scale = vector_data.transform.decompose_scale();
|
|
|
|
|
@@ -1195,8 +1234,9 @@ async fn area(_: (), vector_data: impl Node<Footprint, Output = VectorData>) ->
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
|
|
|
async fn centroid(_: (), vector_data: impl Node<Footprint, Output = VectorData>, centroid_type: CentroidType) -> DVec2 {
|
|
|
|
|
async fn centroid(_: (), vector_data: impl Node<Footprint, Output = VectorDataTable>, centroid_type: CentroidType) -> DVec2 {
|
|
|
|
|
let vector_data = vector_data.eval(Footprint::default()).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
|
|
|
|
|
if centroid_type == CentroidType::Area {
|
|
|
|
|
let mut area = 0.;
|
|
|
|
|
@@ -1260,8 +1300,8 @@ mod test {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn vector_node(data: Subpath<PointId>) -> FutureWrapperNode<VectorData> {
|
|
|
|
|
FutureWrapperNode(VectorData::from_subpath(data))
|
|
|
|
|
fn vector_node(data: Subpath<PointId>) -> FutureWrapperNode<VectorDataTable> {
|
|
|
|
|
FutureWrapperNode(VectorDataTable::new(VectorData::from_subpath(data)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
@@ -1270,6 +1310,7 @@ mod test {
|
|
|
|
|
let instances = 3;
|
|
|
|
|
let repeated = super::repeat(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
|
|
|
|
let vector_data = super::flatten_vector_elements(Footprint::default(), &FutureWrapperNode(repeated)).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
assert_eq!(vector_data.region_bezier_paths().count(), 3);
|
|
|
|
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
|
|
|
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
|
|
|
|
@@ -1281,6 +1322,7 @@ mod test {
|
|
|
|
|
let instances = 8;
|
|
|
|
|
let repeated = super::repeat(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
|
|
|
|
let vector_data = super::flatten_vector_elements(Footprint::default(), &FutureWrapperNode(repeated)).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
|
|
|
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
|
|
|
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
|
|
|
|
@@ -1290,6 +1332,7 @@ mod test {
|
|
|
|
|
async fn circle_repeat() {
|
|
|
|
|
let repeated = super::circular_repeat(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)), 45., 4., 8).await;
|
|
|
|
|
let vector_data = super::flatten_vector_elements(Footprint::default(), &FutureWrapperNode(repeated)).await;
|
|
|
|
|
let vector_data = vector_data.one_item();
|
|
|
|
|
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
|
|
|
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
|
|
|
let expected_angle = (index as f64 + 1.) * 45.;
|
|
|
|
|
@@ -1304,18 +1347,20 @@ mod test {
|
|
|
|
|
vector_data: vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)),
|
|
|
|
|
};
|
|
|
|
|
let bounding_box = bounding_box.eval(Footprint::default()).await;
|
|
|
|
|
let bounding_box = bounding_box.one_item();
|
|
|
|
|
assert_eq!(bounding_box.region_bezier_paths().count(), 1);
|
|
|
|
|
let subpath = bounding_box.region_bezier_paths().next().unwrap().1;
|
|
|
|
|
assert_eq!(&subpath.anchors()[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
|
|
|
|
|
|
|
|
|
|
// test a VectorData with non-zero rotation
|
|
|
|
|
// Test a VectorData with non-zero rotation
|
|
|
|
|
let mut square = VectorData::from_subpath(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE));
|
|
|
|
|
square.transform *= DAffine2::from_angle(core::f64::consts::FRAC_PI_4);
|
|
|
|
|
let bounding_box = BoundingBoxNode {
|
|
|
|
|
vector_data: FutureWrapperNode(square),
|
|
|
|
|
vector_data: FutureWrapperNode(VectorDataTable::new(square)),
|
|
|
|
|
}
|
|
|
|
|
.eval(Footprint::default())
|
|
|
|
|
.await;
|
|
|
|
|
let bounding_box = bounding_box.one_item();
|
|
|
|
|
assert_eq!(bounding_box.region_bezier_paths().count(), 1);
|
|
|
|
|
let subpath = bounding_box.region_bezier_paths().next().unwrap().1;
|
|
|
|
|
let sqrt2 = core::f64::consts::SQRT_2;
|
|
|
|
|
@@ -1329,6 +1374,7 @@ mod test {
|
|
|
|
|
let expected_points = VectorData::from_subpath(points.clone()).point_domain.positions().to_vec();
|
|
|
|
|
let copy_to_points = super::copy_to_points(Footprint::default(), &vector_node(points), &vector_node(instance), 1., 1., 0., 0, 0., 0).await;
|
|
|
|
|
let flattened_copy_to_points = super::flatten_vector_elements(Footprint::default(), &FutureWrapperNode(copy_to_points)).await;
|
|
|
|
|
let flattened_copy_to_points = flattened_copy_to_points.one_item();
|
|
|
|
|
assert_eq!(flattened_copy_to_points.region_bezier_paths().count(), expected_points.len());
|
|
|
|
|
for (index, (_, subpath)) in flattened_copy_to_points.region_bezier_paths().enumerate() {
|
|
|
|
|
let offset = expected_points[index];
|
|
|
|
|
@@ -1342,6 +1388,7 @@ mod test {
|
|
|
|
|
async fn sample_points() {
|
|
|
|
|
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
|
|
|
|
let sample_points = super::sample_points(Footprint::default(), &vector_node(path), 30., 0., 0., false, &FutureWrapperNode(vec![100.])).await;
|
|
|
|
|
let sample_points = sample_points.one_item();
|
|
|
|
|
assert_eq!(sample_points.point_domain.positions().len(), 4);
|
|
|
|
|
for (pos, expected) in sample_points.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
|
|
|
|
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
|
|
|
|
@@ -1351,6 +1398,7 @@ mod test {
|
|
|
|
|
async fn adaptive_spacing() {
|
|
|
|
|
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
|
|
|
|
let sample_points = super::sample_points(Footprint::default(), &vector_node(path), 18., 45., 10., true, &FutureWrapperNode(vec![100.])).await;
|
|
|
|
|
let sample_points = sample_points.one_item();
|
|
|
|
|
assert_eq!(sample_points.point_domain.positions().len(), 4);
|
|
|
|
|
for (pos, expected) in sample_points.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
|
|
|
|
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
|
|
|
|
@@ -1365,6 +1413,7 @@ mod test {
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
let sample_points = sample_points.one_item();
|
|
|
|
|
assert!(
|
|
|
|
|
(20..=40).contains(&sample_points.point_domain.positions().len()),
|
|
|
|
|
"actual len {}",
|
|
|
|
|
@@ -1383,6 +1432,7 @@ mod test {
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn spline() {
|
|
|
|
|
let spline = splines_from_points(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.))).await;
|
|
|
|
|
let spline = spline.one_item();
|
|
|
|
|
assert_eq!(spline.stroke_bezier_paths().count(), 1);
|
|
|
|
|
assert_eq!(spline.point_domain.positions(), &[DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]);
|
|
|
|
|
}
|
|
|
|
|
@@ -1391,6 +1441,7 @@ mod test {
|
|
|
|
|
let source = Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.);
|
|
|
|
|
let target = Subpath::new_ellipse(DVec2::NEG_ONE * 100., DVec2::ZERO);
|
|
|
|
|
let sample_points = super::morph(Footprint::default(), &vector_node(source), &vector_node(target), 0.5, 0).await;
|
|
|
|
|
let sample_points = sample_points.one_item();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
&sample_points.point_domain.positions()[..4],
|
|
|
|
|
vec![DVec2::new(-25., -50.), DVec2::new(50., -25.), DVec2::new(25., 50.), DVec2::new(-50., 25.)]
|
|
|
|
|
@@ -1408,6 +1459,8 @@ mod test {
|
|
|
|
|
async fn bevel_rect() {
|
|
|
|
|
let source = Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.);
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &vector_node(source), 5.).await;
|
|
|
|
|
let beveled = beveled.one_item();
|
|
|
|
|
|
|
|
|
|
assert_eq!(beveled.point_domain.positions().len(), 8);
|
|
|
|
|
assert_eq!(beveled.segment_domain.ids().len(), 8);
|
|
|
|
|
|
|
|
|
|
@@ -1429,6 +1482,7 @@ mod test {
|
|
|
|
|
let curve = Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::new(10., 0.), DVec2::new(10., 100.), DVec2::X * 100.);
|
|
|
|
|
let source = Subpath::from_beziers(&[Bezier::from_linear_dvec2(DVec2::X * -100., DVec2::ZERO), curve], false);
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &vector_node(source), 5.).await;
|
|
|
|
|
let beveled = beveled.one_item();
|
|
|
|
|
|
|
|
|
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
|
|
|
|
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
|
|
|
|
@@ -1449,7 +1503,8 @@ mod test {
|
|
|
|
|
let mut vector_data = VectorData::from_subpath(source);
|
|
|
|
|
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.));
|
|
|
|
|
vector_data.transform = transform;
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &FutureWrapperNode(vector_data), 5.).await;
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &FutureWrapperNode(VectorDataTable::new(vector_data)), 5.).await;
|
|
|
|
|
let beveled = beveled.one_item();
|
|
|
|
|
|
|
|
|
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
|
|
|
|
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
|
|
|
|
@@ -1468,6 +1523,8 @@ mod test {
|
|
|
|
|
async fn bevel_too_high() {
|
|
|
|
|
let source = Subpath::from_anchors([DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)], false);
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &vector_node(source), 999.).await;
|
|
|
|
|
let beveled = beveled.one_item();
|
|
|
|
|
|
|
|
|
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
|
|
|
|
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
|
|
|
|
|
|
|
|
|
@@ -1487,6 +1544,7 @@ mod test {
|
|
|
|
|
let point = Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::ZERO, DVec2::ZERO);
|
|
|
|
|
let source = Subpath::from_beziers(&[Bezier::from_linear_dvec2(DVec2::X * -100., DVec2::ZERO), point, curve], false);
|
|
|
|
|
let beveled = super::bevel(Footprint::default(), &vector_node(source), 5.).await;
|
|
|
|
|
let beveled = beveled.one_item();
|
|
|
|
|
|
|
|
|
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
|
|
|
|
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
|
|
|
|
|