Skip to content

Commit

Permalink
Update node "Voronoi on Solid":
Browse files Browse the repository at this point in the history
- update using Voronoi. Get new cells for border cells.
- update parameter inset (now it use distance. Before was percent.)
  • Loading branch information
satabol committed Aug 13, 2023
1 parent 6278893 commit f03eb72
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 4 deletions.
2 changes: 1 addition & 1 deletion docs/nodes/spatial/populate_solid.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,5 @@ Example of "Radius Field" mode usage:
* Generator-> :doc:`IcoSphere </nodes/generator/icosphere>`
* Fields-> :doc:`Scalar Field Formula </nodes/field/scalar_field_formula>`
* Solids-> :doc:`Cylinder (Solid) </nodes/solid/cylinder_solid>`
* Solids-> :doc:`Voronoi on Solid </nodes/spatial/voronoi_on_solid>`
* Solids-> :doc:`Voronoi on Solid </nodes/spatial/voronoi_on_solid_mk2>`
* Viz-> :doc:`Viewer Draw </nodes/viz/viewer_draw_mk4>`
File renamed without changes.
2 changes: 1 addition & 1 deletion index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnSolidNode
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
- SvLloyd3dNode
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 @@ -507,7 +507,7 @@
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnSolidNode
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
- SvLloyd3dNode
Expand Down
2 changes: 1 addition & 1 deletion menus/full_nortikin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@
- SvExVoronoiSphereNode
- SvVoronoiOnSurfaceNode
- SvVoronoiOnMeshNode
- SvVoronoiOnSolidNode
- SvVoronoiOnSolidNodeMK2
- ---
- SvLloyd2dNode
- SvLloyd3dNode
Expand Down
202 changes: 202 additions & 0 deletions nodes/spatial/voronoi_on_solid_mk2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# ##### 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 itertools
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, repeat_last_for_length
from sverchok.utils.voronoi3d import voronoi_on_mesh_bmesh
from sverchok.utils.geom import scale_relative, center, diameter
from sverchok.utils.solid import BMESH, svmesh_to_solid
from sverchok.dependencies import FreeCAD

if FreeCAD is not None:
import Part


class SvVoronoiOnSolidNodeMK2(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Voronoi Solid
Tooltip: Generate Voronoi diagram on the Solid object
"""
bl_idname = 'SvVoronoiOnSolidNodeMK2'
bl_label = 'Voronoi on Solid'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_VORONOI'
sv_dependencies = {'scipy', 'FreeCAD'}

modes = [
('SURFACE', "Surface", "Generate regions of Voronoi diagram on the surface of the solid", 0),
('VOLUME', "Volume", "Split volume of the solid body into regions of Voronoi diagram", 2)
]

mode : EnumProperty(
name = "Mode",
items = modes,
update = updateNode)

accuracy : IntProperty(
name = "Accuracy",
description = "Accuracy for mesh to solid transformation",
default = 6,
min = 1,
update = updateNode)

inset : FloatProperty(
name = "Inset",
min = 0.0, #max = 1.0,
default = 0.1,
description="Distance to leave between generated Voronoi regions",
update = updateNode)

scale_types = [
('SITE', "Site", "Scale each region relative to corresponding site location", 0),
('MEAN', "Barycenter", "Scale each region relative to it's barycenter, i.e. average location of it's vertices", 1)
]

scale_center : EnumProperty(
name = "Scale around",
description = "Defines the center, along which the regions of Voronoi diagram are to be scaled in order to make inset",
items = scale_types,
default = 'SITE',
update = updateNode)

flat_output : BoolProperty(
name = "Flat output",
description = "If checked, output single flat list of fragments for all input solids; otherwise, output a separate list of fragments for each solid.",
default = True,
update = updateNode)

def sv_init(self, context):
self.inputs.new('SvSolidSocket', 'Solid')
self.inputs.new('SvVerticesSocket', "Sites")
self.inputs.new('SvStringsSocket', "Inset").prop_name = 'inset'
self.outputs.new('SvSolidSocket', "InnerSolid")
self.outputs.new('SvSolidSocket', "OuterSolid")

def draw_buttons(self, context, layout):
layout.prop(self, "mode")
layout.prop(self, "flat_output")

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

def scale_cells(self, verts, sites, insets, precision):
if all(i == 0.0 for i in insets):
return verts
verts_out = []
for vs, site, inset in zip(verts, sites, insets):
if inset >= 1.0:
continue
if self.scale_center == 'SITE':
c = site
else:
c = center(vs)
vs1 = scale_relative(vs, c, 1.0 - inset)
if diameter(vs1, axis=None) <= precision:
continue
verts_out.append(vs1)
return verts_out

def process(self):

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

solid_in = self.inputs['Solid'].sv_get()
sites_in = self.inputs['Sites'].sv_get()
inset_in = self.inputs['Inset'].sv_get()

solid_in = ensure_nesting_level(solid_in, 2, data_types=(Part.Shape,))
input_level = get_data_nesting_level(sites_in)
sites_in = ensure_nesting_level(sites_in, 4)
inset_in = ensure_min_nesting(inset_in, 2)

nested_output = input_level > 3
need_inner = self.outputs['InnerSolid'].is_linked
need_outer = self.outputs['OuterSolid'].is_linked

precision = 10 ** (-self.accuracy)

inner_fragments_out = []
outer_fragments_out = []
for params in zip_long_repeat(solid_in, sites_in, inset_in):
new_inner_fragments = []
new_outer_fragments = []
for solid, sites, inset in zip_long_repeat(*params):
#verts, edges, faces = voronoi_on_solid(solid, sites, do_clip=True, clipping=None)
box = solid.BoundBox
clipping = 1
x_min, x_max = box.XMin - clipping, box.XMax + clipping
y_min, y_max = box.YMin - clipping, box.YMax + clipping
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]))
verts, edges, faces = voronoi_on_mesh_bmesh(bounds, [ [0,1,3,2], [2,3,7,6], [6,7,5,4], [4,5,1,0], [2,6,4,0], [7,3,1,5] ], len(sites), sites, spacing=inset, mode='VOLUME' )

if isinstance(inset, list):
inset = repeat_last_for_length(inset, len(sites))
else:
inset = [inset for i in range(len(sites))]
#verts = self.scale_cells(verts, sites, inset, precision)
fragments = [svmesh_to_solid(vs, fs, precision, method=BMESH, remove_splitter=False) for vs, fs in zip(verts, faces)]

if self.mode == 'SURFACE':
if solid.Shells:
shell = solid.Shells[0]
else:
shell = Part.Shell(solid.Faces)
src = shell
else: # VOLUME
src = solid

if need_inner:
inner = [src.common(fragments)]
if self.flat_output:
new_inner_fragments.extend(inner)
else:
new_inner_fragments.append(inner)

if need_outer:
outer = [src.cut(fragments)]
if self.flat_output:
new_outer_fragments.extend(outer)
else:
new_outer_fragments.append(outer)

if nested_output:
inner_fragments_out.append(new_inner_fragments)
outer_fragments_out.append(new_outer_fragments)
else:
inner_fragments_out.extend(new_inner_fragments)
outer_fragments_out.extend(new_outer_fragments)

self.outputs['InnerSolid'].sv_set(inner_fragments_out)
self.outputs['OuterSolid'].sv_set(outer_fragments_out)


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


def unregister():
bpy.utils.unregister_class(SvVoronoiOnSolidNodeMK2)
File renamed without changes.

0 comments on commit f03eb72

Please sign in to comment.