Открыть боковую панель
nikitronn
sverchok
Коммиты
7f54f40b
Коммит
7f54f40b
создал
Янв 03, 2021
по автору
Ilya Portnov
Просмотр файлов
"Relax mesh" node.
владелец
b875f9b5
Изменения
4
Скрыть пробелы
Построчно
Рядом
index.md
Просмотр файла @
7f54f40b
...
...
@@ -470,6 +470,7 @@
SvInsetFaces
SvLatheNode
SvSmoothNode
SvRelaxMeshNode
SvSmoothLines
---
CrossSectionNode
...
...
nodes/modifier_change/relax_mesh.py
0 → 100644
Просмотр файла @
7f54f40b
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
import
bpy
from
bpy.props
import
IntProperty
,
FloatProperty
,
BoolProperty
,
EnumProperty
from
sverchok.node_tree
import
SverchCustomTreeNode
from
sverchok.data_structure
import
updateNode
,
zip_long_repeat
,
throttle_and_update_node
,
get_data_nesting_level
,
ensure_nesting_level
from
sverchok.utils.relax_mesh
import
*
class
SvRelaxMeshNode
(
bpy
.
types
.
Node
,
SverchCustomTreeNode
):
"""
Triggers: Relax Mesh
Tooltip: Relax mesh
"""
bl_idname
=
'SvRelaxMeshNode'
bl_label
=
'Relax Mesh'
bl_icon
=
'MOD_SMOOTH'
iterations
:
IntProperty
(
name
=
"Iterations"
,
min
=
0
,
max
=
1000
,
default
=
1
,
update
=
updateNode
)
factor
:
FloatProperty
(
name
=
"Factor"
,
description
=
"Smoothing factor"
,
min
=
0.0
,
max
=
1.0
,
default
=
0.5
,
update
=
updateNode
)
@
throttle_and_update_node
def
update_sockets
(
self
,
context
):
self
.
inputs
[
'Factor'
].
hide_safe
=
self
.
algorithm
not
in
{
'EDGES'
,
'FACES'
}
algorithms
=
[
(
'LLOYD'
,
"Lloyd"
,
"Lloyd"
,
0
),
(
'EDGES'
,
"Edges"
,
"Edges"
,
1
),
(
'FACES'
,
"Faces"
,
"Faces"
,
2
)
]
algorithm
:
EnumProperty
(
name
=
"Algorithm"
,
items
=
algorithms
,
default
=
'LLOYD'
,
update
=
update_sockets
)
def
get_available_methods
(
self
,
context
):
items
=
[]
if
self
.
algorithm
in
{
'EDGES'
,
'FACES'
}:
items
.
append
((
NONE
,
"Do not use"
,
"Do not use"
,
0
))
if
self
.
algorithm
==
'LLOYD'
:
items
.
append
((
LINEAR
,
"Linear"
,
"Linear"
,
1
))
items
.
append
((
NORMAL
,
"Normal"
,
"Move points along mesh tangent only"
,
2
))
items
.
append
((
BVH
,
"BVH"
,
"Use BVH Tree"
,
3
))
return
items
preserve_shape
:
EnumProperty
(
name
=
"Preserve shape"
,
items
=
get_available_methods
,
update
=
updateNode
)
skip_bounds
:
BoolProperty
(
name
=
"Skip bounds"
,
description
=
"Leave boundary vertices where they were"
,
default
=
True
,
update
=
updateNode
)
targets
=
[
(
AVERAGE
,
"Average"
,
"Average"
,
0
),
(
MINIMUM
,
"Minimum"
,
"Minimum"
,
1
),
(
MAXIMUM
,
"Maximum"
,
"Maximum"
,
2
)
]
target
:
EnumProperty
(
name
=
"Target"
,
items
=
targets
,
default
=
AVERAGE
,
update
=
updateNode
)
def
sv_init
(
self
,
context
):
self
.
inputs
.
new
(
'SvVerticesSocket'
,
"Vertices"
)
self
.
inputs
.
new
(
'SvStringsSocket'
,
'Edges'
)
self
.
inputs
.
new
(
'SvStringsSocket'
,
'Faces'
)
self
.
inputs
.
new
(
'SvStringsSocket'
,
'VertMask'
)
self
.
inputs
.
new
(
'SvStringsSocket'
,
'Iterations'
).
prop_name
=
"iterations"
self
.
inputs
.
new
(
'SvStringsSocket'
,
'Factor'
).
prop_name
=
"factor"
self
.
outputs
.
new
(
'SvVerticesSocket'
,
'Vertices'
)
#self.outputs.new('SvStringsSocket', 'Edges')
#self.outputs.new('SvStringsSocket', 'Faces')
self
.
update_sockets
(
context
)
def
draw_buttons
(
self
,
context
,
layout
):
layout
.
prop
(
self
,
'algorithm'
)
if
self
.
algorithm
in
{
'EDGES'
,
'FACES'
}:
layout
.
prop
(
self
,
'target'
)
layout
.
prop
(
self
,
'preserve_shape'
)
layout
.
prop
(
self
,
'skip_bounds'
)
def
process
(
self
):
if
not
any
(
output
.
is_linked
for
output
in
self
.
outputs
):
return
vertices_s
=
self
.
inputs
[
'Vertices'
].
sv_get
(
deepcopy
=
False
)
edges_s
=
self
.
inputs
[
'Edges'
].
sv_get
(
default
=
[[]],
deepcopy
=
False
)
faces_s
=
self
.
inputs
[
'Faces'
].
sv_get
(
default
=
[[]],
deepcopy
=
False
)
masks_s
=
self
.
inputs
[
'VertMask'
].
sv_get
(
default
=
[[
1
]],
deepcopy
=
False
)
iterations_s
=
self
.
inputs
[
'Iterations'
].
sv_get
(
deepcopy
=
False
)
factor_s
=
self
.
inputs
[
'Factor'
].
sv_get
(
deepcopy
=
False
)
input_level
=
get_data_nesting_level
(
vertices_s
)
vertices_s
=
ensure_nesting_level
(
vertices_s
,
4
)
edges_s
=
ensure_nesting_level
(
edges_s
,
4
)
faces_s
=
ensure_nesting_level
(
faces_s
,
4
)
masks_s
=
ensure_nesting_level
(
masks_s
,
3
)
iterations_s
=
ensure_nesting_level
(
iterations_s
,
2
)
factor_s
=
ensure_nesting_level
(
factor_s
,
2
)
nested_output
=
input_level
>
3
verts_out
=
[]
for
params
in
zip_long_repeat
(
vertices_s
,
edges_s
,
faces_s
,
masks_s
,
iterations_s
,
factor_s
):
for
vertices
,
edges
,
faces
,
mask
,
iterations
,
factor
in
zip_long_repeat
(
*
params
):
if
self
.
algorithm
==
'LLOYD'
:
vertices
=
lloyd_relax
(
vertices
,
faces
,
iterations
,
mask
=
mask
,
method
=
self
.
preserve_shape
,
skip_boundary
=
self
.
skip_bounds
)
elif
self
.
algorithm
==
'EDGES'
:
vertices
=
edges_relax
(
vertices
,
edges
,
faces
,
iterations
,
k
=
factor
,
mask
=
mask
,
method
=
self
.
preserve_shape
,
target
=
self
.
target
,
skip_boundary
=
self
.
skip_bounds
)
elif
self
.
algorithm
==
'FACES'
:
vertices
=
faces_relax
(
vertices
,
edges
,
faces
,
iterations
,
k
=
factor
,
mask
=
mask
,
method
=
self
.
preserve_shape
,
target
=
self
.
target
,
skip_boundary
=
self
.
skip_bounds
)
else
:
raise
Exception
(
"Unsupported algorithm"
)
verts_out
.
append
(
vertices
)
self
.
outputs
[
'Vertices'
].
sv_set
(
verts_out
)
def
register
():
bpy
.
utils
.
register_class
(
SvRelaxMeshNode
)
def
unregister
():
bpy
.
utils
.
unregister_class
(
SvRelaxMeshNode
)
nodes/modifier_change/triangulate.py
Просмотр файла @
7f54f40b
...
...
@@ -22,6 +22,7 @@ import bmesh.ops
from
sverchok.node_tree
import
SverchCustomTreeNode
from
sverchok.data_structure
import
updateNode
,
match_long_repeat
,
repeat_last_for_length
from
sverchok.utils.sv_mesh_utils
import
get_unique_faces
from
sverchok.utils.sv_bmesh_utils
import
bmesh_from_pydata
,
pydata_from_bmesh
...
...
@@ -125,6 +126,8 @@ class SvTriangulateNode(bpy.types.Node, SverchCustomTreeNode):
new_face_data
=
[]
bm
.
free
()
new_faces
=
get_unique_faces
(
new_faces
)
result_vertices
.
append
(
new_vertices
)
result_edges
.
append
(
new_edges
)
result_faces
.
append
(
new_faces
)
...
...
utils/relax_mesh.py
0 → 100644
Просмотр файла @
7f54f40b
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
import
numpy
as
np
from
collections
import
defaultdict
from
math
import
sqrt
import
bmesh
import
mathutils
from
mathutils.bvhtree
import
BVHTree
from
sverchok.data_structure
import
repeat_last_for_length
from
sverchok.utils.sv_mesh_utils
import
polygons_to_edges
from
sverchok.utils.sv_bmesh_utils
import
pydata_from_bmesh
,
bmesh_from_pydata
from
sverchok.utils.geom
import
center
,
linear_approximation
NONE
=
'NONE'
BVH
=
'BVH'
LINEAR
=
'LINEAR'
NORMAL
=
'NORMAL'
MINIMUM
=
'MIN'
MAXIMUM
=
'MAX'
AVERAGE
=
'MEAN'
def
lloyd_relax
(
vertices
,
faces
,
iterations
,
mask
=
None
,
method
=
NORMAL
,
skip_boundary
=
True
):
"""
supported shape preservation methods: NORMAL, LINEAR, BVH
"""
def
do_iteration
(
bvh
,
bm
):
verts_out
=
[]
face_centers
=
np
.
array
([
face
.
calc_center_median
()
for
face
in
bm
.
faces
])
for
bm_vert
in
bm
.
verts
:
co
=
bm_vert
.
co
if
(
skip_boundary
and
bm_vert
.
is_boundary
)
or
(
mask
is
not
None
and
not
mask
[
bm_vert
.
index
]):
new_vert
=
tuple
(
co
)
else
:
normal
=
bm_vert
.
normal
cs
=
np
.
array
([
face_centers
[
face
.
index
]
for
face
in
bm_vert
.
link_faces
])
if
method
==
NORMAL
:
median
=
mathutils
.
Vector
(
cs
.
mean
(
axis
=
0
))
dv
=
median
-
co
dv
=
dv
-
dv
.
project
(
normal
)
new_vert
=
co
+
dv
elif
method
==
LINEAR
:
approx
=
linear_approximation
(
cs
)
median
=
mathutils
.
Vector
(
approx
.
center
)
plane
=
approx
.
most_similar_plane
()
dist
=
plane
.
distance_to_point
(
bm_vert
.
co
)
new_vert
=
median
+
plane
.
normal
.
normalized
()
*
dist
elif
method
==
BVH
:
median
=
mathutils
.
Vector
(
cs
.
mean
(
axis
=
0
))
new_vert
,
normal
,
idx
,
dist
=
bvh
.
find_nearest
(
median
)
else
:
raise
Exception
(
"Unsupported volume preservation method"
)
new_vert
=
tuple
(
new_vert
)
verts_out
.
append
(
new_vert
)
return
verts_out
if
mask
is
not
None
:
mask
=
repeat_last_for_length
(
mask
,
len
(
vertices
))
bvh
=
BVHTree
.
FromPolygons
(
vertices
,
faces
)
for
i
in
range
(
iterations
):
bm
=
bmesh_from_pydata
(
vertices
,
[],
faces
,
normal_update
=
True
)
vertices
=
do_iteration
(
bvh
,
bm
)
bm
.
free
()
return
vertices
def
edges_relax
(
vertices
,
edges
,
faces
,
iterations
,
k
,
mask
=
None
,
method
=
NONE
,
target
=
AVERAGE
,
skip_boundary
=
True
):
"""
supported shape preservation methods: NONE, NORMAL, BVH
"""
def
do_iteration
(
bvh
,
bm
,
verts
):
verts
=
np
.
asarray
(
verts
)
v1s
=
verts
[
edges
[:,
0
]]
v2s
=
verts
[
edges
[:,
1
]]
edge_vecs
=
v2s
-
v1s
edge_lens
=
np
.
linalg
.
norm
(
edge_vecs
,
axis
=
1
)
if
target
==
MINIMUM
:
target_len
=
np
.
min
(
edge_lens
)
elif
target
==
MAXIMUM
:
target_len
=
np
.
max
(
edge_lens
)
elif
target
==
AVERAGE
:
target_len
=
np
.
mean
(
edge_lens
)
else
:
raise
Exception
(
"Unsupported target edge length type"
)
forces
=
defaultdict
(
lambda
:
np
.
zeros
((
3
,)))
counts
=
defaultdict
(
int
)
for
edge_idx
,
(
v1_idx
,
v2_idx
)
in
enumerate
(
edges
):
edge_vec
=
edge_vecs
[
edge_idx
]
edge_len
=
edge_lens
[
edge_idx
]
d_len
=
(
edge_len
-
target_len
)
/
2.0
dv1
=
d_len
*
edge_vec
dv2
=
-
d_len
*
edge_vec
forces
[
v1_idx
]
+=
dv1
forces
[
v2_idx
]
+=
dv2
counts
[
v1_idx
]
+=
1
counts
[
v2_idx
]
+=
1
target_verts
=
verts
.
copy
()
for
v_idx
in
range
(
len
(
verts
)):
if
skip_boundary
and
bm
.
verts
[
v_idx
].
is_boundary
:
continue
if
mask
is
not
None
and
not
mask
[
v_idx
]:
continue
count
=
counts
[
v_idx
]
if
count
:
forces
[
v_idx
]
/=
count
target_verts
[
v_idx
]
+=
k
*
forces
[
v_idx
]
if
method
==
NONE
:
verts_out
=
target_verts
.
tolist
()
elif
method
==
NORMAL
:
verts_out
=
[]
for
bm_vert
in
bm
.
verts
:
normal
=
bm_vert
.
normal
dv
=
mathutils
.
Vector
(
target_verts
[
bm_vert
.
index
])
-
bm_vert
.
co
dv
=
dv
-
dv
.
project
(
normal
)
new_vert
=
tuple
(
bm_vert
.
co
+
dv
)
verts_out
.
append
(
new_vert
)
elif
method
==
BVH
:
verts_out
=
[]
for
vert
in
target_verts
:
new_vert
,
normal
,
idx
,
dist
=
bvh
.
find_nearest
(
vert
)
verts_out
.
append
(
tuple
(
new_vert
))
else
:
raise
Exception
(
"Unsupported shape preservation method"
)
return
verts_out
if
not
edges
:
edges
=
polygons_to_edges
([
faces
],
unique_edges
=
True
)[
0
]
edges
=
np
.
array
(
edges
)
if
mask
is
not
None
:
mask
=
repeat_last_for_length
(
mask
,
len
(
vertices
))
bvh
=
BVHTree
.
FromPolygons
(
vertices
,
faces
)
for
i
in
range
(
iterations
):
bm
=
bmesh_from_pydata
(
vertices
,
edges
,
faces
,
normal_update
=
True
)
vertices
=
do_iteration
(
bvh
,
bm
,
vertices
)
bm
.
free
()
return
vertices
def
faces_relax
(
vertices
,
edges
,
faces
,
iterations
,
k
,
mask
=
None
,
method
=
NONE
,
target
=
AVERAGE
,
skip_boundary
=
True
):
"""
supported shape preservation methods: NONE, NORMAL, BVH
"""
def
do_iteration
(
bvh
,
bm
):
areas
=
np
.
array
([
face
.
calc_area
()
for
face
in
bm
.
faces
])
vert_cos
=
np
.
array
([
tuple
(
vert
.
co
)
for
vert
in
bm
.
verts
])
if
target
==
MINIMUM
:
target_area
=
areas
.
min
()
elif
target
==
MAXIMUM
:
target_area
=
areas
.
max
()
elif
target
==
AVERAGE
:
target_area
=
areas
.
mean
()
else
:
raise
Exception
(
"Unsupported target face area type"
)
forces
=
defaultdict
(
lambda
:
np
.
zeros
((
3
,)))
counts
=
defaultdict
(
int
)
for
bm_face
in
bm
.
faces
:
face_vert_idxs
=
[
vert
.
index
for
vert
in
bm_face
.
verts
]
face_verts
=
vert_cos
[
face_vert_idxs
]
mean
=
face_verts
.
mean
(
axis
=
0
)
face_verts_0
=
face_verts
-
mean
src_area
=
areas
[
bm_face
.
index
]
scale
=
sqrt
(
target_area
/
src_area
)
dvs
=
(
scale
-
1
)
*
face_verts_0
for
vert_idx
,
dv
in
zip
(
face_vert_idxs
,
dvs
):
forces
[
vert_idx
]
+=
dv
counts
[
vert_idx
]
+=
1
target_verts
=
vert_cos
.
copy
()
for
bm_vert
in
bm
.
verts
:
idx
=
bm_vert
.
index
if
skip_boundary
and
bm_vert
.
is_boundary
:
continue
if
mask
is
not
None
and
not
mask
[
idx
]:
continue
count
=
counts
[
idx
]
if
count
:
forces
[
idx
]
/=
count
force
=
forces
[
idx
]
target_verts
[
idx
]
+=
k
*
force
if
method
==
NONE
:
verts_out
=
target_verts
.
tolist
()
elif
method
==
NORMAL
:
verts_out
=
[]
for
bm_vert
in
bm
.
verts
:
idx
=
bm_vert
.
index
dv
=
mathutils
.
Vector
(
target_verts
[
idx
])
-
bm_vert
.
co
normal
=
bm_vert
.
normal
dv
=
dv
-
dv
.
project
(
normal
)
new_vert
=
tuple
(
bm_vert
.
co
+
dv
)
verts_out
.
append
(
new_vert
)
elif
method
==
BVH
:
verts_out
=
[]
for
bm_vert
in
bm
.
verts
:
new_vert
,
normal
,
idx
,
dist
=
bvh
.
find_nearest
(
bm_vert
.
co
)
verts_out
.
append
(
tuple
(
new_vert
))
else
:
raise
Exception
(
"Unsupported shape preservation method"
)
return
verts_out
if
mask
is
not
None
:
mask
=
repeat_last_for_length
(
mask
,
len
(
vertices
))
bvh
=
BVHTree
.
FromPolygons
(
vertices
,
faces
)
for
i
in
range
(
iterations
):
bm
=
bmesh_from_pydata
(
vertices
,
edges
,
faces
,
normal_update
=
True
)
vertices
=
do_iteration
(
bvh
,
bm
)
bm
.
free
()
return
vertices
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать