Merge branch 'v0.8.0' of github.com:IfcOpenShell/IfcOpenShell into v0.8.0

This commit is contained in:
Bruno Postle
2025-01-18 19:18:15 +00:00
22 changed files with 517 additions and 77 deletions
+6 -2
View File
@@ -33,6 +33,7 @@ from bonsai.bim.module.model.workspace import LIST_OF_TOOLS, TOOLS_TO_CLASSES_MA
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator
from bonsai.bim.module.nest.decorator import NestDecorator
from mathutils import Vector
from math import cos, degrees
from typing import Union, Callable
@@ -323,11 +324,14 @@ def load_post(scene):
# Bonsai overlays
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
aggregate_props = bpy.context.scene.BIMAggregateProperties
nest_props = bpy.context.scene.BIMNestProperties
model_props = bpy.context.scene.BIMModelProperties
if aggregate_props.aggregate_decorator:
AggregateDecorator.install(bpy.context)
if georeference_props.should_visualise:
GeoreferenceDecorator.install(bpy.context)
if aggregate_props.aggregate_decorator:
AggregateDecorator.install(bpy.context)
if nest_props.nest_decorator:
NestDecorator.install(bpy.context)
if model_props.show_wall_axis:
WallAxisDecorator.install(bpy.context)
if model_props.show_slab_direction:
@@ -167,6 +167,8 @@ class AggregateDecorator:
theme = context.preferences.themes.items()[0][1]
selected_object_color = (*theme.view_3d.object_active, 1)
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader.bind()
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
self.line_shader.bind()
# POLYLINE_UNIFORM_COLOR specific uniforms
@@ -3037,6 +3037,23 @@ class OverrideMove(bpy.types.Operator):
return {"FINISHED"}
def _execute(self, context):
# Get filling objects
selection = []
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
selection.append(obj)
for rel in getattr(element, "HasOpenings", []) or []:
opening = rel.RelatedOpeningElement
for rel2 in getattr(opening, "HasFillings", []) or []:
filling = rel2.RelatedBuildingElement
selection.append(tool.Ifc.get_object(filling))
for obj in selection:
obj.select_set(True)
self.new_active_obj = obj
# Get aggregates
props = context.scene.BIMAggregateProperties
not_editing_objs = [o.obj for o in props.not_editing_objects]
@@ -3046,6 +3063,8 @@ class OverrideMove(bpy.types.Operator):
if obj in not_editing_objs:
obj.select_set(False)
continue
if obj == props.editing_aggregate:
continue
element = tool.Ifc.get_entity(obj)
if not element or not element.is_a("IfcElement"):
continue
@@ -3079,6 +3098,8 @@ class OverrideMove(bpy.types.Operator):
if obj in not_editing_objs:
obj.select_set(False)
continue
if obj == props.editing_nest:
continue
element = tool.Ifc.get_entity(obj)
if not element or not element.is_a("IfcElement"):
continue
@@ -88,6 +88,7 @@ classes = (
opening.HideOpenings,
opening.PurgeUnusedOpenings,
opening.RecalculateFill,
opening.RemoveBoolean,
opening.RemoveBooleans,
opening.ShowBooleans,
opening.ShowOpenings,
@@ -1144,6 +1144,27 @@ class PurgeUnusedOpenings(Operator, tool.Ifc.Operator):
return {"FINISHED"}
class RemoveBoolean(Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_boolean"
bl_label = "Remove Boolean"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Removes the actively selected boolean"
@classmethod
def poll(cls, context):
props = context.scene.BIMBooleanProperties
return props.active_boolean
def _execute(self, context):
props = context.scene.BIMBooleanProperties
ifcopenshell.api.geometry.remove_boolean(
tool.Ifc.get(), tool.Ifc.get().by_id(props.active_boolean.ifc_definition_id)
)
bpy.ops.bim.enable_editing_booleans()
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
tool.Geometry.reload_representation(rep_obj)
# TODO: merge with ProfileDecorator?
class DecorationsHandler:
installed = None
@@ -590,6 +590,11 @@ class EditObjectUI:
row = cls.layout.row(align=True)
op = row.operator("bim.disable_aggregate_mode", text="", icon="X")
op = row.operator("bim.toggle_aggregate_mode_local_view", text="", icon="ZOOM_SELECTED")
if context.scene.BIMNestProperties.in_nest_mode:
layout.label(text=f"Nest Mode", icon="EMPTY_AXIS")
row = cls.layout.row(align=True)
op = row.operator("bim.disable_nest_mode", text="", icon="X")
op = row.operator("bim.toggle_nest_mode_local_view", text="", icon="ZOOM_SELECTED")
text = format_ifc_camel_case(AuthoringData.data["active_class"])
layout.label(text=f"{text} Edit Tools:", icon="RESTRICT_SELECT_OFF")
@@ -26,6 +26,8 @@ classes = (
operator.BIM_OT_select_components,
operator.BIM_OT_select_nest,
operator.BIM_OT_nest_unassign_object,
operator.BIM_OT_disable_nest_mode,
operator.BIM_OT_toggle_nest_mode_local_view,
prop.BIMObjectNestProperties,
prop.Objects,
prop.BIMNestProperties,
+32 -26
View File
@@ -86,7 +86,7 @@ class NestDecorator:
if cls.is_installed:
cls.uninstall()
handler = cls()
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_aggregate, (context,), "WINDOW", "POST_VIEW"))
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_nest, (context,), "WINDOW", "POST_VIEW"))
cls.is_installed = True
@classmethod
@@ -153,8 +153,8 @@ class NestDecorator:
shader.uniform_float("color", color)
batch.draw(shader)
def draw_aggregate(self, context):
if context.scene.BIMAggregateProperties.in_aggregate_mode:
def draw_nest(self, context):
if context.scene.BIMNestProperties.in_nest_mode:
return
self.addon_prefs = tool.Blender.get_addon_preferences()
decorator_color_special = self.addon_prefs.decorator_color_special
@@ -165,11 +165,13 @@ class NestDecorator:
theme = context.preferences.themes.items()[0][1]
selected_object_color = (*theme.view_3d.object_active, 1)
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader.bind()
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
self.line_shader.bind()
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
aggregates = []
nests = []
if not (selected_objects := context.selected_objects):
return
for obj in selected_objects:
@@ -177,35 +179,39 @@ class NestDecorator:
if not element or not element.is_a("IfcElement"):
return
parts = ifcopenshell.util.element.get_parts(element)
if parts:
aggregates.append(obj)
components = ifcopenshell.util.element.get_components(element)
if components:
nests.append(obj)
continue
aggregate = ifcopenshell.util.element.get_aggregate(element)
if aggregate:
aggregates.append(tool.Ifc.get_object(aggregate))
nest = ifcopenshell.util.element.get_nest(element)
if nest:
nests.append(tool.Ifc.get_object(nest))
aggregates = set(aggregates)
for aggregate in aggregates:
nests = set(nests)
for nest in nests:
self.line_shader.uniform_float("lineWidth", 1.0)
color = decorator_color_unselected
if aggregate in selected_objects:
if nest in selected_objects:
color = selected_object_color
size = aggregate.empty_display_size
location = aggregate.location
line_x = (location - Vector((size, 0.0, 0.0)), location + Vector((size, 0.0, 0.0)))
self.draw_batch("LINES", line_x, color, [(0, 1)])
line_y = (location - Vector((0.0, size, 0.0)), location + Vector((0.0, size, 0.0)))
self.draw_batch("LINES", line_y, color, [(0, 1)])
line_z = (location - Vector((0.0, 0.0, size)), location + Vector((0.0, 0.0, size)))
self.draw_batch("LINES", line_z, color, [(0, 1)])
if context.scene.BIMAggregateProperties.in_aggregate_mode:
return
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(aggregate))
parts_objs = [tool.Ifc.get_object(p) for p in parts]
size = nest.empty_display_size
location = nest.location
if nest.type == "EMPTY":
line_x = (location - Vector((size, 0.0, 0.0)), location + Vector((size, 0.0, 0.0)))
self.draw_batch("LINES", line_x, color, [(0, 1)])
line_y = (location - Vector((0.0, size, 0.0)), location + Vector((0.0, size, 0.0)))
self.draw_batch("LINES", line_y, color, [(0, 1)])
line_z = (location - Vector((0.0, 0.0, size)), location + Vector((0.0, 0.0, size)))
self.draw_batch("LINES", line_z, color, [(0, 1)])
else:
self.draw_batch("POINTS", [location], color)
# if context.scene.BIMNestProperties.in_aggregate_mode:
# return
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(nest))
components_objs = [tool.Ifc.get_object(p) for p in components]
components_objs.append(nest)
indices, edges = create_bounding_box(parts_objs)
indices, edges = create_bounding_box(components_objs)
self.line_shader.uniform_float("lineWidth", 0.5)
self.draw_batch("LINES", indices, color, edges)
line = (Vector(indices[0]), location)
@@ -140,3 +140,33 @@ class BIM_OT_select_nest(bpy.types.Operator):
nest_obj.select_set(True)
bpy.context.view_layer.objects.active = nest_obj
return {"FINISHED"}
class BIM_OT_disable_nest_mode(bpy.types.Operator):
bl_idname = "bim.disable_nest_mode"
bl_label = "Disable Nest Mode"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bpy.ops.object.select_all(action="DESELECT")
core.disable_nest_mode(tool.Nest)
return {"FINISHED"}
class BIM_OT_toggle_nest_mode_local_view(bpy.types.Operator):
bl_idname = "bim.toggle_nest_mode_local_view"
bl_label = "Toggle Nest Mode Local View"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMNestProperties
objs = [o.obj for o in props.editing_objects]
if props.in_nest_mode:
if context.space_data.local_view:
bpy.ops.view3d.localview()
else:
for obj in objs:
obj.select_set(True)
bpy.ops.view3d.localview()
return {"FINISHED"}
@@ -45,3 +45,8 @@ class BIMBooleanProperties(PropertyGroup):
name="Operator",
default="DIFFERENCE",
)
@property
def active_boolean(self):
if self.booleans and self.active_boolean_index < len(self.booleans):
return self.booleans[self.active_boolean_index]
+4
View File
@@ -176,6 +176,10 @@ class BIM_PT_booleans(Panel):
row.prop(props, "operator", text="")
row.operator("bim.add_boolean", text="", icon="ADD")
row = layout.row(align=True)
row.alignment = "RIGHT"
row.operator("bim.remove_boolean", text="", icon="X")
self.layout.template_list("BIM_UL_booleans", "", props, "booleans", props, "active_boolean_index")
+8 -5
View File
@@ -1209,8 +1209,9 @@ class BIM_PT_decorators_overlay(Panel):
view = context.space_data
overlay = view.overlay
geo_props = bpy.context.scene.BIMGeoreferenceProperties
agg_props = bpy.context.scene.BIMAggregateProperties
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
aggregate_props = bpy.context.scene.BIMAggregateProperties
nest_props = bpy.context.scene.BIMNestProperties
model_props = bpy.context.scene.BIMModelProperties
display_all = overlay.show_overlays
@@ -1218,10 +1219,12 @@ class BIM_PT_decorators_overlay(Panel):
col.active = display_all
row = col.row(align=True)
row.prop(geo_props, "should_visualise", text="Georeference")
row.prop(geo_props, "visualization_scale", text="Size", slider=True)
row.prop(georeference_props, "should_visualise", text="Georeference")
row.prop(georeference_props, "visualization_scale", text="Size", slider=True)
row = col.row(align=True)
row.prop(agg_props, "aggregate_decorator", text="Aggregate")
row.prop(aggregate_props, "aggregate_decorator", text="Aggregate")
row = col.row(align=True)
row.prop(nest_props, "nest_decorator", text="Nest")
row = col.row(align=True)
row.prop(model_props, "show_wall_axis", text="Wall Axis")
row = col.row(align=True)
+1
View File
@@ -145,3 +145,4 @@ class Aggregate(bonsai.core.tool.Aggregate):
props.in_aggregate_mode = False
props.not_editing_objects.clear()
props.editing_objects.clear()
props.editing_aggregate = None
+2
View File
@@ -1742,6 +1742,8 @@ class Geometry(bonsai.core.tool.Geometry):
cls.unlock_object(props.representation_obj)
tool.Blender.set_active_object(props.representation_obj)
cls.sync_item_positions()
representation = cls.get_active_representation(props.representation_obj)
ifcopenshell.api.geometry.validate_type(tool.Ifc.get(), representation)
props.is_changing_mode = True
if props.mode != "OBJECT":
props.mode = "OBJECT"
+1
View File
@@ -120,3 +120,4 @@ class Nest(bonsai.core.tool.Nest):
props.in_nest_mode = False
props.not_editing_objects.clear()
props.editing_objects.clear()
props.editing_nest = None