Convert path modify to a flip kernel over one default lane

This commit is contained in:
Dennis Kobert
2026-08-22 17:29:51 +00:00
parent 4e8a44650e
commit 7569bdb9cb
2 changed files with 29 additions and 22 deletions
+3 -1
View File
@@ -325,7 +325,9 @@ macro_rules! tagged_value {
check_level!(List<Graphic>, Graphic); check_level!(List<Graphic>, Graphic);
check_level!(List<Artboard>, Artboard); check_level!(List<Artboard>, Artboard);
check_level!(List<Raster<CPU>>, Raster<CPU>); check_level!(List<Raster<CPU>>, Raster<CPU>);
check_level!(List<Vector>, Vector); // One default lane rather than an empty level: an unwired path input
// starts from a blank vector, as the legacy path modify synthesized itself.
if name == std::any::type_name::<List<Vector>>() { return Ok(leveled_record_value_edge(vec![Vector::default()])); }
check_level!(List<String>, String); check_level!(List<String>, String);
if name == std::any::type_name::<DocumentNode>() { return Ok(record_value_edge(DocumentNode::default())); } if name == std::any::type_name::<DocumentNode>() { return Ok(record_value_edge(DocumentNode::default())); }
if name == std::any::type_name::<Resource>() { return Ok(record_value_edge(Resource::default())); } if name == std::any::type_name::<Resource>() { return Ok(record_value_edge(Resource::default())); }
@@ -1,37 +1,42 @@
use core_types::attribute::{Attr, EditorLayerPath, RemoveAttr};
use core_types::gpoll::{GraphError, Interrupt};
use core_types::list::List; use core_types::list::List;
use core_types::uuid::NodeId; use core_types::uuid::NodeId;
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Ctx}; use core_types::{ATTR_TRANSFORM, Ctx, ExtractIndex, InjectIndex};
use glam::DAffine2; use glam::DAffine2;
use graphic_types::Vector; use graphic_types::Vector;
use vector_types::ATTR_EDITOR_CLICK_TARGET; use vector_types::markers::EditorClickTarget;
use vector_types::vector::VectorModification; use vector_types::vector::VectorModification;
/// Applies a differential modification to a vector path, associating changes made by the Pen and Path tools to indices of edited points and segments. /// Applies a differential modification to a vector path, associating changes made by the Pen and Path tools to indices of edited points and segments.
/// The modification applies to the level's first lane only; an unwired input serves one blank lane through the
/// `TypeDefault` value form, so a fresh path starts from a default vector.
#[node_macro::node(category(""))] #[node_macro::node(category(""))]
fn path_modify(_ctx: impl Ctx, mut vector: List<Vector>, modification: Box<VectorModification>, node_path: Vec<NodeId>) -> List<Vector> { fn path_modify<'e>(
use core_types::list::Item; ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy,
(element, existing): (Vector, Attr<'e, EditorLayerPath>),
if vector.is_empty() { modification: Box<VectorModification>,
vector.push(Item::default()); node_path: Vec<NodeId>,
) -> Result<(Vector, Attr<'e, EditorLayerPath>, RemoveAttr<EditorClickTarget>), Interrupt> {
let mut element = element;
if ctx.innermost_index() == 0 {
modification.apply(&mut element);
} }
modification.apply(vector.element_mut(0).expect("push should give one item"));
// Drop stale click-target override so hit testing uses the geometry the user is now editing
vector.remove_attribute(ATTR_EDITOR_CLICK_TARGET);
// Set the path to the encapsulating subgraph (drop our own trailing entry from `node_path`), // Set the path to the encapsulating subgraph (drop our own trailing entry from `node_path`),
// matching the `path_of_subgraph` proto so editor tools can route data back to the parent layer. // matching the `path_of_subgraph` proto so editor tools can route data back to the parent layer.
let subgraph_path: List<NodeId> = { let path = match existing.is_empty() {
let len = node_path.len(); false => existing.to_vec(),
node_path.into_iter().take(len.saturating_sub(1)).map(Item::new_from_element).collect() true => {
let len = node_path.len();
node_path.into_iter().take(len.saturating_sub(1)).collect()
}
}; };
let existing: List<NodeId> = vector.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0); let (parked, _) = ctx.arena().alloc(path).ok_or(GraphError {
vector.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, if existing.is_empty() { subgraph_path } else { existing }); kind: core_types::gpoll::ErrorKind::ArenaExhausted,
trace: Vec::new(),
if vector.len() > 1 { })?;
warn!("The path modify ran on {} vector items. Only the first can be modified.", vector.len()); Ok((element, Attr(parked.as_slice()), RemoveAttr::new()))
}
vector
} }
/// 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.