mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Add upgrade script to convert "Spline" node to "Path" -> "Spline from Points" (#2274)
* write document upgrade code to transfrom Spline node into Path -> Spline from Points * fix only connecting to single output * shift position of newly inserted Path -> Spline from Points node * refactor * remove all old Spline node code * rename Spline from Points node to Spline * Code cleanup * Update the demo art to natively use the new Spline node --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
95bbc95606
commit
2d90bb0cbf
7 changed files with 87 additions and 40 deletions
2
demo-artwork/procedural-string-lights.graphite
generated
2
demo-artwork/procedural-string-lights.graphite
generated
File diff suppressed because one or more lines are too long
|
|
@ -2012,29 +2012,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Spline",
|
||||
category: "Vector: Shape",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::vector::generator_nodes::SplineNode"),
|
||||
manual_composition: Some(concrete!(())),
|
||||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::None, false),
|
||||
// TODO: Modify the proto node generation macro to accept this default value, then remove this definition for Spline
|
||||
NodeInput::value(TaggedValue::VecDVec2(vec![DVec2::new(0., -50.), DVec2::new(25., 0.), DVec2::new(0., 50.)]), false),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_properties: vec!["None".into(), PropertiesRow::with_override("Points", WidgetOverride::Custom("spline_input".to_string()))],
|
||||
output_names: vec!["Vector".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Path",
|
||||
category: "Vector",
|
||||
|
|
|
|||
|
|
@ -16,10 +16,13 @@ use crate::messages::prelude::*;
|
|||
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
|
||||
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
||||
|
||||
use bezier_rs::Subpath;
|
||||
use glam::IVec2;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_std::vector::style::{Fill, FillType, Gradient};
|
||||
use graphene_std::vector::{VectorData, VectorDataTable};
|
||||
use interpreted_executor::dynamic_executor::IntrospectError;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
|
@ -480,6 +483,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
("graphene_core::raster::VibranceNode", "graphene_core::raster::adjustments::VibranceNode"),
|
||||
("graphene_core::text::TextGeneratorNode", "graphene_core::text::TextNode"),
|
||||
("graphene_core::transform::SetTransformNode", "graphene_core::transform::ReplaceTransformNode"),
|
||||
("graphene_core::vector::SplinesFromPointsNode", "graphene_core::vector::SplineNode"),
|
||||
("graphene_core::vector::generator_nodes::EllipseGenerator", "graphene_core::vector::generator_nodes::EllipseNode"),
|
||||
("graphene_core::vector::generator_nodes::LineGenerator", "graphene_core::vector::generator_nodes::LineNode"),
|
||||
("graphene_core::vector::generator_nodes::PathGenerator", "graphene_core::vector::generator_nodes::PathNode"),
|
||||
|
|
@ -488,7 +492,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
"graphene_core::vector::generator_nodes::RegularPolygonGenerator",
|
||||
"graphene_core::vector::generator_nodes::RegularPolygonNode",
|
||||
),
|
||||
("graphene_core::vector::generator_nodes::SplineGenerator", "graphene_core::vector::generator_nodes::SplineNode"),
|
||||
("graphene_core::vector::generator_nodes::StarGenerator", "graphene_core::vector::generator_nodes::StarNode"),
|
||||
("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"),
|
||||
("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"),
|
||||
|
|
@ -637,6 +640,77 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
}
|
||||
}
|
||||
|
||||
// Rename the old "Splines from Points" node to "Spline" and upgrade it to the new "Spline" node
|
||||
if reference == "Splines from Points" {
|
||||
document.network_interface.set_reference(node_id, &[], Some("Spline".to_string()));
|
||||
}
|
||||
|
||||
// Upgrade the old "Spline" node to the new "Spline" node
|
||||
if reference == "Spline" {
|
||||
// Retrieve the proto node identifier and verify it is the old "Spline" node, otherwise skip it if this is the new "Spline" node
|
||||
let identifier = document.network_interface.implementation(node_id, &[]).and_then(|implementation| implementation.get_proto_node());
|
||||
if identifier.map(|identifier| &identifier.name) != Some(&"graphene_core::vector::generator_nodes::SplineNode".into()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Obtain the document node for the given node ID, extract the vector points, and create vector data from the list of points
|
||||
let node = document.network_interface.document_node(node_id, &[]).unwrap();
|
||||
let Some(TaggedValue::VecDVec2(points)) = node.inputs.get(1).and_then(|tagged_value| tagged_value.as_value()) else {
|
||||
log::error!("The old Spline node's input at index 1 is not a TaggedValue::VecDVec2");
|
||||
continue;
|
||||
};
|
||||
let vector_data = VectorData::from_subpath(Subpath::from_anchors_linear(points.to_vec(), false));
|
||||
|
||||
// Retrieve the output connectors linked to the "Spline" node's output port
|
||||
let spline_outputs = document
|
||||
.network_interface
|
||||
.outward_wires(&[])
|
||||
.unwrap()
|
||||
.get(&OutputConnector::node(*node_id, 0))
|
||||
.expect("Vec of InputConnector Spline node is connected to its output port 0.")
|
||||
.clone();
|
||||
|
||||
// Get the node's current position in the graph
|
||||
let Some(node_position) = document.network_interface.position(node_id, &[]) else {
|
||||
log::error!("Could not get position of spline node.");
|
||||
continue;
|
||||
};
|
||||
|
||||
// Get the "Path" node definition and fill it in with the vector data and default vector modification
|
||||
let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist.");
|
||||
let path_node = path_node_type.node_template_input_override([
|
||||
Some(NodeInput::value(TaggedValue::VectorData(VectorDataTable::new(vector_data)), true)),
|
||||
Some(NodeInput::value(TaggedValue::VectorModification(Default::default()), false)),
|
||||
]);
|
||||
|
||||
// Get the "Spline" node definition and wire it up with the "Path" node as input
|
||||
let spline_node_type = resolve_document_node_type("Spline").expect("Spline node does not exist.");
|
||||
let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]);
|
||||
|
||||
// Create a new node group with the "Path" and "Spline" nodes and generate new node IDs for them
|
||||
let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)];
|
||||
let new_ids = nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect::<HashMap<_, _>>();
|
||||
let new_spline_id = *new_ids.get(&NodeId(0)).unwrap();
|
||||
let new_path_id = *new_ids.get(&NodeId(1)).unwrap();
|
||||
|
||||
// Remove the old "Spline" node from the document
|
||||
document.network_interface.delete_nodes(vec![*node_id], false, &[]);
|
||||
|
||||
// Insert the new "Path" and "Spline" nodes into the network interface with generated IDs
|
||||
document.network_interface.insert_node_group(nodes.clone(), new_ids, &[]);
|
||||
|
||||
// Reposition the new "Spline" node to match the original "Spline" node's position
|
||||
document.network_interface.shift_node(&new_spline_id, node_position, &[]);
|
||||
|
||||
// Reposition the new "Path" node with an offset relative to the original "Spline" node's position
|
||||
document.network_interface.shift_node(&new_path_id, node_position + IVec2::new(-7, 0), &[]);
|
||||
|
||||
// Redirect each output connection from the old node to the new "Spline" node's output port
|
||||
for input_connector in spline_outputs {
|
||||
document.network_interface.set_input(&input_connector, NodeInput::node(new_spline_id, 0), &[]);
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
|
||||
if reference == "Text" && inputs_count != 8 {
|
||||
let node_definition = resolve_document_node_type(reference).unwrap();
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ impl Fsm for SplineToolFsmState {
|
|||
|
||||
let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let path_node = path_node_type.default_node_template();
|
||||
let spline_node_type = resolve_document_node_type("Splines from Points").expect("Spline from Points node does not exist");
|
||||
let spline_node_type = resolve_document_node_type("Spline").expect("Spline node does not exist");
|
||||
let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]);
|
||||
let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)];
|
||||
|
||||
|
|
|
|||
|
|
@ -106,17 +106,6 @@ fn line<F: 'n + Send>(#[implementations((), Footprint)] _footprint: F, _primary:
|
|||
VectorDataTable::new(VectorData::from_subpath(Subpath::new_line(start, end)))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn spline<F: 'n + Send>(#[implementations((), Footprint)] _footprint: F, _primary: (), points: Vec<DVec2>) -> VectorDataTable {
|
||||
let mut spline = VectorData::from_subpath(Subpath::new_cubic_spline(points));
|
||||
|
||||
for pair in spline.segment_domain.ids().windows(2) {
|
||||
spline.colinear_manipulators.push([HandleId::end(pair[0]), HandleId::primary(pair[1])]);
|
||||
}
|
||||
|
||||
VectorDataTable::new(spline)
|
||||
}
|
||||
|
||||
// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node
|
||||
#[node_macro::node(category(""))]
|
||||
fn path<F: 'n + Send>(#[implementations((), Footprint)] _footprint: F, path_data: Vec<Subpath<PointId>>, colinear_manipulators: Vec<PointId>) -> VectorDataTable {
|
||||
|
|
|
|||
|
|
@ -864,8 +864,8 @@ async fn subpath_segment_lengths<F: 'n + Send>(
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[node_macro::node(name("Splines from Points"), category("Vector"), path(graphene_core::vector))]
|
||||
async fn splines_from_points<F: 'n + Send>(
|
||||
#[node_macro::node(name("Spline"), category("Vector"), path(graphene_core::vector))]
|
||||
async fn spline<F: 'n + Send>(
|
||||
#[implementations(
|
||||
(),
|
||||
Footprint,
|
||||
|
|
@ -1431,7 +1431,7 @@ mod test {
|
|||
}
|
||||
#[tokio::test]
|
||||
async fn spline() {
|
||||
let spline = splines_from_points(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.))).await;
|
||||
let spline = super::spline(Footprint::default(), &vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.))).await;
|
||||
let spline = spline.one_item();
|
||||
assert_eq!(spline.stroke_bezier_paths().count(), 1);
|
||||
assert_eq!(spline.point_domain.positions(), &[DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]);
|
||||
|
|
|
|||
|
|
@ -576,6 +576,13 @@ impl DocumentNodeImplementation {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_proto_node(&self) -> Option<&ProtoNodeIdentifier> {
|
||||
match self {
|
||||
DocumentNodeImplementation::ProtoNode(p) => Some(p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn proto(name: &'static str) -> Self {
|
||||
Self::ProtoNode(ProtoNodeIdentifier::new(name))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue