Move all network metadata to node network

This commit is contained in:
Adam
2024-09-06 16:27:22 -07:00
parent 73a30368c0
commit de31dbfb19
5 changed files with 424 additions and 322 deletions

View File

@@ -1,8 +1,10 @@
use crate::document::value::TaggedValue;
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
use bezier_rs::Subpath;
use dyn_any::{DynAny, StaticType};
use graphene_core::memo::MemoHashGuard;
use graphene_core::renderer::ClickTarget;
pub use graphene_core::uuid::generate_uuid;
use graphene_core::{Cow, MemoHash, ProtoNodeIdentifier, Type};
@@ -73,7 +75,7 @@ pub enum OldNodeInput {
}
// TODO: Eventually remove this (probably starting late 2024)
use serde::Deserialize;
use serde::{Deserialize, Serialize};
fn deserialize_inputs<'de, D>(deserializer: D) -> Result<Vec<NodeInput>, D::Error>
where
D: serde::Deserializer<'de>,
@@ -1381,7 +1383,7 @@ impl RootNode {
}
/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, Hash, DynAny)]
pub enum InputConnector {
#[serde(rename = "node")]
Node {
@@ -1421,7 +1423,7 @@ impl InputConnector {
}
/// Represents an output connector
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, DynAny)]
pub enum OutputConnector {
#[serde(rename = "node")]
Node {
@@ -1484,6 +1486,150 @@ impl PTZ {
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Hash, DynAny)]
pub enum LayerOwner {
// Used to get the layer that should be shifted when there is a collision.
Layer(NodeId),
// The vertical offset of a node from the start of its shift. Should be reset when the drag ends.
None(i32),
}
#[derive(Debug, Clone, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub struct Ports {
#[serde(skip)]
input_ports: Vec<(usize, ClickTarget)>,
#[serde(skip)]
output_ports: Vec<(usize, ClickTarget)>,
}
impl Default for Ports {
fn default() -> Self {
Self::new()
}
}
impl Ports {
pub fn new() -> Ports {
Ports {
input_ports: Vec::new(),
output_ports: Vec::new(),
}
}
pub fn input_ports(&self) -> impl Iterator<Item = (usize, &ClickTarget)> {
self.input_ports.iter().map(|(index, click_target)| (*index, click_target))
}
pub fn output_ports(&self) -> impl Iterator<Item = (usize, &ClickTarget)> {
self.output_ports.iter().map(|(index, click_target)| (*index, click_target))
}
pub fn click_targets(&self) -> impl Iterator<Item = &ClickTarget> {
self.input_ports
.iter()
.map(|(_, click_target)| click_target)
.chain(self.output_ports.iter().map(|(_, click_target)| click_target))
}
pub fn insert_input_port_at_center(&mut self, input_index: usize, center: DVec2) {
let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.));
self.input_ports.push((input_index, ClickTarget::new(subpath, 0.)));
}
pub fn insert_output_port_at_center(&mut self, output_index: usize, center: DVec2) {
let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.));
self.output_ports.push((output_index, ClickTarget::new(subpath, 0.)));
}
pub fn insert_node_input(&mut self, input_index: usize, row_index: usize, node_top_left: DVec2) {
// The center of the click target is always 24 px down from the top left corner of the node
let center = node_top_left + DVec2::new(0., 24. + 24. * row_index as f64);
self.insert_input_port_at_center(input_index, center);
}
pub fn insert_node_output(&mut self, output_index: usize, row_index: usize, node_top_left: DVec2) {
// The center of the click target is always 24 px down from the top left corner of the node
let center = node_top_left + DVec2::new(5. * 24., 24. + 24. * row_index as f64);
self.insert_output_port_at_center(output_index, center);
}
pub fn insert_layer_input(&mut self, input_index: usize, node_top_left: DVec2) {
let center = if input_index == 0 {
node_top_left + DVec2::new(2. * 24., 24. * 2. + 8.)
} else {
node_top_left + DVec2::new(0., 24. * 1.)
};
self.insert_input_port_at_center(input_index, center);
}
pub fn insert_layer_output(&mut self, node_top_left: DVec2) {
// The center of the click target is always 24 px down from the top left corner of the node
let center = node_top_left + DVec2::new(2. * 24., -8.0);
self.insert_output_port_at_center(0, center);
}
pub fn clicked_input_port_from_point(&self, point: DVec2) -> Option<usize> {
self.input_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port))
}
pub fn clicked_output_port_from_point(&self, point: DVec2) -> Option<usize> {
self.output_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port))
}
pub fn input_port_position(&self, index: usize) -> Option<DVec2> {
self.input_ports
.get(index)
.and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.)))
}
pub fn output_port_position(&self, index: usize) -> Option<DVec2> {
self.output_ports
.get(index)
.and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.)))
}
}
/// This is the same as Option, but more clear in the context of having cached metadata either being loaded or unloaded
#[derive(Debug, Default, Clone, PartialEq, serde::Deserialize, Hash)]
pub enum TransientMetadata<T> {
Loaded(T),
#[default]
Unloaded,
}
unsafe impl<T: 'static> StaticType for TransientMetadata<T> {
type Static = TransientMetadata<T>;
}
// Ensure transient metadata is always serialized as Unloaded
impl<T> Serialize for TransientMetadata<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
// Always serialize as Unloaded
serializer.serialize_unit_variant("TransientMetadata", 1, "Unloaded")
}
}
impl<T> TransientMetadata<T> {
/// Set the current transient metadata to unloaded
pub fn unload(&mut self) {
*self = TransientMetadata::Unloaded;
}
pub fn is_loaded(&self) -> bool {
matches!(self, TransientMetadata::Loaded(_))
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, DynAny)]
pub struct NetworkEdgeDistance {
/// The viewport pixel distance distance between the left edge of the node graph and the exports.
pub exports_to_edge_distance: DVec2,
/// The viewport pixel distance between the left edge of the node graph and the imports.
pub imports_to_edge_distance: DVec2,
}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -1,4 +1,4 @@
use super::DocumentNode;
use super::{DocumentNode, TransientMetadata};
use crate::document::NodeId;
pub use crate::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
use crate::proto::{Any as DAny, FutureAny};
@@ -11,7 +11,7 @@ use graphene_core::{Color, MemoHash, Node, Type};
use dyn_any::DynAny;
pub use dyn_any::StaticType;
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
use std::collections::VecDeque;
use std::collections::{HashMap, VecDeque};
use std::fmt::Display;
use std::hash::Hash;
use std::marker::PhantomData;
@@ -179,9 +179,16 @@ tagged_value! {
CentroidType(graphene_core::vector::misc::CentroidType),
BooleanOperation(graphene_core::vector::misc::BooleanOperation),
FontCache(Arc<graphene_core::text::FontCache>),
// Persistent Network Metadata
Previewing(crate::document::Previewing),
PTZ(crate::document::PTZ),
SelectionHistory(VecDeque<Vec<NodeId>>),
// Transient Network Metadata
StackDependents(TransientMetadata<HashMap<NodeId, crate::document::LayerOwner>>),
AllNodesBoundingBox(TransientMetadata<[DVec2; 2]>),
OutwardWires(TransientMetadata<HashMap<crate::document::OutputConnector, Vec<crate::document::InputConnector>>>),
ImportExportPorts(TransientMetadata<crate::document::Ports>),
RoundedNetworkEdgeDistance(TransientMetadata<crate::document::NetworkEdgeDistance>),
}
impl TaggedValue {
@@ -258,6 +265,10 @@ trait FakeHash {
fn hash<H: core::hash::Hasher>(&self, state: &mut H);
}
mod fake_hash {
use std::collections::HashMap;
use crate::document::{InputConnector, OutputConnector, Ports, TransientMetadata};
use super::*;
impl FakeHash for f64 {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
@@ -274,6 +285,22 @@ mod fake_hash {
self.to_cols_array().iter().for_each(|x| x.to_bits().hash(state))
}
}
// impl<K: FakeHash, V: FakeHash> FakeHash for HashMap<K, V> {
// fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
// self.iter().for_each(|(k, v)| {
// k.hash(state);
// v.hash(state);
// })
// }
// }
impl<K: Hash, V: Hash> FakeHash for HashMap<K, V> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.iter().for_each(|(k, v)| {
k.hash(state);
v.hash(state);
})
}
}
impl<X: FakeHash> FakeHash for Option<X> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
if let Some(x) = self {
@@ -284,6 +311,16 @@ mod fake_hash {
}
}
}
impl<X: FakeHash> FakeHash for TransientMetadata<X> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
if let TransientMetadata::Loaded(x) = self {
1.hash(state);
x.hash(state);
} else {
0.hash(state);
}
}
}
impl<X: FakeHash> FakeHash for Vec<X> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.len().hash(state);
@@ -308,4 +345,71 @@ mod fake_hash {
self.zoom.hash(state);
}
}
// impl FakeHash for Option<HashMap<NodeId, crate::document::LayerOwner>> {
// fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
// if let Some(x) = self {
// 1.hash(state);
// x.iter().for_each(|(k, v)| {
// k.hash(state);
// v.hash(state);
// });
// } else {
// 0.hash(state);
// }
// }
// }
// impl FakeHash for HashMap<crate::document::OutputConnector, Vec<crate::document::InputConnector>> {
// fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
// self.iter().for_each(|(k, v)| {
// k.hash(state);
// v.hash(state);
// });
// }
// }
impl FakeHash for OutputConnector {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
OutputConnector::Node { node_id, output_index } => {
0.hash(state);
node_id.hash(state);
output_index.hash(state);
}
OutputConnector::Import(import_index) => {
1.hash(state);
import_index.hash(state);
}
}
}
}
impl FakeHash for InputConnector {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
InputConnector::Node { node_id, input_index } => {
0.hash(state);
node_id.hash(state);
input_index.hash(state);
}
InputConnector::Export(export_index) => {
1.hash(state);
export_index.hash(state);
}
}
}
}
impl FakeHash for crate::document::NetworkEdgeDistance {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.exports_to_edge_distance.hash(state);
self.imports_to_edge_distance.hash(state);
}
}
impl FakeHash for Ports {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.input_ports.iter().chain(self.output_ports.iter()).for_each(|(index, click_target)| {
index.hash(state);
click_target.subpath().hash(state);
click_target.stroke_width().hash(state);
click_target.bounding_box().hash(state)
});
}
}
}