Example #1
0
        /// <summary>
        /// Make sure our list of PortViews and editables sync up with our NodePorts
        /// </summary>
        protected void UpdatePorts()
        {
            foreach (var port in target.ports)
            {
                if (port.isInput)
                {
                    AddInputPort(port);
                }
                else
                {
                    AddOutputPort(port);
                }
            }

            var reflectionData = NodeReflection.GetNodeType(target.GetType());

            if (reflectionData != null)
            {
                foreach (var editable in reflectionData.editables)
                {
                    AddEditableField(m_SerializedNode.FindPropertyRelative(editable.fieldName));
                }
            }

            // Toggle visibility of the extension container
            RefreshExpandedState();

            // Update state classes
            EnableInClassList("hasInputs", inputs.Count > 0);
            EnableInClassList("hasOutputs", outputs.Count > 0);
        }
Example #2
0
        /// <summary>
        /// Create a new node from reflection data and insert into the Graph.
        /// </summary>
        internal void AddNodeFromSearch(Node node, Vector2 screenPosition, PortView connectedPort = null, bool registerUndo = true)
        {
            // Calculate where to place this node on the graph
            var windowRoot          = EditorWindow.rootVisualElement;
            var windowMousePosition = EditorWindow.rootVisualElement.ChangeCoordinatesTo(
                windowRoot.parent,
                screenPosition - EditorWindow.position.position
                );

            var graphMousePosition = contentViewContainer.WorldToLocal(windowMousePosition);

            // Track undo and add to the graph
            if (registerUndo)
            {
                Undo.RegisterCompleteObjectUndo(Graph, $"Add Node {node.Name}");
            }

            node.Position = graphMousePosition;

            Graph.AddNode(node);
            serializedGraph.Update();
            EditorUtility.SetDirty(Graph);

            // Add a node to the visual graph
            var editorType = NodeReflection.GetNodeEditorType(node.GetType());
            var element    = Activator.CreateInstance(editorType) as NodeView;

            element.Initialize(node, this, edgeConnectorListener);

            AddElement(element);

            // If there was a provided existing port to connect to, find the best
            // candidate port on the new node and connect.
            if (connectedPort != null)
            {
                var edge = new GraphViewEdge();

                if (connectedPort.direction == Direction.Input)
                {
                    edge.input  = connectedPort;
                    edge.output = element.GetCompatibleOutputPort(connectedPort);
                }
                else
                {
                    edge.output = connectedPort;
                    edge.input  = element.GetCompatibleInputPort(connectedPort);
                }

                AddEdge(edge, false);
            }

            Dirty(element);
        }
Example #3
0
        public List <SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
        {
            var tree = new List <SearchTreeEntry>();

            // First item is the title of the window
            tree.Add(new SearchTreeGroupEntry(new GUIContent("Add Node"), 0));

            // TODO: Hooks for custom top level pieces (Comments, new variables, etc)

            // Construct a tree of available nodes by module path
            var nodes = NodeReflection.GetNodeTypes();

            var groups = new SearchGroup(1);

            foreach (var node in nodes.Values)
            {
                var path = node.path;

                // Skip the node if it the module isn't whitelisted
                if (!IsInSupportedModule(path))
                {
                    continue;
                }

                // If we're coming from a port, make sure to only add nodes that accept
                // an input (or output) that's compatible.
                if (sourcePort == null || IsCompatibleWithSourcePort(node))
                {
                    var group = groups;
                    if (path != null)
                    {
                        for (int i = 0; i < path.Length; i++)
                        {
                            if (!group.subgroups.ContainsKey(path[i]))
                            {
                                group.subgroups.Add(path[i], new SearchGroup(group.depth + 1));
                            }

                            group = group.subgroups[path[i]];
                        }
                    }

                    group.nodes.Add(node);
                }
            }

            groups.AddToTree(tree);

            return(tree);
        }
Example #4
0
        public void Load(Graph graph)
        {
            Graph           = graph;
            serializedGraph = new SerializedObject(Graph);
            title.text      = graph.Title;
            SetupZoom(graph.ZoomMinScale, graph.ZoomMaxScale);

            AddNodeViews(graph.Nodes);
            AddCommentViews(graph.Comments);

            // Reset the search to a new set of tags and providers
            searchWindow.ClearSearchProviders();
            searchWindow.ClearTags();

            foreach (var provider in NodeReflection.SearchProviders)
            {
                if (provider.IsSupported(graph))
                {
                    searchWindow.AddSearchProvider(provider);
                }
            }

            // TODO: Move into reflection
            var attrs = graph.GetType().GetCustomAttributes(true);


            foreach (var attr in attrs)
            {
                //Add Tags for search provider
                if (attr is IncludeTagsAttribute include)
                {
                    searchWindow.IncludeTags.AddRange(include.Tags);
                }

                //Add Required nodes from GraphAttributes
                if (attr is RequireNodeAttribute required)
                {
                    Node node = graph.GetNode(required.type);
                    if (node == null)
                    {
                        node          = NodeReflection.Instantiate(required.type);
                        node.Graph    = graph;
                        node.Name     = required.nodeName;
                        node.Position = required.position;
                        AddNodeFromSearch(node, node.Position, null, false);
                    }
                }
            }
        }
 public IEnumerable <SearchResult> GetSearchResults(SearchFilter filter)
 {
     foreach (var entry in NodeReflection.GetNodeTypes())
     {
         var node = entry.Value;
         if (
             IsCompatible(filter.SourcePort, node) &&
             IsInSupportedTags(filter.IncludeTags, node.Tags)
             )
         {
             yield return(new SearchResult
             {
                 Name = node.Name,
                 Path = node.Path,
                 UserData = node,
             });
         }
     }
 }
