Skip to content

Commit

Permalink
Merge pull request #4980 from nortikin/fix_4979_add_output_socket_Sit…
Browse files Browse the repository at this point in the history
…es_for_node_Voronoi_on_Mesh

Calculation of used indices sites for node "Voronoi on Mesh".
  • Loading branch information
satabol authored Aug 15, 2023
2 parents 90c470e + 4eaf654 commit 3fdff8e
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 11 deletions.
2 changes: 1 addition & 1 deletion docs/nodes/pulga_physics/pulga_springs_force.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Example in description:

* Generator-> :doc:`Cylinder </nodes/generator/cylinder_mk2>`
* Spatial-> :doc:`Populate Mesh </nodes/spatial/random_points_on_mesh>`
* Spatial-> :doc:`Voronoi on Mesh </nodes/spatial/voronoi_on_mesh>`
* Spatial-> :doc:`Voronoi on Mesh </nodes/spatial/voronoi_on_mesh_mk2>`
* Modifiers->Modifier Change-> :doc:`Mesh Join </nodes/modifier_change/mesh_join_mk2>`
* Modifiers->Modifier Change-> :doc:`Merge by Distance </nodes/modifier_change/merge_by_distance>`
* Analyzers->Component Analyzer **Vertices->Sharpness**: :ref:`Vertices Sharpness<VERTICES_SHARPNESS>`
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
- SvExVoronoi3DNode
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnMeshNodeMK2
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
Expand Down
2 changes: 1 addition & 1 deletion menus/full_by_data_type.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@
- SvExVoronoi3DNode
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnMeshNodeMK2
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
Expand Down
2 changes: 1 addition & 1 deletion menus/full_nortikin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@
- SvExVoronoi3DNode
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnMeshNodeMK2
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
Expand Down
193 changes: 193 additions & 0 deletions nodes/spatial/voronoi_on_mesh_mk2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# ##### 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 FloatProperty, EnumProperty, BoolProperty, IntProperty

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level,\
ensure_min_nesting
from sverchok.utils.sv_bmesh_utils import recalc_normals
from sverchok.utils.sv_mesh_utils import mesh_join
from sverchok.utils.voronoi3d import voronoi_on_mesh


