Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bisect Node upgrade #4014

Merged
merged 2 commits into from
Apr 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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