2019-03-22 11:57:03 +01:00
|
|
|
import bpy
|
|
|
|
|
from collections import namedtuple
|
|
|
|
|
|
2019-09-19 10:34:13 +02:00
|
|
|
from . base import BaseTree, BaseNode
|
2019-11-14 16:31:24 +01:00
|
|
|
from . graph import DirectedGraphBuilder, DirectedGraph
|
2019-03-22 11:57:03 +01:00
|
|
|
|
2019-11-06 16:08:20 +01:00
|
|
|
class FunctionTree(bpy.types.NodeTree, BaseTree):
|
2019-03-22 11:57:03 +01:00
|
|
|
bl_idname = "FunctionTree"
|
|
|
|
|
bl_icon = "MOD_DATA_TRANSFER"
|
|
|
|
|
bl_label = "Function Nodes"
|
|
|
|
|
|
2019-11-13 15:29:00 +01:00
|
|
|
def get_input_nodes(self):
|
2019-12-03 15:07:40 +01:00
|
|
|
input_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupInputNode"]
|
2019-11-13 15:29:00 +01:00
|
|
|
sorted_input_nodes = sorted(input_nodes, key=lambda node: (node.sort_index, node.name))
|
|
|
|
|
return sorted_input_nodes
|
2019-03-22 11:57:03 +01:00
|
|
|
|
2019-11-13 15:29:00 +01:00
|
|
|
def get_output_nodes(self):
|
2019-12-03 15:07:40 +01:00
|
|
|
output_nodes = [node for node in self.nodes if node.bl_idname == "fn_GroupOutputNode"]
|
2019-11-13 15:29:00 +01:00
|
|
|
sorted_output_nodes = sorted(output_nodes, key=lambda node: (node.sort_index, node.name))
|
|
|
|
|
return sorted_output_nodes
|
2019-11-06 16:08:20 +01:00
|
|
|
|
2019-11-14 16:31:24 +01:00
|
|
|
def get_directly_used_trees(self):
|
2019-11-06 16:08:20 +01:00
|
|
|
trees = set()
|
|
|
|
|
for node in self.nodes:
|
|
|
|
|
if isinstance(node, BaseNode):
|
2019-11-14 16:31:24 +01:00
|
|
|
trees.update(node.iter_directly_used_trees())
|
|
|
|
|
return trees
|
|
|
|
|
|
2019-12-18 13:17:49 +01:00
|
|
|
def find_callable_trees(self):
|
|
|
|
|
used_by_trees = FunctionTree.BuildInvertedCallGraph().reachable(self)
|
|
|
|
|
trees = [tree for tree in bpy.data.node_groups
|
|
|
|
|
if isinstance(tree, FunctionTree) and tree not in used_by_trees]
|
|
|
|
|
return trees
|
|
|
|
|
|
2019-11-14 16:31:24 +01:00
|
|
|
@staticmethod
|
|
|
|
|
def BuildTreeCallGraph() -> DirectedGraph:
|
|
|
|
|
'''
|
|
|
|
|
Every vertex is a tree.
|
|
|
|
|
Every edge (A, B) means: Tree A uses tree B.
|
|
|
|
|
'''
|
|
|
|
|
builder = DirectedGraphBuilder()
|
|
|
|
|
for tree in bpy.data.node_groups:
|
|
|
|
|
if isinstance(tree, FunctionTree):
|
|
|
|
|
builder.add_vertex(tree)
|
|
|
|
|
for dependency_tree in tree.get_directly_used_trees():
|
|
|
|
|
builder.add_directed_edge(
|
|
|
|
|
from_v=tree,
|
|
|
|
|
to_v=dependency_tree)
|
|
|
|
|
return builder.build()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def BuildInvertedCallGraph() -> DirectedGraph:
|
|
|
|
|
'''
|
|
|
|
|
Builds a directed graph in which every tree is a vertex.
|
|
|
|
|
Every edge (A, B) means: Changes in A might affect B.
|
|
|
|
|
'''
|
|
|
|
|
return FunctionTree.BuildTreeCallGraph().inverted()
|