mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
The experimental cutting tool now supports a bisect mode for batch cuts
This commit is contained in:
@@ -125,15 +125,25 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"Will unassign element from a type if type has a representation."
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
mode: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects and tool.Ifc.get()
|
||||
return context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
cutter = context.active_object
|
||||
objs = [o for o in context.selected_objects if o != cutter]
|
||||
|
||||
if not tool.Ifc.get():
|
||||
if self.mode == "BOOLEAN":
|
||||
tool.Misc.boolean_objects_with_cutter(objs, cutter)
|
||||
elif self.mode == "BISECT":
|
||||
tool.Misc.bisect_objects_with_cutter(objs, cutter)
|
||||
for obj in objs:
|
||||
bpy.data.objects.remove(obj)
|
||||
return
|
||||
|
||||
objs_to_cut = []
|
||||
# Splitting only works on meshes
|
||||
for obj in objs:
|
||||
@@ -169,7 +179,11 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
objs_to_cut.append(obj)
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs_to_cut, cutter)
|
||||
if self.mode == "BOOLEAN":
|
||||
new_objs = tool.Misc.boolean_objects_with_cutter(objs_to_cut, cutter)
|
||||
elif self.mode == "BISECT":
|
||||
new_objs = tool.Misc.bisect_objects_with_cutter(objs_to_cut, cutter)
|
||||
|
||||
for obj in new_objs:
|
||||
bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
@@ -39,8 +39,9 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
|
||||
row = layout.split(factor=0.2, align=True)
|
||||
row.prop(props, "total_storeys", text="")
|
||||
row.operator("bim.resize_to_storey").total_storeys = props.total_storeys
|
||||
row = layout.row()
|
||||
row.operator("bim.split_along_edge")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.split_along_edge", text="Split Along Edge").mode = "BOOLEAN"
|
||||
row.operator("bim.split_along_edge", text="Bisect At Faces").mode = "BISECT"
|
||||
row = layout.row()
|
||||
row.operator("bim.get_connected_system_elements")
|
||||
row = layout.row()
|
||||
|
||||
@@ -99,7 +99,7 @@ class Misc(bonsai.core.tool.Misc):
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
|
||||
@classmethod
|
||||
def split_objects_with_cutter(
|
||||
def boolean_objects_with_cutter(
|
||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||
) -> list[bpy.types.Object]:
|
||||
cutter_mesh = cutter.data
|
||||
@@ -144,3 +144,74 @@ class Misc(bonsai.core.tool.Misc):
|
||||
bm.free()
|
||||
bm_flipped.free()
|
||||
return new_objs
|
||||
|
||||
@classmethod
|
||||
def bisect_objects_with_cutter(
|
||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||
) -> list[bpy.types.Object]:
|
||||
cutter_mesh = cutter.data
|
||||
assert isinstance(cutter_mesh, bpy.types.Mesh)
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(cutter_mesh)
|
||||
bm.faces.ensure_lookup_table()
|
||||
|
||||
planes = []
|
||||
for face in bm.faces:
|
||||
no = face.normal.to_4d()
|
||||
no.w = 0.0
|
||||
no = cutter.matrix_world @ no
|
||||
co = cutter.matrix_world @ face.verts[0].co
|
||||
planes.append((co, no))
|
||||
|
||||
bm.free()
|
||||
|
||||
new_objs = []
|
||||
for obj in objs:
|
||||
if not isinstance(obj.data, bpy.types.Mesh) or obj == cutter:
|
||||
continue
|
||||
matrix_i = obj.matrix_world.inverted()
|
||||
bm_obj = bmesh.new()
|
||||
bm_obj.from_mesh(obj.data)
|
||||
|
||||
bms = [bm_obj.copy()]
|
||||
for co, no in planes:
|
||||
co = matrix_i @ co
|
||||
no = (matrix_i @ no).to_3d()
|
||||
new_bms = []
|
||||
for bm in bms:
|
||||
bm1 = bm.copy()
|
||||
bm2 = bm.copy()
|
||||
geom = bm1.verts[:] + bm1.edges[:] + bm1.faces[:]
|
||||
bisect1 = bmesh.ops.bisect_plane(
|
||||
bm1, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_inner=True
|
||||
)
|
||||
if not bisect1["geom"] or not [g for g in bisect1["geom"] if isinstance(g, bmesh.types.BMFace)]:
|
||||
new_bms.append(bm)
|
||||
continue
|
||||
edges = [g for g in bisect1["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||
bmesh.ops.triangle_fill(bm1, use_dissolve=True, edges=edges)
|
||||
geom = bm2.verts[:] + bm2.edges[:] + bm2.faces[:]
|
||||
bisect2 = bmesh.ops.bisect_plane(
|
||||
bm2, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_outer=True
|
||||
)
|
||||
if not bisect2["geom"] or not [g for g in bisect2["geom"] if isinstance(g, bmesh.types.BMFace)]:
|
||||
new_bms.append(bm)
|
||||
continue
|
||||
edges = [g for g in bisect2["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||
bmesh.ops.triangle_fill(bm2, use_dissolve=True, edges=edges)
|
||||
|
||||
new_bms.append(bm1)
|
||||
new_bms.append(bm2)
|
||||
bms = new_bms
|
||||
|
||||
for bm in bms:
|
||||
mesh = obj.data
|
||||
new_obj = obj.copy()
|
||||
new_obj.data = mesh.copy()
|
||||
bm.to_mesh(new_obj.data)
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
new_objs.append(new_obj)
|
||||
|
||||
return new_objs
|
||||
|
||||
Reference in New Issue
Block a user