mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
You can now associate IFC elements with Brick elements and navigate to them
This commit is contained in:
@@ -20,7 +20,9 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AssignBrickReference,
|
||||
operator.CloseBrickProject,
|
||||
operator.ConvertBrickProject,
|
||||
operator.LoadBrickProject,
|
||||
operator.RewindBrickClass,
|
||||
operator.ViewBrickClass,
|
||||
@@ -28,6 +30,7 @@ classes = (
|
||||
prop.Brick,
|
||||
prop.BIMBrickProperties,
|
||||
ui.BIM_PT_brickschema,
|
||||
ui.BIM_PT_ifc_brickschema_references,
|
||||
ui.BIM_UL_bricks,
|
||||
)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from blenderbim.tool.brick import BrickStore
|
||||
|
||||
def refresh():
|
||||
BrickschemaData.is_loaded = False
|
||||
BrickschemaReferencesData.is_loaded = False
|
||||
|
||||
|
||||
class BrickschemaData:
|
||||
@@ -76,3 +77,45 @@ class BrickschemaData:
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
class BrickschemaReferencesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {"is_loaded": cls.get_is_loaded(), "libraries": cls.libraries(), "references": cls.references()}
|
||||
|
||||
@classmethod
|
||||
def get_is_loaded(cls):
|
||||
return BrickStore.graph is not None
|
||||
|
||||
@classmethod
|
||||
def libraries(cls):
|
||||
results = []
|
||||
for library in tool.Ifc.get().by_type("IfcLibraryInformation"):
|
||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
results = []
|
||||
for rel in getattr(tool.Ifc.get_entity(bpy.context.active_object), "HasAssociations", []):
|
||||
if rel.is_a("IfcRelAssociatesLibrary"):
|
||||
reference = rel.RelatingLibrary
|
||||
if tool.Ifc.get_schema() == "IFC2X3" and "#" not in reference.ItemReference:
|
||||
continue
|
||||
if tool.Ifc.get_schema() != "IFC2X3" and "#" not in reference.Identification:
|
||||
continue
|
||||
results.append(
|
||||
{
|
||||
"id": reference.id(),
|
||||
"identification": reference.ItemReference
|
||||
if tool.Ifc.get_schema() == "IFC2X3"
|
||||
else reference.Identification,
|
||||
"name": reference.Name or "Unnamed",
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
@@ -82,3 +82,28 @@ class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.close_brick_project(tool.Brick)
|
||||
|
||||
|
||||
class ConvertBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.convert_brick_project"
|
||||
bl_label = "Convert Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_brick_project(tool.Ifc, tool.Brick)
|
||||
|
||||
|
||||
class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.assign_brick_reference"
|
||||
bl_label = "Assign Brick Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
core.assign_brick_reference(
|
||||
tool.Ifc,
|
||||
tool.Brick,
|
||||
obj=context.active_object,
|
||||
library=tool.Ifc.get().by_id(int(props.libraries)),
|
||||
brick_uri=props.bricks[props.active_brick_index].uri,
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.module.brick.data import BrickschemaData
|
||||
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -36,6 +36,12 @@ def update_active_brick_index(self, context):
|
||||
BrickschemaData.is_loaded = False
|
||||
|
||||
|
||||
def get_libraries(self, context):
|
||||
if not BrickschemaReferencesData.is_loaded:
|
||||
BrickschemaReferencesData.load()
|
||||
return BrickschemaReferencesData.data["libraries"]
|
||||
|
||||
|
||||
class Brick(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
uri: StringProperty(name="URI")
|
||||
@@ -47,3 +53,4 @@ class BIMBrickProperties(PropertyGroup):
|
||||
brick_breadcrumbs: CollectionProperty(name="Brick Breadcrumbs", type=StrProperty)
|
||||
bricks: CollectionProperty(name="Bricks", type=Brick)
|
||||
active_brick_index: IntProperty(name="Active Brick Index", update=update_active_brick_index)
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
# 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 blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.module.brick.data import BrickschemaData
|
||||
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
||||
|
||||
|
||||
class BIM_PT_brickschema(Panel):
|
||||
@@ -55,6 +56,54 @@ class BIM_PT_brickschema(Panel):
|
||||
op.item = attribute["value_uri"]
|
||||
|
||||
|
||||
class BIM_PT_ifc_brickschema_references(Panel):
|
||||
bl_label = "IFC Brickschema References"
|
||||
bl_idname = "BIM_PT_ifc_brickschema_references"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not BrickschemaReferencesData.is_loaded:
|
||||
BrickschemaReferencesData.load()
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
if not BrickschemaReferencesData.data["is_loaded"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Brickschema Project Loaded")
|
||||
return
|
||||
|
||||
if not BrickschemaReferencesData.data["libraries"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.assign_brick_reference", icon="ADD")
|
||||
|
||||
if not BrickschemaReferencesData.data["references"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="No References")
|
||||
|
||||
for reference in BrickschemaReferencesData.data["references"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=reference["identification"], icon="ASSET_MANAGER")
|
||||
row.label(text=reference["name"])
|
||||
row.operator("bim.unassign_library_reference", text="", icon="X").reference = reference["id"]
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
|
||||
|
||||
class BIM_UL_bricks(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
|
||||
@@ -47,3 +47,16 @@ def rewind_brick_class(brick):
|
||||
|
||||
def close_brick_project(brick):
|
||||
brick.clear_project()
|
||||
|
||||
|
||||
def convert_brick_project(ifc, brick):
|
||||
library = ifc.run("library.add_library", name=brick.get_brick_path_name())
|
||||
ifc.run("library.edit_library", library=library, attributes={"Location": brick.get_brick_path()})
|
||||
|
||||
|
||||
def assign_brick_reference(ifc, brick, obj=None, library=None, brick_uri=None):
|
||||
reference = brick.get_library_brick_reference(library, brick_uri)
|
||||
if not reference:
|
||||
reference = ifc.run("library.add_reference", library=library)
|
||||
ifc.run("library.edit_reference", reference=reference, attributes=brick.export_brick_attributes(brick_uri))
|
||||
ifc.run("library.assign_reference", product=ifc.get_entity(obj), reference=reference)
|
||||
|
||||
@@ -46,7 +46,11 @@ class Brick:
|
||||
def add_brick_breadcrumb(cls): pass
|
||||
def clear_brick_browser(cls): pass
|
||||
def clear_project(cls): pass
|
||||
def export_brick_attributes(cls, brick_uri): pass
|
||||
def get_brick_path(cls): pass
|
||||
def get_brick_path_name(cls): pass
|
||||
def get_item_class(cls, item): pass
|
||||
def get_library_brick_reference(cls, library, brick_uri): pass
|
||||
def import_brick_classes(cls, brick_class): pass
|
||||
def import_brick_items(cls, brick_class): pass
|
||||
def load_brick_file(cls, filepath): pass
|
||||
|
||||
@@ -48,6 +48,18 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
bpy.context.scene.BIMBrickProperties.active_brick_class == ""
|
||||
bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.clear()
|
||||
|
||||
@classmethod
|
||||
def export_brick_attributes(cls, brick_uri):
|
||||
return {"Identification": brick_uri, "Name": brick_uri.split("#")[-1]}
|
||||
|
||||
@classmethod
|
||||
def get_brick_path(cls):
|
||||
return BrickStore.path
|
||||
|
||||
@classmethod
|
||||
def get_brick_path_name(cls):
|
||||
return os.path.basename(BrickStore.path)
|
||||
|
||||
@classmethod
|
||||
def get_item_class(cls, item):
|
||||
query = BrickStore.graph.query(
|
||||
@@ -64,6 +76,17 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
for row in query:
|
||||
return row.get("class").toPython().split("#")[-1]
|
||||
|
||||
@classmethod
|
||||
def get_library_brick_reference(cls, library, brick_uri):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
for reference in library.LibraryReference:
|
||||
if reference.ItemReference == brick_uri:
|
||||
return reference
|
||||
else:
|
||||
for reference in library.HasLibraryReferences:
|
||||
if reference.Identification == brick_uri:
|
||||
return reference
|
||||
|
||||
@classmethod
|
||||
def import_brick_classes(cls, brick_class):
|
||||
query = BrickStore.graph.query(
|
||||
@@ -115,6 +138,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
schema_path = os.path.join(cwd, "..", "bim", "schema", "Brick.ttl")
|
||||
BrickStore.schema.load_file(schema_path)
|
||||
BrickStore.graph = brickschema.Graph().load_file(filepath) + BrickStore.schema
|
||||
BrickStore.path = filepath
|
||||
|
||||
@classmethod
|
||||
def pop_brick_breadcrumb(cls):
|
||||
@@ -138,8 +162,10 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
class BrickStore:
|
||||
schema = None
|
||||
graph = None
|
||||
path = None
|
||||
|
||||
@staticmethod
|
||||
def purge():
|
||||
BrickStore.schema = None
|
||||
BrickStore.graph = None
|
||||
BrickStore.path = None
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.core.brick as subject
|
||||
from test.core.bootstrap import brick
|
||||
from test.core.bootstrap import ifc, brick
|
||||
|
||||
|
||||
class TestLoadBrickProject:
|
||||
@@ -64,3 +64,31 @@ class TestCloseBrickProject:
|
||||
def test_run(self, brick):
|
||||
brick.clear_project().should_be_called()
|
||||
subject.close_brick_project(brick)
|
||||
|
||||
|
||||
class TestConvertBrickProject:
|
||||
def test_run(self, ifc, brick):
|
||||
brick.get_brick_path_name().should_be_called().will_return("foo.ttl")
|
||||
ifc.run("library.add_library", name="foo.ttl").should_be_called().will_return("library")
|
||||
brick.get_brick_path().should_be_called().will_return("/path/to/foo.ttl")
|
||||
ifc.run(
|
||||
"library.edit_library", library="library", attributes={"Location": "/path/to/foo.ttl"}
|
||||
).should_be_called()
|
||||
subject.convert_brick_project(ifc, brick)
|
||||
|
||||
|
||||
class TestAssignBrickReference:
|
||||
def test_assigning_to_a_new_reference(self, ifc, brick):
|
||||
brick.get_library_brick_reference("library", "brick").should_be_called().will_return(None)
|
||||
ifc.run("library.add_reference", library="library").should_be_called().will_return("reference")
|
||||
brick.export_brick_attributes("brick").should_be_called().will_return("attributes")
|
||||
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
||||
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
||||
|
||||
def test_assigning_to_an_existing_reference(self, ifc, brick):
|
||||
brick.get_library_brick_reference("library", "brick").should_be_called().will_return("reference")
|
||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
||||
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
||||
|
||||
@@ -59,12 +59,45 @@ class TestClearProject(NewFile):
|
||||
assert len(bpy.context.scene.BIMBrickProperties.brick_breadcrumbs) == 0
|
||||
|
||||
|
||||
class TestExportBrickAttributes(NewFile):
|
||||
def test_run(self):
|
||||
assert subject.export_brick_attributes("ex:#floor") == {"Identification": "ex:#floor", "Name": "floor"}
|
||||
|
||||
|
||||
class TestGetBrickPath(NewFile):
|
||||
def test_run(self):
|
||||
TestLoadBrickFile().test_run()
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
assert subject.get_brick_path() == os.path.join(cwd, "..", "files", "spaces.ttl")
|
||||
|
||||
|
||||
class TestGetBrickPathName(NewFile):
|
||||
def test_run(self):
|
||||
TestLoadBrickFile().test_run()
|
||||
assert subject.get_brick_path_name() == "spaces.ttl"
|
||||
|
||||
|
||||
class TestGetItemClass(NewFile):
|
||||
def test_run(self):
|
||||
TestLoadBrickFile().test_run()
|
||||
assert subject.get_item_class("ex:#floor") == "Floor"
|
||||
|
||||
|
||||
class TestGetLibraryBrickReference(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
library = ifc.createIfcLibraryInformation()
|
||||
reference = ifc.createIfcLibraryReference(Identification="ex:#floor", ReferencedLibrary=library)
|
||||
assert subject.get_library_brick_reference(library, "ex:#floor") == reference
|
||||
|
||||
def test_run_ifc2x3(self):
|
||||
ifc = ifcopenshell.file(schema="IFC2X3")
|
||||
tool.Ifc.set(ifc)
|
||||
reference = ifc.createIfcLibraryReference(ItemReference="ex:#floor")
|
||||
library = ifc.createIfcLibraryInformation(LibraryReference=[reference])
|
||||
assert subject.get_library_brick_reference(library, "ex:#floor") == reference
|
||||
|
||||
|
||||
class TestImportBrickClasses(NewFile):
|
||||
def test_run(self):
|
||||
TestLoadBrickFile().test_run()
|
||||
|
||||
@@ -11,4 +11,10 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if self.file.schema == "IFC2X3":
|
||||
reference = self.file.createIfcLibraryReference()
|
||||
references = list(self.settings["library"].LibraryReference or [])
|
||||
references.append(reference)
|
||||
self.settings["library"].LibraryReference = references
|
||||
return reference
|
||||
return self.file.createIfcLibraryReference(ReferencedLibrary=self.settings["library"])
|
||||
|
||||
@@ -8,3 +8,11 @@ class TestAddReference(test.bootstrap.IFC4):
|
||||
reference = ifcopenshell.api.run("library.add_reference", self.file, library=library)
|
||||
assert reference.is_a("IfcLibraryReference")
|
||||
assert reference.ReferencedLibrary == library
|
||||
|
||||
|
||||
class TestAddReferenceIFC2X3(test.bootstrap.IFC2X3):
|
||||
def test_adding_a_reference(self):
|
||||
library = ifcopenshell.api.run("library.add_library", self.file, name="Name")
|
||||
reference = ifcopenshell.api.run("library.add_reference", self.file, library=library)
|
||||
assert reference.is_a("IfcLibraryReference")
|
||||
assert library.LibraryReference == (reference,)
|
||||
|
||||
Reference in New Issue
Block a user