This commit is contained in:
Dion Moult
2025-01-10 15:21:18 +11:00
parent 4a1422d292
commit e4412e2f51
16 changed files with 63 additions and 59 deletions
-1
View File
@@ -1129,7 +1129,6 @@ class IfcImporter:
tool.Aggregate.constrain_all_parts_to_aggregate(relating_obj)
bpy.context.scene.BIMAggregateProperties.aggregate_decorator = True
def lock_scales(self) -> None:
elements = set(self.file.by_type("IfcProduct"))
while elements:
@@ -207,7 +207,6 @@ class AggregateDecorator:
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(aggregate))
parts_objs = [tool.Ifc.get_object(p) for p in parts]
indices, edges = create_bounding_box(parts_objs)
self.line_shader.uniform_float("lineWidth", 0.5)
self.draw_batch("LINES", indices, color, edges)
@@ -230,9 +229,7 @@ class AggregateModeDecorator:
cls.handlers.append(
SpaceView3D.draw_handler_add(handler.draw_aggregate_empty, (context,), "WINDOW", "POST_VIEW")
)
cls.handlers.append(
SpaceView3D.draw_handler_add(handler.draw_new_objects, (context,), "WINDOW", "POST_VIEW")
)
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_new_objects, (context,), "WINDOW", "POST_VIEW"))
cls.is_installed = True
@classmethod
@@ -352,12 +349,11 @@ class AggregateModeDecorator:
new_objs = [o for o in new_objs if o.data]
for obj in new_objs:
element = tool.Ifc.get_entity(obj)
if (aggregate := ifcopenshell.util.element.get_aggregate(element)) == tool.Ifc.get_entity(props.editing_aggregate):
if (aggregate := ifcopenshell.util.element.get_aggregate(element)) == tool.Ifc.get_entity(
props.editing_aggregate
):
continue
if element and element.is_a("IfcElement"):
data = ItemDecorator.get_obj_data(obj)
if data:
self.draw_batch(
"TRIS", data["verts"], transparent_color((1, 0, 0, 1)), data["tris"]
)
self.draw_batch("TRIS", data["verts"], transparent_color((1, 0, 0, 1)), data["tris"])
@@ -375,6 +375,7 @@ class BIM_OT_select_linked_aggregates(bpy.types.Operator):
return {"FINISHED"}
class BIM_OT_disable_aggregate_mode(bpy.types.Operator):
bl_idname = "bim.disable_aggregate_mode"
bl_label = "Disable Aggregate Mode"
@@ -385,6 +386,7 @@ class BIM_OT_disable_aggregate_mode(bpy.types.Operator):
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
return {"FINISHED"}
class BIM_OT_toggle_aggregate_mode_local_view(bpy.types.Operator):
bl_idname = "bim.toggle_aggregate_mode_local_view"
bl_label = "Toggle Aggregate Mode Local View"
@@ -403,6 +405,7 @@ class BIM_OT_toggle_aggregate_mode_local_view(bpy.types.Operator):
return {"FINISHED"}
class BIM_OT_aggregate_assign_new_objects_in_aggregate_mode(bpy.types.Operator):
bl_idname = "bim.aggregate_assign_new_objects_in_aggregate_mode"
bl_label = "Aggregate Assign New Objects In Aggregate Mode"
@@ -424,7 +427,9 @@ class BIM_OT_aggregate_assign_new_objects_in_aggregate_mode(bpy.types.Operator):
new_objs = [o for o in new_objs if o.data]
for obj in new_objs:
element = tool.Ifc.get_entity(obj)
if (aggregate := ifcopenshell.util.element.get_aggregate(element)) == tool.Ifc.get_entity(props.editing_aggregate):
if (aggregate := ifcopenshell.util.element.get_aggregate(element)) == tool.Ifc.get_entity(
props.editing_aggregate
):
continue
if element and element.is_a("IfcElement"):
obj.select_set(True)
@@ -439,4 +444,3 @@ class BIM_OT_aggregate_assign_new_objects_in_aggregate_mode(bpy.types.Operator):
BIM_OT_aggregate_assign_object._execute(self, context)
return {"FINISHED"}
@@ -1858,7 +1858,7 @@ class OverrideEscape(bpy.types.Operator):
bpy.ops.bim.hide_all_openings()
elif context.scene.BIMAggregateProperties.in_aggregate_mode:
bpy.ops.bim.disable_aggregate_mode()
elif active_object:=context.active_object:
elif active_object := context.active_object:
if tool.Blender.Modifier.try_canceling_editing_modifier_parameters_or_path(active_object):
pass
return {"FINISHED"}
@@ -1902,7 +1902,9 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly")
elif obj in [o.obj for o in context.scene.BIMAggregateProperties.not_editing_objects]:
obj.select_set(False)
self.report({"ERROR"}, f"Element '{obj.name}' does not belong to this aggregate and cannot be edited directly")
self.report(
{"ERROR"}, f"Element '{obj.name}' does not belong to this aggregate and cannot be edited directly"
)
elif obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
self.report({"ERROR"}, "Clipping planes cannot be edited")
elif element:
@@ -25,7 +25,7 @@ import bmesh
import ifcopenshell
import bonsai.tool as tool
import math
from math import sin, cos, radians
from math import sin, cos, radians
from bpy.types import SpaceView3D
from bpy_extras import view3d_utils
from mathutils import Vector, Matrix
@@ -785,7 +785,7 @@ class ProductDecorator:
@classmethod
def uninstall(cls):
props = bpy.context.scene.BIMProductPreviewProperties # updated by model/polyline.py
props = bpy.context.scene.BIMProductPreviewProperties # updated by model/polyline.py
props.verts.clear()
props.edges.clear()
props.tris.clear()
@@ -806,7 +806,7 @@ class ProductDecorator:
props = context.scene.BIMProductPreviewProperties
data = {}
data["verts"] = [(*v.value_3d,) for v in props.verts]
data["edges"] = [(int(e.value_2d[0]),int(e.value_2d[1])) for e in props.edges]
data["edges"] = [(int(e.value_2d[0]), int(e.value_2d[1])) for e in props.edges]
data["tris"] = [(int(t.value_3d[0]), int(t.value_3d[1]), int(t.value_3d[2])) for t in props.tris]
return data
+17 -19
View File
@@ -200,6 +200,7 @@ def get_wall_preview_data(context, relating_type):
return data
def get_vertical_profile_preview_data(context, relating_type):
material = ifcopenshell.util.element.get_material(relating_type)
try:
@@ -261,7 +262,7 @@ def get_vertical_profile_preview_data(context, relating_type):
# Create extrusion bmesh
bm = bmesh.new()
grouped_verts.append(grouped_verts[0]) # Close profile
grouped_verts.append(grouped_verts[0]) # Close profile
new_verts = [bm.verts.new(v) for v in grouped_verts]
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(grouped_verts) - 1)]
@@ -296,11 +297,13 @@ def get_vertical_profile_preview_data(context, relating_type):
# Add only profile edges
edges = []
for edge in bm.edges:
if (edge.verts[0].co.z == min_z and edge.verts[1].co.z == min_z) or (edge.verts[0].co.z == max_z and edge.verts[1].co.z == max_z):
if (edge.verts[0].co.z == min_z and edge.verts[1].co.z == min_z) or (
edge.verts[0].co.z == max_z and edge.verts[1].co.z == max_z
):
edges.append(edge)
# Add axis edge
edges = [(edge.verts[0].index, edge.verts[1].index) for edge in edges]
edges.append((len(verts)-1, len(verts)-2))
edges.append((len(verts) - 1, len(verts) - 2))
data["verts"] = verts
data["edges"] = edges
data["tris"] = tris
@@ -327,7 +330,7 @@ def get_horizontal_profile_preview_data(context, relating_type):
return
for point in polyline_points:
polyline_verts.append(Vector((point.x, point.y, point.z)))
polyline_edges = [(i, i+1) for i in range(len(polyline_verts)-1)]
polyline_edges = [(i, i + 1) for i in range(len(polyline_verts) - 1)]
# Get profile shape
settings = ifcopenshell.geom.settings()
@@ -372,26 +375,25 @@ def get_horizontal_profile_preview_data(context, relating_type):
case "9":
grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
# Create profile curve
scale_mat = Matrix.Scale(-1, 4, (1.0, 0.0, 0.0))
grouped_verts = [scale_mat @ Vector(v) for v in grouped_verts]
profile_curve = bpy.data.curves.new("Profile", type='CURVE')
profile_curve = bpy.data.curves.new("Profile", type="CURVE")
profile_curve.dimensions = "2D"
profile_curve.splines.new('POLY')
profile_curve.splines.new("POLY")
profile_curve.splines[0].points.add(len(grouped_verts))
for i, point in enumerate(profile_curve.splines[0].points):
if i == len(grouped_verts): # Close curve
if i == len(grouped_verts): # Close curve
point.co = Vector((*grouped_verts[0], 0))
continue
point.co = Vector((*grouped_verts[i], 0))
profile_obj = bpy.data.objects.new("Profile", profile_curve)
# Create path curve with profile object as bevel
path_curve = bpy.data.curves.new("Polyline", type='CURVE')
path_curve = bpy.data.curves.new("Polyline", type="CURVE")
path_curve.dimensions = "2D"
path_curve.splines.new('POLY')
path_curve.splines.new("POLY")
path_curve.splines[0].points.add(len(polyline_verts) - 1)
for i, point in enumerate(path_curve.splines[0].points):
point.co = Vector((*polyline_verts[i], 0))
@@ -400,12 +402,12 @@ def get_horizontal_profile_preview_data(context, relating_type):
path_curve.bevel_object = profile_obj
# Convert path curve to mesh
# This operation throws a warning when done during gpu drawing, so it was removed from the decorator file to be handled here
# This operation throws a warning when done during gpu drawing, so it was removed from the decorator file to be handled here
path_obj = bpy.data.objects.new("Preview", path_curve)
context.scene.collection.objects.link(path_obj)
bpy.context.view_layer.objects.active = path_obj
dg = context.evaluated_depsgraph_get()
path_obj = path_obj.evaluated_get(dg)
path_obj = path_obj.evaluated_get(dg)
me = path_obj.to_mesh()
# Create bmesh from path mesh
@@ -812,13 +814,9 @@ class PolylineOperator:
tool.Blender.update_viewport()
def get_product_preview_data(self, context: bpy.types.Context, relating_type: ifcopenshell.entity_isntance):
if (
tool.Model.get_usage_type(relating_type) == "PROFILE" and relating_type.is_a() not in {"IfcColumnType"}
):
if tool.Model.get_usage_type(relating_type) == "PROFILE" and relating_type.is_a() not in {"IfcColumnType"}:
data = get_horizontal_profile_preview_data(context, relating_type)
elif (
tool.Model.get_usage_type(relating_type) == "PROFILE" and relating_type.is_a() in {"IfcColumnType"}
):
elif tool.Model.get_usage_type(relating_type) == "PROFILE" and relating_type.is_a() in {"IfcColumnType"}:
data = get_vertical_profile_preview_data(context, relating_type)
elif tool.Model.get_usage_type(relating_type) == "LAYER2":
data = get_wall_preview_data(context, relating_type)
@@ -832,7 +830,7 @@ class PolylineOperator:
props.verts.clear()
props.edges.clear()
props.tris.clear()
for vert in data["verts"]:
v = props.verts.add()
v.value_3d = vert
@@ -1145,7 +1145,6 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
for profile1, profile2 in zip(profiles[:-1], profiles[1:]):
DumbProfileJoiner().join_V(profile2["obj"], profile1["obj"])
def modal(self, context, event):
if not self.relating_type:
self.report({"WARNING"}, "You need to select a profile type.")
@@ -1176,7 +1175,6 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
size = len(items)
context.scene.BIMModelProperties.offset_type = items[((index + 1) % size)]
props = bpy.context.scene.BIMModelProperties
wall_config = f"""Direction: {props.direction_sense}
Offset Type: {props.offset_type}
@@ -1222,4 +1220,3 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
self.tool_state.use_default_container = True
self.tool_state.plane_method = "XY"
return {"RUNNING_MODAL"}
+2 -1
View File
@@ -821,12 +821,13 @@ class BIMPolylineProperties(PropertyGroup):
insertion_polyline: bpy.props.CollectionProperty(type=Polyline)
measurement_polyline: bpy.props.CollectionProperty(type=Polyline)
class ProductPreviewItem(PropertyGroup):
value_3d: bpy.props.FloatVectorProperty()
value_2d: bpy.props.FloatVectorProperty(size=2)
class BIMProductPreviewProperties(PropertyGroup):
verts: bpy.props.CollectionProperty(type=ProductPreviewItem)
edges: bpy.props.CollectionProperty(type=ProductPreviewItem)
tris: bpy.props.CollectionProperty(type=ProductPreviewItem)
@@ -591,7 +591,7 @@ class EditObjectUI:
op = row.operator("bim.disable_aggregate_mode", text="", icon="X")
op = row.operator("bim.toggle_aggregate_mode_local_view", text="", icon="ZOOM_SELECTED")
op = row.operator("bim.aggregate_assign_new_objects_in_aggregate_mode", text="", icon="CUBE")
text = format_ifc_camel_case(AuthoringData.data["active_class"])
layout.label(text=f"{text} Edit Tools:", icon="RESTRICT_SELECT_OFF")
cls.draw_parameter_adjustments(context)
@@ -954,7 +954,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
def hotkey_S_A(self):
props = bpy.context.scene.BIMModelProperties
relating_type_id = AuthoringData.data["relating_type_id_current"]
relating_type_class = AuthoringData.data["ifc_class_current"]
relating_type_class = AuthoringData.data["ifc_class_current"]
if relating_type_id is None:
self.report({"ERROR"}, "No relating type selected")
return
@@ -993,7 +993,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
):
bpy.ops.bim.draw_polyline_slab("INVOKE_DEFAULT")
elif (
relating_type_id and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "PROFILE" and relating_type_class not in {"IfcColumnType"}
relating_type_id
and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "PROFILE"
and relating_type_class not in {"IfcColumnType"}
):
bpy.ops.bim.draw_polyline_profile("INVOKE_DEFAULT")
else:
+3 -1
View File
@@ -197,7 +197,9 @@ def draw_psetqto_ui(
row = box.row(align=True)
row.scale_y = 0.8
row.label(text=prop["Name"])
op = row.operator("bim.select_similar", text=get_display_value(nominal_value), icon="NONE", emboss=False)
op = row.operator(
"bim.select_similar", text=get_display_value(nominal_value), icon="NONE", emboss=False
)
op.key = '"' + pset["Name"].replace('"', '\\"') + '"."' + prop["Name"].replace('"', '\\"') + '"'
# calculate sum of all selected objects
if active_operator:
+9 -7
View File
@@ -246,9 +246,10 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
)
should_play_chaching_sound: BoolProperty(name="Play A Cha-Ching Sound When Project Costs Updates", default=False)
spatial_elements_unselectable: BoolProperty(
name="Make Spatial Elements Unselectable By Default",
default=True,
description="If disabled, it will be possible to select spatial elements in the 3D viewport.\nIt is recommended to keep this `Enabled`, as this can have unintended consequences")
name="Make Spatial Elements Unselectable By Default",
default=True,
description="If disabled, it will be possible to select spatial elements in the 3D viewport.\nIt is recommended to keep this `Enabled`, as this can have unintended consequences",
)
decorations_colour: bpy.props.FloatVectorProperty(
name="Decorations Color", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
)
@@ -1191,15 +1192,16 @@ def draw_custom_context_menu(self: bpy.types.Menu, context: bpy.types.Context) -
url_op = layout.operator("bim.open_uri", icon="URL", text="Online IFC Documentation")
url_op.uri = url
class BIM_PT_decorators_overlay(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_parent_id = 'VIEW3D_PT_overlay'
bl_space_type = "VIEW_3D"
bl_region_type = "HEADER"
bl_parent_id = "VIEW3D_PT_overlay"
bl_label = "Bonsai Decorators"
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
return context.mode == "OBJECT"
def draw(self, context):
layout = self.layout
+6 -1
View File
@@ -86,12 +86,17 @@ def add_part_to_object(
assign_object(ifc, aggregator, collector, relating_obj=obj, related_obj=part_obj)
blender.set_active_object(obj)
def enable_aggregate_mode(aggregator: tool.Aggregate, obj: bpy.types.Object,):
def enable_aggregate_mode(
aggregator: tool.Aggregate,
obj: bpy.types.Object,
):
if aggregator.get_aggregate_mode():
disable_aggregate_mode(aggregator)
aggregator.enable_aggregate_mode(obj)
def disable_aggregate_mode(aggregator: tool.Aggregate):
aggregator.disable_aggregate_mode()
+1 -3
View File
@@ -1795,9 +1795,7 @@ class Geometry(bonsai.core.tool.Geometry):
return False
@classmethod
def get_bvh_tree(cls, obj:bpy.types.Object) -> BVHTree:
def get_bvh_tree(cls, obj: bpy.types.Object) -> BVHTree:
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
bm.transform(obj.matrix_world)
return BVHTree.FromBMesh(bm)
+1 -2
View File
@@ -983,8 +983,7 @@ class Loader(bonsai.core.tool.Loader):
tool.Loader.load_indexed_colour_map(rep, mesh)
ios_edges_values = [
(e.vertices[0], e.vertices[1]) in ios_edges
or (e.vertices[1], e.vertices[0]) in ios_edges
(e.vertices[0], e.vertices[1]) in ios_edges or (e.vertices[1], e.vertices[0]) in ios_edges
for e in mesh.edges
]
tool.Blender.Attribute.fill_attribute(mesh, "ios_edges", "EDGE", "BOOLEAN", ios_edges_values)
+1 -1
View File
@@ -206,7 +206,7 @@ class Raycast(bonsai.core.tool.Raycast):
bm.free()
snapping_points = []
sorted_points = sorted(points, key=lambda x : x[0])
sorted_points = sorted(points, key=lambda x: x[0])
for p in sorted_points:
point = copy.deepcopy(p)
snapping_points.append(point[1])
-1
View File
@@ -354,7 +354,6 @@ class Root(bonsai.core.tool.Root):
tool.Aggregate.constrain_all_parts_to_aggregate(tool.Ifc.get_object(new_aggregate[0]))
tool.Blender.select_and_activate_single_object(bpy.context, tool.Ifc.get_object(new_aggregate[0]))
@classmethod
def run_geometry_add_representation(
cls,