mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
Add "nest mode" similar to "aggregate mode".
This commit is contained in:
@@ -6,6 +6,7 @@ IfcOpenShell
|
||||
<img src="https://github.com/IfcOpenShell/IfcOpenShell/assets/88302/34901387-e2dd-4a0c-8e38-9ffc32a66cde">
|
||||
</p>
|
||||
|
||||
|
||||
IfcOpenShell is an open source ([LGPL]) software library for working with Industry Foundation Classes ([IFC]). Complete
|
||||
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and [IFC4x3 Add2]. Extensive geometric support
|
||||
is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1]. Extending with support for arbitrary IFC schemas
|
||||
|
||||
@@ -1894,12 +1894,20 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bonsai.core.aggregate.enable_aggregate_mode(tool.Aggregate, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
if self.has_nests(selected_objs):
|
||||
if not context.scene.BIMNestProperties.in_nest_mode:
|
||||
bonsai.core.nest.enable_nest_mode(tool.Nest, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
if len(selected_objs) == 1 and context.active_object == selected_objs[0]:
|
||||
self.handle_single_object(context, context.active_object)
|
||||
elif len(selected_objs) == 0:
|
||||
if context.scene.BIMAggregateProperties.in_aggregate_mode:
|
||||
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
|
||||
return {"FINISHED"}
|
||||
if context.scene.BIMNestProperties.in_nest_mode:
|
||||
bonsai.core.nest.disable_nest_mode(tool.Nest)
|
||||
return {"FINISHED"}
|
||||
tool.Geometry.disable_item_mode()
|
||||
elif len(selected_objs) > 1:
|
||||
self.handle_multiple_selected_objects(context)
|
||||
@@ -2018,6 +2026,18 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
return False
|
||||
|
||||
def has_nests(self, objs):
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
nest = ifcopenshell.util.element.get_nest(element)
|
||||
components = ifcopenshell.util.element.get_components(element)
|
||||
if (nest or components) and not bpy.context.scene.BIMNestProperties.in_nest_mode:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Switch from Edit to Item or Object mode"
|
||||
@@ -3017,6 +3037,7 @@ class OverrideMoveAggregate(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def _execute(self, context):
|
||||
# Get aggregates
|
||||
props = context.scene.BIMAggregateProperties
|
||||
not_editing_objs = [o.obj for o in props.not_editing_objects]
|
||||
aggregates_to_move = []
|
||||
@@ -3039,10 +3060,47 @@ class OverrideMoveAggregate(bpy.types.Operator):
|
||||
aggregates_to_move.append(tool.Ifc.get_object(aggregate))
|
||||
obj.select_set(False)
|
||||
aggregates_to_move = set(aggregates_to_move)
|
||||
for obj in aggregates_to_move:
|
||||
obj.select_set(True)
|
||||
for part in tool.Aggregate.get_parts_recursively(tool.Ifc.get_entity(obj)):
|
||||
part_obj = tool.Ifc.get_object(part)
|
||||
part_obj.select_set(True)
|
||||
self.new_active_obj = obj
|
||||
|
||||
if aggregates_to_move:
|
||||
for obj in aggregates_to_move:
|
||||
obj.select_set(True)
|
||||
for part in tool.Aggregate.get_parts_recursively(tool.Ifc.get_entity(obj)):
|
||||
part_obj = tool.Ifc.get_object(part)
|
||||
part_obj.select_set(True)
|
||||
self.new_active_obj = obj
|
||||
return {"FINISHED"}
|
||||
|
||||
# Get nests
|
||||
props = context.scene.BIMNestProperties
|
||||
not_editing_objs = [o.obj for o in props.not_editing_objects]
|
||||
nests_to_move = []
|
||||
for obj in context.selected_objects:
|
||||
self.new_active_obj = None
|
||||
if obj in not_editing_objs:
|
||||
obj.select_set(False)
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not element.is_a("IfcElement"):
|
||||
continue
|
||||
components = ifcopenshell.util.element.get_components(element)
|
||||
if components:
|
||||
nests_to_move.append(tool.Ifc.get_object(element))
|
||||
continue
|
||||
if not components and props.in_nest_mode:
|
||||
continue
|
||||
nest = ifcopenshell.util.element.get_nest(element)
|
||||
if nest:
|
||||
nests_to_move.append(tool.Ifc.get_object(nest))
|
||||
obj.select_set(False)
|
||||
nests_to_move = set(nests_to_move)
|
||||
|
||||
if nests_to_move:
|
||||
for obj in nests_to_move:
|
||||
obj.select_set(True)
|
||||
for component in tool.Nest.get_components_recursively(tool.Ifc.get_entity(obj)):
|
||||
component_obj = tool.Ifc.get_object(component)
|
||||
component_obj.select_set(True)
|
||||
self.new_active_obj = obj
|
||||
return {"FINISHED"}
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -27,13 +27,17 @@ classes = (
|
||||
operator.BIM_OT_select_nest,
|
||||
operator.BIM_OT_nest_unassign_object,
|
||||
prop.BIMObjectNestProperties,
|
||||
prop.Objects,
|
||||
prop.BIMNestProperties,
|
||||
ui.BIM_PT_nest,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Object.BIMObjectNestProperties = bpy.props.PointerProperty(type=prop.BIMObjectNestProperties)
|
||||
bpy.types.Scene.BIMNestProperties = bpy.props.PointerProperty(type=prop.BIMNestProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Object.BIMObjectNestProperties
|
||||
del bpy.types.Scene.BIMNestProperties
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bonsai.bim.module.spatial.data import SpatialData
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -30,6 +31,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from bonsai.bim.module.nest.decorator import NestDecorator, NestModeDecorator
|
||||
|
||||
|
||||
def update_relating_object(self, context):
|
||||
@@ -46,3 +48,33 @@ def update_relating_object(self, context):
|
||||
class BIMObjectNestProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_object: PointerProperty(name="Nest Host", type=bpy.types.Object, update=update_relating_object)
|
||||
|
||||
def update_nest_decorator(self, context):
|
||||
if self.nest_decorator:
|
||||
NestDecorator.install(bpy.context)
|
||||
else:
|
||||
NestDecorator.uninstall()
|
||||
|
||||
|
||||
def update_nest_mode_decorator(self, context):
|
||||
if self.in_nest_mode:
|
||||
NestModeDecorator.install(bpy.context)
|
||||
else:
|
||||
NestModeDecorator.uninstall()
|
||||
|
||||
|
||||
class Objects(bpy.types.PropertyGroup):
|
||||
obj: PointerProperty(type=bpy.types.Object)
|
||||
previous_display_type: bpy.props.StringProperty(default="TEXTURED")
|
||||
|
||||
|
||||
class BIMNestProperties(PropertyGroup):
|
||||
in_nest_mode: BoolProperty(name="In Edit Mode", update=update_nest_mode_decorator)
|
||||
editing_nest: PointerProperty(name="Editing nest", type=bpy.types.Object)
|
||||
editing_objects: CollectionProperty(type=Objects)
|
||||
not_editing_objects: CollectionProperty(type=Objects)
|
||||
nest_decorator: BoolProperty(
|
||||
name="Display Nest",
|
||||
default=False,
|
||||
update=update_nest_decorator,
|
||||
)
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
|
||||
def enable_editing_nest(nest, obj=None):
|
||||
nest.enable_editing(obj)
|
||||
@@ -58,3 +65,17 @@ def add_part_to_object(ifc, nest, collector, blender, obj, part_class, part_name
|
||||
part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name)
|
||||
assign_object(ifc, nest, collector, relating_obj=obj, related_obj=part_obj)
|
||||
blender.set_active_object(obj)
|
||||
|
||||
|
||||
def enable_nest_mode(
|
||||
nest: tool.Nest,
|
||||
obj: bpy.types.Object,
|
||||
):
|
||||
if nest.get_nest_mode():
|
||||
disable_nest_mode(nest)
|
||||
|
||||
nest.enable_nest_mode(obj)
|
||||
|
||||
|
||||
def disable_nest_mode(nest: tool.Nest):
|
||||
nest.disable_nest_mode()
|
||||
|
||||
@@ -90,5 +90,7 @@ def assign_class(
|
||||
ifc.run("spatial.assign_container", products=[element], relating_structure=default_container)
|
||||
if relating_obj := root.is_in_aggregate_mode(element):
|
||||
ifc.run("aggregate.assign_object", products=[element], relating_object=relating_obj)
|
||||
if relating_obj := root.is_in_nest_mode(element):
|
||||
ifc.run("nest.assign_object", related_objects=[element], relating_object=relating_obj)
|
||||
collector.assign(obj)
|
||||
return element
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
@@ -47,3 +48,75 @@ class Nest(bonsai.core.tool.Nest):
|
||||
@classmethod
|
||||
def get_relating_object(cls, related_element):
|
||||
return ifcopenshell.util.element.get_nest(related_element)
|
||||
|
||||
@classmethod
|
||||
def get_components_recursively(cls, element: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Get elements components recursively, resulting set includes `element`."""
|
||||
components = set()
|
||||
queue = {element}
|
||||
while queue:
|
||||
element = queue.pop()
|
||||
queue.update(new_components := set(ifcopenshell.util.element.get_components(element)))
|
||||
components.update(new_components)
|
||||
return components
|
||||
|
||||
@classmethod
|
||||
def get_nest_mode(cls):
|
||||
return bpy.context.scene.BIMNestProperties.in_nest_mode
|
||||
|
||||
@classmethod
|
||||
def enable_nest_mode(cls, active_object: bpy.types.Object):
|
||||
context = bpy.context
|
||||
props = context.scene.BIMNestProperties
|
||||
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
if not element:
|
||||
return {"FINISHED"}
|
||||
nest = ifcopenshell.util.element.get_nest(element)
|
||||
components = ifcopenshell.util.element.get_components(element)
|
||||
if not nest and not components:
|
||||
return {"FINISHED"}
|
||||
if not components:
|
||||
components = ifcopenshell.util.element.get_components(nest)
|
||||
if components:
|
||||
props.editing_nest = tool.Ifc.get_object(nest) if nest else tool.Ifc.get_object(element)
|
||||
components_objs = [tool.Ifc.get_object(component) for component in components]
|
||||
objs = []
|
||||
visible_objects = tool.Raycast.get_visible_objects(context)
|
||||
for obj in visible_objects:
|
||||
if obj.visible_in_viewport_get(context.space_data):
|
||||
objs.append(obj.original)
|
||||
for obj in objs:
|
||||
if obj.original not in components_objs:
|
||||
if obj == props.editing_nest:
|
||||
continue
|
||||
not_editing_obj = props.not_editing_objects.add()
|
||||
not_editing_obj.obj = obj.original
|
||||
not_editing_obj.previous_display_type = obj.original.display_type
|
||||
obj.original.display_type = "WIRE"
|
||||
else:
|
||||
editing_obj = props.editing_objects.add()
|
||||
editing_obj.obj = obj.original
|
||||
|
||||
props.in_nest_mode = True
|
||||
return {"FINISHED"}
|
||||
|
||||
@classmethod
|
||||
def disable_nest_mode(cls):
|
||||
context = bpy.context
|
||||
props = context.scene.BIMNestProperties
|
||||
for obj_prop in props.not_editing_objects:
|
||||
obj = obj_prop.obj
|
||||
obj.original.display_type = obj_prop.previous_display_type
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(props.editing_nest))
|
||||
objs = [tool.Ifc.get_object(component) for component in components]
|
||||
if context.space_data.local_view:
|
||||
bpy.ops.view3d.localview()
|
||||
|
||||
props.in_nest_mode = False
|
||||
props.not_editing_objects.clear()
|
||||
props.editing_objects.clear()
|
||||
|
||||
@@ -223,6 +223,12 @@ class Root(bonsai.core.tool.Root):
|
||||
if props.editing_aggregate and props.in_aggregate_mode:
|
||||
return tool.Ifc.get_entity(props.editing_aggregate)
|
||||
|
||||
@classmethod
|
||||
def is_in_nest_mode(cls, element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
props = bpy.context.scene.BIMNestProperties
|
||||
if props.editing_nest and props.in_nest_mode:
|
||||
return tool.Ifc.get_entity(props.editing_nest)
|
||||
|
||||
@classmethod
|
||||
def reload_grid_decorator(cls) -> None:
|
||||
axes = bpy.context.scene.BIMGridProperties.grid_axes
|
||||
|
||||
Reference in New Issue
Block a user