mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +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."
|
"Will unassign element from a type if type has a representation."
|
||||||
)
|
)
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
mode: bpy.props.StringProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.selected_objects and tool.Ifc.get()
|
return context.selected_objects
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
cutter = context.active_object
|
cutter = context.active_object
|
||||||
objs = [o for o in context.selected_objects if o != cutter]
|
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 = []
|
objs_to_cut = []
|
||||||
# Splitting only works on meshes
|
# Splitting only works on meshes
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
@@ -169,7 +179,11 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
objs_to_cut.append(obj)
|
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:
|
for obj in new_objs:
|
||||||
bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||||
bpy.ops.bim.update_representation(obj=obj.name)
|
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 = layout.split(factor=0.2, align=True)
|
||||||
row.prop(props, "total_storeys", text="")
|
row.prop(props, "total_storeys", text="")
|
||||||
row.operator("bim.resize_to_storey").total_storeys = props.total_storeys
|
row.operator("bim.resize_to_storey").total_storeys = props.total_storeys
|
||||||
row = layout.row()
|
row = layout.row(align=True)
|
||||||
row.operator("bim.split_along_edge")
|
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 = layout.row()
|
||||||
row.operator("bim.get_connected_system_elements")
|
row.operator("bim.get_connected_system_elements")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def split_objects_with_cutter(
|
def boolean_objects_with_cutter(
|
||||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||||
) -> list[bpy.types.Object]:
|
) -> list[bpy.types.Object]:
|
||||||
cutter_mesh = cutter.data
|
cutter_mesh = cutter.data
|
||||||
@@ -144,3 +144,74 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
bm.free()
|
bm.free()
|
||||||
bm_flipped.free()
|
bm_flipped.free()
|
||||||
return new_objs
|
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