mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-08 13:08:12 +08:00
Convert the per-lane vector geometry nodes and the level measures to record kernels
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
use core_types::attribute::{Attr, EditorLayerPath, RemoveAttr};
|
use core_types::attribute::{Attr, EditorLayerPath, RemoveAttr, Transform as TransformAttr};
|
||||||
use core_types::gpoll::{GraphError, Interrupt};
|
use core_types::gpoll::{GraphError, Interrupt};
|
||||||
use core_types::list::List;
|
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{ATTR_TRANSFORM, Ctx, ExtractIndex, InjectIndex};
|
use core_types::{Ctx, ExtractIndex, InjectIndex};
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
use vector_types::markers::EditorClickTarget;
|
use vector_types::markers::EditorClickTarget;
|
||||||
@@ -41,16 +40,12 @@ fn path_modify<'e>(
|
|||||||
|
|
||||||
/// Applies the vector path's local transformation to its geometry and resets the transform to the identity.
|
/// Applies the vector path's local transformation to its geometry and resets the transform to the identity.
|
||||||
#[node_macro::node(category("Vector"))]
|
#[node_macro::node(category("Vector"))]
|
||||||
fn apply_transform(_ctx: impl Ctx, mut vector: List<Vector>) -> List<Vector> {
|
fn apply_transform(_ctx: impl Ctx, (mut vector, transform): (Vector, Attr<TransformAttr>)) -> (Vector, Attr<TransformAttr>) {
|
||||||
let (elements, transforms) = vector.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
let transform: DAffine2 = *transform;
|
||||||
for (element, transform) in elements.iter_mut().zip(transforms.iter_mut()) {
|
for (_, point) in vector.point_domain.positions_mut() {
|
||||||
for (_, point) in element.point_domain.positions_mut() {
|
|
||||||
*point = transform.transform_point2(*point);
|
*point = transform.transform_point2(*point);
|
||||||
}
|
}
|
||||||
element.segment_domain.transform(*transform);
|
vector.segment_domain.transform(transform);
|
||||||
|
|
||||||
*transform = DAffine2::IDENTITY;
|
(vector, Attr(DAffine2::IDENTITY))
|
||||||
}
|
|
||||||
|
|
||||||
vector
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::f64::consts::{PI, TAU};
|
use core::f64::consts::{PI, TAU};
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
|
use core_types::attribute::Transform as TransformAttr;
|
||||||
|
use core_types::attribute::{Attr, BlendMode as BlendModeAttr, ClippingMask, EditorLayerPath, Opacity, OpacityFill};
|
||||||
use core_types::blending::BlendMode;
|
use core_types::blending::BlendMode;
|
||||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::gpoll::Interrupt;
|
|
||||||
use core_types::list::{Item, ItemAttributeValues, List, ListDyn};
|
|
||||||
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
|
|
||||||
use core_types::transform::{Footprint, Transform};
|
|
||||||
use core_types::uuid::NodeId;
|
|
||||||
use core_types::attribute::{Attr, BlendMode as BlendModeAttr, ClippingMask, EditorLayerPath, Opacity, OpacityFill};
|
|
||||||
use core_types::context::IndexLink;
|
use core_types::context::IndexLink;
|
||||||
use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
||||||
use core_types::gpoll::{Extent, GPoll};
|
|
||||||
use core_types::gpoll::GraphError;
|
use core_types::gpoll::GraphError;
|
||||||
|
use core_types::gpoll::Interrupt;
|
||||||
|
use core_types::gpoll::{Extent, GPoll};
|
||||||
|
use core_types::list::{Item, ItemAttributeValues, List, ListDyn};
|
||||||
|
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
|
||||||
|
use core_types::transform::Transform;
|
||||||
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
|
||||||
use core_types::attribute::Transform as TransformAttr;
|
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
use graphic_types::graphic::{bake_paint_transforms, graphic_list_at, has_paint_at, is_paint_present, set_paint_attribute_at};
|
use graphic_types::graphic::{bake_paint_transforms, graphic_list_at, has_paint_at, is_paint_present, set_paint_attribute_at};
|
||||||
|
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList};
|
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList};
|
||||||
use kurbo::simplify::{SimplifyOptions, simplify_bezpath};
|
use kurbo::simplify::{SimplifyOptions, simplify_bezpath};
|
||||||
@@ -27,7 +27,6 @@ use rand::{Rng, SeedableRng};
|
|||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use vector_types::gradient::{build_transform_with_y_preservation, initial_gradient_transform_for_bounding_box};
|
use vector_types::gradient::{build_transform_with_y_preservation, initial_gradient_transform_for_bounding_box};
|
||||||
use vector_types::subpath::{BezierHandles, ManipulatorGroup};
|
use vector_types::subpath::{BezierHandles, ManipulatorGroup};
|
||||||
use vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
|
|
||||||
use vector_types::vector::PointDomain;
|
use vector_types::vector::PointDomain;
|
||||||
use vector_types::vector::algorithms::bezpath_algorithms::{self, TValue, eval_pathseg_euclidean, evaluate_bezpath, split_bezpath, tangent_on_bezpath};
|
use vector_types::vector::algorithms::bezpath_algorithms::{self, TValue, eval_pathseg_euclidean, evaluate_bezpath, split_bezpath, tangent_on_bezpath};
|
||||||
use vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
use vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||||
@@ -39,6 +38,7 @@ use vector_types::vector::misc::{
|
|||||||
};
|
};
|
||||||
use vector_types::vector::style::{GradientStops, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
use vector_types::vector::style::{GradientStops, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
use vector_types::vector::{FillId, PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorExt};
|
use vector_types::vector::{FillId, PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorExt};
|
||||||
|
use vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
|
||||||
use vector_types::{GradientSpreadMethod, GradientType};
|
use vector_types::{GradientSpreadMethod, GradientType};
|
||||||
|
|
||||||
/// The standard row attributes a per-lane re-emission carries from its
|
/// The standard row attributes a per-lane re-emission carries from its
|
||||||
@@ -101,9 +101,7 @@ fn assign_colors<'e>(
|
|||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
}
|
}
|
||||||
let element = content.element_ref(lane).clone();
|
let element = content.element_ref(lane).clone();
|
||||||
let park_existing = |paint: Option<&List<Graphic>>| -> Result<Option<&'e List<Graphic>>, Interrupt> {
|
let park_existing = |paint: Option<&List<Graphic>>| -> Result<Option<&'e List<Graphic>>, Interrupt> { paint.map(|paint| park_paint(ctx.arena(), paint.clone())).transpose() };
|
||||||
paint.map(|paint| park_paint(ctx.arena(), paint.clone())).transpose()
|
|
||||||
};
|
|
||||||
let existing_fill = park_existing(content.lane(lane).attr::<Fill>())?;
|
let existing_fill = park_existing(content.lane(lane).attr::<Fill>())?;
|
||||||
let existing_stroke = park_existing(content.lane(lane).attr::<StrokeAttr>())?;
|
let existing_stroke = park_existing(content.lane(lane).attr::<StrokeAttr>())?;
|
||||||
let carried = carried_lane_attrs(ctx.arena(), content.lane(lane))?;
|
let carried = carried_lane_attrs(ctx.arena(), content.lane(lane))?;
|
||||||
@@ -161,8 +159,7 @@ fn assign_colors_extent(
|
|||||||
fn assign_colors_graphic<'e>(
|
fn assign_colors_graphic<'e>(
|
||||||
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
||||||
content: IList<Graphic>,
|
content: IList<Graphic>,
|
||||||
#[default(true)]
|
#[default(true)] fill: bool,
|
||||||
fill: bool,
|
|
||||||
stroke: bool,
|
stroke: bool,
|
||||||
gradient: IList<GradientStops>,
|
gradient: IList<GradientStops>,
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
@@ -316,8 +313,7 @@ fn fill<'e>(
|
|||||||
fn fill_graphic_leveled<'e>(
|
fn fill_graphic_leveled<'e>(
|
||||||
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
||||||
(element, _content_fill): (Graphic, Attr<'e, Fill>),
|
(element, _content_fill): (Graphic, Attr<'e, Fill>),
|
||||||
#[default(Color::BLACK)]
|
#[default(Color::BLACK)] fill: IList<Graphic>,
|
||||||
fill: IList<Graphic>,
|
|
||||||
_backup_color: IList<Color>,
|
_backup_color: IList<Color>,
|
||||||
_backup_gradient: IList<GradientStops>,
|
_backup_gradient: IList<GradientStops>,
|
||||||
_gradient_type: GradientType,
|
_gradient_type: GradientType,
|
||||||
@@ -415,20 +411,17 @@ fn for_each_interior_vector_mut(element: &mut Graphic, mut f: impl FnMut(&mut Ve
|
|||||||
fn stroke_graphic_leveled<'e>(
|
fn stroke_graphic_leveled<'e>(
|
||||||
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
||||||
(element, content_transform): (Graphic, Attr<TransformAttr>),
|
(element, content_transform): (Graphic, Attr<TransformAttr>),
|
||||||
#[default(Color::BLACK)]
|
#[default(Color::BLACK)] paint: IList<Graphic>,
|
||||||
paint: IList<Graphic>,
|
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[default(2.)]
|
#[default(2.)]
|
||||||
weight: f64,
|
weight: f64,
|
||||||
align: StrokeAlign,
|
align: StrokeAlign,
|
||||||
cap: StrokeCap,
|
cap: StrokeCap,
|
||||||
join: StrokeJoin,
|
join: StrokeJoin,
|
||||||
#[default(4.)]
|
#[default(4.)] miter_limit: f64,
|
||||||
miter_limit: f64,
|
|
||||||
paint_order: PaintOrder,
|
paint_order: PaintOrder,
|
||||||
dash_lengths: IList<f64>,
|
dash_lengths: IList<f64>,
|
||||||
#[unit(" px")]
|
#[unit(" px")] dash_offset: f64,
|
||||||
dash_offset: f64,
|
|
||||||
) -> Result<(Graphic, Attr<TransformAttr>, Attr<'e, StrokeAttr>), Interrupt> {
|
) -> Result<(Graphic, Attr<TransformAttr>, Attr<'e, StrokeAttr>), Interrupt> {
|
||||||
let dash_lengths = (0..dash_lengths.len()).map(|index| dash_lengths.get(index).max(0.)).collect();
|
let dash_lengths = (0..dash_lengths.len()).map(|index| dash_lengths.get(index).max(0.)).collect();
|
||||||
let stroke = Stroke {
|
let stroke = Stroke {
|
||||||
@@ -570,7 +563,7 @@ fn copy_to_points_extent(
|
|||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn round_corners(
|
fn round_corners(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
source: List<Vector>,
|
(source, transform): (Vector, Attr<TransformAttr>),
|
||||||
#[hard(0..)]
|
#[hard(0..)]
|
||||||
#[default(10.)]
|
#[default(10.)]
|
||||||
radius: PixelLength,
|
radius: PixelLength,
|
||||||
@@ -583,13 +576,9 @@ fn round_corners(
|
|||||||
#[hard(0..180)]
|
#[hard(0..180)]
|
||||||
#[default(5.)]
|
#[default(5.)]
|
||||||
min_angle_threshold: Angle,
|
min_angle_threshold: Angle,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
(0..source.len())
|
let source_transform: DAffine2 = *transform;
|
||||||
.map(|index| {
|
|
||||||
let source_transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
|
||||||
let source_transform_inverse = source_transform.inverse();
|
let source_transform_inverse = source_transform.inverse();
|
||||||
let attributes = source.clone_item_attributes(index);
|
|
||||||
let source = source.element(index).unwrap();
|
|
||||||
|
|
||||||
// Flip the roundness to help with user intuition
|
// Flip the roundness to help with user intuition
|
||||||
let roundness = 1. - roundness;
|
let roundness = 1. - roundness;
|
||||||
@@ -674,37 +663,23 @@ fn round_corners(
|
|||||||
result.append_bezpath(rounded_subpath);
|
result.append_bezpath(rounded_subpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::from_parts(result, attributes)
|
(result, Attr(source_transform))
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(name("Merge by Distance"), category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(name("Merge by Distance"), category("Vector: Modifier"), path(core_types::vector))]
|
||||||
pub fn merge_by_distance(
|
pub fn merge_by_distance(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
content: List<Vector>,
|
(mut content, transform): (Vector, Attr<TransformAttr>),
|
||||||
#[default(0.1)]
|
#[default(0.1)]
|
||||||
#[hard(0.0001..)]
|
#[hard(0.0001..)]
|
||||||
distance: PixelLength,
|
distance: PixelLength,
|
||||||
algorithm: MergeByDistanceAlgorithm,
|
algorithm: MergeByDistanceAlgorithm,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
match algorithm {
|
match algorithm {
|
||||||
MergeByDistanceAlgorithm::Spatial => content
|
MergeByDistanceAlgorithm::Spatial => content.merge_by_distance_spatial(*transform, distance),
|
||||||
.into_iter()
|
MergeByDistanceAlgorithm::Topological => content.merge_by_distance_topological(distance),
|
||||||
.map(|mut row| {
|
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
row.element_mut().merge_by_distance_spatial(transform, distance);
|
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
MergeByDistanceAlgorithm::Topological => content
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
row.element_mut().merge_by_distance_topological(distance);
|
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
|
(content, Attr(*transform))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod extrude_algorithms {
|
pub mod extrude_algorithms {
|
||||||
@@ -905,23 +880,19 @@ pub mod extrude_algorithms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn extrude(_: impl Ctx, mut source: List<Vector>, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> List<Vector> {
|
fn extrude(_: impl Ctx, mut source: Vector, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> Vector {
|
||||||
for vector in source.iter_element_values_mut() {
|
extrude_algorithms::extrude(&mut source, direction, joining_algorithm);
|
||||||
extrude_algorithms::extrude(vector, direction, joining_algorithm);
|
|
||||||
}
|
|
||||||
source
|
source
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn box_warp(_: impl Ctx, content: List<Vector>, #[expose] rectangle: List<Vector>) -> List<Vector> {
|
fn box_warp(_: impl Ctx + ExtractIndex + InjectIndex + Copy, (vector, transform): (Vector, Attr<TransformAttr>), #[expose] rectangle: IList<Vector>) -> (Vector, Attr<TransformAttr>) {
|
||||||
let Some(target) = rectangle.element(0).cloned() else { return content };
|
if rectangle.is_empty() {
|
||||||
let target_transform: DAffine2 = rectangle.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
return (vector, Attr(*transform));
|
||||||
|
}
|
||||||
content
|
let target = rectangle.element_ref(0);
|
||||||
.into_iter()
|
let target_transform: DAffine2 = rectangle.lane(0).attr::<TransformAttr>();
|
||||||
.map(|mut row| {
|
let transform: DAffine2 = *transform;
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let vector = std::mem::take(row.element_mut());
|
|
||||||
|
|
||||||
// Get the bounding box of the source vector geometry
|
// Get the bounding box of the source vector geometry
|
||||||
let source_bbox = vector.bounding_box_with_transform(transform).unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
let source_bbox = vector.bounding_box_with_transform(transform).unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
||||||
@@ -978,12 +949,8 @@ fn box_warp(_: impl Ctx, content: List<Vector>, #[expose] rectangle: List<Vector
|
|||||||
|
|
||||||
result.set_stroke_transform(DAffine2::IDENTITY);
|
result.set_stroke_transform(DAffine2::IDENTITY);
|
||||||
|
|
||||||
// Add this to the `List` and reset the transform since we've applied it directly to the points
|
// Reset the transform since we've applied it directly to the points
|
||||||
*row.element_mut() = result;
|
(result, Attr(DAffine2::IDENTITY))
|
||||||
row.set_attribute(ATTR_TRANSFORM, DAffine2::IDENTITY);
|
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate within a quadrilateral using normalized coordinates (0-1)
|
// Interpolate within a quadrilateral using normalized coordinates (0-1)
|
||||||
@@ -1121,7 +1088,7 @@ where
|
|||||||
#[node_macro::node(category("Vector: Modifier"), name("Auto-Tangents"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), name("Auto-Tangents"), path(core_types::vector))]
|
||||||
fn auto_tangents(
|
fn auto_tangents(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
source: List<Vector>,
|
(source, lane_transform): (Vector, Attr<TransformAttr>),
|
||||||
/// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread).
|
/// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread).
|
||||||
#[default(0.5)]
|
#[default(0.5)]
|
||||||
#[range]
|
#[range]
|
||||||
@@ -1130,12 +1097,8 @@ fn auto_tangents(
|
|||||||
/// If active, existing non-zero handles won't be affected.
|
/// If active, existing non-zero handles won't be affected.
|
||||||
#[default(true)]
|
#[default(true)]
|
||||||
preserve_existing: bool,
|
preserve_existing: bool,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
(0..source.len())
|
let transform: DAffine2 = *lane_transform;
|
||||||
.map(|index| {
|
|
||||||
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
|
||||||
let attributes = source.clone_item_attributes(index);
|
|
||||||
let source = source.element(index).unwrap();
|
|
||||||
|
|
||||||
let mut result = Vector {
|
let mut result = Vector {
|
||||||
stroke: source.stroke.clone(),
|
stroke: source.stroke.clone(),
|
||||||
@@ -1267,18 +1230,11 @@ fn auto_tangents(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::from_parts(result, attributes)
|
(result, Attr(transform))
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn bounding_box(_: impl Ctx, content: List<Vector>) -> List<Vector> {
|
fn bounding_box(_: impl Ctx, vector: Vector) -> Vector {
|
||||||
content
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
let vector = std::mem::take(row.element_mut());
|
|
||||||
|
|
||||||
let mut result = vector
|
let mut result = vector
|
||||||
.bounding_box_rect()
|
.bounding_box_rect()
|
||||||
.map(|bbox| {
|
.map(|bbox| {
|
||||||
@@ -1291,16 +1247,13 @@ fn bounding_box(_: impl Ctx, content: List<Vector>) -> List<Vector> {
|
|||||||
result.stroke = vector.stroke.clone();
|
result.stroke = vector.stroke.clone();
|
||||||
result.set_stroke_transform(DAffine2::IDENTITY);
|
result.set_stroke_transform(DAffine2::IDENTITY);
|
||||||
|
|
||||||
*row.element_mut() = result;
|
result
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn dimensions(_: impl Ctx, content: List<Vector>) -> DVec2 {
|
fn dimensions(_: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Vector>) -> DVec2 {
|
||||||
(0..content.len())
|
(0..content.len())
|
||||||
.filter_map(|index| content.element(index).unwrap().bounding_box_with_transform(content.attribute_cloned_or_default(ATTR_TRANSFORM, index)))
|
.filter_map(|index| content.element_ref(index).bounding_box_with_transform(content.lane(index).attr::<TransformAttr>()))
|
||||||
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
|
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
|
||||||
.map(|[top_left, bottom_right]| bottom_right - top_left)
|
.map(|[top_left, bottom_right]| bottom_right - top_left)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@@ -1308,18 +1261,17 @@ fn dimensions(_: impl Ctx, content: List<Vector>) -> DVec2 {
|
|||||||
|
|
||||||
/// Type-asserts a value to be vector data.
|
/// Type-asserts a value to be vector data.
|
||||||
#[node_macro::node(category("Vector"), name("As Vector"), path(core_types::vector))]
|
#[node_macro::node(category("Vector"), name("As Vector"), path(core_types::vector))]
|
||||||
fn as_vector(_: impl Ctx, value: List<Vector>) -> List<Vector> {
|
fn as_vector(_: impl Ctx, value: Vector) -> Vector {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a polyline from a series of vector points, replacing any existing segments and regions that may already exist.
|
/// Creates a polyline from a series of vector points, replacing any existing segments and regions that may already exist.
|
||||||
#[node_macro::node(category("Vector"), name("Points to Polyline"), path(core_types::vector))]
|
#[node_macro::node(category("Vector"), name("Points to Polyline"), path(core_types::vector))]
|
||||||
fn points_to_polyline(_: impl Ctx, mut points: List<Vector>, #[default(true)] closed: bool) -> List<Vector> {
|
fn points_to_polyline(_: impl Ctx, mut points: Vector, #[default(true)] closed: bool) -> Vector {
|
||||||
for vector in points.iter_element_values_mut() {
|
|
||||||
let mut segment_domain = SegmentDomain::new();
|
let mut segment_domain = SegmentDomain::new();
|
||||||
let mut next_id = SegmentId::ZERO;
|
let mut next_id = SegmentId::ZERO;
|
||||||
|
|
||||||
let points_count = vector.point_domain.ids().len();
|
let points_count = points.point_domain.ids().len();
|
||||||
|
|
||||||
if points_count >= 2 {
|
if points_count >= 2 {
|
||||||
(0..points_count - 1).for_each(|i| {
|
(0..points_count - 1).for_each(|i| {
|
||||||
@@ -1329,26 +1281,21 @@ fn points_to_polyline(_: impl Ctx, mut points: List<Vector>, #[default(true)] cl
|
|||||||
if closed && points_count != 2 {
|
if closed && points_count != 2 {
|
||||||
segment_domain.push(next_id.next_id(), points_count - 1, 0, BezierHandles::Linear, StrokeId::generate());
|
segment_domain.push(next_id.next_id(), points_count - 1, 0, BezierHandles::Linear, StrokeId::generate());
|
||||||
|
|
||||||
vector
|
points
|
||||||
.region_domain
|
.region_domain
|
||||||
.push(RegionId::generate(), segment_domain.ids()[0]..=*segment_domain.ids().last().unwrap(), FillId::generate());
|
.push(RegionId::generate(), segment_domain.ids()[0]..=*segment_domain.ids().last().unwrap(), FillId::generate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.segment_domain = segment_domain;
|
points.segment_domain = segment_domain;
|
||||||
}
|
|
||||||
|
|
||||||
points
|
points
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("offset_path_properties"))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("offset_path_properties"))]
|
||||||
fn offset_path(_: impl Ctx, content: List<Vector>, distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> List<Vector> {
|
fn offset_path(_: impl Ctx, (vector, lane_transform): (Vector, Attr<TransformAttr>), distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> (Vector, Attr<TransformAttr>) {
|
||||||
content
|
let transform_attribute: DAffine2 = *lane_transform;
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let vector = std::mem::take(row.element_mut());
|
|
||||||
|
|
||||||
let bezpaths = vector.stroke_bezpath_iter();
|
let bezpaths = vector.stroke_bezpath_iter();
|
||||||
let mut result = Vector {
|
let mut result = Vector {
|
||||||
@@ -1379,10 +1326,7 @@ fn offset_path(_: impl Ctx, content: List<Vector>, distance: f64, join: StrokeJo
|
|||||||
result.append_bezpath(bezpath_out);
|
result.append_bezpath(bezpath_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
*row.element_mut() = result;
|
(result, Attr(transform_attribute))
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solidify_stroke_core(graphic_list: List<Graphic>) -> List<Vector> {
|
fn solidify_stroke_core(graphic_list: List<Graphic>) -> List<Vector> {
|
||||||
@@ -1610,10 +1554,12 @@ fn solidify_stroke<'e>(
|
|||||||
/// count as a lower bound and consumers drain to the past-end signal.
|
/// count as a lower bound and consumers drain to the past-end signal.
|
||||||
fn solidify_stroke_extent(content: ListIn<'_, Graphic>, level: LevelIn) -> GPoll<Extent> {
|
fn solidify_stroke_extent(content: ListIn<'_, Graphic>, level: LevelIn) -> GPoll<Extent> {
|
||||||
match level.top() {
|
match level.top() {
|
||||||
true => content.total().map(|total| Extent::AtLeast(match total {
|
true => content.total().map(|total| {
|
||||||
|
Extent::AtLeast(match total {
|
||||||
Extent::Exactly(count) | Extent::AtLeast(count) => count,
|
Extent::Exactly(count) | Extent::AtLeast(count) => count,
|
||||||
Extent::Free => 0,
|
Extent::Free => 0,
|
||||||
})),
|
})
|
||||||
|
}),
|
||||||
false => GPoll::Final(Extent::Exactly(1)),
|
false => GPoll::Final(Extent::Exactly(1)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1644,10 +1590,12 @@ fn solidify_stroke_vector<'e>(
|
|||||||
|
|
||||||
fn solidify_stroke_vector_extent(content: ListIn<'_, Vector>, level: LevelIn) -> GPoll<Extent> {
|
fn solidify_stroke_vector_extent(content: ListIn<'_, Vector>, level: LevelIn) -> GPoll<Extent> {
|
||||||
match level.top() {
|
match level.top() {
|
||||||
true => content.total().map(|total| Extent::AtLeast(match total {
|
true => content.total().map(|total| {
|
||||||
|
Extent::AtLeast(match total {
|
||||||
Extent::Exactly(count) | Extent::AtLeast(count) => count,
|
Extent::Exactly(count) | Extent::AtLeast(count) => count,
|
||||||
Extent::Free => 0,
|
Extent::Free => 0,
|
||||||
})),
|
})
|
||||||
|
}),
|
||||||
false => GPoll::Final(Extent::Exactly(1)),
|
false => GPoll::Final(Extent::Exactly(1)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1720,7 +1668,17 @@ fn map_points(ctx: impl Ctx + DeriveCtx, content: List<Vector>, mapped: impl Nod
|
|||||||
fn flatten_path_core<'e>(
|
fn flatten_path_core<'e>(
|
||||||
arena: &'e core_types::arena::Arena,
|
arena: &'e core_types::arena::Arena,
|
||||||
graphic_list: List<Graphic>,
|
graphic_list: List<Graphic>,
|
||||||
) -> Result<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>, Attr<'e, EditorMergedLayers>), Interrupt> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Vector,
|
||||||
|
Attr<'e, TransformAttr>,
|
||||||
|
Attr<'e, Fill>,
|
||||||
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, EditorLayerPath>,
|
||||||
|
Attr<'e, EditorMergedLayers>,
|
||||||
|
),
|
||||||
|
Interrupt,
|
||||||
|
> {
|
||||||
let flattened = graphic_list.clone().into_flattened_list::<Vector>();
|
let flattened = graphic_list.clone().into_flattened_list::<Vector>();
|
||||||
|
|
||||||
let mut output = Vector::default();
|
let mut output = Vector::default();
|
||||||
@@ -1787,7 +1745,17 @@ fn flatten_path_core<'e>(
|
|||||||
pub fn flatten_path<'e>(
|
pub fn flatten_path<'e>(
|
||||||
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
||||||
content: IList<Graphic>,
|
content: IList<Graphic>,
|
||||||
) -> Result<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>, Attr<'e, EditorMergedLayers>), Interrupt> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Vector,
|
||||||
|
Attr<'e, TransformAttr>,
|
||||||
|
Attr<'e, Fill>,
|
||||||
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, EditorLayerPath>,
|
||||||
|
Attr<'e, EditorMergedLayers>,
|
||||||
|
),
|
||||||
|
Interrupt,
|
||||||
|
> {
|
||||||
// SAFETY: a materialized input's frames are arena-resident.
|
// SAFETY: a materialized input's frames are arena-resident.
|
||||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||||
let content = graphic_types::graphic::run_to_render_list::<Graphic>(&item).expect("the run holds the row's element type");
|
let content = graphic_types::graphic::run_to_render_list::<Graphic>(&item).expect("the run holds the row's element type");
|
||||||
@@ -1800,7 +1768,17 @@ pub fn flatten_path<'e>(
|
|||||||
pub fn flatten_path_vector<'e>(
|
pub fn flatten_path_vector<'e>(
|
||||||
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
|
||||||
content: IList<Vector>,
|
content: IList<Vector>,
|
||||||
) -> Result<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>, Attr<'e, EditorMergedLayers>), Interrupt> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Vector,
|
||||||
|
Attr<'e, TransformAttr>,
|
||||||
|
Attr<'e, Fill>,
|
||||||
|
Attr<'e, StrokeAttr>,
|
||||||
|
Attr<'e, EditorLayerPath>,
|
||||||
|
Attr<'e, EditorMergedLayers>,
|
||||||
|
),
|
||||||
|
Interrupt,
|
||||||
|
> {
|
||||||
// SAFETY: a materialized input's frames are arena-resident.
|
// SAFETY: a materialized input's frames are arena-resident.
|
||||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||||
let content = graphic_types::graphic::run_to_render_list::<Vector>(&item)
|
let content = graphic_types::graphic::run_to_render_list::<Vector>(&item)
|
||||||
@@ -1896,31 +1874,28 @@ fn sample_polyline(
|
|||||||
fn simplify(
|
fn simplify(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// The vector paths to simplify.
|
/// The vector paths to simplify.
|
||||||
content: List<Vector>,
|
(content, lane_transform): (Vector, Attr<TransformAttr>),
|
||||||
/// The maximum distance the simplified path may deviate from the original.
|
/// The maximum distance the simplified path may deviate from the original.
|
||||||
#[default(5.)]
|
#[default(5.)]
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
tolerance: Length,
|
tolerance: Length,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
if tolerance <= 0. {
|
if tolerance <= 0. {
|
||||||
return content;
|
return (content, Attr(*lane_transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = SimplifyOptions::default();
|
let options = SimplifyOptions::default();
|
||||||
|
|
||||||
content
|
let transform_attribute: DAffine2 = *lane_transform;
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let inverse_transform = transform.inverse();
|
let inverse_transform = transform.inverse();
|
||||||
|
|
||||||
let mut result = Vector {
|
let mut result = Vector {
|
||||||
stroke: std::mem::take(&mut row.element_mut().stroke),
|
stroke: content.stroke.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for mut bezpath in row.element().stroke_bezpath_iter() {
|
for mut bezpath in content.stroke_bezpath_iter() {
|
||||||
bezpath.apply_affine(transform);
|
bezpath.apply_affine(transform);
|
||||||
|
|
||||||
let mut simplified = simplify_bezpath(bezpath, tolerance, &options);
|
let mut simplified = simplify_bezpath(bezpath, tolerance, &options);
|
||||||
@@ -1929,10 +1904,7 @@ fn simplify(
|
|||||||
result.append_bezpath(simplified);
|
result.append_bezpath(simplified);
|
||||||
}
|
}
|
||||||
|
|
||||||
*row.element_mut() = result;
|
(result, Attr(transform_attribute))
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decimates vector paths into polylines by sampling any curves into line segments, then removing points that don't significantly contribute to the shape using the Ramer-Douglas-Peucker algorithm.
|
/// Decimates vector paths into polylines by sampling any curves into line segments, then removing points that don't significantly contribute to the shape using the Ramer-Douglas-Peucker algorithm.
|
||||||
@@ -1940,15 +1912,15 @@ fn simplify(
|
|||||||
fn decimate(
|
fn decimate(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// The vector paths to decimate.
|
/// The vector paths to decimate.
|
||||||
content: List<Vector>,
|
(content, lane_transform): (Vector, Attr<TransformAttr>),
|
||||||
/// The maximum distance a point can deviate from the simplified path before it is kept.
|
/// The maximum distance a point can deviate from the simplified path before it is kept.
|
||||||
#[default(5.)]
|
#[default(5.)]
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
tolerance: Length,
|
tolerance: Length,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
// Tolerance of 0 means no simplification is possible, so return immediately
|
// Tolerance of 0 means no simplification is possible, so return immediately
|
||||||
if tolerance <= 0. {
|
if tolerance <= 0. {
|
||||||
return content;
|
return (content, Attr(*lane_transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below this squared length, a line segment is treated as a degenerate point and the distance
|
// Below this squared length, a line segment is treated as a degenerate point and the distance
|
||||||
@@ -2004,19 +1976,16 @@ fn decimate(
|
|||||||
points.iter().enumerate().filter(|(i, _)| keep[*i]).map(|(_, p)| *p).collect()
|
points.iter().enumerate().filter(|(i, _)| keep[*i]).map(|(_, p)| *p).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
content
|
let transform_attribute: DAffine2 = *lane_transform;
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let inverse_transform = transform.inverse();
|
let inverse_transform = transform.inverse();
|
||||||
|
|
||||||
let mut result = Vector {
|
let mut result = Vector {
|
||||||
stroke: std::mem::take(&mut row.element_mut().stroke),
|
stroke: content.stroke.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for mut bezpath in row.element().stroke_bezpath_iter() {
|
for mut bezpath in content.stroke_bezpath_iter() {
|
||||||
bezpath.apply_affine(transform);
|
bezpath.apply_affine(transform);
|
||||||
|
|
||||||
let is_closed = matches!(bezpath.elements().last(), Some(PathEl::ClosePath));
|
let is_closed = matches!(bezpath.elements().last(), Some(PathEl::ClosePath));
|
||||||
@@ -2055,10 +2024,7 @@ fn decimate(
|
|||||||
result.append_bezpath(new_bezpath);
|
result.append_bezpath(new_bezpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
*row.element_mut() = result;
|
(result, Attr(transform_attribute))
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cuts a path at a given progression from 0 to 1 along the path, creating two new subpaths from the original one (if the path is initially open) or one open subpath (if the path is initially closed).
|
/// Cuts a path at a given progression from 0 to 1 along the path, creating two new subpaths from the original one (if the path is initially open) or one open subpath (if the path is initially closed).
|
||||||
@@ -2116,9 +2082,10 @@ fn cut_path(
|
|||||||
|
|
||||||
/// Cuts path segments into separate disconnected pieces where each is a distinct subpath.
|
/// Cuts path segments into separate disconnected pieces where each is a distinct subpath.
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn cut_segments(_: impl Ctx, mut content: List<Vector>) -> List<Vector> {
|
fn cut_segments(_: impl Ctx, mut content: Vector) -> Vector {
|
||||||
// Iterate through every segment and make a copy of each of its endpoints, then reassign each segment's endpoints to its own unique point copy
|
// Make a copy of each segment's endpoints, then reassign each segment's endpoints to its own unique point copy
|
||||||
for vector in content.iter_element_values_mut() {
|
{
|
||||||
|
let vector = &mut content;
|
||||||
let points_count = vector.point_domain.ids().len();
|
let points_count = vector.point_domain.ids().len();
|
||||||
let segments_count = vector.segment_domain.ids().len();
|
let segments_count = vector.segment_domain.ids().len();
|
||||||
|
|
||||||
@@ -2176,9 +2143,9 @@ fn cut_segments(_: impl Ctx, mut content: List<Vector>) -> List<Vector> {
|
|||||||
/// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it.
|
/// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it.
|
||||||
#[node_macro::node(name("Position on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(name("Position on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
fn position_on_path(
|
fn position_on_path(
|
||||||
_: impl Ctx,
|
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||||
/// The path to traverse.
|
/// The path to traverse.
|
||||||
content: List<Vector>,
|
content: IList<Vector>,
|
||||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||||
progression: Progression,
|
progression: Progression,
|
||||||
/// Swap the direction of the path.
|
/// Swap the direction of the path.
|
||||||
@@ -2190,8 +2157,8 @@ fn position_on_path(
|
|||||||
|
|
||||||
let mut bezpaths: Vec<_> = (0..content.len())
|
let mut bezpaths: Vec<_> = (0..content.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = content.lane(index).attr::<TransformAttr>();
|
||||||
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
content.element_ref(index).stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let bezpath_count = bezpaths.len() as f64;
|
let bezpath_count = bezpaths.len() as f64;
|
||||||
@@ -2214,9 +2181,9 @@ fn position_on_path(
|
|||||||
/// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it.
|
/// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it.
|
||||||
#[node_macro::node(name("Tangent on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(name("Tangent on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
fn tangent_on_path(
|
fn tangent_on_path(
|
||||||
_: impl Ctx,
|
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||||
/// The path to traverse.
|
/// The path to traverse.
|
||||||
content: List<Vector>,
|
content: IList<Vector>,
|
||||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||||
progression: Progression,
|
progression: Progression,
|
||||||
/// Swap the direction of the path.
|
/// Swap the direction of the path.
|
||||||
@@ -2230,8 +2197,8 @@ fn tangent_on_path(
|
|||||||
|
|
||||||
let mut bezpaths: Vec<_> = (0..content.len())
|
let mut bezpaths: Vec<_> = (0..content.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = content.lane(index).attr::<TransformAttr>();
|
||||||
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
content.element_ref(index).stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let bezpath_count = bezpaths.len() as f64;
|
let bezpath_count = bezpaths.len() as f64;
|
||||||
@@ -2447,21 +2414,17 @@ fn jitter_points(
|
|||||||
fn offset_points(
|
fn offset_points(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// The vector geometry with points to be offset.
|
/// The vector geometry with points to be offset.
|
||||||
content: List<Vector>,
|
(mut element, transform): (Vector, Attr<TransformAttr>),
|
||||||
/// The distance to offset each anchor point along its normal. Positive values move outward, negative values move inward.
|
/// The distance to offset each anchor point along its normal. Positive values move outward, negative values move inward.
|
||||||
#[default(10.)]
|
#[default(10.)]
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
distance: f64,
|
distance: f64,
|
||||||
) -> List<Vector> {
|
) -> (Vector, Attr<TransformAttr>) {
|
||||||
content
|
let inverse_linear = inverse_linear_or_repair(transform.matrix2);
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let inverse_linear = inverse_linear_or_repair(transform_attribute.matrix2);
|
|
||||||
|
|
||||||
let deltas: Vec<_> = (0..row.element().point_domain.positions().len())
|
let deltas: Vec<_> = (0..element.point_domain.positions().len())
|
||||||
.map(|point_index| {
|
.map(|point_index| {
|
||||||
let Some(normal) = row.element().segment_domain.point_tangent(point_index, row.element().point_domain.positions()).map(|t| -t.perp()) else {
|
let Some(normal) = element.segment_domain.point_tangent(point_index, element.point_domain.positions()).map(|t| -t.perp()) else {
|
||||||
return DVec2::ZERO;
|
return DVec2::ZERO;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2469,12 +2432,9 @@ fn offset_points(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
apply_point_deltas(&mut element, &deltas, *transform);
|
||||||
apply_point_deltas(row.element_mut(), &deltas, transform);
|
|
||||||
|
|
||||||
row
|
(element, Attr(*transform))
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpolates the geometry, appearance, and transform between multiple vector layers, producing a single morphed vector shape.
|
/// Interpolates the geometry, appearance, and transform between multiple vector layers, producing a single morphed vector shape.
|
||||||
@@ -3481,34 +3441,21 @@ fn bevel_algorithm(mut vector: Vector, transform: DAffine2, distance: f64) -> Ve
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn bevel(_: impl Ctx, source: List<Vector>, #[default(10.)] distance: Length) -> List<Vector> {
|
fn bevel(_: impl Ctx, (element, transform): (Vector, Attr<TransformAttr>), #[default(10.)] distance: Length) -> (Vector, Attr<TransformAttr>) {
|
||||||
source
|
(bevel_algorithm(element, *transform, distance), Attr(*transform))
|
||||||
.into_iter()
|
|
||||||
.map(|row| {
|
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
|
||||||
let (element, attributes) = row.into_parts();
|
|
||||||
|
|
||||||
Item::from_parts(bevel_algorithm(element, transform, distance), attributes)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
fn close_path(_: impl Ctx, source: List<Vector>) -> List<Vector> {
|
fn close_path(_: impl Ctx, mut source: Vector) -> Vector {
|
||||||
|
source.close_subpaths();
|
||||||
source
|
source
|
||||||
.into_iter()
|
|
||||||
.map(|mut row| {
|
|
||||||
row.element_mut().close_subpaths();
|
|
||||||
row
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn point_inside(_: impl Ctx, source: List<Vector>, point: DVec2) -> bool {
|
fn point_inside(_: impl Ctx + ExtractIndex + InjectIndex + Copy, source: IList<Vector>, point: DVec2) -> bool {
|
||||||
source.into_iter().any(|row| {
|
(0..source.len()).any(|index| {
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
let transform: DAffine2 = source.lane(index).attr::<TransformAttr>();
|
||||||
row.element().check_point_inside_shape(transform, point)
|
source.element_ref(index).check_point_inside_shape(transform, point)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3520,21 +3467,21 @@ fn count_elements(_: impl Ctx, content: ListDyn) -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
fn count_points(_: impl Ctx, content: List<Vector>) -> f64 {
|
fn count_points(_: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Vector>) -> f64 {
|
||||||
content.iter_element_values().map(|vector| vector.point_domain.positions().len() as f64).sum()
|
(0..content.len()).map(|index| content.element_ref(index).point_domain.positions().len() as f64).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `List` of vector elements.
|
/// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `List` of vector elements.
|
||||||
/// If no value exists at that index, the position (0, 0) is returned.
|
/// If no value exists at that index, the position (0, 0) is returned.
|
||||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
fn index_points(
|
fn index_points(
|
||||||
_: impl Ctx,
|
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||||
/// The vector element or elements containing the anchor points to be retrieved.
|
/// The vector element or elements containing the anchor points to be retrieved.
|
||||||
content: List<Vector>,
|
content: IList<Vector>,
|
||||||
/// The index of the points to retrieve, starting from 0 for the first point. Negative indices count backwards from the end, starting from -1 for the last item.
|
/// The index of the points to retrieve, starting from 0 for the first point. Negative indices count backwards from the end, starting from -1 for the last item.
|
||||||
index: f64,
|
index: f64,
|
||||||
) -> DVec2 {
|
) -> DVec2 {
|
||||||
let points_count = content.iter_element_values().map(|vector| vector.point_domain.positions().len()).sum::<usize>();
|
let points_count = (0..content.len()).map(|row| content.element_ref(row).point_domain.positions().len()).sum::<usize>();
|
||||||
|
|
||||||
if points_count == 0 {
|
if points_count == 0 {
|
||||||
return DVec2::ZERO;
|
return DVec2::ZERO;
|
||||||
@@ -3549,7 +3496,8 @@ fn index_points(
|
|||||||
|
|
||||||
// Find the point at the given index across all vector elements
|
// Find the point at the given index across all vector elements
|
||||||
let mut accumulated = 0;
|
let mut accumulated = 0;
|
||||||
for vector in content.iter_element_values() {
|
for row in 0..content.len() {
|
||||||
|
let vector = content.element_ref(row);
|
||||||
let row_point_count = vector.point_domain.positions().len();
|
let row_point_count = vector.point_domain.positions().len();
|
||||||
if index - accumulated < row_point_count {
|
if index - accumulated < row_point_count {
|
||||||
return vector.point_domain.positions()[index - accumulated];
|
return vector.point_domain.positions()[index - accumulated];
|
||||||
@@ -3561,14 +3509,13 @@ fn index_points(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn path_length(_: impl Ctx, source: List<Vector>) -> f64 {
|
fn path_length(_: impl Ctx + ExtractIndex + InjectIndex + Copy, source: IList<Vector>) -> f64 {
|
||||||
(0..source.len())
|
(0..source.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = source.lane(index).attr::<TransformAttr>();
|
||||||
|
|
||||||
source
|
source
|
||||||
.element(index)
|
.element_ref(index)
|
||||||
.unwrap()
|
|
||||||
.stroke_bezpath_iter()
|
.stroke_bezpath_iter()
|
||||||
.map(|mut bezpath| {
|
.map(|mut bezpath| {
|
||||||
bezpath.apply_affine(Affine::new(transform.to_cols_array()));
|
bezpath.apply_affine(Affine::new(transform.to_cols_array()));
|
||||||
@@ -3580,22 +3527,21 @@ fn path_length(_: impl Ctx, source: List<Vector>) -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn area(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output = List<Vector>>) -> Result<f64, Interrupt> {
|
// The legacy form reset the footprint before evaluating; the nullification
|
||||||
let vector = content.eval(&ctx.with_footprint(&Footprint::DEFAULT))?;
|
// pass now strips it upstream since this node declares no footprint feature.
|
||||||
|
fn area(_: impl Ctx + ExtractIndex + InjectIndex + Copy, vector: IList<Vector>) -> Result<f64, Interrupt> {
|
||||||
Ok((0..vector.len())
|
Ok((0..vector.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = vector.lane(index).attr::<TransformAttr>();
|
||||||
let area_scale = transform.matrix2.determinant().abs();
|
let area_scale = transform.matrix2.determinant().abs();
|
||||||
vector.element(index).unwrap().stroke_bezpath_iter().map(|subpath| subpath.area() * area_scale).sum::<f64>()
|
vector.element_ref(index).stroke_bezpath_iter().map(|subpath| subpath.area() * area_scale).sum::<f64>()
|
||||||
})
|
})
|
||||||
.sum())
|
.sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn centroid(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output = List<Vector>>, centroid_type: CentroidType) -> Result<DVec2, Interrupt> {
|
// The footprint reset moved to the nullification pass, as in `area`.
|
||||||
let vector = content.eval(&ctx.with_footprint(&Footprint::DEFAULT))?;
|
fn centroid(_: impl Ctx + ExtractIndex + InjectIndex + Copy, vector: IList<Vector>, centroid_type: CentroidType) -> Result<DVec2, Interrupt> {
|
||||||
|
|
||||||
if vector.is_empty() {
|
if vector.is_empty() {
|
||||||
return Ok(DVec2::ZERO);
|
return Ok(DVec2::ZERO);
|
||||||
}
|
}
|
||||||
@@ -3606,14 +3552,14 @@ fn centroid(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output =
|
|||||||
let mut sum = 0.;
|
let mut sum = 0.;
|
||||||
|
|
||||||
for index in 0..vector.len() {
|
for index in 0..vector.len() {
|
||||||
let Some(element) = vector.element(index) else { continue };
|
let element = vector.element_ref(index);
|
||||||
for subpath in element.stroke_bezier_paths() {
|
for subpath in element.stroke_bezier_paths() {
|
||||||
let partial = match centroid_type {
|
let partial = match centroid_type {
|
||||||
CentroidType::Area => subpath.area_centroid_and_area(Some(1e-3), Some(1e-3)).filter(|(_, area)| *area > 0.),
|
CentroidType::Area => subpath.area_centroid_and_area(Some(1e-3), Some(1e-3)).filter(|(_, area)| *area > 0.),
|
||||||
CentroidType::Length => subpath.length_centroid_and_length(None, true),
|
CentroidType::Length => subpath.length_centroid_and_length(None, true),
|
||||||
};
|
};
|
||||||
if let Some((subpath_centroid, area_or_length)) = partial {
|
if let Some((subpath_centroid, area_or_length)) = partial {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = vector.lane(index).attr::<TransformAttr>();
|
||||||
let subpath_centroid = transform.transform_point2(subpath_centroid);
|
let subpath_centroid = transform.transform_point2(subpath_centroid);
|
||||||
|
|
||||||
sum += area_or_length;
|
sum += area_or_length;
|
||||||
@@ -3631,10 +3577,9 @@ fn centroid(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output =
|
|||||||
|
|
||||||
let summed_positions = (0..vector.len())
|
let summed_positions = (0..vector.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
let transform: DAffine2 = vector.lane(index).attr::<TransformAttr>();
|
||||||
vector
|
vector
|
||||||
.element(index)
|
.element_ref(index)
|
||||||
.unwrap()
|
|
||||||
.point_domain
|
.point_domain
|
||||||
.positions()
|
.positions()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -3651,6 +3596,7 @@ fn centroid(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output =
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use core_types::transform::Footprint;
|
||||||
use kurbo::{CubicBez, Ellipse, Point, Rect};
|
use kurbo::{CubicBez, Ellipse, Point, Rect};
|
||||||
use vector_types::vector::algorithms::bezpath_algorithms::{TValue, trim_pathseg};
|
use vector_types::vector::algorithms::bezpath_algorithms::{TValue, trim_pathseg};
|
||||||
use vector_types::vector::misc::pathseg_abs_diff_eq;
|
use vector_types::vector::misc::pathseg_abs_diff_eq;
|
||||||
@@ -3659,16 +3605,9 @@ mod test {
|
|||||||
List::new_from_element(Vector::from_bezpath(bezpath))
|
List::new_from_element(Vector::from_bezpath(bezpath))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vector_item(bezpath: BezPath, transform: DAffine2) -> Item<Vector> {
|
|
||||||
let mut row = Vector::default();
|
|
||||||
row.append_bezpath(bezpath);
|
|
||||||
Item::new_from_element(row).with_attribute(ATTR_TRANSFORM, transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bounding_box() {
|
fn bounding_box() {
|
||||||
let bounding_box = super::bounding_box(&(), vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY)));
|
let bounding_box = super::bounding_box(&(), Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY)));
|
||||||
let bounding_box = bounding_box.element(0).unwrap();
|
|
||||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||||
let manipulator_groups_anchors = bounding_box
|
let manipulator_groups_anchors = bounding_box
|
||||||
.region_manipulator_groups()
|
.region_manipulator_groups()
|
||||||
@@ -3681,12 +3620,9 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(&manipulator_groups_anchors[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
|
assert_eq!(&manipulator_groups_anchors[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
|
||||||
|
|
||||||
// Test a rectangular path with non-zero rotation
|
// The box spans local space, so a lane rotation leaves it unchanged
|
||||||
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
||||||
let mut square = List::new_from_element(square);
|
|
||||||
square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
|
|
||||||
let bounding_box = super::bounding_box(&(), square);
|
let bounding_box = super::bounding_box(&(), square);
|
||||||
let bounding_box = bounding_box.element(0).unwrap();
|
|
||||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||||
let manipulator_groups_anchors = bounding_box
|
let manipulator_groups_anchors = bounding_box
|
||||||
.region_manipulator_groups()
|
.region_manipulator_groups()
|
||||||
@@ -3705,7 +3641,16 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sample_polyline() {
|
fn sample_polyline() {
|
||||||
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
||||||
let (sample_polyline, _) = super::sample_polyline(&Footprint::default(), (Vector::from_bezpath(path), Attr(DAffine2::IDENTITY)), PointSpacingType::Separation, 30., 0, 0., 0., false);
|
let (sample_polyline, _) = super::sample_polyline(
|
||||||
|
&Footprint::default(),
|
||||||
|
(Vector::from_bezpath(path), Attr(DAffine2::IDENTITY)),
|
||||||
|
PointSpacingType::Separation,
|
||||||
|
30.,
|
||||||
|
0,
|
||||||
|
0.,
|
||||||
|
0.,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let sample_polyline = &sample_polyline;
|
let sample_polyline = &sample_polyline;
|
||||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
||||||
@@ -3715,7 +3660,16 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sample_polyline_adaptive_spacing() {
|
fn sample_polyline_adaptive_spacing() {
|
||||||
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
||||||
let (sample_polyline, _) = super::sample_polyline(&Footprint::default(), (Vector::from_bezpath(path), Attr(DAffine2::IDENTITY)), PointSpacingType::Separation, 18., 0, 45., 10., true);
|
let (sample_polyline, _) = super::sample_polyline(
|
||||||
|
&Footprint::default(),
|
||||||
|
(Vector::from_bezpath(path), Attr(DAffine2::IDENTITY)),
|
||||||
|
PointSpacingType::Separation,
|
||||||
|
18.,
|
||||||
|
0,
|
||||||
|
45.,
|
||||||
|
10.,
|
||||||
|
true,
|
||||||
|
);
|
||||||
let sample_polyline = &sample_polyline;
|
let sample_polyline = &sample_polyline;
|
||||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
||||||
@@ -3742,15 +3696,27 @@ mod test {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn path_length() {
|
fn path_length() {
|
||||||
|
core_types::record::stack::reserve(1 << 16);
|
||||||
|
let arena = core_types::arena::Arena::new(1 << 20).unwrap();
|
||||||
|
let generations = [];
|
||||||
|
let scope = core_types::context::EvalScope::new(None, None, None, &generations, &arena);
|
||||||
|
let ctx = core_types::context::ContextImpl::root(&scope);
|
||||||
|
|
||||||
let bezpath = Rect::new(100., 100., 201., 201.).to_path(DEFAULT_ACCURACY);
|
let bezpath = Rect::new(100., 100., 201., 201.).to_path(DEFAULT_ACCURACY);
|
||||||
let transform = DAffine2::from_scale(DVec2::new(2., 2.));
|
let mut row = Vector::default();
|
||||||
let row = create_vector_item(bezpath, transform);
|
row.append_bezpath(bezpath);
|
||||||
let list = (0..5).map(|_| row.clone()).collect::<List<Vector>>();
|
// Element-only lanes read identity lane transforms; the transform term
|
||||||
|
// rides the demo gate.
|
||||||
|
let source = core_types::value::LeveledValueSource::new(vec![row; 5]);
|
||||||
|
let core_types::record::LevelStatus::Batch(batch, _) = core_types::record::materialize_level(&source, &ctx, &arena) else {
|
||||||
|
panic!("materialize failed")
|
||||||
|
};
|
||||||
|
let list = unsafe { core_types::node::List::<Vector>::new(batch) };
|
||||||
|
|
||||||
let length = super::path_length(&Footprint::default(), list);
|
let length = super::path_length(&ctx, list);
|
||||||
|
|
||||||
// 101 (each rectangle edge length) * 4 (rectangle perimeter) * 2 (scale) * 5 (number of rows)
|
// 101 (each rectangle edge length) * 4 (rectangle perimeter) * 5 (number of rows)
|
||||||
assert_eq!(length, 101. * 4. * 2. * 5.);
|
assert_eq!(length, 101. * 4. * 5.);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn spline() {
|
fn spline() {
|
||||||
@@ -3825,8 +3791,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn bevel_rect() {
|
fn bevel_rect() {
|
||||||
let source = Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY);
|
let source = Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY);
|
||||||
let beveled = super::bevel(&Footprint::default(), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
let (beveled, _) = super::bevel(&Footprint::default(), (Vector::from_bezpath(source), Attr(DAffine2::IDENTITY)), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = &beveled;
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 8);
|
assert_eq!(beveled.point_domain.positions().len(), 8);
|
||||||
assert_eq!(beveled.segment_domain.ids().len(), 8);
|
assert_eq!(beveled.segment_domain.ids().len(), 8);
|
||||||
@@ -3853,8 +3819,8 @@ mod test {
|
|||||||
source.line_to(Point::ZERO);
|
source.line_to(Point::ZERO);
|
||||||
source.push(curve.as_path_el());
|
source.push(curve.as_path_el());
|
||||||
|
|
||||||
let beveled = super::bevel(&(), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
let (beveled, _) = super::bevel(&(), (Vector::from_bezpath(source), Attr(DAffine2::IDENTITY)), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = &beveled;
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||||
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
||||||
@@ -3877,13 +3843,11 @@ mod test {
|
|||||||
source.line_to(Point::ZERO);
|
source.line_to(Point::ZERO);
|
||||||
source.push(curve.as_path_el());
|
source.push(curve.as_path_el());
|
||||||
|
|
||||||
|
// The legacy test set the transform on a list it never passed, so the
|
||||||
|
// evaluated lane used the identity; keep that behavior explicit.
|
||||||
let vector = Vector::from_bezpath(source);
|
let vector = Vector::from_bezpath(source);
|
||||||
let mut vector_list = List::new_from_element(vector.clone());
|
let (beveled, _) = super::bevel(&(), (vector, Attr(DAffine2::IDENTITY)), 2_f64.sqrt() * 10.);
|
||||||
|
let beveled = &beveled;
|
||||||
vector_list.set_attribute(ATTR_TRANSFORM, 0, DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)));
|
|
||||||
|
|
||||||
let beveled = super::bevel(&(), List::new_from_element(vector), 2_f64.sqrt() * 10.);
|
|
||||||
let beveled = beveled.element(0).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||||
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
assert_eq!(beveled.segment_domain.ids().len(), 3);
|
||||||
@@ -3905,8 +3869,8 @@ mod test {
|
|||||||
source.line_to(Point::new(100., 100.));
|
source.line_to(Point::new(100., 100.));
|
||||||
source.line_to(Point::new(0., 100.));
|
source.line_to(Point::new(0., 100.));
|
||||||
|
|
||||||
let beveled = super::bevel(&Footprint::default(), vector_node_from_bezpath(source), 999.);
|
let (beveled, _) = super::bevel(&Footprint::default(), (Vector::from_bezpath(source), Attr(DAffine2::IDENTITY)), 999.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = &beveled;
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||||
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
||||||
@@ -3929,8 +3893,8 @@ mod test {
|
|||||||
|
|
||||||
let subpath = BezPath::from_path_segments([line, point, curve].into_iter());
|
let subpath = BezPath::from_path_segments([line, point, curve].into_iter());
|
||||||
|
|
||||||
let beveled_list = super::bevel(&Footprint::default(), vector_node_from_bezpath(subpath), 5.);
|
let (beveled, _) = super::bevel(&Footprint::default(), (Vector::from_bezpath(subpath), Attr(DAffine2::IDENTITY)), 5.);
|
||||||
let beveled = beveled_list.element(0).unwrap();
|
let beveled = &beveled;
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||||
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
||||||
|
|||||||
Reference in New Issue
Block a user