[Bf-blender-cvs] [af2f924b119] functions: Unified socket declaration system

Jacques Lucke noreply at git.blender.org
Mon Mar 18 16:11:09 CET 2019


Commit: af2f924b119c8be7a27176a63afcc6256d19b73b
Author: Jacques Lucke
Date:   Mon Mar 18 16:05:21 2019 +0100
Branches: functions
https://developer.blender.org/rBaf2f924b119c8be7a27176a63afcc6256d19b73b

Unified socket declaration system

Function input/output use the same structure to declare
sockets as other nodes now.

The type inferencer can be invoked in the panel on the
right in the node editor.

The list nodes work with floats, vectors and ints.

===================================================================

M	release/scripts/startup/function_nodes/base.py
M	release/scripts/startup/function_nodes/inferencer.py
M	release/scripts/startup/function_nodes/nodes/append_to_list.py
M	release/scripts/startup/function_nodes/nodes/clamp.py
M	release/scripts/startup/function_nodes/nodes/combine_lists.py
M	release/scripts/startup/function_nodes/nodes/combine_vector.py
M	release/scripts/startup/function_nodes/nodes/float_math.py
M	release/scripts/startup/function_nodes/nodes/function_input.py
M	release/scripts/startup/function_nodes/nodes/function_output.py
M	release/scripts/startup/function_nodes/nodes/get_list_element.py
M	release/scripts/startup/function_nodes/nodes/map_range.py
M	release/scripts/startup/function_nodes/nodes/object_transforms.py
M	release/scripts/startup/function_nodes/nodes/random_number.py
M	release/scripts/startup/function_nodes/nodes/separate_vector.py
M	release/scripts/startup/function_nodes/nodes/vector_distance.py
M	release/scripts/startup/function_nodes/search.py
M	release/scripts/startup/function_nodes/socket_decl.py
M	release/scripts/startup/function_nodes/sockets.py
A	release/scripts/startup/function_nodes/tree_panel.py
M	release/scripts/startup/function_nodes/update_sockets.py
M	release/scripts/startup/function_nodes/utils/generic.py
M	source/blender/functions/frontends/data_flow_nodes/builder.cpp
M	source/blender/functions/frontends/data_flow_nodes/builder.hpp
M	source/blender/functions/frontends/data_flow_nodes/graph_generation.cpp
M	source/blender/functions/frontends/data_flow_nodes/test_nodes.cpp
M	source/blender/functions/frontends/data_flow_nodes/test_sockets.cpp
M	source/blender/functions/functions/lists.cpp
M	source/blender/functions/functions/lists.hpp
M	source/blender/functions/types/numeric_lists.cpp
M	source/blender/functions/types/numeric_lists.hpp

===================================================================

diff --git a/release/scripts/startup/function_nodes/base.py b/release/scripts/startup/function_nodes/base.py
index 8af190b9903..cbd4c87dadc 100644
--- a/release/scripts/startup/function_nodes/base.py
+++ b/release/scripts/startup/function_nodes/base.py
@@ -1,14 +1,108 @@
 import bpy
 from bpy.props import *
 from . utils.generic import iter_subclasses_recursive
+import itertools
+from collections import defaultdict
 
-class FunctionNodeTree(bpy.types.NodeTree):
+class BaseTree:
+    def new_link(self, a, b):
+        if a.is_output:
+            self.links.new(a, b)
+        else:
+            self.links.new(b, a)
+
+class FunctionNodeTree(bpy.types.NodeTree, BaseTree):
     bl_idname = "FunctionNodeTree"
     bl_icon = "MOD_DATA_TRANSFER"
     bl_label = "Function Nodes"
 
 
+class NodeStorage:
+    def __init__(self, node):
+        self.node = node
+        self.set_current_declaration(*node.get_sockets())
+
+    def set_current_declaration(self, inputs, outputs):
+        self.inputs_decl = inputs
+        self.outputs_decl = outputs
+
+        self.inputs_per_decl = {}
+        sockets = iter(self.node.inputs)
+        for decl in self.inputs_decl:
+            group = tuple(itertools.islice(sockets, decl.amount(self.node)))
+            self.inputs_per_decl[decl] = group
+
+        self.outputs_per_decl = {}
+        sockets = iter(self.node.outputs)
+        for decl in self.outputs_decl:
+            group = tuple(itertools.islice(sockets, decl.amount(self.node)))
+            self.outputs_per_decl[decl] = group
+
+        self.sockets_per_decl = {}
+        self.sockets_per_decl.update(self.inputs_per_decl)
+        self.sockets_per_decl.update(self.outputs_per_decl)
+
+        self.decl_per_socket = {}
+        self.decl_index_per_socket = {}
+        for decl, sockets in self.sockets_per_decl.items():
+            for i, socket in enumerate(sockets):
+                self.decl_per_socket[socket] = decl
+                self.decl_index_per_socket[socket] = i
+
+_storage_per_node = {}
+
 class BaseNode:
