Skip to content

Instantly share code, notes, and snippets.

@zeffii
Created November 23, 2014 20:22
Show Gist options
  • Save zeffii/6b7f63e440283d67fb2a to your computer and use it in GitHub Desktop.
Save zeffii/6b7f63e440283d67fb2a 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 #####
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