2029 lines
93 KiB
Rust
2029 lines
93 KiB
Rust
// TODO: Eventually remove this document upgrade code
|
|
// This file contains lots of hacky code for upgrading old documents to the new format
|
|
|
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type};
|
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
|
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector};
|
|
use crate::messages::prelude::DocumentMessageHandler;
|
|
use glam::{DVec2, IVec2};
|
|
use graph_craft::document::DocumentNode;
|
|
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
|
use graphene_std::ProtoNodeIdentifier;
|
|
use graphene_std::text::{TextAlign, TypesettingConfig};
|
|
use graphene_std::transform::ScaleType;
|
|
use graphene_std::uuid::NodeId;
|
|
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
|
use std::collections::HashMap;
|
|
use std::f64::consts::PI;
|
|
|
|
const TEXT_REPLACEMENTS: &[(&str, &str)] = &[
|
|
("graphene_core::vector::vector_nodes::SamplePointsNode", "graphene_core::vector::SamplePolylineNode"),
|
|
("graphene_core::vector::vector_nodes::SubpathSegmentLengthsNode", "graphene_core::vector::SubpathSegmentLengthsNode"),
|
|
("\"manual_composition\":null", "\"manual_composition\":{\"Generic\":\"T\"}"),
|
|
(
|
|
"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>",
|
|
"core::option::Option<alloc::sync::Arc<core_types::context::OwnedContextImpl>>",
|
|
),
|
|
("graphene_core::transform::Footprint", "graphene_core::transform::Footprint"),
|
|
("\"OptionalF64\":", "\"F64\":"),
|
|
("\"path_bool_nodes::BooleanOperation\"", "\"vector_types::vector::misc::BooleanOperation\""),
|
|
];
|
|
|
|
pub struct NodeReplacement<'a> {
|
|
node: ProtoNodeIdentifier,
|
|
aliases: &'a [&'a str],
|
|
}
|
|
|
|
const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
|
// ================================
|
|
// blending
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::blending_nodes::blending::IDENTIFIER,
|
|
aliases: &["graphene_core::raster::BlendingNode", "graphene_core::blending_nodes::BlendingNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::blending_nodes::blend_mode::IDENTIFIER,
|
|
aliases: &["graphene_core::raster::BlendModeNode", "graphene_core::blending_nodes::BlendModeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::blending_nodes::opacity::IDENTIFIER,
|
|
aliases: &["graphene_core::raster::OpacityNode", "graphene_core::blending_nodes::OpacityNode"],
|
|
},
|
|
// ================================
|
|
// brush
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::brush::brush::blit::IDENTIFIER,
|
|
aliases: &["graphene_brush::BlitNode", "graphene_std::brush::BlitNode", "graphene_brush::brush::BlitNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::brush::brush::brush::IDENTIFIER,
|
|
aliases: &["graphene_brush::BrushNode", "graphene_std::brush::BrushNode", "graphene_brush::brush::BrushNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::brush::brush::brush_stamp_generator::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_brush::BrushStampGeneratorNode",
|
|
"graphene_std::brush::BrushStampGeneratorNode",
|
|
"graphene_brush::brush::BrushStampGeneratorNode",
|
|
],
|
|
},
|
|
// ================================
|
|
// gcore
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::animation::animation_time::IDENTIFIER,
|
|
aliases: &["graphene_core::animation::AnimationTimeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::debug::clone::IDENTIFIER,
|
|
aliases: &["graphene_core::ops::CloneNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::extract_xy::extract_xy::IDENTIFIER,
|
|
aliases: &["graphene_core::ops::ExtractXyNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::ops::identity::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::transform::CullNode",
|
|
"graphene_core::transform::BoundlessFootprintNode",
|
|
"graphene_core::transform::FreezeRealTimeNode",
|
|
"graphene_core::transform_nodes::BoundlessFootprintNode",
|
|
"graphene_core::transform_nodes::FreezeRealTimeNode",
|
|
"graphene_core::vector::SubpathSegmentLengthsNode",
|
|
"core_types::vector::SubpathSegmentLengthsNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::memo::monitor::IDENTIFIER,
|
|
aliases: &["graphene_core::memo::MonitorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::memo::memo::IDENTIFIER,
|
|
aliases: &["graphene_core::memo::MemoNode", "graphene_core::memo::ImpureMemoNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::animation::real_time::IDENTIFIER,
|
|
aliases: &["graphene_core::animation::RealTimeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::debug::size_of::IDENTIFIER,
|
|
aliases: &["graphene_core::ops::SizeOfNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::debug::some::IDENTIFIER,
|
|
aliases: &["graphene_core::ops::SomeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::debug::unwrap_option::IDENTIFIER,
|
|
aliases: &["graphene_core::ops::UnwrapNode", "graphene_core::debug::UnwrapNode"],
|
|
},
|
|
// ================================
|
|
// graphic
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::artboard::create_artboard::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::artboard::CreateArtboardNode",
|
|
"graphene_core::ConstructArtboardNode",
|
|
"graphene_core::graphic_element::ToArtboardNode",
|
|
"graphene_core::artboard::ToArtboardNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::extend::IDENTIFIER,
|
|
aliases: &["graphene_core::graphic::graphic::ExtendNode", "graphene_core::graphic::ExtendNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::flatten_graphic::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::graphic::FlattenGraphicNode",
|
|
"graphene_core::graphic_element::FlattenGroupNode",
|
|
"graphene_core::graphic_types::FlattenGroupNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::flatten_vector::IDENTIFIER,
|
|
aliases: &["graphene_core::graphic::FlattenVectorNode", "graphene_core::graphic_element::FlattenVectorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::index_elements::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::graphic_element::IndexNode",
|
|
"graphene_core::graphic::IndexNode",
|
|
"graphene_core::graphic::IndexElementsNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::legacy_layer_extend::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::graphic_element::LayerNode",
|
|
"graphene_core::graphic_types::LayerNode",
|
|
// Converted from "Append Artboard"
|
|
"graphene_core::AddArtboardNode",
|
|
"graphene_core::graphic_element::AppendArtboardNode",
|
|
"graphene_core::graphic_types::AppendArtboardNode",
|
|
"graphene_core::artboard::AppendArtboardNode",
|
|
"graphene_core::graphic::LegacyLayerExtendNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::to_graphic::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::ToGraphicGroupNode",
|
|
"graphene_core::graphic_element::ToGroupNode",
|
|
"graphene_core::graphic_types::ToGroupNode",
|
|
"graphene_core::graphic::ToGraphicNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::wrap_graphic::IDENTIFIER,
|
|
aliases: &[
|
|
// Converted from "To Element"
|
|
"graphene_core::ToGraphicElementNode",
|
|
"graphene_core::graphic_element::ToElementNode",
|
|
"graphene_core::graphic_types::ToElementNode",
|
|
"graphene_core::graphic::WrapGraphicNode",
|
|
],
|
|
},
|
|
// ================================
|
|
// math
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::absolute_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::AbsoluteValueNode", "graphene_core::ops::AbsoluteValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::add::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::AddNode", "graphene_core::ops::AddNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::bool_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::BoolValueNode", "graphene_core::ops::BoolValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::ceiling::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::CeilingNode", "graphene_core::ops::CeilingNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::clamp::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ClampNode", "graphene_core::ops::ClampNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::color_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ColorValueNode", "graphene_core::ops::ColorValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::cosine::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::CosineNode", "graphene_core::ops::CosineNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::cosine_inverse::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::CosineInverseNode", "graphene_core::ops::CosineInverseNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::divide::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::DivideNode", "graphene_core::ops::DivideNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::dot_product::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::DotProductNode", "graphene_core::ops::DotProductNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::equals::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::EqualsNode", "graphene_core::ops::EqualsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::exponent::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ExponentNode", "graphene_core::ops::ExponentNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::floor::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::FloorNode", "graphene_core::ops::FloorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::footprint_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::FootprintValueNode", "graphene_core::ops::FootprintValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::gradient_value::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_math_nodes::GradientValueNode",
|
|
"graphene_core::ops::GradientValueNode",
|
|
"graphene_math_nodes::GradientTableValueNode",
|
|
"graphene_core::ops::GradientTableValueNode",
|
|
"math_nodes::GradientTableValueNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::greater_than::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::GreaterThanNode", "graphene_core::ops::GreaterThanNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::greatest_common_divisor::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_math_nodes::GreatestCommonDivisor",
|
|
"graphene_core::ops::GreatestCommonDivisor",
|
|
"graphene_math_nodes::GreatestCommonDivisorNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::least_common_multiple::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_math_nodes::LeastCommonMultiple",
|
|
"graphene_core::ops::LeastCommonMultiple",
|
|
"graphene_math_nodes::LeastCommonMultipleNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::length::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::LengthNode", "graphene_core::ops::LenghtNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::less_than::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::LessThanNode", "graphene_core::ops::LessThanNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::logarithm::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::LogarithmNode", "graphene_core::ops::LogarithmNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::logical_and::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::ops::LogicalAndNode",
|
|
"graphene_core::ops::LogicNotNode",
|
|
"graphene_core::logic::LogicNotNode",
|
|
"graphene_math_nodes::LogicalAndNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::logical_not::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::ops::LogicalNotNode",
|
|
"graphene_core::ops::LogicOrNode",
|
|
"graphene_core::logic::LogicOrNode",
|
|
"graphene_math_nodes::LogicalNotNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::logical_or::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::ops::LogicalOrNode",
|
|
"graphene_core::ops::LogicAndNode",
|
|
"graphene_core::logic::LogicAndNode",
|
|
"graphene_math_nodes::LogicalOrNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::math::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::MathNode", "graphene_core::ops::MathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::max::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::MaxNode", "graphene_core::ops::MaxNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::min::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::MinNode", "graphene_core::ops::MinNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::modulo::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ModuloNode", "graphene_core::ops::ModuloNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::multiply::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::MultiplyNode", "graphene_core::ops::MultiplyNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::normalize::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::NormalizeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::not_equals::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::NotEqualsNode", "graphene_core::ops::NotEqualsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::number_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::NumberValueNode", "graphene_core::ops::NumberValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::percentage_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::PercentageValueNode", "graphene_core::ops::PercentageValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::random::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::RandomNode", "graphene_core::ops::RandomNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::remap::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::RemapNode", "graphene_core::ops::RemapNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::root::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::RootNode", "graphene_core::ops::RootNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::round::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::RoundNode", "graphene_core::ops::RoundNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::sample_gradient::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::SampleGradientNode", "graphene_core::ops::SampleGradientNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::sine::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::SineNode", "graphene_core::ops::SineNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::sine_inverse::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::SineInverseNode", "graphene_core::ops::SineInverseNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::subtract::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::SubtractNode", "graphene_core::ops::SubtractNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::tangent::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::TangentNode", "graphene_core::ops::TangentNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::tangent_inverse::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::TangentInverseNode", "graphene_core::ops::TangentInverseNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::to_f_64::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ToF64Node", "graphene_core::ops::ToF64Node"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::to_u_32::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ToU32Node", "graphene_core::ops::ToU32Node", "math_nodes::ToU32Node"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::to_u_64::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::ToU64Node", "graphene_core::ops::ToU64Node"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::vec_2_value::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_math_nodes::Vec2ValueNode",
|
|
"graphene_core::ops::ConstructVector2",
|
|
"graphene_core::ops::Vector2ValueNode",
|
|
"graphene_core::ops::CoordinateValueNode",
|
|
"graphene_math_nodes::CoordinateValueNode",
|
|
],
|
|
},
|
|
// ================================
|
|
// path bool
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER,
|
|
aliases: &["graphene_path_bool::BooleanOperationNode", "graphene_std::vector::BooleanOperationNode"],
|
|
},
|
|
// ================================
|
|
// raster
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::black_and_white::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::BlackAndWhiteNode",
|
|
"graphene_core::raster::adjustments::BlackAndWhiteNode",
|
|
"graphene_core::raster::BlackAndWhiteNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::blending_nodes::mix::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::BlendNode",
|
|
"raster_nodes::adjustments::BlendNode",
|
|
"graphene_core::raster::adjustments::BlendNode",
|
|
"graphene_core::raster::BlendNode",
|
|
"graphene_raster_nodes::blending_nodes::BlendNode",
|
|
"raster_nodes::blending_nodes::BlendNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::filter::blur::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::filter::BlurNode", "graphene_std::filter::BlurNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::brightness_contrast::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::BrightnessContrastNode",
|
|
"graphene_core::raster::adjustments::BrightnessContrastNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::brightness_contrast_classic::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::adjustments::BrightnessContrastClassicNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::channel_mixer::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::ChannelMixerNode",
|
|
"graphene_core::raster::adjustments::ChannelMixerNode",
|
|
"graphene_core::raster::ChannelMixerNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::blending_nodes::color_overlay::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::ColorOverlayNode",
|
|
"graphene_raster_nodes::generate_curves::ColorOverlayNode",
|
|
"raster_nodes::adjustments::ColorOverlayNode",
|
|
"graphene_core::raster::adjustments::ColorOverlayNode",
|
|
"raster_nodes::generate_curves::ColorOverlayNode",
|
|
"graphene_raster_nodes::blending_nodes::ColorOverlayNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::combine_channels::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::CombineChannelsNode", "graphene_std::raster::CombineChannelsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::dehaze::dehaze::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::dehaze::DehazeNode", "graphene_std::dehaze::DehazeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::empty_image::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::EmptyImageNode", "graphene_std::raster::EmptyImageNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::exposure::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::ExposureNode",
|
|
"graphene_core::raster::adjustments::ExposureNode",
|
|
"graphene_core::raster::ExposureNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::extend_image_to_bounds::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::ExtendImageToBoundsNode", "graphene_std::raster::ExtendImageToBoundsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::extract_channel::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::ExtractChannelNode",
|
|
"graphene_core::raster::adjustments::ExtractChannelNode",
|
|
"graphene_core::raster::ExtractChannelNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::gamma_correction::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::adjustments::GammaCorrectionNode", "graphene_core::raster::adjustments::GammaCorrectionNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::generate_curves::generate_curves::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::generate_curves::GenerateCurvesNode", "graphene_core::raster::adjustments::GenerateCurvesNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::gradient_map::gradient_map::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::gradient_map::GradientMapNode",
|
|
"graphene_raster_nodes::adjustments::GradientMapNode",
|
|
"raster_nodes::gradient_map::GradientMapNode",
|
|
"raster_nodes::adjustments::GradientMapNode",
|
|
"graphene_core::raster::adjustments::GradientMapNode",
|
|
"graphene_core::raster::GradientMapNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::hue_saturation::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::HueSaturationNode",
|
|
"graphene_core::raster::adjustments::HueSaturationNode",
|
|
"graphene_core::raster::HueSaturationNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::image_color_palette::image_color_palette::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::image_color_palette::ImageColorPaletteNode",
|
|
"graphene_std::image_color_palette::ImageColorPaletteNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::image::IDENTIFIER,
|
|
aliases: &[
|
|
"raster_nodes::std_nodes::ImageValueNode",
|
|
"graphene_raster_nodes::std_nodes::ImageValueNode",
|
|
"graphene_std::raster::ImageValueNode",
|
|
"graphene_std::raster::ImageNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::invert::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::InvertNode",
|
|
"graphene_core::raster::adjustments::InvertNode",
|
|
"graphene_core::raster::InvertNode",
|
|
"graphene_core::raster::InvertRGBNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::levels::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::LevelsNode",
|
|
"graphene_core::raster::adjustments::LevelsNode",
|
|
"graphene_core::raster::LevelsNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::luminance::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::LuminanceNode",
|
|
"graphene_core::raster::adjustments::LuminanceNode",
|
|
"graphene_core::raster::LuminanceNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::make_opaque::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::MakeOpaqueNode",
|
|
"graphene_core::raster::adjustments::MakeOpaqueNode",
|
|
"graphene_core::raster::ExtractOpaqueNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::mandelbrot::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::MandelbrotNode", "graphene_std::raster::MandelbrotNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::mask::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::MaskNode", "graphene_std::raster::MaskNode", "graphene_std::raster::MaskImageNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::posterize::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::PosterizeNode",
|
|
"graphene_core::raster::adjustments::PosterizeNode",
|
|
"graphene_core::raster::PosterizeNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::std_nodes::NoisePatternNode", "graphene_std::raster::NoisePatternNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::std_nodes::sample_image::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::std_nodes::SampleImageNode",
|
|
"graphene_std::raster::SampleImageNode",
|
|
"graphene_std::raster::SampleNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::selective_color::IDENTIFIER,
|
|
aliases: &["graphene_raster_nodes::adjustments::SelectiveColorNode", "graphene_core::raster::adjustments::SelectiveColorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::threshold::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::ThresholdNode",
|
|
"graphene_core::raster::adjustments::ThresholdNode",
|
|
"graphene_core::raster::ThresholdNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::raster_nodes::adjustments::vibrance::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_raster_nodes::adjustments::VibranceNode",
|
|
"graphene_core::raster::adjustments::VibranceNode",
|
|
"graphene_core::raster::VibranceNode",
|
|
],
|
|
},
|
|
// ================================
|
|
// text
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::text::text::IDENTIFIER,
|
|
aliases: &["graphene_core::text::text::TextNode", "graphene_core::text::TextGeneratorNode", "graphene_core::text::TextNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_value::IDENTIFIER,
|
|
aliases: &["graphene_math_nodes::StringValueNode", "graphene_core::ops::StringValueNode", "math_nodes::StringValueNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_concatenate::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::StringConcatenateNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_length::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::StringLengthNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_replace::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::StringReplaceNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_slice::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::StringSliceNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::string_split::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::StringSplitNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::math_nodes::switch::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::SwitchNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::to_string::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::ToStringNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::json::query_json::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::JsonGetNode", "graphene_std::text_nodes::JsonGetNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::text_nodes::serialize::IDENTIFIER,
|
|
aliases: &["graphene_core::logic::SerializeNode"],
|
|
},
|
|
// ================================
|
|
// transform
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::decompose_rotation::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::transform_nodes::RotationScaleNode",
|
|
"graphene_core::transform::RotationScaleNode",
|
|
"graphene_core::transform_nodes::DecomposeRotationNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::decompose_scale::IDENTIFIER,
|
|
aliases: &["graphene_core::transform_nodes::DecomposeScaleNode", "graphene_core::transform::DecomposeScaleNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::decompose_translation::IDENTIFIER,
|
|
aliases: &["graphene_core::transform_nodes::DecomposeTranslationNode", "graphene_core::transform::DecomposeTranslationNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::extract_transform::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::transform_nodes::ExtractTransformNode",
|
|
"graphene_core::transform::ExtractTransformNode",
|
|
"graphene_core::vector::ExtractTransformNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::invert_transform::IDENTIFIER,
|
|
aliases: &["graphene_core::transform_nodes::InvertTransformNode", "graphene_core::transform::InvertTransformNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::replace_transform::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::transform_nodes::ReplaceTransformNode",
|
|
"graphene_core::transform::SetTransformNode",
|
|
"graphene_core::transform::ReplaceTransformNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
|
aliases: &["graphene_core::transform_nodes::TransformNode", "graphene_core::transform::TransformNode"],
|
|
},
|
|
// ================================
|
|
// vector
|
|
// ================================
|
|
NodeReplacement {
|
|
node: graphene_std::vector::apply_transform::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::ApplyTransformNode", "graphene_core::vector::vector_modification::ApplyTransformNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::area::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::AreaNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::assign_colors::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::AssignColorsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::auto_tangents::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::vector_nodes::AutoTangentsNode", "graphene_core::vector::AutoTangentsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::bevel::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::BevelNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::bounding_box::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::BoundingBoxNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::box_warp::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::BoxWarpNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::centroid::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::CentroidNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::close_path::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::ClosePathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::count_elements::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::CountElementsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::cut_path::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::vector_nodes::SplitPathNode", "graphene_core::vector::SplitPathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::cut_segments::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::vector_nodes::SplitSegmentsNode",
|
|
"graphene_core::vector::SplitSegmentsNode",
|
|
"graphene_core::vector::CutSegmentsNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::dimensions::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::DimensionsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector_nodes::fill::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::vector_nodes::FillNode", "graphene_core::vector::FillNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::flatten_path::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::vector_nodes::FlattenPathNode",
|
|
"graphene_core::vector::FlattenVectorElementsNode",
|
|
"graphene_core::vector::FlattenPathNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::arc::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::ArcNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::circle::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::CircleNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::ellipse::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::EllipseNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::grid::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::GridNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::line::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::LineNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::rectangle::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::RectangleNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::RegularPolygonNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::spiral::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::SpiralNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::generator_nodes::star::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::generator_nodes::StarNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::context::read_index::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstanceIndexNode", "core_types::vector::InstanceIndexNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::map::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstanceMapNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::context::read_position::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstancePositionNode", "core_types::vector::InstancePositionNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::context::read_vector::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstanceVectorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::repeat::repeat::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstanceRepeatNode", "core_types::vector::InstanceRepeatNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::repeat::repeat_array::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::RepeatNode", "core_types::vector::RepeatNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::repeat::repeat_radial::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::CircularRepeatNode", "core_types::vector::CircularRepeatNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::repeat::repeat_on_points::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::InstanceOnPointsNode", "core_types::vector::InstanceOnPointsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::copy_to_points::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::CopyToPointsNode", "core_types::vector::CopyToPointsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::jitter_points::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::JitterPointsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::merge_by_distance::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::MergeByDistanceNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::graphic::mirror::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::MirrorNode", "core_types::vector::MirrorNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::morph::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::MorphNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::offset_path::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::OffsetPathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::path_length::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::PathLengthNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::path_modify::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::vector_modification_nodes::PathModifyNode",
|
|
"graphene_core::vector::vector_data::modification::PathModifyNode",
|
|
"graphene_core::vector::vector_modification::PathModifyNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::point_inside::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::PointInsideNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::points_to_polyline::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::PointsToPolylineNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::scatter_points::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::PoissonDiskPointsNode", "core_types::vector::PoissonDiskPointsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::position_on_path::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::PositionOnPathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::round_corners::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::RoundCornersNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::sample_polyline::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::SamplePolylineNode",
|
|
"graphene_core::vector::SamplePointsNode",
|
|
"graphene_core::vector::vector_nodes::SamplePointsNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::separate_subpaths::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::SeparateSubpathsNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::solidify_stroke::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::SolidifyStrokeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::spline::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::vector_nodes::SplineNode",
|
|
"graphene_core::vector::SplinesFromPointsNode",
|
|
"graphene_core::vector::SplineNode",
|
|
],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::stroke::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::StrokeNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::tangent_on_path::IDENTIFIER,
|
|
aliases: &["graphene_core::vector::TangentOnPathNode"],
|
|
},
|
|
NodeReplacement {
|
|
node: graphene_std::vector::vec_2_to_point::IDENTIFIER,
|
|
aliases: &[
|
|
"graphene_core::vector::vector_nodes::PositionToPointNode",
|
|
"graphene_core::vector::PositionToPointNode",
|
|
"graphene_core::vector::Vec2ToPointNode",
|
|
],
|
|
},
|
|
];
|
|
|
|
const REPLACEMENTS: &[(&str, &str)] = &[];
|
|
|
|
pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String {
|
|
let document_serialized_content = replace_optional_f64_null(&document_serialized_content);
|
|
|
|
TEXT_REPLACEMENTS
|
|
.iter()
|
|
.fold(document_serialized_content, |document_serialized_content, (old, new)| document_serialized_content.replace(old, new))
|
|
}
|
|
|
|
fn replace_optional_f64_null(input: &str) -> String {
|
|
let mut result = String::new();
|
|
let mut last_end = 0;
|
|
let key = "\"OptionalF64\":";
|
|
|
|
for (start, _) in input.match_indices(key) {
|
|
let search_start = start + key.len();
|
|
if search_start >= input.len() {
|
|
continue;
|
|
}
|
|
|
|
let mut after_key_start = search_start;
|
|
for (i, c) in input[search_start..].char_indices() {
|
|
if !c.is_whitespace() {
|
|
after_key_start = search_start + i;
|
|
break;
|
|
}
|
|
// If we reach the end and it's all whitespace, update after_key_start
|
|
if search_start + i + c.len_utf8() == input.len() {
|
|
after_key_start = input.len();
|
|
}
|
|
}
|
|
|
|
if input[after_key_start..].starts_with("null") {
|
|
result.push_str(&input[last_end..start]);
|
|
result.push_str(key);
|
|
result.push_str("0.0");
|
|
last_end = after_key_start + "null".len();
|
|
}
|
|
}
|
|
|
|
result.push_str(&input[last_end..]);
|
|
result
|
|
}
|
|
|
|
pub fn document_migration_reset_node_definition(document_serialized_content: &str) -> bool {
|
|
// Upgrade a document being opened to use fresh copies of all nodes
|
|
if document_serialized_content.contains("node_output_index") {
|
|
return true;
|
|
}
|
|
|
|
// Upgrade layer implementation from https://github.com/GraphiteEditor/Graphite/pull/1946 (see also `fn fix_nodes()` in `main.rs` of Graphene CLI)
|
|
if document_serialized_content.contains("graphene_core::ConstructLayerNode") || document_serialized_content.contains("graphene_core::AddArtboardNode") {
|
|
return true;
|
|
}
|
|
|
|
// The `source_node_id` proto node was removed in favor of `parent_layer` + `write_attribute`.
|
|
// Documents that still reference it inside their Merge or Artboard layer networks need those layer definitions
|
|
// reset to the current default so the new internal plumbing replaces the obsolete node.
|
|
if document_serialized_content.contains("graphic_nodes::graphic::SourceNodeIdNode")
|
|
|| document_serialized_content.contains("graphene_core::graphic::graphic::SourceNodeIdNode")
|
|
|| document_serialized_content.contains("graphene_core::graphic::SourceNodeIdNode")
|
|
{
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) {
|
|
document.network_interface.migrate_path_modify_node();
|
|
|
|
let network = document.network_interface.document_network().clone();
|
|
|
|
// Apply string and node replacements to each node
|
|
let mut replacements = HashMap::<&str, ProtoNodeIdentifier>::new();
|
|
Iterator::chain(
|
|
NODE_REPLACEMENTS.iter().flat_map(|NodeReplacement { node, aliases }| aliases.iter().map(|old| (*old, node.clone()))),
|
|
REPLACEMENTS.iter().map(|(old, new)| (*old, ProtoNodeIdentifier::new(new))),
|
|
)
|
|
.for_each(|(old, new)| {
|
|
if replacements.insert(old, new).is_some() {
|
|
panic!("Duplicate old name `{old}`");
|
|
}
|
|
});
|
|
|
|
for (node_id, node, network_path) in network.recursive_nodes() {
|
|
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
|
|
let node_path_without_type_args = protonode_id.as_str().split('<').next();
|
|
if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) {
|
|
let mut default_template = NodeTemplate::default();
|
|
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
|
|
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
|
|
document.network_interface.set_call_argument(node_id, &network_path, default_template.document_node.call_argument);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply upgrades to each unmodified node.
|
|
let nodes = document
|
|
.network_interface
|
|
.document_network()
|
|
.recursive_nodes()
|
|
.map(|(node_id, node, path)| (*node_id, node.clone(), path))
|
|
.collect::<Vec<(NodeId, graph_craft::document::DocumentNode, Vec<NodeId>)>>();
|
|
for (node_id, node, network_path) in &nodes {
|
|
migrate_node(node_id, node, network_path, document, reset_node_definitions_on_open);
|
|
}
|
|
}
|
|
|
|
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
|
|
// Must run before the reset block below: a node referencing a removed catalog entry would otherwise abort
|
|
// `migrate_node` via the `?` on `resolve_document_node_type`, preventing subsequent migration blocks from running.
|
|
migrate_removed_catalog_definitions(node_id, node, network_path, document);
|
|
|
|
if reset_node_definitions_on_open && let Some(reference) = document.network_interface.reference(node_id, network_path) {
|
|
let node_definition = resolve_document_node_type(&reference)?;
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template());
|
|
}
|
|
|
|
// Upgrade old nodes to use `Context` instead of `()` or `Footprint` as their call argument
|
|
if node.call_argument == graph_craft::concrete!(()) || node.call_argument == graph_craft::concrete!(graphene_std::transform::Footprint) {
|
|
document.network_interface.set_call_argument(node_id, network_path, graph_craft::concrete!(graphene_std::Context));
|
|
}
|
|
|
|
// Only nodes that have not been modified and still refer to a definition can be updated
|
|
let reference = document.network_interface.reference(node_id, network_path)?;
|
|
|
|
let mut inputs_count = node.inputs.len();
|
|
|
|
// Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644)
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER) && inputs_count == 8 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
let align_input = NodeInput::value(TaggedValue::StrokeAlign(StrokeAlign::Center), false);
|
|
let paint_order_input = NodeInput::value(TaggedValue::PaintOrder(PaintOrder::StrokeAbove), false);
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), align_input, network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[6].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[7].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 7), paint_order_input, network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 8), old_inputs[3].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 9), old_inputs[4].clone(), network_path);
|
|
}
|
|
|
|
// 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 == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER) && inputs_count == 8 {
|
|
let mut template: NodeTemplate = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut template);
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 4),
|
|
if inputs_count == 6 {
|
|
old_inputs[4].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 5),
|
|
if inputs_count == 6 {
|
|
old_inputs[5].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 6),
|
|
if inputs_count >= 7 {
|
|
old_inputs[6].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().max_width.unwrap_or_default()), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 7),
|
|
if inputs_count >= 8 {
|
|
old_inputs[7].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().max_width.unwrap_or_default()), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 8),
|
|
if inputs_count >= 9 {
|
|
old_inputs[8].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().tilt), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 9),
|
|
if inputs_count >= 10 {
|
|
old_inputs[9].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::TextAlign(TextAlign::default()), false)
|
|
},
|
|
network_path,
|
|
);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 10),
|
|
if inputs_count >= 11 {
|
|
old_inputs[10].clone()
|
|
} else {
|
|
NodeInput::value(TaggedValue::Bool(false), false)
|
|
},
|
|
network_path,
|
|
);
|
|
inputs_count = 11
|
|
}
|
|
|
|
// Insert bool parameters for `has_max_width` and `has_max_height`:
|
|
// https://github.com/GraphiteEditor/Graphite/pull/3643
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER) && inputs_count == 11 {
|
|
let mut template: NodeTemplate = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut template);
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?;
|
|
|
|
// Copy over old inputs
|
|
#[allow(clippy::needless_range_loop)]
|
|
for i in 0..=5 {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, i), old_inputs[i].clone(), network_path);
|
|
}
|
|
|
|
// Max Width
|
|
let Some(&TaggedValue::F64(old_max_width)) = old_inputs[6].as_value() else { return None };
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(old_max_width != 0.), false), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 7),
|
|
NodeInput::value(TaggedValue::F64(if old_max_width == 0. { 100. } else { old_max_width }), false),
|
|
network_path,
|
|
);
|
|
|
|
// Max Height
|
|
let Some(&TaggedValue::F64(old_max_height)) = old_inputs[7].as_value() else { return None };
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 8), NodeInput::value(TaggedValue::Bool(old_max_height != 0.), false), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 9),
|
|
NodeInput::value(TaggedValue::F64(if old_max_height == 0. { 100. } else { old_max_height }), false),
|
|
network_path,
|
|
);
|
|
|
|
// Copy over old inputs
|
|
#[allow(clippy::needless_range_loop)]
|
|
for i in 10..=12 {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, i), old_inputs[i - 2].clone(), network_path);
|
|
}
|
|
}
|
|
|
|
// Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
|
|
if inputs_count == 1
|
|
&& (reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::sine::IDENTIFIER)
|
|
|| reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::cosine::IDENTIFIER)
|
|
|| reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::tangent::IDENTIFIER))
|
|
{
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
|
}
|
|
|
|
// Upgrade the 'Tangent on Path' node to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::tangent_on_path::IDENTIFIER) && inputs_count == 4 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 4), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
|
}
|
|
|
|
// Upgrade the Modulo node to include a boolean input for whether the output should be always positive, which was previously not an option
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::modulo::IDENTIFIER) && inputs_count == 2 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path);
|
|
}
|
|
|
|
// Upgrade the Mirror node to add the `keep_original` boolean input
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::graphic::mirror::IDENTIFIER) && inputs_count == 3 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
|
}
|
|
|
|
// Upgrade the Mirror node to add the `reference_point` input and change `offset` from `DVec2` to `f64`
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::graphic::mirror::IDENTIFIER) && inputs_count == 4 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
let Some(&TaggedValue::DVec2(old_offset)) = old_inputs[1].as_value() else { return None };
|
|
let old_offset = if old_offset.x.abs() > old_offset.y.abs() { old_offset.x } else { old_offset.y };
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 1),
|
|
NodeInput::value(TaggedValue::ReferencePoint(graphene_std::transform::ReferencePoint::Center), false),
|
|
network_path,
|
|
);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::F64(old_offset), false), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[3].clone(), network_path);
|
|
}
|
|
|
|
// Upgrade artboard name being passed as hidden value input to "Create Artboard"
|
|
if reference == DefinitionIdentifier::Network("Artboard".into()) && reset_node_definitions_on_open {
|
|
let label = document.network_interface.display_name(node_id, network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER) && inputs_count == 1 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
|
|
|
// Insert a new empty input for the image
|
|
document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", "", &[*node_id]);
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER) && inputs_count == 15 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), network_path);
|
|
for (i, input) in old_inputs.iter().enumerate() {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, i + 1), input.clone(), network_path);
|
|
}
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::repeat::repeat_on_points::IDENTIFIER) && inputs_count == 2 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::brush::brush::brush::IDENTIFIER) && inputs_count == 4 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
// We have removed the second input ("bounds"), so we don't add index 1 and we shift the rest of the inputs down by one
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path);
|
|
}
|
|
|
|
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();
|
|
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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::F64(0.), false), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path);
|
|
}
|
|
|
|
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();
|
|
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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::merge_by_distance::IDENTIFIER) && inputs_count == 2 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 2),
|
|
NodeInput::value(TaggedValue::MergeByDistanceAlgorithm(graphene_std::vector::misc::MergeByDistanceAlgorithm::Topological), false),
|
|
network_path,
|
|
);
|
|
}
|
|
|
|
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::SpatialMergeByDistanceNode")) {
|
|
let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::merge_by_distance::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)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(
|
|
&InputConnector::node(*node_id, 2),
|
|
NodeInput::value(TaggedValue::MergeByDistanceAlgorithm(graphene_std::vector::misc::MergeByDistanceAlgorithm::Spatial), false),
|
|
network_path,
|
|
);
|
|
}
|
|
|
|
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);
|
|
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
let new_spacing_value = NodeInput::value(TaggedValue::PointSpacingType(graphene_std::vector::misc::PointSpacingType::Separation), false);
|
|
let new_quantity_value = NodeInput::value(TaggedValue::U32(100), false);
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), new_spacing_value, network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[4].clone(), network_path);
|
|
}
|
|
|
|
// Make the "Quantity" parameter a u32 instead of f64
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::sample_polyline::IDENTIFIER) {
|
|
// Get the inputs, obtain the quantity value, and put the inputs back
|
|
let quantity_value = document
|
|
.network_interface
|
|
.input_from_connector(&InputConnector::Node { node_id: *node_id, input_index: 3 }, network_path)?;
|
|
|
|
if let NodeInput::Value { tagged_value, exposed } = quantity_value
|
|
&& let TaggedValue::F64(value) = **tagged_value
|
|
{
|
|
let new_quantity_value = NodeInput::value(TaggedValue::U32(value as u32), *exposed);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path);
|
|
}
|
|
}
|
|
|
|
// Make the "Grid" node, if its input of index 3 is a DVec2 for "angles" instead of a u32 for the "columns" input that now succeeds "angles", move the angle to index 5 (after "columns" and "rows")
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::grid::IDENTIFIER) && inputs_count == 6 {
|
|
let node_definition = resolve_document_node_type(&reference)?;
|
|
let mut new_node_template = node_definition.default_node_template();
|
|
|
|
let mut current_node_template = document.network_interface.create_node_template(node_id, network_path)?;
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut new_node_template)?;
|
|
let index_3_value = old_inputs.get(3).cloned();
|
|
|
|
let mut upgraded = false;
|
|
|
|
if let Some(NodeInput::Value { tagged_value, exposed: _ }) = index_3_value
|
|
&& matches!(*tagged_value, TaggedValue::DVec2(_))
|
|
{
|
|
// Move index 3 to the end
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
|
|
|
|
upgraded = true;
|
|
}
|
|
|
|
if !upgraded {
|
|
let _ = document.network_interface.replace_inputs(node_id, network_path, &mut current_node_template);
|
|
}
|
|
}
|
|
|
|
// Add the "Depth" parameter to the "Read Index" node
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::context::read_index::IDENTIFIER) && inputs_count == 0 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
|
document.network_interface.set_display_name(node_id, "Read Index".to_string(), network_path);
|
|
|
|
let mut node_path = network_path.to_vec();
|
|
node_path.push(*node_id);
|
|
|
|
document.network_interface.add_import(TaggedValue::None, false, 0, "Primary", "", &node_path);
|
|
document.network_interface.add_import(TaggedValue::U32(0), false, 1, "Loop Level", "TODO", &node_path);
|
|
}
|
|
|
|
// Migrate the Transform node to use degrees instead of radians
|
|
if reference == DefinitionIdentifier::Network("Transform".into()) && node.inputs.get(6).is_none() {
|
|
let mut node_template = resolve_network_node_type("Transform")?.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)?;
|
|
|
|
// Value
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
// Translation
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
// Rotation
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
// Scale
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path);
|
|
// Skew
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[4].clone(), network_path);
|
|
// Origin Offset
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 5), NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false), network_path);
|
|
// Scale Appearance
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
|
|
|
// Migrate rotation from radians to degrees
|
|
match node.inputs.get(2)? {
|
|
NodeInput::Value { tagged_value, exposed } => {
|
|
// Read the existing Properties panel number value, which used to be in radians
|
|
let TaggedValue::F64(radians) = *tagged_value.clone().into_inner() else { return None };
|
|
|
|
// Convert the radians to degrees and set it back as the new input value
|
|
let degrees = NodeInput::value(TaggedValue::F64(radians.to_degrees()), *exposed);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), degrees, network_path);
|
|
}
|
|
NodeInput::Node { .. } => {
|
|
// Construct a new Multiply node for converting from degrees to radians
|
|
let Some(multiply_node) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::multiply::IDENTIFIER)) else {
|
|
log::error!("Could not get multiply node from definition when upgrading transform");
|
|
return None;
|
|
};
|
|
let mut multiply_template = multiply_node.default_node_template();
|
|
multiply_template.document_node.inputs[1] = NodeInput::value(TaggedValue::F64(180. / PI), false);
|
|
|
|
// Decide on the placement position of the new Multiply node
|
|
let multiply_node_id = NodeId::new();
|
|
let Some(transform_position) = document.network_interface.position_from_downstream_node(node_id, network_path) else {
|
|
log::error!("Could not get positon for transform node {node_id}");
|
|
return None;
|
|
};
|
|
let multiply_position = transform_position + IVec2::new(-7, 1);
|
|
|
|
// Insert the new Multiply node into the network directly before it's used
|
|
document.network_interface.insert_node(multiply_node_id, multiply_template, network_path);
|
|
document.network_interface.shift_absolute_node_position(&multiply_node_id, multiply_position, network_path);
|
|
document.network_interface.insert_node_between(&multiply_node_id, &InputConnector::node(*node_id, 2), 0, network_path);
|
|
}
|
|
_ => {}
|
|
};
|
|
|
|
// Migrate skew from radians to degrees
|
|
if let NodeInput::Value { tagged_value, exposed } = node.inputs.get(4)? {
|
|
// Read the existing Properties panel number value, which used to be in radians
|
|
let TaggedValue::DVec2(old_value) = *tagged_value.clone().into_inner() else { return None };
|
|
|
|
// The previous value stored the tangent of the displayed degrees. Now it stores the degrees, so take the arctan of it and convert to degrees.
|
|
let new_value = DVec2::new(old_value.x.atan().to_degrees(), old_value.y.atan().to_degrees());
|
|
let new_input = NodeInput::value(TaggedValue::DVec2(new_value), *exposed);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), new_input, network_path);
|
|
}
|
|
}
|
|
|
|
// Upgrade the "Animation" node to add the "Rate" input
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::animation::animation_time::IDENTIFIER) && inputs_count < 2 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
|
let _ = document.network_interface.replace_inputs(node_id, network_path, &mut node_template);
|
|
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::F64(1.), false), network_path);
|
|
}
|
|
|
|
// Upgrade the "Read Position" node to add the "Loop Level" input
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::context::read_position::IDENTIFIER) && inputs_count < 2 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
|
let _ = document.network_interface.replace_inputs(node_id, network_path, &mut node_template);
|
|
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::U32(0), false), network_path);
|
|
}
|
|
|
|
// Migrate from the old source/target v1 "Morph" node to the new vector table based v2 "Morph" node.
|
|
// This doesn't produce exactly equivalent results in cases involving input vector tables with multiple rows.
|
|
// The old version would zip the source and target table rows, interpoleating each pair together.
|
|
// The migrated version will instead deeply flatten both merged tables and morph sequentially between all source vectors and all target vector elements.
|
|
// This migration assumes most usages didn't involve multiple parallel vector elements, and instead morphed from a single source to a single target vector element.
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER) && (inputs_count == 3 || inputs_count == 4) {
|
|
// 3 inputs - old signature (#3405):
|
|
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction) -> Table<Vector> { ... }
|
|
//
|
|
// 4 inputs - even older signature (commit 80b8df8d4298b6669f124b929ce61bfabfc44e41):
|
|
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction, start_index: u32) -> Table<Vector> { ... }
|
|
//
|
|
// v2 signature:
|
|
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: I, progression: Progression) -> Table<Vector> { ... }
|
|
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
// Create a new Merge node
|
|
let Some(merge_node_type) = resolve_network_node_type("Merge") else {
|
|
log::error!("Could not get merge node from definition when upgrading morph");
|
|
return None;
|
|
};
|
|
let merge_template = merge_node_type.default_node_template();
|
|
let merge_node_id = NodeId::new();
|
|
|
|
// Decide on the placement position of the new Merge node
|
|
let Some(morph_position) = document.network_interface.position_from_downstream_node(node_id, network_path) else {
|
|
log::error!("Could not get position for morph node {node_id}");
|
|
return None;
|
|
};
|
|
let merge_position = morph_position + IVec2::new(-7, 0);
|
|
|
|
// Insert the new Merge node into the network
|
|
document.network_interface.insert_node(merge_node_id, merge_template, network_path);
|
|
document.network_interface.set_to_node_or_layer(&merge_node_id, network_path, false);
|
|
document.network_interface.shift_absolute_node_position(&merge_node_id, merge_position, network_path);
|
|
|
|
// Connect the old 'source' and 'target' inputs to the new Merge node
|
|
document.network_interface.set_input(&InputConnector::node(merge_node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(merge_node_id, 1), old_inputs[1].clone(), network_path);
|
|
|
|
// Connect the new Merge node to the 'content' input of the Morph node
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 0), NodeInput::node(merge_node_id, 0), network_path);
|
|
// Connect the old 'progression' input to the new 'progression' input of the Morph node
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[2].clone(), network_path);
|
|
|
|
inputs_count = 2;
|
|
}
|
|
|
|
// 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 Count Elements → 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();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
// Reconnect content (input 0) and leave path (input 4) as default
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
|
|
let Some(morph_position) = document.network_interface.position_from_downstream_node(node_id, network_path) else {
|
|
log::error!("Could not get position for morph node {node_id}");
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
return None;
|
|
};
|
|
|
|
// Create Count Elements node: counts content table rows → N
|
|
let Some(count_elements_def) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::count_elements::IDENTIFIER)) else {
|
|
log::error!("Could not get count_elements node from definition when upgrading morph");
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
return None;
|
|
};
|
|
let count_elements_template = count_elements_def.default_node_template();
|
|
let count_elements_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 {
|
|
log::error!("Could not get subtract node from definition when upgrading morph");
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
return None;
|
|
};
|
|
let mut subtract_template = subtract_def.default_node_template();
|
|
subtract_template.document_node.inputs[1] = NodeInput::value(TaggedValue::F64(1.), false);
|
|
let subtract_id = NodeId::new();
|
|
|
|
// Create Divide node: old_progression / (N-1) → new progression
|
|
let Some(divide_def) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::divide::IDENTIFIER)) else {
|
|
log::error!("Could not get divide node from definition when upgrading morph");
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
return None;
|
|
};
|
|
let divide_template = divide_def.default_node_template();
|
|
let divide_id = NodeId::new();
|
|
|
|
// Insert and position nodes
|
|
document.network_interface.insert_node(count_elements_id, count_elements_template, network_path);
|
|
document
|
|
.network_interface
|
|
.shift_absolute_node_position(&count_elements_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);
|
|
|
|
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 → Count Elements input 0
|
|
document.network_interface.set_input(&InputConnector::node(count_elements_id, 0), old_inputs[0].clone(), network_path);
|
|
|
|
// Wire: Count Elements output → Subtract input 0 (minuend)
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(subtract_id, 0), NodeInput::node(count_elements_id, 0), network_path);
|
|
|
|
// Wire: old progression → Divide input 0 (numerator)
|
|
document.network_interface.set_input(&InputConnector::node(divide_id, 0), old_inputs[1].clone(), network_path);
|
|
|
|
// Wire: Subtract output → Divide input 1 (denominator)
|
|
document.network_interface.set_input(&InputConnector::node(divide_id, 1), NodeInput::node(subtract_id, 0), network_path);
|
|
|
|
// Wire: Divide output → Morph progression input
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), NodeInput::node(divide_id, 0), network_path);
|
|
}
|
|
|
|
// Migrate old Arrow node from (start, end, shaft_width, head_width, head_length) to (arrow_to, shaft_width, head_width, head_length) with a Transform node for positioning
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::arrow::IDENTIFIER) && inputs_count == 6 {
|
|
// Read old start and end values
|
|
let start = match node.inputs.get(1)? {
|
|
NodeInput::Value { tagged_value, .. } => {
|
|
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
|
|
v
|
|
} else {
|
|
DVec2::ZERO
|
|
}
|
|
}
|
|
_ => DVec2::ZERO,
|
|
};
|
|
let end = match node.inputs.get(2)? {
|
|
NodeInput::Value { tagged_value, .. } => {
|
|
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
|
|
v
|
|
} else {
|
|
DVec2::new(100., 0.)
|
|
}
|
|
}
|
|
_ => DVec2::new(100., 0.),
|
|
};
|
|
|
|
// Replace inputs with the new node definition (primary + arrow_to + shaft_width + head_width + head_length)
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
// Preserve primary input connection
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
// Set arrow_to = end - start
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(end - start), false), network_path);
|
|
// Preserve shaft_width, head_width, head_length (shifted from indices 3,4,5 to 2,3,4)
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
|
|
|
|
// Find downstream connection to insert Transform node
|
|
let downstream = document
|
|
.network_interface
|
|
.outward_wires(network_path)
|
|
.and_then(|wires| wires.get(&OutputConnector::node(*node_id, 0)))
|
|
.and_then(|connections| connections.first().cloned());
|
|
|
|
if let Some(downstream_input) = downstream {
|
|
// Create a Transform node with translation = start
|
|
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
|
|
log::error!("Transform node definition not found during Arrow migration");
|
|
return None;
|
|
};
|
|
let mut transform_template = transform_node_type.default_node_template();
|
|
transform_template.document_node.inputs[1] = NodeInput::value(TaggedValue::DVec2(start), false);
|
|
|
|
let transform_id = NodeId::new();
|
|
|
|
// Position the Transform node to the right of the Arrow node
|
|
let arrow_position = document.network_interface.position(node_id, network_path).unwrap_or_default();
|
|
document.network_interface.insert_node(transform_id, transform_template, network_path);
|
|
document.network_interface.shift_absolute_node_position(&transform_id, arrow_position + IVec2::new(7, 0), network_path);
|
|
document.network_interface.insert_node_between(&transform_id, &downstream_input, 0, network_path);
|
|
}
|
|
}
|
|
|
|
// Migrate old Line node from (start, end) to (line_to) with a Transform node for positioning
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER) && inputs_count == 3 {
|
|
// Read old start and end values
|
|
let start = match node.inputs.get(1)? {
|
|
NodeInput::Value { tagged_value, .. } => {
|
|
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
|
|
v
|
|
} else {
|
|
DVec2::ZERO
|
|
}
|
|
}
|
|
_ => DVec2::ZERO,
|
|
};
|
|
let end = match node.inputs.get(2)? {
|
|
NodeInput::Value { tagged_value, .. } => {
|
|
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
|
|
v
|
|
} else {
|
|
DVec2::new(100., 100.)
|
|
}
|
|
}
|
|
_ => DVec2::new(100., 100.),
|
|
};
|
|
|
|
// Replace inputs with the new node definition (primary + line_to)
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
// Preserve primary input connection
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
// Set line_to = end - start
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(end - start), false), network_path);
|
|
|
|
// Find downstream connection to insert Transform node
|
|
let downstream = document
|
|
.network_interface
|
|
.outward_wires(network_path)
|
|
.and_then(|wires| wires.get(&OutputConnector::node(*node_id, 0)))
|
|
.and_then(|connections| connections.first().cloned());
|
|
|
|
if let Some(downstream_input) = downstream {
|
|
// Create a Transform node with translation = start
|
|
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
|
|
log::error!("Transform node definition not found during Line migration");
|
|
return None;
|
|
};
|
|
let mut transform_template = transform_node_type.default_node_template();
|
|
transform_template.document_node.inputs[1] = NodeInput::value(TaggedValue::DVec2(start), false);
|
|
|
|
let transform_id = NodeId::new();
|
|
|
|
// Position the Transform node to the right of the Line node
|
|
let line_position = document.network_interface.position(node_id, network_path).unwrap_or_default();
|
|
document.network_interface.insert_node(transform_id, transform_template, network_path);
|
|
document.network_interface.shift_absolute_node_position(&transform_id, line_position + IVec2::new(7, 0), network_path);
|
|
document.network_interface.insert_node_between(&transform_id, &downstream_input, 0, network_path);
|
|
}
|
|
}
|
|
|
|
// Add context features to nodes that don't have them (fine-grained context caching migration)
|
|
if node.context_features == graphene_std::ContextDependencies::default()
|
|
&& let Some(reference) = document.network_interface.reference(node_id, network_path).clone()
|
|
&& let Some(node_definition) = resolve_document_node_type(&reference)
|
|
{
|
|
let context_features = node_definition.node_template.document_node.context_features;
|
|
document.network_interface.set_context_features(node_id, network_path, context_features);
|
|
}
|
|
|
|
// Add the "Scale Type" parameter to the "Decompose Scale" node
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::decompose_scale::IDENTIFIER) && inputs_count == 1 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::ScaleType(ScaleType::Magnitude), false), network_path);
|
|
}
|
|
|
|
// Add the "Along Normals" parameter to the "Jitter Points" node
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::jitter_points::IDENTIFIER) && inputs_count == 3 {
|
|
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
|
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Bool(false), false), network_path);
|
|
}
|
|
|
|
// Migrate Path nodes that stored geometry directly in input 0 (as a Table<Vector>) to instead use a VectorModification in input 1
|
|
if reference == DefinitionIdentifier::Network("Path".into()) {
|
|
let input_0 = node.inputs.first()?;
|
|
if let NodeInput::Value { tagged_value, exposed } = input_0
|
|
&& !exposed
|
|
&& let TaggedValue::Vector(vector_table) = &**tagged_value
|
|
&& !vector_table.is_empty()
|
|
{
|
|
let vector = vector_table.element(0)?;
|
|
let modification = Box::new(graphene_std::vector::VectorModification::create_from_vector(vector));
|
|
|
|
// Reset input 0 to the default exposed state
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::Vector(Default::default()), true), network_path);
|
|
|
|
// Store the converted VectorModification in input 1
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::VectorModification(modification), false), network_path);
|
|
}
|
|
}
|
|
|
|
// Migrate Image nodes that stored a Table<Raster<CPU>> in input 1 to instead use bare Image<Color> via TaggedValue::ImageData
|
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER)
|
|
&& let Some(NodeInput::Value { tagged_value, .. }) = node.inputs.get(1)
|
|
&& let TaggedValue::Raster(raster_table) = &**tagged_value
|
|
&& let Some(element) = raster_table.element(0)
|
|
{
|
|
let image = element.data().clone();
|
|
|
|
document
|
|
.network_interface
|
|
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::ImageData(image), false), network_path);
|
|
}
|
|
|
|
// ==================================
|
|
// PUT ALL MIGRATIONS ABOVE THIS LINE
|
|
// ==================================
|
|
|
|
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
|
|
document.network_interface.load_structure();
|
|
let all_layers = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
|
|
for layer in all_layers {
|
|
let (downstream_node, input_index) = document
|
|
.network_interface
|
|
.outward_wires(&[])
|
|
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(layer.to_node(), 0)))
|
|
.and_then(|outward_wires| outward_wires.first())
|
|
.and_then(|input_connector| input_connector.node_id().map(|node_id| (node_id, input_connector.input_index())))?;
|
|
// If the downstream node is a layer and the input is the first input and the current layer is not in a stack
|
|
if input_index == 0 && document.network_interface.is_layer(&downstream_node, &[]) && !document.network_interface.is_stack(&layer.to_node(), &[]) {
|
|
// Ensure the layer is horizontally aligned with the downstream layer to prevent changing the layout of old files
|
|
let (Some(layer_position), Some(downstream_position)) = (document.network_interface.position(&layer.to_node(), &[]), document.network_interface.position(&downstream_node, &[])) else {
|
|
log::error!("Could not get position for layer {:?} or downstream node {} when opening file", layer.to_node(), downstream_node);
|
|
return None;
|
|
};
|
|
if layer_position.x == downstream_position.x {
|
|
document.network_interface.set_stack_position_calculated_offset(&layer.to_node(), &downstream_node, &[]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Some(())
|
|
}
|
|
|
|
/// Migrates document nodes whose catalog definitions have been removed.
|
|
///
|
|
/// This is called from `migrate_node` BEFORE its standard reset/migration logic, since that logic aborts when it can't
|
|
/// resolve the node's `reference` against the current catalog. Each block here detects a specific decommissioned
|
|
/// definition by its old reference name, swaps it to a still-supported implementation, and preserves the user's inputs.
|
|
/// After this runs, the node's reference resolves cleanly so the rest of `migrate_node` proceeds normally.
|
|
fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler) -> Option<()> {
|
|
// Collapse the legacy "Sample Polyline" wrapper network into the standalone `sample_polyline` proto node.
|
|
// The proto node now computes per-bezpath segment lengths inline, so the wrapper's separate `subpath_segment_lengths`
|
|
// and `Memo` nodes are no longer needed. The 7 user-facing inputs are positionally identical between the
|
|
// old wrapper and the new proto node.
|
|
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
|
&& name == "Sample Polyline"
|
|
&& node.inputs.len() == 7
|
|
{
|
|
let mut node_template = resolve_proto_node_type(graphene_std::vector::sample_polyline::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)?;
|
|
for (index, input) in old_inputs.iter().take(7).enumerate() {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
|
}
|
|
}
|
|
|
|
// Collapse the legacy "Scatter Points" wrapper network into the standalone `scatter_points` proto node.
|
|
// The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the
|
|
// proto node, so the wrapper itself is redundant. The 3 user-facing inputs are positionally identical
|
|
// between the old wrapper and the new proto node.
|
|
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
|
&& name == "Scatter Points"
|
|
&& node.inputs.len() == 3
|
|
{
|
|
let mut node_template = resolve_proto_node_type(graphene_std::vector::scatter_points::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)?;
|
|
for (index, input) in old_inputs.iter().take(3).enumerate() {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
|
}
|
|
}
|
|
|
|
// Collapse the legacy "Boolean Operation" wrapper network into the standalone `boolean_operation` proto node.
|
|
// The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the
|
|
// proto node, so the wrapper itself is redundant. The 2 user-facing inputs are positionally identical
|
|
// between the old wrapper and the new proto node.
|
|
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
|
&& name == "Boolean Operation"
|
|
&& node.inputs.len() == 2
|
|
{
|
|
let mut node_template = resolve_proto_node_type(graphene_std::path_bool_nodes::boolean_operation::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)?;
|
|
for (index, input) in old_inputs.iter().take(2).enumerate() {
|
|
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
|
}
|
|
}
|
|
|
|
Some(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_no_duplicate_node_replacements() {
|
|
let mut hashmap = HashMap::<ProtoNodeIdentifier, u32>::new();
|
|
NODE_REPLACEMENTS.iter().for_each(|node| {
|
|
*hashmap.entry(node.node.clone()).or_default() += 1;
|
|
});
|
|
let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| node.as_str()).collect::<Vec<_>>();
|
|
if !duplicates.is_empty() {
|
|
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {duplicates:?}");
|
|
}
|
|
}
|
|
}
|