You can now show and hide distribution ports of an element.

This commit is contained in:
Dion Moult
2022-01-29 08:52:37 +11:00
parent 92a1739d93
commit 9279aeccf6
17 changed files with 194 additions and 18 deletions
+8 -4
View File
@@ -21,6 +21,7 @@ import bpy
import time import time
import bmesh import bmesh
import shutil import shutil
import logging
import threading import threading
import mathutils import mathutils
import numpy as np import numpy as np
@@ -1610,7 +1611,8 @@ class IfcImporter:
if "-" in geometry.id: if "-" in geometry.id:
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0]) mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0])
else: else:
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id) # TODO: See #2002
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.replace(",", ""))
def set_matrix_world(self, obj, matrix_world): def set_matrix_world(self, obj, matrix_world):
obj.matrix_world = matrix_world obj.matrix_world = matrix_world
@@ -1637,11 +1639,13 @@ class IfcImportSettings:
self.collection_mode = "DECOMPOSITION" self.collection_mode = "DECOMPOSITION"
@staticmethod @staticmethod
def factory(context, input_file, logger): def factory(context=None, input_file=None, logger=None):
scene_diff = context.scene.DiffProperties scene_diff = bpy.context.scene.DiffProperties
props = context.scene.BIMProjectProperties props = bpy.context.scene.BIMProjectProperties
settings = IfcImportSettings() settings = IfcImportSettings()
settings.input_file = input_file settings.input_file = input_file
if logger is None:
logger = logging.getLogger("ImportIFC")
settings.logger = logger settings.logger = logger
settings.diff_file = scene_diff.diff_json_file settings.diff_file = scene_diff.diff_json_file
settings.collection_mode = props.collection_mode settings.collection_mode = props.collection_mode
@@ -81,8 +81,6 @@ class BIM_PT_material_attributes(Panel):
bl_space_type = "PROPERTIES" bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW" bl_region_type = "WINDOW"
bl_context = "material" bl_context = "material"
bl_parent_id = "BIM_PT_object_metadata"
bl_options = {"DEFAULT_CLOSED"}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@@ -32,6 +32,7 @@
import bpy import bpy
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
# Properties have many different data types. We won't use all of them in this # Properties have many different data types. We won't use all of them in this
# demo module, but this is a list for your reference. # demo module, but this is a list for your reference.
from bpy.props import ( from bpy.props import (
@@ -53,5 +53,8 @@ def update_ifc_class(self, context):
class BIMModelProperties(PropertyGroup): class BIMModelProperties(PropertyGroup):
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class)
relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type") relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type")
occurrence_name_style: bpy.props.EnumProperty(items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")], name="Occurrence Name Style") occurrence_name_style: bpy.props.EnumProperty(
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
name="Occurrence Name Style",
)
occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function")
@@ -26,14 +26,17 @@ classes = (
operator.DisableSystemEditingUI, operator.DisableSystemEditingUI,
operator.EditSystem, operator.EditSystem,
operator.EnableEditingSystem, operator.EnableEditingSystem,
operator.HidePorts,
operator.LoadSystems, operator.LoadSystems,
operator.RemoveSystem, operator.RemoveSystem,
operator.SelectSystemProducts, operator.SelectSystemProducts,
operator.ShowPorts,
operator.UnassignSystem, operator.UnassignSystem,
prop.System, prop.System,
prop.BIMSystemProperties, prop.BIMSystemProperties,
ui.BIM_PT_systems, ui.BIM_PT_systems,
ui.BIM_PT_object_systems, ui.BIM_PT_object_systems,
ui.BIM_PT_ports,
ui.BIM_UL_systems, ui.BIM_UL_systems,
ui.BIM_UL_object_systems, ui.BIM_UL_object_systems,
) )
@@ -80,3 +80,20 @@ class ObjectSystemData:
@classmethod @classmethod
def total_systems(cls): def total_systems(cls):
return len(tool.Ifc.get().by_type("IfcSystem")) return len(tool.Ifc.get().by_type("IfcSystem"))
class PortData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"total_ports": cls.total_ports(),
}
cls.is_loaded = True
@classmethod
def total_ports(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
return len(ifcopenshell.util.system.get_ports(element))
@@ -132,3 +132,21 @@ class SelectSystemProducts(bpy.types.Operator, Operator):
def _execute(self, context): def _execute(self, context):
core.select_system_products(tool.System, system=tool.Ifc.get().by_id(self.system)) core.select_system_products(tool.System, system=tool.Ifc.get().by_id(self.system))
class ShowPorts(bpy.types.Operator, Operator):
bl_idname = "bim.show_ports"
bl_label = "Show Ports"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.show_ports(tool.System, element=tool.Ifc.get_entity(context.active_object))
class HidePorts(bpy.types.Operator, Operator):
bl_idname = "bim.hide_ports"
bl_label = "Hide Ports"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.hide_ports(tool.System, element=tool.Ifc.get_entity(context.active_object))
@@ -16,9 +16,10 @@
# 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.system.data import SystemData, ObjectSystemData from blenderbim.bim.module.system.data import SystemData, ObjectSystemData, PortData
class BIM_PT_systems(Panel): class BIM_PT_systems(Panel):
@@ -125,6 +126,35 @@ class BIM_PT_object_systems(Panel):
self.layout.label(text="No System associated with Active Object") self.layout.label(text="No System associated with Active Object")
class BIM_PT_ports(Panel):
bl_label = "IFC Ports"
bl_idname = "BIM_PT_ports"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_services_object"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
element = tool.Ifc.get_entity(context.active_object)
if not element or not element.is_a("IfcDistributionElement"):
return False
return True
def draw(self, context):
if not PortData.is_loaded:
PortData.load()
self.props = context.scene.BIMSystemProperties
row = self.layout.row(align=True)
row.label(text=f"{PortData.data['total_ports']} Ports Found", icon="PLUGIN")
row.operator("bim.show_ports", icon="HIDE_OFF", text="")
row.operator("bim.hide_ports", icon="HIDE_ON", text="")
class BIM_UL_systems(UIList): class BIM_UL_systems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
system_icons = { system_icons = {
+10
View File
@@ -64,3 +64,13 @@ def unassign_system(ifc, system=None, product=None):
def select_system_products(system_tool, system=None): def select_system_products(system_tool, system=None):
system_tool.select_system_products(system) system_tool.select_system_products(system)
def show_ports(system, element=None):
ports = system.get_ports(element)
system.load_ports(ports)
system.select_elements(ports)
def hide_ports(system, element=None):
system.delete_element_objects(system.get_ports(element))
+4
View File
@@ -359,12 +359,16 @@ class Surveyor:
@interface @interface
class System: class System:
def delete_element_objects(cls, elements): pass
def disable_editing_system(cls): pass def disable_editing_system(cls): pass
def disable_system_editing_ui(cls): pass def disable_system_editing_ui(cls): pass
def enable_system_editing_ui(cls): pass def enable_system_editing_ui(cls): pass
def export_system_attributes(cls): pass def export_system_attributes(cls): pass
def get_ports(cls, element): pass
def import_system_attributes(cls, system): pass def import_system_attributes(cls, system): pass
def import_systems(cls): pass def import_systems(cls): pass
def load_ports(cls, port): pass
def select_elements(cls, elements): pass
def select_system_products(cls, system): pass def select_system_products(cls, system): pass
def set_active_system(cls, system): pass def set_active_system(cls, system): pass
@@ -58,9 +58,7 @@ class Boundary(blenderbim.core.tool.Boundary):
def polyline_from_indexes(cls, mesh, indexes): def polyline_from_indexes(cls, mesh, indexes):
return tuple(mesh.vertices[i].co for i in indexes) return tuple(mesh.vertices[i].co for i in indexes)
@classmethod @classmethod
def polyline_to_2d(cls, polyline, placement_matrix): def polyline_to_2d(cls, polyline, placement_matrix):
matrix_inv = placement_matrix.inverted() matrix_inv = placement_matrix.inverted()
return tuple((matrix_inv @ v).to_2d() for v in polyline) return tuple((matrix_inv @ v).to_2d() for v in polyline)
+33 -2
View File
@@ -20,9 +20,17 @@ import bpy
import ifcopenshell.util.system import ifcopenshell.util.system
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim import import_ifc
class System(blenderbim.core.tool.System): class System(blenderbim.core.tool.System):
@classmethod
def delete_element_objects(cls, elements):
for element in elements:
obj = tool.Ifc.get_object(element)
if obj:
bpy.data.objects.remove(obj)
@classmethod @classmethod
def disable_editing_system(cls): def disable_editing_system(cls):
bpy.context.scene.BIMSystemProperties.active_system_id = 0 bpy.context.scene.BIMSystemProperties.active_system_id = 0
@@ -39,6 +47,10 @@ class System(blenderbim.core.tool.System):
def export_system_attributes(cls): def export_system_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMSystemProperties.system_attributes) return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMSystemProperties.system_attributes)
@classmethod
def get_ports(cls, element):
return ifcopenshell.util.system.get_ports(element)
@classmethod @classmethod
def import_system_attributes(cls, system): def import_system_attributes(cls, system):
blenderbim.bim.helper.import_attributes2(system, bpy.context.scene.BIMSystemProperties.system_attributes) blenderbim.bim.helper.import_attributes2(system, bpy.context.scene.BIMSystemProperties.system_attributes)
@@ -56,12 +68,31 @@ class System(blenderbim.core.tool.System):
new.ifc_class = system.is_a() new.ifc_class = system.is_a()
@classmethod @classmethod
def select_system_products(cls, system): def load_ports(cls, ports):
for element in ifcopenshell.util.system.get_system_elements(system): if not ports:
return
ifc_import_settings = import_ifc.IfcImportSettings.factory()
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = tool.Ifc.get()
ifc_importer.calculate_unit_scale()
ports = set(ports)
ports -= ifc_importer.create_products(ports)
if ports:
for port in ports:
ifc_importer.create_product(port)
ifc_importer.place_objects_in_collections()
@classmethod
def select_elements(cls, elements):
for element in elements:
obj = tool.Ifc.get_object(element) obj = tool.Ifc.get_object(element)
if obj: if obj:
obj.select_set(True) obj.select_set(True)
@classmethod
def select_system_products(cls, system):
cls.select_elements(ifcopenshell.util.system.get_system_elements(system))
@classmethod @classmethod
def set_active_system(cls, system): def set_active_system(cls, system):
bpy.context.scene.BIMSystemProperties.active_system_id = system.id() bpy.context.scene.BIMSystemProperties.active_system_id = system.id()
+1
View File
@@ -33,6 +33,7 @@
# We always call the module we're testing the "test subject". This makes our # We always call the module we're testing the "test subject". This makes our
# tests simple to read: just look for where the "subject" is called! # tests simple to read: just look for where the "subject" is called!
import blenderbim.core.demo as subject import blenderbim.core.demo as subject
# These are like mocks, stubs, or spy objects. They don't do anything, but they # These are like mocks, stubs, or spy objects. They don't do anything, but they
# let us check our test expectations. # let us check our test expectations.
from test.core.bootstrap import ifc, demo from test.core.bootstrap import ifc, demo
+15
View File
@@ -88,3 +88,18 @@ class TestSelectSystemProducts:
def test_run(self, system): def test_run(self, system):
system.select_system_products("system").should_be_called() system.select_system_products("system").should_be_called()
subject.select_system_products(system, system="system") subject.select_system_products(system, system="system")
class TestShowPorts:
def test_run(self, system):
system.get_ports("element").should_be_called().will_return(["port"])
system.load_ports(["port"]).should_be_called()
system.select_elements(["port"]).should_be_called()
subject.show_ports(system, element="element")
class TestHidePorts:
def test_run(self, system):
system.get_ports("element").should_be_called().will_return(["port"])
system.delete_element_objects(["port"]).should_be_called()
subject.hide_ports(system, element="element")
+1 -5
View File
@@ -350,11 +350,7 @@ class TestRemoveBrick(NewFile):
BrickStore.graph = brickschema.Graph() BrickStore.graph = brickschema.Graph()
result = subject.add_brick("http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment") result = subject.add_brick("http://example.org/digitaltwin#", "https://brickschema.org/schema/Brick#Equipment")
subject.remove_brick(result) subject.remove_brick(result)
assert not list( assert not list(BrickStore.graph.triples((URIRef(result), None, None)))
BrickStore.graph.triples(
(URIRef(result), None, None)
)
)
class TestRunAssignBrickReference(NewFile): class TestRunAssignBrickReference(NewFile):
+1 -1
View File
@@ -48,5 +48,5 @@ class TestGenerateOccurrenceName(NewFile):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
element_type = ifc.createIfcWallType() element_type = ifc.createIfcWallType()
bpy.context.scene.BIMModelProperties.occurrence_name_style = "CUSTOM" bpy.context.scene.BIMModelProperties.occurrence_name_style = "CUSTOM"
bpy.context.scene.BIMModelProperties.occurrence_name_function = "\"Foobar\"" bpy.context.scene.BIMModelProperties.occurrence_name_function = '"Foobar"'
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Foobar" assert subject.generate_occurrence_name(element_type, "IfcWall") == "Foobar"
+47
View File
@@ -29,6 +29,17 @@ class TestImplementsTool(NewFile):
assert isinstance(subject(), blenderbim.core.tool.System) assert isinstance(subject(), blenderbim.core.tool.System)
class TestDeleteElementObjects(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcWall()
tool.Ifc.link(element, obj)
subject.delete_element_objects([element])
assert not bpy.data.objects.get("Object")
class TestDisableEditingSystem(NewFile): class TestDisableEditingSystem(NewFile):
def test_run(self): def test_run(self):
bpy.context.scene.BIMSystemProperties.active_system_id = 10 bpy.context.scene.BIMSystemProperties.active_system_id = 10
@@ -60,6 +71,16 @@ class TestExportSystemAttributes(NewFile):
} }
class TestGetPorts(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifc.createIfcDuctSegment()
port = ifc.createIfcDistributionPort()
ifcopenshell.api.run("system.assign_port", ifc, element=element, port=port)
subject.get_ports(element) == [port]
class TestImportSystemAttributes(NewFile): class TestImportSystemAttributes(NewFile):
def test_importing_a_system(self): def test_importing_a_system(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
@@ -129,6 +150,32 @@ class TestImportSystems(NewFile):
assert props.systems[0].ifc_class == "IfcDistributionSystem" assert props.systems[0].ifc_class == "IfcDistributionSystem"
class TestLoadPorts(NewFile):
def test_run(self):
ifc = ifcopenshell.api.run("project.create_file")
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", ifc)
tool.Ifc().set(ifc)
port = ifc.createIfcDistributionPort()
subject.load_ports([port])
obj = tool.Ifc.get_object(port)
assert obj
assert obj.users_collection
assert list(obj.location) == [0, 0, 0]
class TestSelectElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(element, obj)
subject.select_elements([element])
assert obj in bpy.context.selected_objects
class TestSelectSystemProducts(NewFile): class TestSelectSystemProducts(NewFile):
def test_run(self): def test_run(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()