mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 18:21:59 +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">
|
<img src="https://github.com/IfcOpenShell/IfcOpenShell/assets/88302/34901387-e2dd-4a0c-8e38-9ffc32a66cde">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
IfcOpenShell is an open source ([LGPL]) software library for working with Industry Foundation Classes ([IFC]). Complete
|
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
|
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
|
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)
|
bonsai.core.aggregate.enable_aggregate_mode(tool.Aggregate, context.active_object)
|
||||||
return {"FINISHED"}
|
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]:
|
if len(selected_objs) == 1 and context.active_object == selected_objs[0]:
|
||||||
self.handle_single_object(context, context.active_object)
|
self.handle_single_object(context, context.active_object)
|
||||||
elif len(selected_objs) == 0:
|
elif len(selected_objs) == 0:
|
||||||
if context.scene.BIMAggregateProperties.in_aggregate_mode:
|
if context.scene.BIMAggregateProperties.in_aggregate_mode:
|
||||||
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
|
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
if context.scene.BIMNestProperties.in_nest_mode:
|
||||||
|
bonsai.core.nest.disable_nest_mode(tool.Nest)
|
||||||
|
return {"FINISHED"}
|
||||||
tool.Geometry.disable_item_mode()
|
tool.Geometry.disable_item_mode()
|
||||||
elif len(selected_objs) > 1:
|
elif len(selected_objs) > 1:
|
||||||
self.handle_multiple_selected_objects(context)
|
self.handle_multiple_selected_objects(context)
|
||||||
@@ -2018,6 +2026,18 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
else:
|
else:
|
||||||
return False
|
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):
|
class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_description = "Switch from Edit to Item or Object mode"
|
bl_description = "Switch from Edit to Item or Object mode"
|
||||||
@@ -3017,6 +3037,7 @@ class OverrideMoveAggregate(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
|
# Get aggregates
|
||||||
props = context.scene.BIMAggregateProperties
|
props = context.scene.BIMAggregateProperties
|
||||||
not_editing_objs = [o.obj for o in props.not_editing_objects]
|
not_editing_objs = [o.obj for o in props.not_editing_objects]
|
||||||
aggregates_to_move = []
|
aggregates_to_move = []
|
||||||
@@ -3039,10 +3060,47 @@ class OverrideMoveAggregate(bpy.types.Operator):
|
|||||||
aggregates_to_move.append(tool.Ifc.get_object(aggregate))
|
aggregates_to_move.append(tool.Ifc.get_object(aggregate))
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
aggregates_to_move = set(aggregates_to_move)
|
aggregates_to_move = set(aggregates_to_move)
|
||||||
for obj in aggregates_to_move:
|
|
||||||
obj.select_set(True)
|
if aggregates_to_move:
|
||||||
for part in tool.Aggregate.get_parts_recursively(tool.Ifc.get_entity(obj)):
|
for obj in aggregates_to_move:
|
||||||
part_obj = tool.Ifc.get_object(part)
|
obj.select_set(True)
|
||||||
part_obj.select_set(True)
|
for part in tool.Aggregate.get_parts_recursively(tool.Ifc.get_entity(obj)):
|
||||||
self.new_active_obj = 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"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -27,13 +27,17 @@ classes = (
|
|||||||
operator.BIM_OT_select_nest,
|
operator.BIM_OT_select_nest,
|
||||||
operator.BIM_OT_nest_unassign_object,
|
operator.BIM_OT_nest_unassign_object,
|
||||||
prop.BIMObjectNestProperties,
|
prop.BIMObjectNestProperties,
|
||||||
|
prop.Objects,
|
||||||
|
prop.BIMNestProperties,
|
||||||
ui.BIM_PT_nest,
|
ui.BIM_PT_nest,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Object.BIMObjectNestProperties = bpy.props.PointerProperty(type=prop.BIMObjectNestProperties)
|
bpy.types.Object.BIMObjectNestProperties = bpy.props.PointerProperty(type=prop.BIMObjectNestProperties)
|
||||||
|
bpy.types.Scene.BIMNestProperties = bpy.props.PointerProperty(type=prop.BIMNestProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Object.BIMObjectNestProperties
|
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/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import bonsai.tool as tool
|
||||||
from bonsai.bim.prop import StrProperty, Attribute
|
from bonsai.bim.prop import StrProperty, Attribute
|
||||||
from bonsai.bim.module.spatial.data import SpatialData
|
from bonsai.bim.module.spatial.data import SpatialData
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -30,6 +31,7 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
|
from bonsai.bim.module.nest.decorator import NestDecorator, NestModeDecorator
|
||||||
|
|
||||||
|
|
||||||
def update_relating_object(self, context):
|
def update_relating_object(self, context):
|
||||||
@@ -46,3 +48,33 @@ def update_relating_object(self, context):
|
|||||||
class BIMObjectNestProperties(PropertyGroup):
|
class BIMObjectNestProperties(PropertyGroup):
|
||||||
is_editing: BoolProperty(name="Is Editing")
|
is_editing: BoolProperty(name="Is Editing")
|
||||||
relating_object: PointerProperty(name="Nest Host", type=bpy.types.Object, update=update_relating_object)
|
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
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# 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):
|
def enable_editing_nest(nest, obj=None):
|
||||||
nest.enable_editing(obj)
|
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)
|
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)
|
assign_object(ifc, nest, collector, relating_obj=obj, related_obj=part_obj)
|
||||||
blender.set_active_object(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)
|
ifc.run("spatial.assign_container", products=[element], relating_structure=default_container)
|
||||||
if relating_obj := root.is_in_aggregate_mode(element):
|
if relating_obj := root.is_in_aggregate_mode(element):
|
||||||
ifc.run("aggregate.assign_object", products=[element], relating_object=relating_obj)
|
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)
|
collector.assign(obj)
|
||||||
return element
|
return element
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
@@ -47,3 +48,75 @@ class Nest(bonsai.core.tool.Nest):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_relating_object(cls, related_element):
|
def get_relating_object(cls, related_element):
|
||||||
return ifcopenshell.util.element.get_nest(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:
|
if props.editing_aggregate and props.in_aggregate_mode:
|
||||||
return tool.Ifc.get_entity(props.editing_aggregate)
|
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
|
@classmethod
|
||||||
def reload_grid_decorator(cls) -> None:
|
def reload_grid_decorator(cls) -> None:
|
||||||
axes = bpy.context.scene.BIMGridProperties.grid_axes
|
axes = bpy.context.scene.BIMGridProperties.grid_axes
|
||||||
|
|||||||
Reference in New Issue
Block a user