Коммит 98b16325 создал по автору Victor Doval's avatar Victor Doval
Просмотр файлов

added Tension Node

владелец 987fc402
......@@ -65,6 +65,7 @@
SvRaycasterLiteNode
SvOBJInsolationNode
EvaluateImageNode
SvTensionNode
## Transforms
SvRotationNode
......
# ##### 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 bpy.props import BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, match_long_repeat, match_long_cycle
import numpy as np
def edge_elongation(np_verts, np_verts_n, np_edges):
'''Calculate edge length variation'''
pairs_edges = np_verts[np_edges, :]
vect_rest = (pairs_edges[:, 0, :] - pairs_edges[:, 1, :])
dist_rest = np.linalg.norm(vect_rest, axis=1)
pairs = np_verts_n[np_edges, :]
dif_v = pairs[:, 0, :] - pairs[:, 1, :]
dist = np.linalg.norm(dif_v, axis=1)
dif_l = dist - dist_rest
return dif_l
def vert_edge_tension(dif_l, np_verts, np_edges):
'''Redistribute edge length variation to verts'''
x = dif_l[:, np.newaxis] / 2
v_len = len(np_verts)
tension = np.zeros((v_len, v_len, 1), dtype=np.float32)
tension[np_edges[:, 0], np_edges[:, 1], :] = x
tension[np_edges[:, 1], np_edges[:, 0], :] = x
tension = np.sum(tension, axis=1)[:, 0]
return tension
def area_calc_setup(pols):
'''Analyze pols information'''
np_pols = np.array(pols)
p_len = len(pols)
if np_pols.dtype == np.object:
np_len = np.vectorize(len)
pols_sides = np_len(np_pols)
pols_sides_max = np.amax(pols_sides)
pols = match_long_cycle(pols)
np_pols = np.array(pols)
p_non_regular = True
else:
p_non_regular = False
pols_sides = np.array(p_len)
pols_sides_max = len(pols[0])
return [np_pols, pols_sides_max, pols_sides, p_len, p_non_regular]
def get_normals(v_pols):
'''calculate polygon normals'''
v1 = v_pols[:, 1, :] - v_pols[:, 0, :]
v2 = v_pols[:, 2, :] - v_pols[:, 0, :]
pols_normal = np.cross(v1, v2)
pols_normal_d = np.linalg.norm(pols_normal, axis=1)
return pols_normal / pols_normal_d[:, np.newaxis]
def area_calc(np_verts, area_params):
'''Calculate polygons area'''
np_pols, pols_sides_max, pols_sides, p_len, p_non_regular = area_params
v_pols = np_verts[np_pols, :]
pols_normal = get_normals(v_pols)
prod = np.zeros((pols_sides_max, p_len, 3), dtype=np.float32)
if p_non_regular:
for i in range(pols_sides_max):
mask = pols_sides > i
end_i = (i + 1) % pols_sides_max
prod[i, mask, :] = np.cross(v_pols[mask, i, :], v_pols[mask, end_i, :])
prod = np.sum(prod, axis=0)
area = abs(np.sum(prod * pols_normal, axis=1) / 2)
else:
for i in range(pols_sides_max):
end_i = (i + 1) % pols_sides_max
prod[i, :, :] = np.cross(v_pols[:, i, :], v_pols[:, end_i, :])
prod = np.sum(prod, axis=0)
area = abs(np.sum(prod * pols_normal, axis=1) / 2)
return area
def area_to_verts(np_verts, area_params, pols_tension):
'''Redistribute area variation to verts'''
np_pols, pols_sides_max, pols_sides, p_len, advance = area_params
pol_id = np.arange(p_len)
pol_tens_to_vert = pols_tension / pols_sides
tension = np.zeros((len(np_verts), p_len), dtype=np.float32)
if advance:
for i in range(pols_sides_max):
mask = pols_sides > i
tension[np_pols[mask, i], pol_id[mask]] += pol_tens_to_vert[mask]
else:
for i in range(pols_sides_max):
tension[np_pols[:, i], pol_id] += pols_tension
return np.sum(tension, axis=1)
def calc_pols_tension(np_verts, np_verts_n, area_params):
relax_area = area_calc(np_verts, area_params)
tension_area = area_calc(np_verts_n, area_params)
return tension_area - relax_area
def calc_tensions(meshes, gates, result):
for vertices, vertices_n, edges, pols in zip(*meshes):
np_verts = np.array(vertices)
np_verts_n = np.array(vertices_n)
if len(edges) > 0 and (gates[0] or gates[1]):
np_edges = np.array(edges)
dif_l = edge_elongation(np_verts, np_verts_n, np_edges)
result[0].append(dif_l if gates[4] else dif_l.tolist())
if gates[1]:
tension = vert_edge_tension(dif_l, np_verts, np_edges)
result[1].append(tension if gates[4] else tension.tolist())
if len(pols) > 0 and (gates[2] or gates[3]):
area_params = area_calc_setup(pols)
pols_tension = calc_pols_tension(np_verts, np_verts_n, area_params)
result[2].append(pols_tension if gates[4] else pols_tension.tolist())
if gates[3]:
tens_verts_pols = area_to_verts(np_verts, area_params, pols_tension)
result[3].append(tens_verts_pols if gates[4] else tens_verts_pols.tolist())
return result
class SvTensionNode(bpy.types.Node, SverchCustomTreeNode):
'''
Triggers: Measure deformation
Tooltip: Measure deformation
'''
bl_idname = 'SvTensionNode'
bl_label = 'Tension'
bl_icon = 'SNAP_NORMAL'
output_numpy = BoolProperty(
name='Output NumPy', description='output NumPy arrays',
default=False, update=updateNode)
def draw_buttons_ext(self, context, layout):
layout.prop(self, "output_numpy", toggle=False)
def sv_init(self, context):
sinw = self.inputs.new
sonw = self.outputs.new
sinw('VerticesSocket', "Rest Verts")
sinw('VerticesSocket', "Distort Verts")
sinw('StringsSocket', "Edges")
sinw('StringsSocket', "Pols")
sonw('StringsSocket', "Edges_Tension")
sonw('StringsSocket', "Pols_Tension")
sonw('StringsSocket', "Vert_Edge_T")
sonw('StringsSocket', "Vert_Pol_T")
def get_data(self):
si = self.inputs
vertices_s = si['Rest Verts'].sv_get(default=[[]])
vertices_n = si['Distort Verts'].sv_get(default=[[]])
edges_in = si['Edges'].sv_get(default=[[]])
pols_in = si['Pols'].sv_get(default=[[]])
return match_long_repeat([vertices_s, vertices_n, edges_in, pols_in])
def ready(self):
'''check if there are the needed links'''
si = self.inputs
so = self.outputs
ready = any(s.is_linked for s in so)
ready = ready and si[0].is_linked and si[1].is_linked
ready = ready and (si[2].is_linked or si[3].is_linked)
return ready
def process(self):
so = self.outputs
if not self.ready():
return
result = [[], [], [], []]
gates = []
gates.append(so['Edges_Tension'].is_linked)
gates.append(so['Vert_Edge_T'].is_linked)
gates.append(so['Pols_Tension'].is_linked)
gates.append(so['Vert_Pol_T'].is_linked)
gates.append(self.output_numpy)
meshes = self.get_data()
result = calc_tensions(meshes, gates, result)
if gates[0]:
so['Edges_Tension'].sv_set(result[0])
if gates[1]:
so['Vert_Edge_T'].sv_set(result[1])
if gates[2]:
so['Pols_Tension'].sv_set(result[2])
if gates[3]:
so['Vert_Pol_T'].sv_set(result[3])
def register():
bpy.utils.register_class(SvTensionNode)
def unregister():
bpy.utils.unregister_class(SvTensionNode)
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать