diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 370391240d..3397fdee92 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -325,7 +325,9 @@ macro_rules! tagged_value { check_level!(List, Graphic); check_level!(List, Artboard); check_level!(List>, Raster); - check_level!(List, 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::>() { return Ok(leveled_record_value_edge(vec![Vector::default()])); } check_level!(List, String); if name == std::any::type_name::() { return Ok(record_value_edge(DocumentNode::default())); } if name == std::any::type_name::() { return Ok(record_value_edge(Resource::default())); } diff --git a/node-graph/nodes/vector/src/vector_modification_nodes.rs b/node-graph/nodes/vector/src/vector_modification_nodes.rs index 4c3d4fd997..6a24315a66 100644 --- a/node-graph/nodes/vector/src/vector_modification_nodes.rs +++ b/node-graph/nodes/vector/src/vector_modification_nodes.rs @@ -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::uuid::NodeId; -use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Ctx}; +use core_types::{ATTR_TRANSFORM, Ctx, ExtractIndex, InjectIndex}; use glam::DAffine2; use graphic_types::Vector; -use vector_types::ATTR_EDITOR_CLICK_TARGET; +use vector_types::markers::EditorClickTarget; 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. +/// 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(""))] -fn path_modify(_ctx: impl Ctx, mut vector: List, modification: Box, node_path: Vec) -> List { - use core_types::list::Item; - - if vector.is_empty() { - vector.push(Item::default()); +fn path_modify<'e>( + ctx: impl Ctx + ExtractArena<'e> + ExtractIndex + InjectIndex + Copy, + (element, existing): (Vector, Attr<'e, EditorLayerPath>), + modification: Box, + node_path: Vec, +) -> Result<(Vector, Attr<'e, EditorLayerPath>, RemoveAttr), 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`), // matching the `path_of_subgraph` proto so editor tools can route data back to the parent layer. - let subgraph_path: List = { - let len = node_path.len(); - node_path.into_iter().take(len.saturating_sub(1)).map(Item::new_from_element).collect() + let path = match existing.is_empty() { + false => existing.to_vec(), + true => { + let len = node_path.len(); + node_path.into_iter().take(len.saturating_sub(1)).collect() + } }; - let existing: List = vector.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0); - vector.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, if existing.is_empty() { subgraph_path } else { existing }); - - if vector.len() > 1 { - warn!("The path modify ran on {} vector items. Only the first can be modified.", vector.len()); - } - vector + let (parked, _) = ctx.arena().alloc(path).ok_or(GraphError { + kind: core_types::gpoll::ErrorKind::ArenaExhausted, + trace: Vec::new(), + })?; + Ok((element, Attr(parked.as_slice()), RemoveAttr::new())) } /// Applies the vector path's local transformation to its geometry and resets the transform to the identity.