Example #6
0
        public NodeReflectionData(Type type, NodeAttribute nodeAttr)
        {
            Type           = type;
            Name           = nodeAttr.Name ?? ObjectNames.NicifyVariableName(type.Name);
            Path           = nodeAttr.Path?.Split('/');
            Help           = nodeAttr.Help;
            Deletable      = nodeAttr.Deletable;
            Moveable       = nodeAttr.Moveable;
            EditorType     = NodeReflection.GetNodeEditorType(type);
            contextMethods = new Dictionary <ContextMenu, MethodInfo>();

            var attrs = type.GetCustomAttributes(true);

            foreach (var attr in attrs)
            {
                if (attr is TagsAttribute tagAttr)
                {
                    // Load any tags associated with the node
                    Tags.AddRange(tagAttr.Tags);
                }
                else if (attr is OutputAttribute output)
                {
                    // Load any Outputs defined at the class level
                    Ports.Add(new PortReflectionData()
                    {
                        Name              = output.Name,
                        Type              = output.Type,
                        Direction         = PortDirection.Output,
                        Capacity          = output.Multiple ? PortCapacity.Multiple : PortCapacity.Single,
                        HasControlElement = false
                    });
                }
            }

            // Load additional data from class fields
            AddFieldsFromClass(type);
            SetScriptNodeType();
            SetScriptNodeViewType();
            LoadContextMethods();
        }
Example #7
0
        /// <summary>
        /// Append views for a set of nodes
        /// </summary>
        void AddNodeViews(List <AbstractNode> nodes, bool selectOnceAdded = false, bool centerOnMouse = false)
        {
            var serializedNodesArr = m_SerializedGraph.FindProperty("nodes");

            // Add views of each node from the graph
            Dictionary <AbstractNode, NodeView> nodeMap = new Dictionary <AbstractNode, NodeView>();

            // TODO: Could just be a list with index checking.

            for (int i = 0; i < nodes.Count; i++)
            {
                var node     = nodes[i];
                var graphIdx = m_Graph.nodes.IndexOf(node);

                if (graphIdx < 0)
                {
                    Debug.LogError("Cannot add NodeView: Node is not indexed on the graph");
                }
                else
                {
                    var serializedNode = serializedNodesArr.GetArrayElementAtIndex(graphIdx);

                    var editorType = NodeReflection.GetNodeEditorType(node.GetType());
                    var element    = Activator.CreateInstance(editorType) as NodeView;

                    element.Initialize(node, serializedNode, m_EdgeListener);
                    AddElement(element);

                    nodeMap.Add(node, element);
                    Dirty(element);

                    if (selectOnceAdded)
                    {
                        AddToSelection(element);
                    }
                }
            }

            if (centerOnMouse)
            {
                Rect    bounds        = GetBounds(nodeMap.Values);
                Vector2 worldPosition = contentViewContainer.WorldToLocal(m_LastMousePosition);
                Vector2 delta         = worldPosition - bounds.center;

                foreach (var node in nodeMap)
                {
                    node.Value.SetPosition(new Rect(node.Key.graphPosition + delta, Vector2.one));
                }
            }

            // Sync edges on the graph with our graph's connections
            // TODO: Deal with trash connections from bad imports
            // and try to just refactor this whole thing tbh
            foreach (var node in nodeMap)
            {
                foreach (var port in node.Key.ports)
                {
                    if (!port.isInput)
                    {
                        continue;
                    }

                    var connections = port.ConnectedPorts;
                    foreach (var conn in connections)
                    {
                        if (conn.node == null)
                        {
                            Debug.LogError(
                                $"Could not connect `{node.Value.title}:{port.name}`: " +
                                $"Connected node no longer exists."
                                );
                            continue;
                        }

                        // Only add if the linked node is in the collection
                        if (nodeMap.ContainsKey(conn.node))
                        {
                            var inPort  = node.Value.GetInputPort(port.name);
                            var outPort = nodeMap[conn.node].GetOutputPort(conn.name);

                            if (inPort == null)
                            {
                                Debug.LogError(
                                    $"Could not connect `{node.Value.title}:{port.name}` -> `{conn.node.name}:{conn.name}`. " +
                                    $"Input port `{port.name}` no longer exists."
                                    );
                            }
                            else if (outPort == null)
                            {
                                Debug.LogError(
                                    $"Could not connect `{conn.node.name}:{conn.name}` to `{node.Value.name}:{port.name}`. " +
                                    $"Output port `{conn.name}` no longer exists."
                                    );
                            }
                            else
                            {
                                var edge = inPort.ConnectTo(outPort);
                                AddElement(edge);
                            }
                        }
                    }
                }
            }
        }
