You can now add new mesh representation items

This commit is contained in:
Dion Moult
2024-09-21 16:34:41 +10:00
parent 2a8d43c42c
commit ce010f1e56
6 changed files with 210 additions and 99 deletions
@@ -20,6 +20,7 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddMeshlikeItem,
operator.AddRepresentation,
operator.CopyRepresentation,
operator.DisableEditingRepresentationItemShapeAspect,
+147 -85
View File
@@ -1742,6 +1742,93 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
should_save: bpy.props.BoolProperty(name="Should Save", default=True)
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not context.active_object:
return {"FINISHED"}
self.is_valid = True
if context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")
if not tool.Ifc.get():
return {"FINISHED"}
context.scene.BIMGeometryProperties.is_changing_mode = True
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 context.active_object:
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):
self.edit_representation_item(context.active_object)
return {"FINISHED"}
objs = context.selected_objects or [context.active_object]
print("objs are", objs)
self.edited_objs = []
self.unchanged_objs_with_openings = []
for obj in objs:
if not obj:
continue
element = tool.Ifc.get_entity(obj)
if not element:
continue
if tool.Profile.is_editing_profile():
profile_id = context.scene.BIMProfileProperties.active_profile_id
if profile_id:
profile = tool.Ifc.get().by_id(profile_id)
if tool.Ifc.get_object(profile): # We are editing an arbitrary profile
bpy.ops.bim.edit_arbitrary_profile()
elif tool.Blender.Modifier.is_railing(element):
bpy.ops.bim.finish_editing_railing_path()
elif tool.Blender.Modifier.is_roof(element):
bpy.ops.bim.finish_editing_roof_path()
elif tool.Model.get_usage_type(element) == "PROFILE":
bpy.ops.bim.edit_extrusion_axis()
# if in the process of editing arbitrary profile
elif context.scene.BIMProfileProperties.active_arbitrary_profile_id:
bpy.ops.bim.edit_arbitrary_profile()
else:
bpy.ops.bim.edit_extrusion_profile()
return self.execute(context)
elif obj.data.BIMMeshProperties.ifc_definition_id:
if not tool.Geometry.has_geometric_data(obj):
self.is_valid = False
self.should_save = False
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if tool.Geometry.is_meshlike(
representation
) and obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
self.edited_objs.append(obj)
elif getattr(element, "HasOpenings", None):
self.unchanged_objs_with_openings.append(obj)
else:
tool.Ifc.finish_edit(obj)
elif element.is_a("IfcGridAxis"):
if not tool.Geometry.has_geometric_data(obj):
self.is_valid = False
self.should_save = False
if obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
self.edited_objs.append(obj)
else:
tool.Ifc.finish_edit(obj)
return self.execute(context)
def _execute(self, context):
if not context.active_object:
return {"FINISHED"}
@@ -1866,91 +1953,6 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
context.scene.BIMGeometryProperties.mode = "EDIT"
context.scene.BIMGeometryProperties.is_changing_mode = False
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not context.active_object:
return {"FINISHED"}
self.is_valid = True
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
context.scene.BIMGeometryProperties.is_changing_mode = True
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():
return {"FINISHED"}
if context.active_object:
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):
self.edit_representation_item(context.active_object)
return {"FINISHED"}
objs = context.selected_objects or [context.active_object]
self.edited_objs = []
self.unchanged_objs_with_openings = []
for obj in objs:
if not obj:
continue
element = tool.Ifc.get_entity(obj)
if not element:
continue
if tool.Profile.is_editing_profile():
profile_id = context.scene.BIMProfileProperties.active_profile_id
if profile_id:
profile = tool.Ifc.get().by_id(profile_id)
if tool.Ifc.get_object(profile): # We are editing an arbitrary profile
bpy.ops.bim.edit_arbitrary_profile()
elif tool.Blender.Modifier.is_railing(element):
bpy.ops.bim.finish_editing_railing_path()
elif tool.Blender.Modifier.is_roof(element):
bpy.ops.bim.finish_editing_roof_path()
elif tool.Model.get_usage_type(element) == "PROFILE":
bpy.ops.bim.edit_extrusion_axis()
# if in the process of editing arbitrary profile
elif context.scene.BIMProfileProperties.active_arbitrary_profile_id:
bpy.ops.bim.edit_arbitrary_profile()
else:
bpy.ops.bim.edit_extrusion_profile()
return self.execute(context)
elif obj.data.BIMMeshProperties.ifc_definition_id:
if not tool.Geometry.has_geometric_data(obj):
self.is_valid = False
self.should_save = False
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if tool.Geometry.is_meshlike(
representation
) and obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
self.edited_objs.append(obj)
elif getattr(element, "HasOpenings", None):
self.unchanged_objs_with_openings.append(obj)
else:
tool.Ifc.finish_edit(obj)
elif element.is_a("IfcGridAxis"):
if not tool.Geometry.has_geometric_data(obj):
self.is_valid = False
self.should_save = False
if obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
self.edited_objs.append(obj)
else:
tool.Ifc.finish_edit(obj)
return self.execute(context)
class FlipObject(bpy.types.Operator):
bl_idname = "bim.flip_object"
@@ -2409,3 +2411,63 @@ class UpdateItemAttributes(bpy.types.Operator, tool.Ifc.Operator):
item.Depth = attribute.float_value
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
tool.Geometry.import_item(obj)
class AddMeshlikeItem(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_meshlike_item"
bl_label = "Add Meshlike Item"
bl_options = {"REGISTER", "UNDO"}
shape: bpy.props.StringProperty(name="Shape")
def _execute(self, context):
props = context.scene.BIMGeometryProperties
mesh = bpy.data.meshes.new("Tmp")
obj = bpy.data.objects.new("Tmp", mesh)
scene = bpy.context.scene
scene.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bm = bmesh.new()
matrix = props.representation_obj.matrix_world.copy()
matrix.translation = context.scene.cursor.location
matrix = props.representation_obj.matrix_world.inverted() @ matrix
if self.shape == "CUBE":
bmesh.ops.create_cube(bm, matrix=matrix, size=0.5)
elif self.shape == "PLANE":
bmesh.ops.create_grid(bm, matrix=matrix, size=0.5)
elif self.shape == "CIRCLE":
bmesh.ops.create_circle(bm, matrix=matrix, radius=0.25, segments=16, cap_ends=True)
elif self.shape == "UVSPHERE":
bmesh.ops.create_uvsphere(bm, matrix=matrix, radius=0.25, u_segments=16, v_segments=16)
elif self.shape == "ICOSPHERE":
bmesh.ops.create_icosphere(bm, matrix=matrix, radius=0.25, subdivisions=2)
elif self.shape == "CYLINDER":
# Cone is legitimate.
bmesh.ops.create_cone(bm, matrix=matrix, radius1=0.25, radius2=0.25, depth=0.5, segments=16, cap_ends=True)
bm.to_mesh(mesh)
bm.free()
mesh.update()
obj.matrix_world = props.representation_obj.matrix_world
new = props.item_objs.add()
new.obj = obj
tool.Geometry.lock_object(obj)
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]
representation = tool.Geometry.get_active_representation(props.representation_obj)
representation = ifcopenshell.util.representation.resolve_representation(representation)
if representation.RepresentationType in ("Brep", "AdvancedBrep"):
item = builder.faceted_brep(verts, faces)
elif representation.RepresentationType in ("Tessellation"):
item = builder.mesh(verts, faces)
representation.Items = list(representation.Items) + [item]
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
@@ -64,6 +64,7 @@ classes = (
product.LoadTypeThumbnails,
product.MirrorElements,
workspace.Hotkey,
workspace.BIM_MT_add_meshlike_item,
wall.AlignWall,
wall.ChangeExtrusionDepth,
wall.ChangeExtrusionXAngle,
@@ -30,6 +30,7 @@ from natsort import natsorted
def refresh():
AuthoringData.is_loaded = False
ItemData.is_loaded = False
ArrayData.is_loaded = False
StairData.is_loaded = False
SverchokData.is_loaded = False
@@ -620,3 +621,27 @@ class RoofData:
general_params[prop_readable_name] = prop_value
return general_params
class ItemData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {}
cls.data["representation_identifier"] = cls.representation_identifier()
cls.data["representation_type"] = cls.representation_type()
@classmethod
def representation_identifier(cls):
props = bpy.context.scene.BIMGeometryProperties
rep = tool.Geometry.get_active_representation(props.representation_obj)
return rep.RepresentationIdentifier
@classmethod
def representation_type(cls):
props = bpy.context.scene.BIMGeometryProperties
rep = tool.Geometry.get_active_representation(props.representation_obj)
return rep.RepresentationType
@@ -24,8 +24,8 @@ import bonsai.tool as tool
import bonsai.core.model as core
from bonsai.bim.module.model.wall import DumbWallJoiner
from bonsai.bim.helper import prop_with_search
from bpy.types import WorkSpaceTool
from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool, Menu
from bonsai.bim.module.model.data import AuthoringData, ItemData
from bonsai.bim.module.system.data import PortData
from bonsai.bim.module.model.prop import get_ifc_class
@@ -262,9 +262,17 @@ def format_ifc_camel_case(string):
class EditItemUI:
@classmethod
def draw(cls, context, layout):
if not ItemData.is_loaded:
ItemData.load()
cls.layout = layout
row = cls.layout.row()
row.label(text="Item Mode", icon="MESH_DATA")
row = cls.layout.row()
row.label(text="Context: " + ItemData.data["representation_identifier"], icon="SCENE_DATA")
row = cls.layout.row()
row.label(text="Type: " + ItemData.data["representation_type"], icon="OUTLINER_OB_MESH")
cls.layout.menu("BIM_MT_add_meshlike_item", icon="ADD")
if not (obj := context.active_object) or not tool.Geometry.is_representation_item(obj):
return
for item_attribute in obj.data.BIMMeshProperties.item_attributes:
@@ -275,6 +283,19 @@ class EditItemUI:
row.operator("bim.update_item_attributes", icon="FILE_REFRESH", text="")
class BIM_MT_add_meshlike_item(Menu):
bl_idname = "BIM_MT_add_meshlike_item"
bl_label = "Add Item"
def draw(self, context):
self.layout.operator("bim.add_meshlike_item", icon="MESH_PLANE", text="Mesh Plane").shape = "PLANE"
self.layout.operator("bim.add_meshlike_item", icon="MESH_CUBE", text="Mesh Cube").shape = "CUBE"
self.layout.operator("bim.add_meshlike_item", icon="MESH_CIRCLE", text="Mesh Circle").shape = "CIRCLE"
self.layout.operator("bim.add_meshlike_item", icon="MESH_UVSPHERE", text="Mesh UV Sphere").shape = "UVSPHERE"
self.layout.operator("bim.add_meshlike_item", icon="MESH_ICOSPHERE", text="Mesh Icosphere").shape = "ICOSPHERE"
self.layout.operator("bim.add_meshlike_item", icon="MESH_CYLINDER", text="Mesh Cylinder").shape = "CYLINDER"
class CreateObjectUI:
@classmethod
def draw(cls, context, layout, ifc_element_type=None):
+13 -12
View File
@@ -1422,21 +1422,22 @@ class Geometry(bonsai.core.tool.Geometry):
has_changed = False
for item_obj in props.item_objs:
obj = item_obj.obj
if not tool.Ifc.is_moved(obj):
continue
has_changed = True
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
old_position = item.Position
if np.allclose(np.array(obj.matrix_world), np.array(rep_obj.matrix_world), atol=1e-4):
if item.is_a("IfcSweptAreaSolid"):
if not tool.Ifc.is_moved(obj):
continue
has_changed = True
old_position = item.Position
if np.allclose(np.array(obj.matrix_world), np.array(rep_obj.matrix_world), atol=1e-4):
if old_position:
item.Position = None
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
continue
rel_matrix = rep_matrix_i @ obj.matrix_world
rel_matrix.translation /= unit_scale
item.Position = builder.create_axis2_placement_3d_from_matrix(np.array(rel_matrix))
if old_position:
item.Position = None
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
continue
rel_matrix = rep_matrix_i @ obj.matrix_world
rel_matrix.translation /= unit_scale
item.Position = builder.create_axis2_placement_3d_from_matrix(np.array(rel_matrix))
if old_position:
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
if has_changed:
cls.reload_representation(rep_obj)