Skip to content

Commit

Permalink
Bisect Node upgrade (#4014)
Browse files Browse the repository at this point in the history
* Bisect Node upgrade

* restore list match items
  • Loading branch information
vicdoval authored Apr 3, 2021
1 parent 0da4d16 commit bf9612d
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 92 deletions.
2 changes: 1 addition & 1 deletion data_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ def levels_of_list_or_np(lst):
return level
return 0

SIMPLE_DATA_TYPES = (float, int, float64, int32, int64, str)
SIMPLE_DATA_TYPES = (float, int, float64, int32, int64, str, Matrix)


def get_data_nesting_level(data, data_types=SIMPLE_DATA_TYPES):
Expand Down
38 changes: 26 additions & 12 deletions docs/nodes/modifier_make/bisect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Functionality
This can give the cross section of an object shape from any angle. The implementation is from ``bmesh.ops.bisect_plane``. It can also provide either side of the cut, separate or joined.


Inputs
Inputs
------

*Vertices*, *PolyEdges* and *Matrix*
Expand All @@ -16,20 +16,35 @@ Inputs
Parameters
----------

+-------------+------+---------------------------------------------------+
| Parameter | Type | Description |
+=============+======+===================================================+
| Clear Inner | bool | don't include the negative side of the Matrix cut |
+-------------+------+---------------------------------------------------+
| Clear Outer | bool | don't include the positive side of the Matrix cut |
+-------------+------+---------------------------------------------------+
| Fill cuts | bool | generates a polygon from the bisections |
+-------------+------+---------------------------------------------------+
+-------------+------+-----------------------------------------------------+
| Parameter | Type | Description |
+=============+======+=====================================================+
| Clear Inner | bool | don't include the negative side of the Matrix cut |
+-------------+------+-----------------------------------------------------+
| Clear Outer | bool | don't include the positive side of the Matrix cut |
+-------------+------+-----------------------------------------------------+
| Fill cuts | bool | generates a polygon from the bisections |
+-------------+------+-----------------------------------------------------+
| Per Object | bool | One matrix per mesh or multiple matrixes per object |
+-------------+------+-----------------------------------------------------+

Advanced Parameters
-------------------

In the N-Panel (and on the right-click menu) you can find:

**Simplify Output**: Method to keep output data suitable for most of the rest of the Sverchok nodes
- None: Do not perform any change on the data. Only for advanced users
- Join: The node will join the deepest level of bisections in one object
- Flat: It will flat the output to keep the one bisection per object (default)

**Match List Global**: Define how list with different lengths should be matched. Refers to the matching of groups (level 1)


Outputs
-------

*Vertices*, *Edges*, and *Polygons*.
*Vertices*, *Edges*, and *Polygons*.



Expand All @@ -44,4 +59,3 @@ Examples

Notes
-----

174 changes: 110 additions & 64 deletions nodes/modifier_make/bisect.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,22 @@
# ##### END GPL LICENSE BLOCK #####

import bpy
from bpy.props import BoolProperty
from bpy.props import BoolProperty, IntVectorProperty
import bmesh
from mathutils import Vector, Matrix

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, Matrix_generate, Vector_generate

from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode
from sverchok.utils.mesh_functions import mesh_join
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata, pydata_from_bmesh

# based on CrossSectionNode
# but using python bmesh code for driving
# by Linus Yng / edits+upgrades Dealga McArdle

# by Linus Yng / edits+upgrades Dealga McArdle and Victor Doval

def bisect(cut_me_vertices, cut_me_edges, pp, pno, outer, inner, fill):
def bisect_bmesh(bm, pp, pno, outer, inner, fill):

if not cut_me_edges or not cut_me_vertices:
return False

cut_me_polygons = []
if len(cut_me_edges[0]) > 2:
cut_me_polygons = cut_me_edges.copy()
cut_me_edges = []

bm = bmesh.new()
new_vert = bm.verts.new
new_edge = bm.edges.new
new_face = bm.faces.new
bm_verts = [new_vert(v) for v in cut_me_vertices]
if cut_me_edges:
for edge in cut_me_edges:
new_edge((bm_verts[edge[0]], bm_verts[edge[1]]))
else:
for face in cut_me_polygons:
new_face([bm_verts[i] for i in face])

geom_in = bm.verts[:] + bm.edges[:] + bm.faces[:]
res = bmesh.ops.bisect_plane(
Expand All @@ -70,26 +52,37 @@ def bisect(cut_me_vertices, cut_me_edges, pp, pno, outer, inner, fill):
bm.verts.index_update()
bm.edges.index_update()
bm.faces.index_update()

for edge in bm.edges[:]:
edges.append([v.index for v in edge.verts[:]])
verts = [vert.co[:] for vert in bm.verts[:]]
for face in bm.faces:
faces.append([v.index for v in face.verts[:]])
verts, edges, faces = pydata_from_bmesh(bm)

bm.clear()
bm.free()

return (verts, edges, faces)

def bisect(cut_me_vertices, cut_me_edges, pp, pno, outer, inner, fill):

class SvBisectNode(bpy.types.Node, SverchCustomTreeNode):
if not cut_me_edges or not cut_me_vertices:
return False

if len(cut_me_edges[0]) > 2:
bm = bmesh_from_pydata(cut_me_vertices, [], cut_me_edges)
else:
bm = bmesh_from_pydata(cut_me_vertices, cut_me_edges, [])

return bisect_bmesh(bm, pp, pno, outer, inner, fill)



class SvBisectNode(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode):
''' Matrix Cuts geometry'''
bl_idname = 'SvBisectNode'
bl_label = 'Bisect'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_BISECT'

build_bmesh = True
bmesh_inputs = [0, 1]

inner: BoolProperty(
name='inner', description='clear inner',
default=False, update=updateNode)
Expand All @@ -106,6 +99,25 @@ class SvBisectNode(bpy.types.Node, SverchCustomTreeNode):
name="Per Object", update=updateNode, default=False,
description="slice each object with all matrices, or match object and matrices individually")

slice_mode: BoolProperty(
name="Per Object", update=updateNode, default=False,
description="slice each object with all matrices, or match object and matrices individually")

remove_empty: BoolProperty(
name="Clean Output", update=updateNode, default=False,
description="Remove empty objects from output")

correct_output_modes = [
('NONE', 'None', 'Leave at multi-object level (Advanced)', 0),
('JOIN', 'Join', 'Join (mesh join) last level of objects', 1),
('FLAT', 'Flat Output', 'Flat to object level', 2),
]
correct_output: bpy.props.EnumProperty(
name="Simplify Output",
description="Behavior on different list lengths, object level",
items=correct_output_modes, default="FLAT",
update=updateNode)

def sv_init(self, context):
self.inputs.new('SvVerticesSocket', 'vertices')
self.inputs.new('SvStringsSocket', 'edg_pol')
Expand All @@ -116,62 +128,96 @@ def sv_init(self, context):
self.outputs.new('SvStringsSocket', 'polygons')

def draw_buttons(self, context, layout):
row = layout.row(align=True)
col = layout.column(align=True)
col.label(text='Remove:')
row = col.row(align=True)
row.prop(self, 'inner', text="Inner", toggle=True)
row.prop(self, 'outer', text="Outer", toggle=True)
row = layout.row(align=True)
row.prop(self, 'fill', text="Fill", toggle=True)
if hasattr(self, 'slice_mode'):
row.prop(self, 'slice_mode', toggle=True)
layout.prop(self, 'remove_empty')

def process(self):
row.prop(self, 'slice_mode', toggle=True)

if not all([s.is_linked for s in self.inputs[:2]]):
return
def draw_buttons_ext(self, context, layout):
self.draw_buttons(context, layout)
layout.prop(self, 'correct_output')
layout.prop(self, 'list_match')

if not self.outputs['vertices'].is_linked:
return
def rclick_menu(self, context, layout):
layout.prop_menu_enum(self, "list_match", text="List Match")
if not self.slice_mode:
layout.prop_menu_enum(self, 'correct_output')

verts_ob = Vector_generate(self.inputs['vertices'].sv_get())
edg_pols = self.inputs['edg_pol'].sv_get()
cut_mats = self.inputs['cut_matrix'].sv_get(default=[Matrix()])
verts_out = []
edges_out = []
polys_out = []
def pre_setup(self):
self.inputs['vertices'].is_mandatory = True
self.inputs['edg_pol'].is_mandatory = True

if not hasattr(self, 'slice_mode') or not self.slice_mode:
if self.slice_mode:
self.inputs['cut_matrix'].nesting_level = 1
else:
self.inputs['cut_matrix'].nesting_level = 2

for cut_mat in cut_mats:
pp = cut_mat.to_translation()
pno = Vector((0.0, 0.0, 1.0)) @ cut_mat.to_3x3().transposed()
for obj in zip(verts_ob, edg_pols):
res = bisect(obj[0], obj[1], pp, pno, self.outer, self.inner, self.fill)
if not res:
return
verts_out.append(res[0])
edges_out.append(res[1])
polys_out.append(res[2])
self.inputs['cut_matrix'].default_mode = 'MATRIX'

else:

for idx, (obj) in enumerate(zip(verts_ob, edg_pols)):
def process_data(self, params):

cut_mat = cut_mats[idx if idx < len(cut_mats) else -1]
verts_out = []
edges_out = []
polys_out = []
if self.slice_mode:
bms, cut_mats = params
for cut_mat, bm in zip(cut_mats, bms):
pp = cut_mat.to_translation()
pno = Vector((0.0, 0.0, 1.0)) @ cut_mat.to_3x3().transposed()

res = bisect(obj[0], obj[1], pp, pno, self.outer, self.inner, self.fill)
res = bisect_bmesh(bm.copy(), pp, pno, self.outer, self.inner, self.fill)
if not res:
return
if self.remove_empty:
if not res[0]:
continue
verts_out.append(res[0])
edges_out.append(res[1])
polys_out.append(res[2])
else:
bms, cut_mats_s = params
for cut_mats, bm in zip(cut_mats_s, bms):
vs, es, ps = [], [], []
for cut_mat in cut_mats:
pp = cut_mat.to_translation()
pno = Vector((0.0, 0.0, 1.0)) @ cut_mat.to_3x3().transposed()
res = bisect_bmesh(bm.copy(), pp, pno, self.outer, self.inner, self.fill)
if not res:
return
if self.remove_empty:
if not res[0]:
continue
if self.correct_output == 'FLAT':
verts_out.append(res[0])
edges_out.append(res[1])
polys_out.append(res[2])
else:
vs.append(res[0])
es.append(res[1])
ps.append(res[2])

if self.correct_output == 'NONE':
verts_out.append(vs)
edges_out.append(es)
polys_out.append(ps)
elif self.correct_output == 'JOIN':
r = mesh_join(vs, es, ps)
verts_out.extend(r[0])
edges_out.extend(r[1])
polys_out.extend(r[2])



return verts_out, edges_out, polys_out


self.outputs['vertices'].sv_set(verts_out)
self.outputs['edges'].sv_set(edges_out)
self.outputs['polygons'].sv_set(polys_out)


def register():
bpy.utils.register_class(SvBisectNode)
Expand Down
12 changes: 12 additions & 0 deletions utils/mesh_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,15 @@ def repeat_meshes(meshes: Iterator[Mesh], number: int = -1) -> Iterator[Mesh]:

def apply_matrix_to_vertices_py(vertices: List[Vertex], matrix: Matrix) -> List[Vertex]:
return [(matrix @ Vector(v)).to_tuple() for v in vertices]

def mesh_join(vertices: List[List[Vertex]],
edges: List[List[Edge]],
polygons: List[List[Polygon]]) -> Tuple[List[List[Vertex]],
List[List[Edge]],
List[List[Polygon]]]:
is_py_input = isinstance(vertices[0], (list, tuple))
meshes = (meshes_py if is_py_input else meshes_np)(vertices, edges, polygons)
meshes = join_meshes(meshes)
out_vertices, out_edges, out_polygons = to_elements(meshes)

return out_vertices, out_edges, out_polygons
Loading

0 comments on commit bf9612d

Please sign in to comment.