mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Basic support for deleting items, and editing meshlike items
This commit is contained in:
@@ -1370,46 +1370,7 @@ class IfcImporter:
|
||||
verts = geometry.verts
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
if geometry.faces:
|
||||
num_vertices = len(verts) // 3
|
||||
total_faces = len(geometry.faces)
|
||||
loop_start = range(0, total_faces, 3)
|
||||
num_loops = total_faces // 3
|
||||
loop_total = [3] * num_loops
|
||||
num_vertex_indices = len(geometry.faces)
|
||||
|
||||
# See bug 3546
|
||||
# ios_edges holds true edges that aren't triangulated.
|
||||
#
|
||||
# we do `.tolist()` because Blender can't assign `np.int32` to it's custom attributes
|
||||
mesh["ios_edges"] = list(set(tuple(e) for e in ifcopenshell.util.shape.get_edges(geometry).tolist()))
|
||||
mesh["ios_item_ids"] = ifcopenshell.util.shape.get_representation_item_ids(geometry).tolist()
|
||||
|
||||
mesh.vertices.add(num_vertices)
|
||||
mesh.vertices.foreach_set("co", verts)
|
||||
mesh.loops.add(num_vertex_indices)
|
||||
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
||||
mesh.polygons.add(num_loops)
|
||||
mesh.polygons.foreach_set("loop_start", loop_start)
|
||||
mesh.polygons.foreach_set("loop_total", loop_total)
|
||||
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
|
||||
mesh.update()
|
||||
|
||||
rep_str: str = geometry.id
|
||||
if "openings" not in rep_str:
|
||||
rep_id = rep_str.split("-", 1)[0]
|
||||
rep = self.file.by_id(int(rep_id))
|
||||
tool.Loader.load_indexed_colour_map(rep, mesh)
|
||||
else:
|
||||
e = geometry.edges
|
||||
v = verts
|
||||
vertices = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)]
|
||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||
mesh.from_pydata(vertices, edges, [])
|
||||
|
||||
mesh["ios_materials"] = [m.instance_id() for m in geometry.materials]
|
||||
mesh["ios_material_ids"] = geometry.material_ids
|
||||
return mesh
|
||||
return tool.Loader.convert_geometry_to_mesh(geometry, mesh, verts=verts)
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
|
||||
import traceback
|
||||
|
||||
@@ -47,18 +47,18 @@ class ViewportData:
|
||||
@classmethod
|
||||
def mode(cls):
|
||||
obj = bpy.context.active_object
|
||||
obj_mode = [("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0)]
|
||||
mesh_modes = [
|
||||
modes = [
|
||||
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
|
||||
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
|
||||
("ITEM", "IFC Item Mode", "", "MESH_DATA", 1),
|
||||
]
|
||||
if (
|
||||
not obj
|
||||
or not tool.Blender.is_editable(obj)
|
||||
or ((element := tool.Ifc.get_entity(obj)) and tool.Geometry.is_locked(element))
|
||||
):
|
||||
return obj_mode
|
||||
return mesh_modes
|
||||
return modes
|
||||
modes.append(("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 2))
|
||||
return modes
|
||||
|
||||
|
||||
class RepresentationsData:
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import blf
|
||||
import gpu
|
||||
import json
|
||||
import bmesh
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import SpaceView3D
|
||||
@@ -125,9 +124,25 @@ class ItemDecorator:
|
||||
edges = unselected_edges
|
||||
verts = unselected_verts
|
||||
i = len(verts)
|
||||
edges.extend([[ei + i for ei in e] for e in json.loads(item.edges)])
|
||||
|
||||
matrix_world = obj.matrix_world
|
||||
verts.extend([matrix_world @ Vector(v) for v in json.loads(item.verts)])
|
||||
bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box]
|
||||
bbox_edges = [
|
||||
(0 + i, 3 + i),
|
||||
(3 + i, 7 + i),
|
||||
(7 + i, 4 + i),
|
||||
(4 + i, 0 + i),
|
||||
(0 + i, 1 + i),
|
||||
(3 + i, 2 + i),
|
||||
(7 + i, 6 + i),
|
||||
(4 + i, 5 + i),
|
||||
(1 + i, 2 + i),
|
||||
(2 + i, 6 + i),
|
||||
(6 + i, 5 + i),
|
||||
(5 + i, 1 + i),
|
||||
]
|
||||
edges.extend(bbox_edges)
|
||||
verts.extend(bbox_verts)
|
||||
|
||||
if unselected_verts:
|
||||
self.draw_batch("LINES", unselected_verts, transparent_color(unselected_elements_color), unselected_edges)
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import bpy
|
||||
import bmesh
|
||||
import logging
|
||||
import numpy as np
|
||||
import json
|
||||
import re
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
@@ -637,6 +636,8 @@ class OverrideDelete(bpy.types.Operator):
|
||||
self.report({"INFO"}, "Elements that are part of an array cannot be deleted.")
|
||||
return {"FINISHED"}
|
||||
tool.Geometry.delete_ifc_object(obj)
|
||||
elif tool.Geometry.is_representation_item(obj):
|
||||
tool.Geometry.delete_ifc_item(obj)
|
||||
else:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@@ -1591,6 +1592,12 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return bpy.ops.bim.enable_editing_boundary_geometry()
|
||||
|
||||
for obj in selected_objs:
|
||||
if obj == context.scene.BIMGeometryProperties.representation_obj:
|
||||
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly.")
|
||||
obj.select_set(False)
|
||||
continue
|
||||
if tool.Geometry.is_representation_item(obj):
|
||||
return self.enable_editing_representation_item(context, obj)
|
||||
if not tool.Blender.is_editable(obj):
|
||||
self.report({"INFO"}, f"Object cannot be edited: {obj.name}")
|
||||
obj.select_set(False)
|
||||
@@ -1694,6 +1701,21 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
context.view_layer.objects.active = active_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
def enable_editing_representation_item(self, context, obj):
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
|
||||
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
return {"FINISHED"}
|
||||
else:
|
||||
self.report({"INFO"}, f"Editing {item.is_a()} geometry is not supported")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
|
||||
|
||||
@@ -1716,28 +1738,49 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if self.should_save:
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="")
|
||||
if getattr(tool.Ifc.get_entity(obj), "HasOpenings", False):
|
||||
self.reload_representation(obj)
|
||||
tool.Geometry.reload_representation(obj)
|
||||
else:
|
||||
self.reload_representation(obj)
|
||||
tool.Geometry.reload_representation(obj)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
|
||||
for obj in self.unchanged_objs_with_openings:
|
||||
self.reload_representation(obj)
|
||||
tool.Geometry.reload_representation(obj)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
def reload_representation(self, obj):
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
core.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
def edit_representation_item(self, obj):
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
if tool.Geometry.has_geometric_data(obj) and obj.data.polygons:
|
||||
if obj.data.BIMMeshProperties.mesh_checksum == tool.Geometry.get_mesh_checksum(obj.data):
|
||||
return {"FINISHED"}
|
||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
verts = [v.co / unit_scale for v in obj.data.vertices]
|
||||
faces = [p.vertices[:] for p in obj.data.polygons]
|
||||
if item.is_a("IfcAdvancedBrep"):
|
||||
new_item = builder.faceted_brep(verts, faces)
|
||||
else:
|
||||
new_item = builder.mesh(verts, faces)
|
||||
for inverse in tool.Ifc.get().get_inverse(item):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, new_item)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = new_item.id()
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
return {"FINISHED"}
|
||||
else:
|
||||
self.reload_item(obj)
|
||||
return {"FINISHED"}
|
||||
return {"FINISHED"}
|
||||
|
||||
def reload_item(self, obj):
|
||||
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
|
||||
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
||||
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
geometry = tool.Loader.create_generic_shape(item)
|
||||
obj.data.clear_geometry()
|
||||
tool.Loader.convert_geometry_to_mesh(geometry, obj.data)
|
||||
|
||||
def invoke(self, context, event):
|
||||
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
|
||||
@@ -1750,8 +1793,12 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
|
||||
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "OBJECT":
|
||||
context.scene.BIMGeometryProperties.mode = "OBJECT"
|
||||
if context.scene.BIMGeometryProperties.representation_obj:
|
||||
if context.scene.BIMGeometryProperties.mode != "ITEM":
|
||||
context.scene.BIMGeometryProperties.mode = "ITEM"
|
||||
else:
|
||||
if context.scene.BIMGeometryProperties.mode != "OBJECT":
|
||||
context.scene.BIMGeometryProperties.mode = "OBJECT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
|
||||
if not tool.Ifc.get():
|
||||
@@ -1761,6 +1808,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
if element and element.is_a("IfcRelSpaceBoundary"):
|
||||
return bpy.ops.bim.edit_boundary_geometry()
|
||||
elif tool.Geometry.is_representation_item(context.active_object):
|
||||
return self.edit_representation_item(context.active_object)
|
||||
|
||||
objs = context.selected_objects or [context.active_object]
|
||||
|
||||
@@ -1909,7 +1958,6 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_representation_item"
|
||||
bl_label = "Remove Representation Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
representation_item_id: bpy.props.IntProperty()
|
||||
|
||||
@classmethod
|
||||
@@ -1923,7 +1971,6 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMGeometryProperties
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
representation_item = ifc_file.by_id(self.representation_item_id)
|
||||
@@ -2217,7 +2264,7 @@ class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, tool.Ifc.Opera
|
||||
|
||||
|
||||
class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.xxx_import_representation_items"
|
||||
bl_idname = "bim.import_representation_items"
|
||||
bl_label = "Import Representation Items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -2225,85 +2272,38 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
|
||||
tool.Geometry.dissolve_triangulated_edges(obj)
|
||||
bm_dict = self.separate_faces_by_vertex_group(obj)
|
||||
# bm_dict = self.separate_faces_by_id(obj)
|
||||
props = context.scene.BIMGeometryProperties
|
||||
props.item_objs.clear()
|
||||
props.representation_obj = obj
|
||||
tool.Geometry.lock_object(obj)
|
||||
|
||||
for item_id, bm in bm_dict.items():
|
||||
item_mesh = bpy.data.meshes.new(f"mesh_id_{item_id}")
|
||||
bm.to_mesh(item_mesh)
|
||||
bm.free()
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "ITEM":
|
||||
context.scene.BIMGeometryProperties.mode = "ITEM"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
|
||||
item_ids = set()
|
||||
for vgroup in obj.vertex_groups:
|
||||
if vgroup.name.startswith("ios_item_id_"):
|
||||
item_ids.add(int(vgroup.name.split("_")[-1]))
|
||||
|
||||
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
|
||||
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
||||
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
||||
|
||||
for item_id in item_ids:
|
||||
item = tool.Ifc.get().by_id(item_id)
|
||||
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
|
||||
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
|
||||
geometry = tool.Loader.create_generic_shape(item)
|
||||
tool.Loader.convert_geometry_to_mesh(geometry, item_mesh)
|
||||
|
||||
item_obj = bpy.data.objects.new(f"Item/{item.is_a()}/{item_id}", item_mesh)
|
||||
item_obj.matrix_world = obj.matrix_world
|
||||
item_obj.show_in_front = True
|
||||
bpy.context.collection.objects.link(item_obj)
|
||||
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Geometry.lock_object(item_obj)
|
||||
|
||||
new = props.item_objs.add()
|
||||
new.obj = item_obj
|
||||
|
||||
verts = [list(co) for co in item_obj.bound_box]
|
||||
edges = [(0, 3), (3, 7), (7, 4), (4, 0), (0, 1), (3, 2), (7, 6), (4, 5), (1, 2), (2, 6), (6, 5), (5, 1)]
|
||||
new.verts = json.dumps(verts)
|
||||
new.edges = json.dumps(edges)
|
||||
|
||||
def separate_faces_by_id(self, obj):
|
||||
mesh = obj.data
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
results = {}
|
||||
face_ids = mesh["ios_item_ids"]
|
||||
unique_ids = set(face_ids)
|
||||
|
||||
for item_id in unique_ids:
|
||||
item_bm = bmesh.new()
|
||||
for face, face_id in zip(bm.faces, face_ids):
|
||||
if face_id == item_id:
|
||||
# Copy the face and its vertices into the new BMesh
|
||||
new_face_verts = [item_bm.verts.new(v.co) for v in face.verts]
|
||||
item_bm.faces.new(new_face_verts)
|
||||
item_bm.verts.ensure_lookup_table()
|
||||
item_bm.faces.ensure_lookup_table()
|
||||
results[item_id] = item_bm
|
||||
|
||||
return results
|
||||
|
||||
def separate_faces_by_vertex_group(self, obj):
|
||||
mesh = obj.data
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
|
||||
# Ensure the bmesh has up-to-date vertex weights (vertex groups)
|
||||
bm.verts.layers.deform.verify()
|
||||
|
||||
results = {}
|
||||
for vgroup in obj.vertex_groups:
|
||||
group_bm = bmesh.new()
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
|
||||
# Iterate over the faces and check if all vertices of the face belong to the vertex group
|
||||
for face in bm.faces:
|
||||
face_in_group = True
|
||||
for vert in face.verts:
|
||||
# Get the vertex groups the vertex belongs to
|
||||
deform = vert[deform_layer]
|
||||
|
||||
# If this vertex does not belong to the current vertex group, mark the face as outside the group
|
||||
if vgroup.index not in deform:
|
||||
face_in_group = False
|
||||
break
|
||||
|
||||
# If all vertices of the face belong to the current vertex group, add the face to the new BMesh
|
||||
if face_in_group:
|
||||
new_face_verts = [group_bm.verts.new(v.co) for v in face.verts]
|
||||
group_bm.faces.new(new_face_verts)
|
||||
|
||||
# Ensure the mesh is valid and doesn't have duplicate elements
|
||||
group_bm.verts.ensure_lookup_table()
|
||||
group_bm.faces.ensure_lookup_table()
|
||||
|
||||
results[int(vgroup.name.split("_")[3])] = group_bm
|
||||
|
||||
return results
|
||||
|
||||
@@ -43,11 +43,23 @@ def update_mode(self, context):
|
||||
if self.is_changing_mode:
|
||||
return
|
||||
if self.mode == "OBJECT":
|
||||
if self.representation_obj:
|
||||
tool.Geometry.unlock_object(self.representation_obj)
|
||||
self.representation_obj = None
|
||||
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
|
||||
elif self.mode == "ITEM":
|
||||
bpy.ops.bim.import_representation_items()
|
||||
elif self.mode == "EDIT":
|
||||
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
|
||||
|
||||
|
||||
def update_representation_obj(self, context):
|
||||
for item_obj in self.item_objs:
|
||||
if item_obj.obj:
|
||||
bpy.data.objects.remove(item_obj.obj)
|
||||
self.item_objs.clear()
|
||||
|
||||
|
||||
def get_mode(self, context):
|
||||
if not ViewportData.is_loaded:
|
||||
ViewportData.load()
|
||||
@@ -147,6 +159,7 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
|
||||
is_changing_mode: BoolProperty(name="Is Changing Mode", default=False)
|
||||
mode: EnumProperty(items=get_mode, name="IFC Interaction Mode", update=update_mode)
|
||||
representation_obj: PointerProperty(name="Representation Object", type=bpy.types.Object, update=update_representation_obj)
|
||||
item_objs: CollectionProperty(name="Item Objects", type=RepresentationItemObject)
|
||||
|
||||
def is_object_valid_for_representation_copy(self, obj: bpy.types.Object) -> bool:
|
||||
|
||||
@@ -456,7 +456,11 @@ class BIM_PT_tabs(Panel):
|
||||
op.uri = "https://docs.bonsaibim.org/guides/troubleshooting.html#saving-and-loading-blend-files"
|
||||
row.operator("bim.close_blend_warning", text="", icon="CANCEL")
|
||||
|
||||
if not context.mode.startswith(context.scene.BIMGeometryProperties.mode):
|
||||
if context.mode == "OBJECT" and context.scene.BIMGeometryProperties.mode in ("OBJECT", "ITEM"):
|
||||
pass
|
||||
elif context.mode.startswith("EDIT") and context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
pass
|
||||
else:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
row = box.row(align=True)
|
||||
|
||||
@@ -22,6 +22,7 @@ import struct
|
||||
import hashlib
|
||||
import logging
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.grid
|
||||
@@ -114,6 +115,20 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
obj.lock_location = (False, False, False)
|
||||
obj.lock_rotation = (False, False, False)
|
||||
|
||||
@classmethod
|
||||
def delete_ifc_item(cls, obj: bpy.types.Object) -> None:
|
||||
props = bpy.context.scene.BIMGeometryProperties
|
||||
if len(props.item_objs) == 1:
|
||||
return
|
||||
for i, item_obj in enumerate(props.item_objs):
|
||||
if item_obj.obj == obj:
|
||||
props.item_objs.remove(i)
|
||||
break
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
cls.remove_representation_item(item)
|
||||
cls.reload_representation(props.representation_obj)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def delete_ifc_object(cls, obj: bpy.types.Object) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -184,6 +199,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
bonsai.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
try:
|
||||
obj.name
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj == obj:
|
||||
bpy.context.scene.BIMGeometryProperties.representation_obj = None
|
||||
bpy.data.objects.remove(obj)
|
||||
except:
|
||||
pass
|
||||
@@ -401,6 +418,11 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
|
||||
@classmethod
|
||||
def get_data_representation(cls, data: bpy.types.Mesh) -> ifcopenshell.entity_instance | None:
|
||||
if hasattr(data, "BIMMeshProperties") and data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(data.BIMMeshProperties.ifc_definition_id)
|
||||
|
||||
@classmethod
|
||||
def get_active_representation_context(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance:
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
@@ -806,6 +828,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def is_meshlike_item(cls, item: ifcopenshell.entity_instance) -> bool:
|
||||
return item.is_a("IfcTessellatedItem") or item.is_a("IfcManifoldSolidBrep")
|
||||
|
||||
@classmethod
|
||||
def is_profile_based(cls, data: bpy.types.Mesh) -> bool:
|
||||
return data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
@@ -816,6 +842,14 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
"SweptSolid",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_representation_item(cls, obj: bpy.types.Object) -> bool:
|
||||
return (
|
||||
obj.data
|
||||
and (ifc_id := obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
and tool.Ifc.get().by_id(ifc_id).is_a("IfcRepresentationItem")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_text_literal(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
items = ifcopenshell.util.representation.resolve_items(representation)
|
||||
@@ -1365,3 +1399,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
use_immediate_repr = apply_openings and bool(getattr(element, "HasOpenings", None))
|
||||
use_immediate_repr = use_immediate_repr or cls.has_material_style_override(element)
|
||||
return use_immediate_repr
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_representation(cls, representation):
|
||||
return ifcopenshell.util.element.get_elements_by_representation(tool.Ifc.get(), representation)
|
||||
|
||||
@@ -879,3 +879,48 @@ class Loader(bonsai.core.tool.Loader):
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
return Matrix(matrix.tolist())
|
||||
|
||||
@classmethod
|
||||
def convert_geometry_to_mesh(cls, geometry, mesh: bpy.types.Mesh, verts=None) -> bpy.types.Mesh:
|
||||
if verts is None:
|
||||
verts = geometry.verts
|
||||
if geometry.faces:
|
||||
num_vertices = len(verts) // 3
|
||||
total_faces = len(geometry.faces)
|
||||
loop_start = range(0, total_faces, 3)
|
||||
num_loops = total_faces // 3
|
||||
loop_total = [3] * num_loops
|
||||
num_vertex_indices = len(geometry.faces)
|
||||
|
||||
# See bug 3546
|
||||
# ios_edges holds true edges that aren't triangulated.
|
||||
#
|
||||
# we do `.tolist()` because Blender can't assign `np.int32` to it's custom attributes
|
||||
mesh["ios_edges"] = list(set(tuple(e) for e in ifcopenshell.util.shape.get_edges(geometry).tolist()))
|
||||
mesh["ios_item_ids"] = ifcopenshell.util.shape.get_representation_item_ids(geometry).tolist()
|
||||
|
||||
mesh.vertices.add(num_vertices)
|
||||
mesh.vertices.foreach_set("co", verts)
|
||||
mesh.loops.add(num_vertex_indices)
|
||||
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
||||
mesh.polygons.add(num_loops)
|
||||
mesh.polygons.foreach_set("loop_start", loop_start)
|
||||
mesh.polygons.foreach_set("loop_total", loop_total)
|
||||
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
|
||||
mesh.update()
|
||||
|
||||
# TODO: geometry id is not always an int.
|
||||
rep_id = geometry.id
|
||||
if rep_id.isdigit():
|
||||
if (rep := tool.Ifc.get().by_id(int(rep_id))) and rep.is_a("IfcShapeRepresentation"):
|
||||
tool.Loader.load_indexed_colour_map(rep, mesh)
|
||||
else:
|
||||
e = geometry.edges
|
||||
v = verts
|
||||
vertices = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)]
|
||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||
mesh.from_pydata(vertices, edges, [])
|
||||
|
||||
mesh["ios_materials"] = [m.instance_id() for m in geometry.materials]
|
||||
mesh["ios_material_ids"] = geometry.material_ids
|
||||
return mesh
|
||||
|
||||
Reference in New Issue
Block a user