Created
November 23, 2014 20:22
-
-
Save zeffii/6b7f63e440283d67fb2a 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 ##### | |
from math import degrees | |
from itertools import zip_longest | |
import bpy | |
from bpy.props import EnumProperty, BoolProperty, StringProperty | |
from mathutils import Vector | |
from mathutils.noise import noise_vector, cell_vector, noise, cell | |
from sverchok.node_tree import SverchCustomTreeNode, VerticesSocket, StringsSocket | |
from sverchok.data_structure import fullList, levelsOflist, updateNode | |
''' | |
using slice [:] to generate 3-tuple instead of .to_tuple() | |
because it tests slightly faster on larger data. | |
''' | |
scalar_out = { | |
"DOT": (2, lambda u, v: Vector(u).dot(v)), | |
"DISTANCE": (2, lambda u, v: (Vector(u) - Vector(v)).length), | |
"ANGLE RAD": (2, lambda u, v: Vector(u).angle(v, 0)), | |
"ANGLE DEG": (2, lambda u, v: degrees(Vector(u).angle(v, 0))), | |
"LEN": (1, lambda u: Vector(u).length), | |
"NOISE-S": (1, lambda u: noise(Vector(u))), | |
"CELL-S": (1, lambda u: cell(Vector(u))) | |
} | |
vector_out = { | |
"CROSS": (2, lambda u, v: Vector(u).cross(v)[:]), | |
"ADD": (2, lambda u, v: (u[0]+v[0], u[1]+v[1], u[2]+v[2])), | |
"SUB": (2, lambda u, v: (u[0]-v[0], u[1]-v[1], u[2]-v[2])), | |
"REFLECT": (2, lambda u, v: Vector(u).reflect(v)[:]), | |
"PROJECT": (2, lambda u, v: Vector(u).project(v)[:]), | |
"SCALAR": (2, lambda u, s: (Vector(u) * s)[:]), | |
"1/SCALAR": (2, lambda u, s: (Vector(u) * (1 / s))[:]), | |
"ROUND": (2, lambda u, s: Vector(u).to_tuple(s)), | |
"NORMALIZE": (1, lambda u: Vector(u).normalized()[:]), | |
"NEG": (1, lambda u: (-Vector(u))[:]), | |
"NOISE-V": (1, lambda u: noise_vector(Vector(u))[:]), | |
"CELL-V": (1, lambda u: cell_vector(Vector(u))[:]), | |
"COMPONENT-WISE": (2, lambda u, v: (u[0]*v[0], u[1]*v[1], u[2]*v[2])) | |
} | |
class VectorMathNode(bpy.types.Node, SverchCustomTreeNode): | |
''' VectorMath Node ''' | |
bl_idname = 'VectorMathNode' | |
bl_label = 'Vector Math' | |
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), | |
("NOISE-V", "Noise Vector", "", 8), | |
("NOISE-S", "Noise Scalar", "", 9), | |
("CELL-V", "Vector Cell noise", "", 10), | |
("CELL-S", "Scalar Cell noise", "", 11), | |
("ANGLE DEG", "Angle Degrees", "", 12), | |
("PROJECT", "Project", "", 13), | |
("REFLECT", "Reflect", "", 14), | |
("SCALAR", "Multiply Scalar", "", 15), | |
("1/SCALAR", "Multiply 1/Scalar", "", 16), | |
("ANGLE RAD", "Angle Radians", "", 17), | |
("ROUND", "Round s digits", "", 18), | |
("COMPONENT-WISE", "Component-wise U*V", "", 19) | |
] | |
def mode_change(self, context): | |
# backwards compatibility | |
self.current_op = self.items_ | |
inputs = self.inputs | |
outputs = self.outputs | |
scalar_output = (self.items_ in scalar_out) | |
vector_output = not scalar_output | |
# set the output socket | |
outputs['out'].enabled = scalar_output | |
outputs['W'].enabled = not scalar_output | |
# input 2 could be scalar or vector. | |
if vector_output: | |
s = (self.items_ in ["SCALAR", "1/SCALAR", "ROUND"]) | |
inputs["V"].enabled = not s | |
inputs["S"].enabled = s | |
n = vector_out.get(self.items_)[0] | |
else: | |
inputs["V"].enabled = True | |
inputs["S"].enabled = False | |
n = scalar_out.get(self.items_)[0] | |
# redux inputs | |
if n == 1: | |
inputs['S'].enabled = False | |
inputs['V'].enabled = False | |
updateNode(self, context) | |
items_ = EnumProperty( | |
items=mode_items, | |
name="Function", | |
description="Function choice", | |
default="CROSS", | |
update=mode_change) | |
# matches default of CROSS product, defaults to False at init time. | |
scalar_output_socket = BoolProperty() | |
current_op = StringProperty(default="CROSS") | |
def draw_buttons(self, context, layout): | |
row = layout.row(align=True) | |
row.prop(self, "items_", text="") | |
def sv_init(self, context): | |
inputs = self.inputs | |
outputs = self.outputs | |
# declare them all upfront. | |
inputs.new('VerticesSocket', "U", "u").enabled = True | |
inputs.new('VerticesSocket', "V", "v").enabled = True | |
inputs.new('StringsSocket', "S", "S").enabled = False | |
outputs.new('VerticesSocket', "W", "W").enabled = True | |
outputs.new('StringsSocket', "out", "out").enabled = False | |
def process(self): | |
inputs = self.inputs | |
outputs = self.outputs | |
operation = self.items_ | |
self.label = self.items_ | |
print('lalala') | |
# if not len(outputs) == 1: | |
# return | |
# if not outputs[0].is_linked: | |
# return | |
# # this input is shared over both. | |
# vector1 = [] | |
# if inputs['U'].is_linked: | |
# if isinstance(inputs['U'].links[0].from_socket, VerticesSocket): | |
# vector1 = self.inputs['U'].sv_get(deepcopy=False) | |
# if not vector1: | |
# return | |
# # reaches here only if we have vector1 | |
# u = vector1 | |
# leve = levelsOflist(u) | |
# scalars = ["SCALAR", "1/SCALAR", "ROUND"] | |
# result = [] | |
# # vector-output | |
# if 'W' in outputs and outputs['W'].is_linked: | |
# func = vector_out[operation][0] | |
# if len(inputs) == 1: | |
# try: | |
# result = self.recurse_fx(u, func, leve - 1) | |
# except: | |
# print('one input only, failed') | |
# return | |
# elif len(inputs) == 2: | |
# ''' | |
# get second input sockets content, depending on mode | |
# ''' | |
# b = [] | |
# if operation in scalars: | |
# socket = ['S', StringsSocket] | |
# msg = "two inputs, 1 scalar, " | |
# else: | |
# socket = ['V', VerticesSocket] | |
# msg = "two inputs, both vector, " | |
# name, _type = socket | |
# if name in inputs and inputs[name].is_linked: | |
# if isinstance(inputs[name].links[0].from_socket, _type): | |
# b = self.inputs[name].sv_get(deepcopy=False) | |
# # this means one of the necessary sockets is not connected | |
# if not b: | |
# return | |
# try: | |
# result = self.recurse_fxy(u, b, func, leve - 1) | |
# except: | |
# print(self.name, msg, 'failed') | |
# return | |
# else: | |
# return # fail! | |
# self.outputs['W'].sv_set(result) | |
# # scalar-output | |
# if 'out' in outputs and outputs['out'].is_linked: | |
# vector2, result = [], [] | |
# func = scalar_out[operation][0] | |
# num_inputs = len(inputs) | |
# try: | |
# if num_inputs == 1: | |
# result = self.recurse_fx(u, func, leve - 1) | |
# elif num_inputs == 2: | |
# vector2 = self.inputs['V'].sv_get(deepcopy=False) | |
# result = self.recurse_fxy(u, vector2, func, leve-1) | |
# except: | |
# print('failed scalar out, {} inputs'.format(num_inputs)) | |
# return | |
# finally: | |
# self.outputs['out'].sv_set(result) | |
''' | |
apply f to all values recursively | |
- fx and fxy do full list matching by length | |
''' | |
# vector -> scalar | vector | |
def recurse_fx(self, l, f, leve): | |
if not leve: | |
return f(l) | |
else: | |
rfx = self.recurse_fx | |
t = [rfx(i, f, leve-1) for i in l] | |
return t | |
def recurse_fxy(self, l1, l2, f, leve): | |
res = [] | |
res_append = res.append | |
# will only be used if lists are of unequal length | |
fl = l2[-1] if len(l1) > len(l2) else l1[-1] | |
if leve == 1: | |
for u, v in zip_longest(l1, l2, fillvalue=fl): | |
res_append(f(u, v)) | |
else: | |
for u, v in zip_longest(l1, l2, fillvalue=fl): | |
res_append(self.recurse_fxy(u, v, f, leve-1)) | |
return res | |
def register(): | |
bpy.utils.register_class(VectorMathNode) | |
def unregister(): | |
bpy.utils.unregister_class(VectorMathNode) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment