mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 22:50:21 +00:00
Merge remote-tracking branch 'origin/v0.7.0' into v08attempt1
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
name: Publish-ifcsverchok
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/ci-ifcsverchok-build.yml'
|
||||||
|
- 'src/ifcsverchok/*'
|
||||||
|
branches:
|
||||||
|
- v0.7.0
|
||||||
|
|
||||||
|
env:
|
||||||
|
major: 0
|
||||||
|
minor: 0
|
||||||
|
name: ifcsverchok
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
activate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: |
|
||||||
|
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||||
|
steps:
|
||||||
|
- name: Set env
|
||||||
|
run: echo ok go
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: activate
|
||||||
|
name: ifcsverchok
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||||
|
with:
|
||||||
|
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||||
|
python-version: '3.11'
|
||||||
|
- run: echo ${{ env.DATE }}
|
||||||
|
- name: Get current date
|
||||||
|
id: date
|
||||||
|
run: echo "::set-output name=date::$(date +'%y%m%d')"
|
||||||
|
- name: Compile
|
||||||
|
run: |
|
||||||
|
cd src/ifcsverchok
|
||||||
|
make dist
|
||||||
|
- name: Upload Zip file to release
|
||||||
|
uses: svenstaro/upload-release-action@v2
|
||||||
|
with:
|
||||||
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
file: src/ifcsverchok/dist/ifcsverchok-${{steps.date.outputs.date}}.zip
|
||||||
|
asset_name: ifcsverchok-${{steps.date.outputs.date}}.zip
|
||||||
|
tag: "ifcsverchok-${{steps.date.outputs.date}}"
|
||||||
|
overwrite: true
|
||||||
|
body: "ifcsverchok build for ${{steps.date.outputs.date}}"
|
||||||
@@ -12,7 +12,7 @@ RUN apt-get -y update && apt-get -y install unzip curl
|
|||||||
# Install AWS Lambda runtime interface client
|
# Install AWS Lambda runtime interface client
|
||||||
RUN pip install --target ${FUNCTION_DIR} awslambdaric
|
RUN pip install --target ${FUNCTION_DIR} awslambdaric
|
||||||
|
|
||||||
# Set the IfcOpenShell build version (check available builds at: https://blenderbim.org/docs-python/ifcopenshell-python/installation.html)
|
# Set the IfcOpenShell build version (check available builds at: https://docs.ifcopenshell.org/ifcopenshell-python/installation.html)
|
||||||
ARG IFC_OPENSHELL_BUILD="39-v0.7.0-476ab50"
|
ARG IFC_OPENSHELL_BUILD="39-v0.7.0-476ab50"
|
||||||
|
|
||||||
# Download and extract IfcOpenShell
|
# Download and extract IfcOpenShell
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<licenseUrl>https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/COPYING</licenseUrl>
|
<licenseUrl>https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/COPYING</licenseUrl>
|
||||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||||
<projectSourceUrl>https://github.com/IfcOpenShell/IfcOpenShell</projectSourceUrl>
|
<projectSourceUrl>https://github.com/IfcOpenShell/IfcOpenShell</projectSourceUrl>
|
||||||
<docsUrl>https://blenderbim.org/docs/</docsUrl>
|
<docsUrl>https://docs.blenderbim.org/</docsUrl>
|
||||||
<!--<mailingListUrl></mailingListUrl>-->
|
<!--<mailingListUrl></mailingListUrl>-->
|
||||||
<bugTrackerUrl>https://github.com/IfcOpenShell/IfcOpenShell/issues</bugTrackerUrl>
|
<bugTrackerUrl>https://github.com/IfcOpenShell/IfcOpenShell/issues</bugTrackerUrl>
|
||||||
<tags>blender bim blenderbim ifc python opensource foss</tags>
|
<tags>blender bim blenderbim ifc python opensource foss</tags>
|
||||||
|
|||||||
@@ -857,7 +857,11 @@ class IfcImporter:
|
|||||||
print("Done creating geometry")
|
print("Done creating geometry")
|
||||||
|
|
||||||
def create_spatial_elements(self) -> None:
|
def create_spatial_elements(self) -> None:
|
||||||
self.create_generic_elements(self.spatial_elements, unselectable=True)
|
if bpy.context.preferences.addons["blenderbim"].preferences.spatial_elements_unselectable:
|
||||||
|
self.create_generic_elements(self.spatial_elements, unselectable=True)
|
||||||
|
else:
|
||||||
|
self.create_generic_elements(self.spatial_elements, unselectable=False)
|
||||||
|
|
||||||
|
|
||||||
def create_elements(self) -> None:
|
def create_elements(self) -> None:
|
||||||
self.create_generic_elements(self.elements)
|
self.create_generic_elements(self.elements)
|
||||||
|
|||||||
@@ -47,10 +47,18 @@ class EnableEditingAttributes(bpy.types.Operator):
|
|||||||
obj = bpy.data.objects.get(self.obj)
|
obj = bpy.data.objects.get(self.obj)
|
||||||
elif self.obj_type == "Material":
|
elif self.obj_type == "Material":
|
||||||
obj = bpy.data.materials.get(self.obj)
|
obj = bpy.data.materials.get(self.obj)
|
||||||
oprops = obj.BIMObjectProperties
|
|
||||||
props = obj.BIMAttributeProperties
|
props = obj.BIMAttributeProperties
|
||||||
props.attributes.clear()
|
props.attributes.clear()
|
||||||
|
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
has_inherited_predefined_type = False
|
||||||
|
if not element.is_a("IfcTypeObject") and (element_type := ifcopenshell.util.element.get_type(element)):
|
||||||
|
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
|
||||||
|
has_inherited_predefined_type = ifcopenshell.util.element.get_predefined_type(element_type) not in (
|
||||||
|
"NOTDEFINED",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
def callback(name, prop, data):
|
def callback(name, prop, data):
|
||||||
if name in ("RefLatitude", "RefLongitude"):
|
if name in ("RefLatitude", "RefLongitude"):
|
||||||
new = props.attributes.add()
|
new = props.attributes.add()
|
||||||
@@ -62,10 +70,11 @@ class EnableEditingAttributes(bpy.types.Operator):
|
|||||||
new.string_value = "" if new.is_null else json.dumps(data[name])
|
new.string_value = "" if new.is_null else json.dumps(data[name])
|
||||||
blenderbim.bim.helper.add_attribute_description(new)
|
blenderbim.bim.helper.add_attribute_description(new)
|
||||||
new.description += " The degrees, minutes and seconds should follow this format : [12,34,56]"
|
new.description += " The degrees, minutes and seconds should follow this format : [12,34,56]"
|
||||||
|
if name in ("PredefinedType", "ObjectType") and has_inherited_predefined_type:
|
||||||
|
props.attributes.remove(len(props.attributes) - 1)
|
||||||
|
return True
|
||||||
|
|
||||||
blenderbim.bim.helper.import_attributes2(
|
blenderbim.bim.helper.import_attributes2(element, props.attributes, callback=callback)
|
||||||
tool.Ifc.get().by_id(oprops.ifc_definition_id), props.attributes, callback=callback
|
|
||||||
)
|
|
||||||
props.is_editing_attributes = True
|
props.is_editing_attributes = True
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import bpy
|
|||||||
import json
|
import json
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.classification
|
||||||
|
import ifcopenshell.util.element
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
@@ -87,7 +89,7 @@ class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
reference = ifcopenshell.api.run(
|
reference = ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
product=product,
|
products=[product],
|
||||||
classification=classification,
|
classification=classification,
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Unnamed",
|
name="Unnamed",
|
||||||
@@ -292,6 +294,7 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
active_reference = tool.Ifc.get().by_id(self.reference)
|
active_reference = tool.Ifc.get().by_id(self.reference)
|
||||||
identification = active_reference[1]
|
identification = active_reference[1]
|
||||||
|
|
||||||
|
elements_by_references: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]] = {}
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
@@ -300,12 +303,16 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if (identification and reference[1] == identification) or (
|
if (identification and reference[1] == identification) or (
|
||||||
not identification and reference == active_reference
|
not identification and reference == active_reference
|
||||||
):
|
):
|
||||||
ifcopenshell.api.run(
|
elements_by_references.setdefault(reference, []).append(element)
|
||||||
"classification.remove_reference",
|
|
||||||
tool.Ifc.get(),
|
if elements_by_references:
|
||||||
reference=reference,
|
for reference, products in elements_by_references.items():
|
||||||
product=element,
|
ifcopenshell.api.run(
|
||||||
)
|
"classification.remove_reference",
|
||||||
|
tool.Ifc.get(),
|
||||||
|
reference=reference,
|
||||||
|
products=products,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -357,15 +364,18 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
classification = element
|
classification = element
|
||||||
break
|
break
|
||||||
|
|
||||||
for obj in objects:
|
ifc_file = tool.Ifc.get()
|
||||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
products = [
|
||||||
if not ifc_definition_id:
|
ifc_file.by_id(ifc_definition_id)
|
||||||
continue
|
for obj in objects
|
||||||
|
if (ifc_definition_id := tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context))
|
||||||
|
]
|
||||||
|
if products:
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
reference=IfcStore.classification_file.by_id(self.reference),
|
reference=IfcStore.classification_file.by_id(self.reference),
|
||||||
product=tool.Ifc.get().by_id(ifc_definition_id),
|
products=products,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -413,7 +423,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
reference = ifcopenshell.api.run(
|
reference = ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
product=element,
|
products=[element],
|
||||||
classification=classification,
|
classification=classification,
|
||||||
identification=bsdd_classification.reference_code,
|
identification=bsdd_classification.reference_code,
|
||||||
name=bsdd_classification.name,
|
name=bsdd_classification.name,
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class EditObjective(bpy.types.Operator):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"constraint.edit_objective",
|
"constraint.edit_objective",
|
||||||
self.file,
|
self.file,
|
||||||
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes},
|
||||||
)
|
)
|
||||||
bpy.ops.bim.load_objectives()
|
bpy.ops.bim.load_objectives()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -179,19 +179,17 @@ class AssignConstraint(bpy.types.Operator):
|
|||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = tool.Ifc.get()
|
||||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||||
for obj in objs:
|
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
|
||||||
obj_id = obj.BIMObjectProperties.ifc_definition_id
|
if products:
|
||||||
if not obj_id:
|
|
||||||
continue
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"constraint.assign_constraint",
|
"constraint.assign_constraint",
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
**{
|
||||||
"product": self.file.by_id(obj_id),
|
"products": products,
|
||||||
"constraint": self.file.by_id(self.constraint),
|
"constraint": self.file.by_id(self.constraint),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -207,18 +205,16 @@ class UnassignConstraint(bpy.types.Operator):
|
|||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = tool.Ifc.get()
|
||||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||||
for obj in objs:
|
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
|
||||||
obj_id = obj.BIMObjectProperties.ifc_definition_id
|
if products:
|
||||||
if not obj_id:
|
|
||||||
continue
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"constraint.unassign_constraint",
|
"constraint.unassign_constraint",
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
**{
|
||||||
"product": self.file.by_id(obj_id),
|
"products": products,
|
||||||
"constraint": self.file.by_id(self.constraint),
|
"constraint": self.file.by_id(self.constraint),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -2570,8 +2570,16 @@ class EnableEditingElementFilter(bpy.types.Operator, Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
obj = bpy.context.scene.camera
|
obj = bpy.context.scene.camera
|
||||||
if obj:
|
if not obj:
|
||||||
obj.data.BIMCameraProperties.filter_mode = self.filter_mode
|
return
|
||||||
|
obj.data.BIMCameraProperties.filter_mode = self.filter_mode
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if query := ifcopenshell.util.element.get_pset(element, "EPset_Drawing", self.filter_mode.title()):
|
||||||
|
filter_groups = tool.Search.get_filter_groups(f"drawing_{self.filter_mode.lower()}")
|
||||||
|
try:
|
||||||
|
tool.Search.import_filter_query(query, filter_groups)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EditElementFilter(bpy.types.Operator, Operator):
|
class EditElementFilter(bpy.types.Operator, Operator):
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ def get_location_hint(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_diagram_scale(self, context):
|
def update_diagram_scale(self, context):
|
||||||
|
if not context.scene.camera or context.scene.camera.data != self.id_data:
|
||||||
|
return
|
||||||
|
element = tool.Ifc.get_entity(context.scene.camera)
|
||||||
|
if not element:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
element = (
|
element = (
|
||||||
tool.Ifc.get()
|
tool.Ifc.get()
|
||||||
@@ -87,6 +92,11 @@ def update_diagram_scale(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_is_nts(self, context):
|
def update_is_nts(self, context):
|
||||||
|
if not context.scene.camera or context.scene.camera.data != self.id_data:
|
||||||
|
return
|
||||||
|
element = tool.Ifc.get_entity(context.scene.camera)
|
||||||
|
if not element:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
element = (
|
element = (
|
||||||
tool.Ifc.get()
|
tool.Ifc.get()
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ class BIM_PT_object_material(Panel):
|
|||||||
|
|
||||||
if ObjectMaterialData.data["type_material"]:
|
if ObjectMaterialData.data["type_material"]:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="FILE_PARENT")
|
row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="CON_CHILDOF")
|
||||||
|
|
||||||
if ObjectMaterialData.data["material_class"]:
|
if ObjectMaterialData.data["material_class"]:
|
||||||
return self.draw_material_ui()
|
return self.draw_material_ui()
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ classes = (
|
|||||||
operator.AppendLibraryElement,
|
operator.AppendLibraryElement,
|
||||||
operator.AppendLibraryElementByQuery,
|
operator.AppendLibraryElementByQuery,
|
||||||
operator.AssignLibraryDeclaration,
|
operator.AssignLibraryDeclaration,
|
||||||
|
operator.BIM_OT_load_clipping_planes,
|
||||||
|
operator.BIM_OT_save_clipping_planes,
|
||||||
operator.ChangeLibraryElement,
|
operator.ChangeLibraryElement,
|
||||||
operator.CreateClippingPlane,
|
operator.CreateClippingPlane,
|
||||||
operator.CreateProject,
|
operator.CreateProject,
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
from blenderbim.bim.ui import IFCFileSelector
|
from blenderbim.bim.ui import IFCFileSelector
|
||||||
from blenderbim.bim import import_ifc
|
from blenderbim.bim import import_ifc
|
||||||
from blenderbim.bim import export_ifc
|
from blenderbim.bim import export_ifc
|
||||||
|
from collections import defaultdict
|
||||||
|
import json
|
||||||
from math import radians
|
from math import radians
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
@@ -1999,3 +2001,61 @@ class FlipClippingPlane(bpy.types.Operator):
|
|||||||
obj.rotation_euler[0] += radians(180)
|
obj.rotation_euler[0] += radians(180)
|
||||||
context.view_layer.update()
|
context.view_layer.update()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
CLIPPING_PLANES_FILE_NAME = "ClippingPlanes.json" # TODO un-hardcode :=
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_save_clipping_planes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.save_clipping_planes"
|
||||||
|
bl_label = "Save Clipping Planes"
|
||||||
|
bl_description = "Save Clipping Planes to Disk"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if IfcStore.path:
|
||||||
|
return context.scene.BIMProjectProperties.clipping_planes
|
||||||
|
cls.poll_message_set("Please Save The IFC File")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
clipping_planes_to_serialize = defaultdict(dict)
|
||||||
|
clipping_planes = context.scene.BIMProjectProperties.clipping_planes
|
||||||
|
for clipping_plane in clipping_planes:
|
||||||
|
obj = clipping_plane.obj
|
||||||
|
name = obj.name
|
||||||
|
clipping_planes_to_serialize[name]["location"] = obj.location[0:3]
|
||||||
|
clipping_planes_to_serialize[name]["rotation"] = obj.rotation_euler[0:3]
|
||||||
|
with open(Path(IfcStore.path).with_name(CLIPPING_PLANES_FILE_NAME), "w") as file:
|
||||||
|
json.dump(clipping_planes_to_serialize, file, indent=4)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_load_clipping_planes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_clipping_planes"
|
||||||
|
bl_label = "Load Clipping Planes"
|
||||||
|
bl_description = "Load Clipping Planes from Disk"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if filepath := IfcStore.path:
|
||||||
|
if Path(filepath).with_name(CLIPPING_PLANES_FILE_NAME).exists():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
cls.poll_message_set(f"No Clipping Planes File in Folder {filepath}")
|
||||||
|
else:
|
||||||
|
cls.poll_message_set("Please Save The IFC File")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.data.batch_remove(context.scene.BIMProjectProperties.clipping_planes_objs)
|
||||||
|
context.scene.BIMProjectProperties.clipping_planes.clear()
|
||||||
|
with open(Path(IfcStore.path).with_name(CLIPPING_PLANES_FILE_NAME), "r") as file:
|
||||||
|
clipping_planes_dict = json.load(file)
|
||||||
|
for name, values in clipping_planes_dict.items():
|
||||||
|
bpy.ops.bim.create_clipping_plane()
|
||||||
|
obj = context.scene.BIMProjectProperties.clipping_planes_objs[-1]
|
||||||
|
obj.name = name
|
||||||
|
obj.location = values["location"]
|
||||||
|
obj.rotation_euler = values["rotation"]
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class ObjectPsetsData(Data):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = {
|
cls.data = {
|
||||||
|
"is_occurrence": cls.is_occurrence(),
|
||||||
"psets": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), psets_only=True),
|
"psets": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), psets_only=True),
|
||||||
"inherited_psets": cls.inherited_psets(),
|
"inherited_psets": cls.inherited_psets(),
|
||||||
"pset_name": cls.pset_name(),
|
"pset_name": cls.pset_name(),
|
||||||
@@ -76,6 +77,10 @@ class ObjectPsetsData(Data):
|
|||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_occurrence(cls):
|
||||||
|
return not tool.Ifc.get_entity(bpy.context.active_object).is_a("IfcTypeObject")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def inherited_psets(cls):
|
def inherited_psets(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
@@ -126,11 +131,16 @@ class ObjectQtosData(Data):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = {
|
cls.data = {
|
||||||
|
"is_occurrence": cls.is_occurrence(),
|
||||||
"qtos": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), qtos_only=True),
|
"qtos": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), qtos_only=True),
|
||||||
"inherited_qsets": cls.inherited_qsets(),
|
"inherited_qsets": cls.inherited_qsets(),
|
||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_occurrence(cls):
|
||||||
|
return not tool.Ifc.get_entity(bpy.context.active_object).is_a("IfcTypeObject")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def inherited_qsets(cls):
|
def inherited_qsets(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
|
|||||||
@@ -208,12 +208,15 @@ class BIM_PT_object_psets(Panel):
|
|||||||
draw_psetqto_ui(context, 0, {}, props, self.layout, "Object")
|
draw_psetqto_ui(context, 0, {}, props, self.layout, "Object")
|
||||||
|
|
||||||
if ObjectPsetsData.data["psets"]:
|
if ObjectPsetsData.data["psets"]:
|
||||||
self.layout.label(text="Instance:")
|
if ObjectPsetsData.data["is_occurrence"]:
|
||||||
|
self.layout.label(text="Occurrence Properties:")
|
||||||
|
else:
|
||||||
|
self.layout.label(text="Type Properties:")
|
||||||
for pset in ObjectPsetsData.data["psets"]:
|
for pset in ObjectPsetsData.data["psets"]:
|
||||||
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
|
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
|
||||||
|
|
||||||
if ObjectPsetsData.data["inherited_psets"]:
|
if ObjectPsetsData.data["inherited_psets"]:
|
||||||
self.layout.label(text="Type:")
|
self.layout.label(text="Inherited Type Properties:", icon="CON_CHILDOF")
|
||||||
for pset in ObjectPsetsData.data["inherited_psets"]:
|
for pset in ObjectPsetsData.data["inherited_psets"]:
|
||||||
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object", allow_removing=False)
|
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object", allow_removing=False)
|
||||||
|
|
||||||
@@ -252,12 +255,15 @@ class BIM_PT_object_qtos(Panel):
|
|||||||
draw_psetqto_ui(context, 0, {}, props, self.layout, "Object")
|
draw_psetqto_ui(context, 0, {}, props, self.layout, "Object")
|
||||||
|
|
||||||
if ObjectQtosData.data["qtos"]:
|
if ObjectQtosData.data["qtos"]:
|
||||||
self.layout.label(text="Instance:")
|
if ObjectQtosData.data["is_occurrence"]:
|
||||||
|
self.layout.label(text="Occurrence Quantities:")
|
||||||
|
else:
|
||||||
|
self.layout.label(text="Type Quantities:")
|
||||||
for qto in ObjectQtosData.data["qtos"]:
|
for qto in ObjectQtosData.data["qtos"]:
|
||||||
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Object")
|
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Object")
|
||||||
|
|
||||||
if ObjectQtosData.data["inherited_qsets"]:
|
if ObjectQtosData.data["inherited_qsets"]:
|
||||||
self.layout.label(text="Type:")
|
self.layout.label(text="Inherited Type Quantities:", icon="CON_CHILDOF")
|
||||||
for qset in ObjectQtosData.data["inherited_qsets"]:
|
for qset in ObjectQtosData.data["inherited_qsets"]:
|
||||||
draw_psetqto_ui(context, qset["id"], qset, props, self.layout, "Object", allow_removing=False)
|
draw_psetqto_ui(context, qset["id"], qset, props, self.layout, "Object", allow_removing=False)
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class IfcClassData:
|
|||||||
cls.data["contexts"] = cls.contexts()
|
cls.data["contexts"] = cls.contexts()
|
||||||
cls.data["has_entity"] = cls.has_entity()
|
cls.data["has_entity"] = cls.has_entity()
|
||||||
cls.data["name"] = cls.name()
|
cls.data["name"] = cls.name()
|
||||||
|
cls.data["has_inherited_predefined_type"] = cls.has_inherited_predefined_type()
|
||||||
cls.data["ifc_class"] = cls.ifc_class()
|
cls.data["ifc_class"] = cls.ifc_class()
|
||||||
cls.data["ifc_predefined_types"] = cls.ifc_predefined_types()
|
cls.data["ifc_predefined_types"] = cls.ifc_predefined_types()
|
||||||
cls.data["can_reassign_class"] = cls.can_reassign_class()
|
cls.data["can_reassign_class"] = cls.can_reassign_class()
|
||||||
@@ -162,6 +163,16 @@ class IfcClassData:
|
|||||||
name += f"[{predefined_type}]"
|
name += f"[{predefined_type}]"
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_inherited_predefined_type(cls):
|
||||||
|
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
||||||
|
if not element:
|
||||||
|
return
|
||||||
|
if element_type := ifcopenshell.util.element.get_type(element):
|
||||||
|
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
|
||||||
|
return ifcopenshell.util.element.get_predefined_type(element_type) not in ("NOTDEFINED", None)
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_class(cls):
|
def ifc_class(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ class BIM_PT_class(Panel):
|
|||||||
self.layout.prop(context.scene.BIMRootProperties, "relating_class_object", icon="COPYDOWN")
|
self.layout.prop(context.scene.BIMRootProperties, "relating_class_object", icon="COPYDOWN")
|
||||||
else:
|
else:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(text=IfcClassData.data["name"])
|
row.label(
|
||||||
|
text=IfcClassData.data["name"],
|
||||||
|
icon="CON_CHILDOF" if IfcClassData.data["has_inherited_predefined_type"] else "NONE",
|
||||||
|
)
|
||||||
row.operator("bim.select_ifc_class", text="", icon="RESTRICT_SELECT_OFF")
|
row.operator("bim.select_ifc_class", text="", icon="RESTRICT_SELECT_OFF")
|
||||||
row.operator("bim.unlink_object", icon="UNLINKED", text="")
|
row.operator("bim.unlink_object", icon="UNLINKED", text="")
|
||||||
if IfcClassData.data["can_reassign_class"]:
|
if IfcClassData.data["can_reassign_class"]:
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class LoadSearch(Operator, tool.Ifc.Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
filter_groups = tool.Search.get_filter_groups(self.module)
|
filter_groups = tool.Search.get_filter_groups(self.module)
|
||||||
group = tool.Ifc.get().by_id(int(context.scene.BIMSearchProperties.saved_searches))
|
group = tool.Ifc.get().by_id(int(context.scene.BIMSearchProperties.saved_searches))
|
||||||
query = tool.Search.import_filter_query(tool.Search.get_group_query(group), filter_groups)
|
tool.Search.import_filter_query(tool.Search.get_group_query(group), filter_groups)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
props = context.scene.BIMSearchProperties
|
props = context.scene.BIMSearchProperties
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ class BIM_PT_section_with_cappings(Panel):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
header = box.row(align=True)
|
header = box.row(align=True)
|
||||||
header.label(text="Clipping Planes")
|
header.label(text="Clipping Planes")
|
||||||
|
header.operator("bim.save_clipping_planes", text="", icon="EXPORT")
|
||||||
|
header.operator("bim.load_clipping_planes", text="", icon="IMPORT")
|
||||||
header.operator("bim.create_clipping_plane", text="", icon="ADD")
|
header.operator("bim.create_clipping_plane", text="", icon="ADD")
|
||||||
|
|
||||||
box.template_list(
|
box.template_list(
|
||||||
@@ -150,7 +152,7 @@ class BIM_PT_section_with_cappings(Panel):
|
|||||||
|
|
||||||
class BIM_UL_clipping_plane(bpy.types.UIList):
|
class BIM_UL_clipping_plane(bpy.types.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):
|
||||||
if item:
|
if item and item.obj:
|
||||||
obj = item.obj
|
obj = item.obj
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(obj, "name", text="", emboss=False)
|
row.prop(obj, "name", text="", emboss=False)
|
||||||
@@ -202,6 +204,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
||||||
)
|
)
|
||||||
lock_grids_on_import: BoolProperty(name="Should Lock Grids By Default", default=True)
|
lock_grids_on_import: BoolProperty(name="Should Lock Grids By Default", default=True)
|
||||||
|
spatial_elements_unselectable: BoolProperty(name="Should Make Spatial Elements Unselectable By Default", default=True)
|
||||||
decorations_colour: bpy.props.FloatVectorProperty(
|
decorations_colour: bpy.props.FloatVectorProperty(
|
||||||
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
|
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
|
||||||
)
|
)
|
||||||
@@ -283,6 +286,10 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
row.prop(self, "should_play_chaching_sound")
|
row.prop(self, "should_play_chaching_sound")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(self, "lock_grids_on_import")
|
row.prop(self, "lock_grids_on_import")
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(self, "spatial_elements_unselectable")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
row.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ def assign_brick_reference(ifc, brick, element=None, library=None, brick_uri=Non
|
|||||||
if not reference:
|
if not reference:
|
||||||
reference = ifc.run("library.add_reference", library=library)
|
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.edit_reference", reference=reference, attributes=brick.export_brick_attributes(brick_uri))
|
||||||
ifc.run("library.assign_reference", product=element, reference=reference)
|
ifc.run("library.assign_reference", products=[element], reference=reference)
|
||||||
project = brick.get_brickifc_project()
|
project = brick.get_brickifc_project()
|
||||||
if not project:
|
if not project:
|
||||||
project = brick.add_brickifc_project(brick.get_namespace(brick_uri))
|
project = brick.add_brickifc_project(brick.get_namespace(brick_uri))
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ def remove_document(ifc, document_tool, document=None):
|
|||||||
|
|
||||||
|
|
||||||
def assign_document(ifc, product=None, document=None):
|
def assign_document(ifc, product=None, document=None):
|
||||||
ifc.run("document.assign_document", product=product, document=document)
|
ifc.run("document.assign_document", products=[product], document=document)
|
||||||
|
|
||||||
|
|
||||||
def unassign_document(ifc, product=None, document=None):
|
def unassign_document(ifc, product=None, document=None):
|
||||||
ifc.run("document.unassign_document", product=product, document=document)
|
ifc.run("document.unassign_document", products=[product], document=document)
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
|||||||
attributes = {"Identification": "X", "Name": drawing_name, "Scope": "DRAWING"}
|
attributes = {"Identification": "X", "Name": drawing_name, "Scope": "DRAWING"}
|
||||||
ifc.run("document.edit_information", information=information, attributes=attributes)
|
ifc.run("document.edit_information", information=information, attributes=attributes)
|
||||||
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
|
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
|
||||||
ifc.run("document.assign_document", product=element, document=reference)
|
ifc.run("document.assign_document", products=[element], document=reference)
|
||||||
drawing.import_drawings()
|
drawing.import_drawings()
|
||||||
|
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati
|
|||||||
ifc.run("group.assign_group", group=new_group, products=[new_annotation])
|
ifc.run("group.assign_group", group=new_group, products=[new_annotation])
|
||||||
|
|
||||||
old_reference = drawing_tool.get_drawing_document(new_drawing)
|
old_reference = drawing_tool.get_drawing_document(new_drawing)
|
||||||
ifc.run("document.unassign_document", product=new_drawing, document=old_reference)
|
ifc.run("document.unassign_document", products=[new_drawing], document=old_reference)
|
||||||
|
|
||||||
information = ifc.run("document.add_information")
|
information = ifc.run("document.add_information")
|
||||||
uri = drawing_tool.get_default_drawing_path(drawing_name)
|
uri = drawing_tool.get_default_drawing_path(drawing_name)
|
||||||
@@ -276,7 +276,7 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati
|
|||||||
attributes = {"Identification": "X", "Name": drawing_name, "Scope": "DRAWING"}
|
attributes = {"Identification": "X", "Name": drawing_name, "Scope": "DRAWING"}
|
||||||
ifc.run("document.edit_information", information=information, attributes=attributes)
|
ifc.run("document.edit_information", information=information, attributes=attributes)
|
||||||
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
|
ifc.run("document.edit_reference", reference=reference, attributes={"Location": uri})
|
||||||
ifc.run("document.assign_document", product=new_drawing, document=reference)
|
ifc.run("document.assign_document", products=[new_drawing], document=reference)
|
||||||
|
|
||||||
drawing_tool.import_drawings()
|
drawing_tool.import_drawings()
|
||||||
return new_drawing
|
return new_drawing
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ def edit_library_reference(ifc, library):
|
|||||||
|
|
||||||
|
|
||||||
def assign_library_reference(ifc, obj=None, reference=None):
|
def assign_library_reference(ifc, obj=None, reference=None):
|
||||||
ifc.run("library.assign_reference", product=ifc.get_entity(obj), reference=reference)
|
ifc.run("library.assign_reference", products=[ifc.get_entity(obj)], reference=reference)
|
||||||
|
|
||||||
|
|
||||||
def unassign_library_reference(ifc, obj=None, reference=None):
|
def unassign_library_reference(ifc, obj=None, reference=None):
|
||||||
ifc.run("library.unassign_reference", product=ifc.get_entity(obj), reference=reference)
|
ifc.run("library.unassign_reference", products=[ifc.get_entity(obj)], reference=reference)
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ def add_usage_constraint(ifc, resource_tool, resource=None, reference_path=None)
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
ifc.run("constraint.add_metric_reference", metric=metric, reference_path=reference_path)
|
ifc.run("constraint.add_metric_reference", metric=metric, reference_path=reference_path)
|
||||||
ifc.run("constraint.assign_constraint", product=resource, constraint=objective)
|
ifc.run("constraint.assign_constraint", products=[resource], constraint=objective)
|
||||||
|
|
||||||
|
|
||||||
def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
||||||
@@ -206,7 +206,7 @@ def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
|||||||
reference = resource_tool.get_metric_reference(metric, is_deep=True)
|
reference = resource_tool.get_metric_reference(metric, is_deep=True)
|
||||||
if reference == reference_path:
|
if reference == reference_path:
|
||||||
ifc.run("constraint.remove_metric", metric=metric)
|
ifc.run("constraint.remove_metric", metric=metric)
|
||||||
ifc.run("constraint.unassign_constraint", product=resource, constraint=constraint)
|
ifc.run("constraint.unassign_constraint", products=[resource], constraint=constraint)
|
||||||
ifc.run("constraint.remove_constraint", constraint=constraint)
|
ifc.run("constraint.remove_constraint", constraint=constraint)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def reference_structure(
|
|||||||
element: Optional[ifcopenshell.entity_instance] = None,
|
element: Optional[ifcopenshell.entity_instance] = None,
|
||||||
) -> Union[ifcopenshell.entity_instance, None]:
|
) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
if spatial.can_reference(structure, element):
|
if spatial.can_reference(structure, element):
|
||||||
return ifc.run("spatial.reference_structure", product=element, relating_structure=structure)
|
return ifc.run("spatial.reference_structure", products=[element], relating_structure=structure)
|
||||||
|
|
||||||
|
|
||||||
def dereference_structure(
|
def dereference_structure(
|
||||||
@@ -42,7 +42,7 @@ def dereference_structure(
|
|||||||
element: Optional[ifcopenshell.entity_instance] = None,
|
element: Optional[ifcopenshell.entity_instance] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if spatial.can_reference(structure, element):
|
if spatial.can_reference(structure, element):
|
||||||
return ifc.run("spatial.dereference_structure", product=element, relating_structure=structure)
|
return ifc.run("spatial.dereference_structure", products=[element], relating_structure=structure)
|
||||||
|
|
||||||
|
|
||||||
def assign_container(
|
def assign_container(
|
||||||
|
|||||||
@@ -733,7 +733,6 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
|
|
||||||
obj.BIMObjectProperties.ifc_definition_id = ifc_definition_id
|
obj.BIMObjectProperties.ifc_definition_id = ifc_definition_id
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_drawings(cls):
|
def import_drawings(cls):
|
||||||
props = bpy.context.scene.DocProperties
|
props = bpy.context.scene.DocProperties
|
||||||
@@ -1776,7 +1775,6 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
element_obj_names = set()
|
element_obj_names = set()
|
||||||
for element in filtered_elements:
|
for element in filtered_elements:
|
||||||
obj = tool.Ifc.get_object(element)
|
obj = tool.Ifc.get_object(element)
|
||||||
element_obj_names.add(obj.name)
|
|
||||||
current_representation = tool.Geometry.get_active_representation(obj)
|
current_representation = tool.Geometry.get_active_representation(obj)
|
||||||
if current_representation:
|
if current_representation:
|
||||||
subcontext = current_representation.ContextOfItems
|
subcontext = current_representation.ContextOfItems
|
||||||
@@ -1805,10 +1803,14 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
|
|
||||||
# Don't hide IfcAnnotations as some of them might exist without representations
|
# Don't hide IfcAnnotations as some of them might exist without representations
|
||||||
if has_context or element.is_a("IfcAnnotation"):
|
if has_context or element.is_a("IfcAnnotation"):
|
||||||
# Note that render visibility is only set on drawing generation time for speed.
|
element_obj_names.add(obj.name)
|
||||||
obj.hide_set(False)
|
|
||||||
|
|
||||||
[obj.hide_set(False) for obj in bpy.context.view_layer.objects if obj.name not in element_obj_names]
|
# Note that render visibility is only set on drawing generation time for speed.
|
||||||
|
[
|
||||||
|
obj.hide_set(False) # Show the object
|
||||||
|
for obj in bpy.context.view_layer.objects
|
||||||
|
if obj.name in element_obj_names or not tool.Ifc.get_entity(obj)
|
||||||
|
]
|
||||||
|
|
||||||
cls.import_camera_props(drawing, camera)
|
cls.import_camera_props(drawing, camera)
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,17 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
if element.is_a("IfcRelSpaceBoundary"):
|
if element.is_a("IfcRelSpaceBoundary"):
|
||||||
ifcopenshell.api.run("boundary.remove_boundary", tool.Ifc.get(), boundary=element)
|
ifcopenshell.api.run("boundary.remove_boundary", tool.Ifc.get(), boundary=element)
|
||||||
return bpy.data.objects.remove(obj)
|
return bpy.data.objects.remove(obj)
|
||||||
|
if element.is_a("IfcGridAxis"):
|
||||||
|
is_last_axis = False
|
||||||
|
# Deleting the last W axis is OK
|
||||||
|
if ((grid := element.PartOfU) and len(grid[0].UAxes) == 1) or (
|
||||||
|
(grid := element.PartOfV) and len(grid[0].VAxes) == 1
|
||||||
|
):
|
||||||
|
is_last_axis = True
|
||||||
|
if is_last_axis:
|
||||||
|
return
|
||||||
|
ifcopenshell.api.run("grid.remove_grid_axis", tool.Ifc.get(), axis=element)
|
||||||
|
return bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
collection = obj.BIMObjectProperties.collection
|
collection = obj.BIMObjectProperties.collection
|
||||||
if collection:
|
if collection:
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ Some of these add-ons are not shipped with Blender:
|
|||||||
- `Sverchok <https://github.com/nortikin/sverchok/>`__ - Sverchok is a visual
|
- `Sverchok <https://github.com/nortikin/sverchok/>`__ - Sverchok is a visual
|
||||||
programming add-on for Blender that allows you to generate parametric
|
programming add-on for Blender that allows you to generate parametric
|
||||||
geometry, create scripts for non-programmers, model solids from FreeCAD, and
|
geometry, create scripts for non-programmers, model solids from FreeCAD, and
|
||||||
much more.
|
much more. There is also
|
||||||
|
`IfcSverchok <https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/ifcsverchok/README.md/>`__
|
||||||
|
that adds IFC features to Sverchok.
|
||||||
- `BlenderGIS <https://github.com/domlysz/BlenderGIS>`__ - BlenderGIS lets you
|
- `BlenderGIS <https://github.com/domlysz/BlenderGIS>`__ - BlenderGIS lets you
|
||||||
import GIS data, grab elevation data from the web, and generate TINs from
|
import GIS data, grab elevation data from the web, and generate TINs from
|
||||||
survey points and contours.
|
survey points and contours.
|
||||||
|
|||||||
@@ -124,21 +124,21 @@ class TestAssignBrickReference:
|
|||||||
ifc.run("library.add_reference", library="library").should_be_called().will_return("reference")
|
ifc.run("library.add_reference", library="library").should_be_called().will_return("reference")
|
||||||
brick.export_brick_attributes("brick_uri").should_be_called().will_return("attributes")
|
brick.export_brick_attributes("brick_uri").should_be_called().will_return("attributes")
|
||||||
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
||||||
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
|
||||||
brick.get_brickifc_project().should_be_called().will_return("project")
|
brick.get_brickifc_project().should_be_called().will_return("project")
|
||||||
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
|
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
|
||||||
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
|
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
|
||||||
|
|
||||||
def test_assigning_to_an_existing_reference(self, ifc, brick):
|
def test_assigning_to_an_existing_reference(self, ifc, brick):
|
||||||
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
|
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
|
||||||
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
|
||||||
brick.get_brickifc_project().should_be_called().will_return("project")
|
brick.get_brickifc_project().should_be_called().will_return("project")
|
||||||
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
|
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
|
||||||
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
|
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
|
||||||
|
|
||||||
def test_adding_a_brickifc_project_if_it_doesnt_exist(self, ifc, brick):
|
def test_adding_a_brickifc_project_if_it_doesnt_exist(self, ifc, brick):
|
||||||
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
|
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
|
||||||
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
|
||||||
brick.get_brickifc_project().should_be_called().will_return(None)
|
brick.get_brickifc_project().should_be_called().will_return(None)
|
||||||
brick.get_namespace("brick_uri").should_be_called().will_return("namespace")
|
brick.get_namespace("brick_uri").should_be_called().will_return("namespace")
|
||||||
brick.add_brickifc_project("namespace").should_be_called().will_return("project")
|
brick.add_brickifc_project("namespace").should_be_called().will_return("project")
|
||||||
@@ -277,4 +277,4 @@ class TestSetBrickListRoot:
|
|||||||
class TestRemoveBrickRelation:
|
class TestRemoveBrickRelation:
|
||||||
def test_run(self, brick):
|
def test_run(self, brick):
|
||||||
brick.remove_relation("brick_uri", "predicate", "object").should_be_called()
|
brick.remove_relation("brick_uri", "predicate", "object").should_be_called()
|
||||||
subject.remove_brick_relation(brick, brick_uri="brick_uri", predicate="predicate", object="object")
|
subject.remove_brick_relation(brick, brick_uri="brick_uri", predicate="predicate", object="object")
|
||||||
|
|||||||
@@ -133,11 +133,11 @@ class TestRemoveDocument:
|
|||||||
|
|
||||||
class TestAssignDocument:
|
class TestAssignDocument:
|
||||||
def test_run(self, ifc):
|
def test_run(self, ifc):
|
||||||
ifc.run("document.assign_document", product="product", document="document").should_be_called()
|
ifc.run("document.assign_document", products=["product"], document="document").should_be_called()
|
||||||
subject.assign_document(ifc, product="product", document="document")
|
subject.assign_document(ifc, product="product", document="document")
|
||||||
|
|
||||||
|
|
||||||
class TestUnassignDocument:
|
class TestUnassignDocument:
|
||||||
def test_run(self, ifc):
|
def test_run(self, ifc):
|
||||||
ifc.run("document.unassign_document", product="product", document="document").should_be_called()
|
ifc.run("document.unassign_document", products=["product"], document="document").should_be_called()
|
||||||
subject.unassign_document(ifc, product="product", document="document")
|
subject.unassign_document(ifc, product="product", document="document")
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ class TestAddDrawing:
|
|||||||
attributes={"Identification": "X", "Name": "name", "Scope": "DRAWING"},
|
attributes={"Identification": "X", "Name": "name", "Scope": "DRAWING"},
|
||||||
).should_be_called()
|
).should_be_called()
|
||||||
ifc.run("document.edit_reference", reference="reference", attributes={"Location": "uri"}).should_be_called()
|
ifc.run("document.edit_reference", reference="reference", attributes={"Location": "uri"}).should_be_called()
|
||||||
ifc.run("document.assign_document", product="element", document="reference").should_be_called()
|
ifc.run("document.assign_document", products=["element"], document="reference").should_be_called()
|
||||||
drawing.import_drawings().should_be_called()
|
drawing.import_drawings().should_be_called()
|
||||||
subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint")
|
subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint")
|
||||||
|
|
||||||
@@ -391,7 +391,7 @@ class TestDuplicateDrawing:
|
|||||||
ifc.run("group.assign_group", group="new_group", products=["new_annotation"]).should_be_called()
|
ifc.run("group.assign_group", group="new_group", products=["new_annotation"]).should_be_called()
|
||||||
|
|
||||||
drawing.get_drawing_document("new_drawing").should_be_called().will_return("old_reference")
|
drawing.get_drawing_document("new_drawing").should_be_called().will_return("old_reference")
|
||||||
ifc.run("document.unassign_document", product="new_drawing", document="old_reference").should_be_called()
|
ifc.run("document.unassign_document", products=["new_drawing"], document="old_reference").should_be_called()
|
||||||
|
|
||||||
ifc.run("document.add_information").should_be_called().will_return("information")
|
ifc.run("document.add_information").should_be_called().will_return("information")
|
||||||
ifc.run("document.add_reference", information="information").should_be_called().will_return("reference")
|
ifc.run("document.add_reference", information="information").should_be_called().will_return("reference")
|
||||||
@@ -405,7 +405,7 @@ class TestDuplicateDrawing:
|
|||||||
ifc.run(
|
ifc.run(
|
||||||
"document.edit_reference", reference="reference", attributes={"Location": "drawing_path"}
|
"document.edit_reference", reference="reference", attributes={"Location": "drawing_path"}
|
||||||
).should_be_called()
|
).should_be_called()
|
||||||
ifc.run("document.assign_document", product="new_drawing", document="reference").should_be_called()
|
ifc.run("document.assign_document", products=["new_drawing"], document="reference").should_be_called()
|
||||||
|
|
||||||
drawing.import_drawings().should_be_called()
|
drawing.import_drawings().should_be_called()
|
||||||
subject.duplicate_drawing(ifc, drawing, drawing="drawing", should_duplicate_annotations=True)
|
subject.duplicate_drawing(ifc, drawing, drawing="drawing", should_duplicate_annotations=True)
|
||||||
|
|||||||
@@ -114,12 +114,12 @@ class TestEditLibraryReference:
|
|||||||
class TestAssignLibraryReference:
|
class TestAssignLibraryReference:
|
||||||
def test_run(self, ifc):
|
def test_run(self, ifc):
|
||||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||||
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", products=["product"], reference="reference").should_be_called()
|
||||||
subject.assign_library_reference(ifc, obj="obj", reference="reference")
|
subject.assign_library_reference(ifc, obj="obj", reference="reference")
|
||||||
|
|
||||||
|
|
||||||
class TestUnassignLibraryReference:
|
class TestUnassignLibraryReference:
|
||||||
def test_run(self, ifc):
|
def test_run(self, ifc):
|
||||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||||
ifc.run("library.unassign_reference", product="product", reference="reference").should_be_called()
|
ifc.run("library.unassign_reference", products=["product"], reference="reference").should_be_called()
|
||||||
subject.unassign_library_reference(ifc, obj="obj", reference="reference")
|
subject.unassign_library_reference(ifc, obj="obj", reference="reference")
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ from test.core.bootstrap import ifc, collector, spatial
|
|||||||
class TestReferenceStructure:
|
class TestReferenceStructure:
|
||||||
def test_run(self, ifc, spatial):
|
def test_run(self, ifc, spatial):
|
||||||
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
||||||
ifc.run("spatial.reference_structure", product="element", relating_structure="structure").should_be_called()
|
ifc.run("spatial.reference_structure", products=["element"], relating_structure="structure").should_be_called()
|
||||||
subject.reference_structure(ifc, spatial, structure="structure", element="element")
|
subject.reference_structure(ifc, spatial, structure="structure", element="element")
|
||||||
|
|
||||||
|
|
||||||
class TestDereferenceStructure:
|
class TestDereferenceStructure:
|
||||||
def test_run(self, ifc, spatial):
|
def test_run(self, ifc, spatial):
|
||||||
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
||||||
ifc.run("spatial.dereference_structure", product="element", relating_structure="structure").should_be_called()
|
ifc.run("spatial.dereference_structure", products=["element"], relating_structure="structure").should_be_called()
|
||||||
subject.dereference_structure(ifc, spatial, structure="structure", element="element")
|
subject.dereference_structure(ifc, spatial, structure="structure", element="element")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from test.bim.bootstrap import NewFile
|
from test.bim.bootstrap import NewFile
|
||||||
@@ -188,7 +189,7 @@ class TestImportReferences(NewFile):
|
|||||||
assert len(props.documents) == 1
|
assert len(props.documents) == 1
|
||||||
assert props.documents[0].ifc_definition_id == reference.id()
|
assert props.documents[0].ifc_definition_id == reference.id()
|
||||||
assert props.documents[0].name == "Unnamed"
|
assert props.documents[0].name == "Unnamed"
|
||||||
assert props.documents[0].identification == "*"
|
assert props.documents[0].identification == "X"
|
||||||
assert props.documents[0].is_information is False
|
assert props.documents[0].is_information is False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ authors:
|
|||||||
- name: "IfcOpenShell contributors"authors:
|
- name: "IfcOpenShell contributors"authors:
|
||||||
repository-code: >-
|
repository-code: >-
|
||||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/ifcbimtester
|
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/ifcbimtester
|
||||||
url: 'https://blenderbim.org/docs-python/bimtester.html'
|
url: 'https://docs.ifcopenshell.org/bimtester.html'
|
||||||
abstract: Wrapper for Gherkin based unit testing for IFC models
|
abstract: Wrapper for Gherkin based unit testing for IFC models
|
||||||
keywords:
|
keywords:
|
||||||
- Gherkin
|
- Gherkin
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ interface to access the IfcOpenShell utilities.
|
|||||||
|
|
||||||
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
||||||
installation documentation
|
installation documentation
|
||||||
<https://blenderbim.org/docs/users/installation.html>`_.
|
<https://docs.blenderbim.org/users/installation.html>`_.
|
||||||
|
|
||||||
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
|
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
|
||||||
Panel**. Scroll down to the **IFC Collaboration > IFC CSV Import / Export**
|
Panel**. Scroll down to the **IFC Collaboration > IFC CSV Import / Export**
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ interface to access the IfcOpenShell utilities.
|
|||||||
|
|
||||||
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
||||||
installation documentation
|
installation documentation
|
||||||
<https://blenderbim.org/docs/users/installation.html>`_.
|
<https://docs.blenderbim.org/users/installation.html>`_.
|
||||||
|
|
||||||
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
|
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
|
||||||
Panel**. Scroll down to the **IFC Quality Control > IFC Diff** panel.
|
Panel**. Scroll down to the **IFC Quality Control > IFC Diff** panel.
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ The BlenderBIM Add-on is available either as a stable build or a daily build.
|
|||||||
|
|
||||||
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
|
||||||
installation documentation
|
installation documentation
|
||||||
<https://blenderbim.org/docs/users/installation.html>`_.
|
<https://docs.blenderbim.org/users/installation.html>`_.
|
||||||
|
|
||||||
2. Launch Blender. On the top left of the Viewport panel, click the **Editor
|
2. Launch Blender. On the top left of the Viewport panel, click the **Editor
|
||||||
Type** icon to change the viewport into a **Python Console**.
|
Type** icon to change the viewport into a **Python Console**.
|
||||||
@@ -261,7 +261,7 @@ and run your script using the **Text > Run Script** menu or by clicking on the
|
|||||||
Blender. This can help when learning how to write scripts as you can double
|
Blender. This can help when learning how to write scripts as you can double
|
||||||
check the results of your scripts with what you see in the graphical
|
check the results of your scripts with what you see in the graphical
|
||||||
interface. `Read more
|
interface. `Read more
|
||||||
<https://blenderbim.org/docs/users/exploring_an_ifc_model.html>`_.
|
<https://docs.blenderbim.org/users/exploring_an_ifc_model.html>`_.
|
||||||
|
|
||||||
From source with precompiled binaries
|
From source with precompiled binaries
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Packaged installation
|
|||||||
|
|
||||||
IfcSverchok is packaged like a regular Blender add-on, so installation is the
|
IfcSverchok is packaged like a regular Blender add-on, so installation is the
|
||||||
same as any other Blender add-on. `Download IfcSverchok here
|
same as any other Blender add-on. `Download IfcSverchok here
|
||||||
<https://blenderbim.org/builds/ifcsverchok-230823.zip>`__.
|
<https://github.com/IfcOpenShell/IfcOpenShell/releases/download/ifcsverchok-240417/ifcsverchok-240417.zip>`__.
|
||||||
|
|
||||||
Like all Blender add-ons, they can be installed using ``Edit > Preferences >
|
Like all Blender add-ons, they can be installed using ``Edit > Preferences >
|
||||||
Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox``. You can
|
Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox``. You can
|
||||||
|
|||||||
@@ -87,6 +87,36 @@ ARGUMENTS_DEPRECATION = {
|
|||||||
"material.unassign_material": partial(
|
"material.unassign_material": partial(
|
||||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
),
|
),
|
||||||
|
"classification.add_reference": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"classification.remove_reference": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"library.assign_reference": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"library.unassign_reference": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"document.assign_document": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"document.unassign_document": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"spatial.reference_structure": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"spatial.dereference_structure": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"constraint.assign_constraint": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
|
"constraint.unassign_constraint": partial(
|
||||||
|
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ class Usecase:
|
|||||||
self.settings["product"].PredefinedType = "USERDEFINED"
|
self.settings["product"].PredefinedType = "USERDEFINED"
|
||||||
elif hasattr(self.settings["product"], "ObjectType"):
|
elif hasattr(self.settings["product"], "ObjectType"):
|
||||||
relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
|
relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
|
||||||
if relating_type and relating_type.PredefinedType != "NOTDEFINED":
|
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
|
||||||
|
if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
|
||||||
self.settings["product"].ObjectType = None
|
self.settings["product"].ObjectType = None
|
||||||
self.settings["product"].PredefinedType = None
|
self.settings["product"].PredefinedType = None
|
||||||
elif (
|
elif (
|
||||||
|
|||||||
@@ -19,10 +19,11 @@
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
import ifcopenshell.util.date
|
import ifcopenshell.util.date
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, classification=None):
|
def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]):
|
||||||
"""Adds a new classification system to the project
|
"""Adds a new classification system to the project
|
||||||
|
|
||||||
External classification systems such as Uniclass or Omniclass are
|
External classification systems such as Uniclass or Omniclass are
|
||||||
@@ -81,7 +82,7 @@ class Usecase:
|
|||||||
"classification": classification,
|
"classification": classification,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> ifcopenshell.entity_instance:
|
||||||
if isinstance(self.settings["classification"], str):
|
if isinstance(self.settings["classification"], str):
|
||||||
classification = self.file.createIfcClassification(Name=self.settings["classification"])
|
classification = self.file.createIfcClassification(Name=self.settings["classification"])
|
||||||
self.relate_to_project(classification)
|
self.relate_to_project(classification)
|
||||||
|
|||||||
@@ -17,12 +17,24 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, reference=None, identification=None, name=None, classification=None, is_lightweight=True):
|
def __init__(
|
||||||
"""Adds a new classification reference and assigns it to a product
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
reference: Optional[ifcopenshell.entity_instance] = None,
|
||||||
|
identification: Optional[str] = None,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
classification: Optional[ifcopenshell.entity_instance] = None,
|
||||||
|
is_lightweight=True,
|
||||||
|
):
|
||||||
|
"""Adds a new classification reference and assigns it to the list of products
|
||||||
|
|
||||||
A classification reference is a single entry such as "Pr_12_23_34" that
|
A classification reference is a single entry such as "Pr_12_23_34" that
|
||||||
is part of an external classification system (such as Uniclass or
|
is part of an external classification system (such as Uniclass or
|
||||||
@@ -33,7 +45,7 @@ class Usecase:
|
|||||||
resources such as profiles, documents, libraries, and so on.
|
resources such as profiles, documents, libraries, and so on.
|
||||||
|
|
||||||
Classification references can be added in two ways. Option 1) specify a
|
Classification references can be added in two ways. Option 1) specify a
|
||||||
custom arbitrary reference, where you have the manually specify the
|
custom arbitrary reference, where you have to manually specify the
|
||||||
identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
|
identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
|
||||||
Option 2) add a reference from an IFC classification library. The latter
|
Option 2) add a reference from an IFC classification library. The latter
|
||||||
is preferred if you are using a common classification system such as
|
is preferred if you are using a common classification system such as
|
||||||
@@ -52,13 +64,13 @@ class Usecase:
|
|||||||
assigned to both the type and an occurrence, then the assignment at the
|
assigned to both the type and an occurrence, then the assignment at the
|
||||||
occurrence will override the type classification.
|
occurrence will override the type classification.
|
||||||
|
|
||||||
:param product: The IFC object, property, or resource you want to
|
:param product: The list of IFC objects, properties, or resources you want to
|
||||||
associate the classification reference to.
|
associate the classification reference to.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param reference: The classification reference entity taken from an
|
:param reference: The classification reference entity taken from an
|
||||||
IFC classification library. If you supply this parameter, you will
|
IFC classification library. If you supply this parameter, you will
|
||||||
use option 2.
|
use option 2.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance, optional
|
:type reference: ifcopenshell.entity_instance.entity_instance, optional
|
||||||
:param identification: If you choose option 1 and do not specify a
|
:param identification: If you choose option 1 and do not specify a
|
||||||
reference, you may manually specify an identification code. The code
|
reference, you may manually specify an identification code. The code
|
||||||
is typically a short identifier and may have punctuation to separate
|
is typically a short identifier and may have punctuation to separate
|
||||||
@@ -70,7 +82,7 @@ class Usecase:
|
|||||||
:param classification: The IfcClassification entity in your IFC model
|
:param classification: The IfcClassification entity in your IFC model
|
||||||
(not the library, if you are doing option 2) that the reference is
|
(not the library, if you are doing option 2) that the reference is
|
||||||
part of.
|
part of.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type classification: ifcopenshell.entity_instance.entity_instance
|
||||||
:param is_lightweight: If you are doing option 2, choose whether or not
|
:param is_lightweight: If you are doing option 2, choose whether or not
|
||||||
to only add that particular reference (lighweight) or also add all
|
to only add that particular reference (lighweight) or also add all
|
||||||
of its parent references in the classification hierarchy (not
|
of its parent references in the classification hierarchy (not
|
||||||
@@ -81,8 +93,12 @@ class Usecase:
|
|||||||
is generally unnecessary. Using lightweight classifications are
|
is generally unnecessary. Using lightweight classifications are
|
||||||
recommended and is the default.
|
recommended and is the default.
|
||||||
:type is_lightweight: bool, optional
|
:type is_lightweight: bool, optional
|
||||||
|
|
||||||
|
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
|
||||||
|
|
||||||
:return: The newly added IfcClassificationReference
|
:return: The newly added IfcClassificationReference
|
||||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
or `None` if `products` was empty list.
|
||||||
|
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -93,7 +109,7 @@ class Usecase:
|
|||||||
classification = ifcopenshell.api.run("classification.add_classification",
|
classification = ifcopenshell.api.run("classification.add_classification",
|
||||||
model, classification="MyCustomClassification")
|
model, classification="MyCustomClassification")
|
||||||
ifcopenshell.api.run("classification.add_reference", model,
|
ifcopenshell.api.run("classification.add_reference", model,
|
||||||
product=wall_type, classification=classification,
|
products=[wall_type], classification=classification,
|
||||||
identification="W_01", name="Interior Walls")
|
identification="W_01", name="Interior Walls")
|
||||||
|
|
||||||
# Option 2: adding a popular classification from a library
|
# Option 2: adding a popular classification from a library
|
||||||
@@ -104,12 +120,12 @@ class Usecase:
|
|||||||
reference = [r for r in library.by_type("IfcClassificationReference")
|
reference = [r for r in library.by_type("IfcClassificationReference")
|
||||||
if r.Identification == "XYZ"][0]
|
if r.Identification == "XYZ"][0]
|
||||||
ifcopenshell.api.run("classification.add_reference", model,
|
ifcopenshell.api.run("classification.add_reference", model,
|
||||||
product=wall_type, classification=classification,
|
products=[wall_type], classification=classification,
|
||||||
reference=reference)
|
reference=reference)
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"reference": reference,
|
"reference": reference,
|
||||||
"identification": identification,
|
"identification": identification,
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -117,8 +133,27 @@ class Usecase:
|
|||||||
"is_lightweight": is_lightweight,
|
"is_lightweight": is_lightweight,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
self.is_rooted = self.settings["product"].is_a("IfcRoot")
|
if not self.settings["products"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.settings["reference"]:
|
||||||
|
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||||
|
if set(self.settings["products"]).issubset(referenced):
|
||||||
|
# nothing to do, all elements already have this reference assigned
|
||||||
|
return self.settings["reference"]
|
||||||
|
|
||||||
|
self.rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||||
|
self.non_rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||||
|
for product in self.settings["products"]:
|
||||||
|
if product.is_a("IfcRoot"):
|
||||||
|
self.rooted_products.add(product)
|
||||||
|
else:
|
||||||
|
self.non_rooted_products.add(product)
|
||||||
|
|
||||||
|
if self.non_rooted_products and self.file.schema == "IFC2X3":
|
||||||
|
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {self.non_rooted_products}.")
|
||||||
|
|
||||||
if self.settings["reference"]:
|
if self.settings["reference"]:
|
||||||
return self.add_from_library()
|
return self.add_from_library()
|
||||||
return self.add_from_identification()
|
return self.add_from_identification()
|
||||||
@@ -134,14 +169,10 @@ class Usecase:
|
|||||||
else:
|
else:
|
||||||
reference.Identification = self.settings["identification"]
|
reference.Identification = self.settings["identification"]
|
||||||
|
|
||||||
relationship = self.get_existing_relationship(reference)
|
self.update_relationships(reference)
|
||||||
if relationship:
|
|
||||||
self.add_to_existing_relationship(relationship)
|
|
||||||
else:
|
|
||||||
self.add_new_relationship(reference)
|
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
def add_from_library(self):
|
def add_from_library(self) -> ifcopenshell.entity_instance:
|
||||||
if hasattr(self.settings["reference"], "ItemReference"):
|
if hasattr(self.settings["reference"], "ItemReference"):
|
||||||
identification = self.settings["reference"].ItemReference # IFC2X3
|
identification = self.settings["reference"].ItemReference # IFC2X3
|
||||||
else:
|
else:
|
||||||
@@ -155,8 +186,9 @@ class Usecase:
|
|||||||
old_referenced_source = self.settings["reference"].ReferencedSource
|
old_referenced_source = self.settings["reference"].ReferencedSource
|
||||||
self.settings["reference"].ReferencedSource = None
|
self.settings["reference"].ReferencedSource = None
|
||||||
else:
|
else:
|
||||||
|
classification_name = self.settings["classification"].Name
|
||||||
existing_classification = [
|
existing_classification = [
|
||||||
c for c in self.file.by_type("IfcClassification") if c.Name == self.settings["classification"].Name
|
c for c in self.file.by_type("IfcClassification") if c.Name == classification_name
|
||||||
]
|
]
|
||||||
|
|
||||||
reference = migrator.migrate(self.settings["reference"], self.file)
|
reference = migrator.migrate(self.settings["reference"], self.file)
|
||||||
@@ -174,15 +206,10 @@ class Usecase:
|
|||||||
for element in to_delete:
|
for element in to_delete:
|
||||||
self.file.remove(element)
|
self.file.remove(element)
|
||||||
|
|
||||||
relationship = self.get_existing_relationship(reference)
|
self.update_relationships(reference)
|
||||||
if relationship:
|
|
||||||
self.add_to_existing_relationship(relationship)
|
|
||||||
else:
|
|
||||||
self.add_new_relationship(reference)
|
|
||||||
|
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
def get_existing_reference(self, identification):
|
def get_existing_reference(self, identification: Optional[str] = None) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
for reference in self.file.by_type("IfcClassificationReference"):
|
for reference in self.file.by_type("IfcClassificationReference"):
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
if reference.ItemReference == identification:
|
if reference.ItemReference == identification:
|
||||||
@@ -191,39 +218,39 @@ class Usecase:
|
|||||||
if reference.Identification == identification:
|
if reference.Identification == identification:
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
def add_new_relationship(self, reference):
|
def update_relationships(self, reference: ifcopenshell.entity_instance) -> None:
|
||||||
if self.is_rooted:
|
root_rel, non_root_rel = None, None
|
||||||
self.file.create_entity(
|
if self.rooted_products:
|
||||||
"IfcRelAssociatesClassification",
|
|
||||||
GlobalId=ifcopenshell.guid.new(),
|
|
||||||
RelatedObjects=[self.settings["product"]],
|
|
||||||
RelatingClassification=reference,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.file.create_entity(
|
|
||||||
"IfcExternalReferenceRelationship",
|
|
||||||
RelatingReference=reference,
|
|
||||||
RelatedResourceObjects=[self.settings["product"]],
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_to_existing_relationship(self, rel):
|
|
||||||
if self.is_rooted:
|
|
||||||
related_objects = set(rel.RelatedObjects)
|
|
||||||
related_objects.add(self.settings["product"])
|
|
||||||
rel.RelatedObjects = list(related_objects)
|
|
||||||
else:
|
|
||||||
related_objects = set(rel.RelatedResourceObjects)
|
|
||||||
related_objects.add(self.settings["product"])
|
|
||||||
rel.RelatedResourceObjects = list(related_objects)
|
|
||||||
|
|
||||||
def get_existing_relationship(self, reference):
|
|
||||||
if self.is_rooted:
|
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
||||||
if rel.RelatingClassification == reference:
|
if rel.RelatingClassification == reference:
|
||||||
return rel
|
root_rel = rel
|
||||||
elif reference.ClassificationRefForObjects:
|
break
|
||||||
return reference.ClassificationRefForObjects[0]
|
else:
|
||||||
elif self.file.schema != "IFC2X3":
|
root_rel = next(iter(reference.ClassificationRefForObjects), None)
|
||||||
if reference.ExternalReferenceForResources:
|
|
||||||
return reference.ExternalReferenceForResources[0]
|
if root_rel:
|
||||||
|
related_objects = set(root_rel.RelatedObjects) | self.rooted_products
|
||||||
|
root_rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": root_rel})
|
||||||
|
else:
|
||||||
|
self.file.create_entity(
|
||||||
|
"IfcRelAssociatesClassification",
|
||||||
|
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||||
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
|
RelatedObjects=list(self.rooted_products),
|
||||||
|
RelatingClassification=reference,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.non_rooted_products:
|
||||||
|
# NOTE: Only Ifc4+. Ifc2x3 is already handled by raising TypeError
|
||||||
|
non_root_rel = next(iter(reference.ExternalReferenceForResources), None)
|
||||||
|
if non_root_rel:
|
||||||
|
related_objects = set(non_root_rel.RelatedResourceObjects) | self.non_rooted_products
|
||||||
|
non_root_rel.RelatedResourceObjects = list(related_objects)
|
||||||
|
else:
|
||||||
|
self.file.create_entity(
|
||||||
|
"IfcExternalReferenceRelationship",
|
||||||
|
RelatingReference=reference,
|
||||||
|
RelatedResourceObjects=list(self.non_rooted_products),
|
||||||
|
)
|
||||||
|
|||||||
@@ -17,12 +17,18 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, reference=None, product=None):
|
def __init__(
|
||||||
"""Removes a classification reference from a product
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
reference: ifcopenshell.entity_instance,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
):
|
||||||
|
"""Removes a classification reference from the list of products
|
||||||
|
|
||||||
If the classification reference is no longer associated to any products,
|
If the classification reference is no longer associated to any products,
|
||||||
the classification reference itself is also removed.
|
the classification reference itself is also removed.
|
||||||
@@ -30,9 +36,12 @@ class Usecase:
|
|||||||
:param reference: The IfcClassificationReference entity of the
|
:param reference: The IfcClassificationReference entity of the
|
||||||
relationship you want to remove.
|
relationship you want to remove.
|
||||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||||
:param product: The object entity of the relationship you want to
|
:param product: The list fo object entities of the relationship you want to
|
||||||
remove.
|
remove.
|
||||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
|
||||||
|
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
|
||||||
|
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -44,42 +53,75 @@ class Usecase:
|
|||||||
classification = ifcopenshell.api.run("classification.add_classification",
|
classification = ifcopenshell.api.run("classification.add_classification",
|
||||||
model, classification="MyCustomClassification")
|
model, classification="MyCustomClassification")
|
||||||
reference = ifcopenshell.api.run("classification.add_reference", model,
|
reference = ifcopenshell.api.run("classification.add_reference", model,
|
||||||
product=wall_type, classification=classification,
|
products=[wall_type], classification=classification,
|
||||||
identification="W_01", name="Interior Walls")
|
identification="W_01", name="Interior Walls")
|
||||||
ifcopenshell.api.run("classification.remove_reference", model,
|
ifcopenshell.api.run("classification.remove_reference", model,
|
||||||
reference=reference, product=wall_type)
|
reference=reference, products=[wall_type])
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {"reference": reference, "product": product}
|
self.settings = {"reference": reference, "products": products}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> None:
|
||||||
if self.settings["product"].is_a("IfcRoot"):
|
is_ifc2x3 = self.file.schema == "IFC2X3"
|
||||||
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
products = set(self.settings["products"])
|
||||||
if rel.RelatingClassification == self.settings["reference"] and rel.RelatedObjects:
|
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||||
if self.settings["product"] in rel.RelatedObjects:
|
products -= products.difference(referenced)
|
||||||
related_objects = list(rel.RelatedObjects)
|
|
||||||
related_objects.remove(self.settings["product"])
|
# all products are already unassigned from a reference
|
||||||
if len(related_objects):
|
if not products:
|
||||||
rel.RelatedObjects = related_objects
|
return
|
||||||
else:
|
|
||||||
history = rel.OwnerHistory
|
rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||||
self.file.remove(rel)
|
non_rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||||
if history:
|
for product in self.settings["products"]:
|
||||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
if product.is_a("IfcRoot"):
|
||||||
else:
|
rooted_products.add(product)
|
||||||
for rel in self.file.by_type("IfcExternalReferenceRelationship"):
|
else:
|
||||||
if rel.RelatingReference == self.settings["reference"] and rel.RelatedResourceObjects:
|
non_rooted_products.add(product)
|
||||||
if self.settings["product"] in rel.RelatedResourceObjects:
|
|
||||||
related_objects = list(rel.RelatedResourceObjects)
|
if non_rooted_products and is_ifc2x3:
|
||||||
related_objects.remove(self.settings["product"])
|
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.")
|
||||||
if len(related_objects):
|
|
||||||
rel.RelatedResourceObjects = related_objects
|
if rooted_products:
|
||||||
else:
|
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||||
self.file.remove(rel)
|
for product in rooted_products:
|
||||||
|
reference_rels.update(product.HasAssociations)
|
||||||
|
|
||||||
|
reference_rels = {
|
||||||
|
rel
|
||||||
|
for rel in reference_rels
|
||||||
|
if rel.is_a("IfcRelAssociatesClassification")
|
||||||
|
and rel.RelatingClassification == self.settings["reference"]
|
||||||
|
}
|
||||||
|
|
||||||
|
for rel in reference_rels:
|
||||||
|
related_objects = set(rel.RelatedObjects) - rooted_products
|
||||||
|
if related_objects:
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
else:
|
||||||
|
history = rel.OwnerHistory
|
||||||
|
self.file.remove(rel)
|
||||||
|
if history:
|
||||||
|
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||||
|
|
||||||
|
if non_rooted_products:
|
||||||
|
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||||
|
for product in non_rooted_products:
|
||||||
|
rels = getattr(product, "HasExternalReferences", None)
|
||||||
|
if rels is None:
|
||||||
|
rels = getattr(product, "HasExternalReference", [])
|
||||||
|
reference_rels.update(rels)
|
||||||
|
|
||||||
|
reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]}
|
||||||
|
for rel in reference_rels:
|
||||||
|
related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
|
||||||
|
if related_objects:
|
||||||
|
rel.RelatedResourceObjects = list(related_objects)
|
||||||
|
else:
|
||||||
|
self.file.remove(rel)
|
||||||
|
|
||||||
# TODO: we only handle lightweight classifications here
|
# TODO: we only handle lightweight classifications here
|
||||||
if (
|
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||||
not self.settings["reference"].ClassificationRefForObjects
|
if not referenced_elements:
|
||||||
and not self.settings["reference"].ExternalReferenceForResources
|
|
||||||
):
|
|
||||||
self.file.remove(self.settings["reference"])
|
self.file.remove(self.settings["reference"])
|
||||||
|
|||||||
@@ -17,11 +17,18 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, constraint=None):
|
def __init__(
|
||||||
"""Assigns a constraint to a product
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
constraint: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Assigns a constraint to a list of products
|
||||||
|
|
||||||
This assigns a relationship between a product and a constraint, so that
|
This assigns a relationship between a product and a constraint, so that
|
||||||
when a product's properties and quantities do not match the requirements
|
when a product's properties and quantities do not match the requirements
|
||||||
@@ -31,36 +38,58 @@ class Usecase:
|
|||||||
constraints are inherited from the type. This way, it is not necessary
|
constraints are inherited from the type. This way, it is not necessary
|
||||||
to create lots of constraint assignments.
|
to create lots of constraint assignments.
|
||||||
|
|
||||||
:param product: The product the constraint applies to. This is anything
|
:param products: The list of products the constraint applies to. This is anything
|
||||||
which can have properties or quantities.
|
which can have properties or quantities.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param constraint: The IfcObjective constraint
|
:param constraint: The IfcObjective constraint
|
||||||
:type constraint: ifcopenshell.entity_instance.entity_instance
|
:type constraint: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: The new or updated IfcRelAssociatesConstraint relationship
|
:return: The new or updated IfcRelAssociatesConstraint relationship
|
||||||
|
or `None` if `products` was an empty list.
|
||||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"constraint": constraint,
|
"constraint": constraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
rel = self.get_constraint_rel()
|
products = set(self.settings["products"])
|
||||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
if not products:
|
||||||
related_objects.add(self.settings["product"])
|
return
|
||||||
rel.RelatedObjects = list(related_objects)
|
|
||||||
return rel
|
self.constraint = self.settings["constraint"]
|
||||||
|
|
||||||
|
rels = self.get_constraint_rels()
|
||||||
|
related_objects = set()
|
||||||
|
for rel in rels:
|
||||||
|
related_objects.update(rel.RelatedObjects)
|
||||||
|
|
||||||
|
products_to_assign = products - related_objects
|
||||||
|
if not products_to_assign:
|
||||||
|
return rels[0]
|
||||||
|
|
||||||
|
rel = next(iter(rels), None)
|
||||||
|
|
||||||
|
if rel:
|
||||||
|
related_objects = set(rel.RelatedObjects) | products_to_assign
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
return rel
|
||||||
|
|
||||||
def get_constraint_rel(self):
|
|
||||||
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
|
|
||||||
if rel.RelatingConstraint == self.settings["constraint"]:
|
|
||||||
return rel
|
|
||||||
return self.file.create_entity(
|
return self.file.create_entity(
|
||||||
"IfcRelAssociatesConstraint",
|
"IfcRelAssociatesConstraint",
|
||||||
**{
|
**{
|
||||||
"GlobalId": ifcopenshell.guid.new(),
|
"GlobalId": ifcopenshell.guid.new(),
|
||||||
# TODO: owner history
|
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||||
"RelatingConstraint": self.settings["constraint"],
|
"RelatingConstraint": self.constraint,
|
||||||
|
"RelatedObjects": list(products_to_assign),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
|
||||||
|
rels = []
|
||||||
|
for rel in self.file.get_inverse(self.constraint):
|
||||||
|
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||||
|
rels.append(rel)
|
||||||
|
return rels
|
||||||
|
|||||||
@@ -17,18 +17,24 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, constraint=None):
|
def __init__(
|
||||||
"""Unassigns a constraint to a product
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
constraint: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Unassigns a constraint from a list of products
|
||||||
|
|
||||||
The constraint will not be deleted and is available to be assigned to
|
The constraint will not be deleted and is available to be assigned to
|
||||||
other products.
|
other products.
|
||||||
|
|
||||||
:param product: The product the constraint applies to.
|
:param products: The list of products the constraint applies to.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param constraint: The IfcObjective constraint
|
:param constraint: The IfcObjective constraint
|
||||||
:type constraint: ifcopenshell.entity_instance.entity_instance
|
:type constraint: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: None
|
:return: None
|
||||||
@@ -36,14 +42,42 @@ class Usecase:
|
|||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"constraint": constraint,
|
"constraint": constraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
for rel in self.settings["product"].HasAssociations:
|
products = set(self.settings["products"])
|
||||||
if rel.is_a("IfcRelAssociatesConstraint") and rel.RelatingConstraint == self.settings["constraint"]:
|
if not products:
|
||||||
history = rel.OwnerHistory
|
return
|
||||||
self.file.remove(rel)
|
|
||||||
if history:
|
self.constraint = self.settings["constraint"]
|
||||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
rels = self.get_constraint_rels()
|
||||||
|
related_objects = set()
|
||||||
|
for rel in rels:
|
||||||
|
related_objects.update(rel.RelatedObjects)
|
||||||
|
|
||||||
|
if not related_objects.intersection(products):
|
||||||
|
return
|
||||||
|
|
||||||
|
for rel in rels:
|
||||||
|
related_objects = set(rel.RelatedObjects)
|
||||||
|
if not related_objects.intersection(products):
|
||||||
|
continue
|
||||||
|
related_objects -= products
|
||||||
|
if related_objects:
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
continue
|
||||||
|
|
||||||
|
history = rel.OwnerHistory
|
||||||
|
self.file.remove(rel)
|
||||||
|
if history:
|
||||||
|
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||||
|
|
||||||
|
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
|
||||||
|
rels = []
|
||||||
|
for rel in self.file.get_inverse(self.constraint):
|
||||||
|
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||||
|
rels.append(rel)
|
||||||
|
return rels
|
||||||
|
|||||||
@@ -69,10 +69,12 @@ class Usecase:
|
|||||||
|
|
||||||
def execute(self) -> ifcopenshell.entity_instance:
|
def execute(self) -> ifcopenshell.entity_instance:
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
reference = self.file.create_entity("IfcDocumentReference")
|
reference = self.file.create_entity("IfcDocumentReference", ItemReference="X")
|
||||||
if self.settings["information"]:
|
if self.settings["information"]:
|
||||||
references = list(self.settings["information"].DocumentReferences or [])
|
references = list(self.settings["information"].DocumentReferences or [])
|
||||||
references.append(reference)
|
references.append(reference)
|
||||||
self.settings["information"].DocumentReferences = references
|
self.settings["information"].DocumentReferences = references
|
||||||
return reference
|
return reference
|
||||||
return self.file.create_entity("IfcDocumentReference", ReferencedDocument=self.settings["information"])
|
return self.file.create_entity(
|
||||||
|
"IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X"
|
||||||
|
)
|
||||||
|
|||||||
@@ -17,11 +17,19 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, document=None):
|
def __init__(
|
||||||
"""Assigns a document to a product
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
document: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Assigns a document to a list of products
|
||||||
|
|
||||||
An object may be assigned to zero, one, or multiple documents. Almost
|
An object may be assigned to zero, one, or multiple documents. Almost
|
||||||
any object or property may be assigned to a document, though typically
|
any object or property may be assigned to a document, though typically
|
||||||
@@ -32,14 +40,16 @@ class Usecase:
|
|||||||
consistent with other external relationships (such as classification
|
consistent with other external relationships (such as classification
|
||||||
systems or libraries).
|
systems or libraries).
|
||||||
|
|
||||||
:param product: The object to associate the document to. This could be
|
:param product: The list of objects to associate the document to. This could be
|
||||||
almost any sensible object in IFC.
|
almost any sensible object in IFC.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param document: The IfcDocumentReference to associate to, or
|
:param document: The IfcDocumentReference to associate to, or
|
||||||
alternatively an IfcDocumentInformation, though this is not
|
alternatively an IfcDocumentInformation, though this is not
|
||||||
recommended.
|
recommended.
|
||||||
:type document: ifcopenshell.entity_instance.entity_instance
|
:type document: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: The IfcRelAssociatesDocument relationship
|
:return: The IfcRelAssociatesDocument relationship
|
||||||
|
or `None` if `products` was an empty list or all products were
|
||||||
|
already assigned to the `document`.
|
||||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -54,42 +64,51 @@ class Usecase:
|
|||||||
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
||||||
|
|
||||||
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
||||||
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
|
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"document": document,
|
"document": document,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
rel = self.get_document_rel()
|
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
# NOTE: reuses code from `library.assign_reference`
|
||||||
related_objects.add(self.settings["product"])
|
|
||||||
rel.RelatedObjects = list(related_objects)
|
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"])
|
||||||
|
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
|
||||||
|
products = products - referenced_elements
|
||||||
|
|
||||||
|
if not products:
|
||||||
|
return
|
||||||
|
|
||||||
def get_document_rel(self):
|
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
for rel in self.file.by_type("IfcRelAssociatesDocument"):
|
rel = next(
|
||||||
if rel.RelatingDocument == self.settings["document"]:
|
(
|
||||||
return rel
|
r
|
||||||
|
for r in self.file.by_type("IfcRelAssociatesDocument")
|
||||||
|
if r.RelatingDocument == self.settings["document"]
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if (
|
ifc_class = self.settings["document"].is_a()
|
||||||
hasattr(self.settings["document"], "DocumentRefForObjects")
|
if ifc_class == "IfcDocumentReference":
|
||||||
and self.settings["document"].DocumentRefForObjects
|
rel = next(iter(self.settings["document"].DocumentRefForObjects), None)
|
||||||
):
|
elif ifc_class == "IfcDocumentInformation":
|
||||||
return self.settings["document"].DocumentRefForObjects[0]
|
rel = next(iter(self.settings["document"].DocumentInfoForObjects), None)
|
||||||
elif (
|
|
||||||
hasattr(self.settings["document"], "DocumentInfoForObjects")
|
|
||||||
and self.settings["document"].DocumentInfoForObjects
|
|
||||||
):
|
|
||||||
return self.settings["document"].DocumentInfoForObjects[0]
|
|
||||||
|
|
||||||
return self.file.create_entity(
|
if not rel:
|
||||||
"IfcRelAssociatesDocument",
|
return self.file.create_entity(
|
||||||
**{
|
"IfcRelAssociatesDocument",
|
||||||
"GlobalId": ifcopenshell.guid.new(),
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||||
"RelatingDocument": self.settings["document"],
|
RelatedObjects=list(products),
|
||||||
}
|
RelatingDocument=self.settings["document"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
related_objects = set(rel.RelatedObjects) | products
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
|
||||||
|
return rel
|
||||||
|
|||||||
@@ -17,16 +17,22 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, document=None):
|
def __init__(
|
||||||
"""Unassigns a document and a product association
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
document: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Unassigns a document and an association to the list of products
|
||||||
|
|
||||||
:param product: The object that the document reference or information is
|
:param product: The list of objects that the document reference or information is
|
||||||
related to.
|
related to.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param document: The IfcDocumentReference (typically) or in rare cases
|
:param document: The IfcDocumentReference (typically) or in rare cases
|
||||||
the IfcDocumentInformation that is associated with the product
|
the IfcDocumentInformation that is associated with the product
|
||||||
:type document: ifcopenshell.entity_instance.entity_instance
|
:type document: ifcopenshell.entity_instance.entity_instance
|
||||||
@@ -45,24 +51,39 @@ class Usecase:
|
|||||||
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
||||||
|
|
||||||
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
||||||
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
|
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
|
||||||
|
|
||||||
# Now let's change our mind and remove the association
|
# Now let's change our mind and remove the association
|
||||||
ifcopenshell.api.run("document.unassign_document", model, product=storey, document=reference)
|
ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"document": document,
|
"document": document,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
for rel in self.settings["product"].HasAssociations:
|
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]:
|
# NOTE: reuses code from `library.un assign_reference`
|
||||||
if len(rel.RelatedObjects) == 1:
|
|
||||||
history = rel.OwnerHistory
|
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||||
self.file.remove(rel)
|
products = set(self.settings["products"])
|
||||||
if history:
|
for product in products:
|
||||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
reference_rels.update(product.HasAssociations)
|
||||||
else:
|
|
||||||
rel.RelatedObjects = [o for o in rel.RelatedObjects if o != self.settings["product"]]
|
reference_rels = {
|
||||||
|
rel
|
||||||
|
for rel in reference_rels
|
||||||
|
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]
|
||||||
|
}
|
||||||
|
|
||||||
|
for rel in reference_rels:
|
||||||
|
related_objects = set(rel.RelatedObjects) - products
|
||||||
|
if related_objects:
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
else:
|
||||||
|
history = rel.OwnerHistory
|
||||||
|
self.file.remove(rel)
|
||||||
|
if history:
|
||||||
|
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Usecase:
|
|||||||
surveyor, and a third-party digital engineer with expertise in IFC to
|
surveyor, and a third-party digital engineer with expertise in IFC to
|
||||||
moderate. For more information, read the BlenderBIM Add-on documentation
|
moderate. For more information, read the BlenderBIM Add-on documentation
|
||||||
for Georeferencing:
|
for Georeferencing:
|
||||||
https://blenderbim.org/docs/users/georeferencing.html
|
https://docs.blenderbim.org/users/georeferencing.html
|
||||||
|
|
||||||
For more information about the attributes and data types of an
|
For more information about the attributes and data types of an
|
||||||
IfcMapConversion, consult the IFC documentation.
|
IfcMapConversion, consult the IFC documentation.
|
||||||
|
|||||||
@@ -17,23 +17,30 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, reference=None):
|
def __init__(
|
||||||
"""Associates a product with a library reference
|
self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
|
||||||
|
):
|
||||||
|
"""Associates a list products with a library reference
|
||||||
|
|
||||||
A product may be associated with zero, one, or many references across
|
A product may be associated with zero, one, or many references across
|
||||||
multiple libraries. See ifcopenshell.api.library.add_reference for more
|
multiple libraries. See ifcopenshell.api.library.add_reference for more
|
||||||
detail about how references work.
|
detail about how references work.
|
||||||
|
|
||||||
:param product: The IfcProduct you want to associate with the reference
|
:param products: The list of IfcProducts you want to associate with the reference
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param reference: The IfcLibraryReference you want the product to be
|
:param reference: The IfcLibraryReference you want the product to be
|
||||||
associated with.
|
associated with.
|
||||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: The IfcRelAssociatesLibrary relationship entity
|
:return: The IfcRelAssociatesLibrary relationship entity
|
||||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
or `None` if `products` was an empty list or all products were
|
||||||
|
already assigned to the `reference`.
|
||||||
|
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -51,40 +58,46 @@ class Usecase:
|
|||||||
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
||||||
|
|
||||||
# And now assign the IFC model's AHU with its Brickschema counterpart
|
# And now assign the IFC model's AHU with its Brickschema counterpart
|
||||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
|
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"reference": reference,
|
"reference": reference,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
|
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||||
|
|
||||||
|
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||||
|
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
|
||||||
|
products = products - referenced_elements
|
||||||
|
|
||||||
|
if not products:
|
||||||
|
return
|
||||||
|
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
rels = self.get_ifc2x3_rels()
|
rel = next(
|
||||||
|
(
|
||||||
|
r
|
||||||
|
for r in self.file.by_type("IfcRelAssociatesLibrary")
|
||||||
|
if r.RelatingLibrary == self.settings["reference"]
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
rels = self.settings["reference"].LibraryRefForObjects
|
rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
|
||||||
if not rels:
|
|
||||||
|
if not rel:
|
||||||
return self.file.create_entity(
|
return self.file.create_entity(
|
||||||
"IfcRelAssociatesLibrary",
|
"IfcRelAssociatesLibrary",
|
||||||
GlobalId=ifcopenshell.guid.new(),
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||||
RelatedObjects=[self.settings["product"]],
|
RelatedObjects=list(products),
|
||||||
RelatingLibrary=self.settings["reference"],
|
RelatingLibrary=self.settings["reference"],
|
||||||
)
|
)
|
||||||
|
|
||||||
for rel in rels:
|
related_objects = set(rel.RelatedObjects) | products
|
||||||
if self.settings["product"] in rel.RelatedObjects:
|
rel.RelatedObjects = list(related_objects)
|
||||||
return rel
|
|
||||||
|
|
||||||
rel = rels[0]
|
|
||||||
related_objects = list(rel.RelatedObjects)
|
|
||||||
related_objects.append(self.settings["product"])
|
|
||||||
rel.RelatedObjects = related_objects
|
|
||||||
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
|
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
|
||||||
return rel
|
return rel
|
||||||
|
|
||||||
def get_ifc2x3_rels(self):
|
|
||||||
return [
|
|
||||||
r for r in self.file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == self.settings["reference"]
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -18,18 +18,24 @@
|
|||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.api
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, reference=None, product=None):
|
def __init__(
|
||||||
"""Unassigns a product from a reference
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
reference: ifcopenshell.entity_instance,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
):
|
||||||
|
"""Unassigns a product of products from a reference
|
||||||
|
|
||||||
If the product isn't assigned to the reference, nothing will happen.
|
If the product isn't assigned to the reference, nothing will happen.
|
||||||
|
|
||||||
:param reference: The IfcLibraryReference to unassign from
|
:param reference: The IfcLibraryReference to unassign from
|
||||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||||
:param product: A IfcProduct element to unassign from the reference
|
:param products: A list of IfcProduct elements to unassign from the reference
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -49,27 +55,36 @@ class Usecase:
|
|||||||
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
||||||
|
|
||||||
# And now assign the IFC model's AHU with its Brickschema counterpart
|
# And now assign the IFC model's AHU with its Brickschema counterpart
|
||||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
|
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
|
||||||
|
|
||||||
# Let's change our mind and unassign it.
|
# Let's change our mind and unassign it.
|
||||||
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, product=ahu)
|
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {"reference": reference, "product": product}
|
self.settings = {"reference": reference, "products": products}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
rels = self.settings["reference"].LibraryRefForObjects
|
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||||
if not rels:
|
|
||||||
return
|
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||||
for rel in rels:
|
products = set(self.settings["products"])
|
||||||
if self.settings["product"] in rel.RelatedObjects:
|
for product in products:
|
||||||
if len(rel.RelatedObjects) == 1:
|
reference_rels.update(product.HasAssociations)
|
||||||
history = rel.OwnerHistory
|
|
||||||
self.file.remove(rel)
|
reference_rels = {
|
||||||
if history:
|
rel
|
||||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
for rel in reference_rels
|
||||||
continue
|
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"]
|
||||||
related_objects = list(rel.RelatedObjects)
|
}
|
||||||
related_objects.remove(self.settings["product"])
|
|
||||||
rel.RelatedObjects = related_objects
|
for rel in reference_rels:
|
||||||
|
related_objects = set(rel.RelatedObjects) - products
|
||||||
|
if related_objects:
|
||||||
|
rel.RelatedObjects = list(related_objects)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
else:
|
||||||
|
history = rel.OwnerHistory
|
||||||
|
self.file.remove(rel)
|
||||||
|
if history:
|
||||||
|
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instanc
|
|||||||
if not app and ifc.schema == "IFC2X3":
|
if not app and ifc.schema == "IFC2X3":
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Please create an application to continue. See the owner.create_owner_history docs for more info."
|
"Please create an application to continue. See the owner.create_owner_history docs for more info."
|
||||||
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||||
)
|
)
|
||||||
return (app or [None])[0]
|
return (app or [None])[0]
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
|||||||
if not pao and ifc.schema == "IFC2X3":
|
if not pao and ifc.schema == "IFC2X3":
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Please create a user to continue. See the owner.create_owner_history docs for more info."
|
"Please create a user to continue. See the owner.create_owner_history docs for more info."
|
||||||
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||||
)
|
)
|
||||||
return (pao or [None])[0]
|
return (pao or [None])[0]
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,16 @@ import ifcopenshell.util.element
|
|||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, relating_structure=None):
|
def __init__(
|
||||||
"""Dereferences the a product and space
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
relating_structure: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Dereferences a list of products and space
|
||||||
|
|
||||||
:param product: The physical IfcElement that exists in the space.
|
:param products: The list of physical IfcElements that exists in the space.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param relating_structure: The IfcSpatialStructureElement element, such
|
:param relating_structure: The IfcSpatialStructureElement element, such
|
||||||
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
||||||
exists in.
|
exists in.
|
||||||
@@ -59,23 +64,24 @@ class Usecase:
|
|||||||
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
||||||
|
|
||||||
# And referenced in the others
|
# And referenced in the others
|
||||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
|
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2)
|
||||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
|
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3)
|
||||||
|
|
||||||
# Actually, it only goes up to storey 2.
|
# Actually, it only goes up to storey 2.
|
||||||
ifcopenshell.api.run("spatial.dereference_structure", model, product=column, relating_structure=storey3)
|
ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3)
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {"product": product, "relating_structure": relating_structure}
|
self.settings = {"products": products, "relating_structure": relating_structure}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> None:
|
||||||
for rel in self.settings["product"].ReferencedInStructures:
|
products = set(self.settings["products"])
|
||||||
if rel.RelatingStructure != self.settings["relating_structure"]:
|
for rel in self.settings["relating_structure"].ReferencesElements:
|
||||||
|
related_elements = set(rel.RelatedElements)
|
||||||
|
if not related_elements.intersection(products):
|
||||||
continue
|
continue
|
||||||
related_elements = list(rel.RelatedElements)
|
related_elements = related_elements - products
|
||||||
related_elements.remove(self.settings["product"])
|
|
||||||
if related_elements:
|
if related_elements:
|
||||||
rel.RelatedElements = related_elements
|
rel.RelatedElements = list(related_elements)
|
||||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
else:
|
else:
|
||||||
history = rel.OwnerHistory
|
history = rel.OwnerHistory
|
||||||
|
|||||||
@@ -18,11 +18,18 @@
|
|||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, product=None, relating_structure=None):
|
def __init__(
|
||||||
"""Denote that a product is related to a spatial structure
|
self,
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
products: list[ifcopenshell.entity_instance],
|
||||||
|
relating_structure: ifcopenshell.entity_instance,
|
||||||
|
):
|
||||||
|
"""Denote that a list products is related to a list of spatial structures
|
||||||
|
|
||||||
This is similar to ifcopenshell.api.spatial.assign_container, except
|
This is similar to ifcopenshell.api.spatial.assign_container, except
|
||||||
that containment can only occur between a product and a single spatial
|
that containment can only occur between a product and a single spatial
|
||||||
@@ -39,13 +46,15 @@ class Usecase:
|
|||||||
Referencing is non-hierarchical, so a door may be referenced in multiple
|
Referencing is non-hierarchical, so a door may be referenced in multiple
|
||||||
spaces simultaneously.
|
spaces simultaneously.
|
||||||
|
|
||||||
:param product: The physical IfcElement that exists in the space.
|
:param products: The list of physical IfcElements that exists in the space.
|
||||||
:type product: ifcopenshell.entity_instance.entity_instance
|
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
:param relating_structure: The IfcSpatialStructureElement element, such
|
:param relating_structure: The IfcSpatialStructureElement element, such
|
||||||
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
||||||
exists in.
|
exists in.
|
||||||
|
:type relating_structure: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: The IfcRelReferencedInSpatialStructure relationship instance
|
:return: The IfcRelReferencedInSpatialStructure relationship instance
|
||||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
or `None` if `products` was an empty list.
|
||||||
|
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -73,37 +82,43 @@ class Usecase:
|
|||||||
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
||||||
|
|
||||||
# And referenced in the others
|
# And referenced in the others
|
||||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
|
ifcopenshell.api.run(
|
||||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
|
"spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3]
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"product": product,
|
"products": products,
|
||||||
"relating_structure": relating_structure,
|
"relating_structure": relating_structure,
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self):
|
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
referenced_in_structures = self.settings["product"].ReferencedInStructures
|
structure = self.settings["relating_structure"]
|
||||||
references_elements = self.settings["relating_structure"].ReferencesElements
|
products = set(self.settings["products"])
|
||||||
|
|
||||||
for rel in referenced_in_structures:
|
if not products:
|
||||||
if rel.RelatingStructure == self.settings["relating_structure"]:
|
return
|
||||||
return
|
|
||||||
|
|
||||||
if references_elements:
|
referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure)
|
||||||
related_elements = list(references_elements[0].RelatedElements)
|
products_to_assign = products - referenced
|
||||||
related_elements.append(self.settings["product"])
|
rel = next(iter(structure.ReferencesElements), None)
|
||||||
references_elements[0].RelatedElements = related_elements
|
|
||||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": references_elements[0]})
|
if not products_to_assign:
|
||||||
else:
|
return rel
|
||||||
references_elements = self.file.create_entity(
|
|
||||||
|
if rel is None:
|
||||||
|
rel = self.file.create_entity(
|
||||||
"IfcRelReferencedInSpatialStructure",
|
"IfcRelReferencedInSpatialStructure",
|
||||||
**{
|
**{
|
||||||
"GlobalId": ifcopenshell.guid.new(),
|
"GlobalId": ifcopenshell.guid.new(),
|
||||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||||
"RelatedElements": [self.settings["product"]],
|
"RelatedElements": list(products_to_assign),
|
||||||
"RelatingStructure": self.settings["relating_structure"],
|
"RelatingStructure": structure,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
related_elements = set(rel.RelatedElements) | products_to_assign
|
||||||
|
rel.RelatedElements = list(related_elements)
|
||||||
|
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||||
|
|
||||||
return references_elements
|
return rel
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import functools
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from typing import Union, Any, Callable, TypeVar
|
from typing import Union, Any, Callable, TypeVar, overload
|
||||||
|
|
||||||
from . import ifcopenshell_wrapper
|
from . import ifcopenshell_wrapper
|
||||||
from . import settings
|
from . import settings
|
||||||
@@ -317,15 +317,25 @@ class entity_instance(object):
|
|||||||
|
|
||||||
return self.wrapped_data.to_string(valid_spf)
|
return self.wrapped_data.to_string(valid_spf)
|
||||||
|
|
||||||
def is_a(self, *args) -> Union[str, bool]:
|
@overload
|
||||||
|
def is_a(self) -> str: ...
|
||||||
|
@overload
|
||||||
|
def is_a(self, ifc_class: str) -> bool: ...
|
||||||
|
@overload
|
||||||
|
def is_a(self, with_schema: bool) -> str: ...
|
||||||
|
def is_a(self, *args: Union[str, bool]) -> Union[str, bool]:
|
||||||
"""Return the IFC class name of an instance, or checks if an instance belongs to a class.
|
"""Return the IFC class name of an instance, or checks if an instance belongs to a class.
|
||||||
|
|
||||||
The check will also return true if a parent class name is provided.
|
The check will also return true if a parent class name is provided.
|
||||||
|
|
||||||
:param args: If specified, is a case insensitive IFC class name to check
|
:param args: If specified, is a case insensitive IFC class name to check
|
||||||
:type args: string
|
or if specified as a boolean then will define whether
|
||||||
|
returned IFC class name should include schema name
|
||||||
|
(e.g. "IFC4.IfcWall" if `True` and "IfcWall" if `False`).
|
||||||
|
If omitted will act as `False`.
|
||||||
|
:type args: Union[str, bool]
|
||||||
:returns: Either the name of the class, or a boolean if it passes the check
|
:returns: Either the name of the class, or a boolean if it passes the check
|
||||||
:rtype: string|bool
|
:rtype: Union[str, bool]
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ from typing import Optional
|
|||||||
def get_references(element: ifcopenshell.entity_instance, should_inherit=True) -> set[ifcopenshell.entity_instance]:
|
def get_references(element: ifcopenshell.entity_instance, should_inherit=True) -> set[ifcopenshell.entity_instance]:
|
||||||
results = set()
|
results = set()
|
||||||
if not element.is_a("IfcRoot"):
|
if not element.is_a("IfcRoot"):
|
||||||
if hasattr(element, "HasExternalReferences"):
|
if (references := getattr(element, "HasExternalReferences", None)) is not None or (
|
||||||
return {r.RelatingReference for r in element.HasExternalReferences or []}
|
references := getattr(element, "HasExternalReference", None)
|
||||||
elif hasattr(element, "HasExternalReference"): # Seriously, IFC?
|
) is not None:
|
||||||
return {r.RelatingReference for r in element.HasExternalReference or []}
|
return {r.RelatingReference for r in references}
|
||||||
if should_inherit:
|
if should_inherit:
|
||||||
element_type = ifcopenshell.util.element.get_type(element)
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
if element_type and element_type != element:
|
if element_type and element_type != element:
|
||||||
|
|||||||
@@ -18,20 +18,59 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
def get_constraints(product):
|
import ifcopenshell
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
def get_constraints(product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||||
|
"""
|
||||||
|
Retrieves the constraints assigned to the `product`.
|
||||||
|
|
||||||
|
:param product: The IFC element.
|
||||||
|
:type product: ifcopenshell.entity_instance.entity_instance
|
||||||
|
:return: List of assigned constraints.
|
||||||
|
:rtype: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
"""
|
||||||
constraints = []
|
constraints = []
|
||||||
for rel in product.HasAssociations or []:
|
for rel in product.HasAssociations or []:
|
||||||
if rel.is_a("IfcRelAssociatesConstraint"):
|
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||||
constraints.append(rel.RelatingConstraint)
|
constraints.append(rel.RelatingConstraint)
|
||||||
return constraints
|
return constraints
|
||||||
|
|
||||||
def get_metrics(constraint):
|
|
||||||
|
def get_constrained_elements(constraint: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||||
|
"""
|
||||||
|
Retrieves the elements constrained by a `constraint`.
|
||||||
|
|
||||||
|
:param product: The IFC element.
|
||||||
|
:type product: ifcopenshell.entity_instance.entity_instance
|
||||||
|
:return: Set of elements constrained by a `constrant`.
|
||||||
|
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
"""
|
||||||
|
elements = set()
|
||||||
|
for rel in constraint.file.get_inverse(constraint):
|
||||||
|
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||||
|
elements.update(rel.RelatedObjects)
|
||||||
|
return elements
|
||||||
|
|
||||||
|
|
||||||
|
def get_metrics(constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||||
|
"""
|
||||||
|
Retrieves the list of nested constraints for a IfcObjective `constraint`.
|
||||||
|
|
||||||
|
:param product: IfcObjective constraint.
|
||||||
|
:type product: ifcopenshell.entity_instance.entity_instance
|
||||||
|
:return: List of nested constraints.
|
||||||
|
:rtype: list[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
"""
|
||||||
|
|
||||||
metrics = []
|
metrics = []
|
||||||
for metric in constraint.BenchmarkValues or []:
|
for metric in constraint.BenchmarkValues or []:
|
||||||
metrics.append(metric)
|
metrics.append(metric)
|
||||||
return metrics
|
return metrics
|
||||||
|
|
||||||
def get_metric_reference(metric, is_deep=True):
|
|
||||||
|
def get_metric_reference(metric: ifcopenshell.entity_instance, is_deep=True):
|
||||||
def get_reference_Attribute(ref, path):
|
def get_reference_Attribute(ref, path):
|
||||||
if ref:
|
if ref:
|
||||||
if is_deep:
|
if is_deep:
|
||||||
@@ -47,7 +86,10 @@ def get_metric_reference(metric, is_deep=True):
|
|||||||
reference = metric.ReferencePath
|
reference = metric.ReferencePath
|
||||||
return get_reference_Attribute(reference, "")
|
return get_reference_Attribute(reference, "")
|
||||||
|
|
||||||
def get_metric_constraints(resource, attribute):
|
|
||||||
|
def get_metric_constraints(
|
||||||
|
resource: ifcopenshell.entity_instance, attribute
|
||||||
|
) -> Union[list[ifcopenshell.entity_instance], None]:
|
||||||
metrics = []
|
metrics = []
|
||||||
for constraint in get_constraints(resource) or []:
|
for constraint in get_constraints(resource) or []:
|
||||||
for metric in get_metrics(constraint) or []:
|
for metric in get_metrics(constraint) or []:
|
||||||
@@ -60,15 +102,14 @@ def get_metric_constraints(resource, attribute):
|
|||||||
return metrics
|
return metrics
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_hard_constraint(metric):
|
|
||||||
if metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO":
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_attribute_locked(product, attribute):
|
def is_hard_constraint(metric: ifcopenshell.entity_instance) -> bool:
|
||||||
|
return metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO"
|
||||||
|
|
||||||
|
|
||||||
|
def is_attribute_locked(product: ifcopenshell.entity_instance, attribute) -> bool:
|
||||||
is_locked = False
|
is_locked = False
|
||||||
metrics = get_metric_constraints(
|
metrics = get_metric_constraints(product, attribute)
|
||||||
product, attribute
|
|
||||||
)
|
|
||||||
for metric in metrics or []:
|
for metric in metrics or []:
|
||||||
if is_hard_constraint(metric):
|
if is_hard_constraint(metric):
|
||||||
is_locked = True
|
is_locked = True
|
||||||
|
|||||||
@@ -20,16 +20,17 @@ from __future__ import annotations
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
from typing import Any, Callable, Optional, Union, Literal, overload
|
from typing import Any, Callable, Optional, Union, Literal, overload
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
def get_pset(
|
def get_pset(
|
||||||
element: ifcopenshell.entity_instance,
|
element: ifcopenshell.entity_instance,
|
||||||
name: str,
|
name: str,
|
||||||
prop: Optional[str] = None,
|
prop: Optional[str] = None,
|
||||||
psets_only=False,
|
psets_only: bool = False,
|
||||||
qtos_only=False,
|
qtos_only: bool = False,
|
||||||
should_inherit=True,
|
should_inherit: bool = True,
|
||||||
verbose=False,
|
verbose: bool = False,
|
||||||
) -> Union[Any, dict[str, Any]]:
|
) -> Union[Any, dict[str, Any]]:
|
||||||
"""Retrieve a single property set or single property
|
"""Retrieve a single property set or single property
|
||||||
|
|
||||||
@@ -429,8 +430,7 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
|
|||||||
element = ifcopenshell.by_type("IfcWall")[0]
|
element = ifcopenshell.by_type("IfcWall")[0]
|
||||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||||
"""
|
"""
|
||||||
element_type = get_type(element)
|
if element_type := get_type(element):
|
||||||
if element_type:
|
|
||||||
predefined_type = getattr(element_type, "PredefinedType", None)
|
predefined_type = getattr(element_type, "PredefinedType", None)
|
||||||
if predefined_type == "USERDEFINED" or not predefined_type:
|
if predefined_type == "USERDEFINED" or not predefined_type:
|
||||||
predefined_type = getattr(element_type, "ElementType", ...)
|
predefined_type = getattr(element_type, "ElementType", ...)
|
||||||
@@ -563,7 +563,9 @@ def get_material(
|
|||||||
return get_material(relating_type, should_skip_usage)
|
return get_material(relating_type, should_skip_usage)
|
||||||
|
|
||||||
|
|
||||||
def get_materials(element: ifcopenshell.entity_instance, should_inherit=True) -> list[ifcopenshell.entity_instance]:
|
def get_materials(
|
||||||
|
element: ifcopenshell.entity_instance, should_inherit: bool = True
|
||||||
|
) -> list[ifcopenshell.entity_instance]:
|
||||||
"""Gets individual materials of an element
|
"""Gets individual materials of an element
|
||||||
|
|
||||||
If the element has a material set, the individual materials of that set are
|
If the element has a material set, the individual materials of that set are
|
||||||
@@ -826,7 +828,7 @@ def get_layers(
|
|||||||
|
|
||||||
|
|
||||||
def get_container(
|
def get_container(
|
||||||
element: ifcopenshell.entity_instance, should_get_direct=False, ifc_class: Optional[str] = None
|
element: ifcopenshell.entity_instance, should_get_direct: bool = False, ifc_class: Optional[str] = None
|
||||||
) -> ifcopenshell.entity_instance:
|
) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
Retrieves the spatial structure container of an element.
|
Retrieves the spatial structure container of an element.
|
||||||
@@ -902,6 +904,27 @@ def get_referenced_structures(element: ifcopenshell.entity_instance) -> list[ifc
|
|||||||
return [r.RelatingStructure for r in getattr(element, "ReferencedInStructures", [])]
|
return [r.RelatingStructure for r in getattr(element, "ReferencedInStructures", [])]
|
||||||
|
|
||||||
|
|
||||||
|
def get_structure_referenced_elements(structure: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||||
|
"""Retreives a set of elements referenced by a structure
|
||||||
|
|
||||||
|
:param structure: IfcSpatialElement
|
||||||
|
:type element: ifcopenshell.entity_instance.entity_instance
|
||||||
|
:return: A set of referenced elements, IfcSpatialReferenceSelect
|
||||||
|
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
element = file.by_type("IfcBuildingStorey")[0]
|
||||||
|
print(ifcopenshell.util.element.get_structure_referenced_elements(element))
|
||||||
|
"""
|
||||||
|
referenced = set()
|
||||||
|
for rel in structure.ReferencesElements:
|
||||||
|
referenced.update(rel.RelatedElements)
|
||||||
|
return referenced
|
||||||
|
|
||||||
|
|
||||||
def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]:
|
def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]:
|
||||||
"""
|
"""
|
||||||
Retrieves all subelements of an element based on the spatial decomposition
|
Retrieves all subelements of an element based on the spatial decomposition
|
||||||
@@ -1092,6 +1115,66 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) -
|
|||||||
return is_decomposed_by[0].RelatedObjects
|
return is_decomposed_by[0].RelatedObjects
|
||||||
|
|
||||||
|
|
||||||
|
ReferenceData = namedtuple("ReferenceData", "inverse_attribute, rel_class, relating_element_attribute")
|
||||||
|
|
||||||
|
# References below are omitted because they do not introduce
|
||||||
|
# any additional referenced objects besides the objects
|
||||||
|
# from their supertype IfcExternalReference
|
||||||
|
# - IfcExternallyDefinedHatchStyle
|
||||||
|
# - IfcExternallyDefinedSurfaceStyle
|
||||||
|
# - IfcExternallyDefinedTextFont
|
||||||
|
|
||||||
|
REFERENCE_TYPES: dict[str, ReferenceData] = {
|
||||||
|
"IfcClassificationReference": ReferenceData(
|
||||||
|
"ClassificationRefForObjects",
|
||||||
|
"IfcRelAssociatesClassification",
|
||||||
|
"RelatingClassification",
|
||||||
|
),
|
||||||
|
"IfcDocumentReference": ReferenceData("DocumentRefForObjects", "IfcRelAssociatesDocument", "RelatingDocument"),
|
||||||
|
"IfcLibraryReference": ReferenceData("LibraryRefForObjects", "IfcRelAssociatesLibrary", "RelatingLibrary"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_referenced_elements(reference: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||||
|
"""Get all elements with assigned `reference`
|
||||||
|
|
||||||
|
:param reference: IfcExternalReference subtype reference
|
||||||
|
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||||
|
:return: The elements with assigned `reference`
|
||||||
|
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
reference = file.by_type("IfcClassificationReference")[0]
|
||||||
|
elements = ifcopenshell.util.element.get_referenced_elements(reference)
|
||||||
|
"""
|
||||||
|
|
||||||
|
related_objects: set[ifcopenshell.entity_instance] = set()
|
||||||
|
ifc_file = reference.file
|
||||||
|
ifc_class = reference.is_a()
|
||||||
|
|
||||||
|
if ifc_file.schema == "IFC2X3":
|
||||||
|
reference_data = REFERENCE_TYPES.get(ifc_class)
|
||||||
|
if reference_data:
|
||||||
|
for rel in ifc_file.by_type(reference_data.rel_class):
|
||||||
|
if getattr(rel, reference_data.relating_element_attribute) == reference:
|
||||||
|
related_objects.update(rel.RelatedObjects)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# IfcExternalReference
|
||||||
|
for external_rel in reference.ExternalReferenceForResources:
|
||||||
|
related_objects.update(external_rel.RelatedResourceObjects)
|
||||||
|
|
||||||
|
reference_data = REFERENCE_TYPES.get(ifc_class)
|
||||||
|
if reference_data:
|
||||||
|
for rel in getattr(reference, reference_data.inverse_attribute):
|
||||||
|
related_objects.update(rel.RelatedObjects)
|
||||||
|
|
||||||
|
return related_objects
|
||||||
|
|
||||||
|
|
||||||
def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None:
|
def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None:
|
||||||
for i, attribute_value in enumerate(element):
|
for i, attribute_value in enumerate(element):
|
||||||
if has_element_reference(attribute_value, old):
|
if has_element_reference(attribute_value, old):
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import shapely
|
import shapely
|
||||||
|
import shapely.ops
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
|
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
|
||||||
]
|
]
|
||||||
dependencies = ["mathutils", "shapely", "numpy", "isodate", "dateutil", "lark"]
|
dependencies = ["mathutils", "shapely", "numpy", "isodate", "python-dateutil", "lark"]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "http://ifcopenshell.org"
|
"Homepage" = "http://ifcopenshell.org"
|
||||||
|
|||||||
+79
-36
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
@@ -23,85 +24,127 @@ import ifcopenshell.util.classification
|
|||||||
|
|
||||||
class TestAddReference(test.bootstrap.IFC4):
|
class TestAddReference(test.bootstrap.IFC4):
|
||||||
def test_adding_a_reference(self):
|
def test_adding_a_reference(self):
|
||||||
|
is_ifc2x3 = self.file.schema == "IFC2X3"
|
||||||
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element, element2],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
)
|
)
|
||||||
references = list(ifcopenshell.util.classification.get_references(element))
|
references = list(ifcopenshell.util.classification.get_references(element))
|
||||||
assert len(references) == 1
|
assert len(references) == 1
|
||||||
assert references[0].Identification == "X"
|
assert getattr(references[0], "ItemReference" if is_ifc2x3 else "Identification") == "X"
|
||||||
assert references[0].Name == "Foobar"
|
assert references[0].Name == "Foobar"
|
||||||
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
||||||
|
|
||||||
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
references2 = list(ifcopenshell.util.classification.get_references(element))
|
||||||
ifcopenshell.api.run(
|
assert len(references2) == 1
|
||||||
"classification.add_reference",
|
assert getattr(references2[0], "ItemReference" if is_ifc2x3 else "Identification") == "X"
|
||||||
self.file,
|
assert references2[0].Name == "Foobar"
|
||||||
product=element2,
|
assert references2[0] == references[0]
|
||||||
identification="X",
|
|
||||||
name="Foobar",
|
rel = next(
|
||||||
classification=result,
|
rel
|
||||||
|
for rel in self.file.by_type("IfcRelAssociatesClassification")
|
||||||
|
if rel.RelatingClassification == references[0]
|
||||||
)
|
)
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
|
assert len(rel.RelatedObjects) == 2
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
|
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
|
|
||||||
|
|
||||||
def test_adding_a_library_based_reference(self):
|
def test_adding_a_library_based_reference(self):
|
||||||
|
is_ifc2x3 = self.file.schema == "IFC2X3"
|
||||||
|
|
||||||
library = ifcopenshell.file()
|
library = ifcopenshell.file()
|
||||||
classification = library.createIfcClassification(Name="Name")
|
classification = library.createIfcClassification(Name="Name")
|
||||||
reference = library.createIfcClassificationReference(Identification="1", ReferencedSource=classification)
|
reference = library.createIfcClassificationReference(Identification="1", ReferencedSource=classification)
|
||||||
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification=classification)
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification=classification)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element, element2],
|
||||||
reference=reference,
|
reference=reference,
|
||||||
classification=result,
|
classification=result,
|
||||||
)
|
)
|
||||||
references = list(ifcopenshell.util.classification.get_references(element))
|
references = list(ifcopenshell.util.classification.get_references(element))
|
||||||
assert len(references) == 1
|
assert len(references) == 1
|
||||||
assert references[0].Identification == "1"
|
assert getattr(references[0], "ItemReference" if is_ifc2x3 else "Identification") == "1"
|
||||||
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
||||||
|
|
||||||
def test_adding_a_reference_to_a_resource(self):
|
references2 = list(ifcopenshell.util.classification.get_references(element2))
|
||||||
|
assert len(references2) == 1
|
||||||
|
assert getattr(references2[0], "ItemReference" if is_ifc2x3 else "Identification") == "1"
|
||||||
|
assert references2[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
||||||
|
assert references[0] == references2[0]
|
||||||
|
|
||||||
|
rel = next(
|
||||||
|
rel
|
||||||
|
for rel in self.file.by_type("IfcRelAssociatesClassification")
|
||||||
|
if rel.RelatingClassification == references[0]
|
||||||
|
)
|
||||||
|
assert len(rel.RelatedObjects) == 2
|
||||||
|
|
||||||
|
def test_adding_a_reference_to_a_resource_and_to_a_root(self):
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = self.file.createIfcMaterial()
|
element = self.file.createIfcMaterial()
|
||||||
|
element2 = self.file.createIfcCostValue()
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
ifcopenshell.api.run(
|
|
||||||
"classification.add_reference",
|
if self.file.schema == "IFC2X3":
|
||||||
self.file,
|
with pytest.raises(TypeError):
|
||||||
product=element,
|
ifcopenshell.api.run(
|
||||||
identification="X",
|
"classification.add_reference",
|
||||||
name="Foobar",
|
self.file,
|
||||||
classification=result,
|
products=[element, element2, element3],
|
||||||
)
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
products=[element, element2, element3],
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
|
||||||
references = list(ifcopenshell.util.classification.get_references(element))
|
references = list(ifcopenshell.util.classification.get_references(element))
|
||||||
assert len(references) == 1
|
assert len(references) == 1
|
||||||
assert references[0].Identification == "X"
|
assert references[0].Identification == "X"
|
||||||
assert references[0].Name == "Foobar"
|
assert references[0].Name == "Foobar"
|
||||||
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
||||||
|
|
||||||
element2 = self.file.createIfcCostValue()
|
references2 = list(ifcopenshell.util.classification.get_references(element2))
|
||||||
ifcopenshell.api.run(
|
assert references2[0].Identification == "X"
|
||||||
"classification.add_reference",
|
assert references2[0].Name == "Foobar"
|
||||||
self.file,
|
assert references2[0] == references[0]
|
||||||
product=element2,
|
|
||||||
identification="X",
|
references3 = list(ifcopenshell.util.classification.get_references(element3))
|
||||||
name="Foobar",
|
assert references3[0].Identification == "X"
|
||||||
classification=result,
|
assert references3[0].Name == "Foobar"
|
||||||
)
|
assert references3[0] == references[0]
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
|
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
|
|
||||||
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
|
|
||||||
|
|
||||||
assert len(self.file.by_type("IfcExternalReferenceRelationship")[0].RelatedResourceObjects) == 2
|
assert len(self.file.by_type("IfcExternalReferenceRelationship")[0].RelatedResourceObjects) == 2
|
||||||
|
rel = next(
|
||||||
|
rel
|
||||||
|
for rel in self.file.by_type("IfcRelAssociatesClassification")
|
||||||
|
if rel.RelatingClassification == references[0]
|
||||||
|
)
|
||||||
|
assert len(rel.RelatedObjects) == 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestAddReferenceIFC2X3(test.bootstrap.IFC2X3, TestAddReference):
|
||||||
|
pass
|
||||||
@@ -34,7 +34,7 @@ class TestRemoveClassification(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
@@ -51,7 +51,7 @@ class TestRemoveClassification(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
|
|||||||
+47
-24
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
@@ -25,58 +26,80 @@ class TestRemoveReference(test.bootstrap.IFC4):
|
|||||||
def test_removing_a_reference(self):
|
def test_removing_a_reference(self):
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
reference = ifcopenshell.api.run(
|
reference = ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element, element2],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
|
ifcopenshell.api.run(
|
||||||
|
"classification.remove_reference", self.file, products=[element, element2], reference=reference
|
||||||
|
)
|
||||||
assert len(ifcopenshell.util.classification.get_references(element)) == 0
|
assert len(ifcopenshell.util.classification.get_references(element)) == 0
|
||||||
|
assert len(ifcopenshell.util.classification.get_references(element2)) == 0
|
||||||
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
||||||
|
|
||||||
def test_removing_a_reference_from_a_resource(self):
|
def test_removing_a_reference_from_a_resource_and_from_a_root(self):
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = self.file.createIfcMaterial()
|
element = self.file.createIfcMaterial()
|
||||||
|
element2 = self.file.createIfcCostValue()
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
reference = ifcopenshell.api.run(
|
if self.file.schema == "IFC2X3":
|
||||||
"classification.add_reference",
|
with pytest.raises(TypeError):
|
||||||
self.file,
|
reference = ifcopenshell.api.run(
|
||||||
product=element,
|
"classification.add_reference",
|
||||||
identification="X",
|
self.file,
|
||||||
name="Foobar",
|
products=[element, element2, element3],
|
||||||
classification=result,
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
reference = ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
products=[element, element2, element3],
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"classification.remove_reference", self.file, products=[element, element2, element3], reference=reference
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
|
|
||||||
assert len(ifcopenshell.util.classification.get_references(element)) == 0
|
assert len(ifcopenshell.util.classification.get_references(element)) == 0
|
||||||
|
assert len(ifcopenshell.util.classification.get_references(element2)) == 0
|
||||||
|
assert len(ifcopenshell.util.classification.get_references(element3)) == 0
|
||||||
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
||||||
|
|
||||||
def test_retaining_the_reference_if_still_in_use(self):
|
def test_retaining_the_reference_if_still_in_use(self):
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
element = self.file.createIfcMaterial()
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
element2 = self.file.createIfcMaterial()
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
reference = ifcopenshell.api.run(
|
reference = ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element, element2, element3],
|
||||||
identification="X",
|
|
||||||
name="Foobar",
|
|
||||||
classification=result,
|
|
||||||
)
|
|
||||||
reference2 = ifcopenshell.api.run(
|
|
||||||
"classification.add_reference",
|
|
||||||
self.file,
|
|
||||||
product=element2,
|
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
)
|
)
|
||||||
assert len(self.file.by_type("IfcClassificationReference")) == 1
|
assert len(self.file.by_type("IfcClassificationReference")) == 1
|
||||||
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
|
ifcopenshell.api.run(
|
||||||
|
"classification.remove_reference", self.file, products=[element, element2], reference=reference
|
||||||
|
)
|
||||||
assert len(self.file.by_type("IfcClassificationReference")) == 1
|
assert len(self.file.by_type("IfcClassificationReference")) == 1
|
||||||
ifcopenshell.api.run("classification.remove_reference", self.file, product=element2, reference=reference2)
|
ifcopenshell.api.run("classification.remove_reference", self.file, products=[element3], reference=reference)
|
||||||
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestRemoveReferenceIFC2X3(test.bootstrap.IFC2X3, TestRemoveReference):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# IfcOpenShell - IFC toolkit and geometry engine
|
||||||
|
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of IfcOpenShell.
|
||||||
|
#
|
||||||
|
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import test.bootstrap
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.constraint
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssignConstraint(test.bootstrap.IFC4):
|
||||||
|
def test_assign_a_constraint(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(constraint) == {element, element2}
|
||||||
|
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 1
|
||||||
|
|
||||||
|
def test_doing_nothing_if_the_constraint_is_already_assigned(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
total_elements = len([e for e in self.file])
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
assert len([e for e in self.file]) == total_elements
|
||||||
|
|
||||||
|
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run("constraint.assign_constraint", self.file, products=[element1], constraint=constraint)
|
||||||
|
rel = self.file.by_type("IfcRelAssociatesConstraint")[0]
|
||||||
|
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element2, element3], constraint=constraint
|
||||||
|
)
|
||||||
|
assert len(rel.RelatedObjects) == 3
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssignConstraintIFC2X3(test.bootstrap.IFC2X3, TestAssignConstraint):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# IfcOpenShell - IFC toolkit and geometry engine
|
||||||
|
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of IfcOpenShell.
|
||||||
|
#
|
||||||
|
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import test.bootstrap
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.constraint
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignConstraint(test.bootstrap.IFC4):
|
||||||
|
def test_unassigning_a_constraint(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.unassign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
|
||||||
|
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 0
|
||||||
|
|
||||||
|
def test_doing_nothing_if_no_constraint(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.unassign_constraint", self.file, products=[element, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(element2) == set()
|
||||||
|
|
||||||
|
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run("constraint.assign_constraint", self.file, products=[element1], constraint=constraint)
|
||||||
|
rel = self.file.by_type("IfcRelAssociatesConstraint")[0]
|
||||||
|
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, products=[element2, element3], constraint=constraint
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.unassign_constraint", self.file, products=[element1, element2], constraint=constraint
|
||||||
|
)
|
||||||
|
assert rel.RelatedObjects == (element3,)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignConstraintIFC2X3(test.bootstrap.IFC2X3, TestUnassignConstraint):
|
||||||
|
pass
|
||||||
@@ -18,22 +18,25 @@
|
|||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class TestAssignDocument(test.bootstrap.IFC4):
|
class TestAssignDocument(test.bootstrap.IFC4):
|
||||||
def test_assigning_a_document(self):
|
def test_assigning_a_document(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
|
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
|
||||||
assert element.HasAssociations[0].RelatingDocument == reference
|
assert element.HasAssociations[0].RelatingDocument == reference
|
||||||
|
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element}
|
||||||
|
|
||||||
def test_assigning_multiple_documents(self):
|
def test_assigning_multiple_documents(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
|
ifcopenshell.api.run("document.assign_document", self.file, products=[element, element2], document=reference)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
|
|
||||||
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 1
|
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 1
|
||||||
assert element.HasAssociations[0].RelatingDocument == reference
|
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element, element2}
|
||||||
assert element2.HasAssociations[0].RelatingDocument == reference
|
|
||||||
assert element.HasAssociations[0] == element.HasAssociations[0]
|
|
||||||
|
class TestAssignDocumentIFC2X3(test.bootstrap.IFC2X3, TestAssignDocument):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
|
|||||||
wall = self.file.createIfcWall()
|
wall = self.file.createIfcWall()
|
||||||
information = ifcopenshell.api.run("document.add_information", self.file, parent=None)
|
information = ifcopenshell.api.run("document.add_information", self.file, parent=None)
|
||||||
reference = ifcopenshell.api.run("document.add_reference", self.file, information=information)
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=information)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=wall, document=reference)
|
ifcopenshell.api.run("document.assign_document", self.file, products=[wall], document=reference)
|
||||||
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 2
|
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 2
|
||||||
ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
|
ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
|
||||||
assert len(self.file.by_type("IfcDocumentReference")) == 0
|
assert len(self.file.by_type("IfcDocumentReference")) == 0
|
||||||
|
|||||||
@@ -18,23 +18,29 @@
|
|||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class TestUnassignDocument(test.bootstrap.IFC4):
|
class TestUnassignDocument(test.bootstrap.IFC4):
|
||||||
def test_unassigning_a_document(self):
|
def test_unassigning_a_document(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
|
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
|
||||||
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
|
ifcopenshell.api.run("document.unassign_document", self.file, products=[element], document=reference)
|
||||||
assert not element.HasAssociations
|
assert not element.HasAssociations
|
||||||
assert not len(self.file.by_type("IfcRelAssociatesDocument"))
|
assert not len(self.file.by_type("IfcRelAssociatesDocument"))
|
||||||
|
|
||||||
def test_unassigning_a_document_used_by_multiple_entities(self):
|
def test_unassigning_a_document_used_by_multiple_entities(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
|
ifcopenshell.api.run(
|
||||||
ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
|
"document.assign_document", self.file, products=[element, element2, element3], document=reference
|
||||||
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
|
)
|
||||||
assert not element.HasAssociations
|
ifcopenshell.api.run("document.unassign_document", self.file, products=[element, element2], document=reference)
|
||||||
assert element2.HasAssociations[0].RelatingDocument == reference
|
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element3}
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignDocumentIFC2X3(test.bootstrap.IFC2X3, TestUnassignDocument):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -20,39 +20,48 @@ import test.bootstrap
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
|
||||||
|
|
||||||
|
def validate_ifc_file(ifc_file: ifcopenshell.file, use_json=True):
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.validate
|
||||||
|
|
||||||
|
if use_json:
|
||||||
|
logger = ifcopenshell.validate.json_logger()
|
||||||
|
else:
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger("validate")
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
ifcopenshell.validate.validate(ifc_file, logger, express_rules=True)
|
||||||
|
if use_json:
|
||||||
|
if logger.statements:
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
pprint(logger.statements)
|
||||||
|
else:
|
||||||
|
print("IFC is completely valid.")
|
||||||
|
|
||||||
|
|
||||||
class TestAssignReference(test.bootstrap.IFC4):
|
class TestAssignReference(test.bootstrap.IFC4):
|
||||||
def test_assigning_a_reference(self):
|
def test_assigning_a_reference(self):
|
||||||
reference = self.file.createIfcLibraryReference()
|
reference = self.file.createIfcLibraryReference()
|
||||||
product = self.file.createIfcWall()
|
product = self.file.createIfcWall()
|
||||||
product2 = self.file.createIfcWall()
|
product2 = self.file.createIfcWall()
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
product3 = self.file.createIfcWall()
|
||||||
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
|
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
|
||||||
assert reference.LibraryRefForObjects[0].RelatedObjects == (product, product2)
|
assert rel.RelatedObjects == (product,)
|
||||||
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product2, product3], reference=reference)
|
||||||
|
assert set(rel.RelatedObjects) == set((product, product2, product3))
|
||||||
|
|
||||||
def test_not_assigning_twice(self):
|
def test_not_assigning_twice(self):
|
||||||
reference = self.file.createIfcLibraryReference()
|
reference = self.file.createIfcLibraryReference()
|
||||||
product = self.file.createIfcWall()
|
product = self.file.createIfcWall()
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
|
||||||
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3):
|
|
||||||
def test_assigning_a_reference(self):
|
|
||||||
reference = self.file.createIfcLibraryReference()
|
|
||||||
product = self.file.createIfcWall()
|
|
||||||
product2 = self.file.createIfcWall()
|
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
|
||||||
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
|
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
|
||||||
assert rel.RelatedObjects == (product,)
|
assert rel.RelatedObjects == (product,)
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
|
|
||||||
assert rel.RelatedObjects == (product, product2)
|
|
||||||
|
|
||||||
def test_not_assigning_twice(self):
|
|
||||||
reference = self.file.createIfcLibraryReference()
|
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3, TestAssignReference):
|
||||||
product = self.file.createIfcWall()
|
pass
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
|
||||||
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
|
|
||||||
assert rel.RelatedObjects == (product,)
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
|
|||||||
def test_removing_a_reference(self):
|
def test_removing_a_reference(self):
|
||||||
reference = self.file.createIfcLibraryReference()
|
reference = self.file.createIfcLibraryReference()
|
||||||
product = self.file.createIfcWall()
|
product = self.file.createIfcWall()
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
|
||||||
ifcopenshell.api.run("library.remove_reference", self.file, reference=reference)
|
ifcopenshell.api.run("library.remove_reference", self.file, reference=reference)
|
||||||
assert len(self.file.by_type("IfcLibraryReference")) == 0
|
assert len(self.file.by_type("IfcLibraryReference")) == 0
|
||||||
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
|
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
|
||||||
|
|||||||
@@ -18,12 +18,20 @@
|
|||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class TestUnassignReference(test.bootstrap.IFC4):
|
class TestUnassignReference(test.bootstrap.IFC4):
|
||||||
def test_unassigning_a_reference(self):
|
def test_unassigning_a_reference(self):
|
||||||
reference = self.file.createIfcLibraryReference()
|
reference = self.file.createIfcLibraryReference()
|
||||||
product = self.file.createIfcWall()
|
products = [self.file.createIfcWall() for i in range(3)]
|
||||||
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
ifcopenshell.api.run("library.assign_reference", self.file, products=products, reference=reference)
|
||||||
ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference)
|
ifcopenshell.api.run("library.unassign_reference", self.file, products=products[:1], reference=reference)
|
||||||
|
assert ifcopenshell.util.element.get_referenced_elements(reference) == set(products[1:])
|
||||||
|
|
||||||
|
ifcopenshell.api.run("library.unassign_reference", self.file, products=products[1:], reference=reference)
|
||||||
|
assert ifcopenshell.util.element.get_referenced_elements(reference) == set()
|
||||||
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
|
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
|
||||||
|
|
||||||
|
class TestUnassignReferenceIFC2X3(test.bootstrap.IFC2X3, TestUnassignReference):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -26,28 +26,42 @@ class TestDereferenceStructure(test.bootstrap.IFC4):
|
|||||||
def test_removing_a_container(self):
|
def test_removing_a_container(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
||||||
|
assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0
|
||||||
|
|
||||||
def test_doing_nothing_if_no_container(self):
|
def test_doing_nothing_if_no_container(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
||||||
|
assert ifcopenshell.util.element.get_referenced_structures(subelement2) == []
|
||||||
|
|
||||||
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
|
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element)
|
subelement3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element)
|
ifcopenshell.api.run(
|
||||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement1, relating_structure=element)
|
"spatial.reference_structure", self.file, products=[subelement1], relating_structure=element
|
||||||
assert self.file.by_type("IfcRelReferencedInSpatialStructure")[0].RelatedElements == (subelement2,)
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement2, subelement3], relating_structure=element
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.dereference_structure", self.file, products=[subelement1, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
|
assert element.ReferencesElements[0].RelatedElements == (subelement3,)
|
||||||
|
|
||||||
def test_deleting_the_rel_when_a_container_is_removed_with_no_elements(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
class TestDereferenceStructureIFC2X3(test.bootstrap.IFC2X3, TestDereferenceStructure):
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
pass
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
|
||||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
|
||||||
assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0
|
|
||||||
|
|||||||
@@ -26,25 +26,39 @@ class TestReferenceStructure(test.bootstrap.IFC4):
|
|||||||
def test_referencing_a_structure(self):
|
def test_referencing_a_structure(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
rel = ifcopenshell.api.run(
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
"spatial.reference_structure", self.file, product=subelement, relating_structure=element
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
)
|
)
|
||||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element]
|
assert ifcopenshell.util.element.get_structure_referenced_elements(element) == {subelement, subelement2}
|
||||||
assert rel.is_a("IfcRelReferencedInSpatialStructure")
|
|
||||||
|
|
||||||
def test_doing_nothing_if_the_structure_is_already_referenced(self):
|
def test_doing_nothing_if_the_structure_is_already_referenced(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
total_elements = len([e for e in self.file])
|
total_elements = len([e for e in self.file])
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
|
||||||
|
)
|
||||||
assert len([e for e in self.file]) == total_elements
|
assert len([e for e in self.file]) == total_elements
|
||||||
|
|
||||||
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
|
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
|
||||||
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement1], relating_structure=element
|
||||||
|
)
|
||||||
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element1)
|
subelement3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element1)
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement2, subelement3], relating_structure=element
|
||||||
|
)
|
||||||
rel = subelement1.ReferencedInStructures[0]
|
rel = subelement1.ReferencedInStructures[0]
|
||||||
assert len(rel.RelatedElements) == 2
|
assert len(rel.RelatedElements) == 3
|
||||||
|
|
||||||
|
|
||||||
|
class TestReferenceStructureIFC2X3(test.bootstrap.IFC2X3, TestReferenceStructure):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.classification
|
||||||
|
import ifcopenshell.util.constraint
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.system
|
import ifcopenshell.util.system
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -186,3 +188,126 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4):
|
|||||||
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 0
|
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 0
|
||||||
assert len(self.file.by_type("IfcWall")) == 1
|
assert len(self.file.by_type("IfcWall")) == 1
|
||||||
assert len(self.file.by_type("IfcMaterial")) == 1
|
assert len(self.file.by_type("IfcMaterial")) == 1
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_adding_a_reference(self):
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
product=element,
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
references = list(ifcopenshell.util.classification.get_references(element))
|
||||||
|
assert len(references) == 1
|
||||||
|
assert references[0].Identification == "X"
|
||||||
|
assert references[0].Name == "Foobar"
|
||||||
|
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
|
||||||
|
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
product=element2,
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
|
||||||
|
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
|
||||||
|
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_removing_a_reference(self):
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
|
reference = ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
products=[element],
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
|
||||||
|
assert len(ifcopenshell.util.classification.get_references(element)) == 0
|
||||||
|
assert len(self.file.by_type("IfcClassificationReference")) == 0
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_assigning_a_reference(self):
|
||||||
|
reference = self.file.createIfcLibraryReference()
|
||||||
|
product = self.file.createIfcWall()
|
||||||
|
product2 = self.file.createIfcWall()
|
||||||
|
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
|
||||||
|
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
|
||||||
|
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
|
||||||
|
assert set(reference.LibraryRefForObjects[0].RelatedObjects) == set((product, product2))
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_unassigning_a_reference(self):
|
||||||
|
reference = self.file.createIfcLibraryReference()
|
||||||
|
product = self.file.createIfcWall()
|
||||||
|
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
|
||||||
|
ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference)
|
||||||
|
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_assigning_a_document(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
|
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
|
||||||
|
assert element.HasAssociations[0].RelatingDocument == reference
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_unassigning_a_document(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
|
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
|
||||||
|
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
|
||||||
|
assert not element.HasAssociations
|
||||||
|
assert not len(self.file.by_type("IfcRelAssociatesDocument"))
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_referencing_a_structure(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
rel = ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, product=subelement, relating_structure=element
|
||||||
|
)
|
||||||
|
assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element]
|
||||||
|
assert rel.is_a("IfcRelReferencedInSpatialStructure")
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_removing_a_container(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"spatial.reference_structure", self.file, products=[subelement], relating_structure=element
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
||||||
|
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_assign_a_constraint(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
ifcopenshell.api.run("constraint.assign_constraint", self.file, product=element, constraint=constraint)
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(constraint) == {element}
|
||||||
|
|
||||||
|
@deprecation_check
|
||||||
|
def test_unassigning_a_constraint(self):
|
||||||
|
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.assign_constraint", self.file, product=element, constraint=constraint
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"constraint.unassign_constraint", self.file, product=element, constraint=constraint
|
||||||
|
)
|
||||||
|
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
|
||||||
|
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 0
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ class TestGetReferences(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
reference=reference1,
|
reference=reference1,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
reference=reference2,
|
reference=reference2,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
@@ -54,7 +54,7 @@ class TestGetReferences(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
@@ -74,14 +74,14 @@ class TestGetReferences(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
reference=reference1,
|
reference=reference1,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element_type,
|
products=[element_type],
|
||||||
reference=reference2,
|
reference=reference2,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
@@ -103,14 +103,14 @@ class TestGetReferences(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
reference=reference1,
|
reference=reference1,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element_type,
|
products=[element_type],
|
||||||
reference=reference2,
|
reference=reference2,
|
||||||
classification=classification,
|
classification=classification,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -291,6 +291,16 @@ class TestGetPredefinedTypeIFC4(test.bootstrap.IFC4):
|
|||||||
element_type.ProcessType = "FOOBAR"
|
element_type.ProcessType = "FOOBAR"
|
||||||
assert subject.get_predefined_type(element) == "FOOBAR"
|
assert subject.get_predefined_type(element) == "FOOBAR"
|
||||||
|
|
||||||
|
def test_getting_an_element_type_predefined_type(self):
|
||||||
|
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
|
||||||
|
element_type.PredefinedType = "PARTITIONING"
|
||||||
|
assert subject.get_predefined_type(element_type) == "PARTITIONING"
|
||||||
|
|
||||||
|
def test_getting_an_element_type_null_predefined_type(self):
|
||||||
|
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
|
||||||
|
element_type.PredefinedType = "NOTDEFINED"
|
||||||
|
assert subject.get_predefined_type(element_type) == "NOTDEFINED"
|
||||||
|
|
||||||
|
|
||||||
class TestGetTypeIFC4(test.bootstrap.IFC4):
|
class TestGetTypeIFC4(test.bootstrap.IFC4):
|
||||||
def test_getting_the_type_of_a_product(self):
|
def test_getting_the_type_of_a_product(self):
|
||||||
@@ -352,12 +362,16 @@ class TestGetMaterial(test.bootstrap.IFC4):
|
|||||||
|
|
||||||
def test_getting_a_material_layer_set_of_a_product(self):
|
def test_getting_a_material_layer_set_of_a_product(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
rel = ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialLayerSet")
|
rel = ifcopenshell.api.run(
|
||||||
|
"material.assign_material", self.file, products=[element], type="IfcMaterialLayerSet"
|
||||||
|
)
|
||||||
assert subject.get_material(element) == rel.RelatingMaterial
|
assert subject.get_material(element) == rel.RelatingMaterial
|
||||||
|
|
||||||
def test_getting_a_material_profile_set_of_a_product(self):
|
def test_getting_a_material_profile_set_of_a_product(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
rel = ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialProfileSet")
|
rel = ifcopenshell.api.run(
|
||||||
|
"material.assign_material", self.file, products=[element], type="IfcMaterialProfileSet"
|
||||||
|
)
|
||||||
assert subject.get_material(element) == rel.RelatingMaterial
|
assert subject.get_material(element) == rel.RelatingMaterial
|
||||||
|
|
||||||
def test_getting_a_material_layer_set_usage_of_a_product(self):
|
def test_getting_a_material_layer_set_usage_of_a_product(self):
|
||||||
@@ -512,7 +526,9 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
|
|||||||
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet")
|
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet")
|
||||||
ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material)
|
ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material)
|
||||||
ifcopenshell.api.run("material.assign_material", self.file, products=[element_type], material=material_set)
|
ifcopenshell.api.run("material.assign_material", self.file, products=[element_type], material=material_set)
|
||||||
ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialProfileSetUsage")
|
ifcopenshell.api.run(
|
||||||
|
"material.assign_material", self.file, products=[element], type="IfcMaterialProfileSetUsage"
|
||||||
|
)
|
||||||
usage = self.file.by_type("IfcMaterialProfileSetUsage")[0]
|
usage = self.file.by_type("IfcMaterialProfileSetUsage")[0]
|
||||||
assert subject.get_elements_by_material(self.file, material) == {element, element_type}
|
assert subject.get_elements_by_material(self.file, material) == {element, element_type}
|
||||||
assert subject.get_elements_by_material(self.file, material_set) == {element, element_type}
|
assert subject.get_elements_by_material(self.file, material_set) == {element, element_type}
|
||||||
@@ -703,13 +719,33 @@ class TestGetReferencedStructures(test.bootstrap.IFC4):
|
|||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
assert subject.get_referenced_structures(element) == []
|
assert subject.get_referenced_structures(element) == []
|
||||||
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building)
|
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building)
|
||||||
assert subject.get_referenced_structures(element) == [building]
|
assert subject.get_referenced_structures(element) == [building]
|
||||||
building2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
building2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building2)
|
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building2)
|
||||||
assert subject.get_referenced_structures(element) == [building, building2]
|
assert subject.get_referenced_structures(element) == [building, building2]
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetReferencedStructuresIFC2X3(test.bootstrap.IFC2X3, TestGetReferencedStructures):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetStructureReferencedElements(test.bootstrap.IFC4):
|
||||||
|
def test_getting_references_of_an_element(self):
|
||||||
|
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
assert subject.get_structure_referenced_elements(building) == set()
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building)
|
||||||
|
assert subject.get_structure_referenced_elements(building) == {element}
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element2], relating_structure=building)
|
||||||
|
assert subject.get_structure_referenced_elements(building) == {element, element2}
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetStructureReferencedElementsIFC2X3(test.bootstrap.IFC2X3, TestGetStructureReferencedElements):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestGetDecompositionIFC4(test.bootstrap.IFC4):
|
class TestGetDecompositionIFC4(test.bootstrap.IFC4):
|
||||||
def test_getting_decomposed_subelements_of_an_element(self):
|
def test_getting_decomposed_subelements_of_an_element(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly")
|
||||||
@@ -769,6 +805,50 @@ class TestGetNestIFC2X3(test.bootstrap.IFC2X3, TestGetNestIFC4):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetReferencedElements(test.bootstrap.IFC4):
|
||||||
|
# TODO: test other references:
|
||||||
|
# IfcExternallyDefinedHatchStyle
|
||||||
|
# IfcExternallyDefinedSurfaceStyle
|
||||||
|
# IfcExternallyDefinedTextFont
|
||||||
|
def test_get_elements_referenced_by_classification_reference(self):
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||||
|
elements = [ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")]
|
||||||
|
if self.file.schema != "IFC2X3":
|
||||||
|
elements.append(self.file.create_entity("IfcCostValue"))
|
||||||
|
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
|
||||||
|
reference = ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
|
self.file,
|
||||||
|
products=elements,
|
||||||
|
identification="X",
|
||||||
|
name="Foobar",
|
||||||
|
classification=result,
|
||||||
|
)
|
||||||
|
assert subject.get_referenced_elements(reference) == set(elements)
|
||||||
|
|
||||||
|
def test_get_elements_referenced_by_library_reference(self):
|
||||||
|
reference = self.file.createIfcLibraryReference()
|
||||||
|
elements = [
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
|
||||||
|
]
|
||||||
|
ifcopenshell.api.run("library.assign_reference", self.file, reference=reference, products=elements)
|
||||||
|
assert subject.get_referenced_elements(reference) == set(elements)
|
||||||
|
|
||||||
|
def test_get_elements_referenced_by_document_reference(self):
|
||||||
|
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
|
||||||
|
elements = [
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
|
||||||
|
]
|
||||||
|
ifcopenshell.api.run("document.assign_document", self.file, document=reference, products=elements)
|
||||||
|
assert subject.get_referenced_elements(reference) == set(elements)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetReferencedElementsIFC2X3(test.bootstrap.IFC2X3, TestGetReferencedElements):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestReplaceAttributeIFC4(test.bootstrap.IFC4):
|
class TestReplaceAttributeIFC4(test.bootstrap.IFC4):
|
||||||
def test_replacing_an_elements_attribute(self):
|
def test_replacing_an_elements_attribute(self):
|
||||||
element = self.file.createIfcWall("foo")
|
element = self.file.createIfcWall("foo")
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ class TestFilterElements(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
product=element,
|
products=[element],
|
||||||
identification="X",
|
identification="X",
|
||||||
name="Foobar",
|
name="Foobar",
|
||||||
classification=result,
|
classification=result,
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
N.B.! IfcSverchok nodes are WIP. You can experience Blender crashes while using them, especially if you're undoing (ctrl/cmd-Z) something in the node tree.
|
N.B.! IfcSverchok nodes are WIP. You can experience Blender crashes while using them, especially if you're undoing (ctrl/cmd-Z) something in the node tree.
|
||||||
|
|
||||||
## Packaged installation[](https://blenderbim.org/docs-python/ifcsverchok/installation.html#packaged-installation "Permalink to this headline")
|
## Packaged installation[](https://docs.ifcopenshell.org/ifcsverchok/installation.html#packaged-installation "Permalink to this headline")
|
||||||
|
|
||||||
IfcSverchok is packaged like a regular Blender add-on, so installation is the same as any other Blender add-on. [Download IfcSverchok here](https://blenderbim.org/builds/ifcsverchok-230823.zip).
|
IfcSverchok is packaged like a regular Blender add-on, so installation is the same as any other Blender add-on. [Download IfcSverchok here](https://github.com/IfcOpenShell/IfcOpenShell/releases/download/ifcsverchok-240417/ifcsverchok-240417.zip).
|
||||||
|
|
||||||
Like all Blender add-ons, they can be installed using `Edit > Preferences > Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox`. You can enable add-ons permanently by using `Save User Settings` from the Addons menu.
|
Like all Blender add-ons, they can be installed using `Edit > Preferences > Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox`. You can enable add-ons permanently by using `Save User Settings` from the Addons menu.
|
||||||
|
|
||||||
|
|||||||
+29
-30
@@ -32,6 +32,26 @@ import importlib
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("sverchok.ifc")
|
logger = logging.getLogger("sverchok.ifc")
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_addons_are_enabled(*addon_names: str) -> None:
|
||||||
|
errors = []
|
||||||
|
for addon_name in addon_names:
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(addon_name)
|
||||||
|
# `__addon_enabled__` is not present if addon wasn't enabled before
|
||||||
|
if not getattr(module, "__addon_enabled__", False):
|
||||||
|
errors.append(f"- Addon {addon_name} appears to be disabled, it should be enabled before IFC Sverchok.")
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
errors.append(f"- Addon {addon_name} is not installed.")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise Exception("Some issues were found trying to enable IFC Sverchok:\n" + "\n".join(errors))
|
||||||
|
|
||||||
|
|
||||||
|
ensure_addons_are_enabled("blenderbim", "sverchok")
|
||||||
|
|
||||||
|
|
||||||
from sverchok.ui.nodeview_space_menu import add_node_menu
|
from sverchok.ui.nodeview_space_menu import add_node_menu
|
||||||
|
|
||||||
|
|
||||||
@@ -69,36 +89,15 @@ def nodes_index():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
node_categories = [
|
def make_node_categories() -> list[dict[str, list[str]]]:
|
||||||
{
|
node_categories = [{}]
|
||||||
"IFC": [
|
for category, nodes in nodes_index():
|
||||||
"SvIfcCreateFile",
|
nodes = [node_name for idname, node_name in nodes]
|
||||||
"SvIfcReadFile",
|
node_categories[0][category] = nodes
|
||||||
"SvIfcWriteFile",
|
return node_categories
|
||||||
"SvIfcCreateEntity",
|
|
||||||
"SvIfcCreateShape",
|
|
||||||
"SvIfcReadEntity",
|
node_categories = make_node_categories()
|
||||||
"SvIfcPickIfcClass",
|
|
||||||
"SvIfcById",
|
|
||||||
"SvIfcByGuid",
|
|
||||||
"SvIfcByType",
|
|
||||||
"SvIfcByQuery",
|
|
||||||
"SvIfcAdd",
|
|
||||||
"SvIfcAddPset",
|
|
||||||
"SvIfcAddSpatialElement",
|
|
||||||
"SvIfcRemove",
|
|
||||||
"SvIfcGenerateGuid",
|
|
||||||
"SvIfcGetProperty",
|
|
||||||
"SvIfcGetAttribute",
|
|
||||||
"SvIfcSelectBlenderObjects",
|
|
||||||
"SvIfcApi",
|
|
||||||
"SvIfcBMeshToIfcRepr",
|
|
||||||
"SvIfcSverchokToIfcRepr",
|
|
||||||
"SvIfcCreateProject",
|
|
||||||
"SvIfcQuickProjectSetup",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def make_node_list():
|
def make_node_list():
|
||||||
|
|||||||
@@ -4,6 +4,23 @@ With **IfcTester**, you can author and read **Information Delivery Specification
|
|||||||
|
|
||||||
## How to use it
|
## How to use it
|
||||||
|
|
||||||
|
### Command line use
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# run console reporter
|
||||||
|
python -m ifctester example.ids example.ifc
|
||||||
|
python -m ifctester example.ids example.ifc -r Html -o report.html
|
||||||
|
|
||||||
|
Available flags:
|
||||||
|
|
||||||
|
- ``-r`` / ``--reporter``: The reporting method to view audit results. Availabe reporters: Console, Txt, Json, Html, Ods, Bcf
|
||||||
|
- ``--no-color``: Disable colour output (supported by Console reporting).
|
||||||
|
- ``--excel-safe``: Make sure exported ODS is safely exported for Excel.
|
||||||
|
- ``-o`` / ``--output``: Output file (supported for all types of reporting except Console).
|
||||||
|
|
||||||
|
### Code example
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from ifctester import ids, reporter
|
from ifctester import ids, reporter
|
||||||
|
|||||||
@@ -31,10 +31,13 @@ parser.add_argument(
|
|||||||
"-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console"
|
"-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-color", help="Disable colour output supported by Console reporting", action="store_true"
|
"--no-color", help="Disable colour output (supported by Console reporting)", action="store_true"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-o", "--output", help="Output file supported by Json reporting"
|
"--excel-safe", help="Make sure exported ODS is safely exported for Excel", action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o", "--output", help="Output file (supported for all types of reporting except Console)"
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -56,7 +59,7 @@ elif args.reporter == "Json":
|
|||||||
elif args.reporter == "Html":
|
elif args.reporter == "Html":
|
||||||
engine = reporter.Html(specs)
|
engine = reporter.Html(specs)
|
||||||
elif args.reporter == "Ods":
|
elif args.reporter == "Ods":
|
||||||
engine = reporter.Ods(specs)
|
engine = reporter.Ods(specs, excel_safe=args.excel_safe)
|
||||||
elif args.reporter == "Bcf":
|
elif args.reporter == "Bcf":
|
||||||
engine = reporter.Bcf(specs)
|
engine = reporter.Bcf(specs)
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from xmlschema.validators import identities
|
from xmlschema.validators import identities
|
||||||
from typing import Union, Optional, Any, Literal, TYPE_CHECKING
|
from typing import Union, Optional, Any, Literal, TYPE_CHECKING, TypedDict
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -61,12 +61,17 @@ def get_psets(element):
|
|||||||
Cardinality = Literal["required", "optional", "prohibited"]
|
Cardinality = Literal["required", "optional", "prohibited"]
|
||||||
|
|
||||||
|
|
||||||
|
class FacetFailure(TypedDict):
|
||||||
|
element: ifcopenshell.entity_instance
|
||||||
|
reason: str
|
||||||
|
|
||||||
|
|
||||||
class Facet:
|
class Facet:
|
||||||
cardinality: Cardinality
|
cardinality: Cardinality
|
||||||
|
|
||||||
def __init__(self, *parameters):
|
def __init__(self, *parameters):
|
||||||
self.status = None
|
self.status = None
|
||||||
self.failures = []
|
self.failures: list[FacetFailure] = []
|
||||||
for i, name in enumerate(self.parameters):
|
for i, name in enumerate(self.parameters):
|
||||||
setattr(self, name.replace("@", ""), parameters[i])
|
setattr(self, name.replace("@", ""), parameters[i])
|
||||||
|
|
||||||
@@ -105,7 +110,7 @@ class Facet:
|
|||||||
clause_type: str,
|
clause_type: str,
|
||||||
specification: Optional[Specification] = None,
|
specification: Optional[Specification] = None,
|
||||||
requirement: Optional[Facet] = None,
|
requirement: Optional[Facet] = None,
|
||||||
):
|
) -> str:
|
||||||
if clause_type == "applicability":
|
if clause_type == "applicability":
|
||||||
templates = self.applicability_templates
|
templates = self.applicability_templates
|
||||||
elif clause_type == "requirement":
|
elif clause_type == "requirement":
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -34,14 +35,19 @@ from .facet import (
|
|||||||
get_pset,
|
get_pset,
|
||||||
get_psets,
|
get_psets,
|
||||||
Cardinality,
|
Cardinality,
|
||||||
|
FacetFailure,
|
||||||
)
|
)
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union, overload, Literal
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
schema = None
|
schema = None
|
||||||
|
|
||||||
|
|
||||||
def open(filepath, validate=False):
|
@overload
|
||||||
|
def open(filepath: str, validate: Literal[False] = False) -> Ids: ...
|
||||||
|
@overload
|
||||||
|
def open(filepath: str, validate: Literal[True]) -> None: ...
|
||||||
|
def open(filepath: str, validate=False) -> Union[Ids, None]:
|
||||||
if validate:
|
if validate:
|
||||||
get_schema().validate(filepath)
|
get_schema().validate(filepath)
|
||||||
return Ids().parse(
|
return Ids().parse(
|
||||||
@@ -265,11 +271,11 @@ class Specification:
|
|||||||
if self.maxOccurs != 0: # This is a required or optional specification
|
if self.maxOccurs != 0: # This is a required or optional specification
|
||||||
if not is_pass:
|
if not is_pass:
|
||||||
self.failed_entities.add(element)
|
self.failed_entities.add(element)
|
||||||
facet.failures.append({"element": element, "reason": str(result)})
|
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||||
else: # This is a prohibited specification
|
else: # This is a prohibited specification
|
||||||
if is_pass:
|
if is_pass:
|
||||||
self.failed_entities.add(element)
|
self.failed_entities.add(element)
|
||||||
facet.failures.append({"element": element, "reason": str(result)})
|
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||||
|
|
||||||
self.status = True
|
self.status = True
|
||||||
for facet in self.requirements:
|
for facet in self.requirements:
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
@@ -24,12 +26,15 @@ import datetime
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from .ids import Specification, Ids
|
||||||
|
from .facet import Facet, FacetFailure
|
||||||
|
from typing import TypedDict, Union, Literal, Optional
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class Reporter:
|
class Reporter:
|
||||||
def __init__(self, ids):
|
def __init__(self, ids: Ids):
|
||||||
self.ids = ids
|
self.ids = ids
|
||||||
|
|
||||||
def report(self, ids):
|
def report(self, ids):
|
||||||
@@ -42,8 +47,79 @@ class Reporter:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
ResultsPercent = Union[int, Literal["N/A"]]
|
||||||
|
|
||||||
|
|
||||||
|
class Results(TypedDict):
|
||||||
|
title: str
|
||||||
|
date: str
|
||||||
|
filepath: str
|
||||||
|
filename: str
|
||||||
|
specifications: list[ResultsSpecification]
|
||||||
|
status: bool
|
||||||
|
total_specifications: int
|
||||||
|
total_specifications: int
|
||||||
|
total_specifications_pass: int
|
||||||
|
total_specifications_fail: int
|
||||||
|
percent_specifications_pass: ResultsPercent
|
||||||
|
total_requirements: int
|
||||||
|
total_requirements_pass: int
|
||||||
|
total_requirements_fail: int
|
||||||
|
percent_requirements_pass: ResultsPercent
|
||||||
|
total_checks: int
|
||||||
|
total_checks_pass: int
|
||||||
|
total_checks_fail: int
|
||||||
|
percent_checks_pass: ResultsPercent
|
||||||
|
|
||||||
|
|
||||||
|
class ResultsSpecification(TypedDict):
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
instructions: str
|
||||||
|
status: bool
|
||||||
|
total_applicable: int
|
||||||
|
total_applicable_pass: int
|
||||||
|
total_applicable_fail: int
|
||||||
|
percent_applicable_pass: ResultsPercent
|
||||||
|
total_checks: int
|
||||||
|
total_checks_pass: int
|
||||||
|
total_checks_fail: int
|
||||||
|
percent_checks_pass: ResultsPercent
|
||||||
|
required: bool
|
||||||
|
applicability: list[str]
|
||||||
|
requirements: list[ResultsRequirement]
|
||||||
|
|
||||||
|
|
||||||
|
class ResultsRequirement(TypedDict):
|
||||||
|
description: str
|
||||||
|
status: bool
|
||||||
|
failed_entities: list[ResultsFailedEntity]
|
||||||
|
total_applicable: int
|
||||||
|
total_pass: int
|
||||||
|
total_fail: int
|
||||||
|
percent_pass: ResultsPercent
|
||||||
|
|
||||||
|
|
||||||
|
# use different syntax because of the "class" key
|
||||||
|
ResultsFailedEntity = TypedDict(
|
||||||
|
"ResultsFailedEntity",
|
||||||
|
{
|
||||||
|
"reason": str,
|
||||||
|
"element": str,
|
||||||
|
"element_type": str,
|
||||||
|
"class": str,
|
||||||
|
"predefined_type": str,
|
||||||
|
"name": Union[str, None],
|
||||||
|
"description": Union[str, None],
|
||||||
|
"id": int,
|
||||||
|
"global_id": Union[str, None],
|
||||||
|
"tag": Union[str, None],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Console(Reporter):
|
class Console(Reporter):
|
||||||
def __init__(self, ids, use_colour=True):
|
def __init__(self, ids: Ids, use_colour=True):
|
||||||
super().__init__(ids)
|
super().__init__(ids)
|
||||||
self.use_colour = use_colour
|
self.use_colour = use_colour
|
||||||
self.colours = {
|
self.colours = {
|
||||||
@@ -59,14 +135,14 @@ class Console(Reporter):
|
|||||||
"reverse": "\033[;7m",
|
"reverse": "\033[;7m",
|
||||||
}
|
}
|
||||||
|
|
||||||
def report(self):
|
def report(self) -> None:
|
||||||
self.set_style("bold", "blue")
|
self.set_style("bold", "blue")
|
||||||
self.print(self.ids.info.get("title", "Untitled IDS"))
|
self.print(self.ids.info.get("title", "Untitled IDS"))
|
||||||
for specification in self.ids.specifications:
|
for specification in self.ids.specifications:
|
||||||
self.report_specification(specification)
|
self.report_specification(specification)
|
||||||
self.set_style("reset")
|
self.set_style("reset")
|
||||||
|
|
||||||
def report_specification(self, specification):
|
def report_specification(self, specification: Specification) -> None:
|
||||||
if specification.status is True:
|
if specification.status is True:
|
||||||
self.set_style("bold", "green")
|
self.set_style("bold", "green")
|
||||||
self.print("[PASS] ", end="")
|
self.print("[PASS] ", end="")
|
||||||
@@ -113,7 +189,7 @@ class Console(Reporter):
|
|||||||
self.print(" " * 12 + f"... {len(requirement.failures)} in total ...")
|
self.print(" " * 12 + f"... {len(requirement.failures)} in total ...")
|
||||||
self.set_style("reset")
|
self.set_style("reset")
|
||||||
|
|
||||||
def report_reason(self, failure):
|
def report_reason(self, failure: FacetFailure) -> None:
|
||||||
is_bold = False
|
is_bold = False
|
||||||
for substring in failure["reason"].split('"'):
|
for substring in failure["reason"].split('"'):
|
||||||
if is_bold:
|
if is_bold:
|
||||||
@@ -126,11 +202,11 @@ class Console(Reporter):
|
|||||||
self.print(" - " + str(failure["element"]))
|
self.print(" - " + str(failure["element"]))
|
||||||
self.set_style("reset")
|
self.set_style("reset")
|
||||||
|
|
||||||
def set_style(self, *colours):
|
def set_style(self, *colours: str):
|
||||||
if self.use_colour:
|
if self.use_colour:
|
||||||
sys.stdout.write("".join([self.colours[c] for c in colours]))
|
sys.stdout.write("".join([self.colours[c] for c in colours]))
|
||||||
|
|
||||||
def print(self, txt, end=None):
|
def print(self, txt: str, end: Optional[str] = None):
|
||||||
if end is not None:
|
if end is not None:
|
||||||
print(txt, end=end)
|
print(txt, end=end)
|
||||||
else:
|
else:
|
||||||
@@ -138,14 +214,14 @@ class Console(Reporter):
|
|||||||
|
|
||||||
|
|
||||||
class Txt(Console):
|
class Txt(Console):
|
||||||
def __init__(self, ids):
|
def __init__(self, ids: Ids):
|
||||||
super().__init__(ids, use_colour=False)
|
super().__init__(ids, use_colour=False)
|
||||||
self.text = ""
|
self.text = ""
|
||||||
|
|
||||||
def print(self, txt, end=None):
|
def print(self, txt: str, end: Optional[str] = None):
|
||||||
self.text += txt + "\n" if end is None else txt
|
self.text += txt + "\n" if end is None else end
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self) -> None:
|
||||||
print(self.text)
|
print(self.text)
|
||||||
|
|
||||||
def to_file(self, filepath: str) -> None:
|
def to_file(self, filepath: str) -> None:
|
||||||
@@ -154,11 +230,11 @@ class Txt(Console):
|
|||||||
|
|
||||||
|
|
||||||
class Json(Reporter):
|
class Json(Reporter):
|
||||||
def __init__(self, ids):
|
def __init__(self, ids: Ids):
|
||||||
super().__init__(ids)
|
super().__init__(ids)
|
||||||
self.results = {}
|
self.results = Results()
|
||||||
|
|
||||||
def report(self):
|
def report(self) -> Results:
|
||||||
self.results["title"] = self.ids.info.get("title", "Untitled IDS")
|
self.results["title"] = self.ids.info.get("title", "Untitled IDS")
|
||||||
self.results["date"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
self.results["date"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
self.results["filepath"] = self.ids.filepath
|
self.results["filepath"] = self.ids.filepath
|
||||||
@@ -203,7 +279,7 @@ class Json(Reporter):
|
|||||||
)
|
)
|
||||||
return self.results
|
return self.results
|
||||||
|
|
||||||
def report_specification(self, specification):
|
def report_specification(self, specification: Specification) -> ResultsSpecification:
|
||||||
applicability = [a.to_string("applicability") for a in specification.applicability]
|
applicability = [a.to_string("applicability") for a in specification.applicability]
|
||||||
total_applicable = len(specification.applicable_entities)
|
total_applicable = len(specification.applicable_entities)
|
||||||
total_checks = 0
|
total_checks = 0
|
||||||
@@ -216,57 +292,60 @@ class Json(Reporter):
|
|||||||
total_checks += total_applicable
|
total_checks += total_applicable
|
||||||
total_checks_pass += total_pass
|
total_checks_pass += total_pass
|
||||||
requirements.append(
|
requirements.append(
|
||||||
{
|
ResultsRequirement(
|
||||||
"description": requirement.to_string("requirement", specification, requirement),
|
description=requirement.to_string("requirement", specification, requirement),
|
||||||
"status": requirement.status,
|
status=requirement.status,
|
||||||
"failed_entities": self.report_failed_entities(requirement),
|
failed_entities=self.report_failed_entities(requirement),
|
||||||
"total_applicable": total_applicable,
|
total_applicable=total_applicable,
|
||||||
"total_pass": total_pass,
|
total_pass=total_pass,
|
||||||
"total_fail": total_fail,
|
total_fail=total_fail,
|
||||||
"percent_pass": percent_pass,
|
percent_pass=percent_pass,
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
total_applicable_pass = total_applicable - len(specification.failed_entities)
|
total_applicable_pass = total_applicable - len(specification.failed_entities)
|
||||||
percent_applicable_pass = (
|
percent_applicable_pass = (
|
||||||
math.floor((total_applicable_pass / total_applicable) * 100) if total_applicable else "N/A"
|
math.floor((total_applicable_pass / total_applicable) * 100) if total_applicable else "N/A"
|
||||||
)
|
)
|
||||||
percent_checks_pass = math.floor((total_checks_pass / total_checks) * 100) if total_checks else "N/A"
|
percent_checks_pass = math.floor((total_checks_pass / total_checks) * 100) if total_checks else "N/A"
|
||||||
return {
|
|
||||||
"name": specification.name,
|
|
||||||
"description": specification.description,
|
|
||||||
"instructions": specification.instructions,
|
|
||||||
"status": specification.status,
|
|
||||||
"total_applicable": total_applicable,
|
|
||||||
"total_applicable_pass": total_applicable_pass,
|
|
||||||
"total_applicable_fail": total_applicable - total_applicable_pass,
|
|
||||||
"percent_applicable_pass": percent_applicable_pass,
|
|
||||||
"total_checks": total_checks,
|
|
||||||
"total_checks_pass": total_checks_pass,
|
|
||||||
"total_checks_fail": total_checks - total_checks_pass,
|
|
||||||
"percent_checks_pass": percent_checks_pass,
|
|
||||||
"required": specification.minOccurs != 0,
|
|
||||||
"applicability": applicability,
|
|
||||||
"requirements": requirements,
|
|
||||||
}
|
|
||||||
|
|
||||||
def report_failed_entities(self, requirement):
|
return ResultsSpecification(
|
||||||
|
name=specification.name,
|
||||||
|
description=specification.description,
|
||||||
|
instructions=specification.instructions,
|
||||||
|
status=specification.status,
|
||||||
|
total_applicable=total_applicable,
|
||||||
|
total_applicable_pass=total_applicable_pass,
|
||||||
|
total_applicable_fail=total_applicable - total_applicable_pass,
|
||||||
|
percent_applicable_pass=percent_applicable_pass,
|
||||||
|
total_checks=total_checks,
|
||||||
|
total_checks_pass=total_checks_pass,
|
||||||
|
total_checks_fail=total_checks - total_checks_pass,
|
||||||
|
percent_checks_pass=percent_checks_pass,
|
||||||
|
required=specification.minOccurs != 0,
|
||||||
|
applicability=applicability,
|
||||||
|
requirements=requirements,
|
||||||
|
)
|
||||||
|
|
||||||
|
def report_failed_entities(self, requirement: Facet) -> list[ResultsFailedEntity]:
|
||||||
return [
|
return [
|
||||||
{
|
ResultsFailedEntity(
|
||||||
"reason": f["reason"],
|
{
|
||||||
"element": str(f["element"]),
|
"reason": f["reason"],
|
||||||
"element_type": str(ifcopenshell.util.element.get_type(f["element"])),
|
"element": str(f["element"]),
|
||||||
"class": f["element"].is_a(),
|
"element_type": str(ifcopenshell.util.element.get_type(f["element"])),
|
||||||
"predefined_type": ifcopenshell.util.element.get_predefined_type(f["element"]),
|
"class": f["element"].is_a(),
|
||||||
"name": getattr(f["element"], "Name", None),
|
"predefined_type": ifcopenshell.util.element.get_predefined_type(f["element"]),
|
||||||
"description": getattr(f["element"], "Description", None),
|
"name": getattr(f["element"], "Name", None),
|
||||||
"id": f["element"].id(),
|
"description": getattr(f["element"], "Description", None),
|
||||||
"global_id": getattr(f["element"], "GlobalId", None),
|
"id": f["element"].id(),
|
||||||
"tag": getattr(f["element"], "Tag", None),
|
"global_id": getattr(f["element"], "GlobalId", None),
|
||||||
}
|
"tag": getattr(f["element"], "Tag", None),
|
||||||
|
}
|
||||||
|
)
|
||||||
for f in requirement.failures
|
for f in requirement.failures
|
||||||
]
|
]
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self) -> str:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
return json.dumps(self.results)
|
return json.dumps(self.results)
|
||||||
@@ -279,11 +358,10 @@ class Json(Reporter):
|
|||||||
|
|
||||||
|
|
||||||
class Html(Json):
|
class Html(Json):
|
||||||
def __init__(self, ids):
|
def __init__(self, ids: Ids):
|
||||||
super().__init__(ids)
|
super().__init__(ids)
|
||||||
self.results = {}
|
|
||||||
|
|
||||||
def report(self):
|
def report(self) -> None:
|
||||||
super().report()
|
super().report()
|
||||||
entity_limit = 100
|
entity_limit = 100
|
||||||
for spec in self.results["specifications"]:
|
for spec in self.results["specifications"]:
|
||||||
@@ -294,7 +372,7 @@ class Html(Json):
|
|||||||
requirement["total_entities"] = total
|
requirement["total_entities"] = total
|
||||||
requirement["total_omitted"] = total - entity_limit
|
requirement["total_omitted"] = total - entity_limit
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self) -> str:
|
||||||
import pystache
|
import pystache
|
||||||
|
|
||||||
with open(os.path.join(cwd, "templates", "report.html"), "r") as file:
|
with open(os.path.join(cwd, "templates", "report.html"), "r") as file:
|
||||||
@@ -309,15 +387,42 @@ class Html(Json):
|
|||||||
|
|
||||||
|
|
||||||
class Ods(Json):
|
class Ods(Json):
|
||||||
def __init__(self, ids):
|
def __init__(self, ids: Ids, excel_safe=False):
|
||||||
super().__init__(ids)
|
super().__init__(ids)
|
||||||
|
self.excel_safe = excel_safe
|
||||||
self.colours = {
|
self.colours = {
|
||||||
"h": "cccccc", # Header
|
"h": "cccccc", # Header
|
||||||
"p": "97cc64", # Pass
|
"p": "97cc64", # Pass
|
||||||
"f": "fb5a3e", # Fail
|
"f": "fb5a3e", # Fail
|
||||||
"t": "ffffff", # Regular text
|
"t": "ffffff", # Regular text
|
||||||
}
|
}
|
||||||
self.results = {}
|
|
||||||
|
def excel_safe_spreadsheet_name(self, name: str) -> str:
|
||||||
|
if not self.excel_safe:
|
||||||
|
return name
|
||||||
|
|
||||||
|
warning = (
|
||||||
|
f'WARNING. Sheet name "{name}" is not valid for Excel and will be changed. '
|
||||||
|
"See: https://support.microsoft.com/en-us/office/rename-a-worksheet-3f1f7148-ee83-404d-8ef0-9ff99fbad1f9"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not name or name == "History":
|
||||||
|
print(warning)
|
||||||
|
return "placeholder spreadsheet name"
|
||||||
|
|
||||||
|
if name.startswith("'") or name.endswith("'"):
|
||||||
|
print(warning)
|
||||||
|
name = name.strip("'")
|
||||||
|
|
||||||
|
pattern = r"[\\\/\?\*\:\[\]]"
|
||||||
|
if re.search(pattern, name):
|
||||||
|
name = re.sub(pattern, "", name)
|
||||||
|
print(warning)
|
||||||
|
|
||||||
|
if len(name) > 31:
|
||||||
|
name = name[:31]
|
||||||
|
print(warning)
|
||||||
|
return name
|
||||||
|
|
||||||
def to_file(self, filepath: str) -> None:
|
def to_file(self, filepath: str) -> None:
|
||||||
from odf.opendocument import OpenDocumentSpreadsheet
|
from odf.opendocument import OpenDocumentSpreadsheet
|
||||||
@@ -334,7 +439,7 @@ class Ods(Json):
|
|||||||
self.doc.automaticstyles.addElement(style)
|
self.doc.automaticstyles.addElement(style)
|
||||||
self.cell_formats[key] = style
|
self.cell_formats[key] = style
|
||||||
|
|
||||||
table = Table(name=self.results["title"])
|
table = Table(name=self.excel_safe_spreadsheet_name(self.results["title"]))
|
||||||
tr = TableRow()
|
tr = TableRow()
|
||||||
for header in ["Specification", "Status", "Total Pass", "Total Checks", "Percentage Pass"]:
|
for header in ["Specification", "Status", "Total Pass", "Total Checks", "Percentage Pass"]:
|
||||||
tc = TableCell(valuetype="string", stylename="h")
|
tc = TableCell(valuetype="string", stylename="h")
|
||||||
@@ -371,7 +476,7 @@ class Ods(Json):
|
|||||||
for specification in self.results["specifications"]:
|
for specification in self.results["specifications"]:
|
||||||
if specification["status"]:
|
if specification["status"]:
|
||||||
continue
|
continue
|
||||||
table = Table(name=specification["name"])
|
table = Table(name=self.excel_safe_spreadsheet_name(specification["name"]))
|
||||||
tr = TableRow()
|
tr = TableRow()
|
||||||
for header in [
|
for header in [
|
||||||
"Requirement",
|
"Requirement",
|
||||||
@@ -419,12 +524,12 @@ class Ods(Json):
|
|||||||
table.addElement(tr)
|
table.addElement(tr)
|
||||||
self.doc.spreadsheet.addElement(table)
|
self.doc.spreadsheet.addElement(table)
|
||||||
|
|
||||||
self.doc.save(filepath, addsuffix=filepath.lower().endswith(".ods"))
|
self.doc.save(filepath, addsuffix=not filepath.lower().endswith(".ods"))
|
||||||
|
|
||||||
|
|
||||||
class Bcf(Json):
|
class Bcf(Json):
|
||||||
def report_failed_entities(self, requirement):
|
def report_failed_entities(self, requirement: Facet) -> list[FacetFailure]:
|
||||||
return [{"reason": f["reason"], "element": f["element"]} for f in requirement.failures]
|
return [FacetFailure(f) for f in requirement.failures]
|
||||||
|
|
||||||
def to_file(self, filepath: str) -> None:
|
def to_file(self, filepath: str) -> None:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|||||||
@@ -706,24 +706,24 @@ class TestClassification:
|
|||||||
element0 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
element0 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSlab")
|
element1 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSlab")
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=element1, reference=ref1, classification=system_a
|
"classification.add_reference", ifc, products=[element1], reference=ref1, classification=system_a
|
||||||
)
|
)
|
||||||
element11 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcColumn")
|
element11 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcColumn")
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=element11, reference=ref11, classification=system_a
|
"classification.add_reference", ifc, products=[element11], reference=ref11, classification=system_a
|
||||||
)
|
)
|
||||||
element22 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBeam")
|
element22 = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBeam")
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference",
|
"classification.add_reference",
|
||||||
ifc,
|
ifc,
|
||||||
product=element22,
|
products=[element22],
|
||||||
reference=ref22,
|
reference=ref22,
|
||||||
classification=system_a,
|
classification=system_a,
|
||||||
is_lightweight=False,
|
is_lightweight=False,
|
||||||
)
|
)
|
||||||
material = ifc.createIfcMaterial(Name="Material")
|
material = ifc.createIfcMaterial(Name="Material")
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=material, reference=ref1, classification=system_a
|
"classification.add_reference", ifc, products=[material], reference=ref1, classification=system_a
|
||||||
)
|
)
|
||||||
|
|
||||||
facet = Classification(system="Foobar")
|
facet = Classification(system="Foobar")
|
||||||
@@ -810,15 +810,15 @@ class TestClassification:
|
|||||||
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[wall], relating_type=wall_type)
|
ifcopenshell.api.run("type.assign_type", ifc, related_objects=[wall], relating_type=wall_type)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=wall, reference=ref11, classification=system_a
|
"classification.add_reference", ifc, products=[wall], reference=ref11, classification=system_a
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=wall_type, reference=ref22, classification=system_a
|
"classification.add_reference", ifc, products=[wall_type], reference=ref22, classification=system_a
|
||||||
)
|
)
|
||||||
|
|
||||||
system_b = ifcopenshell.api.run("classification.add_classification", ifc, classification=system_b)
|
system_b = ifcopenshell.api.run("classification.add_classification", ifc, classification=system_b)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"classification.add_reference", ifc, product=wall_type, reference=refx, classification=system_b
|
"classification.add_reference", ifc, products=[wall_type], reference=refx, classification=system_b
|
||||||
)
|
)
|
||||||
|
|
||||||
facet = Classification(system="Foobar", value="11")
|
facet = Classification(system="Foobar", value="11")
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ https://technical.buildingsmart.org/standards/ifc/ifc-schema-specifications/
|
|||||||
https://www.sciencedirect.com/science/article/pii/S0926580523000389
|
https://www.sciencedirect.com/science/article/pii/S0926580523000389
|
||||||
|
|
||||||
- IFCOpenShell documentation
|
- IFCOpenShell documentation
|
||||||
https://blenderbim.org/docs-python/
|
https://docs.ifcopenshell.org/
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user