Compare commits

...

43 Commits

Author SHA1 Message Date
Ryan Schultz 0f835ca739 removed unnecessary code 2024-05-28 18:43:13 -05:00
Ryan Schultz 87442f549f Update src/blenderbim/blenderbim/bim/module/model/prop.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-28 11:02:23 -05:00
Ryan Schultz bf27e41275 Update src/blenderbim/blenderbim/bim/module/model/array.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-28 11:02:09 -05:00
Ryan Schultz 66cf06c990 Update src/blenderbim/blenderbim/bim/module/model/array.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-27 16:10:20 -05:00
Ryan Schultz 5757a62b6f Update src/blenderbim/blenderbim/bim/module/model/prop.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-27 16:09:28 -05:00
Ryan Schultz e2552e3d46 Update src/blenderbim/blenderbim/bim/module/model/prop.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-27 16:09:22 -05:00
Ryan Schultz 1b91540c96 Update src/blenderbim/blenderbim/bim/module/model/prop.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-27 16:09:15 -05:00
Ryan Schultz 274b12e5a0 Update src/blenderbim/blenderbim/bim/module/model/prop.py
Co-authored-by: Bruno Perdigão <57102715+brunoperdigao@users.noreply.github.com>
2024-05-27 16:09:01 -05:00
Ryan Schultz 496ba6f22d fix #3802 - Copy array attributes from another array 2024-05-26 13:56:52 -05:00
Dion Moult b7737cb6d3 Fix #4655. profiles is now available as a keyword in the selector syntax. 2024-05-11 22:57:16 +10:00
Dion Moult 55026ca996 Fix #4616. You can now create a new representation based off any other object. This makes it easy to custom model whatever you want as a starting point. 2024-05-11 22:29:39 +10:00
Dion Moult 3fa573e3c1 Fix #4615. Report to user if they are attempting to create an incompatible context-representation combination. 2024-05-11 20:48:38 +10:00
Dion Moult 00ae6809a9 See #4657. Fallback to copying cache if moving cache fails due to permission error. 2024-05-11 19:59:39 +10:00
Dion Moult c3fbb11093 See #4657. Restrict annotation psets to their particular predefined type. 2024-05-11 19:38:22 +10:00
Kurt Battisti a0d27c3f1c Make Whole status UI message clearer (#4658)
Replace UI text "No Aggregate" with "No Whole relation defined" to avoid misinterpretation when instance is a whole with parts.
2024-05-11 19:04:35 +10:00
Dion Moult 811a908cc3 More segfault fixes in test suite (maybe order matters only on Linux?) 2024-05-11 19:01:57 +10:00
Dion Moult 94e5baf48a Fix #4647. Better error reporting if the user tries to activate a drawing that isn't available. 2024-05-11 19:01:21 +10:00
Dion Moult cd2570cdbb Fix #4649. Bug where editing property set template enumerations didn't clean up properly. 2024-05-11 15:18:18 +10:00
Dion Moult 6df6553935 Notify user if duration parsing not available in util.date 2024-05-11 15:14:39 +10:00
Dion Moult 8e6c372a4f Fix ability to run tests and segfaulting test 2024-05-11 15:00:30 +10:00
Andrej730 371e11de9b root.remove_product to consider IfcRelConnectsPortToElement 2024-05-10 19:18:09 +05:00
Andrej730 d2a708fcc4 fix bug in ifc2x3 tests bootstrap
it was creating new applications and users every time `ifcopenshell.api.owner.settings.get_user` or `ifcopenshell.api.owner.settings.get_application` was called
2024-05-10 19:18:09 +05:00
Andrej730 845d772a08 move sequence.get_related_products to util module 2024-05-10 19:18:09 +05:00
Andrej730 1e630ede08 ifc2x3 tests 2024-05-10 19:18:09 +05:00
Andrej730 c50d1ea2fe typing 2024-05-10 19:18:08 +05:00
Dion Moult e7eb00eaa3 See #4652. Fix bug where auto-detection of a false origin could be incorrect
We were incorrectly multiplying a shape matrix in SI units with a vertex in project units. We also didn't do a final check whether or not that final resultant coordinate was far away or not (for example, origin and vertex can cancel each other out)
2024-05-10 23:28:37 +10:00
Dion Moult cd6eac3c8f See #4652. Fix bug where using create_mesh won't work if you pass it a triangulation. 2024-05-10 22:39:56 +10:00
Dion Moult 69e21da60c Minor naming changes in IfcFM to be more true to IFC jargon 2024-05-10 22:13:39 +10:00
Dion Moult 57b905862a Fix #4650. When the bbim build hotfixes the bot build, it should replace, not copy over. 2024-05-10 18:20:41 +10:00
Dion Moult 51d3a2b77b Whoops 2024-05-10 16:16:43 +10:00
Andrej730 b2920996be remove deprecated api method type.get_related_objects 2024-05-09 18:15:49 +05:00
Andrej730 bdc5a7a01b more descriptive errors creating entities - setting incorrect attributes
example:

```
ifc_file = ifcopenshell.file(schema="IFC4")

entity = ifc_file.create_entity("IfcRoot", "xxx", None, None, None, None)
entity = ifc_file.create_entity("IfcRoot", Location=None)
```
2024-05-09 18:15:49 +05:00
Andrej730 1c6a9e2c49 more descriptive errors settings incorrect data type to attributes
Example:
```
ifc_file = ifcopenshell.file(schema="IFC2X3")
entity = ifc_file.create_entity("IfcCostSchedule")

entity.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
```
2024-05-09 18:15:49 +05:00
Andrej730 8ffb77551f more tests for ifc2x3 2024-05-09 18:15:49 +05:00
Andrej730 2d35869c41 typing 2024-05-09 18:00:17 +05:00
Andrej730 359dab573b document.remove_information to support ifc2x3 #4636 2024-05-09 14:02:10 +05:00
Andrej730 6e2edbf101 explicitly specify available imported symbols
Fix LSP warnings like ""entity_instance" is not exported from module "ifcopenshell"PylancereportPrivateImportUsage" for `ifcopenshell.file`, `ifcopenshell.entity_instance` etc.

After adding py.typed in 722201a1a it's now a typed library and all imported symbols in modules are considered private by default and should be added to `__all__` if they are supposed to be generally available from imported module.

See https://github.com/microsoft/pyright/blob/main/docs/typed-libraries.md#library-interface
2024-05-09 13:35:29 +05:00
Dion Moult 4c8f93d737 Add error catching to model loading and let users hide error dialog 2024-05-09 16:33:05 +10:00
Dion Moult 2789bc565a Write troubleshooting documentation 2024-05-09 16:21:24 +10:00
Dion Moult a1e0b8858e Track last actions and friendlier error reporting when something breaks 2024-05-09 13:37:57 +10:00
Dion Moult f075a6178b Experimental code for serialising API settings 2024-05-09 13:29:26 +10:00
Dion Moult 5fa385e1e0 Allow for having wildcard listeners in the api 2024-05-09 13:27:08 +10:00
Dion Moult b57141a80c New type of IfcTester report which doesn't require audit to be run to see a spreadsheet view of requirements 2024-05-09 10:46:57 +10:00
281 changed files with 2105 additions and 952 deletions
+4 -2
View File
@@ -151,8 +151,10 @@ endif
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.7.0.zip
cd dist/working && unzip v0.7.0.zip
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/util/
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/api/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/util dist/blenderbim/libs/site/packages/ifcopenshell/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/*.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
# Provides bcf functionality
+32 -13
View File
@@ -24,6 +24,7 @@ import traceback
import subprocess
import webbrowser
import addon_utils
from collections import deque
bl_info = {
"name": "BlenderBIM",
@@ -38,6 +39,7 @@ bl_info = {
}
last_error = None
last_actions: deque = deque(maxlen=10)
def get_debug_info():
@@ -60,10 +62,22 @@ def get_debug_info():
"processor": platform.processor(),
"blender_version": bpy.app.version_string,
"blenderbim_version": version,
"last_actions": last_actions,
"last_error": last_error,
}
def format_debug_info(info: dict):
last_actions = ""
for action in info["last_actions"]:
last_actions += f"\n# {action['type']}: {action['name']}"
if settings := action.get("settings"):
last_actions += f"\n>>> {settings}"
info["last_actions"] = last_actions
text = "\n".join(f"{k}: {v}" for k, v in info.items())
return text.strip()
if sys.modules.get("bpy", None):
# Process *.pth in /libs/site/packages to setup globally importable modules
# This is 3 levels deep as required by the static RPATH of ../../ from dependencies taken from Anaconda
@@ -72,6 +86,18 @@ if sys.modules.get("bpy", None):
try:
import blenderbim.bim
import ifcopenshell.api
def log_api(usecase_path, ifc_file, settings):
last_actions.append(
{
"type": "ifcopenshell.api",
"name": usecase_path,
"settings": ifcopenshell.api.serialise_settings(settings),
}
)
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
def register():
blenderbim.bim.register()
@@ -83,7 +109,7 @@ if sys.modules.get("bpy", None):
last_error = traceback.format_exc()
print(last_error)
print(get_debug_info())
print(format_debug_info(get_debug_info()))
print("\nFATAL ERROR: Unable to load the BlenderBIM Add-on")
class BIM_PT_fatal_error(bpy.types.Panel):
@@ -105,7 +131,7 @@ if sys.modules.get("bpy", None):
box.label(text=f"Python {py} BBIM {info['blenderbim_version']}", icon="SCRIPTPLUGINS")
layout.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
op = layout.operator("bim.open_uri", text="How Can I Fix This?")
op.uri = "https://docs.blenderbim.org/users/installation.html#faq"
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html#installation-issues"
class OpenUri(bpy.types.Operator):
bl_idname = "bim.open_uri"
@@ -122,21 +148,14 @@ if sys.modules.get("bpy", None):
bl_description = "Copies debugging information to your clipboard for use in bugreports"
def execute(self, context):
info = get_debug_info()
# Format it in a readable way
text = "\n".join(f"{k}: {v}" for k, v in info.items())
print(text)
info = format_debug_info(get_debug_info())
if platform.system() == "Windows":
command = "echo | set /p nul=" + text.strip()
command = "echo | set /p nul=" + info
elif platform.system() == "Darwin": # for MacOS
command = 'printf "' + text.strip().replace("\n", "\\n").replace('"', "") + '" | pbcopy'
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
else: # Linux
command = (
'printf "'
+ text.strip().replace("\n", "\\n").replace('"', "")
+ '" | xclip -selection clipboard'
)
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
subprocess.run(command, shell=True, check=True)
return {"FINISHED"}
@@ -103,6 +103,7 @@ classes = [
operator.BIM_OT_select_object,
operator.BIM_OT_show_description,
operator.ClippingPlaneCutWithCappings,
operator.CloseError,
operator.EditBlenderCollection,
operator.FileAssociate,
operator.FileUnassociate,
@@ -5,7 +5,7 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22,#23));
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22,#23));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -5,20 +5,20 @@ FILE_NAME('Psets_BBIM_Annotation.ifc','2020-01-01T00:00:00',(),(),'Psets_BBIM_An
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4));
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4));
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separarated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#6,#7));
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/BATTING,IfcTypeProduct',(#6,#7));
#6=IFCSIMPLEPROPERTYTEMPLATE('0t2LEesGT1QRQtrIZUAR8L',$,'Thickness','Batting thickness',.P_SINGLEVALUE.,'IfcPositiveLengthMeasure',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#9,#10,#11,#12,#13));
#8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/SECTION,IfcTypeProduct',(#9,#10,#11,#12,#13));
#9=IFCSIMPLEPROPERTYTEMPLATE('2a_9s8spHDc9dZHtgg71XL',$,'ShowStartArrow','Display start arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3i6SH_GbT7zhKea$wVE56A',$,'StartArrowSymbol','Custom symbol for the start of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1DtsPn5a9FG8$zXHDDMavY',$,'ShowEndArrow','Display end arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('2$6U0mLI9AiPRWeBabdY3u',$,'EndArrowSymbol','Custom symbol for the end of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('1naFqntIL7igCY7hCaE7kq',$,'HasConnectedSectionLine','Connect or disconnect section markers with line (by default = True).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#14=IFCPROPERTYSETTEMPLATE('3V8oZ8YRD3_O7uR5vcUleS',$,'BBIM_Documentation','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#15,#16,#17,#18,#19,#20,#21,#22,#23));
#14=IFCPROPERTYSETTEMPLATE('3V8oZ8YRD3_O7uR5vcUleS',$,'BBIM_Documentation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcProject',(#15,#16,#17,#18,#19,#20,#21,#22,#23));
#15=IFCSIMPLEPROPERTYTEMPLATE('0ulAhgk3v9qfGlDILauR6J',$,'SheetsDir','Default sheets directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#16=IFCSIMPLEPROPERTYTEMPLATE('2yvlVKiQXASucfH40deCvu',$,'LayoutsDir','Default layouts directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#17=IFCSIMPLEPROPERTYTEMPLATE('2kXZqXicL3jRwsOnLM_0ho',$,'TitleblocksDir','Default titleblocks directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -28,7 +28,7 @@ DATA;
#21=IFCSIMPLEPROPERTYTEMPLATE('1UDakJ5_f7kBhggNSW4$h5',$,'SymbolsPath','Default symbols SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#22=IFCSIMPLEPROPERTYTEMPLATE('0d53LEtgLDQxnv__NfgH7i',$,'PatternsPath','Default patterns SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#23=IFCSIMPLEPROPERTYTEMPLATE('26qFNMv7nCHgU6Jd7Anga5',$,'ShadingStylesPath','Default shading styles',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#24=IFCPROPERTYSETTEMPLATE('0I9merLinF5Ap$aZwaclgm',$,'BBIM_Dimension','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#25,#26,#27,#28));
#24=IFCPROPERTYSETTEMPLATE('0I9merLinF5Ap$aZwaclgm',$,'BBIM_Dimension','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/DIMENSION,IfcTypeProduct',(#25,#26,#27,#28));
#25=IFCSIMPLEPROPERTYTEMPLATE('1rL2AbQsXD8RbpoWH5pYOV',$,'ShowDescriptionOnly','Hide the measurement values and show only annotation description',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#26=IFCSIMPLEPROPERTYTEMPLATE('0SVyOfB0rC2xNfdRYf3XvY',$,'SuppressZeroInches','Suppress 0 inch values in dimension annotation text (for example: 12'' - 0" -> 12'')',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#27=IFCSIMPLEPROPERTYTEMPLATE('2bUmj458PBqPAtUoI3MXsb',$,'TextPrefix','Text to add before annotation measurement value',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
+26 -12
View File
@@ -19,12 +19,15 @@
import os
import bpy
import uuid
import shutil
import hashlib
import zipfile
import tempfile
import traceback
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper
import blenderbim
import blenderbim.bim.handler
import blenderbim.tool as tool
from pathlib import Path
@@ -37,19 +40,19 @@ IFC_CONNECTED_TYPE = Union[bpy.types.Material, bpy.types.Object]
class IfcStore:
path: str = ""
file: ifcopenshell.file = None
schema: ifcopenshell.ifcopenshell_wrapper.schema_definition = None
cache: ifcopenshell.ifcopenshell_wrapper.HdfSerializer = None
cache_path: str = None
file: Optional[ifcopenshell.file] = None
schema: Optional[ifcopenshell.ifcopenshell_wrapper.schema_definition] = None
cache: Optional[ifcopenshell.ifcopenshell_wrapper.HdfSerializer] = None
cache_path: Optional[str] = None
id_map: dict[int, IFC_CONNECTED_TYPE] = {}
guid_map: dict[str, IFC_CONNECTED_TYPE] = {}
edited_objs: Set[bpy.types.Object] = set()
pset_template_path: str = ""
pset_template_file: ifcopenshell.file = None
pset_template_file: Optional[ifcopenshell.file] = None
classification_path: str = ""
classification_file: ifcopenshell.file = None
classification_file: Optional[ifcopenshell.file] = None
library_path: str = ""
library_file: ifcopenshell.file = None
library_file: Optional[ifcopenshell.file] = None
current_transaction = ""
last_transaction = ""
history = []
@@ -119,7 +122,13 @@ class IfcStore:
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
IfcStore.cache = None
os.replace(IfcStore.cache_path, new_cache_path)
try:
shutil.move(IfcStore.cache_path, new_cache_path)
except PermissionError:
try:
shutil.copy2(IfcStore.cache_path, new_cache_path)
except PermissionError:
pass # Well we tried. No cache for you!
IfcStore.get_cache()
@staticmethod
@@ -329,6 +338,7 @@ class IfcStore:
@staticmethod
def execute_ifc_operator(operator: bpy.types.Operator, context: bpy.types.Context, is_invoke=False):
blenderbim.last_actions.append({"type": "operator", "name": operator.bl_idname})
bpy.context.scene.BIMProperties.is_dirty = True
is_top_level_operator = not bool(IfcStore.current_transaction)
@@ -343,10 +353,14 @@ class IfcStore:
else:
operator.transaction_key = IfcStore.current_transaction
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context)
try:
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context)
except:
blenderbim.last_error = traceback.format_exc()
raise
if is_top_level_operator:
if tool.Ifc.get():
+16 -12
View File
@@ -702,15 +702,17 @@ class IfcImporter:
mat = np.array(
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
)
point = np.array(
point = mat @ np.array(
(
shape.geometry.verts[0] / self.unit_scale,
shape.geometry.verts[1] / self.unit_scale,
shape.geometry.verts[2] / self.unit_scale,
shape.geometry.verts[0],
shape.geometry.verts[1],
shape.geometry.verts[2],
0.0,
)
)
return mat @ point
point = point / self.unit_scale
if self.is_point_far_away(point, is_meters=False):
return point
def does_element_likely_have_geometry_far_away(self, element):
for representation in element.Representation.Representations:
@@ -1500,10 +1502,9 @@ class IfcImporter:
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
types_collection = project_collection.children[self.type_collection.name]
types_collection.hide_viewport = False
for obj in types_collection.collection.objects: #turn off all objects inside Types collection.
for obj in types_collection.collection.objects: # turn off all objects inside Types collection.
obj.hide_set(True)
def clean_mesh(self):
obj = None
last_obj = None
@@ -1912,11 +1913,7 @@ class IfcImporter:
and geometry.verts
and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2]))
):
m = shape.transformation.matrix.data
mat = np.array(
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
)
offset_point = np.linalg.inv(mat) @ np.array(
offset_point = np.array(
(
float(props.blender_eastings),
float(props.blender_northings),
@@ -1924,8 +1921,15 @@ class IfcImporter:
0.0,
)
)
if geometry != shape:
m = shape.transformation.matrix.data
mat = np.array(
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
)
offset_point = np.linalg.inv(mat) @ offset_point
verts = [None] * len(geometry.verts)
for i in range(0, len(geometry.verts), 3):
# Note: this enh2xyz call is crazy slow.
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
geometry.verts[i],
geometry.verts[i + 1],
@@ -73,7 +73,7 @@ class BIM_PT_aggregate(Panel):
row.operator("bim.add_aggregate", icon="ADD", text="")
op = row.operator("bim.aggregate_unassign_object", icon="X", text="")
else:
row.label(text="No Aggregate", icon="TRIA_UP")
row.label(text="No Whole relation defined", icon="TRIA_UP")
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
@@ -33,7 +33,7 @@ import blenderbim.tool as tool
import blenderbim.core.debug as core
import blenderbim.bim.handler
import blenderbim.bim.import_ifc as import_ifc
from blenderbim import get_debug_info
from blenderbim import get_debug_info, format_debug_info
from blenderbim.bim.ifc import IfcStore
@@ -54,16 +54,18 @@ class CopyDebugInformation(bpy.types.Operator):
}
)
# Format it in a readable way
text = "\n".join(f"{k}: {v}" for k, v in info.items())
text = format_debug_info(info)
print("-" * 80)
print(text)
print("-" * 80)
if platform.system() == "Windows":
command = "echo | set /p nul=" + text.strip()
command = "echo | set /p nul=" + text
elif platform.system() == "Darwin": # for MacOS
command = 'printf "' + text.strip().replace("\n", "\\n").replace('"', "") + '" | pbcopy'
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
else: # Linux
command = 'printf "' + text.strip().replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
subprocess.run(command, shell=True, check=True)
return {"FINISHED"}
@@ -1515,7 +1515,14 @@ class ActivateDrawing(bpy.types.Operator):
if not self.camera_view_point:
viewport_position = tool.Blender.get_viewport_position()
core.activate_drawing_view(tool.Ifc, tool.Drawing, drawing=drawing)
try:
core.activate_drawing_view(tool.Ifc, tool.Blender, tool.Drawing, drawing=drawing)
except core.CameraNotAvailableError:
self.report(
{"ERROR"},
"The drawing view is not available. Ensure you have not excluded it in the active view layer.",
)
return {"CANCELLED"}
if not self.camera_view_point:
tool.Blender.set_viewport_position(viewport_position)
@@ -136,19 +136,23 @@ class AddRepresentation(bpy.types.Operator, Operator):
"for Profile - 2D bounding box by local XZ axes.\n"
"For other contexts - bounding box is 3d.",
),
("PROJECT", "Full Representation", ""),
("OBJECT", "From Object", "Copies geometry from another object"),
("PROJECT", "Full Representation", "Reuses the current representation"),
],
name="Representation Conversion Method",
)
def _execute(self, context):
obj = context.active_object
props = obj.BIMGeometryProperties
ifc_context = int(props.contexts or "0") or None
props = context.scene.BIMGeometryProperties
oprops = obj.BIMGeometryProperties
ifc_context = int(oprops.contexts or "0") or None
if not ifc_context:
return
ifc_context = tool.Ifc.get().by_id(ifc_context)
original_data = obj.data
if self.representation_conversion_method == "OUTLINE":
if ifc_context.ContextType == "Plan":
data = tool.Geometry.generate_outline_mesh(obj, axis="+Z")
@@ -165,17 +169,31 @@ class AddRepresentation(bpy.types.Operator, Operator):
else:
data = tool.Geometry.generate_3d_box_mesh(obj)
tool.Geometry.change_object_data(obj, data, is_global=True)
elif (
self.representation_conversion_method == "OBJECT"
and props.representation_from_object
and props.representation_from_object.data
):
data = tool.Geometry.duplicate_object_data(props.representation_from_object)
tool.Geometry.change_object_data(obj, data, is_global=True)
core.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=ifc_context,
ifc_representation_class=None,
profile_set_usage=None,
)
try:
core.add_representation(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
obj=obj,
context=ifc_context,
ifc_representation_class=None,
profile_set_usage=None,
)
except core.IncompatibleRepresentationError:
if obj.data != original_data:
tool.Geometry.change_object_data(obj, original_data, is_global=True)
bpy.data.meshes.remove(data)
self.report({"ERROR"}, "No compatible representation for the context could be created.")
return {"CANCELLED"}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
@@ -183,6 +201,9 @@ class AddRepresentation(bpy.types.Operator, Operator):
def draw(self, context):
row = self.layout.row()
row.prop(self, "representation_conversion_method", text="")
if self.representation_conversion_method == "OBJECT":
row = self.layout.row()
row.prop(context.scene.BIMGeometryProperties, "representation_from_object", text="")
class SelectConnection(bpy.types.Operator, Operator):
@@ -138,3 +138,4 @@ class BIMGeometryProperties(PropertyGroup):
name="IFC Interaction Mode",
update=update_mode,
)
representation_from_object: PointerProperty(type=bpy.types.Object)
@@ -83,8 +83,17 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
props = obj.BIMArrayProperties
relating_obj = props.relating_array_object
if relating_obj:
element = tool.Ifc.get_entity(relating_obj)
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
parent_element = tool.Ifc.get().by_guid(parent_globalid)
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
else:
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
props.count = data["count"]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
props.x = data["x"] * si_conversion
@@ -93,7 +102,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
props.use_local_space = data.get("use_local_space", False)
props.sync_children = data.get("sync_children", False)
props.method = data.get("method", "OFFSET")
props.is_editing = self.item
return {"FINISHED"}
@@ -137,6 +148,10 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
#clears the relating_array_object so it doesn't show again next time
props.relating_array_object = None
return {"FINISHED"}
@@ -87,6 +87,21 @@ def update_type_page(self, context):
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
def update_relating_array_from_object(self, context):
bpy.ops.bim.enable_editing_array(item=self.is_editing)
return
def is_object_array_applicable(self, obj):
element = tool.Ifc.get_entity(obj)
if not element:
return False
return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
class BIMModelProperties(PropertyGroup):
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
relating_type_id: bpy.props.EnumProperty(
@@ -203,6 +218,14 @@ class BIMArrayProperties(PropertyGroup):
description="Regenerate all children based on the parent object",
default=False,
)
relating_array_object: bpy.props.PointerProperty(
type=bpy.types.Object,
name="Copy Array Properties",
update=update_relating_array_from_object,
poll=is_object_array_applicable,
)
class BIMStairProperties(PropertyGroup):
@@ -223,6 +223,8 @@ class BIM_PT_array(bpy.types.Panel):
row = col.row(align=True)
row.prop(props, "z")
row.operator("bim.input_cursor_z_array", icon="CURSOR", text="")
row = col.row(align=True)
row.prop(props, "relating_array_object", icon="COPYDOWN")
else:
row = box.row(align=True)
name = f"{array['count']} Items ({array.get('method', 'OFFSET').capitalize()})"
@@ -21,6 +21,7 @@ import bpy
import time
import logging
import tempfile
import traceback
import subprocess
import numpy as np
import ifcopenshell
@@ -674,19 +675,23 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
return self.finish_loading_project(context)
def finish_loading_project(self, context):
if not self.is_existing_ifc_file():
return {"FINISHED"}
try:
if not self.is_existing_ifc_file():
return {"FINISHED"}
if tool.Blender.is_default_scene():
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
if tool.Blender.is_default_scene():
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
context.scene.BIMProperties.ifc_file = self.get_filepath()
context.scene.BIMProjectProperties.is_loading = True
context.scene.BIMProjectProperties.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
tool.Blender.register_toolbar()
if not self.is_advanced:
bpy.ops.bim.load_project_elements()
context.scene.BIMProperties.ifc_file = self.get_filepath()
context.scene.BIMProjectProperties.is_loading = True
context.scene.BIMProjectProperties.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
tool.Blender.register_toolbar()
if not self.is_advanced:
bpy.ops.bim.load_project_elements()
except:
blenderbim.last_error = traceback.format_exc()
raise
return {"FINISHED"}
def invoke(self, context, event):
@@ -268,7 +268,6 @@ class RemovePropTemplate(bpy.types.Operator, Operator):
prop_template: bpy.props.IntProperty()
def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties
ifcopenshell.api.run(
"pset_template.remove_prop_template",
IfcStore.pset_template_file,
@@ -287,21 +286,21 @@ class EditPropTemplate(bpy.types.Operator, Operator):
def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties
if props.active_prop_template.template_type == "P_ENUMERATEDVALUE":
enumerator = self.generate_prop_enum(props)
data_type = props.active_prop_template.get_value_name()
prop = props.active_prop_template
enumerators = [getattr(ev, data_type) for ev in prop.enum_values]
else:
enumerator = None
enumerators = None
ifcopenshell.api.run(
"pset_template.edit_prop_template",
IfcStore.pset_template_file,
**{
"prop_template": IfcStore.pset_template_file.by_id(props.active_prop_template_id),
"attributes": {
"Name": props.active_prop_template.name,
"Description": props.active_prop_template.description,
"PrimaryMeasureType": props.active_prop_template.primary_measure_type,
"TemplateType": props.active_prop_template.template_type,
"Enumerators": enumerator,
},
prop_template=IfcStore.pset_template_file.by_id(props.active_prop_template_id),
attributes={
"Name": props.active_prop_template.name,
"Description": props.active_prop_template.description,
"PrimaryMeasureType": props.active_prop_template.primary_measure_type,
"TemplateType": props.active_prop_template.template_type,
"Enumerators": enumerators,
}
)
bpy.ops.bim.disable_editing_prop_template()
@@ -309,18 +308,3 @@ class EditPropTemplate(bpy.types.Operator, Operator):
blenderbim.bim.handler.refresh_ui_data()
if tool.Ifc.get():
blenderbim.bim.schema.reload(tool.Ifc.get().schema)
# TODO -This will need to go into the
# api code at some point - vulevukusej
def generate_prop_enum(self, props):
self.file = IfcStore.pset_template_file
data_type = props.active_prop_template.get_value_name()
prop = props.active_prop_template
prop_enum = self.file.create_entity(
"IFCPROPERTYENUMERATION",
Name=prop.name,
EnumerationValues=tuple(
self.file.create_entity(prop.primary_measure_type, getattr(ev, data_type)) for ev in prop.enum_values
),
)
return prop_enum
@@ -90,6 +90,15 @@ class OpenUri(bpy.types.Operator):
return {"FINISHED"}
class CloseError(bpy.types.Operator):
bl_idname = "bim.close_error"
bl_label = "Close Error"
def execute(self, context):
blenderbim.last_error = None
return {"FINISHED"}
class SelectURIAttribute(bpy.types.Operator):
bl_idname = "bim.select_uri_attribute"
bl_label = "Select URI Attribute"
+13 -1
View File
@@ -29,8 +29,9 @@ from ifcopenshell.util.doc import (
get_attribute_doc,
)
from . import ifc
import blenderbim.tool as tool
from blenderbim import get_debug_info
import blenderbim.bim
import blenderbim.tool as tool
from blenderbim.bim.helper import IfcHeaderExtractor
from blenderbim.bim.prop import Attribute
@@ -402,6 +403,17 @@ class BIM_PT_tabs(Panel):
row = self.layout.row(align=True)
row.prop(aprops, "tab", text="")
if blenderbim.last_error:
box = self.layout.box()
row = box.row(align=True)
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
row.operator("bim.close_error", text="", icon="CANCEL")
box.label(text="View the console for full logs.", icon="CONSOLE")
box.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
op = box.operator("bim.open_uri", text="How Can I Fix This?")
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html"
except:
pass # Prior to load_post, we may not have any area properties setup
+11 -1
View File
@@ -441,9 +441,19 @@ def select_assigned_product(drawing, context):
drawing.select_assigned_product(context)
def activate_drawing_view(ifc, drawing_tool, drawing):
def activate_drawing_view(ifc, blender, drawing_tool, drawing):
camera = ifc.get_object(drawing)
if not camera:
camera = drawing_tool.import_drawing(drawing)
drawing_tool.import_annotations_in_group(drawing_tool.get_drawing_group(drawing))
blender.activate_camera(camera)
drawing_tool.isolate_camera_collection(camera)
try:
blender.set_active_object(camera)
except:
raise CameraNotAvailableError()
drawing_tool.activate_drawing(camera)
class CameraNotAvailableError(Exception):
pass
+8 -1
View File
@@ -47,7 +47,7 @@ def add_representation(
data = geometry.get_object_data(obj)
if not data and ifc_representation_class != "IfcTextLiteral":
return
raise IncompatibleRepresentationError()
representation = ifc.run(
"geometry.add_representation",
@@ -63,6 +63,9 @@ def add_representation(
profile_set_usage=profile_set_usage,
)
if not representation:
raise IncompatibleRepresentationError()
if geometry.is_body_representation(representation):
[geometry.run_style_add_style(obj=mat) for mat in geometry.get_object_materials_without_styles(obj)]
ifc.run(
@@ -221,3 +224,7 @@ def edit_similar_opening_placement(geometry, opening=None, similar_openings=None
old_placement = similar_opening.ObjectPlacement
similar_opening.ObjectPlacement = opening.ObjectPlacement
geometry.delete_opening_object_placement(old_placement)
class IncompatibleRepresentationError(Exception):
pass
+14 -12
View File
@@ -18,6 +18,7 @@
import abc
import inspect
from typing import Optional
# fmt: off
# pylint: skip-file
@@ -72,20 +73,21 @@ class Aggregate:
@interface
class Blender:
def set_active_object(cls, obj): pass
def get_name(cls, ifc_class, name): pass
def get_selected_objects(cls): pass
def create_ifc_object(cls, ifc_class: str, name: str = None, data=None): pass
def get_obj_ifc_definition_id(cls, obj=None, obj_type=None, context=None): pass
def is_ifc_object(cls, obj): pass
def is_ifc_class_active(cls, ifc_class): pass
def get_viewport_context(cls): pass
def update_viewport(cls): pass
def get_default_selection_keypmap(cls): pass
def get_object_bounding_box(cls, obj): pass
def activate_camera(cls, obj): pass
def apply_bmesh(cls, mesh, bm, obj=None): pass
def get_bmesh_for_mesh(cls, mesh, clean=False): pass
def bmesh_join(cls, bm_a, bm_b, callback=None): pass
def create_ifc_object(cls, ifc_class: str, name: Optional[str] = None, data=None): pass
def get_bmesh_for_mesh(cls, mesh, clean=False): pass
def get_default_selection_keypmap(cls): pass
def get_name(cls, ifc_class, name): pass
def get_obj_ifc_definition_id(cls, obj=None, obj_type=None, context=None): pass
def get_object_bounding_box(cls, obj): pass
def get_selected_objects(cls): pass
def get_viewport_context(cls): pass
def is_ifc_class_active(cls, ifc_class): pass
def is_ifc_object(cls, obj): pass
def set_active_object(cls, obj): pass
def update_viewport(cls): pass
@interface
+18 -3
View File
@@ -49,6 +49,23 @@ class Blender(blenderbim.core.tool.Blender):
OBJECT_TYPES_THAT_SUPPORT_EDIT_GPENCIL_MODE = ("GPENCIL",)
TYPE_MANAGER_ICON = "LIGHTPROBE_VOLUME" if bpy.app.version >= (4, 1, 0) else "LIGHTPROBE_GRID"
@classmethod
def activate_camera(cls, obj: bpy.types.Object) -> None:
area = tool.Blender.get_view3d_area()
is_local_view = area.spaces[0].local_view is not None
if is_local_view:
# Turn off local view before activating drawing, and then turn it on again.
for a in bpy.context.screen.areas:
if a.type == "VIEW_3D":
override = bpy.context.copy()
override["area"] = a
bpy.ops.view3d.localview(override)
bpy.context.scene.camera = obj
bpy.ops.view3d.localview(override)
else:
bpy.context.scene.camera = obj
area.spaces[0].region_3d.view_perspective = "CAMERA"
@classmethod
def get_area_props(cls, context: bpy.types.Context) -> Any:
try:
@@ -845,9 +862,7 @@ class Blender(blenderbim.core.tool.Blender):
bpy.utils.register_tool(ws_model.PipeTool, after={"bim.duct_tool"}, separator=False, group=False)
bpy.utils.register_tool(ws_model.BimTool, after={"bim.pipe_tool"}, separator=False, group=False)
bpy.utils.register_tool(ws_drawing.AnnotationTool, after={"bim.bim_tool"}, separator=True, group=False)
bpy.utils.register_tool(
ws_spatial.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False
)
bpy.utils.register_tool(ws_spatial.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False)
bpy.utils.register_tool(
ws_structural.StructuralTool, after={"bim.spatial_tool"}, separator=False, group=False
)
+3 -16
View File
@@ -1743,21 +1743,7 @@ class Drawing(blenderbim.core.tool.Drawing):
bpy.ops.bim.activate_model()
@classmethod
def activate_drawing(cls, camera: bpy.types.Object) -> None:
area = tool.Blender.get_view3d_area()
is_local_view = area.spaces[0].local_view is not None
if is_local_view:
# turn off local view before activating drawing, and then turn it on again.
for a in bpy.context.screen.areas:
if a.type == "VIEW_3D":
override = bpy.context.copy()
override["area"] = a
bpy.ops.view3d.localview(override)
bpy.context.scene.camera = camera
bpy.ops.view3d.localview(override)
else:
bpy.context.scene.camera = camera
area.spaces[0].region_3d.view_perspective = "CAMERA"
def isolate_camera_collection(cls, camera: bpy.types.Object) -> None:
views_collection = bpy.data.collections.get("Views")
for collection in views_collection.children:
# We assume the project collection is at the top level
@@ -1775,8 +1761,9 @@ class Drawing(blenderbim.core.tool.Drawing):
camera.BIMObjectProperties.collection.name
].hide_viewport = False
camera.BIMObjectProperties.collection.hide_render = False
tool.Spatial.set_active_object(camera)
@classmethod
def activate_drawing(cls, camera: bpy.types.Object) -> None:
# Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
drawing = tool.Ifc.get_entity(camera)
+3 -3
View File
@@ -13,9 +13,9 @@ Unstable installation
**Unstable installation** is almost the same as **Stable installation**, except
that they are typically updated every day. Simply download a daily build from
the `Github releases page
<https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, then follow the same
instructions as the **Stable installation**.
the `GitHub releases page
<https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, then follow the
usual :doc:`installation instructions</users/installation>`.
You will need to choose which build to download.
+1
View File
@@ -32,6 +32,7 @@ Learn how to model a small building and create simple architectural plans, secti
users/git_support
users/other_addons
users/general_usage
users/troubleshooting
.. toctree::
:hidden:
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

+32 -91
View File
@@ -58,51 +58,6 @@ You can enable add-ons permanently by using ``Save User Settings`` from the Addo
.. _where is the add-on installed:
Where is the add-on installed?
------------------------------
Upon installation, the BlenderBIM Add-on is stored in the
``scripts/addons/blenderbim/`` directory, within your Blender configuration
folder. However, the location of your Blender configuration folder depends on
how you have installed Blender.
If you downloaded Blender as a ``.zip`` file without running an installer, you
will find the Blender configuration folder in the following directory, where
``X.XX`` is the Blender version:
::
/path/to/blender/X.XX/
Otherwise, if you installed Blender using an installation package, the Blender
configuration folder depends on which operating system you use.
On Linux, if you are installing the add-on as a user:
::
~/.config/blender/X.XX/
On Linux, if you are deploying the add-on system-wide (this may also depend on
your Linux distribution):
::
/usr/share/blender/X.XX/
On Mac, if you are installing the add-on as a user:
::
/Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/
On Mac, if you are deploying the add-on system-wide:
::
/Library/Application Support/Blender/X.XX/
On Windows:
::
C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\X.XX\
Updating
--------
@@ -129,66 +84,52 @@ installed>`.
If you do not restart Blender, the add-on will fail to remove correctly, and you
will need to uninstall manually.
Where is the add-on installed?
------------------------------
FAQ
---
Upon installation, the BlenderBIM Add-on is stored in the
``scripts/addons/blenderbim/`` directory, within your Blender configuration
folder. However, the location of your Blender configuration folder depends on
how you have installed Blender.
If you are unable to install the BlenderBIM Add-on, make sure you are using
**Blender 4.1** installed from https://blender.org/ and are installing the
latest version from https://blenderbim.org.
If you downloaded Blender as a ``.zip`` file without running an installer, the
BlenderBIM Add-on will be installed in the following directory, where ``X.XX``
is the Blender version:
Other common solutions are listed below. If none of these fix the problem, you
can `report a bug <https://github.com/ifcopenshell/ifcopenshell/issues>`_ or
`live chat with a developer <https://osarch.org/chat/>`_.
::
1. **Some other error prevents me from installing or doing basic functions with
the add-on. Is it specific to my environment?**
/path/to/blender/X.XX/scripts/addons/blenderbim/
Try installing and using the BlenderBIM Add-on on a "clean environment". A
clean environment is a fresh Blender installation with no other add-ons
enabled with factory settings.
Otherwise, if you installed Blender using an installation package, the Blender
configuration folder depends on which operating system you use.
To quickly test in a clean environment, find your Blender configuration
folder based on the `where is the add-on installed`_ section. Rename the
folder from ``X.XX`` to something else like ``X.XX_backup``, then restart
Blender and try follow the installation instructions again.
On Linux, if you are installing the add-on as a user:
If this fixes your issue, consider disabling other add-ons one by one until
you find a conflict as a next step to isolating the issue.
::
2. **I get an error similar to "ImportError: IfcOpenShell not built for 'linux/64bit/python3.10'"**
~/.config/blender/X.XX/scripts/addons/blenderbim/
If you are using a Mac, be sure to use the Mac Silicon version if you have a
newer Mac. The only exception is if you have installed Blender using Steam
on a Mac, in which case you need to use the Mac Intel download.
On Linux, if you are deploying the add-on system-wide (this may also depend on
your Linux distribution):
For all other scenarios, check the BlenderBIM Add-on zip file which you
downloaded. The zip will have either ``py39``, ``py310``, or ``py311`` in
the name. See the instructions in the :ref:`devs/installation:unstable
installation` section to check that you have installed the correct version.
::
3. **I am on Ubuntu and get an error similar to "ImportError:
/lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found"**
/usr/share/blender/X.XX/scripts/addons/blenderbim/
Our latest package which uses IfcOpenShell v0.7.0 is built using Ubuntu 20 LTS.
If you have an older Ubuntu version, you can either upgrade to 19.10 or above,
or you'll need to compile IfcOpenShell yourself.
On Mac, if you are installing the add-on as a user:
4. **I get an error saying "ModuleNotFoundError: No module named 'numpy'"**"
::
If you have installed Blender from another source instead of from
`Blender.org <https://www.blender.org/download/>`__, such as from your
distro's package repositories, then you may be missing some modules like
``numpy``. Try installing it manually like ``apt install python-numpy``.
/Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/scripts/addons/blenderbim/
5. **I get an error similar to RuntimeError: Instance #1234 not found**
On Mac, if you are deploying the add-on system-wide:
Blender saves and loads projects to a ``.blend`` file. However. the
BlenderBIM Add-on works with native IFC, and this means instead of saving
and loading ``.blend`` files, you should instead save and load the ``.ifc``
project.
::
If you have opened a ``.blend`` file, there is a risk that the contents of
the ``.blend`` session do not correlate to the contents of the ``.ifc``,
which can cause this error. Unless you are an advanced user, only save and
load ``.ifc`` files.
/Library/Application Support/Blender/X.XX/scripts/addons/blenderbim/
On Windows:
::
C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\X.XX\scripts\addons\blenderbim\
@@ -0,0 +1,93 @@
Troubleshooting
===============
The BlenderBIM Add-on is alpha software. There are many bugs! When something
goes wrong, you may see some computer code flash up on your screen. You may
also see an error message:
.. image:: images/error-message.png
**Don't panic!** Click on the button that says **Copy Error Message To
Clipboard**. You will need to paste this text in a bug report.
If you do not have a GitHub account, you will need to sign up to report a bug.
In addition to pasting the error message text, please also describe what you
were doing, and attach your IFC file or screenshots if relevant.
.. container:: blockbutton
`Report a bug <https://github.com/IfcOpenShell/IfcOpenShell/issues/new>`__
If your issue is particularly complex, you can also chat live with developers
or other powerusers.
.. container:: blockbutton
`Chat live with a developer <https://osarch.org/chat>`_
Installation issues
-------------------
If you are unable to install the BlenderBIM Add-on, make sure you are using
**Blender 4.1** installed from https://blender.org/ and are installing the
latest version from https://blenderbim.org.
Other common solutions are listed below. If none of these fix the problem, you
can `report a bug <https://github.com/ifcopenshell/ifcopenshell/issues>`_ or
`live chat with a developer <https://osarch.org/chat/>`_.
1. **Some other error prevents me from installing or doing basic functions with
the add-on. Is it specific to my environment?**
Try installing and using the BlenderBIM Add-on on a "clean environment". A
clean environment is a fresh Blender installation with no other add-ons
enabled with factory settings.
To quickly test in a clean environment, first :ref:`find your Blender
configuration folder<users/installation:where is the add-on installed?>`.
Rename the folder from ``X.XX`` to something else like ``X.XX_backup``, then
restart Blender and try follow the :doc:`installation
instructions<installation>` again.
If this fixes your issue, consider disabling other add-ons one by one until
you find a conflict as a next step to isolating the issue.
2. **I get an error similar to "ImportError: IfcOpenShell not built for 'linux/64bit/python3.10'"**
If you are using a Mac, be sure to use the Mac Silicon version if you have a
newer Mac. The only exception is if you have installed Blender using Steam
on a Mac, in which case you need to use the Mac Intel download.
For all other scenarios, check the BlenderBIM Add-on zip file which you
downloaded. The zip will have either ``py39``, ``py310``, or ``py311`` in
the name. See the instructions in the :ref:`devs/installation:unstable
installation` section to check that you have installed the correct version.
3. **I am on Ubuntu and get an error similar to "ImportError:
/lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found"**
Our latest package which uses IfcOpenShell v0.7.0 is built using Ubuntu 20 LTS.
If you have an older Ubuntu version, you can either upgrade to 19.10 or above,
or you'll need to compile IfcOpenShell yourself.
4. **I get an error saying "ModuleNotFoundError: No module named 'numpy'"**"
If you have installed Blender from another source instead of from
`Blender.org <https://www.blender.org/download/>`__, such as from your
distro's package repositories, then you may be missing some modules like
``numpy``. Try installing it manually like ``apt install python-numpy``.
Common issues
-------------
1. **I get an error similar to RuntimeError: Instance #1234 not found**
Blender saves and loads projects to a ``.blend`` file. However. the
BlenderBIM Add-on works with native IFC, and this means instead of saving
and loading ``.blend`` files, you should instead save and load the ``.ifc``
project.
If you have opened a ``.blend`` file, there is a risk that the contents of
the ``.blend`` session do not correlate to the contents of the ``.ifc``,
which can cause this error. Unless you are an advanced user, only save and
load ``.ifc`` files.
+7 -7
View File
@@ -72,8 +72,8 @@ def get_facility_data(ifc_file, element):
"ModelProjectID": ifc_file.by_type("IfcProject")[0].GlobalId,
"ModelSiteID": getattr(get_facility_parent(element, "IfcSite"), "GlobalId", None),
"ModelBuildingID": element.GlobalId,
"LinearUnits": "millimeters",
"AreaUnits": "square meters",
"LengthUnit": "millimeters",
"AreaUnit": "square meters",
"Phase": ifc_file.by_type("IfcProject")[0].Phase,
}
@@ -99,7 +99,7 @@ def get_space_data(ifc_file, element):
"Description": element.LongName,
"ClassificationIdentification": get_classification_identification(element),
"ClassificationName": get_classification_name(element),
"LevelName": getattr(get_facility_parent(element, "IfcBuildingStorey"), "Name", None),
"StoreyName": getattr(get_facility_parent(element, "IfcBuildingStorey"), "Name", None),
"OrganizationName": get_owner_name(element),
"CreationDate": get_owner_creation_date(element),
"ModelSoftware": get_owner_application(element),
@@ -261,8 +261,8 @@ config = {
"ModelProjectID",
"ModelSiteID",
"ModelBuildingID",
"LinearUnits",
"AreaUnits",
"LengthUnit",
"AreaUnit",
"Phase",
],
"colours": "ppppreeeeesss",
@@ -295,7 +295,7 @@ config = {
"Description",
"ClassificationIdentification",
"ClassificationName",
"LevelName",
"StoreyName",
"OrganizationName",
"CreationDate",
"ModelSoftware",
@@ -305,7 +305,7 @@ config = {
"NetFloorArea",
],
"colours": "ppprreeess",
"sort": [{"name": "LevelName", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
"sort": [{"name": "StoreyName", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
"get_category_elements": get_spaces,
"get_element_data": get_space_data,
},
@@ -186,6 +186,7 @@ Valid keys are:
"``material`` or ``mat``", "Gets the assigned material, which may be a material set."
"``item`` or ``i``", "If the previous key returns a material set, gets the relevant material set items"
"``materials`` or ``mats``", "Gets a list of IfcMaterials assigned directly or indirectly (such as via a material set) to the element"
"``profiles``", "Gets a list of IfcProfileDefs assigned (such as via a material profile) or used (such as in an extrusion) in the element"
"``x``", "Gets the X coordinate of the element's placement"
"``y``", "Gets the Y coordinate of the element's placement"
"``z``", "Gets the Z coordinate of the element's placement"
@@ -86,6 +86,18 @@ from .file import file
from .entity_instance import entity_instance, register_schema_attributes
from .sql import sqlite, sqlite_entity
# explicitly specify available imported symbols
# (it's a requirement for a typed library)
__all__ = [
"ifcopenshell_wrapper",
"file",
"entity_instance",
"sqlite",
"sqlite_entity",
"stream",
"stream_entity",
]
try:
from .stream import stream, stream_entity
except:
@@ -170,31 +170,6 @@ def run(
for listener in pre_listeners.get(usecase_path, {}).values():
listener(usecase_path, ifc_file, settings)
# TODO: settings serialization for client-server systems
# def serialise_entity_instance(entity):
# return {"cast_type": "entity_instance", "value": entity.id(), "Name": getattr(entity, "Name", None)}
# vcs_settings = settings.copy()
# for key, value in settings.items():
# if isinstance(value, ifcopenshell.entity_instance):
# vcs_settings[key] = serialise_entity_instance(value)
# elif isinstance(value, numpy.ndarray):
# vcs_settings[key] = {"cast_type": "ndarray", "value": value.tolist()}
# elif isinstance(value, list) and value and isinstance(value[0], ifcopenshell.entity_instance):
# vcs_settings[key] = [serialise_entity_instance(i) for i in value]
if "add_representation" in usecase_path:
pass
# print(usecase_path, "{ ... settings too complex right now ... }")
elif "owner." in usecase_path:
pass
else:
pass
# print(vcs_settings)
# try:
# print(usecase_path, json.dumps(vcs_settings))
# except:
# print(usecase_path, vcs_settings)
usecase_class = CACHED_USECASE_CLASSES.get(usecase_path)
if usecase_class is None:
importlib.import_module(f"ifcopenshell.api.{usecase_path}")
@@ -308,6 +283,29 @@ def extract_docs(module, usecase):
return node_data
def serialise_settings(settings):
def serialise_entity_instance(entity):
return {"cast_type": "entity_instance", "value": entity.id(), "Name": getattr(entity, "Name", None)}
vcs_settings = settings.copy()
for key, value in settings.items():
if isinstance(value, ifcopenshell.entity_instance):
vcs_settings[key] = serialise_entity_instance(value)
elif isinstance(value, numpy.ndarray):
vcs_settings[key] = {"cast_type": "ndarray", "value": value.tolist()}
elif isinstance(value, list) and value and isinstance(value[0], ifcopenshell.entity_instance):
vcs_settings[key] = [serialise_entity_instance(i) for i in value]
else:
try:
vcs_settings[key] = str(value)
except:
vcs_settings[key] = "n/a"
try:
return json.dumps(vcs_settings)
except:
return str(vcs_settings)
def wrap_usecase(usecase_path, usecase):
"""Wraps an API function in pre/post listeners."""
@@ -315,7 +313,9 @@ def wrap_usecase(usecase_path, usecase):
ifc_file = args[0] if args else None
nonlocal usecase_path
if should_run_listeners:
for listener in pre_listeners.get(usecase_path, {}).values():
listeners = list(pre_listeners.get(usecase_path, {}).values())
listeners += pre_listeners.get("*", {}).values()
for listener in listeners:
listener(usecase_path, ifc_file, settings)
# see #4531
@@ -329,7 +329,9 @@ def wrap_usecase(usecase_path, usecase):
raise TypeError(msg) from e
if should_run_listeners:
for listener in post_listeners.get(usecase_path, {}).values():
listeners = list(post_listeners.get(usecase_path, {}).values())
listeners += post_listeners.get("*", {}).values()
for listener in listeners:
listener(usecase_path, ifc_file, settings)
return result
@@ -348,7 +350,8 @@ def wrap_usecases(path, name):
module_name = name.split(".")[-1]
module = sys.modules[name]
for loader, usecase_name, is_pkg in pkgutil.iter_modules(path):
usecase = getattr(module, usecase_name)
# We may not be able to get the usecase if we are missing a dependency.
usecase = getattr(module, usecase_name, None)
if callable(usecase):
usecase_path = f"{module_name}.{usecase_name}"
setattr(module, usecase_name, wrap_usecase(usecase_path, usecase))
@@ -17,9 +17,11 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Any
def edit_attributes(file, product=None, attributes=None) -> None:
def edit_attributes(file: ifcopenshell.file, product: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edit the attributes of a product
All IFC entities have attributes. Normally they can be edited directly,
@@ -31,7 +33,7 @@ def edit_attributes(file, product=None, attributes=None) -> None:
entity.
:type product: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -17,17 +17,18 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.unit
from typing import Optional
def assign_connection_geometry(
file,
rel_space_boundary=None,
outer_boundary=None,
inner_boundaries=None,
location=None,
axis=None,
ref_direction=None,
unit_scale=None,
file: ifcopenshell.file,
rel_space_boundary: ifcopenshell.entity_instance,
outer_boundary: list[tuple[float, float]],
location: tuple[float, float, float],
axis: tuple[float, float, float],
ref_direction: tuple[float, float, float],
inner_boundaries: Optional[list[list[tuple[float, float]]]] = None,
unit_scale: Optional[float] = None,
) -> None:
"""Create and assign a connection geometry to a space boundary relationship
@@ -44,24 +45,24 @@ def assign_connection_geometry(
polyline. The last point will connect to the first point. Each
point is represented by an interable of 2 floats. The coordinates of
the points are relative to the positional matrix arguments.
:type outer_boundary: list[list[float]]
:type outer_boundary: list[tuple[float, float]]
:param inner_boundaries: A list of zero or more inner boundaries to use
for the plane. Each boundary is represented by an open polyline, as
defined by the outer_boundary argument.
:type inner_boundaries: list[list[list[float]]], optional
:type inner_boundaries: list[list[tuple[float, float]]], optional
:param location: The local origin of the connection geometry, defined as
an XYZ coordinate relative to the placement of the space that is
being bounded.
:type location: list[float]
:type location: tuple[float, float, float]
:param axis: The local X axis of the connection geometry, defined as an
XYZ vector relative to the placement of the space that is being
bounded.
:type axis: list[float]
:type axis: tuple[float, float, float]
:param ref_direction: The local Z axis of the connection geometry,
defined as an XYZ vector relative to the placement of the space that
is being bounded. The Y vector is automatically derived using the
right hand rule.
:type ref_direction: list[float]
:type ref_direction: tuple[float, float, float]
:param unit_scale: The unit scale as calculated by
ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
will be automatically calculated for you.
@@ -19,13 +19,13 @@
import ifcopenshell.util.element
def copy_boundary(file, boundary=None) -> None:
def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Copies a space boundary
:param boundary: The IfcRelSpaceBoundary you want to copy.
:type boundary: ifcopenshell.entity_instance
:return: None
:rtype: None
:return: Duplicate of the IfcRelSpaceBoundary
:rtype: ifcopenshell.entity_instance
Example:
@@ -15,15 +15,17 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Optional
def edit_attributes(
file,
entity=None,
relating_space=None,
related_building_element=None,
parent_boundary=None,
corresponding_boundary=None,
file: ifcopenshell.file,
entity: ifcopenshell.entity_instance,
relating_space: ifcopenshell.entity_instance,
related_building_element: ifcopenshell.entity_instance,
parent_boundary: Optional[ifcopenshell.entity_instance] = None,
corresponding_boundary: Optional[ifcopenshell.entity_instance] = None,
) -> None:
"""Modify the relationships of a space boundary relationship
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_boundary(file, boundary=None) -> None:
def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instance) -> None:
"""Removes a space boundary
The relating space or related building element is untouched. Only the
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_classification(file, classification=None, attributes=None) -> None:
def edit_classification(
file: ifcopenshell.file, classification: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcClassification
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_classification(file, classification=None, attributes=None) -> None:
:param classification: The IfcClassification entity you want to edit
:type classification: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_reference(file, reference=None, attributes=None) -> None:
def edit_reference(
file: ifcopenshell.file, reference: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcClassificationReference
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_reference(file, reference=None, attributes=None) -> None:
:param reference: The IfcClassificationReference entity you want to edit
:type reference: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_classification(file: ifcopenshell.entity_instance, classification: ifcopenshell.entity_instance) -> None:
def remove_classification(file: ifcopenshell.file, classification: ifcopenshell.entity_instance) -> None:
"""Removes an IfcClassification from the project and all references
The classification and all of its relationships, children references,
@@ -58,15 +58,20 @@ class Usecase:
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
for rel in self.file.by_type("IfcExternalReferenceRelationship"):
if not rel.RelatingReference:
self.file.remove(rel)
def get_references(self, classification):
if self.file.schema != "IFC2X3":
for rel in self.file.by_type("IfcExternalReferenceRelationship"):
if not rel.RelatingReference:
self.file.remove(rel)
def get_references(self, classification: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
results = []
if not classification.HasReferences:
return results
for reference in classification.HasReferences:
results.append(reference)
results.extend(self.get_references(reference))
if self.file.schema == "IFC2X3":
for reference in self.file.by_type("IfcClassificationReference"):
if reference.ReferencedSource == classification:
results.append(reference)
else:
for reference in classification.HasReferences:
results.append(reference)
results.extend(self.get_references(reference))
return results
@@ -19,7 +19,7 @@
import ifcopenshell
def add_metric(file, objective=None) -> None:
def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Add a new metric benchmark
Qualitative constraints may have a series of quantitative benchmarks
@@ -50,7 +50,7 @@ def add_metric(file, objective=None) -> None:
"Name": "Unnamed",
"ConstraintGrade": "NOTDEFINED",
"Benchmark": "EQUALTO",
}
},
)
if settings["objective"]:
benchmark_values = list(settings["objective"].BenchmarkValues or [])
@@ -19,16 +19,18 @@
import ifcopenshell
def add_metric_reference(file, metric=None, reference_path=None) -> None:
def add_metric_reference(
file: ifcopenshell.file, metric: ifcopenshell.entity_instance, reference_path: str
) -> list[ifcopenshell.entity_instance]:
"""
Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute"
Used to reference a value of an attribute of an instance through a metric objective entity.
"""
settings = {"metric": metric, "reference_path": reference_path}
references_created = []
if settings["reference_path"]:
attributes = settings["reference_path"].split(".")
references_created = []
for i in range(len(attributes)):
if i == 0:
reference = file.create_entity("IfcReference")
@@ -40,4 +42,4 @@ def add_metric_reference(file, metric=None, reference_path=None) -> None:
reference.AttributeIdentifier = attributes[i]
references_created[i - 1].InnerReference = reference
references_created.append(reference)
return references_created
return references_created
@@ -19,7 +19,7 @@
import ifcopenshell
def add_objective(file) -> None:
def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
"""Add a new objective constraint
Parametric constraints may be defined by the user. The constraint is defined
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_metric(file, metric=None, attributes=None) -> None:
def edit_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edit the attributes of a metric
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_metric(file, metric=None, attributes=None) -> None:
:param metric: The IfcMetric you want to edit.
:type metric: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_objective(file, objective=None, attributes=None) -> None:
def edit_objective(
file: ifcopenshell.file, objective: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edit the attributes of a objective
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_objective(file, objective=None, attributes=None) -> None:
:param objective: The IfcObjective you want to edit.
:type objective: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_constraint(file, constraint=None) -> None:
def remove_constraint(file: ifcopenshell.file, constraint: ifcopenshell.entity_instance) -> None:
"""Remove a constraint (typically an objective)
Removes a constraint definition and all of its associations to any
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_metric(file, metric=None) -> None:
def remove_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance) -> None:
"""Remove a metric benchmark
Removes a metric benchmark and all of its associations to any products
@@ -17,9 +17,11 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
def remove_context(file: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance) -> None:
def remove_context(file: ifcopenshell.file, context: ifcopenshell.entity_instance) -> None:
"""Removes an IfcGeometricRepresentationContext
Any representation geometry that is assigned to the context is also
@@ -19,9 +19,14 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.guid
from typing import Union
def assign_control(file, relating_control=None, related_object=None) -> None:
def assign_control(
file: ifcopenshell.file,
relating_control: ifcopenshell.entity_instance,
related_object: ifcopenshell.entity_instance,
) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a planning control or constraint to an object
IFC can describe concepts that control other objects. For example, a
@@ -19,9 +19,14 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
def unassign_control(file, relating_control=None, related_object=None) -> None:
def unassign_control(
file: ifcopenshell.file,
relating_control: ifcopenshell.entity_instance,
related_object: ifcopenshell.entity_instance,
) -> Union[ifcopenshell.entity_instance, None]:
"""Unassigns a planning control or constraint to an object
:param relating_control: The IfcControl entity that is creating the
@@ -18,22 +18,29 @@
import ifcopenshell.api
import ifcopenshell.guid
from typing import Optional
def add_cost_item(file, cost_schedule=None, cost_item=None) -> None:
def add_cost_item(
file: ifcopenshell.file,
cost_schedule: Optional[ifcopenshell.entity_instance] = None,
cost_item: Optional[ifcopenshell.entity_instance] = None,
) -> ifcopenshell.entity_instance:
"""Add a new cost item
A cost item represents a single line item in a cost schedule. Cost items
may then be broken down into cost subitems.
Either `cost_schedule` or `cost_item` must be provided.
:param cost_schedule: If the cost item is to be added as a root or top
level cost item to a cost schedule, the IfcCostSchedule may be
specified. This is mutually exlclusive to the cost_item parameter.
:type cost_schedule: ifcopenshell.entity_instance
:type cost_schedule: ifcopenshell.entity_instance, optional.
:param cost_item: If the cost item is to be added as a subitem to an
existing cost item, the parent IfcCostItem may be specified. This is
mutually exclusive to the cost_schedule parameter.
:type cost_item: ifcopenshell.entity_instance
:type cost_item: ifcopenshell.entity_instance, optional
:return: The newly created IfcCostItem
:rtype: ifcopenshell.entity_instance
@@ -62,7 +69,7 @@ def add_cost_item(file, cost_schedule=None, cost_item=None) -> None:
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [cost_item],
"RelatingControl": settings["cost_schedule"],
}
},
)
elif settings["cost_item"]:
ifcopenshell.api.run(
@@ -19,7 +19,9 @@
import ifcopenshell.api
def add_cost_item_quantity(file, cost_item=None, ifc_class="IfcQuantityCount") -> None:
def add_cost_item_quantity(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, ifc_class: str = "IfcQuantityCount"
) -> ifcopenshell.entity_instance:
"""Adds a new quantity associated with a cost item
Cost items calculate their subtotal by multiplying the sum of the cost
@@ -19,9 +19,10 @@
import ifcopenshell.api
import ifcopenshell.util.date
from datetime import datetime
from typing import Optional
def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None:
def add_cost_schedule(file: ifcopenshell.file, name: Optional[str] = None, predefined_type="NOTDEFINED") -> None:
"""Add a new cost schedule
A cost schedule is a group of cost items which typically represent a
@@ -61,5 +62,17 @@ def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None:
predefined_type=settings["predefined_type"],
name=settings["name"],
)
cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
if file.schema == "IFC2X3":
cost_schedule.UpdateDate = createIfcDateAndTime(file, datetime.now())
else:
cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
return cost_schedule
def createIfcDateAndTime(file: ifcopenshell.file, dt: datetime):
ifc_dt = file.create_entity("IfcDateAndTime")
ifc_dt.DateComponent = file.create_entity(
"IfcCalendarDate", **ifcopenshell.util.date.datetime2ifc(dt, "IfcCalendarDate")
)
ifc_dt.TimeComponent = file.create_entity("IfcLocalTime", **ifcopenshell.util.date.datetime2ifc(dt, "IfcLocalTime"))
return ifc_dt
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_cost_value(file, parent=None) -> None:
def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Adds a new value or subvalue to a cost item
A cost item's subtotal can be specified in two ways.
@@ -17,9 +17,15 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api
from typing import Optional
def assign_cost_item_quantity(file, cost_item=None, products=None, prop_name="") -> None:
def assign_cost_item_quantity(
file: ifcopenshell.file,
cost_item: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
prop_name: Optional[str] = "",
) -> None:
"""Adds a cost item quantity that is parametrically connected to a product
A cost item may have its subtotal calculated by multiplying a unit value
@@ -19,7 +19,9 @@
import ifcopenshell.api
def assign_cost_value(file, cost_item=None, cost_rate=None) -> None:
def assign_cost_value(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, cost_rate: ifcopenshell.entity_instance
) -> None:
"""Assigns a cost value to a cost item from a schedule of rates
Instead of assigning cost values from scratch for each cost item in a
@@ -21,7 +21,7 @@ import ifcopenshell.util.date
import ifcopenshell.util.resource
def calculate_cost_item_resource_value(file, cost_item=None) -> None:
def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> None:
"""Calculates the total cost of all resources associated with a cost item
A cost item may have construction resources (e.g. equipment, material,
@@ -19,9 +19,14 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
def copy_cost_item(file, cost_item=None) -> None:
def copy_cost_item(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
# TODO: currently it never returns list of duplicated cost items
# though it is stated in the docs
"""Copies all cost items and related relationships
The following relationships are also duplicated:
@@ -33,7 +38,7 @@ def copy_cost_item(file, cost_item=None) -> None:
:param cost_item: The cost item to be duplicated
:type cost_item: ifcopenshell.entity_instance
:return: The duplicated cost item or the list of duplicated cost items if the latter has children
:rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
:rtype: ifcopenshell.entity_instance or list[ifcopenshell.entity_instance]
Example:
.. code:: python
@@ -55,7 +60,7 @@ def copy_cost_item(file, cost_item=None) -> None:
class Usecase:
def execute(self):
self.new_cost_items = []
self.duplicate_cost_item(self.settings["cost_item"])
return self.duplicate_cost_item(self.settings["cost_item"])
def duplicate_cost_item(self, cost_item):
new_cost_item = ifcopenshell.util.element.copy_deep(self.file, cost_item)
@@ -20,7 +20,9 @@ import ifcopenshell.util.element
import ifcopenshell.api
def copy_cost_item_values(file, source=None, destination=None) -> None:
def copy_cost_item_values(
file: ifcopenshell.file, source: ifcopenshell.entity_instance, destination: ifcopenshell.entity_instance
) -> None:
"""Copies all cost values from one cost item to another
Any previously existing values will be removed. The entire value is
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_cost_item(file, cost_item=None, attributes=None) -> None:
def edit_cost_item(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcCostItem
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_cost_item(file, cost_item=None, attributes=None) -> None:
:param cost_item: The IfcCostItem entity you want to edit
:type cost_item: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> None:
def edit_cost_item_quantity(
file: ifcopenshell.file, physical_quantity: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcPhysicalQuantity
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> No
:param physical_quantity: The IfcPhysicalQuantity entity you want to edit
:type physical_quantity: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None:
def edit_cost_schedule(
file: ifcopenshell.file, cost_schedule: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcCostSchedule
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None:
:param cost_schedule: The IfcCostSchedule entity you want to edit
:type cost_schedule: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -19,9 +19,12 @@
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.element
from typing import Any
def edit_cost_value(file, cost_value=None, attributes=None) -> None:
def edit_cost_value(
file: ifcopenshell.file, cost_value: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcCostValue
For more information about the attributes and data types of an
@@ -30,7 +33,7 @@ def edit_cost_value(file, cost_value=None, attributes=None) -> None:
:param cost_value: The IfcCostValue entity you want to edit
:type cost_value: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -22,7 +22,7 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
def edit_cost_value_formula(file, cost_value=None, formula=None) -> None:
def edit_cost_value_formula(file: ifcopenshell.file, cost_value: ifcopenshell.entity_instance, formula: str) -> None:
"""Sets a cost value based on a formula, similar to formulas in spreadsheets
Costs may be made up of many components (e.g. labour, material, waste
@@ -21,7 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element
def remove_cost_item(file, cost_item=None) -> None:
def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> None:
"""Removes a cost item
All associated relationships with the cost item are also removed,
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_cost_item_quantity(file, cost_item=None, physical_quantity=None) -> None:
def remove_cost_item_quantity(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, physical_quantity: ifcopenshell.entity_instance
) -> None:
"""Removes a quantity assigned to a cost item
If the quantity is part of a product (e.g. wall), then the quantity will
@@ -21,7 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element
def remove_cost_schedule(file, cost_schedule=None) -> None:
def remove_cost_schedule(file: ifcopenshell.file, cost_schedule: ifcopenshell.entity_instance) -> None:
"""Removes a cost schedule
All associated relationships with the cost schedule are also removed,
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_cost_value(file, parent=None, cost_value=None) -> None:
def remove_cost_value(
file: ifcopenshell.file, parent: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance
) -> None:
"""Removes a cost value
The cost value may be assigned either to a cost item, a construction
@@ -19,7 +19,9 @@
import ifcopenshell.api
def unassign_cost_item_quantity(file, cost_item=None, products=None) -> None:
def unassign_cost_item_quantity(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]
) -> None:
"""Removes quantities of a cost item that are calculated on products
A cost item may have quantities that are parametrically calculated on
@@ -18,9 +18,12 @@
import ifcopenshell
import ifcopenshell.guid
from typing import Optional
def add_information(file, parent=None) -> None:
def add_information(
file: ifcopenshell.file, parent: Optional[ifcopenshell.entity_instance] = None
) -> ifcopenshell.entity_instance:
"""Adds a new document information to the project
An IFC document information is a document associated with the project.
@@ -53,11 +56,8 @@ def add_information(file, parent=None) -> None:
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
"""
settings = {"parent": parent}
id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification"
information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"})
parent = settings["parent"]
if not parent and file.by_type("IfcProject"):
parent = file.by_type("IfcProject")[0]
if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
@@ -22,7 +22,7 @@ import ifcopenshell.api
import ifcopenshell.util.element
def remove_information(file, information=None) -> None:
def remove_information(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> None:
"""Removes a document information
All references and associations are also removed.
@@ -41,23 +41,32 @@ def remove_information(file, information=None) -> None:
# ... and remove it!
ifcopenshell.api.run("document.remove_information", model, information=document)
"""
settings = {"information": information}
for reference in settings["information"].HasDocumentReferences or []:
if file.schema == "IFC2X3":
references = information.DocumentReferences or []
else:
references = information.HasDocumentReferences
for reference in references:
ifcopenshell.api.run("document.remove_reference", file, reference=reference)
for rel in settings["information"].IsPointer or []:
for information in rel.RelatedDocuments:
ifcopenshell.api.run("document.remove_information", file, information=information)
for rel in information.IsPointer or []:
for info in rel.RelatedDocuments:
ifcopenshell.api.run("document.remove_information", file, information=info)
for rel in settings["information"].IsPointedTo or []:
if rel.RelatedDocuments == (settings["information"],):
for rel in information.IsPointedTo or []:
if rel.RelatedDocuments == (information,):
# This relationship is non-rooted
file.remove(rel)
for rel in settings["information"].DocumentInfoForObjects or []:
if file.schema == "IFC2X3":
rels = [r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == information]
else:
rels = information.DocumentInfoForObjects
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(settings["information"])
file.remove(information)
@@ -21,7 +21,11 @@ import ifcopenshell.api
import ifcopenshell.guid
def assign_product(file, relating_product=None, related_object=None) -> None:
def assign_product(
file: ifcopenshell.file,
relating_product: ifcopenshell.entity_instance,
related_object: ifcopenshell.entity_instance,
) -> ifcopenshell.entity_instance:
"""Associates a product and an object, typically for annotation
Warning: this is an experimental API.
@@ -104,7 +108,7 @@ def assign_product(file, relating_product=None, related_object=None) -> None:
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [settings["related_object"]],
"RelatingProduct": settings["relating_product"],
}
},
)
if is_grid_axis:
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_text_literal(file, text_literal=None, attributes=None) -> None:
def edit_text_literal(
file: ifcopenshell.file, text_literal: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcTextLiteral
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_text_literal(file, text_literal=None, attributes=None) -> None:
:param reference: The IfcTextLiteral entity you want to edit
:type reference: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -21,7 +21,11 @@ import ifcopenshell.api
import ifcopenshell.util.element
def unassign_product(file, relating_product=None, related_object=None) -> None:
def unassign_product(
file: ifcopenshell.file,
relating_product: ifcopenshell.entity_instance,
related_object: ifcopenshell.entity_instance,
) -> None:
"""Unassigns a product and an object (typically an annotation)
Smart annotation objects can be associated with products so that they
@@ -34,8 +38,8 @@ def unassign_product(file, relating_product=None, related_object=None) -> None:
:param related_object: The object (typically IfcAnnotation) that the
product is related to
:type related_object: ifcopenshell.entity_instance
:return: The created IfcRelAssignsToProduct relationship
:rtype: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -68,4 +72,3 @@ def unassign_product(file, relating_product=None, related_object=None) -> None:
related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
return rel
@@ -352,12 +352,10 @@ class Usecase:
)
def create_curve3d_representation(self):
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"Curve3D",
self.create_curves(),
)
if curves := self.create_curves():
return self.file.createIfcShapeRepresentation(
self.settings["context"], self.settings["context"].ContextIdentifier, "Curve3D", curves
)
def create_curve2d_representation(self):
return self.file.createIfcShapeRepresentation(
@@ -425,7 +423,7 @@ class Usecase:
results.append(self.file.createIfcSweptDiskSolid(curve, radius))
return results
def is_mesh_curve_consequtive(self, geom_data):
def is_mesh_curve_consecutive(self, geom_data):
import blenderbim.tool as tool
bm = tool.Blender.get_bmesh_for_mesh(geom_data)
@@ -475,11 +473,11 @@ class Usecase:
geom_data = self.settings["geometry"]
if isinstance(geom_data, bpy.types.Mesh):
if self.is_mesh_curve_consequtive(geom_data):
if self.file.schema == "IFC2X3":
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
else:
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
if not self.is_mesh_curve_consecutive(geom_data):
return
if self.file.schema == "IFC2X3":
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
import blenderbim.tool as tool
@@ -19,7 +19,7 @@ import ifcopenshell
def add_person(
file: ifcopenshell.entity_instance,
file: ifcopenshell.file,
identification: str = "HSeldon",
family_name: str = "Seldon",
given_name: str = "Hari",
@@ -19,7 +19,7 @@ import ifcopenshell
def add_person_and_organisation(
file: ifcopenshell.entity_instance,
file: ifcopenshell.file,
person: ifcopenshell.entity_instance,
organisation: ifcopenshell.entity_instance,
) -> ifcopenshell.entity_instance:
@@ -22,7 +22,7 @@ import ifcopenshell.api.owner.settings
from typing import Union
def create_owner_history(file: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
def create_owner_history(file: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
"""Creates a new owner history indicating an element was added
Any object in IFC with a unique ID and name (such as physical products,
@@ -19,9 +19,15 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.owner.settings
from typing import Optional
def append_asset(file, library=None, element=None, reuse_identities=None) -> None:
def append_asset(
file: ifcopenshell.file,
library: ifcopenshell.file,
element: ifcopenshell.entity_instance,
reuse_identities: Optional[dict[int, ifcopenshell.entity_instance]] = None,
) -> ifcopenshell.entity_instance:
"""Appends an asset from a library into the active project
A BIM library asset may be a type product (e.g. wall type), product
@@ -24,7 +24,7 @@ from typing import Union
def assign_declaration(
file: ifcopenshell.entity_instance,
file: ifcopenshell.file,
definitions: list[ifcopenshell.entity_instance],
relating_context: ifcopenshell.entity_instance,
) -> Union[ifcopenshell.entity_instance, None]:
@@ -142,6 +142,6 @@ def assign_declaration(
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedDefinitions": list(objects_to_change),
"RelatingContext": relating_context,
}
},
)
return declares
@@ -17,10 +17,11 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.guid
def add_pset(file, product=None, name=None) -> None:
def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance:
"""Adds a new property set to a product
Products, such as physical objects or types in IFC may have properties
@@ -120,27 +121,36 @@ def add_pset(file, product=None, name=None) -> None:
has_property_sets.append(pset)
settings["product"].HasPropertySets = has_property_sets
return pset
elif settings["product"].is_a("IfcMaterialDefinition"):
for definition in settings["product"].HasProperties or []:
if definition.Name == settings["name"]:
# in IFC2X3 IfcMaterialDefinition not yet existed
elif settings["product"].is_a("IfcMaterialDefinition") or settings["product"].is_a("IfcMaterial"):
if file.schema == "IFC2X3":
ifc_class = "IfcExtendedMaterialProperties"
definitions = (d for d in file.by_type("IfcMaterialProperties") if d.Material == settings["product"])
else:
ifc_class = "IfcMaterialProperties"
definitions = settings["product"].HasProperties
for definition in definitions:
# In IFC2X3 not all IfcMaterialProperties has Name
if getattr(definition, "Name") == settings["name"]:
return definition
return file.create_entity(
"IfcMaterialProperties",
ifc_class,
**{
"Name": settings["name"],
"Material": settings["product"],
}
)
elif settings["product"].is_a("IfcProfileDef"):
for definition in settings["product"].HasProperties or []:
if definition.Name == settings["name"]:
return definition
# in IFC2X3 IfcProfileProperties doesn't have Name and we cannot identify them
if file.schema != "IFC2X3":
for definition in settings["product"].HasProperties or []:
if definition.Name == settings["name"]:
return definition
return file.create_entity(
"IfcProfileProperties",
**{
"Name": settings["name"],
"ProfileDefinition": settings["product"],
}
)
kwargs = {}
kwargs["ProfileDefinition"] = settings["product"]
if file.schema != "IFC2X3":
kwargs["Name"] = settings["name"]
return file.create_entity("IfcProfileProperties", **kwargs)
@@ -21,7 +21,7 @@ import ifcopenshell.api
import ifcopenshell.guid
def add_qto(file, product=None, name=None) -> None:
def add_qto(file: ifcopenshell.file, product: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance:
"""Adds a new quantity set to a product
Products, such as physical objects or types in IFC may have quantities
@@ -22,7 +22,7 @@ from typing import Optional, Any, Union
def edit_pset(
file: ifcopenshell.entity_instance,
file: ifcopenshell.file,
pset: ifcopenshell.entity_instance,
name: Optional[str] = None,
properties: Optional[dict[str, Any]] = None,
@@ -18,9 +18,16 @@
import ifcopenshell
import ifcopenshell.util.pset
from typing import Optional, Any
def edit_qto(file, qto=None, name=None, properties=None, pset_template=None) -> None:
def edit_qto(
file: ifcopenshell.file,
qto: ifcopenshell.entity_instance,
name: Optional[str] = None,
properties: Optional[dict[str, Any]] = None,
pset_template: Optional[ifcopenshell.entity_instance] = None,
) -> None:
"""Edits a quantity set and its quantities
At its simplest usage, this may be used to edit the name of a quantity
@@ -50,7 +57,7 @@ def edit_qto(file, qto=None, name=None, properties=None, pset_template=None) ->
:param pset_template: If a quantity set template is provided, this will
be used to determine data types. If no user-defined template is
provided, the built-in buildingSMART templates will be loaded.
:type pset_template: ifcopenshell.entity_instance
:type pset_template: ifcopenshell.entity_instance, optional
:return: None
:rtype: None
@@ -20,7 +20,9 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_pset(file, product=None, pset=None) -> None:
def remove_pset(
file: ifcopenshell.file, product: ifcopenshell.entity_instance, pset: ifcopenshell.entity_instance
) -> None:
"""Removes a property set from a product
All properties that are part of this property set are also removed.
@@ -18,16 +18,17 @@
import ifcopenshell
import ifcopenshell.guid
from typing import Optional
def add_prop_template(
file,
pset_template=None,
name="NewProperty",
description=None,
template_type="P_SINGLEVALUE",
primary_measure_type="IfcLabel",
) -> None:
file: ifcopenshell.file,
pset_template: ifcopenshell.entity_instance,
name: str = "NewProperty",
description: Optional[str] = None,
template_type: str = "P_SINGLEVALUE",
primary_measure_type: str = "IfcLabel",
) -> ifcopenshell.entity_instance:
"""Adds new property templates to a property set template
Assuming you first have a property set template, this allows you to add
@@ -21,11 +21,11 @@ import ifcopenshell.guid
def add_pset_template(
file,
name="New_Pset",
template_type="PSET_TYPEDRIVENOVERRIDE",
applicable_entity="IfcObject,IfcTypeObject",
) -> None:
file: ifcopenshell.file,
name: str = "New_Pset",
template_type: str = "PSET_TYPEDRIVENOVERRIDE",
applicable_entity: str = "IfcObject,IfcTypeObject",
) -> ifcopenshell.entity_instance:
"""Adds a new property set template
This creates a new template for property sets. A template defines what
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_prop_template(file, prop_template=None, attributes=None) -> None:
def edit_prop_template(
file: ifcopenshell.file, prop_template: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcSimplePropertyTemplate
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_prop_template(file, prop_template=None, attributes=None) -> None:
:param prop_template: The IfcSimplePropertyTemplate entity you want to edit
:type prop_template: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -43,7 +47,20 @@ def edit_prop_template(file, prop_template=None, attributes=None) -> None:
ifcopenshell.api.run("pset_template.edit_prop_template", model,
prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"})
"""
settings = {"prop_template": prop_template, "attributes": attributes or {}}
if enum_values := attributes.get("Enumerators", None):
prop_name = attributes.get("Name", None) or getattr(prop_template, "Name", None) or "Unnamed"
primary_measure_type = (
attributes.get("PrimaryMeasureType", None) or getattr(prop_template, "PrimaryMeasureType", None) or "IfcLabel"
)
enum_values = [file.create_entity(primary_measure_type, v) for v in enum_values]
if enumerators := prop_template.Enumerators:
enumerators.Name = prop_name
enumerators.EnumerationValues = enum_values
else:
prop_template.Enumerators = file.create_entity("IfcPropertyEnumeration", prop_name, enum_values)
for name, value in settings["attributes"].items():
setattr(settings["prop_template"], name, value)
if "Enumerators" in attributes:
del attributes["Enumerators"]
for name, value in attributes.items():
setattr(prop_template, name, value)
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_pset_template(file, pset_template=None, attributes=None) -> None:
def edit_pset_template(
file: ifcopenshell.file, pset_template: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcPropertySetTemplate
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_pset_template(file, pset_template=None, attributes=None) -> None:
:param pset_template: The IfcPropertySetTemplate entity you want to edit
:type pset_template: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -41,7 +45,7 @@ def edit_pset_template(file, pset_template=None, attributes=None) -> None:
ifcopenshell.api.run("pset_template.edit_pset_template", model,
pset_template=template, attributes={"Name": "ABC_RiskFactors"})
"""
settings = {"pset_template": pset_template, "attributes": attributes or {}}
settings = {"pset_template": pset_template, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["pset_template"], name, value)
@@ -19,7 +19,7 @@
import ifcopenshell.util.element
def remove_prop_template(file, prop_template=None) -> None:
def remove_prop_template(file: ifcopenshell.file, prop_template: ifcopenshell.entity_instance) -> None:
"""Removes a property template
Note that a property set template should always have at least one
@@ -19,7 +19,7 @@
import ifcopenshell.util.element
def remove_pset_template(file, pset_template=None) -> None:
def remove_pset_template(file: ifcopenshell.file, pset_template: ifcopenshell.entity_instance) -> None:
"""Removes a property set template
All property templates within the property set template are also removed
@@ -17,15 +17,16 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api
from typing import Optional
def add_resource(
file,
parent_resource=None,
ifc_class="IfcCrewResource",
name=None,
predefined_type="NOTDEFINED",
) -> None:
file: ifcopenshell.file,
parent_resource: Optional[ifcopenshell.entity_instance] = None,
ifc_class: str = "IfcCrewResource",
name: Optional[str] = None,
predefined_type: str = "NOTDEFINED",
) -> ifcopenshell.entity_instance:
"""Add a new construction resource
Construction resources may be managed and connected to cost schedules
@@ -48,7 +49,7 @@ def add_resource(
:param parent_resource: If this is a child resource (typically to a crew
resource), then nominate the parent IfcConstructionResource here.
:type parent_resource: ifcopenshell.entity_instance
:type parent_resource: ifcopenshell.entity_instance, optional
:param ifc_class: The class of resource chosen from
IfcConstructionEquipmentResource, IfcConstructionMaterialResource,
IfcConstructionProductResource, IfcCrewResource, IfcLaborResource,
@@ -96,7 +97,7 @@ def add_resource(
related_objects=[resource],
relating_object=settings["parent_resource"],
)
else:
elif file.schema != "IFC2X3":
context = file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
@@ -19,7 +19,9 @@
import ifcopenshell.util.element
def add_resource_quantity(file, resource=None, ifc_class="IfcQuantityCount") -> None:
def add_resource_quantity(
file: ifcopenshell.file, resource: ifcopenshell.entity_instance, ifc_class: str = "IfcQuantityCount"
) -> ifcopenshell.entity_instance:
"""Adds a quantity to a resource
The quantity of a resource represents the "unit quantity" of that
@@ -65,6 +67,7 @@ def add_resource_quantity(file, resource=None, ifc_class="IfcQuantityCount") ->
settings = {"resource": resource, "ifc_class": ifc_class}
quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
# 3 IfcPhysicalSimpleQuantity Value
quantity[3] = 0.0
old_quantity = settings["resource"].BaseQuantity
settings["resource"].BaseQuantity = quantity
@@ -19,7 +19,7 @@
import ifcopenshell.util.date
def add_resource_time(file, resource=None) -> None:
def add_resource_time(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Adds the time that a resource is used for
For labour and equipment resources, the total duration that the resource
@@ -56,10 +56,6 @@ def add_resource_time(file, resource=None) -> None:
ifcopenshell.api.run("resource.edit_resource_time", model,
resource_time=time, attributes={"ScheduleWork": "PT16H"})
"""
settings = {
"resource": resource,
}
resource_time = file.create_entity("IfcResourceTime")
settings["resource"].Usage = resource_time
resource.Usage = resource_time
return resource_time
@@ -21,7 +21,11 @@ import ifcopenshell.api
import ifcopenshell.guid
def assign_resource(file, relating_resource=None, related_object=None) -> None:
def assign_resource(
file: ifcopenshell.file,
relating_resource: ifcopenshell.entity_instance,
related_object: ifcopenshell.entity_instance,
) -> ifcopenshell.entity_instance:
"""Assigns a resource to an object
Two types of objects are typically assigned to resources: products and
@@ -89,7 +93,7 @@ def assign_resource(file, relating_resource=None, related_object=None) -> None:
assignment.is_a("IfclRelAssignsToResource")
and assignment.RelatingResource == settings["relating_resource"]
):
return
return assignment
resource_of = None
if settings["relating_resource"].ResourceOf:
@@ -18,12 +18,13 @@
import math
import ifcopenshell.api
import ifcopenshell.util.constraint
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.resource
def calculate_resource_usage(file, resource=None) -> None:
def calculate_resource_usage(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
"""Calculates the number of resources required to perform scheduled work on a task."""
settings = {"resource": resource}
@@ -18,12 +18,13 @@
import math
import ifcopenshell.api
import ifcopenshell.util.constraint
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.resource
def calculate_resource_work(file, resource=None) -> None:
def calculate_resource_work(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
"""Calculates the work that a resource is used for
This is an unofficial parametric calculation that may be done on a
@@ -55,17 +56,15 @@ def calculate_resource_work(file, resource=None) -> None:
:return None:
:rtype: None:
"""
settings = {"resource": resource}
if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleWork"):
if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleWork"):
return
amount_worked = ifcopenshell.util.resource.get_resource_required_work(settings["resource"])
amount_worked = ifcopenshell.util.resource.get_resource_required_work(resource)
if not amount_worked:
return
if not settings["resource"].Usage:
if not resource.Usage:
ifcopenshell.api.run(
"resource.add_resource_time",
file,
resource=settings["resource"],
resource=resource,
)
settings["resource"].Usage.ScheduleWork = amount_worked
resource.Usage.ScheduleWork = amount_worked
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_resource(file, resource=None, attributes=None) -> None:
def edit_resource(file: ifcopenshell.file, resource: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcResource
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_resource(file, resource=None, attributes=None) -> None:
:param resource: The IfcResource entity you want to edit
:type resource: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -40,7 +42,7 @@ def edit_resource(file, resource=None, attributes=None) -> None:
# Change the name of the resource to "Zone A Crew"
ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"})
"""
settings = {"resource": resource, "attributes": attributes or {}}
settings = {"resource": resource, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["resource"], name, value)
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> None:
def edit_resource_quantity(
file: ifcopenshell.file, physical_quantity: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IFC quantity
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> Non
:param physical_quantity: The IfC quantity entity you want to edit
:type physical_quantity: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -51,7 +55,7 @@ def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> Non
"""
settings = {
"physical_quantity": physical_quantity,
"attributes": attributes or {},
"attributes": attributes,
}
for name, value in settings["attributes"].items():

Some files were not shown because too many files have changed in this diff Show More