mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 16:43:00 +00:00
Merge branch 'v0.7.0' into new_covering_tools
This commit is contained in:
+2
-10
@@ -201,8 +201,8 @@ endif
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifctester/ifctester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCFM functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCOBie functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
||||
# Provides bSDD functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/bsdd/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCDiff functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCSV functionality
|
||||
@@ -594,14 +594,6 @@ endif
|
||||
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCOBie for XLSX support
|
||||
# TODO: see if we can replace this with openpyxl which does both read/write
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz
|
||||
cd dist/working && tar -xzvf XlsxWriter*
|
||||
cd dist/working/XlsxWriter-1.2.9/ && cp -r xlsxwriter ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by augin
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
|
||||
|
||||
@@ -30,6 +30,7 @@ modules = {
|
||||
"project": None,
|
||||
"search": None,
|
||||
"bcf": None,
|
||||
"bsdd": None,
|
||||
"root": None,
|
||||
"unit": None,
|
||||
"model": None,
|
||||
@@ -45,7 +46,6 @@ modules = {
|
||||
"aggregate": None,
|
||||
"geometry": None,
|
||||
"fm": None,
|
||||
"cobie": None,
|
||||
"resource": None,
|
||||
"cost": None,
|
||||
"sequence": None,
|
||||
|
||||
@@ -325,7 +325,7 @@ def draw_filter(layout, props, data, module):
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER")
|
||||
elif ifc_filter.type == "location":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="PACKAGE")
|
||||
row.prop(ifc_filter, "value", text="", icon="PACKAGE")
|
||||
elif ifc_filter.type == "query":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
|
||||
|
||||
@@ -20,6 +20,7 @@ import os
|
||||
import re
|
||||
import bpy
|
||||
import time
|
||||
import json
|
||||
import bmesh
|
||||
import logging
|
||||
import mathutils
|
||||
@@ -145,12 +146,15 @@ class MaterialCreator:
|
||||
faces_remap = None
|
||||
texture_map = None
|
||||
if coordinates.is_a("IfcIndexedPolygonalTextureMap"):
|
||||
faces_remap = [[coordinates_remap[i-1] for i in tex_coord_index.TexCoordsOf.CoordIndex]
|
||||
for tex_coord_index in coordinates.TexCoordIndices]
|
||||
faces_remap = [
|
||||
[coordinates_remap[i - 1] for i in tex_coord_index.TexCoordsOf.CoordIndex]
|
||||
for tex_coord_index in coordinates.TexCoordIndices
|
||||
]
|
||||
texture_map = [tex_coord_index.TexCoordIndex for tex_coord_index in coordinates.TexCoordIndices]
|
||||
elif coordinates.is_a("IfcIndexedTriangleTextureMap"):
|
||||
faces_remap = [[coordinates_remap[i-1] for i in triangle_face]
|
||||
for triangle_face in coordinates.MappedTo.CoordIndex]
|
||||
faces_remap = [
|
||||
[coordinates_remap[i - 1] for i in triangle_face] for triangle_face in coordinates.MappedTo.CoordIndex
|
||||
]
|
||||
texture_map = coordinates.TexCoordIndex
|
||||
|
||||
# apply uv to each face
|
||||
@@ -165,7 +169,7 @@ class MaterialCreator:
|
||||
)
|
||||
# apply uv to each loop
|
||||
for loop, i in zip(bface.loops, texCoordIndex):
|
||||
loop[uv_layer].uv = coordinates.TexCoords.TexCoordsList[i-1]
|
||||
loop[uv_layer].uv = coordinates.TexCoords.TexCoordsList[i - 1]
|
||||
|
||||
# Finish up, write the bmesh back to the mesh
|
||||
bm.to_mesh(self.mesh)
|
||||
@@ -262,6 +266,8 @@ class IfcImporter:
|
||||
self.profile_code("Calculate unit scale")
|
||||
self.calculate_model_offset()
|
||||
self.profile_code("Calculate model offset")
|
||||
self.predict_dense_mesh()
|
||||
self.profile_code("Predict dense mesh")
|
||||
self.set_units()
|
||||
self.profile_code("Set units")
|
||||
self.create_project()
|
||||
@@ -306,6 +312,7 @@ class IfcImporter:
|
||||
self.set_default_context()
|
||||
self.profile_code("Setting default context")
|
||||
self.setup_viewport_camera()
|
||||
self.setup_arrays()
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
@@ -346,6 +353,7 @@ class IfcImporter:
|
||||
)
|
||||
if self.body_contexts:
|
||||
self.settings.set_context_ids(self.body_contexts)
|
||||
self.settings_body_2d.set_context_ids(self.body_contexts)
|
||||
# Annotation ContextType is to accommodate broken Revit files
|
||||
# See https://github.com/Autodesk/revit-ifc/issues/187
|
||||
self.plan_contexts = [
|
||||
@@ -496,6 +504,26 @@ class IfcImporter:
|
||||
products.extend(self.get_products_from_shape_representation(inverse_element))
|
||||
return products
|
||||
|
||||
def predict_dense_mesh(self):
|
||||
threshold = 10000 # Just from experience.
|
||||
|
||||
faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell")]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
return
|
||||
|
||||
if self.file.schema == "IFC2X3":
|
||||
return
|
||||
|
||||
faces = [len(e.Faces) for e in self.file.by_type("IfcPolygonalFaceSet")]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
return
|
||||
|
||||
faces = [len(e.CoordIndex) for e in self.file.by_type("IfcTriangulatedFaceSet")]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
|
||||
def calculate_model_offset(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
@@ -1908,6 +1936,15 @@ class IfcImporter:
|
||||
bpy.ops.view3d.view_selected()
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
def setup_arrays(self):
|
||||
for element in self.file.by_type("IfcElement"):
|
||||
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not pset_data or not pset_data.get("Data", None): # skip array children
|
||||
continue
|
||||
for i in range(len(json.loads(pset_data["Data"]))):
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, i, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
|
||||
class IfcImportSettings:
|
||||
def __init__(self):
|
||||
|
||||
@@ -202,6 +202,7 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
|
||||
aggregate_obj = tool.Ifc.get_object(aggregate)
|
||||
if aggregate_obj in context.selectable_objects:
|
||||
aggregate_obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = aggregate_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
+14
-8
@@ -1,5 +1,5 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
@@ -20,17 +20,23 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.SelectCobieIfcFile,
|
||||
operator.SelectCobieJsonFile,
|
||||
operator.ExecuteIfcCobie,
|
||||
prop.COBieProperties,
|
||||
ui.BIM_PT_cobie,
|
||||
operator.GetBSDDClassificationProperties,
|
||||
operator.LoadBSDDDomains,
|
||||
operator.SearchBSDDClassifications,
|
||||
operator.SetActiveBSDDDomain,
|
||||
prop.BSDDDomain,
|
||||
prop.BSDDClassification,
|
||||
prop.BSDDPset,
|
||||
prop.BIMBSDDProperties,
|
||||
ui.BIM_UL_bsdd_domains,
|
||||
ui.BIM_UL_bsdd_classifications,
|
||||
ui.BIM_PT_bsdd,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.COBieProperties = bpy.props.PointerProperty(type=prop.COBieProperties)
|
||||
bpy.types.Scene.BIMBSDDProperties = bpy.props.PointerProperty(type=prop.BIMBSDDProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.COBieProperties
|
||||
del bpy.types.Scene.BIMBSDDProperties
|
||||
@@ -0,0 +1,132 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import bsdd
|
||||
import json
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class LoadBSDDDomains(bpy.types.Operator):
|
||||
bl_idname = "bim.load_bsdd_domains"
|
||||
bl_label = "Load bSDD Domains"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
props.domains.clear()
|
||||
client = bsdd.Client()
|
||||
for domain in sorted(client.Domain(), key=lambda x: x["name"]):
|
||||
new = props.domains.add()
|
||||
new.name = domain["name"]
|
||||
new.namespace_uri = domain["namespaceUri"]
|
||||
new.default_language_code = domain["defaultLanguageCode"]
|
||||
new.organization_name_owner = domain["organizationNameOwner"]
|
||||
new.status = domain["status"]
|
||||
new.version = domain["version"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetActiveBSDDDomain(bpy.types.Operator):
|
||||
bl_idname = "bim.set_active_bsdd_domain"
|
||||
bl_label = "Load bSDD Domains"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
uri: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
props.active_domain = self.name
|
||||
props.active_uri = self.uri
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SearchBSDDClassifications(bpy.types.Operator):
|
||||
bl_idname = "bim.search_bsdd_classifications"
|
||||
bl_label = "Search bSDD Classifications"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
props.classifications.clear()
|
||||
client = bsdd.Client()
|
||||
related_ifc_entities = []
|
||||
if len(props.keyword) < 3:
|
||||
return {"FINISHED"}
|
||||
if props.should_filter_ifc_class and context.active_object:
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
if element:
|
||||
related_ifc_entities = [element.is_a()]
|
||||
results = client.ClassificationSearchOpen(props.keyword, DomainNamespaceUris=[props.active_uri], RelatedIfcEntities=related_ifc_entities)
|
||||
for result in sorted(results["classifications"], key=lambda x: x["referenceCode"]):
|
||||
new = props.classifications.add()
|
||||
new.name = result["name"]
|
||||
new.reference_code = result["referenceCode"]
|
||||
new.description = result.get("description", "")
|
||||
new.namespace_uri = result["namespaceUri"]
|
||||
new.domain_name = result["domainName"]
|
||||
new.domain_namespace_uri = result["domainNamespaceUri"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetBSDDClassificationProperties(bpy.types.Operator):
|
||||
bl_idname = "bim.get_bsdd_classification_properties"
|
||||
bl_label = "Search bSDD Classifications"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
bprops = context.scene.BIMBSDDProperties
|
||||
bprops.classification_psets.clear()
|
||||
bsdd_classification = bprops.classifications[bprops.active_classification_index]
|
||||
client = bsdd.Client()
|
||||
data = client.Classification(bsdd_classification.namespace_uri)
|
||||
|
||||
properties = data.get("classificationProperties", None)
|
||||
if not properties:
|
||||
return {"FINISHED"}
|
||||
|
||||
psets = {}
|
||||
|
||||
for prop in properties:
|
||||
if prop.get("propertyDomainName") != "IFC":
|
||||
continue
|
||||
pset = prop.get("propertySet", None)
|
||||
if not pset:
|
||||
continue
|
||||
psets.setdefault(pset, {})
|
||||
|
||||
predefined_value = prop.get("predefinedValue")
|
||||
if predefined_value:
|
||||
possible_values = [predefined_value]
|
||||
else:
|
||||
possible_values = prop.get("possibleValues", []) or []
|
||||
possible_values = [v["value"] for v in possible_values]
|
||||
|
||||
psets[pset][prop["name"]] = possible_values
|
||||
|
||||
for pset_name, pset in psets.items():
|
||||
new = bprops.classification_psets.add()
|
||||
new.name = pset_name
|
||||
for name, values in pset.items():
|
||||
new2 = new.properties.add()
|
||||
new2.name = name
|
||||
new2.enum_items = json.dumps(values)
|
||||
new2.data_type = "enum"
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,66 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class BSDDDomain(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
namespace_uri: StringProperty(name="URI")
|
||||
default_language_code: StringProperty(name="Language")
|
||||
organization_name_owner: StringProperty(name="Organization")
|
||||
status: StringProperty(name="Status")
|
||||
version: StringProperty(name="Version")
|
||||
|
||||
|
||||
class BSDDClassification(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
reference_code: StringProperty(name="Reference Code")
|
||||
description: StringProperty(name="Description")
|
||||
namespace_uri: StringProperty(name="Namespace URI")
|
||||
domain_name: StringProperty(name="Domain Name")
|
||||
domain_namespace_uri: StringProperty(name="Domain Namespace URI")
|
||||
|
||||
|
||||
class BSDDPset(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
|
||||
|
||||
class BIMBSDDProperties(PropertyGroup):
|
||||
active_domain: StringProperty(name="Active Domain")
|
||||
active_uri: StringProperty(name="Active URI")
|
||||
domains: CollectionProperty(name="Domains", type=BSDDDomain)
|
||||
active_domain_index: IntProperty(name="Active Domain Index")
|
||||
classifications: CollectionProperty(name="Classifications", type=BSDDClassification)
|
||||
active_classification_index: IntProperty(name="Active Classification Index")
|
||||
keyword: StringProperty(name="Keyword")
|
||||
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
|
||||
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
|
||||
@@ -0,0 +1,71 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_bsdd(Panel):
|
||||
bl_label = "buildingSMART Data Dictionary"
|
||||
bl_idname = "BIM_PT_bsdd"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
if props.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + props.active_domain, icon="URL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
|
||||
if len(props.domains):
|
||||
self.layout.template_list(
|
||||
"BIM_UL_bsdd_domains",
|
||||
"",
|
||||
props,
|
||||
"domains",
|
||||
props,
|
||||
"active_domain_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_bsdd_domains")
|
||||
|
||||
|
||||
class BIM_UL_bsdd_domains(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{item.name} ({item.organization_name_owner})")
|
||||
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.name = item.name
|
||||
op.uri = item.namespace_uri
|
||||
|
||||
|
||||
class BIM_UL_bsdd_classifications(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.reference_code)
|
||||
row.label(text=item.name)
|
||||
@@ -21,7 +21,9 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddClassification,
|
||||
operator.AddClassificationFromBSDD,
|
||||
operator.AddClassificationReference,
|
||||
operator.AddClassificationReferenceFromBSDD,
|
||||
operator.ChangeClassificationLevel,
|
||||
operator.DisableEditingClassification,
|
||||
operator.DisableEditingClassificationReference,
|
||||
|
||||
@@ -54,6 +54,25 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_classification_from_bsdd"
|
||||
bl_label = "Add Classification From bSDD"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
domain = [d for d in props.domains if d.name == props.active_domain][0]
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if element.Name == props.active_domain or element.Location == domain.namespace_uri:
|
||||
return
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
|
||||
)
|
||||
classification.Source = domain.organization_name_owner
|
||||
classification.Location = domain.namespace_uri
|
||||
classification.Edition = domain.version
|
||||
|
||||
|
||||
class EnableEditingClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_classification"
|
||||
bl_label = "Enable Editing Classification"
|
||||
@@ -254,6 +273,70 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_classification_reference_from_bsdd"
|
||||
bl_label = "Add Classification Reference From bSDD"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
if self.obj_type == "Object":
|
||||
if context.selected_objects:
|
||||
objects = [o.name for o in context.selected_objects]
|
||||
else:
|
||||
objects = [context.active_object.name]
|
||||
else:
|
||||
objects = [self.obj]
|
||||
props = context.scene.BIMClassificationProperties
|
||||
bprops = context.scene.BIMBSDDProperties
|
||||
|
||||
bsdd_classification = bprops.classifications[bprops.active_classification_index]
|
||||
|
||||
classification = None
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if (
|
||||
element.Name == bsdd_classification.domain_name
|
||||
or element.Location == bsdd_classification.domain_namespace_uri
|
||||
):
|
||||
classification = element
|
||||
break
|
||||
|
||||
if not classification:
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=bsdd_classification.domain_name
|
||||
)
|
||||
classification.Location = bsdd_classification.domain_namespace_uri
|
||||
|
||||
for obj in objects:
|
||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
||||
if not ifc_definition_id:
|
||||
continue
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
reference = ifcopenshell.api.run(
|
||||
"classification.add_reference",
|
||||
tool.Ifc.get(),
|
||||
product=element,
|
||||
classification=classification,
|
||||
identification=bsdd_classification.reference_code,
|
||||
name=bsdd_classification.name,
|
||||
)
|
||||
reference.Location = bsdd_classification.namespace_uri
|
||||
|
||||
for classification_pset in bprops.classification_psets:
|
||||
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name)
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
else:
|
||||
pset = ifcopenshell.api.run(
|
||||
"pset.add_pset", tool.Ifc.get(), product=element, name=classification_pset.name
|
||||
)
|
||||
properties = {}
|
||||
for prop in classification_pset.properties:
|
||||
properties[prop.name] = prop.get_value()
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
|
||||
|
||||
|
||||
class ChangeClassificationLevel(bpy.types.Operator):
|
||||
bl_idname = "bim.change_classification_level"
|
||||
bl_label = "Change Classification Level"
|
||||
|
||||
@@ -48,6 +48,15 @@ class ClassificationReference(PropertyGroup):
|
||||
|
||||
|
||||
class BIMClassificationProperties(PropertyGroup):
|
||||
classification_source: EnumProperty(
|
||||
items=[
|
||||
("FILE", "IFC File", ""),
|
||||
("BSDD", "buildingSMART Data Dictionary", ""),
|
||||
("MANUAL", "Manual Entry", ""),
|
||||
],
|
||||
name="Classification Source",
|
||||
default="FILE",
|
||||
)
|
||||
available_classifications: EnumProperty(items=get_available_classifications, name="Available Classifications")
|
||||
classification_attributes: CollectionProperty(name="Classification Attributes", type=Attribute)
|
||||
active_classification_id: IntProperty(name="Active Classification Id")
|
||||
|
||||
@@ -48,6 +48,42 @@ class BIM_PT_classifications(Panel):
|
||||
|
||||
self.props = context.scene.BIMClassificationProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Source", icon="OUTLINER")
|
||||
row.prop(self.props, "classification_source", text="")
|
||||
|
||||
if self.props.classification_source == "FILE":
|
||||
self.draw_add_file_ui(context)
|
||||
elif self.props.classification_source == "BSDD":
|
||||
self.draw_add_bsdd_ui(context)
|
||||
elif self.props.classification_source == "MANUAL":
|
||||
self.draw_add_manual_ui(context)
|
||||
|
||||
for classification in ClassificationsData.data["classifications"]:
|
||||
if self.props.active_classification_id == classification["id"]:
|
||||
self.draw_editable_ui()
|
||||
else:
|
||||
self.draw_ui(classification)
|
||||
|
||||
def draw_add_manual_ui(self, context):
|
||||
row = self.layout.row()
|
||||
row.label(text="TODO", icon="ERROR")
|
||||
|
||||
def draw_add_bsdd_ui(self, context):
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
|
||||
if not self.bprops.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
return
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + self.bprops.active_domain, icon="URL")
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_classification_from_bsdd", icon="ADD")
|
||||
|
||||
def draw_add_file_ui(self, context):
|
||||
if ClassificationsData.data["has_classification_file"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_classifications", text="")
|
||||
@@ -58,12 +94,6 @@ class BIM_PT_classifications(Panel):
|
||||
row.label(text="No Active Classification Library")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
|
||||
for classification in ClassificationsData.data["classifications"]:
|
||||
if self.props.active_classification_id == classification["id"]:
|
||||
self.draw_editable_ui()
|
||||
else:
|
||||
self.draw_ui(classification)
|
||||
|
||||
def draw_editable_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_classification", text="Save changes", icon="CHECKMARK")
|
||||
@@ -84,6 +114,7 @@ class ReferenceUI:
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMClassificationProperties
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
self.props = obj.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
@@ -100,14 +131,76 @@ class ReferenceUI:
|
||||
self.draw_reference_ui(reference)
|
||||
|
||||
def draw_add_ui(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Source", icon="OUTLINER")
|
||||
row.prop(self.sprops, "classification_source", text="")
|
||||
|
||||
if self.sprops.classification_source == "FILE":
|
||||
self.draw_add_file_ui(context)
|
||||
elif self.sprops.classification_source == "BSDD":
|
||||
self.draw_add_bsdd_ui(context)
|
||||
elif self.sprops.classification_source == "MANUAL":
|
||||
self.draw_add_manual_ui(context)
|
||||
|
||||
def draw_add_manual_ui(self, context):
|
||||
row = self.layout.row()
|
||||
row.label(text="TODO", icon="ERROR")
|
||||
|
||||
def draw_add_bsdd_ui(self, context):
|
||||
if not self.bprops.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
return
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + self.bprops.active_domain, icon="URL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.bprops, "keyword", text="")
|
||||
row.operator("bim.search_bsdd_classifications", text="", icon="VIEWZOOM")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(self.bprops, "should_filter_ifc_class")
|
||||
|
||||
if len(self.bprops.classifications):
|
||||
self.layout.template_list(
|
||||
"BIM_UL_bsdd_classifications",
|
||||
"",
|
||||
self.bprops,
|
||||
"classifications",
|
||||
self.bprops,
|
||||
"active_classification_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Search Results")
|
||||
|
||||
if self.bprops.active_classification_index < len(self.bprops.classifications):
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator(
|
||||
"bim.add_classification_reference_from_bsdd", text="Add Classification Reference", icon="ADD"
|
||||
)
|
||||
op.obj = self.obj
|
||||
op.obj_type = self.obj_type
|
||||
row.operator("bim.get_bsdd_classification_properties", text="", icon="COPY_ID")
|
||||
|
||||
if len(self.bprops.classification_psets):
|
||||
for pset in self.bprops.classification_psets:
|
||||
box = self.layout.box()
|
||||
row = box.row()
|
||||
row.label(text=pset.name, icon="COPY_ID")
|
||||
blenderbim.bim.helper.draw_attributes(pset.properties, box)
|
||||
|
||||
def draw_add_file_ui(self, context):
|
||||
if not self.data.data["active_classification_library"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Active Classification Library")
|
||||
row.label(text="No Active Classification Library", icon="ERROR")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Active Classification Library: {self.data.data['active_classification_library']}")
|
||||
#row.prop(self.sprops, "available_classifications", text="")
|
||||
# row.prop(self.sprops, "available_classifications", text="")
|
||||
if not self.sprops.available_library_references:
|
||||
op = row.operator("bim.change_classification_level", text="", icon="GREASEPENCIL")
|
||||
op.parent_id = int(self.sprops.available_classifications)
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import os
|
||||
import logging
|
||||
import ifcopenshell
|
||||
import json
|
||||
import webbrowser
|
||||
import tempfile
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class SelectCobieIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_cobie_ifc_file"
|
||||
bl_label = "Select COBie IFC File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.COBieProperties.cobie_ifc_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectCobieJsonFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_cobie_json_file"
|
||||
bl_label = "Select COBie JSON File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.COBieProperties.cobie_json_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class ExecuteIfcCobie(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_ifc_cobie"
|
||||
bl_label = "Execute IFCCOBie"
|
||||
file_format: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.COBieProperties
|
||||
return props.should_load_from_memory or props.cobie_ifc_file
|
||||
|
||||
def execute(self, context):
|
||||
from cobie import IfcCobieParser
|
||||
|
||||
props = context.scene.COBieProperties
|
||||
|
||||
if props.should_load_from_memory:
|
||||
output_dir = tempfile.gettempdir()
|
||||
else:
|
||||
output_dir = os.path.dirname(props.cobie_ifc_file)
|
||||
|
||||
output = os.path.join(output_dir, "output")
|
||||
logger = logging.getLogger("IFCtoCOBie")
|
||||
fh = logging.FileHandler(os.path.join(output_dir, "cobie.log"))
|
||||
fh.setLevel(logging.DEBUG)
|
||||
fh.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(message)s"))
|
||||
logger = logging.getLogger("IFCtoCOBie")
|
||||
logger.addHandler(fh)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
if props.cobie_json_file:
|
||||
with open(props.cobie_json_file, "r") as f:
|
||||
custom_data = json.load(f)
|
||||
else:
|
||||
custom_data = {}
|
||||
parser = IfcCobieParser(logger, selector)
|
||||
|
||||
ifc_file = IfcStore.get_file()
|
||||
|
||||
if not (ifc_file and props.should_load_from_memory):
|
||||
ifc_file = props.cobie_ifc_file
|
||||
|
||||
parser.parse(
|
||||
ifc_file,
|
||||
props.cobie_types,
|
||||
props.cobie_components,
|
||||
custom_data,
|
||||
)
|
||||
if self.file_format == "xlsx":
|
||||
from cobie import CobieXlsWriter
|
||||
|
||||
writer = CobieXlsWriter(parser, output)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output + "." + self.file_format)
|
||||
elif self.file_format == "ods":
|
||||
from cobie import CobieOdsWriter
|
||||
|
||||
writer = CobieOdsWriter(parser, output)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output + "." + self.file_format)
|
||||
else:
|
||||
from cobie import CobieCsvWriter
|
||||
|
||||
writer = CobieCsvWriter(parser, output_dir)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output_dir)
|
||||
webbrowser.open("file://" + output_dir + "/cobie.log")
|
||||
return {"FINISHED"}
|
||||
@@ -1,38 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class COBieProperties(PropertyGroup):
|
||||
cobie_ifc_file: StringProperty(default="", name="COBie IFC File")
|
||||
cobie_types: StringProperty(default=".COBieType", name="COBie Types")
|
||||
cobie_components: StringProperty(default=".COBie", name="COBie Components")
|
||||
cobie_json_file: StringProperty(default="", name="COBie JSON File")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
@@ -1,63 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_cobie(Panel):
|
||||
bl_label = "COBie"
|
||||
bl_idname = "BIM_PT_cobie"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_handover"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
scene = context.scene
|
||||
props = scene.COBieProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
row = layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "cobie_ifc_file")
|
||||
row.operator("bim.select_cobie_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "cobie_types")
|
||||
row = layout.row()
|
||||
row.prop(props, "cobie_components")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "cobie_json_file")
|
||||
row.operator("bim.select_cobie_json_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row()
|
||||
op = row.operator("bim.execute_ifc_cobie", text="CSV")
|
||||
op.file_format = "csv"
|
||||
op = row.operator("bim.execute_ifc_cobie", text="ODS")
|
||||
op.file_format = "ods"
|
||||
op = row.operator("bim.execute_ifc_cobie", text="XLSX")
|
||||
op.file_format = "xlsx"
|
||||
@@ -20,18 +20,19 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ConvertToBlender,
|
||||
operator.CopyDebugInformation,
|
||||
operator.CreateAllShapes,
|
||||
operator.CreateShapeFromStepId,
|
||||
operator.InspectFromObject,
|
||||
operator.InspectFromStepId,
|
||||
operator.OverrideDisplayType,
|
||||
operator.ParseExpress,
|
||||
operator.PrintIfcFile,
|
||||
operator.PrintObjectPlacement,
|
||||
operator.ProfileImportIFC,
|
||||
operator.PurgeHdf5Cache,
|
||||
operator.PurgeIfcLinks,
|
||||
operator.ConvertToBlender,
|
||||
operator.RewindInspector,
|
||||
operator.SelectExpressFile,
|
||||
operator.SelectHighPolygonMeshes,
|
||||
|
||||
@@ -443,3 +443,14 @@ class PurgeHdf5Cache(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
core.purge_hdf5_cache(tool.Debug)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OverrideDisplayType(bpy.types.Operator):
|
||||
bl_idname = "bim.override_display_type"
|
||||
bl_label = "Override Display Type"
|
||||
display: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
obj.display_type = self.display
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -40,3 +40,13 @@ class BIMDebugProperties(PropertyGroup):
|
||||
inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute)
|
||||
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
|
||||
express_file: StringProperty(name="Express File")
|
||||
display_type: EnumProperty(
|
||||
items=[
|
||||
("BOUNDS", "Bounds", ""),
|
||||
("WIRE", "Wire", ""),
|
||||
("SOLID", "Solid", ""),
|
||||
("TEXTURED", "Textured", ""),
|
||||
],
|
||||
name="Display Type",
|
||||
default="BOUNDS",
|
||||
)
|
||||
|
||||
@@ -83,6 +83,10 @@ class BIM_PT_debug(Panel):
|
||||
).percentile = context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.prop(props, "display_type", text="")
|
||||
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
row = layout.row()
|
||||
|
||||
@@ -1926,7 +1926,7 @@ class DecorationsHandler:
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
# NOTE: we USE POST_PIXEL here so that we can draw use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# NOTE: we USE POST_PIXEL here so that we can use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# and drawing text in the same handler. BUT this means that we supply coordinates in WINSPACE
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_PIXEL")
|
||||
|
||||
@@ -1944,6 +1944,7 @@ class DecorationsHandler:
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
# TODO: do it in data instead of the handler for performance?
|
||||
results = []
|
||||
|
||||
for obj in collection.all_objects:
|
||||
|
||||
@@ -345,8 +345,12 @@ class SheetBuilder:
|
||||
def build_drawings(self, root, sheet):
|
||||
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
|
||||
drawing_id = int(view.attrib["data-id"])
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
|
||||
try:
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
|
||||
except:
|
||||
# Perhaps the SVG has outdated content or is edited externally which we cannot control.
|
||||
continue
|
||||
|
||||
images = view.findall("{http://www.w3.org/2000/svg}image")
|
||||
|
||||
@@ -387,8 +391,12 @@ class SheetBuilder:
|
||||
|
||||
def build_schedules(self, root, sheet):
|
||||
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'):
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
|
||||
try:
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
|
||||
except:
|
||||
# Perhaps the SVG has outdated content or is edited externally which we cannot control.
|
||||
continue
|
||||
|
||||
images = view.findall("{http://www.w3.org/2000/svg}image")
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ classes = (
|
||||
operator.OverrideDuplicateMoveLinkedMacro,
|
||||
operator.OverrideDuplicateMoveMacro,
|
||||
operator.OverrideJoin,
|
||||
operator.OverrideMeshSeparate,
|
||||
operator.OverrideModeSetEdit,
|
||||
operator.OverrideModeSetObject,
|
||||
operator.OverrideOriginSet,
|
||||
@@ -53,6 +54,7 @@ classes = (
|
||||
ui.BIM_PT_mesh,
|
||||
ui.BIM_PT_workarounds,
|
||||
ui.BIM_MT_object_set_origin,
|
||||
ui.BIM_MT_separate,
|
||||
)
|
||||
|
||||
|
||||
@@ -73,6 +75,7 @@ def register():
|
||||
bpy.types.VIEW3D_MT_object.append(ui.object_menu)
|
||||
bpy.types.OUTLINER_MT_object.append(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu)
|
||||
bpy.types.VIEW3D_MT_edit_mesh.append(ui.edit_mesh_menu)
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon:
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Object Mode", space_type="EMPTY")
|
||||
@@ -116,6 +119,7 @@ def unregister():
|
||||
bpy.types.OBJECT_PT_transform.remove(ui.BIM_PT_transform)
|
||||
bpy.types.OUTLINER_MT_object.remove(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_edit_mesh.remove(ui.edit_mesh_menu)
|
||||
del bpy.types.Scene.BIMGeometryProperties
|
||||
del bpy.types.Object.BIMGeometryProperties
|
||||
wm = bpy.context.window_manager
|
||||
|
||||
@@ -57,6 +57,39 @@ class EditObjectPlacement(bpy.types.Operator, Operator):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
|
||||
class OverrideMeshSeparate(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.override_mesh_separate"
|
||||
bl_label = "IFC Mesh Separate"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
|
||||
# You cannot separate meshes if the representation is mapped.
|
||||
relating_type = tool.Root.get_element_type(tool.Ifc.get_entity(obj))
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
# We toggle edit mode to ensure that once representations are
|
||||
# unmapped, our Blender mesh only has a single user.
|
||||
tool.Blender.toggle_edit_mode(context)
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
tool.Blender.toggle_edit_mode(context)
|
||||
|
||||
selected_objects = context.selected_objects
|
||||
bpy.ops.mesh.separate(type=self.type)
|
||||
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
||||
new_objs = [obj]
|
||||
for new_obj in context.selected_objects:
|
||||
if new_obj == obj:
|
||||
continue
|
||||
# This is not very efficient, it needlessly copies the representations first.
|
||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||
new_objs.append(new_obj)
|
||||
for new_obj in new_objs:
|
||||
bpy.ops.bim.update_representation(obj=new_obj.name)
|
||||
|
||||
|
||||
class OverrideOriginSet(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.override_origin_set"
|
||||
bl_label = "IFC Origin Set"
|
||||
@@ -88,8 +121,15 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
representation_conversion_method: bpy.props.EnumProperty(
|
||||
items=[
|
||||
("OUTLINE", "Trace Outline", ""),
|
||||
("BOX", "Bounding Box", ""),
|
||||
("OUTLINE", "Trace Outline", "Traces outline by local XY axes, for Profile - by local XZ axes."),
|
||||
(
|
||||
"BOX",
|
||||
"Bounding Box",
|
||||
"Creates a bounding box representation.\n"
|
||||
"For Plan context - 2D bounding box by local XY axes,\n"
|
||||
"for Profile - 2D bounding box by local XZ axes.\n"
|
||||
"For other contexts - bounding box is 3d.",
|
||||
),
|
||||
("PROJECT", "Full Representation", ""),
|
||||
],
|
||||
name="Representation Conversion Method",
|
||||
@@ -499,6 +539,8 @@ class OverrideDelete(bpy.types.Operator):
|
||||
def _execute(self, context):
|
||||
if self.is_batch:
|
||||
ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get())
|
||||
|
||||
self.process_arrays(context)
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -508,6 +550,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
tool.Geometry.delete_ifc_object(obj)
|
||||
else:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
if self.is_batch:
|
||||
old_file = tool.Ifc.get()
|
||||
old_file.end_transaction()
|
||||
@@ -528,6 +571,30 @@ class OverrideDelete(bpy.types.Operator):
|
||||
data["old_file"].redo()
|
||||
tool.Ifc.set(data["new_file"])
|
||||
|
||||
def process_arrays(self, context):
|
||||
selected_objects = set(context.selected_objects)
|
||||
array_parents = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not pset:
|
||||
continue
|
||||
array_parents.add(tool.Ifc.get().by_guid(pset["Parent"]))
|
||||
|
||||
for array_parent in array_parents:
|
||||
array_parent_obj = tool.Ifc.get_object(array_parent)
|
||||
data = [(i, data) for i, data in enumerate(tool.Blender.Modifier.Array.get_modifiers_data(array_parent))]
|
||||
# NOTE: there is a way to remove arrays more precisely but it's more complex
|
||||
for i, modifier_data in reversed(data):
|
||||
children = set(tool.Blender.Modifier.Array.get_children_objects(modifier_data))
|
||||
if children.issubset(selected_objects):
|
||||
with context.temp_override(active_object=array_parent_obj):
|
||||
bpy.ops.bim.remove_array(item=i)
|
||||
else:
|
||||
break # allows to remove only n last layers of an array
|
||||
|
||||
|
||||
class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
bl_idname = "bim.override_outliner_delete"
|
||||
@@ -538,7 +605,7 @@ class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_ids) > 0
|
||||
return len(getattr(context, "selected_ids", [])) > 0
|
||||
|
||||
def execute(self, context):
|
||||
# In this override, we don't check self.hierarchy. This effectively
|
||||
@@ -653,6 +720,13 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
return len(context.selected_objects) > 0
|
||||
|
||||
def execute(self, context):
|
||||
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=False)
|
||||
|
||||
def _execute(self, context):
|
||||
return OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context)
|
||||
|
||||
@staticmethod
|
||||
def execute_duplicate_operator(self, context, linked=False):
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
@@ -663,7 +737,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
new_active_obj = None
|
||||
for obj in context.selected_objects:
|
||||
new_obj = obj.copy()
|
||||
if obj.data:
|
||||
if linked and obj.data:
|
||||
new_obj.data = obj.data.copy()
|
||||
if obj == context.active_object:
|
||||
new_active_obj = new_obj
|
||||
@@ -675,32 +749,50 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
context.view_layer.objects.active = new_active_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
def _execute(self, context):
|
||||
@staticmethod
|
||||
def execute_ifc_duplicate_operator(self, context, linked=False):
|
||||
objects_to_duplicate = set(context.selected_objects)
|
||||
|
||||
# handle arrays
|
||||
arrays_to_duplicate, array_children = OverrideDuplicateMove.process_arrays(self, context)
|
||||
objects_to_duplicate -= array_children
|
||||
for child in array_children:
|
||||
child.select_set(False)
|
||||
|
||||
self.new_active_obj = None
|
||||
# Track decompositions so they can be recreated after the operation
|
||||
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
|
||||
relationships = tool.Root.get_decomposition_relationships(objects_to_duplicate)
|
||||
old_to_new = {}
|
||||
for obj in context.selected_objects:
|
||||
|
||||
for obj in objects_to_duplicate:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element and element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
|
||||
continue # For now, don't copy drawings until we stabilise a bit more. It's tricky.
|
||||
|
||||
linked_non_ifc_object = linked and not element
|
||||
|
||||
# Prior to duplicating, sync the object placement to make decomposition recreation more stable.
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
new_obj = obj.copy()
|
||||
temp_data = None
|
||||
if obj.data:
|
||||
|
||||
if obj.data and not linked_non_ifc_object:
|
||||
# assure root.copy_class won't replace the previous mesh globally
|
||||
temp_data = obj.data.copy()
|
||||
new_obj.data = temp_data
|
||||
|
||||
if obj == context.active_object:
|
||||
self.new_active_obj = new_obj
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
obj.select_set(False)
|
||||
new_obj.select_set(True)
|
||||
|
||||
if linked_non_ifc_object:
|
||||
continue
|
||||
|
||||
# clear object's collection so it will be able to have it's own
|
||||
new_obj.BIMObjectProperties.collection = None
|
||||
# copy the actual class
|
||||
@@ -711,17 +803,58 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
tool.Blender.remove_data_block(temp_data)
|
||||
|
||||
if new:
|
||||
array_pset = ifcopenshell.util.element.get_pset(new, "BBIM_Array")
|
||||
if array_pset:
|
||||
array_pset = tool.Ifc.get().by_id(array_pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new, pset=array_pset)
|
||||
old_to_new[tool.Ifc.get_entity(obj)] = [new]
|
||||
# TODO: handle array data for other cases of duplication
|
||||
array_data = arrays_to_duplicate.get(obj, None)
|
||||
tool.Model.handle_array_on_copied_element(new, array_data)
|
||||
if array_data:
|
||||
for child in tool.Blender.Modifier.Array.get_all_children_objects(new):
|
||||
child.select_set(True)
|
||||
|
||||
# TODO: add new array children to recreate their decomposition too
|
||||
old_to_new[element] = [new]
|
||||
if new.is_a("IfcRelSpaceBoundary"):
|
||||
tool.Boundary.decorate_boundary(new_obj)
|
||||
|
||||
# Recreate decompositions
|
||||
tool.Root.recreate_decompositions(relationships, old_to_new)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
@staticmethod
|
||||
def process_arrays(self, context):
|
||||
selected_objects = set(context.selected_objects)
|
||||
array_parents = set()
|
||||
arrays_to_create = dict()
|
||||
array_children = set() # will be ignored during the duplication
|
||||
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not pset:
|
||||
continue
|
||||
array_parents.add(tool.Ifc.get().by_guid(pset["Parent"]))
|
||||
|
||||
for array_parent in array_parents:
|
||||
array_parent_obj = tool.Ifc.get_object(array_parent)
|
||||
if array_parent_obj not in selected_objects:
|
||||
continue
|
||||
|
||||
array_data = []
|
||||
for modifier_data in tool.Blender.Modifier.Array.get_modifiers_data(array_parent):
|
||||
children = set(tool.Blender.Modifier.Array.get_children_objects(modifier_data))
|
||||
if children.issubset(selected_objects):
|
||||
modifier_data["children"] = []
|
||||
array_data.append(modifier_data)
|
||||
array_children.update(children)
|
||||
else:
|
||||
break # allows to duplicate only n first layers of an array
|
||||
|
||||
if array_data:
|
||||
arrays_to_create[array_parent_obj] = array_data
|
||||
|
||||
return arrays_to_create, array_children
|
||||
|
||||
|
||||
class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro):
|
||||
bl_idname = "bim.override_object_duplicate_move_linked_macro"
|
||||
@@ -739,57 +872,10 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
|
||||
return len(context.selected_objects) > 0
|
||||
|
||||
def execute(self, context):
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
if self.new_active_obj:
|
||||
context.view_layer.objects.active = self.new_active_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
new_active_obj = None
|
||||
for obj in context.selected_objects:
|
||||
new_obj = obj.copy()
|
||||
if obj == context.active_object:
|
||||
new_active_obj = new_obj
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
obj.select_set(False)
|
||||
new_obj.select_set(True)
|
||||
if new_active_obj:
|
||||
context.view_layer.objects.active = new_active_obj
|
||||
return {"FINISHED"}
|
||||
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=True)
|
||||
|
||||
def _execute(self, context):
|
||||
self.new_active_obj = None
|
||||
# Track decompositions so they can be recreated after the operation
|
||||
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
|
||||
old_to_new = {}
|
||||
for obj in context.selected_objects:
|
||||
# Prior to duplicating, sync the object placement to make decomposition recreation more stable.
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
new_obj = obj.copy()
|
||||
if obj.data:
|
||||
new_obj.data = obj.data.copy()
|
||||
if obj == context.active_object:
|
||||
self.new_active_obj = new_obj
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
obj.select_set(False)
|
||||
new_obj.select_set(True)
|
||||
# Copy the actual class
|
||||
new = blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||
if new:
|
||||
array_pset = ifcopenshell.util.element.get_pset(new, "BBIM_Array")
|
||||
if array_pset:
|
||||
array_pset = tool.Ifc.get().by_id(array_pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new, pset=array_pset)
|
||||
old_to_new[tool.Ifc.get_entity(obj)] = new
|
||||
# Recreate decompositions
|
||||
tool.Root.recreate_decompositions(relationships, old_to_new)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
return OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True)
|
||||
|
||||
|
||||
class OverrideDuplicateMoveAggregateMacro(bpy.types.Macro):
|
||||
@@ -809,28 +895,7 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
return len(context.selected_objects) > 0
|
||||
|
||||
def execute(self, context):
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
if self.new_active_obj:
|
||||
context.view_layer.objects.active = self.new_active_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
new_active_obj = None
|
||||
|
||||
for obj in context.selected_objects:
|
||||
new_obj = obj.copy()
|
||||
if obj.data:
|
||||
new_obj.data = obj.data.copy()
|
||||
if obj == context.active_object:
|
||||
new_active_obj = new_obj
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
obj.select_set(False)
|
||||
new_obj.select_set(True)
|
||||
if new_active_obj:
|
||||
context.view_layer.objects.active = new_active_obj
|
||||
return {"FINISHED"}
|
||||
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=False)
|
||||
|
||||
def _execute(self, context):
|
||||
self.new_active_obj = None
|
||||
@@ -1028,12 +1093,7 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
)
|
||||
|
||||
if new_entity:
|
||||
# Checks if the object belongs to an Ifc Array
|
||||
array_pset = ifcopenshell.util.element.get_pset(new_entity, "BBIM_Array")
|
||||
if array_pset:
|
||||
array_pset = tool.Ifc.get().by_id(array_pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new_entity, pset=array_pset)
|
||||
|
||||
tool.Model.handle_array_on_copied_element(new_entity)
|
||||
blenderbim.core.aggregate.unassign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
@@ -1068,6 +1128,17 @@ class OverrideDuplicateMoveAggregate(bpy.types.Operator):
|
||||
|
||||
recreate_data_structure(new_root_entity)
|
||||
|
||||
# Remove connections with old objects
|
||||
for new in old_to_new.values():
|
||||
for connection in new[0].ConnectedTo:
|
||||
entity = connection.RelatedElement
|
||||
if entity in old_to_new.keys():
|
||||
core.remove_connection(tool.Geometry, connection=connection)
|
||||
for connection in new[0].ConnectedFrom:
|
||||
entity = connection.RelatingElement
|
||||
if entity in old_to_new.keys():
|
||||
core.remove_connection(tool.Geometry, connection=connection)
|
||||
|
||||
old_objs = []
|
||||
for old, new in old_to_new.items():
|
||||
old_objs.append(tool.Ifc.get_object(old))
|
||||
@@ -1161,12 +1232,7 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
)
|
||||
|
||||
if new_entity:
|
||||
# Checks if the object belongs to an Ifc Array
|
||||
array_pset = ifcopenshell.util.element.get_pset(new_entity, "BBIM_Array")
|
||||
if array_pset:
|
||||
array_pset = tool.Ifc.get().by_id(array_pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new_entity, pset=array_pset)
|
||||
|
||||
tool.Model.handle_array_on_copied_element(new_entity)
|
||||
blenderbim.core.aggregate.unassign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
@@ -1180,22 +1246,33 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
return new_entity
|
||||
|
||||
if len(context.selected_objects) != 1:
|
||||
self.report({"INFO"}, "Only 1 object need to be selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
selected_root_obj = context.selected_objects[0]
|
||||
selected_root_entity = tool.Ifc.get_entity(selected_root_obj)
|
||||
|
||||
if not selected_root_entity.is_a("IfcElementAssembly"):
|
||||
if selected_root_entity.is_a("IfcElementAssembly"):
|
||||
pass
|
||||
elif selected_root_entity.Decomposes:
|
||||
if selected_root_entity.Decomposes[0].RelatingObject.is_a("IfcElementAssembly"):
|
||||
selected_root_entity = selected_root_entity.Decomposes[0].RelatingObject
|
||||
selected_root_obj = tool.Ifc.get_object(selected_root_entity)
|
||||
else:
|
||||
self.report({"INFO"}, "Object is not part of a IfcElementAssembly.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(selected_root_entity, "BBIM_Aggregate_Data")
|
||||
if not pset:
|
||||
self.report({"INFO"}, "Object is not part of an assembly aggregate.")
|
||||
return {"FINISHED"}
|
||||
|
||||
pset_data = json.loads(pset["Data"])[0]
|
||||
instance_of = pset_data["instance_of"][0]
|
||||
original_root_entity = tool.Ifc.get().by_guid(instance_of)
|
||||
if original_root_entity == selected_root_entity:
|
||||
self.report({"INFO"}, "Cannot refresh original assembly. Select an assembly instance.")
|
||||
return {"FINISHED"}
|
||||
|
||||
parents = remove_objects(selected_root_entity)
|
||||
@@ -1208,13 +1285,25 @@ class RefreshAggregate(bpy.types.Operator):
|
||||
for parent in parents:
|
||||
duplicate_children(parent)
|
||||
|
||||
|
||||
# Remove connections with old objects
|
||||
for new in old_to_new.values():
|
||||
for connection in new[0].ConnectedTo:
|
||||
entity = connection.RelatedElement
|
||||
if entity in old_to_new.keys():
|
||||
core.remove_connection(tool.Geometry, connection=connection)
|
||||
for connection in new[0].ConnectedFrom:
|
||||
entity = connection.RelatingElement
|
||||
if entity in old_to_new.keys():
|
||||
core.remove_connection(tool.Geometry, connection=connection)
|
||||
|
||||
old_objs = []
|
||||
for old, new in old_to_new.items():
|
||||
old_objs.append(tool.Ifc.get_object(old))
|
||||
|
||||
new_obj = tool.Ifc.get_object(new[0])
|
||||
|
||||
matrix_diff = new_obj.matrix_world @ original_matrix
|
||||
matrix_diff = Matrix.inverted(original_matrix) @ new_obj.matrix_world
|
||||
new_matrix = selected_matrix @ matrix_diff
|
||||
|
||||
new_obj.matrix_world = new_matrix
|
||||
|
||||
@@ -32,6 +32,21 @@ def object_menu(self, context):
|
||||
self.layout.menu("BIM_MT_object_set_origin", icon="PLUGIN")
|
||||
|
||||
|
||||
def edit_mesh_menu(self, context):
|
||||
self.layout.separator()
|
||||
self.layout.menu("BIM_MT_separate", icon="PLUGIN")
|
||||
|
||||
|
||||
class BIM_MT_separate(Menu):
|
||||
bl_idname = "BIM_MT_separate"
|
||||
bl_label = "IFC Separate"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.operator("bim.override_mesh_separate", icon="PLUGIN", text="IFC Selection").type = "SELECTED"
|
||||
self.layout.operator("bim.override_mesh_separate", icon="PLUGIN", text="IFC By Material").type = "MATERIAL"
|
||||
self.layout.operator("bim.override_mesh_separate", icon="PLUGIN", text="IFC By Loose Parts").type = "LOOSE"
|
||||
|
||||
|
||||
class BIM_MT_object_set_origin(Menu):
|
||||
bl_idname = "BIM_MT_object_set_origin"
|
||||
bl_label = "IFC Set Origin"
|
||||
|
||||
@@ -22,6 +22,7 @@ import ifcopenshell
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.misc as core
|
||||
import blenderbim.core.geometry as core_geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from mathutils import Vector, Matrix, Euler
|
||||
|
||||
@@ -140,7 +141,51 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
return context.selected_objects and tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
core.split_along_edge(tool.Misc, cutter=context.active_object, objs=context.selected_objects)
|
||||
cutter = context.active_object
|
||||
objs = [o for o in context.selected_objects if o != cutter]
|
||||
|
||||
# Splitting only works on meshes
|
||||
for obj in objs:
|
||||
# You cannot split meshes if the representation is mapped.
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
core_geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=False,
|
||||
)
|
||||
|
||||
if not tool.Geometry.is_meshlike(representation):
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="IfcTessellatedFaceSet")
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs, cutter)
|
||||
for obj in new_objs:
|
||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
for obj in objs:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
core_geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
|
||||
class GetConnectedSystemElements(bpy.types.Operator, Operator):
|
||||
@@ -205,46 +250,50 @@ class DrawSystemArrows(bpy.types.Operator, Operator):
|
||||
return context.selected_objects and tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
curve = bpy.data.objects.new("System Arrows", bpy.data.curves.new("System Arrows", "CURVE"))
|
||||
curve.data.dimensions = "3D"
|
||||
context.scene.collection.objects.link(curve)
|
||||
sinks = []
|
||||
sources = []
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
sources = []
|
||||
sinks = []
|
||||
for rel in getattr(element, "HasPorts", []) or []:
|
||||
if rel.RelatingPort.FlowDirection == "SOURCE":
|
||||
sources.append(
|
||||
self.get_absolute_matrix(
|
||||
ifcopenshell.util.placement.get_local_placement(rel.RelatingPort.ObjectPlacement)
|
||||
)
|
||||
)
|
||||
elif rel.RelatingPort.FlowDirection == "SINK":
|
||||
sinks.append(
|
||||
self.get_absolute_matrix(
|
||||
ifcopenshell.util.placement.get_local_placement(rel.RelatingPort.ObjectPlacement)
|
||||
)
|
||||
)
|
||||
sources_current = []
|
||||
sinks_current = []
|
||||
|
||||
for port in tool.System.get_ports(element):
|
||||
local_placement = ifcopenshell.util.placement.get_local_placement(port.ObjectPlacement)
|
||||
m = self.get_absolute_matrix(local_placement)
|
||||
if port.FlowDirection == "SOURCE":
|
||||
sources_current.append(m)
|
||||
elif port.FlowDirection == "SINK":
|
||||
sinks_current.append(m)
|
||||
else:
|
||||
sources.append(
|
||||
self.get_absolute_matrix(
|
||||
ifcopenshell.util.placement.get_local_placement(rel.RelatingPort.ObjectPlacement)
|
||||
)
|
||||
)
|
||||
sinks.append(
|
||||
self.get_absolute_matrix(
|
||||
ifcopenshell.util.placement.get_local_placement(rel.RelatingPort.ObjectPlacement)
|
||||
)
|
||||
)
|
||||
for sink in sinks:
|
||||
for source in sources:
|
||||
sources_current.append(m)
|
||||
sinks_current.append(m)
|
||||
|
||||
if sinks_current or sources_current:
|
||||
sinks.append(sinks_current)
|
||||
sources.append(sources_current)
|
||||
|
||||
if not sinks:
|
||||
self.report({"INFO"}, "No sinks/sources found for selected objects.")
|
||||
return {"FINISHED"}
|
||||
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
curve = bpy.data.objects.new("System Arrows", bpy.data.curves.new("System Arrows", "CURVE"))
|
||||
curve.data.dimensions = "3D"
|
||||
curve.show_in_front = True
|
||||
context.scene.collection.objects.link(curve)
|
||||
|
||||
for i in range(len(sinks)):
|
||||
for sink in sinks[i]:
|
||||
for source in sources[i]:
|
||||
polyline = curve.data.splines.new("POLY")
|
||||
polyline.points.add(1)
|
||||
polyline.points[0].co = (Matrix(sink).translation * unit_scale).to_4d()
|
||||
polyline.points[1].co = (Matrix(source).translation * unit_scale).to_4d()
|
||||
tool.Blender.select_and_activate_single_object(context, curve)
|
||||
|
||||
def get_absolute_matrix(self, matrix):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
|
||||
@@ -206,15 +206,29 @@ class SelectArrayParent(bpy.types.Operator):
|
||||
bl_idname = "bim.select_array_parent"
|
||||
bl_label = "Select Array Parent"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
parent: bpy.props.StringProperty(description="Parent Element GUID")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
cls.poll_message_set("No active object selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
element = tool.Ifc.get().by_guid(self.parent)
|
||||
except:
|
||||
self.report({"ERROR"}, f"Couldn't find array parent by guid '{self.parent}'")
|
||||
object = context.active_object
|
||||
element = tool.Ifc.get_entity(object)
|
||||
array_pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not array_pset:
|
||||
self.report({"ERROR"}, f"Object is not part of an array.")
|
||||
return {"CANCELLED"}
|
||||
obj = tool.Ifc.get_object(element)
|
||||
|
||||
try:
|
||||
parent_element = tool.Ifc.get().by_guid(array_pset["Parent"])
|
||||
except:
|
||||
self.report({"ERROR"}, f"Couldn't find array parent by guid '{array_pset['Parent']}'")
|
||||
return {"CANCELLED"}
|
||||
|
||||
obj = tool.Ifc.get_object(parent_element)
|
||||
if obj:
|
||||
tool.Blender.select_and_activate_single_object(context, active_object=obj)
|
||||
return {"FINISHED"}
|
||||
@@ -224,13 +238,26 @@ class SelectAllArrayObjects(bpy.types.Operator):
|
||||
bl_idname = "bim.select_all_array_objects"
|
||||
bl_label = "Select All Array Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
parent: bpy.props.StringProperty(description="Parent Element GUID")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
cls.poll_message_set("No active object selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
object = context.active_object
|
||||
element = tool.Ifc.get_entity(object)
|
||||
array_pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not array_pset:
|
||||
self.report({"ERROR"}, f"Object is not part of an array.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
parent_element = tool.Ifc.get().by_guid(self.parent)
|
||||
parent_element = tool.Ifc.get().by_guid(array_pset["Parent"])
|
||||
except RuntimeError:
|
||||
self.report({"ERROR"}, f"Couldn't find array parent by guid '{self.parent}'")
|
||||
self.report({"ERROR"}, f"Couldn't find array parent by guid '{array_pset['Parent']}'")
|
||||
return {"CANCELLED"}
|
||||
|
||||
array_objects = tool.Blender.Modifier.Array.get_all_objects(parent_element)
|
||||
|
||||
@@ -68,6 +68,14 @@ def update_door_modifier_representation(context, obj):
|
||||
},
|
||||
}
|
||||
|
||||
def get_active_representation_context(obj):
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
if active_representation:
|
||||
return active_representation.ContextOfItems
|
||||
return ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
previously_active_context = get_active_representation_context(obj)
|
||||
|
||||
# ELEVATION_VIEW representation
|
||||
profile = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Profile", "ELEVATION_VIEW")
|
||||
if profile:
|
||||
@@ -78,6 +86,7 @@ def update_door_modifier_representation(context, obj):
|
||||
tool.Model.replace_object_ifc_representation(profile, obj, elevation_representation)
|
||||
|
||||
# MODEL_VIEW representation
|
||||
# (Model/Body defined only BEFORE Plan/Body to prevent #2744)
|
||||
body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
representation_data["context"] = body
|
||||
model_representation = ifcopenshell.api.run("geometry.add_door_representation", ifc_file, **representation_data)
|
||||
@@ -113,14 +122,34 @@ def update_door_modifier_representation(context, obj):
|
||||
)
|
||||
tool.Model.replace_object_ifc_representation(plan_annotation, obj, plan_representation)
|
||||
|
||||
if plan_body or plan_annotation:
|
||||
# adding switch representation at the end instead of changing order of representations
|
||||
# to prevent #2744
|
||||
core.switch_representation(
|
||||
# adding switch representation at the end instead of changing order of representations
|
||||
# to prevent #2744
|
||||
if get_active_representation_context(obj) != previously_active_context:
|
||||
previously_active_representation = ifcopenshell.util.representation.get_representation(
|
||||
element,
|
||||
previously_active_context.ContextType,
|
||||
previously_active_context.ContextIdentifier,
|
||||
previously_active_context.TargetView,
|
||||
)
|
||||
|
||||
if not previously_active_representation:
|
||||
# we assume there is no representation because it was
|
||||
# Plan/Annotation/PLAN_VIEW
|
||||
previously_active_context = ifcopenshell.util.representation.get_context(
|
||||
ifc_file, "Plan", "Body", "PLAN_VIEW"
|
||||
)
|
||||
previously_active_representation = ifcopenshell.util.representation.get_representation(
|
||||
element,
|
||||
previously_active_context.ContextType,
|
||||
previously_active_context.ContextIdentifier,
|
||||
previously_active_context.TargetView,
|
||||
)
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=model_representation,
|
||||
representation=previously_active_representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=True,
|
||||
|
||||
@@ -847,6 +847,7 @@ class MEPAddTransition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
tool.Model.replace_object_ifc_representation(body, obj, rep)
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=transition_type, name="BBIM_Fitting")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset",
|
||||
@@ -1188,6 +1189,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
tool.Model.replace_object_ifc_representation(body, obj, rep)
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=bend_type, name="BBIM_Fitting")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset",
|
||||
|
||||
@@ -185,8 +185,13 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
|
||||
tool.Blender.remove_data_block(mesh) # Remove "Instance" mesh
|
||||
|
||||
mesh_data = obj.data
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
||||
if obj.data != mesh_data: # remove orphaned mesh from "bim.assign_class"
|
||||
tool.Blender.remove_data_block(mesh_data)
|
||||
|
||||
# Update required as core.type.assign_type may change obj.data
|
||||
# TODO: This is inefficient. It literally creates a mesh, then potentially removes it.
|
||||
|
||||
@@ -132,6 +132,7 @@ class DumbProfileGenerator:
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"})
|
||||
|
||||
@@ -196,6 +196,7 @@ class DumbSlabGenerator:
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
|
||||
if self.footprint_context:
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
|
||||
@@ -188,10 +188,8 @@ class BIM_PT_array(bpy.types.Panel):
|
||||
if ArrayData.data["parameters"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=ArrayData.data["parameters"]["parent_name"], icon="CON_CHILDOF")
|
||||
op = row.operator("bim.select_array_parent", icon="OBJECT_DATA", text="")
|
||||
op.parent = ArrayData.data["parameters"]["Parent"]
|
||||
op = row.operator("bim.select_all_array_objects", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.parent = ArrayData.data["parameters"]["Parent"]
|
||||
row.operator("bim.select_array_parent", icon="OBJECT_DATA", text="")
|
||||
row.operator("bim.select_all_array_objects", icon="RESTRICT_SELECT_OFF", text="")
|
||||
|
||||
if ArrayData.data["parameters"]["data_dict"]:
|
||||
row.operator("bim.add_array", icon="ADD", text="")
|
||||
|
||||
@@ -637,6 +637,7 @@ class DumbWallGenerator:
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer2"})
|
||||
obj.select_set(True)
|
||||
@@ -827,10 +828,10 @@ class DumbWallJoiner:
|
||||
return
|
||||
|
||||
for rel in element1.ConnectedTo:
|
||||
if rel.RelatingConnectionType in ["ATSTART", "ATEND"]:
|
||||
if rel.is_a("IfcRelConnectsPathElements") and rel.RelatingConnectionType in ["ATSTART", "ATEND"]:
|
||||
rel.RelatingConnectionType = "ATSTART" if rel.RelatingConnectionType == "ATEND" else "ATEND"
|
||||
for rel in element1.ConnectedFrom:
|
||||
if rel.RelatedConnectionType in ["ATSTART", "ATEND"]:
|
||||
if rel.is_a("IfcRelConnectsPathElements") and rel.RelatedConnectionType in ["ATSTART", "ATEND"]:
|
||||
rel.RelatedConnectionType = "ATSTART" if rel.RelatedConnectionType == "ATEND" else "ATEND"
|
||||
|
||||
layers1 = tool.Model.get_material_layer_parameters(element1)
|
||||
|
||||
@@ -81,6 +81,8 @@ def update_simple_openings(element, opening_width, opening_height):
|
||||
has_replaced_opening_representation = True
|
||||
|
||||
tool.Model.reload_body_representation(voided_objs)
|
||||
with bpy.context.temp_override(selected_objects=[tool.Ifc.get_object(f) for f in fillings]):
|
||||
bpy.ops.bim.recalculate_fill()
|
||||
|
||||
|
||||
def update_window_modifier_representation(context, obj):
|
||||
@@ -116,6 +118,14 @@ def update_window_modifier_representation(context, obj):
|
||||
}
|
||||
representation_data["panel_properties"].append(panel_data)
|
||||
|
||||
def get_active_representation_context(obj):
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
if active_representation:
|
||||
return active_representation.ContextOfItems
|
||||
return ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
previously_active_context = get_active_representation_context(obj)
|
||||
|
||||
# ELEVATION_VIEW representation
|
||||
profile = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Profile", "ELEVATION_VIEW")
|
||||
if profile:
|
||||
@@ -126,6 +136,7 @@ def update_window_modifier_representation(context, obj):
|
||||
tool.Model.replace_object_ifc_representation(profile, obj, elevation_representation)
|
||||
|
||||
# MODEL_VIEW representation
|
||||
# (Model/Body defined only BEFORE Plan/Body to prevent #2744)
|
||||
body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
representation_data["context"] = body
|
||||
model_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data)
|
||||
@@ -140,13 +151,20 @@ def update_window_modifier_representation(context, obj):
|
||||
)
|
||||
tool.Model.replace_object_ifc_representation(plan, obj, plan_representation)
|
||||
|
||||
# adding switch representation at the end instead of changing order of representations
|
||||
# to prevent #2744
|
||||
# adding switch representation at the end instead of changing order of representations
|
||||
# to prevent #2744
|
||||
if get_active_representation_context(obj) != previously_active_context:
|
||||
previously_active_representation = ifcopenshell.util.representation.get_representation(
|
||||
element,
|
||||
previously_active_context.ContextType,
|
||||
previously_active_context.ContextIdentifier,
|
||||
previously_active_context.TargetView,
|
||||
)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=model_representation,
|
||||
representation=previously_active_representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=True,
|
||||
|
||||
@@ -347,14 +347,21 @@ class BimToolUI:
|
||||
"IfcDuctSegment",
|
||||
"IfcPipeSegment",
|
||||
):
|
||||
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_F", "")
|
||||
add_layout_hotkey_operator(
|
||||
cls.layout, "Regen MEP", "S_G", bpy.ops.bim.regenerate_distribution_element.__doc__
|
||||
)
|
||||
if context.region.type != "TOOL_HEADER":
|
||||
cls.layout.operator("bim.mep_add_bend")
|
||||
cls.layout.operator("bim.mep_add_transition")
|
||||
cls.layout.operator("bim.mep_add_obstruction")
|
||||
cls.layout.operator("bim.mep_connect_elements")
|
||||
else:
|
||||
add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
|
||||
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
|
||||
add_layout_hotkey_operator(cls.layout, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
|
||||
row.operator("bim.extend_profile", icon="X", text="").join_type = ""
|
||||
|
||||
elif (
|
||||
@@ -427,6 +434,11 @@ class BimToolUI:
|
||||
add_layout_hotkey_operator(cls.layout, "Void", "A_O", "Toggle openings")
|
||||
add_layout_hotkey_operator(cls.layout, "Decomposition", "A_D", "Select decomposition")
|
||||
|
||||
cls.layout.separator()
|
||||
add_layout_hotkey_operator(
|
||||
cls.layout, "Calculate All Quantities", "S_Q", bpy.ops.bim.calculate_all_quantities.__doc__
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def draw_header_interface(cls):
|
||||
cls.draw_type_selection_interface()
|
||||
@@ -667,7 +679,15 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if self.active_material_usage == "LAYER2":
|
||||
bpy.ops.bim.recalculate_wall()
|
||||
elif self.active_material_usage == "PROFILE":
|
||||
bpy.ops.bim.recalculate_profile()
|
||||
if self.active_class in (
|
||||
"IfcCableCarrierSegment",
|
||||
"IfcCableSegment",
|
||||
"IfcDuctSegment",
|
||||
"IfcPipeSegment",
|
||||
):
|
||||
bpy.ops.bim.regenerate_distribution_element()
|
||||
else:
|
||||
bpy.ops.bim.recalculate_profile()
|
||||
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
||||
bpy.ops.bim.recalculate_fill()
|
||||
elif self.active_class in ("IfcSpace"):
|
||||
|
||||
@@ -71,10 +71,10 @@ mapper = {
|
||||
'Area' : "get_net_side_area",
|
||||
},
|
||||
'Qto_DuctSegmentBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_TransformerBaseQuantities' : {
|
||||
@@ -203,10 +203,10 @@ mapper = {
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_DuctFittingBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_UnitaryControlElementBaseQuantities' : {
|
||||
|
||||
@@ -31,6 +31,9 @@ NetFloorArea: it doesn't count the following entities contained in the spatial e
|
||||
NetVolume: like NetFloorArea, it doesn't count the following entities contained in the spatial entity: IfcColumn and IfcWall
|
||||
Also, the entire IfcColumn (or IfcWall) object volume is substracted, so it should be better to substract only the shared volume between IfcSpace and IfcColumn. Look at todo list
|
||||
|
||||
DUCT SEGMENTS AND DUCT FITTINGS
|
||||
The length is calculated like a beam. Also outer surface area. Gross cross section area, net cross section area and weight are not calculated right now because the parametrically cross section area seems filled (without hole).
|
||||
|
||||
WEIGHT
|
||||
The object weight is calculated by multiplying the object mass density with the object volume (net or gross).
|
||||
It's only calculated if the object material has a MassDensity property in the Pset_MaterialCommon.
|
||||
|
||||
@@ -223,7 +223,7 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
new.is_selected = enum in selected_enum_items
|
||||
else:
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
value = prop.NominalValue.wrappedValue
|
||||
value = prop.NominalValue.wrappedValue if prop.NominalValue else None
|
||||
elif prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||
value = prop[3]
|
||||
new_prop = self.props.properties.add()
|
||||
@@ -356,24 +356,7 @@ class AddPset(bpy.types.Operator, Operator):
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
pset_name = get_pset_props(context, self.obj, self.obj_type).pset_name
|
||||
if self.obj_type == "Object":
|
||||
if context.selected_objects:
|
||||
objects = [o.name for o in tool.Blender.get_selected_objects()]
|
||||
else:
|
||||
objects = [context.active_object.name]
|
||||
else:
|
||||
objects = [self.obj]
|
||||
for obj in objects:
|
||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
||||
if not ifc_definition_id:
|
||||
continue
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
if pset_name in blenderbim.bim.schema.ifc.psetqto.get_applicable_names(element.is_a(), pset_only=True):
|
||||
bpy.ops.bim.enable_pset_editing(
|
||||
pset_id=0, pset_name=pset_name, pset_type="PSET", obj=obj, obj_type=self.obj_type
|
||||
)
|
||||
core.add_pset(tool.Ifc, tool.Pset, tool.Blender, obj_name=self.obj, obj_type=self.obj_type)
|
||||
|
||||
|
||||
class AddQto(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -49,7 +49,6 @@ classes = (
|
||||
operator.ExpandResource,
|
||||
operator.GoToResource,
|
||||
operator.ImportResources,
|
||||
operator.LoadResourceProperties,
|
||||
operator.LoadResources,
|
||||
operator.RemoveResource,
|
||||
operator.RemoveResourceQuantity,
|
||||
|
||||
@@ -33,19 +33,6 @@ class LoadResources(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadResourceProperties(bpy.types.Operator):
|
||||
bl_idname = "bim.load_resource_properties"
|
||||
bl_label = "Load Resource Properties"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.load_resource_properties(
|
||||
tool.Resource, resource=tool.Ifc.get().by_id(self.resource) if self.resource else None
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddResource(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_resource"
|
||||
bl_label = "Add Resource"
|
||||
@@ -437,4 +424,4 @@ class CalculateResourceUsage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return False
|
||||
|
||||
def _execute(self, context):
|
||||
core.calculate_resource_usage(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(tool.Resource.get_highlighted_resource()))
|
||||
core.calculate_resource_usage(tool.Ifc, tool.Resource, resource=tool.Resource.get_highlighted_resource())
|
||||
|
||||
@@ -81,8 +81,7 @@ def update_active_resource_index(self, context):
|
||||
|
||||
|
||||
def updateResourceUsage(self, context):
|
||||
props = context.scene.BIMResourceProperties
|
||||
if not props.is_resource_update_enabled:
|
||||
if not context.scene.BIMResourceProperties.is_resource_update_enabled:
|
||||
return
|
||||
if not self.schedule_usage:
|
||||
return
|
||||
|
||||
@@ -20,7 +20,7 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ActivateIfcBuildingStoreyFilter,
|
||||
operator.ActivateContainerFilter,
|
||||
operator.ActivateIfcClassFilter,
|
||||
operator.AddFilter,
|
||||
operator.AddFilterGroup,
|
||||
@@ -58,6 +58,7 @@ classes = (
|
||||
prop.SearchQueryGroup,
|
||||
prop.IfcSelectorProperties,
|
||||
ui.BIM_PT_search,
|
||||
ui.BIM_PT_filter,
|
||||
ui.BIM_PT_colour_by_property,
|
||||
ui.BIM_PT_select_similar,
|
||||
ui.BIM_UL_colourscheme,
|
||||
|
||||
@@ -466,8 +466,8 @@ class ToggleFilterSelection(Operator):
|
||||
if props.filter_type == "CLASSES":
|
||||
for ifc_class in props.filter_classes:
|
||||
ifc_class.is_selected = self.selecting_actionbool
|
||||
elif props.filter_type == "BUILDINGSTOREYS":
|
||||
for building_storey in props.filter_building_storeys:
|
||||
elif props.filter_type == "CONTAINER":
|
||||
for building_storey in props.filter_container:
|
||||
building_storey.is_selected = self.selecting_actionbool
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -525,11 +525,11 @@ class ActivateIfcClassFilter(Operator):
|
||||
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
|
||||
|
||||
|
||||
class ActivateIfcBuildingStoreyFilter(Operator):
|
||||
class ActivateContainerFilter(Operator):
|
||||
"""Filter the current selection by Building Storey"""
|
||||
|
||||
bl_idname = "bim.activate_ifc_building_storey_filter"
|
||||
bl_label = "Filter by Building Storey"
|
||||
bl_idname = "bim.activate_ifc_container_filter"
|
||||
bl_label = "Filter by Container"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -540,27 +540,29 @@ class ActivateIfcBuildingStoreyFilter(Operator):
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
props.filter_building_storeys.clear()
|
||||
props.filter_container.clear()
|
||||
|
||||
ifc_building_storeys = {}
|
||||
containers = {}
|
||||
containers.setdefault("None", 0)
|
||||
for obj in context.selected_objects:
|
||||
storey = tool.Misc.get_object_storey(obj)
|
||||
if not storey:
|
||||
container = tool.Spatial.get_container(tool.Ifc.get_entity(obj))
|
||||
if not container:
|
||||
containers["None"] += 1
|
||||
continue
|
||||
ifc_building_storeys.setdefault(storey.Name, 0)
|
||||
ifc_building_storeys[storey.Name] += 1
|
||||
containers.setdefault(container.Name, 0)
|
||||
containers[container.Name] += 1
|
||||
|
||||
for name, total in dict(sorted(ifc_building_storeys.items())).items():
|
||||
new = props.filter_building_storeys.add()
|
||||
for name, total in dict(sorted(containers.items())).items():
|
||||
new = props.filter_container.add()
|
||||
new.name = name
|
||||
new.total = total
|
||||
|
||||
props.filter_type = "BUILDINGSTOREYS"
|
||||
props.filter_type = "CONTAINER"
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self, width=250)
|
||||
|
||||
def execute(self, context):
|
||||
bpy.context.scene.BIMSearchProperties.filter_building_storeys.clear()
|
||||
bpy.context.scene.BIMSearchProperties.filter_container.clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
@@ -568,12 +570,12 @@ class ActivateIfcBuildingStoreyFilter(Operator):
|
||||
"BIM_UL_ifc_building_storey_filter",
|
||||
"",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_building_storeys",
|
||||
"filter_container",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_building_storeys_index",
|
||||
"filter_container_index",
|
||||
rows=20
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_building_storeys) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_building_storeys),
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_container) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_container),
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
|
||||
@@ -70,15 +70,15 @@ def update_is_class_selected(self, context):
|
||||
new.obj = obj
|
||||
|
||||
|
||||
def update_is_level_selected(self, context):
|
||||
def update_is_container_selected(self, context):
|
||||
if self.is_selected:
|
||||
for obj in self.unselected_objects:
|
||||
obj.obj.select_set(True)
|
||||
self.unselected_objects.clear()
|
||||
else:
|
||||
for obj in context.selected_objects:
|
||||
level = tool.Misc.get_object_storey(obj)
|
||||
if level and level.Name == self.name:
|
||||
container = tool.Spatial.get_container(tool.Ifc.get_entity(obj))
|
||||
if (container and container.Name == self.name) or (not container and self.name== "None"):
|
||||
obj.select_set(False)
|
||||
new = self.unselected_objects.add()
|
||||
new.obj = obj
|
||||
@@ -93,7 +93,7 @@ class BIMFilterClasses(PropertyGroup):
|
||||
|
||||
class BIMFilterBuildingStoreys(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_selected: BoolProperty(name="Is Level Selected", default=True, update=update_is_level_selected)
|
||||
is_selected: BoolProperty(name="Is Level Selected", default=True, update=update_is_container_selected)
|
||||
total: IntProperty(name="Total")
|
||||
unselected_objects: CollectionProperty(type=ObjProperty, name="Unfiltered Objects")
|
||||
|
||||
@@ -140,8 +140,8 @@ class BIMSearchProperties(PropertyGroup):
|
||||
filter_type: StringProperty(name="Filter Type")
|
||||
filter_classes: CollectionProperty(type=BIMFilterClasses, name="Filter Classes")
|
||||
filter_classes_index: IntProperty(name="Filter Classes Index")
|
||||
filter_building_storeys: CollectionProperty(type=BIMFilterBuildingStoreys, name="Filter Level")
|
||||
filter_building_storeys_index: IntProperty(name="Filter Level Index")
|
||||
filter_container: CollectionProperty(type=BIMFilterBuildingStoreys, name="Filter Level")
|
||||
filter_container_index: IntProperty(name="Filter Level Index")
|
||||
|
||||
|
||||
def get_classes(self, ifc_product):
|
||||
|
||||
@@ -42,11 +42,21 @@ class BIM_PT_search(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.search", text="Search", icon="VIEWZOOM")
|
||||
|
||||
return # Temporary for now whilst searching is being upgraded.
|
||||
return
|
||||
|
||||
|
||||
class BIM_PT_filter(Panel):
|
||||
bl_label = "Filter Selection"
|
||||
bl_idname = "BIM_PT_filter"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_grouping_and_filtering"
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.activate_ifc_class_filter", icon="FILTER")
|
||||
row.operator("bim.activate_ifc_building_storey_filter", icon="FILTER")
|
||||
row.operator("bim.activate_ifc_container_filter", icon="FILTER")
|
||||
|
||||
|
||||
class BIM_PT_colour_by_property(Panel):
|
||||
|
||||
@@ -372,6 +372,7 @@ class EditTaskTime(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.edit_task_time(
|
||||
tool.Ifc,
|
||||
tool.Sequence,
|
||||
tool.Resource,
|
||||
task_time=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_time_id),
|
||||
)
|
||||
|
||||
@@ -508,7 +509,7 @@ class AssignProcess(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
if self.related_object_type == "RESOURCE":
|
||||
core.assign_resource(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
||||
core.assign_resource(tool.Ifc, tool.Sequence, tool.Resource, task=tool.Ifc.get().by_id(self.task))
|
||||
elif self.related_object_type == "PRODUCT":
|
||||
if self.related_object:
|
||||
core.assign_input_products(
|
||||
@@ -541,6 +542,7 @@ class UnassignProcess(bpy.types.Operator):
|
||||
core.unassign_resource(
|
||||
tool.Ifc,
|
||||
tool.Sequence,
|
||||
tool.Resource,
|
||||
task=tool.Ifc.get().by_id(self.task),
|
||||
resource=tool.Ifc.get().by_id(self.resource),
|
||||
)
|
||||
|
||||
@@ -224,10 +224,8 @@ def updateTaskDuration(self, context):
|
||||
else:
|
||||
task_time = tool.Ifc.run("sequence.add_task_time", task=task)
|
||||
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
|
||||
SequenceData.load()
|
||||
blenderbim.core.sequence.load_task_properties(tool.Sequence)
|
||||
bpy.ops.bim.load_task_properties()
|
||||
tool.Sequence.load_resources()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
|
||||
|
||||
def get_schedule_predefined_types(self, context):
|
||||
@@ -331,22 +329,24 @@ def get_saved_color_schemes(self, context):
|
||||
def updateAssignedResourceName(self, context):
|
||||
pass
|
||||
|
||||
|
||||
def updateAssignedResourceUsage(self, context):
|
||||
if not context.scene.BIMResourceProperties.is_resource_update_enabled:
|
||||
return
|
||||
if not self.schedule_usage:
|
||||
return
|
||||
resource = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if resource.Usage and resource.Usage.ScheduleUsage == self.schedule_usage:
|
||||
return
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={
|
||||
"ScheduleUsage": self.schedule_usage
|
||||
})
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={"ScheduleUsage": self.schedule_usage})
|
||||
tool.Sequence.load_task_properties()
|
||||
tool.Resource.load_resource_properties()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
blenderbim.bim.module.resource.data.refresh()
|
||||
blenderbim.bim.module.sequence.data.refresh()
|
||||
refresh_sequence_data()
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
|
||||
|
||||
class Task(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=updateTaskName)
|
||||
identification: StringProperty(name="Identification", update=updateTaskIdentification)
|
||||
|
||||
@@ -727,7 +727,11 @@ class BIM_PT_task_icom(Panel):
|
||||
if total_task_outputs:
|
||||
op = row2.operator("bim.unassign_product", icon="REMOVE", text="")
|
||||
op.task = task.ifc_definition_id
|
||||
if not context.selected_objects and self.props.active_task_output_index < total_task_outputs:
|
||||
if (
|
||||
total_task_outputs
|
||||
and not context.selected_objects
|
||||
and self.props.active_task_output_index < total_task_outputs
|
||||
):
|
||||
output_id = self.props.task_outputs[self.props.active_task_output_index].ifc_definition_id
|
||||
op.relating_product = output_id
|
||||
|
||||
@@ -770,6 +774,7 @@ class BIM_UL_task_resources(UIList):
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
row.prop(item, "schedule_usage", emboss=False, text="")
|
||||
|
||||
|
||||
class BIM_UL_animation_colors(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
from . import ui, prop, operator, decorator
|
||||
|
||||
classes = (
|
||||
operator.AddPort,
|
||||
@@ -51,7 +51,9 @@ classes = (
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMSystemProperties = bpy.props.PointerProperty(type=prop.BIMSystemProperties)
|
||||
bpy.app.handlers.load_post.append(decorator.toggle_decorations_on_load)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMSystemProperties
|
||||
bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load)
|
||||
|
||||
@@ -27,6 +27,7 @@ def refresh():
|
||||
SystemData.is_loaded = False
|
||||
ObjectSystemData.is_loaded = False
|
||||
PortData.is_loaded = False
|
||||
SystemDecorationData.is_loaded = False
|
||||
|
||||
|
||||
class SystemData:
|
||||
@@ -140,3 +141,19 @@ class PortData:
|
||||
|
||||
data.append((port, port_obj, connected_element))
|
||||
return data
|
||||
|
||||
|
||||
class SystemDecorationData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"decoration_data": cls.decoration_data(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def decoration_data(cls):
|
||||
return tool.System.get_decoration_data()
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>, @Andrej730
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import gpu
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
from math import sin, cos, radians
|
||||
from bpy.types import SpaceView3D
|
||||
from mathutils import Vector, Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
import ifcopenshell
|
||||
from blenderbim.bim.module.system.data import SystemDecorationData
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
ERROR_ELEMENTS_COLOR = (1, 0.2, 0.322, 1) # RED
|
||||
UNSPECIAL_ELEMENT_COLOR = (0.2, 0.2, 0.2, 1) # GREY
|
||||
|
||||
|
||||
def transparent_color(color, alpha=0.1):
|
||||
color = [i for i in color]
|
||||
color[3] = alpha
|
||||
return color
|
||||
|
||||
|
||||
@persistent
|
||||
def toggle_decorations_on_load(*args):
|
||||
if bpy.context.scene.BIMSystemProperties.should_draw_decorations:
|
||||
SystemDecorator.install(bpy.context)
|
||||
else:
|
||||
SystemDecorator.uninstall()
|
||||
|
||||
|
||||
class SystemDecorator:
|
||||
installed = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
|
||||
"""Note that operators that change mesh in `exit_edit_mode_callback` can freeze blender.
|
||||
The workaround is to move their code to function and use it for callback.
|
||||
|
||||
Example: https://devtalk.blender.org/t/calling-operator-that-saves-bmesh-freezes-blender-forever/28595"""
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.installed = SpaceView3D.draw_handler_add(
|
||||
handler, (context, get_custom_bmesh, draw_faces, exit_edit_mode_callback), "WINDOW", "POST_VIEW"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.installed = None
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_faces(self, bm, vertices_coords):
|
||||
"""mutates original bm (triangulates it)
|
||||
so the triangulation edges will be shown too
|
||||
"""
|
||||
traingulated_bm = bm
|
||||
bmesh.ops.triangulate(traingulated_bm, faces=traingulated_bm.faces)
|
||||
|
||||
face_indices = [[v.index for v in f.verts] for f in traingulated_bm.faces]
|
||||
faces_color = transparent_color(self.addon_prefs.decorator_color_special)
|
||||
self.draw_batch("TRIS", vertices_coords, faces_color, face_indices)
|
||||
|
||||
def __call__(self, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
|
||||
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||
selected_elements_color = self.addon_prefs.decorator_color_selected
|
||||
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
||||
special_elements_color = self.addon_prefs.decorator_color_special
|
||||
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
### Actually drawing
|
||||
all_vertices = []
|
||||
error_vertices = []
|
||||
selected_vertices = []
|
||||
unselected_vertices = []
|
||||
# special = associated with arcs/circles
|
||||
special_vertices = []
|
||||
special_vertex_indices = {}
|
||||
selected_edges = []
|
||||
unselected_edges = []
|
||||
arc_edges = []
|
||||
roof_angle_edges = []
|
||||
preview_edges = []
|
||||
|
||||
if not SystemDecorationData.is_loaded:
|
||||
SystemDecorationData.load()
|
||||
|
||||
decoration_data = SystemDecorationData.data["decoration_data"]
|
||||
all_vertices = decoration_data["all_vertices"]
|
||||
preview_edges = decoration_data["preview_edges"]
|
||||
special_vertices = decoration_data["special_vertices"]
|
||||
selected_edges = decoration_data["selected_edges"]
|
||||
selected_vertices = decoration_data["selected_vertices"]
|
||||
|
||||
### Actually drawing
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
self.draw_batch("LINES", all_vertices, transparent_color(unselected_elements_color), unselected_edges)
|
||||
self.draw_batch("LINES", all_vertices, selected_elements_color, selected_edges)
|
||||
self.draw_batch("LINES", all_vertices, UNSPECIAL_ELEMENT_COLOR, arc_edges)
|
||||
self.draw_batch("LINES", all_vertices, special_elements_color, preview_edges)
|
||||
self.draw_batch("LINES", all_vertices, special_elements_color, roof_angle_edges)
|
||||
|
||||
self.draw_batch("POINTS", unselected_vertices, transparent_color(unselected_elements_color, 0.5))
|
||||
self.draw_batch("POINTS", error_vertices, ERROR_ELEMENTS_COLOR)
|
||||
self.draw_batch("POINTS", special_vertices, special_elements_color)
|
||||
self.draw_batch("POINTS", selected_vertices, selected_elements_color)
|
||||
@@ -219,12 +219,15 @@ class DisconnectPort(bpy.types.Operator, Operator):
|
||||
class MEPConnectElements(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.mep_connect_elements"
|
||||
bl_label = "Connect MEP Elements"
|
||||
bl_description = "Connects two selected elements if they have ports with matching location"
|
||||
bl_description = "Connects two selected elements by their closest located ports and adjusts them"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_objects) == 2
|
||||
if not len(context.selected_objects) == 2:
|
||||
cls.poll_message_set("Need to select 2 objects.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
obj1 = context.active_object
|
||||
@@ -233,23 +236,32 @@ class MEPConnectElements(bpy.types.Operator, Operator):
|
||||
el1 = tool.Ifc.get_entity(obj1)
|
||||
el2 = tool.Ifc.get_entity(obj2)
|
||||
|
||||
connected_elements = ifcopenshell.util.system.get_connected_to(el1)
|
||||
connected_elements += ifcopenshell.util.system.get_connected_to(el2)
|
||||
|
||||
if el2 in connected_elements:
|
||||
self.report({"ERROR"}, "MEP elements are already connected to each other.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
obj1_ports = [p for p in tool.System.get_ports(el1) if not tool.System.get_connected_port(p)]
|
||||
obj2_ports = [p for p in tool.System.get_ports(el2) if not tool.System.get_connected_port(p)]
|
||||
|
||||
if not obj1_ports or not obj2_ports:
|
||||
self.report({"ERROR"}, "Couldn't find free ports to connect.")
|
||||
return
|
||||
return {"CANCELLED"}
|
||||
|
||||
ports_distance = dict()
|
||||
for port1 in obj1_ports:
|
||||
port1_location = tool.Model.get_element_matrix(port1).translation
|
||||
for port2 in obj2_ports:
|
||||
port2_location = tool.Model.get_element_matrix(port2).translation
|
||||
if tool.Cad.are_vectors_equal(port1_location, port2_location):
|
||||
core.connect_port(tool.Ifc, port1, port2)
|
||||
return {"FINISHED"}
|
||||
distance = (port1_location - port2_location).length
|
||||
ports_distance[(port1, port2)] = distance
|
||||
|
||||
self.report({"ERROR"}, "Couldn't find any matching ports to connect.")
|
||||
return {"CANCELLED"}
|
||||
closest_ports = min(ports_distance, key=lambda x: ports_distance[x])
|
||||
core.connect_port(tool.Ifc, *closest_ports)
|
||||
bpy.ops.bim.regenerate_distribution_element()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetFlowDirection(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.module.system.data import SystemData
|
||||
import blenderbim.bim.module.system.decorator as decorator
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -44,6 +45,14 @@ class System(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
def toggle_decorations(self, context):
|
||||
toggle = self.should_draw_decorations
|
||||
if toggle:
|
||||
decorator.SystemDecorator.install(context)
|
||||
else:
|
||||
decorator.SystemDecorator.uninstall()
|
||||
|
||||
|
||||
class BIMSystemProperties(PropertyGroup):
|
||||
system_attributes: CollectionProperty(name="System Attributes", type=Attribute)
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
@@ -52,3 +61,6 @@ class BIMSystemProperties(PropertyGroup):
|
||||
active_system_index: IntProperty(name="Active System Index")
|
||||
active_system_id: IntProperty(name="Active System Id")
|
||||
system_class: EnumProperty(items=get_system_class, name="Class")
|
||||
should_draw_decorations: BoolProperty(
|
||||
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
|
||||
)
|
||||
|
||||
@@ -101,6 +101,10 @@ class BIM_PT_object_systems(Panel):
|
||||
if not ObjectSystemData.is_loaded:
|
||||
ObjectSystemData.load()
|
||||
self.props = context.scene.BIMSystemProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_draw_decorations")
|
||||
|
||||
if self.props.is_editing:
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
@@ -131,7 +131,8 @@ class SelectRequirement(bpy.types.Operator):
|
||||
props.failed_entities.clear()
|
||||
for e in failed_entities:
|
||||
new_entity = props.failed_entities.add()
|
||||
new_entity.element = e["element"]
|
||||
new_entity.ifc_id = e["id"]
|
||||
new_entity.element = f'{e["class"]}/{e["name"]}'
|
||||
new_entity.reason = e["reason"]
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -41,8 +41,9 @@ class Specification(PropertyGroup):
|
||||
|
||||
|
||||
class FailedEntities(PropertyGroup):
|
||||
reason: StringProperty(name="Reason")
|
||||
ifc_id: IntProperty(name="IFC ID")
|
||||
element: StringProperty(name="Element")
|
||||
reason: StringProperty(name="Reason")
|
||||
|
||||
|
||||
class IfcTesterProperties(PropertyGroup):
|
||||
|
||||
@@ -115,17 +115,9 @@ class BIM_UL_tester_failed_entities(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
props = context.scene.IfcTesterProperties
|
||||
if item:
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = tool.Ifc.get()
|
||||
ifc_id = int(item.element[1 : item.element.find("=")])
|
||||
entity = ifc_file.by_id(ifc_id)
|
||||
report_entity = f"[#{ifc_id}][{entity.is_a()}] {entity.Name}"
|
||||
else:
|
||||
report_entity = item.element
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text=report_entity)
|
||||
row.label(text=item.element)
|
||||
row.label(text=item.reason)
|
||||
if props.should_load_from_memory:
|
||||
op = row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.ifc_id = entity.id()
|
||||
op.ifc_id = item.ifc_id
|
||||
|
||||
@@ -42,7 +42,7 @@ from math import radians
|
||||
|
||||
class SetTab(bpy.types.Operator):
|
||||
bl_idname = "bim.set_tab"
|
||||
# NOTE: bl_label is set to empty string intentionally
|
||||
# NOTE: bl_label is set to empty string intentionally
|
||||
# to avoid showing the operator's name in the tooltips, see #3704
|
||||
bl_label = ""
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
@@ -197,21 +197,52 @@ class FileAssociate(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if platform.system() == "Linux":
|
||||
if platform.system() in ("Linux", "Windows"):
|
||||
return True
|
||||
cls.poll_message_set("Option available only on Linux.")
|
||||
# TODO Windows and Darwin
|
||||
cls.poll_message_set("Option available only on Windows & Linux.")
|
||||
# TODO Darwin
|
||||
# https://stackoverflow.com/questions/1082889/how-to-change-filetype-association-in-the-registry
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
# NOTE: really weird thing on windows that typing this command in cmd works
|
||||
# when even if you create .bat with the command below and run it as administrator it won't
|
||||
# Haven't found a workaround yet to automate process completely.
|
||||
command = "ASSOC .IFC=BLENDERBIM"
|
||||
self.layout.label(text="On the next step to create file association ")
|
||||
self.layout.label(text="the system console will be opened ")
|
||||
self.layout.label(text=f"and you will be asked to type command")
|
||||
self.layout.label(text=f'"{command}"')
|
||||
self.layout.label(text="to create an association.")
|
||||
|
||||
def invoke(self, context, event):
|
||||
if platform.system() == "Windows":
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
else:
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
src_dir = os.path.join(os.path.dirname(__file__), "../libs/desktop")
|
||||
binary_path = bpy.app.binary_path
|
||||
if platform.system() == "Linux":
|
||||
destdir = os.path.join(os.environ["HOME"], ".local")
|
||||
self.install_desktop_linux(src_dir=src_dir, destdir=destdir, binary_path=binary_path)
|
||||
elif platform.system() == "Windows":
|
||||
self.install_desktop_windows(src_dir, binary_path)
|
||||
self.report({"INFO"}, "Associations established.")
|
||||
return {"FINISHED"}
|
||||
|
||||
def install_desktop_windows(self, src_dir, binary_path):
|
||||
# very important to clear this regitstry key before creating new association
|
||||
# tried to do the regitsry change from powershell/cmd - but even admin rights are not enough
|
||||
# this is why we're using .reg
|
||||
reg_change_path = os.path.join(src_dir, "windows_bbim_association.reg")
|
||||
subprocess.run(["cmd", "/c", reg_change_path])
|
||||
|
||||
ps_script_path = os.path.join(src_dir, "windows_bbim_association.ps1")
|
||||
# NOTE: call powershell with RunAs to get admin rights from user
|
||||
subprocess.run(["powershell", "-file", ps_script_path, binary_path], shell=True)
|
||||
|
||||
def install_desktop_linux(self, src_dir=None, destdir="/tmp", binary_path="/usr/bin/blender"):
|
||||
"""Creates linux file assocations and launcher icon"""
|
||||
|
||||
@@ -272,17 +303,29 @@ class FileUnassociate(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if platform.system() == "Linux":
|
||||
if platform.system() in ("Linux", "Windows"):
|
||||
return True
|
||||
cls.poll_message_set("Option available only on Linux.")
|
||||
cls.poll_message_set("Option available only on Windows & Linux.")
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
if platform.system() == "Linux":
|
||||
destdir = os.path.join(os.environ["HOME"], ".local")
|
||||
self.uninstall_desktop_linux(destdir=destdir)
|
||||
elif platform.system() == "Windows":
|
||||
self.uninstall_desktop_windows()
|
||||
return {"FINISHED"}
|
||||
|
||||
def uninstall_desktop_windows(self):
|
||||
# NOTE: call powershell with RunAs to get admin rights from user
|
||||
cmd = [
|
||||
"powershell",
|
||||
"-Command",
|
||||
"Start-Process -Verb RunAs -Wait cmd -ArgumentList '/c reg delete HKCR\\BLENDERBIM /f'",
|
||||
]
|
||||
subprocess.run(cmd, check=True)
|
||||
self.report({"INFO"}, "Association removed.")
|
||||
|
||||
def uninstall_desktop_linux(self, destdir="/tmp"):
|
||||
"""Removes linux file assocations and launcher icon"""
|
||||
for rel_path in (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
def add_cost_schedule(ifc, name, predefined_type,object_type):
|
||||
def add_cost_schedule(ifc, name, predefined_type, object_type):
|
||||
ifc.run("cost.add_cost_schedule", name=name, predefined_type=predefined_type, object_type=object_type)
|
||||
|
||||
|
||||
@@ -112,70 +112,89 @@ def assign_cost_item_quantity(ifc, cost, cost_item, related_object_type, prop_na
|
||||
ifc.run("cost.assign_cost_item_quantity", cost_item=cost_item, products=products, prop_name=prop_name)
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type=related_object_type)
|
||||
|
||||
|
||||
def load_cost_item_quantities(cost):
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def load_cost_item_element_quantities(cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT")
|
||||
|
||||
|
||||
def load_cost_item_task_quantities(cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PROCESS")
|
||||
|
||||
|
||||
def load_cost_item_resource_quantities(cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE")
|
||||
|
||||
|
||||
def assign_cost_value(ifc, cost_item, cost_rate):
|
||||
ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate)
|
||||
|
||||
|
||||
def load_schedule_of_rates(cost, schedule_of_rates):
|
||||
cost.load_schedule_of_rates_tree(schedule_of_rates)
|
||||
|
||||
|
||||
def unassign_cost_item_quantity(ifc, cost, cost_item, products):
|
||||
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def enable_editing_cost_item_quantities(cost, cost_item):
|
||||
cost.enable_editing_cost_item_quantities(cost_item)
|
||||
|
||||
|
||||
def enable_editing_cost_item_values(cost, cost_item):
|
||||
cost.enable_editing_cost_item_values(cost_item)
|
||||
|
||||
|
||||
def add_cost_item_quantity(ifc, cost_item, ifc_class):
|
||||
ifc.run("cost.add_cost_item_quantity", cost_item=cost_item, ifc_class=ifc_class)
|
||||
|
||||
|
||||
def remove_cost_item_quantity(ifc, cost_item, physical_quantity):
|
||||
ifc.run("cost.remove_cost_item_quantity", cost_item=cost_item, physical_quantity=physical_quantity)
|
||||
|
||||
|
||||
|
||||
def enable_editing_cost_item_quantity(cost, physical_quantity):
|
||||
cost.load_cost_item_quantity_attributes(physical_quantity)
|
||||
cost.enable_editing_cost_item_quantity(physical_quantity)
|
||||
|
||||
|
||||
def disable_editing_cost_item_quantity(cost):
|
||||
cost.disable_editing_cost_item_quantity()
|
||||
|
||||
|
||||
def edit_cost_item_quantity(ifc, cost, physical_quantity):
|
||||
attributes = cost.get_cost_item_quantity_attributes()
|
||||
ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
|
||||
cost.disable_editing_cost_item_quantity()
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def add_cost_value(ifc, cost, parent, cost_type, cost_category):
|
||||
value = ifc.run("cost.add_cost_value", parent=parent)
|
||||
ifc.run(
|
||||
"cost.edit_cost_value",
|
||||
cost_value=value,
|
||||
attributes=cost.get_attributes_for_cost_value(cost_type, cost_category))
|
||||
attributes=cost.get_attributes_for_cost_value(cost_type, cost_category),
|
||||
)
|
||||
|
||||
|
||||
def remove_cost_value(ifc, parent, cost_value):
|
||||
ifc.run("cost.remove_cost_value", parent=parent, cost_value=cost_value)
|
||||
|
||||
|
||||
def enable_editing_cost_item_value(cost, cost_value):
|
||||
cost.load_cost_item_value_attributes(cost_value)
|
||||
cost.enable_editing_cost_item_value(cost_value)
|
||||
|
||||
|
||||
def disable_editing_cost_item_value(cost):
|
||||
cost.disable_editing_cost_item_value()
|
||||
|
||||
@@ -195,7 +214,7 @@ def edit_cost_value(ifc, cost, cost_value):
|
||||
attributes = cost.get_cost_value_attributes()
|
||||
ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes)
|
||||
cost.disable_editing_cost_item_value()
|
||||
#cost.load_cost_item_values(cost.get_highlighted_cost_item())
|
||||
# cost.load_cost_item_values(cost.get_highlighted_cost_item())
|
||||
|
||||
|
||||
def copy_cost_item_values(ifc, cost, source, destination):
|
||||
@@ -275,11 +294,12 @@ def change_parent_cost_item(ifc, cost, new_parent):
|
||||
cost_item = cost.get_active_cost_item()
|
||||
if cost_item and cost.is_root_cost_item(cost_item):
|
||||
return "Cannot change root cost item"
|
||||
if cost_item :
|
||||
if cost_item:
|
||||
ifc.run("nest.change_nest", item=cost_item, new_parent=new_parent)
|
||||
cost.disable_editing_cost_item_parent()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def copy_cost_item(ifc, cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
if cost_item:
|
||||
@@ -287,9 +307,10 @@ def copy_cost_item(ifc, cost):
|
||||
cost.disable_editing_cost_item_parent()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def add_currency(ifc, cost):
|
||||
unit = ifc.run("unit.add_monetary_unit")
|
||||
attributes = cost.get_currency_attributes()
|
||||
ifc.run("unit.edit_monetary_unit", unit=unit, attributes=attributes)
|
||||
ifc.run("unit.assign_unit", units=[unit])
|
||||
return unit
|
||||
return unit
|
||||
|
||||
@@ -28,11 +28,3 @@ def resize_to_storey(misc, obj=None, total_storeys=None):
|
||||
misc.move_object_to_elevation(obj, misc.get_storey_elevation_in_si(storey))
|
||||
misc.scale_object_to_height(obj, height)
|
||||
misc.mark_object_as_edited(obj)
|
||||
|
||||
|
||||
def split_along_edge(misc, cutter=None, objs=None):
|
||||
new_objs = misc.split_objects_with_cutter(objs, cutter)
|
||||
for obj in new_objs:
|
||||
misc.run_root_copy_class(obj=obj)
|
||||
for obj in objs:
|
||||
misc.mark_object_as_edited(obj)
|
||||
|
||||
@@ -43,3 +43,4 @@ def add_pset(ifc, pset, blender, obj_name, obj_type):
|
||||
ifc_pset = pset.get_element_pset(element, pset_name)
|
||||
if not ifc_pset:
|
||||
ifc.run("pset.add_pset", product=element, name=pset_name)
|
||||
pset.enable_pset_editing(pset_id=0, pset_name=pset_name, pset_type="PSET",obj=obj_name, obj_type=obj_type)
|
||||
@@ -21,15 +21,11 @@
|
||||
|
||||
def load_resources(resource):
|
||||
resource.load_resources()
|
||||
resource.load_resource_properties()
|
||||
|
||||
|
||||
def add_resource(tool_ifc, resource_tool, ifc_class, parent_resource=None):
|
||||
tool_ifc.run("resource.add_resource", ifc_class=ifc_class, parent_resource=parent_resource)
|
||||
load_resources(resource_tool)
|
||||
|
||||
|
||||
def load_resource_properties(resource_tool, resource=None):
|
||||
resource_tool.load_resource_properties()
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def disable_editing_resource(resource_tool):
|
||||
@@ -54,7 +50,7 @@ def edit_resource(ifc, resource_tool, resource):
|
||||
|
||||
def remove_resource(ifc, resource_tool, resource=None):
|
||||
ifc.run("resource.remove_resource", resource=resource)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def enable_editing_resource_time(ifc_tool, resource_tool, resource):
|
||||
@@ -82,7 +78,7 @@ def calculate_resource_work(ifc, resource_tool, resource):
|
||||
nested_resources = resource_tool.get_nested_resources(resource)
|
||||
for nested_resource in nested_resources or []:
|
||||
ifc.run("resource.calculate_resource_work", resource=nested_resource)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def enable_editing_resource_costs(resource_tool, resource):
|
||||
@@ -143,17 +139,17 @@ def edit_resource_quantity(resource_tool, ifc, physical_quantity=None):
|
||||
|
||||
def import_resources(resource_tool, file_path):
|
||||
resource_tool.import_resources(file_path)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def expand_resource(resource_tool, resource):
|
||||
resource_tool.expand_resource(resource)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def contract_resource(resource_tool, resource):
|
||||
resource_tool.contract_resource(resource)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def assign_resource(ifc, spatial, resource=None, products=None):
|
||||
@@ -213,9 +209,11 @@ def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
||||
ifc.run("constraint.unassign_constraint", product=resource, constraint=constraint)
|
||||
ifc.run("constraint.remove_constraint", constraint=constraint)
|
||||
|
||||
|
||||
def go_to_resource(resource_tool, resource):
|
||||
resource_tool.go_to_resource(resource)
|
||||
|
||||
|
||||
def calculate_resource_usage(ifc, resource_tool, resource):
|
||||
ifc.run("resource.calculate_resource_usage", resource=resource)
|
||||
load_resources(resource_tool)
|
||||
resource_tool.load_resources()
|
||||
|
||||
@@ -186,14 +186,14 @@ def enable_editing_task_time(ifc, sequence, task=None):
|
||||
sequence.enable_editing_task_time(task)
|
||||
|
||||
|
||||
def edit_task_time(ifc, sequence, task_time=None):
|
||||
def edit_task_time(ifc, sequence, resource, task_time=None):
|
||||
attributes = sequence.get_task_time_attributes()
|
||||
# TODO: nasty loop goes on when calendar props are messed up
|
||||
ifc.run("sequence.edit_task_time", task_time=task_time, attributes=attributes)
|
||||
task = sequence.get_active_task()
|
||||
sequence.load_task_properties(task=task)
|
||||
sequence.disable_editing_task_time()
|
||||
sequence.load_resources()
|
||||
resource.load_resource_properties()
|
||||
|
||||
|
||||
def assign_predecessor(ifc, sequence, task=None):
|
||||
@@ -248,8 +248,8 @@ def unassign_input_products(ifc, sequence, spatial, task=None, products=None):
|
||||
sequence.load_task_inputs(inputs)
|
||||
|
||||
|
||||
def assign_resource(ifc, sequence, task=None):
|
||||
resource = sequence.get_selected_resource()
|
||||
def assign_resource(ifc, sequence, resource_tool, task=None):
|
||||
resource = resource_tool.get_highlighted_resource()
|
||||
sub_resource = ifc.run(
|
||||
"resource.add_resource",
|
||||
parent_resource=resource,
|
||||
@@ -257,17 +257,15 @@ def assign_resource(ifc, sequence, task=None):
|
||||
name="{}/{}".format(resource.Name or "Unnamed", task.Name or ""),
|
||||
)
|
||||
ifc.run("sequence.assign_process", relating_process=task, related_object=sub_resource)
|
||||
resources = sequence.get_task_resources(task)
|
||||
sequence.load_task_resources(resources)
|
||||
sequence.load_resources()
|
||||
sequence.load_task_resources(task)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def unassign_resource(ifc, sequence, task=None, resource=None):
|
||||
def unassign_resource(ifc, sequence, resource_tool, task=None, resource=None):
|
||||
ifc.run("sequence.unassign_process", relating_process=task, related_object=resource)
|
||||
ifc.run("resource.remove_resource", resource=resource)
|
||||
resources = sequence.get_task_resources(task)
|
||||
sequence.load_task_resources(resources)
|
||||
sequence.load_resources()
|
||||
sequence.load_task_resources(task)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def remove_work_calendar(ifc, work_calendar=None):
|
||||
|
||||
@@ -726,7 +726,6 @@ class Sequence:
|
||||
def get_recurrence_pattern_attributes(cls, recurrence_pattern): pass
|
||||
def get_recurrence_pattern_times(cls): pass
|
||||
def get_rel_sequence_attributes(cls): pass
|
||||
def get_selected_resource(cls): pass
|
||||
def get_start_date(cls): pass
|
||||
def get_task_attribute_value(cls, attribute_name): pass
|
||||
def get_task_attributes(cls): pass
|
||||
@@ -758,7 +757,7 @@ class Sequence:
|
||||
def load_task_inputs(cls, inputs): pass
|
||||
def load_task_outputs(cls, outputs): pass
|
||||
def load_task_properties(cls, task): pass
|
||||
def load_task_resources(cls,resources): pass
|
||||
def load_task_resources(cls, task): pass
|
||||
def load_task_time_attributes(cls, task_time): pass
|
||||
def load_task_tree(cls, work_schedule): pass
|
||||
def load_work_calendar_attributes(cls, work_calendar): pass
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
param(
|
||||
[string]$BlenderPath = "BLENDER_EXE"
|
||||
)
|
||||
|
||||
Start-Process cmd -ArgumentList `
|
||||
"/k ", `
|
||||
"ASSOC .IFC=", `
|
||||
"&", `
|
||||
"FTYPE BLENDERBIM=""$BlenderPath"" --python-expr ""import bpy; bpy.ops.bim.load_project(filepath=r'%1')""",
|
||||
"&", `
|
||||
"echo. & echo. & echo To create an association between .IFC files and BlenderBIM", `
|
||||
"&", `
|
||||
"echo type the command below & echo.", `
|
||||
"&", `
|
||||
"echo ASSOC .IFC=BLENDERBIM & echo." `
|
||||
-Verb RunAs
|
||||
@@ -168,7 +168,8 @@ class Blender:
|
||||
"""
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
region = next(region for region in area.regions if region.type == "WINDOW")
|
||||
context_override = {"area": area, "region": region}
|
||||
space = next(space for space in area.spaces if space.type == "VIEW_3D")
|
||||
context_override = {"area": area, "region": region, "space_data": space}
|
||||
return context_override
|
||||
|
||||
@classmethod
|
||||
@@ -483,6 +484,13 @@ class Blender:
|
||||
if obj:
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def lock_transform(cls, obj, lock_state=True):
|
||||
for prop in ("lock_location", "lock_rotation", "lock_scale"):
|
||||
attr = getattr(obj, prop)
|
||||
for axis_idx in range(3):
|
||||
attr[axis_idx] = lock_state
|
||||
|
||||
class Modifier:
|
||||
@classmethod
|
||||
def is_eligible_for_railing_modifier(cls, obj):
|
||||
@@ -559,10 +567,7 @@ class Blender:
|
||||
modifier_data = list(cls.get_modifiers_data(parent_element))[item]
|
||||
children = cls.get_children_objects(modifier_data)
|
||||
for child_obj in children:
|
||||
for prop in ("lock_location", "lock_rotation", "lock_scale"):
|
||||
attr = getattr(child_obj, prop)
|
||||
for axis_idx in range(3):
|
||||
attr[axis_idx] = lock_state
|
||||
Blender.lock_transform(child_obj, lock_state)
|
||||
|
||||
@classmethod
|
||||
def remove_constraints(cls, parent_element):
|
||||
|
||||
@@ -234,7 +234,6 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
|
||||
@classmethod
|
||||
def get_products(cls, related_object_type=None):
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
if related_object_type == "PRODUCT":
|
||||
products = tool.Spatial.get_selected_products()
|
||||
elif related_object_type == "PROCESS":
|
||||
|
||||
@@ -249,17 +249,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
obj_data = obj.data
|
||||
bpy.data.objects.remove(obj)
|
||||
if obj_data and obj_data.users == 0: # in case we have drawing element types
|
||||
cls.remove_object_data(obj_data)
|
||||
|
||||
@classmethod
|
||||
def remove_object_data(cls, data):
|
||||
"""also removes all related objects"""
|
||||
if isinstance(data, bpy.types.Camera):
|
||||
bpy.data.cameras.remove(data)
|
||||
elif isinstance(data, bpy.types.Mesh):
|
||||
bpy.data.meshes.remove(data)
|
||||
elif isinstance(data, bpy.types.Curve):
|
||||
bpy.data.curves.remove(data)
|
||||
tool.Blender.remove_data_block(obj_data)
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
@@ -445,7 +435,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def get_drawing_target_view(cls, drawing):
|
||||
return ifcopenshell.util.element.get_psets(drawing)["EPset_Drawing"].get("TargetView", "MODEL_VIEW")
|
||||
return ifcopenshell.util.element.get_psets(drawing).get("EPset_Drawing", {}).get("TargetView", "MODEL_VIEW")
|
||||
|
||||
@classmethod
|
||||
def get_group_elements(cls, group):
|
||||
|
||||
@@ -115,6 +115,12 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
for port in ifcopenshell.util.system.get_ports(element):
|
||||
blenderbim.core.system.remove_port(tool.Ifc, tool.System, port=port)
|
||||
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
|
||||
|
||||
if isinstance(obj.data, bpy.types.Mesh) and not tool.Ifc.get_entity_by_id(
|
||||
obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
tool.Blender.remove_data_block(obj.data)
|
||||
|
||||
if is_spatial:
|
||||
blenderbim.core.spatial.load_container_manager(tool.Spatial)
|
||||
try:
|
||||
|
||||
@@ -86,6 +86,15 @@ class Ifc(blenderbim.core.tool.Ifc):
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_entity_by_id(cls, entity_id):
|
||||
"""useful to check whether entity_id is still exists in IFC"""
|
||||
ifc_file = tool.Ifc.get()
|
||||
try:
|
||||
return ifc_file.by_id(entity_id)
|
||||
except RuntimeError:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_object(cls, element):
|
||||
return IfcStore.get_element(element.id())
|
||||
|
||||
@@ -540,11 +540,56 @@ class Model(blenderbim.core.tool.Model):
|
||||
return axes
|
||||
|
||||
@classmethod
|
||||
def regenerate_array(cls, parent, data, keep_objs=False):
|
||||
tool.Blender.Modifier.Array.remove_constraints(tool.Ifc.get_entity(parent))
|
||||
def handle_array_on_copied_element(cls, element, array_data=None):
|
||||
"""if no `array_data` is provided then an array will be removed from the element"""
|
||||
|
||||
if array_data is None:
|
||||
array_pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
if not array_pset:
|
||||
return
|
||||
|
||||
array_pset_data = array_pset["Data"]
|
||||
array_pset = tool.Ifc.get().by_id(array_pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=array_pset)
|
||||
|
||||
# remove constraints
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not array_pset_data: # skip array parents
|
||||
constraint = next((c for c in obj.constraints if c.type == "CHILD_OF"), None)
|
||||
if constraint:
|
||||
matrix = obj.matrix_world.copy()
|
||||
obj.constraints.remove(constraint)
|
||||
# keep the matrix before the constraint
|
||||
# otherwise object will jump to some previous position
|
||||
obj.matrix_world = matrix
|
||||
tool.Blender.lock_transform(obj, False)
|
||||
|
||||
else:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
array_pset = tool.Pset.get_element_pset(element, "BBIM_Array")
|
||||
default_data = '[{"children": []}]'
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset",
|
||||
tool.Ifc.get(),
|
||||
pset=array_pset,
|
||||
properties={"Parent": element.GlobalId, "Data": default_data},
|
||||
)
|
||||
|
||||
tool.Model.regenerate_array(obj, array_data)
|
||||
|
||||
json_data = json.dumps(array_data)
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=array_pset, properties={"Data": json_data})
|
||||
|
||||
for i in range(len(array_data)):
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, i, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
@classmethod
|
||||
def regenerate_array(cls, parent_obj, data, keep_objs=False):
|
||||
tool.Blender.Modifier.Array.remove_constraints(tool.Ifc.get_entity(parent_obj))
|
||||
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
obj_stack = [parent]
|
||||
obj_stack = [parent_obj]
|
||||
|
||||
for array in data:
|
||||
if array["sync_children"]:
|
||||
|
||||
@@ -66,3 +66,10 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
if value is not None:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def enable_pset_editing(cls, pset_id=None, pset_name=None, pset_type=None, obj=None, obj_type=None):
|
||||
#TODO REFACTOR ONCE toll/CORE functions are available
|
||||
bpy.ops.bim.enable_pset_editing(
|
||||
pset_id=0, pset_name=tool.Pset.get_pset_name(obj, obj_type), pset_type="PSET", obj=obj, obj_type=obj_type
|
||||
)
|
||||
@@ -61,6 +61,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
continue
|
||||
create_new_resource_li(resource, 0)
|
||||
cls.load_productivity_data()
|
||||
cls.load_resource_properties()
|
||||
props.is_resource_update_enabled = True
|
||||
props.is_editing = True
|
||||
|
||||
@@ -425,7 +426,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
contracted_resources.remove(ancestor)
|
||||
bpy.context.scene.BIMResourceProperties.contracted_resources = json.dumps(contracted_resources)
|
||||
cls.load_resources()
|
||||
cls.load_resource_properties()
|
||||
|
||||
|
||||
resource_props = bpy.context.scene.BIMResourceTreeProperties
|
||||
expanded_resources = [item.ifc_definition_id for item in resource_props.resources]
|
||||
|
||||
@@ -254,14 +254,6 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return None
|
||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id)
|
||||
|
||||
@classmethod
|
||||
def get_selected_resource(cls):
|
||||
if bpy.context.scene.BIMResourceTreeProperties.resources:
|
||||
selected_resource_id = bpy.context.scene.BIMResourceTreeProperties.resources[
|
||||
bpy.context.scene.BIMResourceProperties.active_resource_index
|
||||
].ifc_definition_id
|
||||
return tool.Ifc.get().by_id(selected_resource_id)
|
||||
|
||||
@classmethod
|
||||
def expand_task(cls, task):
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
@@ -409,19 +401,17 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return blenderbim.bim.helper.export_attributes(props.task_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def load_task_resources(cls, resources):
|
||||
def load_task_resources(cls, task):
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
rprops = bpy.context.scene.BIMResourceProperties
|
||||
props.task_resources.clear()
|
||||
for resource in resources or []:
|
||||
rprops.is_resource_update_enabled = False
|
||||
for resource in cls.get_task_resources(task) or []:
|
||||
new = props.task_resources.add()
|
||||
new.ifc_definition_id = resource.id()
|
||||
new.name = resource.Name or "Unnamed"
|
||||
new.schedule_usage = resource.Usage.ScheduleUsage or 0 if resource.Usage else 0
|
||||
|
||||
@classmethod
|
||||
def load_resources(cls):
|
||||
blenderbim.core.resource.load_resources(tool.Resource)
|
||||
cls.refresh_task_resources
|
||||
rprops.is_resource_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def get_task_inputs(cls, task):
|
||||
@@ -447,6 +437,8 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
|
||||
@classmethod
|
||||
def get_task_resources(cls, task):
|
||||
if not task:
|
||||
return
|
||||
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_resources
|
||||
return ifcopenshell.util.sequence.get_task_resources(task, is_deep)
|
||||
|
||||
@@ -1664,17 +1656,16 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return
|
||||
inputs = cls.get_task_inputs(task)
|
||||
outputs = cls.get_task_outputs(task)
|
||||
resources = cls.get_task_resources(task)
|
||||
cls.load_task_inputs(inputs)
|
||||
cls.load_task_outputs(outputs)
|
||||
cls.load_task_resources(resources)
|
||||
cls.load_task_resources(task)
|
||||
|
||||
@classmethod
|
||||
def refresh_task_resources(cls):
|
||||
task = cls.get_highlighted_task()
|
||||
if not task:
|
||||
return
|
||||
cls.load_task_resources(cls.get_task_resources(task))
|
||||
cls.load_task_resources(task)
|
||||
|
||||
@classmethod
|
||||
def has_duration(cls, task):
|
||||
|
||||
@@ -182,3 +182,80 @@ class System(blenderbim.core.tool.System):
|
||||
@classmethod
|
||||
def set_active_system(cls, system):
|
||||
bpy.context.scene.BIMSystemProperties.active_system_id = system.id()
|
||||
|
||||
@classmethod
|
||||
def get_decoration_data(cls):
|
||||
all_vertices = []
|
||||
preview_edges = []
|
||||
special_vertices = []
|
||||
selected_edges = []
|
||||
selected_vertices = []
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
start_vert_i = 0
|
||||
|
||||
if bpy.context.active_object and (active_element := tool.Ifc.get_entity(bpy.context.active_object)):
|
||||
selected_elements = cls.get_connected_elements(active_element)
|
||||
else:
|
||||
selected_elements = set()
|
||||
|
||||
# TODO: get only objects visible in viewport
|
||||
objects = set(bpy.data.objects) - set(bpy.data.collections["Types"].objects)
|
||||
for obj in objects:
|
||||
start_vert_i = len(all_vertices)
|
||||
if obj.hide_get():
|
||||
continue
|
||||
|
||||
if not isinstance(obj.data, bpy.types.Mesh):
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
if not cls.is_mep_element(element):
|
||||
continue
|
||||
|
||||
ports = tool.System.get_ports(element)
|
||||
|
||||
for port in ports:
|
||||
position = tool.Model.get_element_matrix(port).translation * si_conversion
|
||||
all_vertices.append(position)
|
||||
|
||||
verts = range(start_vert_i, start_vert_i + len(ports))
|
||||
edges = [(i, i + 1) for i in range(start_vert_i, start_vert_i + len(ports) - 1)]
|
||||
if element in selected_elements:
|
||||
selected_vertices.extend(verts)
|
||||
selected_edges.extend(edges)
|
||||
else:
|
||||
special_vertices.extend(verts)
|
||||
preview_edges.extend(edges)
|
||||
|
||||
decoration_data = {
|
||||
"all_vertices": all_vertices,
|
||||
"preview_edges": preview_edges,
|
||||
"special_vertices": [all_vertices[i] for i in special_vertices],
|
||||
"selected_edges": selected_edges,
|
||||
"selected_vertices": [all_vertices[i] for i in selected_vertices],
|
||||
}
|
||||
return decoration_data
|
||||
|
||||
@classmethod
|
||||
def get_connected_elements(cls, element, elements=None):
|
||||
if elements is None:
|
||||
elements = set((element,))
|
||||
|
||||
connected_elements = ifcopenshell.util.system.get_connected_from(element)
|
||||
connected_elements += ifcopenshell.util.system.get_connected_to(element)
|
||||
|
||||
for element in connected_elements:
|
||||
if element in elements:
|
||||
continue
|
||||
elements.add(element)
|
||||
cls.get_connected_elements(element, elements)
|
||||
|
||||
return elements
|
||||
|
||||
@classmethod
|
||||
def is_mep_element(cls, element):
|
||||
return element.is_a("IfcFlowSegment") or element.is_a("IfcFlowFitting")
|
||||
|
||||
@@ -61,6 +61,7 @@ Scenario: Add aggregate
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -73,6 +74,7 @@ Scenario: Add aggregate - with the aggregate inheriting the existing spatial col
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is placed in the collection "IfcBuildingStorey/My Storey"
|
||||
@@ -86,6 +88,7 @@ Scenario: Add aggregate - add a nested aggregate
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is placed in the collection "IfcBuildingStorey/My Storey"
|
||||
|
||||
@@ -38,10 +38,12 @@ Scenario: Copy attribute to selected
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -357,6 +357,7 @@ Scenario: Assign cost item quantity - count based
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
|
||||
@@ -371,6 +372,7 @@ Scenario: Assign cost item quantity - quantity based
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -388,6 +390,7 @@ Scenario: Unassign cost item quantity - selection based
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -404,6 +407,7 @@ Scenario: Unassign cost item quantity - explicit object
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -421,6 +425,7 @@ Scenario: Select cost item products
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -437,6 +442,7 @@ Scenario: Select Cost Schedule Products
|
||||
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -89,6 +89,7 @@ Scenario: Assign document
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When I press "bim.assign_document(document={reference})"
|
||||
@@ -105,6 +106,7 @@ Scenario: Unassign document
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.assign_document(document={reference})"
|
||||
|
||||
@@ -5,6 +5,7 @@ Scenario: Duplicate drawing
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
@@ -17,6 +18,7 @@ Scenario: Create drawing
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
@@ -31,6 +33,7 @@ Scenario: Create drawing after deleting a duplicated object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
@@ -52,6 +55,7 @@ Scenario: Remove drawing
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
@@ -65,6 +69,7 @@ Scenario: Remove drawing - via object deletion
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
@@ -79,6 +84,7 @@ Scenario: Remove drawing - deleting active drawing
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||
|
||||
@@ -5,6 +5,7 @@ Scenario: Edit object placement
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -15,6 +16,7 @@ Scenario: Add representation
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -48,6 +50,7 @@ Scenario: Add representation - add a representation with a scale factor applied
|
||||
And the object "Cube" is selected
|
||||
When the object "Cube" is scaled to "2"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
Then the object "IfcWall/Cube" has no scale
|
||||
@@ -60,6 +63,7 @@ Scenario: Add representation - add a representation with a scale factor removed
|
||||
And the object "Cube" is selected
|
||||
When the object "Cube" is scaled to "2"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
Then the object "IfcWall/Cube" has no scale
|
||||
@@ -69,6 +73,7 @@ Scenario: Switch representation
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
|
||||
@@ -79,6 +84,7 @@ Scenario: Switch representation - current edited representation is updated prior
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.ContextType == 'Plan' and c.ContextIdentifier=='Annotation'][0].id()"
|
||||
@@ -96,6 +102,7 @@ Scenario: Switch representation - current edited representation is discarded if
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the object "IfcWall/Cube" is scaled to "2"
|
||||
@@ -111,6 +118,7 @@ Scenario: Switch representation - existing Blender modifiers must be purged
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add an array modifier
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
|
||||
@@ -122,6 +130,7 @@ Scenario: Remove representation - remove an active representation
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add an array modifier
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
|
||||
@@ -133,6 +142,7 @@ Scenario: Remove representation - remove an unloaded representation
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add an array modifier
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[1].id()"
|
||||
@@ -182,6 +192,7 @@ Scenario: Update representation - updating a tessellation
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.update_representation(obj='IfcWall/Cube')"
|
||||
@@ -192,6 +203,7 @@ Scenario: Update representation - updating a layered extrusion
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -219,6 +231,7 @@ Scenario: Update representation - updating a profiled extrusion
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -242,6 +255,7 @@ Scenario: Get representation IFC parameters
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.update_representation(ifc_representation_class='IfcExtrudedAreaSolid/IfcRectangleProfileDef')"
|
||||
@@ -252,6 +266,7 @@ Scenario: Copy representation
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
@@ -273,6 +288,7 @@ Scenario: Override delete - with active IFC data
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -297,6 +313,7 @@ Scenario: Override duplicate move - with active IFC data
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -315,6 +332,7 @@ Scenario: Override duplicate move - copying a coloured representation
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -347,6 +365,7 @@ Scenario: Override duplicate move - copying a layered extrusion
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -376,6 +395,7 @@ Scenario: Override duplicate move - copying a profiled extrusion
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -412,6 +432,7 @@ Scenario: Override duplicate move linked - with active IFC data
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -440,6 +461,7 @@ Scenario: Override paste buffer - with active IFC data
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -113,6 +113,7 @@ Scenario: Assign library reference
|
||||
And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -128,6 +129,7 @@ Scenario: Unassign library reference
|
||||
And the variable "reference" is "{ifc}.by_type('IfcLibraryReference')[-1].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -72,6 +72,7 @@ Scenario: Assign material - single material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -83,6 +84,7 @@ Scenario: Unassign material - single material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -95,6 +97,7 @@ Scenario: Enable editing assigned material - single material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -107,6 +110,7 @@ Scenario: Disable editing assigned material - single material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -120,6 +124,7 @@ Scenario: Edit assigned material - single material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -160,6 +165,7 @@ Scenario: Unassign material - removing inherited material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
|
||||
@@ -186,6 +192,7 @@ Scenario: Enable editing assigned material - material layer set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -203,6 +210,7 @@ Scenario: Disable editing assigned material - material layer set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -221,6 +229,7 @@ Scenario: Edit assigned material - material layer set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -266,6 +275,7 @@ Scenario: Enable editing assigned material - material profile set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -283,6 +293,7 @@ Scenario: Disable editing assigned material - material profile set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -301,6 +312,7 @@ Scenario: Edit assigned material - material profile set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -320,6 +332,7 @@ Scenario: Assign material - material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -331,6 +344,7 @@ Scenario: Unassign material - material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -343,6 +357,7 @@ Scenario: Enable editing assigned material - material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -355,6 +370,7 @@ Scenario: Disable editing assigned material - material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -368,6 +384,7 @@ Scenario: Edit assigned material - material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -419,6 +436,7 @@ Scenario: Add material set layer
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -438,6 +456,7 @@ Scenario: Remove material set layer
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ Scenario: Resize to storey
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -40,6 +41,7 @@ Scenario: Split along edge
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a plane of size "4" at "0,0,0"
|
||||
|
||||
@@ -284,6 +284,7 @@ Scenario: Assign actor
|
||||
And I press "bim.add_actor"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "actor" is "{ifc}.by_type('IfcActor')[0].id()"
|
||||
@@ -297,6 +298,7 @@ Scenario: Unassign actor
|
||||
And I press "bim.add_actor"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the variable "actor" is "{ifc}.by_type('IfcActor')[0].id()"
|
||||
|
||||
@@ -436,6 +436,7 @@ Scenario: Export IFC - with changed object scale synchronised
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -450,6 +451,7 @@ Scenario: Export IFC - with changed style colour synchronised
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -464,6 +466,7 @@ Scenario: Export IFC - with changed style element synchronised
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -5,6 +5,7 @@ Scenario: Add pset - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -16,11 +17,13 @@ Scenario: Add pset - multiple objects
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -33,6 +36,7 @@ Scenario: Enable pset editing - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -48,6 +52,7 @@ Scenario: Enable pset editing - material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -127,6 +132,7 @@ Scenario: Disable pset editing - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -139,6 +145,7 @@ Scenario: Disable pset editing - material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -204,6 +211,7 @@ Scenario: Edit pset - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -216,6 +224,7 @@ Scenario: Edit qto - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -229,6 +238,7 @@ Scenario: Edit pset - material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
@@ -294,10 +304,12 @@ Scenario: Copy property to selected - copy property
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -312,6 +324,7 @@ Scenario: Remove pset - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -327,11 +340,13 @@ Scenario: Remove pset - multiple objects
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -274,6 +274,7 @@ Scenario: Calculate Resource Work
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -307,6 +308,7 @@ Scenario: Assign Resource
|
||||
And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -322,6 +324,7 @@ Scenario: UnAssign Resource
|
||||
And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
|
||||
@@ -5,6 +5,7 @@ Scenario: Reassign class
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I press "object.duplicate_move"
|
||||
@@ -20,6 +21,7 @@ Scenario: Unlink object
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I add a material
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material"
|
||||
@@ -32,6 +34,7 @@ Scenario: Copy class
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When I press "bim.copy_class(obj='IfcWall/Cube')"
|
||||
@@ -41,6 +44,7 @@ Scenario: Assign a class to a cube
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
When the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
Then the object "IfcWall/Cube" is an "IfcWall"
|
||||
@@ -87,6 +91,7 @@ Scenario: Assign a class to a cube in a collection
|
||||
And I add a cube
|
||||
When the object "Cube" is selected
|
||||
And the object "Cube" is placed in the collection "IfcBuildingStorey/My Storey"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
Then the object "IfcWall/Cube" is contained in "My Storey"
|
||||
@@ -95,6 +100,7 @@ Scenario: Copy a wall
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
When the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I duplicate the selected objects
|
||||
|
||||
@@ -5,6 +5,7 @@ Scenario: Select all walls
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a new item to "scene.IfcSelectorProperties.groups"
|
||||
|
||||
@@ -349,6 +349,8 @@ Scenario: Animate the construction of a wall
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -385,6 +387,7 @@ Scenario: Animate the demolition of a wall
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -424,6 +427,7 @@ Scenario: Animate the operation of a wall
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -458,6 +462,7 @@ Scenario: Animate the movement of a wall
|
||||
And I add a cube
|
||||
And I rename the object "Cube" to "ToObject"
|
||||
And the object "ToObject" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/ToObject" is selected
|
||||
@@ -508,6 +513,7 @@ Scenario: Animate the consumption of a wall
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -549,6 +555,7 @@ Scenario: Clear Previous Animation
|
||||
And I press "bim.edit_task_time"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -758,6 +765,7 @@ Scenario: Assign Product Output
|
||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
@@ -773,10 +781,11 @@ Scenario: Assign Product Input
|
||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.assign_process(task={task}, related_object_type='PRODUCT')"
|
||||
And I press "bim.assign_process(task={task},related_object=0, related_object_type='PRODUCT')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Select Assigned Outputs
|
||||
@@ -788,9 +797,9 @@ Scenario: Select Assigned Outputs
|
||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I press "object.select_all(action='DESELECT')"
|
||||
And I press "bim.assign_class()"
|
||||
And I press "bim.assign_product(task={task})"
|
||||
When I press "bim.select_task_related_products(task={task})"
|
||||
Then nothing happens
|
||||
@@ -804,9 +813,10 @@ Scenario: Select Assigned Inputs
|
||||
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I press "object.select_all(action='DESELECT')"
|
||||
And I press "bim.assign_class()"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.assign_process(task={task}, related_object_type='PRODUCT')"
|
||||
When I press "bim.select_task_related_products(task={task})"
|
||||
Then nothing happens
|
||||
@@ -849,6 +859,7 @@ Scenario: Add Animation Camera
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_animation_camera"
|
||||
|
||||
@@ -21,7 +21,9 @@ Scenario: Assign container
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
And the variable "site" is "tool.Ifc.get().by_type('IfcSite')[0].id()"
|
||||
@@ -32,7 +34,9 @@ Scenario: Copy to container
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
When I set "scene.BIMSpatialProperties.containers[0].is_selected" to "True"
|
||||
@@ -43,7 +47,9 @@ Scenario: Reference structure
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
When I set "scene.BIMSpatialProperties.containers[0].is_selected" to "True"
|
||||
@@ -54,7 +60,9 @@ Scenario: Dereference structure
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
When I set "scene.BIMSpatialProperties.containers[0].is_selected" to "True"
|
||||
@@ -66,7 +74,9 @@ Scenario: Select container
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
And the variable "site" is "tool.Ifc.get().by_type('IfcSite')[0].id()"
|
||||
@@ -78,7 +88,9 @@ Scenario: Select similar container
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
And the variable "site" is "tool.Ifc.get().by_type('IfcSite')[0].id()"
|
||||
|
||||
@@ -24,6 +24,7 @@ Scenario: Enable editing type
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
When I press "bim.enable_editing_type"
|
||||
@@ -33,6 +34,7 @@ Scenario: Disable editing type
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.enable_editing_type"
|
||||
@@ -43,6 +45,7 @@ Scenario: Assign type - assign to an empty type
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -58,6 +61,7 @@ Scenario: Assign type - assign to a type with representation maps
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
@@ -73,6 +77,7 @@ Scenario: Assign type - assign to a type with a material layer set, which automa
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -94,6 +99,7 @@ Scenario: Assign type - assign to a different type with a material layer set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -132,6 +138,7 @@ Scenario: Assign type - assign to a type with a material profile set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -155,6 +162,7 @@ Scenario: Select type objects
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
@@ -172,10 +180,12 @@ Scenario: Select similar type
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add an empty
|
||||
|
||||
@@ -6,6 +6,7 @@ Scenario: Add an opening
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
@@ -19,6 +20,7 @@ Scenario: Add an opening using the BIM tool
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -32,6 +34,7 @@ Scenario: Show openings
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -47,6 +50,7 @@ Scenario: Hide openings
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -62,6 +66,7 @@ Scenario: Edit openings
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -79,6 +84,7 @@ Scenario: Add an opening to Element B with a void that already voids Element A
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
|
||||
@@ -105,6 +111,7 @@ Scenario: Remove opening
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -123,6 +130,7 @@ Scenario: Remove opening - using deletion
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
@@ -140,6 +148,7 @@ Scenario: Remove opening - indirectly by deleting its building element
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_potential_opening"
|
||||
|
||||
@@ -39,11 +39,3 @@ class TestResizeToStorey:
|
||||
misc.get_object_storey("obj").should_be_called().will_return("storey")
|
||||
misc.get_storey_height_in_si("storey", 1).should_be_called().will_return(None)
|
||||
subject.resize_to_storey(misc, obj="obj", total_storeys=1)
|
||||
|
||||
|
||||
class TestSplitAlongEdge:
|
||||
def test_run(self, misc):
|
||||
misc.split_objects_with_cutter(["obj"], "cutter").should_be_called().will_return(["new_obj"])
|
||||
misc.run_root_copy_class(obj="new_obj").should_be_called()
|
||||
misc.mark_object_as_edited("obj").should_be_called()
|
||||
subject.split_along_edge(misc, cutter="cutter", objs=["obj"])
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import numpy as np
|
||||
from test.bim.bootstrap import NewFile
|
||||
from blenderbim.tool.model import Model as subject
|
||||
|
||||
@@ -50,3 +51,31 @@ class TestGenerateOccurrenceName(NewFile):
|
||||
bpy.context.scene.BIMModelProperties.occurrence_name_style = "CUSTOM"
|
||||
bpy.context.scene.BIMModelProperties.occurrence_name_function = '"Foobar"'
|
||||
assert subject.generate_occurrence_name(element_type, "IfcWall") == "Foobar"
|
||||
|
||||
class TestGetManualBooleans(NewFile):
|
||||
def test_run(self):
|
||||
assert isinstance(subject(), blenderbim.core.tool.Model)
|
||||
|
||||
def test_len_returned_boolean(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
length = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT")
|
||||
ifcopenshell.api.run("unit.assign_unit", ifc, units=[length])
|
||||
ifcopenshell.api.run("unit.assign_unit", ifc)
|
||||
element = ifc.createIfcColumn()
|
||||
hea100 = ifc.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
model3d = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
body = ifcopenshell.api.run("context.add_context", ifc,context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
|
||||
representation = ifcopenshell.api.run("geometry.add_profile_representation", ifc, context=body, profile=hea100, depth=5)
|
||||
ifcopenshell.api.run("geometry.assign_representation", ifc, product=element, representation=representation)
|
||||
matrix = np.eye(4)
|
||||
matrix = ifcopenshell.util.placement.rotation(45,"X") @ matrix
|
||||
matrix[:,3][0:3] = (0, 0, 3)
|
||||
matrix = matrix.tolist()
|
||||
ifcopenshell.api.run("geometry.add_boolean", ifc, representation = representation, type = "IfcHalfSpaceSolid", matrix = matrix)
|
||||
assert len(subject.get_manual_booleans(element)) == 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user