From 7e04cfa9b396371232b87b28a7d0a70a78cc35bc Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 23 Oct 2026 21:00:55 -0700 Subject: [PATCH] Rename 'List Length' to 'Count', 'Auto-Tangents' to 'Smooth Anchors', and seven other nodes --- .branding | 4 +- .../graph_operation_message_handler.rs | 2 +- .../document/graph_operation/utility_types.rs | 12 +- .../node_graph/document_node_definitions.rs | 6 +- .../messages/portfolio/document_migration.rs | 161 +++++++++++++++--- frontend/src/icons.ts | 36 ++-- .../interpreted-executor/src/node_registry.rs | 2 +- node-graph/nodes/gcore/src/animation.rs | 8 +- node-graph/nodes/gcore/src/debug.rs | 4 +- node-graph/nodes/graphic/src/graphic.rs | 28 +-- .../nodes/gstd/src/platform_application_io.rs | 4 +- node-graph/nodes/text/src/lib.rs | 6 +- node-graph/nodes/vector/src/vector_nodes.rs | 16 +- 13 files changed, 201 insertions(+), 88 deletions(-) diff --git a/.branding b/.branding index 3f83ae52c1d..a268632f7be 100644 --- a/.branding +++ b/.branding @@ -1,2 +1,2 @@ -https://github.com/Keavon/graphite-branded-assets/archive/ea42bfc35beadaf716bb9efdb036512511dc2c28.tar.gz -1286b27b897d9bad692fc2897eceb25e3dd62476c7be391cf9521917f7fdf0a1 +https://github.com/Keavon/graphite-branded-assets/archive/df284651aeb9370927500ab7f336cfb63ca8069a.tar.gz +d88f21500aba0de491efd03f2553dfc457288409923b7f52386d2a81e0cdaf5d diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs index 4c867ee2321..a39a23d4b96 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs @@ -294,7 +294,7 @@ impl MessageHandler> for (InputConnector::node(morph_node_id, graphene_std::vector::morph::PathInput), "Morph", "Morph Path") }; - // Create the control path layer (Path → Auto-Tangents → Origins to Polyline) + // Create the control path layer (Path → Smooth Anchors → Origins to Polyline) let control_path_layer = modify_inputs.create_layer(control_path_id); let path_node_id = modify_inputs.insert_control_path_data(control_path_layer); diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 68860a640c5..a72e32d4d16 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -128,13 +128,13 @@ impl<'a> ModifyInputsContext<'a> { self.network_interface.insert_node(origins_to_polyline_id, origins_to_polyline, &[]); self.network_interface.move_node_to_chain_start(&origins_to_polyline_id, layer, &[], self.import); - // Add Auto-Tangents node (between Origins to Polyline and Path), with spread=1 and preserve_existing=false - let auto_tangents = resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER) - .expect("Auto-Tangents node does not exist") + // Add Smooth Anchors node (between Origins to Polyline and Path), with spread=1 and preserve_existing=false + let smooth_anchors = resolve_proto_node_type(graphene_std::vector::smooth_anchors::IDENTIFIER) + .expect("Smooth Anchors node does not exist") .node_template_input_override([None, Some(NodeInput::value(TaggedValue::Number(1.), false)), Some(NodeInput::value(TaggedValue::Bool(false), false))]); - let auto_tangents_id = NodeId::new(); - self.network_interface.insert_node(auto_tangents_id, auto_tangents, &[]); - self.network_interface.move_node_to_chain_start(&auto_tangents_id, layer, &[], self.import); + let smooth_anchors_id = NodeId::new(); + self.network_interface.insert_node(smooth_anchors_id, smooth_anchors, &[]); + self.network_interface.move_node_to_chain_start(&smooth_anchors_id, layer, &[], self.import); // Add Path node to chain start (closest to the Merge node) let path = resolve_network_node_type("Path").expect("Path node does not exist").default_node_template(); diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index eb95f203b5a..aeabf8549c2 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -363,9 +363,9 @@ fn document_node_definitions() -> HashMap HashMap] = &[ node: graphene_std::animation::real_time::IDENTIFIER, aliases: &["graphene_core::animation::RealTimeNode"], }, + NodeReplacement { + node: graphene_std::animation::step_animation_time::IDENTIFIER, + aliases: &["graphene_core::animation::QuantizeAnimationTimeNode"], + }, + NodeReplacement { + node: graphene_std::animation::step_real_time::IDENTIFIER, + aliases: &["graphene_core::animation::QuantizeRealTimeNode"], + }, + NodeReplacement { + node: graphene_std::debug::debug_log::IDENTIFIER, + aliases: &["graphene_core::debug::LogToConsoleNode"], + }, // ================================ // graphic // ================================ @@ -174,6 +186,14 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ node: graphene_std::graphic::remove_at_index::IDENTIFIER, aliases: &["graphic_nodes::graphic::OmitElementNode"], }, + NodeReplacement { + node: graphene_std::graphic::indices::IDENTIFIER, + aliases: &["graphic_nodes::graphic::ListIndicesNode"], + }, + NodeReplacement { + node: graphene_std::graphic::slice::IDENTIFIER, + aliases: &["graphic_nodes::graphic::ListSliceNode"], + }, NodeReplacement { node: graphene_std::graphic::legacy_layer_extend::IDENTIFIER, aliases: &[ @@ -205,6 +225,13 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ ], }, // ================================ + // gstd + // ================================ + NodeReplacement { + node: graphene_std::platform_application_io::bytes_to_image::IDENTIFIER, + aliases: &["graphene_std::platform_application_io::DecodeImageNode"], + }, + // ================================ // math // ================================ NodeReplacement { @@ -706,6 +733,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ node: graphene_std::text_nodes::serialize::IDENTIFIER, aliases: &["graphene_core::logic::SerializeNode"], }, + NodeReplacement { + node: graphene_std::text_nodes::string_map::IDENTIFIER, + aliases: &["text_nodes::MapStringNode"], + }, // ================================ // transform // ================================ @@ -769,8 +800,12 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ aliases: &["graphene_core::vector::AssignColorsNode"], }, NodeReplacement { - node: graphene_std::vector::auto_tangents::IDENTIFIER, - aliases: &["graphene_core::vector::vector_nodes::AutoTangentsNode", "graphene_core::vector::AutoTangentsNode"], + node: graphene_std::vector::smooth_anchors::IDENTIFIER, + aliases: &[ + "graphene_core::vector::vector_nodes::AutoTangentsNode", + "graphene_core::vector::AutoTangentsNode", + "core_types::vector::AutoTangentsNode", + ], }, NodeReplacement { node: graphene_std::vector::bevel::IDENTIFIER, @@ -793,8 +828,8 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ aliases: &["graphene_core::vector::ClosePathNode", "core_types::vector::ClosePathNode"], }, NodeReplacement { - node: graphene_std::vector::list_length::IDENTIFIER, - aliases: &["graphene_core::vector::CountElementsNode"], + node: graphene_std::vector::count::IDENTIFIER, + aliases: &["graphene_core::vector::CountElementsNode", "graphene_core::vector::ListLengthNode"], }, NodeReplacement { node: graphene_std::vector::cut_path::IDENTIFIER, @@ -1727,8 +1762,8 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_ (DefinitionIdentifier::ProtoNode(graphene_std::graphic::item_at_index::IDENTIFIER), &[1]), (DefinitionIdentifier::ProtoNode(graphene_std::graphic::remove_at_index::IDENTIFIER), &[1]), (DefinitionIdentifier::ProtoNode(graphene_std::graphic::shift::IDENTIFIER), &[1]), - (DefinitionIdentifier::ProtoNode(graphene_std::graphic::list_indices::IDENTIFIER), &[1]), - (DefinitionIdentifier::ProtoNode(graphene_std::graphic::list_slice::IDENTIFIER), &[1, 2]), + (DefinitionIdentifier::ProtoNode(graphene_std::graphic::indices::IDENTIFIER), &[1]), + (DefinitionIdentifier::ProtoNode(graphene_std::graphic::slice::IDENTIFIER), &[1, 2]), (DefinitionIdentifier::ProtoNode(graphene_std::text_nodes::string_slice::IDENTIFIER), &[1, 2]), (DefinitionIdentifier::ProtoNode(graphene_std::text_nodes::regex::regex_find::IDENTIFIER), &[2]), (DefinitionIdentifier::Network("Regex Find".into()), &[2]), @@ -2748,7 +2783,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], } if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::RemoveHandlesNode")) { - let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::auto_tangents::IDENTIFIER))?.default_node_template(); + let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::smooth_anchors::IDENTIFIER))?.default_node_template(); document.network_interface.replace_implementation(node_id, network_path, &mut node_template); let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; @@ -2763,7 +2798,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], } if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::GenerateHandlesNode")) { - let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::auto_tangents::IDENTIFIER))?.default_node_template(); + let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::smooth_anchors::IDENTIFIER))?.default_node_template(); document.network_interface.replace_implementation(node_id, network_path, &mut node_template); let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; @@ -2856,6 +2891,22 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], reset_default_display_name(document, node_id, network_path, old_default_name); } + // Nodes renamed with unchanged inputs take their new names in place of their old default titles + let renamed = [ + (graphene_std::animation::step_animation_time::IDENTIFIER, "Quantize Animation Time"), + (graphene_std::animation::step_real_time::IDENTIFIER, "Quantize Real Time"), + (graphene_std::debug::debug_log::IDENTIFIER, "Log to Console"), + (graphene_std::vector::smooth_anchors::IDENTIFIER, "Auto-Tangents"), + (graphene_std::platform_application_io::bytes_to_image::IDENTIFIER, "Decode Image"), + (graphene_std::graphic::indices::IDENTIFIER, "List Indices"), + (graphene_std::vector::count::IDENTIFIER, "List Length"), + (graphene_std::graphic::slice::IDENTIFIER, "List Slice"), + (graphene_std::text_nodes::string_map::IDENTIFIER, "Map String"), + ]; + if let Some((_, old_default_name)) = renamed.iter().find(|(identifier, _)| reference == DefinitionIdentifier::ProtoNode(identifier.clone())) { + reset_default_display_name(document, node_id, network_path, old_default_name); + } + if reference == DefinitionIdentifier::Network("Sample Points".into()) && inputs_count == 5 { let mut node_template = resolve_network_node_type("Sample Polyline")?.default_node_template(); document.network_interface.replace_implementation(node_id, network_path, &mut node_template); @@ -3063,7 +3114,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], // Migrate from the v2 "Morph" node (2 inputs: content, progression) to the v3 "Morph" node (5 inputs: content, progression, reverse, distribution, path). // The old progression used integer part for pair selection (range 0..N-1 where N is the number of content objects). // The new progression uses fractional 0..1 for euclidean traversal through all objects. - // We insert List Length → Subtract 1 → Divide to remap: new_progression = old_progression / (N - 1). + // We insert Count → Subtract 1 → Divide to remap: new_progression = old_progression / (N - 1). // For the common 2-object case (N=2), this divides by 1 which is a no-op, preserving identical behavior. if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER) && inputs_count == 2 { let mut node_template = resolve_document_node_type(&reference)?.default_node_template(); @@ -3078,14 +3129,14 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], return None; }; - // Create List Length node: counts content `List` items → N - let Some(list_length_def) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::list_length::IDENTIFIER)) else { - log::error!("Could not get list_length node from definition when upgrading morph"); + // Create Count node: counts content `List` items → N + let Some(count_def) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::count::IDENTIFIER)) else { + log::error!("Could not get count node from definition when upgrading morph"); document.network_interface.set_input(&InputConnector::node_at_index(*node_id, 1), old_inputs[1].clone(), network_path); return None; }; - let list_length_template = list_length_def.default_node_template(); - let list_length_id = NodeId::new(); + let count_template = count_def.default_node_template(); + let count_id = NodeId::new(); // Create Subtract node: N → N-1 let Some(subtract_def) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::subtract::IDENTIFIER)) else { @@ -3107,10 +3158,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], let divide_id = NodeId::new(); // Insert and position nodes - document.network_interface.insert_node(list_length_id, list_length_template, network_path); - document - .network_interface - .shift_absolute_node_position(&list_length_id, morph_position + IVec2::new(-21, 2), network_path); + document.network_interface.insert_node(count_id, count_template, network_path); + document.network_interface.shift_absolute_node_position(&count_id, morph_position + IVec2::new(-21, 2), network_path); document.network_interface.insert_node(subtract_id, subtract_template, network_path); document.network_interface.shift_absolute_node_position(&subtract_id, morph_position + IVec2::new(-14, 2), network_path); @@ -3118,15 +3167,13 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document.network_interface.insert_node(divide_id, divide_template, network_path); document.network_interface.shift_absolute_node_position(÷_id, morph_position + IVec2::new(-7, 1), network_path); - // Wire: content source → List Length input 0 - document - .network_interface - .set_input(&InputConnector::node_at_index(list_length_id, 0), old_inputs[0].clone(), network_path); + // Wire: content source → Count input 0 + document.network_interface.set_input(&InputConnector::node_at_index(count_id, 0), old_inputs[0].clone(), network_path); - // Wire: List Length output → Subtract input 0 (minuend) + // Wire: Count output → Subtract input 0 (minuend) document .network_interface - .set_input(&InputConnector::node_at_index(subtract_id, 0), NodeInput::node(list_length_id, 0), network_path); + .set_input(&InputConnector::node_at_index(subtract_id, 0), NodeInput::node(count_id, 0), network_path); // Wire: old progression → Divide input 0 (numerator) document.network_interface.set_input(&InputConnector::node_at_index(divide_id, 0), old_inputs[1].clone(), network_path); @@ -4593,4 +4640,70 @@ mod tests { assert!(matches!(current_content, Some(NodeInput::Node { node_id, output_index: 0, .. }) if node_id == upstream), "{title}"); } } + + // Nodes saved under their old identifiers and default titles open as the renamed nodes under their new titles + #[test] + fn renamed_nodes_open_under_their_new_names() { + use crate::messages::portfolio::document_storage_io::load_legacy_document; + use crate::test_utils::capture_logged_errors; + use graph_craft::application_io::resource::HashMapResourceStorage; + + let renames = [ + ( + graphene_std::animation::step_animation_time::IDENTIFIER, + "graphene_core::animation::QuantizeAnimationTimeNode", + "Quantize Animation Time", + "Step Animation Time", + ), + ( + graphene_std::animation::step_real_time::IDENTIFIER, + "graphene_core::animation::QuantizeRealTimeNode", + "Quantize Real Time", + "Step Real Time", + ), + (graphene_std::debug::debug_log::IDENTIFIER, "graphene_core::debug::LogToConsoleNode", "Log to Console", "Debug Log"), + ( + graphene_std::vector::smooth_anchors::IDENTIFIER, + "core_types::vector::AutoTangentsNode", + "Auto-Tangents", + "Smooth Anchors", + ), + ( + graphene_std::platform_application_io::bytes_to_image::IDENTIFIER, + "graphene_std::platform_application_io::DecodeImageNode", + "Decode Image", + "Bytes to Image", + ), + (graphene_std::graphic::indices::IDENTIFIER, "graphic_nodes::graphic::ListIndicesNode", "List Indices", "Indices"), + (graphene_std::vector::count::IDENTIFIER, "graphene_core::vector::ListLengthNode", "List Length", "Count"), + (graphene_std::graphic::slice::IDENTIFIER, "graphic_nodes::graphic::ListSliceNode", "List Slice", "Slice"), + (graphene_std::text_nodes::string_map::IDENTIFIER, "text_nodes::MapStringNode", "Map String", "String Map"), + ]; + + // Each node under its old identifier, storing its old default title + let mut document = DocumentMessageHandler::default(); + for (index, (identifier, old_identifier, old_title, _)) in renames.iter().enumerate() { + let definition = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(identifier.clone())).expect("the renamed node's definition should exist"); + let mut node_template = definition.default_node_template(); + node_template.implementation = NodeTemplateImplementation::ProtoNode(ProtoNodeIdentifier::new(old_identifier)); + node_template.display_name = old_title.to_string(); + document.network_interface.insert_node(NodeId(index as u64), node_template, &[]); + } + + let serialized = document.serialize_document(); + for (identifier, ..) in &renames { + assert!(!serialized.contains(identifier.as_str()), "the saved document should name only the old identifiers, not {identifier}"); + } + + let (opened, errors) = capture_logged_errors(|| load_legacy_document(serialized, &HashMapResourceStorage::new(), false)); + let opened = opened.expect("the document should open"); + assert!(errors.is_empty(), "opening the document logged errors: {errors:?}"); + + for (index, (identifier, _, _, title)) in renames.iter().enumerate() { + let node_id = NodeId(index as u64); + let node = opened.network_interface.document_node(&node_id, &[]).expect("the node should still exist"); + assert_eq!(node.implementation, DocumentNodeImplementation::ProtoNode(identifier.clone()), "{title}"); + assert_eq!(opened.network_interface.display_name(&node_id, &[]), *title); + } + } } diff --git a/frontend/src/icons.ts b/frontend/src/icons.ts index baa305d55aa..4d2f323b891 100644 --- a/frontend/src/icons.ts +++ b/frontend/src/icons.ts @@ -124,7 +124,6 @@ import NodeAsVector from "/../branding/assets/16x16-nodes/node-as-vector.svg"; import NodeAssignColors from "/../branding/assets/16x16-nodes/node-assign-colors.svg"; import NodeAssignRotations from "/../branding/assets/16x16-nodes/node-assign-rotations.svg"; import NodeAssignScales from "/../branding/assets/16x16-nodes/node-assign-scales.svg"; -import NodeAutoTangents from "/../branding/assets/16x16-nodes/node-auto-tangents.svg"; import NodeAverage from "/../branding/assets/16x16-nodes/node-average.svg"; import NodeBakeTransform from "/../branding/assets/16x16-nodes/node-bake-transform.svg"; import NodeBasicBrush from "/../branding/assets/16x16-nodes/node-basic-brush.svg"; @@ -140,6 +139,7 @@ import NodeBoxCorners from "/../branding/assets/16x16-nodes/node-box-corners.svg import NodeBoxWarp from "/../branding/assets/16x16-nodes/node-box-warp.svg"; import NodeBrightnessContrast from "/../branding/assets/16x16-nodes/node-brightness-contrast.svg"; import NodeBrushStrokes from "/../branding/assets/16x16-nodes/node-brush-strokes.svg"; +import NodeBytesToImage from "/../branding/assets/16x16-nodes/node-bytes-to-image.svg"; import NodeCeiling from "/../branding/assets/16x16-nodes/node-ceiling.svg"; import NodeCentroid from "/../branding/assets/16x16-nodes/node-centroid.svg"; import NodeChannelMixer from "/../branding/assets/16x16-nodes/node-channel-mixer.svg"; @@ -158,13 +158,14 @@ import NodeConvexHull from "/../branding/assets/16x16-nodes/node-convex-hull.svg import NodeCosineInverse from "/../branding/assets/16x16-nodes/node-cosine-inverse.svg"; import NodeCosine from "/../branding/assets/16x16-nodes/node-cosine.svg"; import NodeCountPoints from "/../branding/assets/16x16-nodes/node-count-points.svg"; +import NodeCount from "/../branding/assets/16x16-nodes/node-count.svg"; import NodeCrossProduct from "/../branding/assets/16x16-nodes/node-cross-product.svg"; import NodeCurves from "/../branding/assets/16x16-nodes/node-curves.svg"; import NodeCustomNode from "/../branding/assets/16x16-nodes/node-custom-node.svg"; import NodeCutPath from "/../branding/assets/16x16-nodes/node-cut-path.svg"; import NodeDashPattern from "/../branding/assets/16x16-nodes/node-dash-pattern.svg"; +import NodeDebugLog from "/../branding/assets/16x16-nodes/node-debug-log.svg"; import NodeDecimate from "/../branding/assets/16x16-nodes/node-decimate.svg"; -import NodeDecodeImage from "/../branding/assets/16x16-nodes/node-decode-image.svg"; import NodeDecomposeRotation from "/../branding/assets/16x16-nodes/node-decompose-rotation.svg"; import NodeDecomposeScale from "/../branding/assets/16x16-nodes/node-decompose-scale.svg"; import NodeDecomposeSkew from "/../branding/assets/16x16-nodes/node-decompose-skew.svg"; @@ -224,6 +225,7 @@ import NodeImageToBytes from "/../branding/assets/16x16-nodes/node-image-to-byte import NodeImageToChannels from "/../branding/assets/16x16-nodes/node-image-to-channels.svg"; import NodeImage from "/../branding/assets/16x16-nodes/node-image.svg"; import NodeIndexPoints from "/../branding/assets/16x16-nodes/node-index-points.svg"; +import NodeIndices from "/../branding/assets/16x16-nodes/node-indices.svg"; import NodeIntoGroup from "/../branding/assets/16x16-nodes/node-into-group.svg"; import NodeInvertTransform from "/../branding/assets/16x16-nodes/node-invert-transform.svg"; import NodeInvert from "/../branding/assets/16x16-nodes/node-invert.svg"; @@ -235,12 +237,8 @@ import NodeLerp from "/../branding/assets/16x16-nodes/node-lerp.svg"; import NodeLessThan from "/../branding/assets/16x16-nodes/node-less-than.svg"; import NodeLevels from "/../branding/assets/16x16-nodes/node-levels.svg"; import NodeLine from "/../branding/assets/16x16-nodes/node-line.svg"; -import NodeListIndices from "/../branding/assets/16x16-nodes/node-list-indices.svg"; -import NodeListLength from "/../branding/assets/16x16-nodes/node-list-length.svg"; -import NodeListSlice from "/../branding/assets/16x16-nodes/node-list-slice.svg"; import NodeLoadImage from "/../branding/assets/16x16-nodes/node-load-image.svg"; import NodeLoadResource from "/../branding/assets/16x16-nodes/node-load-resource.svg"; -import NodeLogToConsole from "/../branding/assets/16x16-nodes/node-log-to-console.svg"; import NodeLogarithm from "/../branding/assets/16x16-nodes/node-logarithm.svg"; import NodeLogicalAnd from "/../branding/assets/16x16-nodes/node-logical-and.svg"; import NodeLogicalNot from "/../branding/assets/16x16-nodes/node-logical-not.svg"; @@ -250,7 +248,6 @@ import NodeMagnitude from "/../branding/assets/16x16-nodes/node-magnitude.svg"; import NodeMakeOpaque from "/../branding/assets/16x16-nodes/node-make-opaque.svg"; import NodeMandelbrot from "/../branding/assets/16x16-nodes/node-mandelbrot.svg"; import NodeMapPoints from "/../branding/assets/16x16-nodes/node-map-points.svg"; -import NodeMapString from "/../branding/assets/16x16-nodes/node-map-string.svg"; import NodeMap from "/../branding/assets/16x16-nodes/node-map.svg"; import NodeMask from "/../branding/assets/16x16-nodes/node-mask.svg"; import NodeMathF from "/../branding/assets/16x16-nodes/node-math-f.svg"; @@ -292,8 +289,6 @@ import NodePointsToVector from "/../branding/assets/16x16-nodes/node-points-to-v import NodePostRequest from "/../branding/assets/16x16-nodes/node-post-request.svg"; import NodePosterize from "/../branding/assets/16x16-nodes/node-posterize.svg"; import NodeQrCode from "/../branding/assets/16x16-nodes/node-qr-code.svg"; -import NodeQuantizeAnimationTime from "/../branding/assets/16x16-nodes/node-quantize-animation-time.svg"; -import NodeQuantizeRealTime from "/../branding/assets/16x16-nodes/node-quantize-real-time.svg"; import NodeQueryJsonAll from "/../branding/assets/16x16-nodes/node-query-json-all.svg"; import NodeQueryJson from "/../branding/assets/16x16-nodes/node-query-json.svg"; import NodeRandom from "/../branding/assets/16x16-nodes/node-random.svg"; @@ -338,11 +333,15 @@ import NodeSign from "/../branding/assets/16x16-nodes/node-sign.svg"; import NodeSimplify from "/../branding/assets/16x16-nodes/node-simplify.svg"; import NodeSineInverse from "/../branding/assets/16x16-nodes/node-sine-inverse.svg"; import NodeSine from "/../branding/assets/16x16-nodes/node-sine.svg"; +import NodeSlice from "/../branding/assets/16x16-nodes/node-slice.svg"; +import NodeSmoothAnchors from "/../branding/assets/16x16-nodes/node-smooth-anchors.svg"; import NodeSolidifyStroke from "/../branding/assets/16x16-nodes/node-solidify-stroke.svg"; import NodeSort from "/../branding/assets/16x16-nodes/node-sort.svg"; import NodeSpiral from "/../branding/assets/16x16-nodes/node-spiral.svg"; import NodeSpline from "/../branding/assets/16x16-nodes/node-spline.svg"; import NodeStar from "/../branding/assets/16x16-nodes/node-star.svg"; +import NodeStepAnimationTime from "/../branding/assets/16x16-nodes/node-step-animation-time.svg"; +import NodeStepRealTime from "/../branding/assets/16x16-nodes/node-step-real-time.svg"; import NodeStringCapitalization from "/../branding/assets/16x16-nodes/node-string-capitalization.svg"; import NodeStringConcatenate from "/../branding/assets/16x16-nodes/node-string-concatenate.svg"; import NodeStringContains from "/../branding/assets/16x16-nodes/node-string-contains.svg"; @@ -350,6 +349,7 @@ import NodeStringEscape from "/../branding/assets/16x16-nodes/node-string-escape import NodeStringFindIndex from "/../branding/assets/16x16-nodes/node-string-find-index.svg"; import NodeStringJoin from "/../branding/assets/16x16-nodes/node-string-join.svg"; import NodeStringLength from "/../branding/assets/16x16-nodes/node-string-length.svg"; +import NodeStringMap from "/../branding/assets/16x16-nodes/node-string-map.svg"; import NodeStringOccurrences from "/../branding/assets/16x16-nodes/node-string-occurrences.svg"; import NodeStringPad from "/../branding/assets/16x16-nodes/node-string-pad.svg"; import NodeStringRepeat from "/../branding/assets/16x16-nodes/node-string-repeat.svg"; @@ -406,7 +406,6 @@ export const ICON_16X16_NODES = { NodeAssignScales: { svg: NodeAssignScales, size: 16 }, NodeAsString: { svg: NodeAsString, size: 16 }, NodeAsVector: { svg: NodeAsVector, size: 16 }, - NodeAutoTangents: { svg: NodeAutoTangents, size: 16 }, NodeAverage: { svg: NodeAverage, size: 16 }, NodeBakeTransform: { svg: NodeBakeTransform, size: 16 }, NodeBasicBrush: { svg: NodeBasicBrush, size: 16 }, @@ -422,6 +421,7 @@ export const ICON_16X16_NODES = { NodeBoxWarp: { svg: NodeBoxWarp, size: 16 }, NodeBrightnessContrast: { svg: NodeBrightnessContrast, size: 16 }, NodeBrushStrokes: { svg: NodeBrushStrokes, size: 16 }, + NodeBytesToImage: { svg: NodeBytesToImage, size: 16 }, NodeCeiling: { svg: NodeCeiling, size: 16 }, NodeCentroid: { svg: NodeCentroid, size: 16 }, NodeChannelMixer: { svg: NodeChannelMixer, size: 16 }, @@ -439,14 +439,15 @@ export const ICON_16X16_NODES = { NodeConvexHull: { svg: NodeConvexHull, size: 16 }, NodeCosine: { svg: NodeCosine, size: 16 }, NodeCosineInverse: { svg: NodeCosineInverse, size: 16 }, + NodeCount: { svg: NodeCount, size: 16 }, NodeCountPoints: { svg: NodeCountPoints, size: 16 }, NodeCrossProduct: { svg: NodeCrossProduct, size: 16 }, NodeCurves: { svg: NodeCurves, size: 16 }, NodeCustomNode: { svg: NodeCustomNode, size: 16 }, NodeCutPath: { svg: NodeCutPath, size: 16 }, NodeDashPattern: { svg: NodeDashPattern, size: 16 }, + NodeDebugLog: { svg: NodeDebugLog, size: 16 }, NodeDecimate: { svg: NodeDecimate, size: 16 }, - NodeDecodeImage: { svg: NodeDecodeImage, size: 16 }, NodeDecomposeRotation: { svg: NodeDecomposeRotation, size: 16 }, NodeDecomposeScale: { svg: NodeDecomposeScale, size: 16 }, NodeDecomposeSkew: { svg: NodeDecomposeSkew, size: 16 }, @@ -506,6 +507,7 @@ export const ICON_16X16_NODES = { NodeImageToBytes: { svg: NodeImageToBytes, size: 16 }, NodeImageToChannels: { svg: NodeImageToChannels, size: 16 }, NodeIndexPoints: { svg: NodeIndexPoints, size: 16 }, + NodeIndices: { svg: NodeIndices, size: 16 }, NodeIntoGroup: { svg: NodeIntoGroup, size: 16 }, NodeInvert: { svg: NodeInvert, size: 16 }, NodeInvertTransform: { svg: NodeInvertTransform, size: 16 }, @@ -517,23 +519,18 @@ export const ICON_16X16_NODES = { NodeLessThan: { svg: NodeLessThan, size: 16 }, NodeLevels: { svg: NodeLevels, size: 16 }, NodeLine: { svg: NodeLine, size: 16 }, - NodeListIndices: { svg: NodeListIndices, size: 16 }, - NodeListLength: { svg: NodeListLength, size: 16 }, - NodeListSlice: { svg: NodeListSlice, size: 16 }, NodeLoadImage: { svg: NodeLoadImage, size: 16 }, NodeLoadResource: { svg: NodeLoadResource, size: 16 }, NodeLogarithm: { svg: NodeLogarithm, size: 16 }, NodeLogicalAnd: { svg: NodeLogicalAnd, size: 16 }, NodeLogicalNot: { svg: NodeLogicalNot, size: 16 }, NodeLogicalOr: { svg: NodeLogicalOr, size: 16 }, - NodeLogToConsole: { svg: NodeLogToConsole, size: 16 }, NodeLoremIpsum: { svg: NodeLoremIpsum, size: 16 }, NodeMagnitude: { svg: NodeMagnitude, size: 16 }, NodeMakeOpaque: { svg: NodeMakeOpaque, size: 16 }, NodeMandelbrot: { svg: NodeMandelbrot, size: 16 }, NodeMap: { svg: NodeMap, size: 16 }, NodeMapPoints: { svg: NodeMapPoints, size: 16 }, - NodeMapString: { svg: NodeMapString, size: 16 }, NodeMask: { svg: NodeMask, size: 16 }, NodeMathF: { svg: NodeMathF, size: 16 }, NodeMathFx: { svg: NodeMathFx, size: 16 }, @@ -574,8 +571,6 @@ export const ICON_16X16_NODES = { NodePosterize: { svg: NodePosterize, size: 16 }, NodePostRequest: { svg: NodePostRequest, size: 16 }, NodeQrCode: { svg: NodeQrCode, size: 16 }, - NodeQuantizeAnimationTime: { svg: NodeQuantizeAnimationTime, size: 16 }, - NodeQuantizeRealTime: { svg: NodeQuantizeRealTime, size: 16 }, NodeQueryJson: { svg: NodeQueryJson, size: 16 }, NodeQueryJsonAll: { svg: NodeQueryJsonAll, size: 16 }, NodeRandom: { svg: NodeRandom, size: 16 }, @@ -620,11 +615,15 @@ export const ICON_16X16_NODES = { NodeSimplify: { svg: NodeSimplify, size: 16 }, NodeSine: { svg: NodeSine, size: 16 }, NodeSineInverse: { svg: NodeSineInverse, size: 16 }, + NodeSlice: { svg: NodeSlice, size: 16 }, + NodeSmoothAnchors: { svg: NodeSmoothAnchors, size: 16 }, NodeSolidifyStroke: { svg: NodeSolidifyStroke, size: 16 }, NodeSort: { svg: NodeSort, size: 16 }, NodeSpiral: { svg: NodeSpiral, size: 16 }, NodeSpline: { svg: NodeSpline, size: 16 }, NodeStar: { svg: NodeStar, size: 16 }, + NodeStepAnimationTime: { svg: NodeStepAnimationTime, size: 16 }, + NodeStepRealTime: { svg: NodeStepRealTime, size: 16 }, NodeStringCapitalization: { svg: NodeStringCapitalization, size: 16 }, NodeStringConcatenate: { svg: NodeStringConcatenate, size: 16 }, NodeStringContains: { svg: NodeStringContains, size: 16 }, @@ -632,6 +631,7 @@ export const ICON_16X16_NODES = { NodeStringFindIndex: { svg: NodeStringFindIndex, size: 16 }, NodeStringJoin: { svg: NodeStringJoin, size: 16 }, NodeStringLength: { svg: NodeStringLength, size: 16 }, + NodeStringMap: { svg: NodeStringMap, size: 16 }, NodeStringOccurrences: { svg: NodeStringOccurrences, size: 16 }, NodeStringPad: { svg: NodeStringPad, size: 16 }, NodeStringRepeat: { svg: NodeStringRepeat, size: 16 }, diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 96c6728bb9d..6cad5f0be45 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -383,7 +383,7 @@ fn node_registry() -> HashMap)); #[cfg(feature = "gpu")] node_types.extend(rank_adapter_nodes!(Raster)); - // Type-erased rows for the `ListDyn` connectors (`Read Attribute`, `List Length`): any `List` wire erases its element type + // Type-erased rows for the `ListDyn` connectors (`Read Attribute`, `Count`): any `List` wire erases its element type macro_rules! list_dyn_rows { ($($element:ty),* $(,)?) => {{ let entries: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ diff --git a/node-graph/nodes/gcore/src/animation.rs b/node-graph/nodes/gcore/src/animation.rs index d736f418c4d..9ff00892af8 100644 --- a/node-graph/nodes/gcore/src/animation.rs +++ b/node-graph/nodes/gcore/src/animation.rs @@ -72,8 +72,8 @@ fn animation_time( Item::new_from_element(ctx.try_animation_time().unwrap_or_default() * *rate.element()) } -#[node_macro::node(category("Debug"), icon("NodeQuantizeRealTime"))] -fn quantize_real_time( +#[node_macro::node(category("Debug"), icon("NodeStepRealTime"))] +fn step_real_time( ctx: impl Ctx + ExtractAll + CloneVarArgs, #[implementations( Context -> Item, @@ -118,8 +118,8 @@ fn quantize_real_time( value.eval(Some(new_context.into())) } -#[node_macro::node(category("Debug"), icon("NodeQuantizeAnimationTime"))] -fn quantize_animation_time( +#[node_macro::node(category("Debug"), icon("NodeStepAnimationTime"))] +fn step_animation_time( ctx: impl Ctx + ExtractAll + CloneVarArgs, #[implementations( Context -> Item, diff --git a/node-graph/nodes/gcore/src/debug.rs b/node-graph/nodes/gcore/src/debug.rs index b4169071a49..64e8be3b732 100644 --- a/node-graph/nodes/gcore/src/debug.rs +++ b/node-graph/nodes/gcore/src/debug.rs @@ -3,8 +3,8 @@ use core_types::list::Item; use glam::{DAffine2, DVec2}; /// Meant for debugging purposes, not general use. Logs the input value to the console and passes it through unchanged. -#[node_macro::node(category("Debug"), icon("NodeLogToConsole"), name("Log to Console"))] -fn log_to_console(_: impl Ctx, #[implementations(bool, f64, i64, DVec2, DAffine2, String)] value: Item) -> Item { +#[node_macro::node(category("Debug"), icon("NodeDebugLog"))] +fn debug_log(_: impl Ctx, #[implementations(bool, f64, i64, DVec2, DAffine2, String)] value: Item) -> Item { // KEEP THIS `debug!()` - It acts as the output for the debug node itself log::debug!("{value:#?}"); value diff --git a/node-graph/nodes/graphic/src/graphic.rs b/node-graph/nodes/graphic/src/graphic.rs index 8158be2de2f..9975f9a2490 100644 --- a/node-graph/nodes/graphic/src/graphic.rs +++ b/node-graph/nodes/graphic/src/graphic.rs @@ -244,8 +244,8 @@ fn number_sequence( // TODO: Return i64 instead of f64 once automatic type conversion is implemented for nodes with generic type inputs, so an integer output doesn't wall these indices off from the generic math nodes. // TODO: (Currently automatic type conversion only works for concrete types, via the Graphene preprocessor and not the full Graphene type system.) /// Counts out the index of each item in a list (0, 1, 2, and so on), producing a list of numbers with one for each item. -#[node_macro::node(category("General"), icon("NodeListIndices"))] -fn list_indices( +#[node_macro::node(category("General"), icon("NodeIndices"))] +fn indices( _: impl Ctx, /// The list whose items are counted. list: ListDyn, @@ -260,8 +260,8 @@ fn list_indices( /// Extracts a portion of a list, starting at "Start" and ending before "End". /// /// Negative indices count from the end of the list. If the index of "Start" equals or exceeds "End", the result is an empty list. -#[node_macro::node(category("General"), icon("NodeListSlice"))] -fn list_slice( +#[node_macro::node(category("General"), icon("NodeSlice"))] +fn slice( _: impl Ctx, /// The list of data to take a portion of. #[implementations( @@ -1135,29 +1135,29 @@ mod test { } #[test] - fn list_indices_counts_each_item() { - let indices = list_indices((), ListDyn::from(list_of(["a".to_string(), "b".to_string(), "c".to_string()])), Item::new_from_element(0_i64)); - assert_eq!(elements(&indices), [0., 1., 2.]); + fn indices_counts_each_item() { + let from_zero = indices((), ListDyn::from(list_of(["a".to_string(), "b".to_string(), "c".to_string()])), Item::new_from_element(0_i64)); + assert_eq!(elements(&from_zero), [0., 1., 2.]); - let from_one = list_indices((), ListDyn::from(list_of(["a".to_string(), "b".to_string(), "c".to_string()])), Item::new_from_element(1_i64)); + let from_one = indices((), ListDyn::from(list_of(["a".to_string(), "b".to_string(), "c".to_string()])), Item::new_from_element(1_i64)); assert_eq!(elements(&from_one), [1., 2., 3.]); } #[test] - fn list_slice_takes_the_portion_between_start_and_end() { - let portion = list_slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(1_i64), Item::new_from_element(3_i64)); + fn slice_takes_the_portion_between_start_and_end() { + let portion = slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(1_i64), Item::new_from_element(3_i64)); assert_eq!(elements(&portion), [2., 3.]); } #[test] - fn list_slice_resolves_negative_indices_from_the_end() { - let portion = list_slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(-2_i64), Item::new_from_element(0_i64)); + fn slice_resolves_negative_indices_from_the_end() { + let portion = slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(-2_i64), Item::new_from_element(0_i64)); assert_eq!(elements(&portion), [4., 5.], "an end of zero reaches through the end of the list"); } #[test] - fn list_slice_yields_nothing_when_start_reaches_end() { - let portion = list_slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(3_i64), Item::new_from_element(3_i64)); + fn slice_yields_nothing_when_start_reaches_end() { + let portion = slice((), list_of([1., 2., 3., 4., 5.]), Item::new_from_element(3_i64), Item::new_from_element(3_i64)); assert!(elements(&portion).is_empty()); } diff --git a/node-graph/nodes/gstd/src/platform_application_io.rs b/node-graph/nodes/gstd/src/platform_application_io.rs index 9009d93928d..ca11108e408 100644 --- a/node-graph/nodes/gstd/src/platform_application_io.rs +++ b/node-graph/nodes/gstd/src/platform_application_io.rs @@ -162,8 +162,8 @@ fn pending_resource(_primary: &(), _url: &Item) -> Item { /// Converts the raw byte data of an image file to a raster image. /// /// Supports the formats: PNG, JPG, GIF, WEBP, TIFF, BMP, TGA, ICO, HDR, EXR. -#[node_macro::node(category("Web Request"), icon("NodeDecodeImage"))] -fn decode_image(_: impl Ctx, data: Item) -> Item> { +#[node_macro::node(category("Web Request"), icon("NodeBytesToImage"), name("Bytes to Image"))] +fn bytes_to_image(_: impl Ctx, data: Item) -> Item> { let data = data.into_element(); let Some(image) = Image::from_encoded(data.as_ref()) else { return Item::default() }; diff --git a/node-graph/nodes/text/src/lib.rs b/node-graph/nodes/text/src/lib.rs index a9b4c9d05f0..84afa444fb2 100644 --- a/node-graph/nodes/text/src/lib.rs +++ b/node-graph/nodes/text/src/lib.rs @@ -948,8 +948,8 @@ fn string_join( } /// Iterates over a list of strings, evaluating the mapped operation for each one. Use the **Read String** node to access the current string inside the loop. -#[node_macro::node(category("Text"), icon("NodeMapString"))] -fn map_string( +#[node_macro::node(category("Text"), icon("NodeStringMap"))] +fn string_map( ctx: impl Ctx + CloneVarArgs + ExtractAll, strings: List, #[expose] @@ -969,7 +969,7 @@ fn map_string( Ok(result) } -/// Reads the current string from within a **Map String** node's loop. +/// Reads the current string from within a **String Map** node's loop. #[node_macro::node(category("Context"), icon("NodeReadContext"))] fn read_string(ctx: impl Ctx + ExtractVarArgs) -> Item { let Ok(var_arg) = ctx.vararg(0) else { return Item::new_from_element(String::new()) }; diff --git a/node-graph/nodes/vector/src/vector_nodes.rs b/node-graph/nodes/vector/src/vector_nodes.rs index be907f47448..4f20cd9ac94 100644 --- a/node-graph/nodes/vector/src/vector_nodes.rs +++ b/node-graph/nodes/vector/src/vector_nodes.rs @@ -1257,8 +1257,8 @@ where } /// Automatically constructs tangents (Bézier handles) for anchor points in a vector path. -#[node_macro::node(category("Vector: Modifier"), icon("NodeAutoTangents"), name("Auto-Tangents"), path(core_types::vector))] -fn auto_tangents( +#[node_macro::node(category("Vector: Modifier"), icon("NodeSmoothAnchors"), path(core_types::vector))] +fn smooth_anchors( _: impl Ctx, #[implementations(Graphic, Vector)] source: Item, /// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread). @@ -3700,8 +3700,8 @@ fn point_inside(_: impl Ctx, source: Item, point: Item) -> Item Item { +#[node_macro::node(category("General"), icon("NodeCount"), path(graphene_core::vector))] +fn count(_: impl Ctx, content: ListDyn) -> Item { Item::new_from_element(content.len() as f64) } @@ -4278,7 +4278,7 @@ mod test { // Existing handles are kept, so only the anchors without them are tangented and smooth #[test] - fn auto_tangents_marks_the_tangented_anchors_smooth() { + fn smooth_anchors_marks_the_tangented_anchors_smooth() { let mut bezpath = BezPath::new(); bezpath.move_to((0., 0.)); bezpath.line_to((10., 10.)); @@ -4294,7 +4294,7 @@ mod test { bezpath.line_to((200., 50.)); bezpath.close_path(); - let output = super::auto_tangents((), vector_item_from_bezpath(bezpath), item(0.5), item(true)); + let output = super::smooth_anchors((), vector_item_from_bezpath(bezpath), item(0.5), item(true)); assert_eq!( smooth_anchors_by_contour(&output), @@ -4304,7 +4304,7 @@ mod test { // A loop of one segment has no neighbor to share a tangent with, so it passes through unchanged #[test] - fn auto_tangents_keeps_a_single_segment_loop() { + fn smooth_anchors_keeps_a_single_segment_loop() { let teardrop = CubicBez::new((0., 0.), (50., -50.), (100., 50.), (0., 0.)); let mut bezpath = BezPath::new(); bezpath.move_to(teardrop.p0); @@ -4315,7 +4315,7 @@ mod test { bezpath.line_to((225., 40.)); bezpath.close_path(); - let output = super::auto_tangents((), vector_item_from_bezpath(bezpath), item(0.5), item(true)); + let output = super::smooth_anchors((), vector_item_from_bezpath(bezpath), item(0.5), item(true)); let paths = item_paths(&output); let segments: Vec = output.element().segment_iter(paths).map(|(_, segment, ..)| segment).collect();