Example #8
0
        /// <summary>
        /// Create a new node from reflection data and insert into the Graph.
        /// </summary>
        internal void AddNodeFromReflectionData(
            NodeReflectionData data,
            Vector2 screenPosition,
            PortView connectedPort = null
            )
        {
            // Calculate where to place this node on the graph
            var windowRoot          = m_EditorWindow.rootVisualElement;
            var windowMousePosition = m_EditorWindow.rootVisualElement.ChangeCoordinatesTo(
                windowRoot.parent,
                screenPosition - m_EditorWindow.position.position
                );

            var graphMousePosition = contentViewContainer.WorldToLocal(windowMousePosition);

            // Create a new node instance and set initial data (ports, etc)
            Undo.RegisterCompleteObjectUndo(m_Graph, $"Add Node {data.name}");

            Debug.Log($"+node {data.name}");

            var node = data.CreateInstance();

            node.graphPosition = graphMousePosition;

            m_Graph.AddNode(node);
            m_SerializedGraph.Update();
            EditorUtility.SetDirty(m_Graph);

            var serializedNodesArr = m_SerializedGraph.FindProperty("nodes");

            var nodeIdx        = m_Graph.nodes.IndexOf(node);
            var serializedNode = serializedNodesArr.GetArrayElementAtIndex(nodeIdx);

            // Add a node to the visual graph
            var editorType = NodeReflection.GetNodeEditorType(data.type);
            var element    = Activator.CreateInstance(editorType) as NodeView;

            element.Initialize(node, serializedNode, m_EdgeListener);

            AddElement(element);

            // If there was a provided existing port to connect to, find the best
            // candidate port on the new node and connect.
            if (connectedPort != null)
            {
                var edge = new Edge();

                if (connectedPort.direction == Direction.Input)
                {
                    edge.input  = connectedPort;
                    edge.output = element.GetCompatibleOutputPort(connectedPort);
                }
                else
                {
                    edge.output = connectedPort;
                    edge.input  = element.GetCompatibleInputPort(connectedPort);
                }

                AddEdge(edge, false);
            }

            Dirty(element);
        }
Example #9
0
        /// <summary>
        /// Deserialize a string back into a CopyPasteGraph.
        ///
        /// If <c>includeTags</c> are empty, no filtering will be done. Otherwise,
        /// only nodes with an intersection to one or more tags will be kept.
        /// </summary>
        public static CopyPasteGraph Deserialize(string data, IEnumerable <string> includeTags)
        {
            var graph = CreateInstance <CopyPasteGraph>();

            JsonUtility.FromJsonOverwrite(data, graph);

            // Remove nodes that aren't on the allow list for tags
            var allowedAllNodes = true;

            if (includeTags.Count() > 0)
            {
                graph.Nodes = graph.Nodes.FindAll((node) => {
                    var reflectedNode = NodeReflection.GetNodeType(node.GetType());
                    var allowed       = includeTags.Intersect(reflectedNode.Tags).Count() > 0;
                    allowedAllNodes   = allowedAllNodes && allowed;

                    return(allowed);
                });
            }

            // If we're excluding any from the paste content, notify the user.
            if (!allowedAllNodes)
            {
                Debug.LogWarning("Could not paste one or more nodes - not allowed by the target graph");
            }

            // Generate new unique IDs for each node in the list
            // in case we're copy+pasting back onto the same graph
            var idMap = new Dictionary <string, string>();

            foreach (var node in graph.Nodes)
            {
                var newId = Guid.NewGuid().ToString();
                idMap[node.ID] = newId;
                node.ID        = newId;
            }

            // Remap connections to new node IDs and drop any connections
            // that were to nodes outside of the subset of pasted nodes
            foreach (var node in graph.Nodes)
            {
                foreach (var port in node.Ports.Values)
                {
                    var edges = new List <Connection>(port.Connections);
                    port.Connections.Clear();

                    // Only re-add connections that are in the new pasted subset
                    foreach (var edge in edges)
                    {
                        if (idMap.ContainsKey(edge.NodeID))
                        {
                            port.Connections.Add(new Connection
                            {
                                NodeID   = idMap[edge.NodeID],
                                PortName = edge.PortName
                            });
                        }
                    }
                }
            }

            return(graph);
        }