Skip to content

Instantly share code, notes, and snippets.

@zeffii
Forked from anonymous/node_VectorMath2.py
Created June 12, 2014 15:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeffii/9eb84eafa858c40ef308 to your computer and use it in GitHub Desktop.
Save zeffii/9eb84eafa858c40ef308 to your computer and use it in GitHub Desktop.
# 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 #####
import bpy
from node_s import *
from util import *
from mathutils import Vector, Matrix
from bpy.props import EnumProperty, StringProperty
scalar_out = {
"DOT": (lambda u, v: u.dot(v), 2),
"DISTANCE": (lambda u, v: (u - v).length, 2),
"LEN": (lambda u: u.length, 1),
"ANGLE": (lambda u, v: u.angle(v, 0), 2),
}
vector_out = {
"CROSS": (lambda u, v: u.cross(v), 2),
"ADD": (lambda u, v: u + v, 2),
"SUB": (lambda u, v: u - v, 2),
"NORMALIZE": (lambda u: u.normalized(), 1),
"NEG": (lambda u: -u, 1),
"REFLECT": (lambda u, v: u.reflect(v), 2),
"PROJECT": (lambda u, v: u.project(v), 2),
"SCALAR": (lambda u, s: u * s, 2),
"1/SCALAR": (lambda u, s: u * (1/s), 2),
}
class VectorMath2Node(Node, SverchCustomTreeNode):
''' VectorMath 2 Node '''
bl_idname = 'VectorMath2Node'
bl_label = 'Vector Math 2'
bl_icon = 'OUTLINER_OB_EMPTY'
# vector math functions
mode_items = [
("CROSS", "Cross product", "", 0),
("DOT", "Dot product", "", 1),
("ADD", "Add", "", 2),
("SUB", "Sub", "", 3),
("LEN", "Length", "", 4),
("DISTANCE", "Distance", "", 5),
("NORMALIZE", "Normalize", "", 6),
("NEG", "Negate", "", 7),
("ANGLE", "Angle", "", 12),
("PROJECT", "Project", "", 13),
("REFLECT", "Reflect", "", 14),
("SCALAR", "Multiply Scalar", "", 15),
("1/SCALAR", "Multiply 1/Scalar", "", 16)
]
items_ = EnumProperty(
items=mode_items,
name="Function",
description="Function choice",
default="CROSS", update=updateNode)
# matches default of CROSS product
current_op = StringProperty(default="CROSS")
scalar_output_socket = False
def draw_buttons(self, context, layout):
layout.prop(self, "items_", "Functions:")
def init(self, context):
self.inputs.new('VerticesSocket', "U", "u")
self.inputs.new('VerticesSocket', "V", "v")
self.outputs.new('VerticesSocket', "W", "W")
def update_outputs_and_inputs(self):
'''
Reaches here only if operation is different from current op
'''
inputs = self.inputs
outputs = self.outputs
operation = self.items_
current_op = self.current_op
scalars = ["SCALAR", "1/SCALAR"]
# check and adjust outputs and input size
if operation in scalar_out:
self.scalar_output_socket = True
nrInputs = scalar_out[operation][1]
if 'W' in outputs:
outputs.remove(outputs['W'])
outputs.new('StringsSocket', "out", "out")
else:
self.scalar_output_socket = False
nrInputs = vector_out[operation][1]
if 'out' in outputs:
outputs.remove(outputs['out'])
outputs.new('VerticesSocket', "W", "W")
'''
this monster removes and adds sockets depending on what kind of switch
between operations is made, some operation changes require addition of sockets
others require deletion or replacement.
'''
if (operation in scalars) and (current_op in scalars):
return # it's OK nothing to change
add_scalar_input = lambda: inputs.new('StringsSocket', "S", "s")
add_vector_input = lambda: inputs.new('VerticesSocket', "V", "v")
remove_last_input = lambda: inputs.remove(inputs[-1])
''' reaches this point, means it needs to rewire '''
if nrInputs < len(inputs):
remove_last_input()
elif nrInputs > len(inputs):
if (operation in scalars):
add_scalar_input()
else:
add_vector_input()
else: # nr inputs == len(inputs)
if nrInputs == 1:
# is only ever a vector u
return
if operation in scalars:
remove_last_input()
add_scalar_input()
elif (current_op in scalars):
remove_last_input()
add_vector_input()
# this is sad.
if len(inputs) > 2:
print("found three removed last")
# remove_last_input()
# at this point all combos are handled.
def nothing_to_process(self):
inputs = self.inputs
outputs = self.outputs
if not (('out' in outputs) or ('W' in outputs)):
return True
if not outputs[0].links:
return True
# if len(inputs) == 0:
# return True
# for i in range(2):
# if (len(inputs) == i+1) and not inputs[i].links:
# return True
def update(self):
'''
because we want to show socket-type changes even when
no processing needs to be done, we detect if we need
to modify sockets before returning early.
'''
inputs = self.inputs
outputs = self.outputs
operation = self.items_
self.label = self.items_
scalars = ["SCALAR", "1/SCALAR"]
# has it changed since setting it last?
if not (operation == self.current_op):
self.update_outputs_and_inputs()
self.current_op = operation
# no output or full input ?
if self.nothing_to_process():
return
# this input is shared over both.
vector1 = []
if 'U' in inputs and inputs['U'].links:
if isinstance(inputs['U'].links[0].from_socket, VerticesSocket):
vector1 = SvGetSocketAnyType(self, inputs['U'])
if not vector1:
return
# reaches here only if we have vector1
u = vector1
leve = levelsOflist(u)
# vector-output
if 'W' in outputs and outputs['W'].links:
func = vector_out[operation][0]
vector2, scalar1, result = [], [], []
# get
if 'V' in inputs and inputs['V'].links:
if isinstance(inputs['V'].links[0].from_socket, VerticesSocket):
vector2 = SvGetSocketAnyType(self, inputs['V'])
elif 'S' in inputs and inputs['S'].links:
if isinstance(inputs['S'].links[0].from_socket, StringsSocket):
scalar1 = SvGetSocketAnyType(self, inputs['S'])
if len(inputs) == 1:
try:
result = self.recurse_fx(u, func, leve - 1)
except:
print(self.name, 'one input only, failed')
elif len(inputs) == 2:
if operation in scalars:
try:
result = self.recurse_fxy2(u, scalar1, func, leve - 1)
except:
print(self.name, 'two inputs, scalar, failed')
else:
try:
result = self.recurse_fxy(u, vector2, func, leve - 1)
except:
print(self.name, 'two inputs, non-scalar, failed')
SvSetSocketAnyType(self, 'W', result)
# scalar-output
elif 'out' in outputs and outputs['out'].links:
func = scalar_out[operation][0]
vector2, result = [], []
if 'V' in inputs and inputs['V'].links:
if isinstance(inputs['V'].links[0].from_socket, VerticesSocket):
vector2 = SvGetSocketAnyType(self, inputs['V'])
if len(inputs) == 1:
try:
result = self.recurse_fx(u, func, leve - 1)
except:
print(self.name)
elif len(inputs) == 2:
try:
result = self.recurse_fxy(u, vector2, func, leve - 1)
except:
print(self.name)
SvSetSocketAnyType(self, 'out', result)
# apply f to all values recursively
def recurse_fx(self, l, f, leve):
if not leve:
w = f(Vector(l))
if self.scalar_output_socket:
return w
else:
return w.to_tuple()
else:
t = []
for i in l:
t.append(self.recurse_fx(i, f, leve - 1))
return t
# match length of lists,
# taken from mathNode
def recurse_fxy(self, l1, l2, f, leve):
if not leve:
w = f(Vector(l1), Vector(l2))
if self.scalar_output_socket:
return w
else:
return w.to_tuple()
else:
max_obj = max(len(l1), len(l2))
fullList(l1, max_obj)
fullList(l2, max_obj)
res = []
for i in range(len(l1)):
res.append(self.recurse_fxy(l1[i], l2[i], f, leve - 1))
return res
# match length of lists, -- this to deal wih vector*scalar only
# taken from mathNode
def recurse_fxy2(self, l1, l2, f, leve):
if not leve:
w = f(Vector(l1), l2)
return w.to_tuple()
else:
max_obj = max(len(l1), len(l2))
fullList(l1, max_obj)
fullList(l2, max_obj)
res = []
for i in range(len(l1)):
res.append(self.recurse_fxy2(l1[i], l2[i], f, leve - 1))
return res
def update_socket(self, context):
self.update()
def register():
bpy.utils.register_class(VectorMath2Node)
def unregister():
bpy.utils.unregister_class(VectorMath2Node)
if __name__ == "__main__":
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment