-
-
Save zeffii/9eb84eafa858c40ef308 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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