+    def init(self, context):
+        inputs, outputs = self.get_sockets()
+        for decl in inputs:
+            decl.build(self, self.inputs)
+        for decl in outputs:
+            decl.build(self, self.outputs)
+
+    def rebuild_and_try_keep_state(self):
+        state = self._get_state()
+        self.rebuild()
+        self._try_set_state(state)
+
+    def rebuild(self):
+        self.inputs.clear()
+        self.outputs.clear()
+
+        inputs, outputs = self.get_sockets()
+        for decl in self.storage.inputs_decl:
+            decl.build(self, self.inputs)
+        for decl in self.storage.outputs_decl:
+            decl.build(self, self.outputs)
+        self.storage.set_current_declaration(inputs, outputs)
+
+    def _get_state(self):
+        links_per_input = defaultdict(set)
+        links_per_output = defaultdict(set)
+
+        for link in self.tree.links:
+            if link.from_node == self:
+                links_per_output[link.from_socket.identifier].add(link.to_socket)
+            if link.to_node == self:
+                links_per_input[link.to_socket.identifier].add(link.from_socket)
+
+        return (links_per_input, links_per_output)
+
+    def _try_set_state(self, state):
+        tree = self.tree
+        for socket in self.inputs:
+            for from_socket in state[0][socket.identifier]:
+                tree.links.new(socket, from_socket)
+        for socket in self.outputs:
+            for to_socket in state[1][socket.identifier]:
+                tree.links.new(to_socket, socket)
+
+    @property
+    def tree(self):
+        return self.id_data
+
+    def get_sockets():
+        return [], []
+
     def draw_buttons(self, context, layout):
         self.draw(layout)
 
@@ -25,54 +119,80 @@ class BaseNode:
         props.function_name = function_name
         props.settings_repr = repr(settings)
 
-    def draw_socket(self, socket, layout, text):
-        socket.draw_self(layout, self, text)
+    def draw_socket(self, socket, layout):
+        decl = self.storage.decl_per_socket[socket]
+        index = self.storage.decl_index_per_socket[socket]
+        decl.draw_socket(layout, self, socket, index)
 
     @classmethod
     def iter_final_subclasses(cls):
         yield from filter(lambda x: issubclass(x, bpy.types.Node), iter_subclasses_recursive(cls))
 
-class BaseSocketDecl:
+    def find_input(self, identifier):
+        for socket in self.inputs:
+            if socket.identifier == identifier:
+                return socket
+        else:
+            return None
+
+    def find_output(self, identifier):
+        for socket in self.outputs:
+            if socket.identifier == identifier:
+                return socket
+        else:
+            return None
+
+    def find_socket(self, identifier, is_output):
+        if is_output:
+            return self.find_output(identifier)
+        else:
+            return self.find_input(identifier)
+
+    def iter_sockets(self):
+        yield from self.inputs
+        yield from self.outputs
+
+    # Storage
+    #########################
+
+    @property
+    def storage(self):
+        if self not in _storage_per_node:
+            _storage_per_node[self] = NodeStorage(self)
+        return _storage_per_node[self]
+
+
+
+class BaseSocket:
     color = (0, 0, 0, 0)
 
     def draw_color(self, context, node):
         return self.color
 
     def draw(self, context, layout, node, text):
-        node.draw_socket(self, layout, text)
+        node.draw_socket(self, layout)
 
-    def draw_self(self, layout, node, text):
-        layout.label(text=text)
+    def draw_self(self, layout, node):
+        layout.label(text=self.name)
 
-class FunctionNode(BaseNode):
-    def init(self, context):
-        self.rebuild()
+    def get_index(self, node):
+        if self.is_output:
+            return tuple(node.outputs).index(self)
+        else:
+            return tuple(node.inputs).index(self)
 
-    def rebuild(self):
-        self.inputs.clear()
-        self.outputs.clear()
+    def to_id(self, node):
+        return (node, self.is_output, self.identifier)
 
-        inputs, outputs = self.get_sockets()
-        for socket_decl in inputs:
-            socket_decl.build(self, self.inputs)
-        for socket_decl in outputs:
-            socket_decl.build(self, self.outputs)
-
-    def rebuild_existing_sockets(self):
-        amount_in = len(self.inputs)
-        amount_out = len(self.outputs)
-        self.rebuild()
-        assert amount_in == len(self.inputs)
-        assert amount_out == len(self.outputs)
-
-    def get_sockets():
-        return [], []
+class FunctionNode(BaseNode):
+    pass
 
-class DataSocket(BaseSocketDecl):
+class DataSocket(BaseSocket):
     data_type: StringProperty(
         maxlen=64)
 
-    def draw_self(self, layout, node, text):
+    def draw_self(self, layout, node):
+        text = self.name
         if not (self.is_linked or self.is_output) and hasattr(self, "draw_property"):
             self.draw_property(layout, node, text)
         else:
diff --git a/release/scripts/startup/function_nodes/inferencer.py b/release/scripts/startup/function_nodes/inferencer.py
index 45131c30f59..925f46dcd72 100644
--- a/release/scripts/startup/function_nodes/inferencer.py
+++ b/release/scripts/startup/function_nodes/inferencer.py
@@ -8,14 +8,18 @@ class Inferencer:
     def insert_final_type(self, id, data_type):
         self.finalized_ids[id] = data_type
 
-    def insert_equality_constraint(self, ids):
-        constraint = EqualityConstraint(ids)
+    def insert_equality_constraint(self, ids, decision_id=None):
+        constraint = EqualityConstraint(ids, decision_id)
         self.constraints.add(constraint)
 
     def insert_list_constraint(self, list_ids, base_ids=tuple(), decision_id=None):
         constraint = ListConstraint(list_ids, base_ids, decision_id, self.type_infos)
         self.constraints.add(constraint)
 
+    def insert_union_constraint(self, ids, allowed_types, decision_id=None):
+        constraint = UnionConstraint(ids, decision_id, allowed_types)
+        self.constraints.add(constraint)
+
     def finalize_id(self, id, data_type):
         if id in self.finalized_ids:
             if self.finalized_ids[id] != data_type:
@@ -66,16 +70,35 @@ class Constraint:
         raise NotImplementedError()
 
 class EqualityConstraint(Constraint):
-    def __init__(self, ids):
+    def __init__(self, ids, decision_id):
         self.ids = set(ids)
+        self.decision_id = decision_id
 
-    def can_be_finalized(self, finalized_ids):
-        return any(id in finalized_ids for id in self.ids)
+    def try_finalize(self, finalized_ids, finalize_do, make_decision):
+        for id in self.ids:
+            if id in finalized_ids:
+                data_type = finalized_ids[id]
+                finalize_do(self.ids, data_type)
+                if self.decision_id is not None:
+                    make_decision(self.decision_id, data_type)
+                return True
+        return False
+
+class UnionConstraint(Constraint):
+    def __init__(self, ids, decision_id, allowed_types):
+        self.ids = set(ids)
+        self.decision_id = decision_id
+        self.allowed_types = set(allowed_types)
 
     def try_finalize(self, finalized_ids, finalize_do, make_decision):
         for id in self.ids:
             if id in finalized_ids:
-                finalize_do(self.ids, finalized_ids[id])
+                data_type = finalized_ids[id]
+                if data_type not in self.allowed_types:
+                    raise InferencingError()
+                finalize_do(self.ids, data_type)
+                if self.decision_id is not None:
+                    make_decision(self.decision_id, data_type)
                 return True
         return False
 
diff --git a/release/scripts/startup/function_nodes/nodes/append_to_list.py b/release/scripts/startup/function_nodes/nodes/append_to_list.py
index 390acb68dfb..c52d679faef 100644
--- a/release/scripts/startup/function_nodes/nodes/append_to_list.py
+++ b/release/scripts/startup/function_nodes/nodes/append_to_list.py
@@ -10,8 +10,8 @@ class AppendToListNode(bpy

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list