mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
New interface to show, select, and remove path connections
This commit is contained in:
@@ -25,17 +25,20 @@ classes = (
|
||||
operator.EditObjectPlacement,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideOutlinerDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
operator.OverrideDuplicateMoveLinked,
|
||||
operator.OverrideOutlinerDelete,
|
||||
operator.OverridePasteBuffer,
|
||||
operator.RemoveConnection,
|
||||
operator.RemoveRepresentation,
|
||||
operator.SelectConnection,
|
||||
operator.SwitchRepresentation,
|
||||
operator.UpdateParametricRepresentation,
|
||||
operator.UpdateRepresentation,
|
||||
prop.BIMGeometryProperties,
|
||||
ui.BIM_PT_derived_placements,
|
||||
ui.BIM_PT_representations,
|
||||
ui.BIM_PT_connections,
|
||||
ui.BIM_PT_mesh,
|
||||
ui.BIM_PT_workarounds,
|
||||
)
|
||||
|
||||
@@ -25,6 +25,7 @@ from mathutils import Vector
|
||||
def refresh():
|
||||
DerivedPlacementsData.is_loaded = False
|
||||
RepresentationsData.is_loaded = False
|
||||
ConnectionsData.is_loaded = False
|
||||
|
||||
|
||||
class RepresentationsData:
|
||||
@@ -64,6 +65,36 @@ class RepresentationsData:
|
||||
return results
|
||||
|
||||
|
||||
class ConnectionsData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"connections": cls.connections()}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def connections(cls):
|
||||
results = []
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
for rel in getattr(element, "ConnectedTo", []):
|
||||
results.append({
|
||||
"id": rel.id(),
|
||||
"is_relating": True,
|
||||
"Name": rel.RelatedElement.Name or "Unnamed",
|
||||
"ConnectionType": rel.RelatingConnectionType,
|
||||
})
|
||||
for rel in getattr(element, "ConnectedFrom", []):
|
||||
results.append({
|
||||
"id": rel.id(),
|
||||
"is_relating": False,
|
||||
"Name": rel.RelatingElement.Name or "Unnamed",
|
||||
"ConnectionType": rel.RelatedConnectionType,
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
class DerivedPlacementsData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@@ -86,6 +86,26 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class SelectConnection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.select_connection"
|
||||
bl_label = "Select Connection"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
connection: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_connection(tool.Geometry, connection=tool.Ifc.get().by_id(self.connection))
|
||||
|
||||
|
||||
class RemoveConnection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_connection"
|
||||
bl_label = "Remove Connection"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
connection: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_connection(tool.Geometry, connection=tool.Ifc.get().by_id(self.connection))
|
||||
|
||||
|
||||
class SwitchRepresentation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.switch_representation"
|
||||
bl_label = "Switch Representation"
|
||||
|
||||
@@ -20,7 +20,7 @@ import bpy
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from blenderbim.bim.module.geometry.data import RepresentationsData, DerivedPlacementsData
|
||||
from blenderbim.bim.module.geometry.data import RepresentationsData, ConnectionsData, DerivedPlacementsData
|
||||
|
||||
|
||||
class BIM_PT_representations(Panel):
|
||||
@@ -68,6 +68,43 @@ class BIM_PT_representations(Panel):
|
||||
row.operator("bim.remove_representation", icon="X", text="").representation_id = representation["id"]
|
||||
|
||||
|
||||
class BIM_PT_connections(Panel):
|
||||
bl_label = "IFC Connections"
|
||||
bl_idname = "BIM_PT_connections"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
bl_parent_id = "BIM_PT_geometry_object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
return False
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
if not ConnectionsData.is_loaded:
|
||||
ConnectionsData.load()
|
||||
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMObjectProperties
|
||||
|
||||
if not ConnectionsData.data["connections"]:
|
||||
layout.label(text="No connections found")
|
||||
|
||||
for connection in ConnectionsData.data["connections"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=connection["Name"], icon="SNAP_ON" if connection["is_relating"] else "SNAP_OFF")
|
||||
row.label(text=connection["ConnectionType"])
|
||||
op = row.operator("bim.select_connection", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.connection = connection["id"]
|
||||
op = row.operator("bim.remove_connection", icon="X", text="")
|
||||
op.connection = connection["id"]
|
||||
|
||||
|
||||
class BIM_PT_mesh(Panel):
|
||||
bl_label = "IFC Representation"
|
||||
bl_idname = "BIM_PT_mesh"
|
||||
|
||||
@@ -140,3 +140,11 @@ def remove_representation(ifc, geometry, obj=None, representation=None):
|
||||
geometry.replace_object_with_empty(obj)
|
||||
ifc.run("geometry.unassign_representation", product=element, representation=representation)
|
||||
ifc.run("geometry.remove_representation", representation=representation)
|
||||
|
||||
|
||||
def select_connection(geometry, connection=None):
|
||||
geometry.select_connection(connection)
|
||||
|
||||
|
||||
def remove_connection(geometry, connection=None):
|
||||
geometry.remove_connection(connection)
|
||||
|
||||
@@ -237,11 +237,13 @@ class Geometry:
|
||||
def link(cls, element, obj): pass
|
||||
def record_object_materials(cls, obj): pass
|
||||
def record_object_position(cls, obj): pass
|
||||
def remove_connection(cls, connection): pass
|
||||
def rename_object(cls, obj, name): pass
|
||||
def replace_object_with_empty(cls, obj): pass
|
||||
def resolve_mapped_representation(cls, representation): pass
|
||||
def run_geometry_update_representation(cls, obj=None): pass
|
||||
def run_style_add_style(cls, obj=None): pass
|
||||
def select_connection(cls, connection): pass
|
||||
def should_force_faceted_brep(cls): pass
|
||||
def should_force_triangulation(cls): pass
|
||||
def should_generate_uvs(cls, obj): pass
|
||||
@@ -428,7 +430,8 @@ class Resource:
|
||||
def expand_resource(cls, resource): pass
|
||||
def contract_resource(cls, resource): pass
|
||||
def import_resources(cls, file_path): pass
|
||||
|
||||
|
||||
|
||||
@interface
|
||||
class Root:
|
||||
def add_dynamic_opening_voids(cls, element, obj): pass
|
||||
@@ -483,7 +486,7 @@ class Sequence:
|
||||
def disable_editing_task(cls): pass
|
||||
def get_task_time_attributes(cls): pass
|
||||
def load_task_resources(cls,resources): pass
|
||||
def load_resources(cls): pass
|
||||
def load_resources(cls): pass
|
||||
def get_task_inputs(cls, task): pass
|
||||
def load_task_inputs(cls, inputs): pass
|
||||
def load_task_outputs(cls, outputs): pass
|
||||
|
||||
@@ -261,6 +261,10 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
|
||||
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
|
||||
|
||||
@classmethod
|
||||
def remove_connection(cls, connection):
|
||||
tool.Ifc.get().remove(connection)
|
||||
|
||||
@classmethod
|
||||
def rename_object(cls, obj, name):
|
||||
obj.name = name
|
||||
@@ -293,6 +297,15 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
def run_style_add_style(cls, obj=None):
|
||||
return blenderbim.core.style.add_style(tool.Ifc, tool.Style, obj=obj)
|
||||
|
||||
@classmethod
|
||||
def select_connection(cls, connection):
|
||||
obj = tool.Ifc.get_object(connection.RelatingElement)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
obj = tool.Ifc.get_object(connection.RelatedElement)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def should_force_faceted_brep(cls):
|
||||
return bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
|
||||
|
||||
@@ -382,3 +382,15 @@ class TestRemoveRepresentation:
|
||||
).should_be_called()
|
||||
ifc.run("geometry.remove_representation", representation="representation").should_be_called()
|
||||
subject.remove_representation(ifc, geometry, obj="obj", representation="representation")
|
||||
|
||||
|
||||
class TestSelectConnection:
|
||||
def test_run(self, geometry):
|
||||
geometry.select_connection("connection").should_be_called()
|
||||
subject.select_connection(geometry, connection="connection")
|
||||
|
||||
|
||||
class TestRemoveConnection:
|
||||
def test_run(self, geometry):
|
||||
geometry.remove_connection("connection").should_be_called()
|
||||
subject.remove_connection(geometry, connection="connection")
|
||||
|
||||
@@ -379,6 +379,17 @@ class TestRecordObjectPosition(NewFile):
|
||||
assert obj.BIMObjectProperties.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3()).tobytes())
|
||||
|
||||
|
||||
class TestRemoveConnection(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
element2 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
rel = ifcopenshell.api.run("geometry.connect_path", ifc, relating_element=element1, related_element=element2)
|
||||
subject.remove_connection(rel)
|
||||
assert not tool.Ifc.get().by_type("IfcRelConnectsPathElements")
|
||||
|
||||
|
||||
class TestRenameObject(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.meshes.new("Mesh")
|
||||
@@ -431,6 +442,28 @@ class TestRunStyleAddStyle(NewFile):
|
||||
pass
|
||||
|
||||
|
||||
class TestSelectConnection(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc().set(ifc)
|
||||
|
||||
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
obj1 = bpy.data.objects.new("Object", None)
|
||||
bpy.context.scene.collection.objects.link(obj1)
|
||||
tool.Ifc.link(element1, obj1)
|
||||
|
||||
element2 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
obj2 = bpy.data.objects.new("Object", None)
|
||||
bpy.context.scene.collection.objects.link(obj2)
|
||||
tool.Ifc.link(element2, obj2)
|
||||
|
||||
rel = ifcopenshell.api.run("geometry.connect_path", ifc, relating_element=element1, related_element=element2)
|
||||
|
||||
subject.select_connection(rel)
|
||||
assert obj1 in bpy.context.selected_objects
|
||||
assert obj2 in bpy.context.selected_objects
|
||||
|
||||
|
||||
class TestShouldForceFacetedBrep(NewFile):
|
||||
def test_run(self):
|
||||
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
|
||||
|
||||
Reference in New Issue
Block a user