using System; using System.Collections.Generic; using System.Linq; namespace UnityEngine.Graphing { [Serializable] public class SerializableGraph : IGraph, ISerializationCallbackReceiver { [NonSerialized] List m_Edges = new List(); [NonSerialized] Dictionary m_Nodes = new Dictionary(); [SerializeField] List m_SerializableNodes = new List(); [SerializeField] List m_SerializableEdges = new List(); public IEnumerable GetNodes() where T : INode { return m_Nodes.Values.OfType(); } public IEnumerable edges { get { return m_Edges; } } public virtual void AddNode(INode node) { m_Nodes.Add(node.guid, node); node.owner = this; ValidateGraph(); } public virtual void RemoveNode(INode node) { if (!node.canDeleteNode) return; m_Nodes.Remove(node.guid); ValidateGraph(); } void RemoveNodeNoValidate(INode node) { if (!node.canDeleteNode) return; m_Nodes.Remove(node.guid); } public virtual Dictionary GetLegacyTypeRemapping() { return new Dictionary(); } public virtual IEdge Connect(SlotReference fromSlotRef, SlotReference toSlotRef) { if (fromSlotRef == null || toSlotRef == null) return null; var fromNode = GetNodeFromGuid(fromSlotRef.nodeGuid); var toNode = GetNodeFromGuid(toSlotRef.nodeGuid); if (fromNode == null || toNode == null) return null; // if fromNode is already connected to toNode // do now allow a connection as toNode will then // have an edge to fromNode creating a cycle. // if this is parsed it will lead to an infinite loop. var dependentNodes = new List(); NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, toNode); if (dependentNodes.Contains(fromNode)) return null; var fromSlot = fromNode.FindSlot(fromSlotRef.slotId); var toSlot = toNode.FindSlot(toSlotRef.slotId); SlotReference outputSlot = null; SlotReference inputSlot = null; // output must connect to input if (fromSlot.isOutputSlot) outputSlot = fromSlotRef; else if (fromSlot.isInputSlot) inputSlot = fromSlotRef; if (toSlot.isOutputSlot) outputSlot = toSlotRef; else if (toSlot.isInputSlot) inputSlot = toSlotRef; if (inputSlot == null || outputSlot == null) return null; var slotEdges = GetEdges(inputSlot).ToList(); // remove any inputs that exits before adding foreach (var edge in slotEdges) { RemoveEdgeNoValidate(edge); } var newEdge = new Edge(outputSlot, inputSlot); m_Edges.Add(newEdge); Debug.Log("Connected edge: " + newEdge); ValidateGraph(); return newEdge; } public virtual void RemoveEdge(IEdge e) { m_Edges.Remove(e); ValidateGraph(); } public void RemoveElements(IEnumerable nodes, IEnumerable edges) { foreach (var edge in edges.ToArray()) RemoveEdgeNoValidate(edge); foreach (var serializableNode in nodes.ToArray()) RemoveNodeNoValidate(serializableNode); ValidateGraph(); } void RemoveEdgeNoValidate(IEdge e) { m_Edges.Remove(e); } public INode GetNodeFromGuid(Guid guid) { INode node; m_Nodes.TryGetValue(guid, out node); return node; } public bool ContainsNodeGuid(Guid guid) { return m_Nodes.ContainsKey(guid); } public T GetNodeFromGuid(Guid guid) where T : INode { var node = GetNodeFromGuid(guid); if (node is T) return (T)node; return default(T); } public IEnumerable GetEdges(SlotReference s) { if (s == null) return new Edge[0]; return m_Edges.Where(x => (x.outputSlot.nodeGuid == s.nodeGuid && x.outputSlot.slotId == s.slotId) || x.inputSlot.nodeGuid == s.nodeGuid && x.inputSlot.slotId == s.slotId).ToList(); } public virtual void OnBeforeSerialize() { m_SerializableNodes = SerializationHelper.Serialize(m_Nodes.Values); m_SerializableEdges = SerializationHelper.Serialize(m_Edges); } public virtual void OnAfterDeserialize() { var nodes = SerializationHelper.Deserialize(m_SerializableNodes, GetLegacyTypeRemapping()); m_Nodes = new Dictionary(nodes.Count); foreach (var node in nodes) { node.owner = this; node.UpdateNodeAfterDeserialization(); m_Nodes.Add(node.guid, node); } m_SerializableNodes = null; m_Edges = SerializationHelper.Deserialize(m_SerializableEdges, null); m_SerializableEdges = null; ValidateGraph(); } public virtual void ValidateGraph() { //First validate edges, remove any //orphans. This can happen if a user //manually modifies serialized data //of if they delete a node in the inspector //debug view. foreach (var edge in edges.ToArray()) { var outputNode = GetNodeFromGuid(edge.outputSlot.nodeGuid); var inputNode = GetNodeFromGuid(edge.inputSlot.nodeGuid); if (outputNode == null || inputNode == null || outputNode.FindOutputSlot(edge.outputSlot.slotId) == null || inputNode.FindInputSlot(edge.inputSlot.slotId) == null) { //orphaned edge RemoveEdgeNoValidate(edge); } } foreach (var node in GetNodes()) node.ValidateNode(); } public void OnEnable() { foreach (var node in GetNodes().OfType()) { node.OnEnable(); } } } }