mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
Node graph improvements (#855)
* Selecting multiple nodes * Improve logs * Log bad types in dyn any * Add (broken) node links * New topological sort * Fix reorder ids function * Input and output node * Add nodes that operate on images * Fixups * Show node parameters together with layer properties * New nodes don't crash editor * Fix tests * Node positions backend * Generate node graph on value change * Add expose input message * Fix tests Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -28,12 +28,19 @@ fn merge_ids(a: u64, b: u64) -> u64 {
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct DocumentNodeMetadata {
|
||||
pub position: (i32, i32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct DocumentNode {
|
||||
pub name: String,
|
||||
pub inputs: Vec<NodeInput>,
|
||||
pub implementation: DocumentNodeImplementation,
|
||||
pub metadata: DocumentNodeMetadata,
|
||||
}
|
||||
|
||||
impl DocumentNode {
|
||||
@@ -54,7 +61,7 @@ impl DocumentNode {
|
||||
let first = self.inputs.remove(0);
|
||||
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
|
||||
let (input, mut args) = match first {
|
||||
NodeInput::Value(tagged_value) => {
|
||||
NodeInput::Value { tagged_value, .. } => {
|
||||
assert_eq!(self.inputs.len(), 0);
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value.to_value()))
|
||||
}
|
||||
@@ -63,7 +70,7 @@ impl DocumentNode {
|
||||
};
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recieved non resolved parameter");
|
||||
assert!(
|
||||
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))),
|
||||
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value { .. })),
|
||||
"recieved value as parameter. inupts: {:#?}, construction_args: {:#?}",
|
||||
&self.inputs,
|
||||
&args
|
||||
@@ -90,7 +97,7 @@ impl DocumentNode {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum NodeInput {
|
||||
Node(NodeId),
|
||||
Value(value::TaggedValue),
|
||||
Value { tagged_value: value::TaggedValue, exposed: bool },
|
||||
Network,
|
||||
}
|
||||
|
||||
@@ -100,13 +107,20 @@ impl NodeInput {
|
||||
*self = NodeInput::Node(f(*id))
|
||||
}
|
||||
}
|
||||
pub fn is_exposed(&self) -> bool {
|
||||
if let NodeInput::Value { exposed, .. } = self {
|
||||
*exposed
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NodeInput {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (&self, &other) {
|
||||
(Self::Node(n1), Self::Node(n2)) => n1 == n2,
|
||||
(Self::Value(v1), Self::Value(v2)) => v1 == v2,
|
||||
(Self::Value { tagged_value: v1, .. }, Self::Value { tagged_value: v2, .. }) => v1 == v2,
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
}
|
||||
}
|
||||
@@ -169,13 +183,14 @@ impl NodeNetwork {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(node, *offset);
|
||||
}
|
||||
NodeInput::Value(value) => {
|
||||
let name = format!("Value: {:?}", value.clone().to_value());
|
||||
NodeInput::Value { tagged_value, exposed } => {
|
||||
let name = format!("Value: {:?}", tagged_value.clone().to_value());
|
||||
let new_id = map_ids(id, gen_id());
|
||||
let value_node = DocumentNode {
|
||||
name: name.clone(),
|
||||
inputs: vec![NodeInput::Value(value)],
|
||||
inputs: vec![NodeInput::Value { tagged_value, exposed }],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
};
|
||||
assert!(!self.nodes.contains_key(&new_id));
|
||||
self.nodes.insert(new_id, value_node);
|
||||
@@ -235,17 +250,19 @@ mod test {
|
||||
(
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "cons".into(),
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -265,17 +282,19 @@ mod test {
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "cons".into(),
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
2,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(1)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -294,8 +313,15 @@ mod test {
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Value(value::TaggedValue::U32(2))],
|
||||
inputs: vec![
|
||||
NodeInput::Network,
|
||||
NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
},
|
||||
],
|
||||
implementation: DocumentNodeImplementation::Network(add_network()),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
@@ -312,9 +338,10 @@ mod test {
|
||||
#[test]
|
||||
fn resolve_proto_node_add() {
|
||||
let document_node = DocumentNode {
|
||||
name: "cons".into(),
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
};
|
||||
|
||||
let proto_node = document_node.resolve_proto_node();
|
||||
@@ -380,30 +407,37 @@ mod test {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![NodeInput::Node(11)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
DocumentNode {
|
||||
name: "cons".into(),
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(14)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
14,
|
||||
DocumentNode {
|
||||
name: "Value: 2".into(),
|
||||
inputs: vec![NodeInput::Value(value::TaggedValue::U32(2))],
|
||||
inputs: vec![NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
}],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(10)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user