[Bf-extensions-cvs] [2a98d83b] master: Node Arrange: 2.81, Thanks JuhaW: T66410

meta-androcto noreply at git.blender.org
Thu Sep 5 02:31:43 CEST 2019


Commit: 2a98d83b4bcb37e3b8de5ccc72c283a5135708ed
Author: meta-androcto
Date:   Thu Sep 5 10:31:18 2019 +1000
Branches: master
https://developer.blender.org/rBA2a98d83b4bcb37e3b8de5ccc72c283a5135708ed

Node Arrange: 2.81, Thanks JuhaW: T66410

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

A	node_arrange.py

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

diff --git a/node_arrange.py b/node_arrange.py
new file mode 100644
index 00000000..370cbeff
--- /dev/null
+++ b/node_arrange.py
@@ -0,0 +1,520 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+bl_info = {
+	"name": "Node Arrange",
+	"author": "JuhaW",
+	"version": (0, 2, 1),
+	"blender": (2, 80, 4),
+	"location": "Node Editor > Properties > Trees",
+	"description": "Node Tree Arrangement Tools",
+	"warning": "",
+	"wiki_url": "",
+	"tracker_url": "https://github.com/JuhaW/NodeArrange/issues",
+	"category": "Node"
+}
+
+
+import sys
+import bpy
+from collections import OrderedDict
+from itertools import repeat
+import pprint
+import pdb
+from bpy.types import Operator, Panel
+from bpy.props import (
+    IntProperty,
+)
+from copy import copy
+
+
+#From Node Wrangler
+def get_nodes_linked(context):
+    tree = context.space_data.node_tree
+
+    # Get nodes from currently edited tree.
+    # If user is editing a group, space_data.node_tree is still the base level (outside group).
+    # context.active_node is in the group though, so if space_data.node_tree.nodes.active is not
+    # the same as context.active_node, the user is in a group.
+    # Check recursively until we find the real active node_tree:
+    if tree.nodes.active:
+        while tree.nodes.active != context.active_node:
+            tree = tree.nodes.active.node_tree
+
+    return tree.nodes, tree.links
+
+class NA_OT_AlignNodes(Operator):
+    '''Align the selected nodes/Tidy loose nodes'''
+    bl_idname = "node.na_align_nodes"
+    bl_label = "Align Nodes"
+    bl_options = {'REGISTER', 'UNDO'}
+    margin: IntProperty(name='Margin', default=50, description='The amount of space between nodes')
+
+    def execute(self, context):
+        nodes, links = get_nodes_linked(context)
+        margin = self.margin
+
+        selection = []
+        for node in nodes:
+            if node.select and node.type != 'FRAME':
+                selection.append(node)
+
+        # If no nodes are selected, align all nodes
+        active_loc = None
+        if not selection:
+            selection = nodes
+        elif nodes.active in selection:
+            active_loc = copy(nodes.active.location)  # make a copy, not a reference
+
+        # Check if nodes should be laid out horizontally or vertically
+        x_locs = [n.location.x + (n.dimensions.x / 2) for n in selection]  # use dimension to get center of node, not corner
+        y_locs = [n.location.y - (n.dimensions.y / 2) for n in selection]
+        x_range = max(x_locs) - min(x_locs)
+        y_range = max(y_locs) - min(y_locs)
+        mid_x = (max(x_locs) + min(x_locs)) / 2
+        mid_y = (max(y_locs) + min(y_locs)) / 2
+        horizontal = x_range > y_range
+
+        # Sort selection by location of node mid-point
+        if horizontal:
+            selection = sorted(selection, key=lambda n: n.location.x + (n.dimensions.x / 2))
+        else:
+            selection = sorted(selection, key=lambda n: n.location.y - (n.dimensions.y / 2), reverse=True)
+
+        # Alignment
+        current_pos = 0
+        for node in selection:
+            current_margin = margin
+            current_margin = current_margin * 0.5 if node.hide else current_margin  # use a smaller margin for hidden nodes
+
+            if horizontal:
+                node.location.x = current_pos
+                current_pos += current_margin + node.dimensions.x
+                node.location.y = mid_y + (node.dimensions.y / 2)
+            else:
+                node.location.y = current_pos
+                current_pos -= (current_margin * 0.3) + node.dimensions.y  # use half-margin for vertical alignment
+                node.location.x = mid_x - (node.dimensions.x / 2)
+
+        # If active node is selected, center nodes around it
+        if active_loc is not None:
+            active_loc_diff = active_loc - nodes.active.location
+            for node in selection:
+                node.location += active_loc_diff
+        else:  # Position nodes centered around where they used to be
+            locs = ([n.location.x + (n.dimensions.x / 2) for n in selection]) if horizontal else ([n.location.y - (n.dimensions.y / 2) for n in selection])
+            new_mid = (max(locs) + min(locs)) / 2
+            for node in selection:
+                if horizontal:
+                    node.location.x += (mid_x - new_mid)
+                else:
+                    node.location.y += (mid_y - new_mid)
+
+        return {'FINISHED'}
+
+class values():
+	average_y = 0
+	x_last = 0
+	margin_x = 100
+	mat_name = ""
+	margin_y = 20
+
+
+class NA_PT_NodePanel(Panel):
+	bl_label = "Node Arrange"
+	bl_space_type = "NODE_EDITOR"
+	bl_region_type = "UI"
+	bl_category = "Arrange"
+
+	def draw(self, context):
+		if context.active_node is not None:
+			layout = self.layout
+			row = layout.row()
+			col = layout.column
+			row.operator('node.button')
+
+			row = layout.row()
+			row.prop(bpy.context.scene, 'nodemargin_x', text="Margin x")
+			row = layout.row()
+			row.prop(bpy.context.scene, 'nodemargin_y', text="Margin y")
+			row = layout.row()
+			row.prop(context.scene, 'node_center', text="Center nodes")
+
+			row = layout.row()
+			row.operator('node.na_align_nodes', text="Align to Selected")
+
+			row = layout.row()
+			node = context.space_data.node_tree.nodes.active
+			if node and node.select:
+				row.prop(node, 'location', text = "Node X", index = 0)
+				row.prop(node, 'location', text = "Node Y", index = 1)
+				row = layout.row()
+				row.prop(node, 'width', text = "Node width")
+
+			row = layout.row()
+			row.operator('node.button_odd')
+
+class NA_OT_NodeButton(Operator):
+
+	'''Arrange Connected Nodes/Arrange All Nodes'''
+	bl_idname = 'node.button'
+	bl_label = 'Arrange All Nodes'
+
+	def execute(self, context):
+		nodemargin(self, context)
+		bpy.context.space_data.node_tree.nodes.update()
+		bpy.ops.node.view_all()
+
+		return {'FINISHED'}
+
+	# not sure this is doing what you expect.
+	# blender.org/api/blender_python_api_current/bpy.types.Operator.html#invoke
+	def invoke(self, context, value):
+		values.mat_name = bpy.context.space_data.node_tree
+		nodemargin(self, context)
+		return {'FINISHED'}
+
+
+class NA_OT_NodeButtonOdd(Operator):
+
+	'Show the nodes for this material'
+	bl_idname = 'node.button_odd'
+	bl_label = 'Select Unlinked'
+
+	def execute(self, context):
+		values.mat_name = bpy.context.space_data.node_tree
+		#mat = bpy.context.object.active_material
+		nodes_iterate(context.space_data.node_tree, False)
+		return {'FINISHED'}
+
+
+class NA_OT_NodeButtonCenter(Operator):
+
+	'Show the nodes for this material'
+	bl_idname = 'node.button_center'
+	bl_label = 'Center nodes (0,0)'
+
+	def execute(self, context):
+		values.mat_name = ""  # reset
+		mat = bpy.context.object.active_material
+		nodes_center(mat)
+		return {'FINISHED'}
+
+
+def nodemargin(self, context):
+
+	values.margin_x = context.scene.nodemargin_x
+	values.margin_y = context.scene.nodemargin_y
+
+	ntree = context.space_data.node_tree
+
+	nodes_iterate(ntree)
+
+	# arrange nodes + this center nodes together
+	if context.scene.node_center:
+		nodes_center(ntree)
+
+
+class NA_OT_ArrangeNodesOp(bpy.types.Operator):
+	bl_idname = 'node.arrange_nodetree'
+	bl_label = 'Nodes Private Op'
+
+	mat_name : bpy.props.StringProperty()
+	margin_x : bpy.props.IntProperty(default=120)
+	margin_y : bpy.props.IntProperty(default=120)
+
+	def nodemargin2(self, context):
+		mat = None
+		mat_found = bpy.data.materials.get(self.mat_name)
+		if self.mat_name and mat_found:
+			mat = mat_found
+			#print(mat)
+
+		if not mat:
+			return
+		else:
+			values.mat_name = self.mat_name
+			scn = context.scene
+			scn.nodemargin_x = self.margin_x
+			scn.nodemargin_y = self.margin_y
+			nodes_iterate(mat)
+			if scn.node_center:
+				nodes_center(mat)
+
+	def execute(self, context):
+		self.nodemargin2(context)
+		return {'FINISHED'}
+
+
+def outputnode_search(ntree):	 # return node/None
+
+	outputnodes = []
+	for node in bpy.context.space_data.node_tree.nodes:
+		if not node.outputs:
+			for input in node.inputs:
+				if input.is_linked:
+					outputnodes.append(node)
+					break
+
+	if not outputnodes:
+		print("No output node found")
+		return None
+	return outputnodes
+
+
+###############################################################
+def nodes_iterate(ntree, arrange=True):
+
+	nodeoutput = outputnode_search(ntree)
+	if nodeoutput is None:
+		#print ("nodeoutput is None")
+		return None
+	a = []
+	a.append([])
+	for i in nodeoutput:
+		a[0].append(i)
+
+
+	level = 0
+
+	while a[level]:
+		a.append([])
+
+		for node in a[level]:
+			inputlist = [i for i in node.inputs if i.is_linked]
+
+			if inputlist:
+
+				for input in inputlist:
+					for nlinks in input.links:
+						node1 = nlinks.from_node
+						a[level + 1].append(node1)
+
+			else:
+				pass
+
+		level += 1
+
+	del a[level]
+	level -= 1
+
+	#remove duplicate nodes at the same level, first wins
+	for x, nodes in enumerate(a):
+		a[x] = list(OrderedDict(zip(a[x], repeat(None))))
+
+	#remove duplicate nodes in all levels, last wins
+	top = level
+	for row1 in range(top, 1, -1):
+		for col1 in a[row1]:
+			for row2 in range(row1-1, 0, -1):
+				for col2 in a[row2]:
+					if col1 == col2:
+						a[row2].remove(col2)
+						break
+
+	"""
+	for x, i in enumerate(a):
+		print (x)
+		for j in i:
+			print (j)
+		#print()
+	"""
+	"""
+	#add node frames to nodelist
+	frames = []
+	print ("Frames

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list