-
Notifications
You must be signed in to change notification settings - Fork 235
/
Copy pathdelaunay3d.py
124 lines (101 loc) · 4.05 KB
/
delaunay3d.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import numpy as np
import bpy
from bpy.props import FloatProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat
from sverchok.dependencies import scipy
if scipy is not None:
from scipy.spatial import Delaunay
class SvExDelaunay3DNode(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Delaunay 3D
Tooltip: Generate 3D Delaunay Triangulation
"""
bl_idname = 'SvExDelaunay3DNode'
bl_label = 'Delaunay 3D'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_VORONOI'
sv_dependencies = {'scipy'}
join : BoolProperty(
name = "Join",
default = False,
update = updateNode)
threshold : FloatProperty(
name = "Threshold",
default = 1e-4,
precision = 4,
update = updateNode)
def draw_buttons(self, context, layout):
layout.prop(self, "join", toggle=True)
def sv_init(self, context):
self.inputs.new('SvVerticesSocket', "Vertices")
self.inputs.new('SvStringsSocket', "Threshold").prop_name = 'threshold'
self.outputs.new('SvVerticesSocket', "Vertices")
self.outputs.new('SvStringsSocket', "Edges")
self.outputs.new('SvStringsSocket', "Faces")
def make_edges(self, idxs):
return [(i, j) for i in idxs for j in idxs if i < j]
def make_faces(self, idxs):
return [(i, j, k) for i in idxs for j in idxs for k in idxs if i < j and j < k]
def get_verts(self, verts, idxs):
return [verts[i] for i in idxs]
def is_planar(self, verts, idxs, threshold):
if threshold == 0:
return True
a, b, c, d = [verts[i] for i in idxs]
a, b, c, d = np.array(a), np.array(b), np.array(c), np.array(d)
v1 = b - a
v2 = c - a
v3 = d - a
v1 = v1 / np.linalg.norm(v1)
v2 = v2 / np.linalg.norm(v2)
v3 = v3 / np.linalg.norm(v3)
volume = np.cross(v1, v2).dot(v3) / 6
return abs(volume) < threshold
def process(self):
if not any(socket.is_linked for socket in self.outputs):
return
vertices_s = self.inputs['Vertices'].sv_get()
threshold_s = self.inputs['Threshold'].sv_get()
verts_out = []
edges_out = []
faces_out = []
for vertices, threshold in zip_long_repeat(vertices_s, threshold_s):
if isinstance(threshold, (list, tuple)):
threshold = threshold[0]
tri = Delaunay(np.array(vertices))
if self.join:
verts_new = vertices
edges_new = set()
faces_new = set()
for simplex in tri.simplices:
if self.is_planar(vertices, simplex, threshold):
continue
edges_new.update(set(self.make_edges(simplex)))
faces_new.update(set(self.make_faces(simplex)))
verts_out.append(verts_new)
edges_out.append(list(edges_new))
faces_out.append(list(faces_new))
else:
verts_new = []
edges_new = []
faces_new = []
for simplex in tri.simplices:
if self.is_planar(vertices, simplex, threshold):
continue
verts_simplex = self.get_verts(vertices, simplex)
edges_simplex = self.make_edges([0, 1, 2, 3])
faces_simplex = self.make_faces([0, 1, 2, 3])
verts_new.append(verts_simplex)
edges_new.append(edges_simplex)
faces_new.append(faces_simplex)
verts_out.extend(verts_new)
edges_out.extend(edges_new)
faces_out.extend(faces_new)
self.outputs['Vertices'].sv_set(verts_out)
self.outputs['Edges'].sv_set(edges_out)
self.outputs['Faces'].sv_set(faces_out)
def register():
bpy.utils.register_class(SvExDelaunay3DNode)
def unregister():
bpy.utils.unregister_class(SvExDelaunay3DNode)