Skip to content

Instantly share code, notes, and snippets.

@zeffii
Forked from anonymous/math.py
Last active August 29, 2015 14:10
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/da9530a713076538e87c to your computer and use it in GitHub Desktop.
Save zeffii/da9530a713076538e87c 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
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):
layout.prop(self, "items_", "Functions:")
def sv_init(self, context):
self.inputs.new('VerticesSocket', "U", "u")
self.inputs.new('VerticesSocket', "V", "v")
self.outputs.new('VerticesSocket', "W", "W")
def process(self):
inputs = self.inputs
outputs = self.outputs
num_inputs = len(inputs)
operation = self.items_
self.label = self.items_
result = []
if not outputs[0].is_linked:
return
# this input is shared over both.
u = inputs['U'].sv_get(deepcopy=False)
if not u:
return
# reaches here only if we have vector1
leve = levelsOflist(u)
scalars = ["SCALAR", "1/SCALAR", "ROUND"]
socket2 = 'S' if operation in scalars else 'V'
output_name = 'W' if outputs['W'].enabled else 'out'
if output_name == 'W':
func = vector_out[operation][1]
else:
func = scalar_out[operation][1]
# get second socket content, and recurse if we can
if num_inputs == 1:
result = self.recurse_fx(u, func, leve - 1)
elif num_inputs == 2:
b = inputs[socket2].sv_get(deepcopy=False)
if not b:
return
result = self.recurse_fxy(u, b, func, leve - 1)
else:
return
if result:
outputs[output_name].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