class SvVoronoiOnMeshNodeMK2(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Voronoi Mesh
Tooltip: Generate Voronoi diagram on the surface of a mesh object
"""
bl_idname = 'SvVoronoiOnMeshNodeMK2'
bl_label = 'Voronoi on Mesh'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_VORONOI'
sv_dependencies = {'scipy'}

modes = [
('VOLUME', "Split Volume", "Split volume of the mesh into regions of Voronoi diagram", 0),
('SURFACE', "Split Surface", "Split the surface of the mesh into regions of Vornoi diagram", 1),
#('RIDGES', "Ridges near Surface", "Generate ridges of 3D Voronoi diagram near the surface of the mesh", 2),
#('REGIONS', "Regions near Surface", "Generate regions of 3D Voronoi diagram near the surface of the mesh", 3)
]

spacing : FloatProperty(
name = "Spacing",
default = 0.0,
min = 0.0,
description="Percent of space to leave between generated fragment meshes",
update=updateNode)

normals : BoolProperty(
name = "Correct normals",
default = True,
description="Make sure that all normals of generated meshes point outside",
update = updateNode)

def update_sockets(self, context):
self.inputs['Spacing'].hide_safe = self.mode not in {'VOLUME', 'SURFACE'}
updateNode(self, context)

mode : EnumProperty(
name = "Mode",
items = modes,
default = 'VOLUME',
update = update_sockets)

join_modes = [
('FLAT', "Flat list", "Output a single flat list of mesh objects (Voronoi diagram ridges / regions) for all input meshes", 0),
('SEPARATE', "Separate lists", "Output a separate list of mesh objects (Voronoi diagram ridges / regions) for each input mesh", 1),
('JOIN', "Join meshes", "Output one mesh, joined from ridges / edges of Voronoi diagram, for each input mesh", 2)
]

join_mode : EnumProperty(
name = "Output mode",
items = join_modes,
default = 'FLAT',
update = updateNode)

accuracy : IntProperty(
name = "Accuracy",
description = "Accuracy for mesh bisecting procedure",
default = 6,
min = 1,
update = updateNode)

def sv_init(self, context):
self.inputs.new('SvVerticesSocket', 'Vertices')
self.inputs.new('SvStringsSocket', 'Faces')
self.inputs.new('SvVerticesSocket', "Sites")
# self.inputs.new('SvStringsSocket', 'Thickness').prop_name = 'thickness'
self.inputs.new('SvStringsSocket', 'Spacing').prop_name = 'spacing'
self.outputs.new('SvVerticesSocket', "Vertices")
self.outputs.new('SvStringsSocket', "Edges")
self.outputs.new('SvStringsSocket', "Faces")
self.outputs.new('SvStringsSocket', "Sites_idx")
self.update_sockets(context)

def draw_buttons(self, context, layout):
layout.label(text="Mode:")
layout.prop(self, "mode", text='')
if self.mode == 'VOLUME':
layout.prop(self, 'normals')
layout.label(text='Output nesting:')
layout.prop(self, 'join_mode', text='')

def draw_buttons_ext(self, context, layout):
self.draw_buttons(context, layout)
layout.prop(self, 'accuracy')

def process(self):

if not any(socket.is_linked for socket in self.outputs):
return

verts_in = self.inputs['Vertices'].sv_get(deepcopy=False)
faces_in = self.inputs['Faces'].sv_get(deepcopy=False)
sites_in = self.inputs['Sites'].sv_get(deepcopy=False)
#thickness_in = self.inputs['Thickness'].sv_get()
spacing_in = self.inputs['Spacing'].sv_get(deepcopy=False)

verts_in = ensure_nesting_level(verts_in, 4)
input_level = get_data_nesting_level(sites_in)
sites_in = ensure_nesting_level(sites_in, 4)
faces_in = ensure_nesting_level(faces_in, 4)
#thickness_in = ensure_nesting_level(thickness_in, 2)
spacing_in = ensure_min_nesting(spacing_in, 2)

nested_output = input_level > 3

precision = 10 ** (-self.accuracy)

verts_out = []
edges_out = []
faces_out = []
sites_idx_out = []
for params in zip_long_repeat(verts_in, faces_in, sites_in, spacing_in):
new_verts = []
new_edges = []
new_faces = []
new_sites = []
for verts, faces, sites, spacing in zip_long_repeat(*params):
verts, edges, faces, used_sites_idx = voronoi_on_mesh(verts, faces, sites, thickness=0,
spacing = spacing,
#clip_inner = self.clip_inner, clip_outer = self.clip_outer,
do_clip=True, clipping=None,
mode = self.mode,
normal_update = self.normals,
precision = precision)

if self.join_mode == 'FLAT':
new_verts.extend(verts)
new_edges.extend(edges)
new_faces.extend(faces)
new_sites.extend([[idx] for idx in used_sites_idx])
elif self.join_mode == 'SEPARATE':
new_verts.append(verts)
new_edges.append(edges)
new_faces.append(faces)
new_sites.append(used_sites_idx)
else: # JOIN
verts, edges, faces = mesh_join(verts, edges, faces)
new_verts.append(verts)
new_edges.append(edges)
new_faces.append(faces)
new_sites.append(used_sites_idx)

if nested_output:
verts_out.append(new_verts)
edges_out.append(new_edges)
faces_out.append(new_faces)
sites_idx_out.append(new_sites)
else:
verts_out.extend(new_verts)
edges_out.extend(new_edges)
faces_out.extend(new_faces)
sites_idx_out.extend(new_sites)

self.outputs['Vertices'].sv_set(verts_out)
self.outputs['Edges'].sv_set(edges_out)
self.outputs['Faces'].sv_set(faces_out)
self.outputs['Sites_idx'].sv_set(sites_idx_out)


def register():
bpy.utils.register_class(SvVoronoiOnMeshNodeMK2)


def unregister():
bpy.utils.unregister_class(SvVoronoiOnMeshNodeMK2)
2 changes: 1 addition & 1 deletion nodes/spatial/voronoi_on_solid_mk2.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def process(self):
z_min, z_max = box.ZMin - clipping, box.ZMax + clipping
bounds = list(itertools.product([x_min,x_max], [y_min, y_max], [z_min, z_max]))
bounds_box_faces = [ [0,1,3,2], [2,3,7,6], [6,7,5,4], [4,5,1,0], [2,6,4,0], [7,3,1,5] ] # cube's faces
verts, edges, faces = voronoi_on_mesh_bmesh(bounds, bounds_box_faces, len(sites), sites, spacing=inset, mode='VOLUME' )
verts, edges, faces, used_sites_idx = voronoi_on_mesh_bmesh(bounds, bounds_box_faces, len(sites), sites, spacing=inset, mode='VOLUME' )

if isinstance(inset, list):
inset = repeat_last_for_length(inset, len(sites))
Expand Down
2 changes: 1 addition & 1 deletion nodes/spatial/voronoi_on_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def process(self):
uvverts, verts, edges, faces = self.voronoi_uv(surface, uvpoints, maxsides)
new_uvverts.append(uvverts)
else:
verts, edges, faces = voronoi_on_surface(surface, uvpoints, thickness, self.do_clip, clipping, self.mode == 'REGIONS')
verts, edges, faces, used_sites = voronoi_on_surface(surface, uvpoints, thickness, self.do_clip, clipping, self.mode == 'REGIONS')

if (self.mode in {'RIDGES', 'REGIONS'} or self.make_faces) and self.normals:
verts, edges, faces = recalc_normals(verts, edges, faces, loop = (self.mode in {'REGIONS', 'RIDGES'}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,4 @@ def register():


def unregister():
bpy.utils.unregister_class(SvVoronoiOnMeshNode)
bpy.utils.unregister_class(SvVoronoiOnMeshNode)
10 changes: 6 additions & 4 deletions utils/voronoi3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def voronoi3d_layer(n_src_sites, all_sites, make_regions, do_clip, clipping, ski
faces_n.append(faces_i)
verts, edges, faces = verts_n, edges_n, faces_n

return verts, edges, faces
return verts, edges, faces, all_sites

def voronoi_on_surface(surface, uvpoints, thickness, do_clip, clipping, make_regions):
u_min, u_max, v_min, v_max = surface.get_domain()
Expand Down Expand Up @@ -418,6 +418,7 @@ def cut_cell(start_mesh, sites_delaunay_params, site_idx, spacing, center_of_mas
bbox_aligned = bounding_box_aligned(verts)[0]

start_mesh = bmesh_from_pydata(verts, [], faces, normal_update=False)
used_sites_idx = []
for site_idx in range(len(sites)):
cell = cut_cell(start_mesh, sites_delaunay_params, site_idx, spacing[site_idx], center_of_mass, bbox_aligned)
if cell is not None:
Expand All @@ -426,6 +427,7 @@ def cut_cell(start_mesh, sites_delaunay_params, site_idx, spacing, center_of_mas
verts_out.append(new_verts)
edges_out.append(new_edges)
faces_out.append(new_faces)
used_sites_idx.append( site_idx )
start_mesh.clear() # remember to clear empty geometry
start_mesh.free()

Expand All @@ -435,7 +437,7 @@ def cut_cell(start_mesh, sites_delaunay_params, site_idx, spacing, center_of_mas
# unb - unpredicted erased mesh (bbox_aligned cannot make predicted results)
# sites - count of sites in process
# print( f"bisects: {num_bisect: 4d}, unb={num_unpredicted_erased: 4d}, sites={len(sites)}")
return verts_out, edges_out, faces_out
return verts_out, edges_out, faces_out, used_sites_idx

def voronoi_on_mesh(verts, faces, sites, thickness,
spacing = 0.0,
Expand Down Expand Up @@ -469,10 +471,10 @@ def voronoi_on_mesh(verts, faces, sites, thickness,

else: # VOLUME, SURFACE
all_points = sites[:]
verts, edges, faces = voronoi_on_mesh_bmesh(verts, faces, len(sites), all_points,
verts, edges, faces, used_sites_idx = voronoi_on_mesh_bmesh(verts, faces, len(sites), all_points,
spacing = spacing, mode = mode, normal_update = normal_update,
precision = precision)
return verts, edges, faces, all_points
return verts, edges, faces, used_sites_idx

def project_solid_normals(shell, pts, thickness, add_plus=True, add_minus=True, predicate_plus=None, predicate_minus=None):
k = 0.5*thickness
Expand Down

0 comments on commit 3fdff8e

Please sign in to comment.