See #1848. Refactor boundary data class.

This commit is contained in:
Dion Moult
2023-01-30 11:27:38 +11:00
parent b939370f2b
commit 044cd6e97b
6 changed files with 71 additions and 42 deletions
@@ -0,0 +1,46 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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/>.
import os
import bpy
import ifcopenshell
import ifcopenshell.util.schema
import blenderbim.tool as tool
def refresh():
SpaceBoundariesData.is_loaded = False
class SpaceBoundariesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"boundaries": cls.boundaries()}
cls.is_loaded = True
@classmethod
def boundaries(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
for rel in element.BoundedBy or []:
description = f"{rel.id()} > {rel.RelatedBuildingElement.is_a()}/{rel.RelatedBuildingElement.Name}"
results.append({"id": rel.id(), "description": description})
return results
@@ -21,7 +21,6 @@ import logging
import ifcopenshell.api
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.boundary.data import Data
import blenderbim.bim.import_ifc as import_ifc
@@ -49,7 +48,7 @@ class Loader:
if not boundary.ConnectionGeometry:
return None
surface = boundary.ConnectionGeometry.SurfaceOnRelatingElement
# workaround for invalid geometry provided by Revit. See https://github.com/IfcOpenShell/IfcOpenShell/issues/635#issuecomment-770366838
# Workaround for invalid geometry provided by Revit. See https://github.com/Autodesk/revit-ifc/issues/270
if surface.is_a("IfcCurveBoundedPlane") and not getattr(surface, "InnerBoundaries", None):
surface.InnerBoundaries = ()
shape = ifcopenshell.geom.create_shape(self.settings, surface)
@@ -69,8 +68,7 @@ class Loader:
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
self.ifc_importer.file = self.ifc_file
def load_boundary(self, boundary_id, blender_space):
boundary = self.ifc_file.by_id(boundary_id)
def load_boundary(self, boundary, blender_space):
obj = tool.Ifc.get_object(boundary)
if obj:
return obj
@@ -90,12 +88,8 @@ class LoadProjectSpaceBoundaries(bpy.types.Operator):
def execute(self, context):
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for space_id, boundaries_id in Data.spaces.items():
blender_space = tool.Ifc.get_object(tool.Ifc.get().by_id(space_id))
for boundary_id in boundaries_id:
loader.load_boundary(boundary_id, blender_space)
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace))
return {"FINISHED"}
@@ -107,10 +101,9 @@ class LoadBoundary(bpy.types.Operator):
def execute(self, context):
loader = Loader()
blender_space = context.active_object
for obj in context.visible_objects:
obj.select_set(False)
obj = loader.load_boundary(self.boundary_id, blender_space)
obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object)
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
return {"FINISHED"}
@@ -122,13 +115,10 @@ class LoadSpaceBoundaries(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
blender_space = context.active_object
oprops = context.active_object.BIMObjectProperties
loader = Loader()
if not Data.is_loaded:
Data.load(tool.Ifc.get())
for boundary_id in Data.spaces.get(oprops.ifc_definition_id, []):
loader.load_boundary(boundary_id, blender_space)
element = tool.Ifc.get_entity(context.active_object)
for rel in element.BoundedBy or []:
loader.load_boundary(rel, context.active_object)
return {"FINISHED"}
@@ -140,10 +130,8 @@ class SelectProjectBoundaries(bpy.types.Operator):
def execute(self, context):
for obj in context.visible_objects:
obj.select_set(False)
ifc_file = tool.Ifc.get()
for boundary_id in Data.boundaries:
boundary = ifc_file.by_id(boundary_id)
obj = tool.Ifc.get_object(boundary)
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
obj = tool.Ifc.get_object(rel)
if obj:
obj.select_set(True)
return {"FINISHED"}
@@ -21,7 +21,7 @@ import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
from ifcopenshell.api.boundary.data import Data
from blenderbim.bim.module.boundary.data import SpaceBoundariesData
class BIM_PT_SceneBoundaries(Panel):
@@ -129,28 +129,22 @@ class BIM_PT_SpaceBoundaries(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
valid_classes = ("IfcSpace", "IfcExternalSpatialElement")
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
for ifc_class in valid_classes:
if entity.is_a(ifc_class):
element = tool.Ifc.get_entity(context.active_object)
for ifc_class in ("IfcSpace", "IfcExternalSpatialElement"):
if element.is_a(ifc_class):
return True
return False
def draw(self, context):
if not SpaceBoundariesData.is_loaded:
SpaceBoundariesData.load()
self.props = context.active_object.BIMObjectProperties
self.ifc_file = tool.Ifc.get()
row = self.layout.row()
row.operator("bim.load_space_boundaries")
if not Data.is_loaded:
Data.load(self.ifc_file)
for boundary_id in Data.spaces.get(self.props.ifc_definition_id, []):
boundary_data = Data.boundaries[boundary_id]
building_element = self.ifc_file.by_id(boundary_data["RelatedBuildingElement"])
for boundary in SpaceBoundariesData.data["boundaries"]:
row = self.layout.row()
if building_element:
bld_el_description = f"{building_element.is_a()}/{building_element.Name}"
else:
bld_el_description = None
row.label(text=f"{boundary_id} > {bld_el_description}", icon="GHOST_ENABLED")
row.label(text=boundary["description"], icon="GHOST_ENABLED")
op = row.operator("bim.load_boundary", text="", icon="RESTRICT_SELECT_OFF")
op.boundary_id = boundary_id
op.boundary_id = boundary["id"]
+1 -1
View File
@@ -261,4 +261,4 @@ def sync_references(ifc, collector, drawing_tool, drawing=None):
drawing_tool.sync_object_representation(reference_obj)
def select_assigned_product(drawing):
drawing.select_assigned_product()
drawing.select_assigned_product()
@@ -7,7 +7,7 @@ Scenario: Execute IFC Patch
And I set "scene.BIMPatchProperties.ifc_patch_input" to "{cwd}/test/files/basic.ifc"
And I set "scene.BIMPatchProperties.ifc_patch_output" to "{cwd}/test/files/basic-patched.ifc"
And I set "scene.BIMPatchProperties.ifc_patch_args" to "[123454321,0,0,0]"
When I press "bim.execute_ifc_patch"
When I press "bim.execute_ifc_patch(use_json_for_args=True)"
Then the file "{cwd}/test/files/basic-patched.ifc" should contain "123454321"
Scenario: Run migrate patch
+3 -2
View File
@@ -33,7 +33,9 @@ class TestImplementsTool(NewFile):
class TestAddSchemaIdentifier(NewFile):
def test_run(self):
subject.add_schema_identifier(TestLoadExpress().test_run())
cwd = os.path.dirname(os.path.realpath(__file__))
schema = subject.load_express(os.path.join(cwd, "..", "files", "test.exp"))
subject.add_schema_identifier(schema)
assert IfcStore.schema_identifiers[-1] == "IFCROGUE"
@@ -42,7 +44,6 @@ class TestLoadExpress(NewFile):
cwd = os.path.dirname(os.path.realpath(__file__))
schema = subject.load_express(os.path.join(cwd, "..", "files", "test.exp"))
assert schema.schema_name == "IFCROGUE"
return schema
class TestPurgeHdf5Cache(NewFile):