mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80d31b4e1a | |||
| dac9bfe3d4 | |||
| 58a7ef62d7 | |||
| d2198568b2 | |||
| b087aa09d9 | |||
| fd62ba63a5 | |||
| 16c5fd56ca | |||
| d85f9af790 | |||
| 2f071bd809 | |||
| 7a44f9e8a7 | |||
| 59a19a1a70 | |||
| 6a8b59f2cf | |||
| ec28d3d0af | |||
| f576aadee1 | |||
| fffd7360cb | |||
| 64ea455c01 | |||
| cf635986a6 | |||
| ac8040008a | |||
| df15a37127 | |||
| 6d887d593e | |||
| fa87092fe0 | |||
| 1a4e4678d0 | |||
| 7f2c7eb467 | |||
| c70745826a | |||
| 291bffca95 | |||
| e5a316e509 | |||
| e4aa97091e | |||
| b4f0a346c1 | |||
| 073e471305 | |||
| 4d9f47b3a7 | |||
| 96088c1d08 | |||
| c405ff1adf | |||
| 02cae9c7c2 | |||
| 3c42962635 | |||
| 0336dbabf7 | |||
| a8729933af | |||
| 993ce46fcc | |||
| edfd446833 | |||
| 87c184ef8a | |||
| e9b9113df8 | |||
| f772a53dd5 | |||
| 85c6b6c4da | |||
| 1aaccba803 | |||
| f2eb08a066 | |||
| f460676e54 | |||
| 382e028b90 | |||
| 53d02446bf | |||
| 5a82d7a9d4 | |||
| 69c197c1a4 | |||
| 4027f28c85 | |||
| 3a1154c4ba | |||
| aeb1e4bf49 | |||
| 4fb34c0816 | |||
| 9b562669d2 | |||
| 295d0a677e | |||
| 9aacafd575 | |||
| ce6ce1b975 | |||
| d064ef1237 | |||
| 2db9bd9c39 | |||
| 7622504d68 | |||
| 221dd9a7a2 | |||
| 1b74a99667 | |||
| 9122f006c7 | |||
| 678dbaa66a | |||
| fb88499197 | |||
| f84adf67cd | |||
| cacb38a251 | |||
| 7c6c01e6b9 | |||
| 4e462a3cf7 | |||
| e7f4c6c9cc | |||
| 7bf8c0bbfb | |||
| c4157673f5 | |||
| 16285e0625 | |||
| a07d3b9669 | |||
| 2d0b356203 | |||
| f666053b63 | |||
| 3bdc969efb | |||
| 932817877c | |||
| cb255a18b9 | |||
| 183028570e | |||
| 341cdd4ef7 | |||
| fe68e16ca3 | |||
| a8f0c90d47 | |||
| 39f550529b | |||
| 25c1a14e64 | |||
| 9f7d710119 | |||
| ea592775e4 | |||
| 42be4eb064 | |||
| 8662e20c58 | |||
| da4ce3773f | |||
| 87c9bbe2a7 | |||
| a66ee06e3d | |||
| 9969de58cb | |||
| 6995cafe63 | |||
| eaa80ad08a | |||
| d001180082 | |||
| 3884403231 | |||
| 8d4b5d83e1 | |||
| e1250f2177 | |||
| a2ee920a5f | |||
| fdbe74a432 | |||
| 4e39fb3edd | |||
| c2049246cd | |||
| 335e2b7a78 | |||
| bb8e84e5ec | |||
| 2fa30be0d0 | |||
| c50149ad87 | |||
| da1bdc802d | |||
| a9cba30da6 | |||
| 3665dda87c | |||
| f2696d5352 | |||
| 5e394e1576 | |||
| ebd03e9290 | |||
| c3bfd7354f | |||
| 1d046eaadf | |||
| 2f554db54b | |||
| 54b4cef25e | |||
| c6106e6636 | |||
| 865dbab3ec | |||
| 76e6c63a01 | |||
| 3320accc7a | |||
| ac686db298 | |||
| aca26c7597 | |||
| b7737cb6d3 | |||
| 55026ca996 | |||
| 3fa573e3c1 | |||
| 00ae6809a9 | |||
| c3fbb11093 | |||
| a0d27c3f1c | |||
| 811a908cc3 | |||
| 94e5baf48a | |||
| cd2570cdbb | |||
| 6df6553935 | |||
| 8e6c372a4f | |||
| 371e11de9b | |||
| d2a708fcc4 | |||
| 845d772a08 | |||
| 1e630ede08 | |||
| c50d1ea2fe | |||
| e7eb00eaa3 | |||
| cd6eac3c8f | |||
| 69e21da60c | |||
| 57b905862a | |||
| 51d3a2b77b | |||
| b2920996be | |||
| bdc5a7a01b | |||
| 1c6a9e2c49 | |||
| 8ffb77551f | |||
| 2d35869c41 | |||
| 359dab573b | |||
| 6e2edbf101 | |||
| 4c8f93d737 | |||
| 2789bc565a | |||
| a1e0b8858e | |||
| f075a6178b | |||
| 5fa385e1e0 | |||
| b57141a80c | |||
| d453adb7cf | |||
| c5a7a8680e | |||
| d4ccadb832 | |||
| cbf04546b8 | |||
| 37c0084874 | |||
| 875afd69d5 | |||
| 67f4ed00d0 | |||
| 10d30b0de7 |
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil
|
||||
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil shapely
|
||||
pip install src/bcf --no-deps
|
||||
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
|
||||
-DCOLLADA_SUPPORT=Off \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3;4x3_add1;4x3_add2" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
-DCGAL_INCLUDE_DIR=/usr/include \
|
||||
|
||||
@@ -36,7 +36,9 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
endif()
|
||||
|
||||
# use extra version to make pre-release using eg semver
|
||||
set(EXTRA_VERSION "-alpha.3")
|
||||
if(NOT DEFINED EXTRA_VERSION)
|
||||
set(EXTRA_VERSION "-alpha.3")
|
||||
endif()
|
||||
|
||||
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
|
||||
option(WASM_BUILD "Build a WebAssembly binary." OFF)
|
||||
@@ -1147,9 +1149,10 @@ endif()
|
||||
|
||||
# Packaging
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES
|
||||
.git
|
||||
.gitignore
|
||||
"/\\\\.git"
|
||||
"/build/"
|
||||
)
|
||||
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/..;/")
|
||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
|
||||
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
@@ -13,7 +13,7 @@ readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
keywords = ["IFC", "BCF", "BIM"]
|
||||
dependencies = [
|
||||
"xsdata",
|
||||
"xsdata>=24.4",
|
||||
"numpy",
|
||||
"ifcopenshell",
|
||||
]
|
||||
|
||||
@@ -10,14 +10,14 @@ from xsdata.formats.dataclass.serializers.config import SerializerConfig
|
||||
def build_xml_parser(context: Optional[XmlContext] = None) -> XmlParser:
|
||||
"""Return a parser for an XML file."""
|
||||
parser = XmlParser(context=context or XmlContext())
|
||||
parser.register_namespace("xs", "http://www.w3.org/2001/XMLSchema")
|
||||
parser.register_namespace(ns_map=parser.ns_map, prefix="xs", uri="http://www.w3.org/2001/XMLSchema")
|
||||
return parser
|
||||
|
||||
|
||||
def build_serializer(context: Optional[XmlContext] = None) -> XmlSerializer:
|
||||
"""Return a serializer for an XML file."""
|
||||
return XmlSerializer(
|
||||
config=SerializerConfig(pretty_print=True),
|
||||
config=SerializerConfig(indent=" "),
|
||||
context=context or XmlContext(),
|
||||
)
|
||||
|
||||
@@ -36,8 +36,9 @@ class AbstractXmlParserSerializer(Protocol):
|
||||
xml: The XML file as bytes.
|
||||
clazz: The class to parse to.
|
||||
"""
|
||||
...
|
||||
|
||||
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
def serialize(self, obj: object, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
"""
|
||||
Serialize an object to XML.
|
||||
|
||||
@@ -48,6 +49,7 @@ class AbstractXmlParserSerializer(Protocol):
|
||||
Returns:
|
||||
The XML as string.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class XmlParserSerializer:
|
||||
@@ -68,7 +70,7 @@ class XmlParserSerializer:
|
||||
"""
|
||||
return self.parser.from_bytes(xml, clazz)
|
||||
|
||||
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
|
||||
def serialize(self, obj: object, ns_map: Optional[dict[Optional[str], str]] = None) -> str:
|
||||
"""
|
||||
Serialize an object to XML.
|
||||
|
||||
@@ -79,5 +81,5 @@ class XmlParserSerializer:
|
||||
Returns:
|
||||
The XML as string.
|
||||
"""
|
||||
ns_map = ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
|
||||
ns_map = ns_map or self.parser.ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
|
||||
return self.serializer.render(obj, ns_map)
|
||||
|
||||
+10
-31
@@ -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
|
||||
@@ -275,34 +277,6 @@ endif
|
||||
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/5b/ef/f97c3e1a7efa00e989a793fe15297214fc95ad7d9e3810586bd08ce9f0f3/xmlschema-2.0.2.tar.gz
|
||||
cd dist/working && tar -xzvf xmlschema*
|
||||
cp -r dist/working/xmlschema-2.0.2/xmlschema dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/ad/c7/17c9d16320d8e2cfdb27d2fd298b5e8f7a3f211025b0c1bc7a39a85bd690/xsdata-22.11.tar.gz
|
||||
cd dist/working && tar -xzvf xsdata*
|
||||
cp -r dist/working/xsdata-22.11/xsdata dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/11/bc/5afb61dd5d863e5cf77cd952445c50c17e65953405986f19e97e4389692a/elementpath-3.0.2.tar.gz
|
||||
cd dist/working && tar -xzvf elementpath*
|
||||
cp -r dist/working/elementpath-3.0.2/elementpath dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by bcf
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/21/9f/b251f7f8a76dec1d6651be194dfba8fb8d7781d10ab3987190de8391d08e/six-1.14.0.tar.gz
|
||||
cd dist/working && tar -xzvf six*
|
||||
cp -r dist/working/six-1.14.0/six.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/18/4d/8d522136c37d9e1ea74062b41b8d5e1318ebf45063ae46ce72ed60af223b/lark-parser-0.8.5.tar.gz
|
||||
@@ -364,7 +338,12 @@ endif
|
||||
# Provides Brickschema functionality
|
||||
cd dist/working && . env/bin/activate && $(PIP) install "brickschema[persistence]==0.7.6a2" --target=./site-packages
|
||||
# Required for SVG to DXF conversion
|
||||
cd dist/working && . env/bin/activate && $(PIP) install "ezdxf" --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install ezdxf --target=./site-packages
|
||||
# Required by bcf
|
||||
cd dist/working && . env/bin/activate && $(PIP) install xsdata --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install xmlschema --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install elementpath --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install six --target=./site-packages
|
||||
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
|
||||
@@ -18,7 +18,21 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import site
|
||||
|
||||
# Ensure we don't try to import bpy or blenderbim.bim
|
||||
# to support running core tests.
|
||||
# We assume if bpy was never loaded in current python session
|
||||
# then we're not in Blender. It's still possible to use
|
||||
# bpy in core and core tests for annotations using TYPE_CHECKING.
|
||||
IN_BLENDER = sys.modules.get("bpy", None)
|
||||
if IN_BLENDER:
|
||||
import bpy
|
||||
import platform
|
||||
import traceback
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import addon_utils
|
||||
from collections import deque
|
||||
|
||||
bl_info = {
|
||||
"name": "BlenderBIM",
|
||||
@@ -32,16 +46,163 @@ bl_info = {
|
||||
"category": "System",
|
||||
}
|
||||
|
||||
if sys.modules.get("bpy", None):
|
||||
last_error = None
|
||||
last_actions: deque = deque(maxlen=10)
|
||||
|
||||
|
||||
def get_debug_info():
|
||||
version = ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
addon.bl_info.get("version", (-1, -1, -1))
|
||||
for addon in addon_utils.modules()
|
||||
if addon.bl_info["name"] == "BlenderBIM"
|
||||
][0]
|
||||
]
|
||||
)
|
||||
return {
|
||||
"os": platform.system(),
|
||||
"os_version": platform.version(),
|
||||
"python_version": platform.python_version(),
|
||||
"architecture": platform.architecture(),
|
||||
"machine": platform.machine(),
|
||||
"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 IN_BLENDER:
|
||||
# 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
|
||||
# site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
|
||||
|
||||
import blenderbim.bim
|
||||
try:
|
||||
import blenderbim.bim
|
||||
import ifcopenshell.api
|
||||
|
||||
def register():
|
||||
blenderbim.bim.register()
|
||||
def log_api(usecase_path, ifc_file, settings):
|
||||
last_actions.append(
|
||||
{
|
||||
"type": "ifcopenshell.api",
|
||||
"name": usecase_path,
|
||||
"settings": ifcopenshell.api.serialise_settings(settings),
|
||||
}
|
||||
)
|
||||
|
||||
def unregister():
|
||||
blenderbim.bim.unregister()
|
||||
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
|
||||
|
||||
def register():
|
||||
blenderbim.bim.register()
|
||||
|
||||
def unregister():
|
||||
blenderbim.bim.unregister()
|
||||
|
||||
except:
|
||||
last_error = traceback.format_exc()
|
||||
|
||||
print(last_error)
|
||||
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):
|
||||
bl_label = "BlenderBIM Fatal Error"
|
||||
bl_idname = "SCENE_PT_error_message"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
info = get_debug_info()
|
||||
|
||||
layout = self.layout
|
||||
layout.alert = True
|
||||
layout.label(text="BlenderBIM could not load.", icon="ERROR")
|
||||
if info["os"] == "Windows":
|
||||
layout.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
|
||||
else:
|
||||
layout.label(text="View the console for full logs.", icon="CONSOLE")
|
||||
box = layout.box()
|
||||
py = ".".join(info["python_version"].split(".")[0:2])
|
||||
b3d = ".".join(info["blender_version"].split(".")[0:2])
|
||||
box.label(text=f"Blender {b3d} {info['os']} {info['machine']}", icon="BLENDER")
|
||||
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/troubleshooting.html#installation-issues"
|
||||
|
||||
layout.label(text="Try Reinstalling:", icon="IMPORT")
|
||||
op = layout.operator("bim.open_uri", text="Re-download Add-on")
|
||||
bbim_date = info["blenderbim_version"].split(".")[-1]
|
||||
py_tag = py.replace(".", "")
|
||||
if "Linux" in info["os"]:
|
||||
os = "linux"
|
||||
elif "Darwin" in info["os"]:
|
||||
if "arm64" in info["machine"]:
|
||||
os = "macosm1"
|
||||
else:
|
||||
os = "macos"
|
||||
else:
|
||||
os = "win"
|
||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_date}/blenderbim-{bbim_date}-py{py_tag}-{os}.zip"
|
||||
|
||||
class OpenUri(bpy.types.Operator):
|
||||
bl_idname = "bim.open_uri"
|
||||
bl_label = "Open URI"
|
||||
uri: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open(self.uri)
|
||||
return {"FINISHED"}
|
||||
|
||||
class CopyDebugInformation(bpy.types.Operator):
|
||||
bl_idname = "bim.copy_debug_information"
|
||||
bl_label = "Copy Debug Information"
|
||||
bl_description = "Copies debugging information to your clipboard for use in bugreports"
|
||||
|
||||
def execute(self, context):
|
||||
info = format_debug_info(get_debug_info())
|
||||
context.window_manager.clipboard = info
|
||||
return {"FINISHED"}
|
||||
|
||||
class HiddenPanel:
|
||||
@classmethod
|
||||
def false_poll(cls, context):
|
||||
return False
|
||||
|
||||
def register():
|
||||
# Only show our error panel and nothing else in the scene tab
|
||||
for item_name in dir(bpy.types):
|
||||
item = getattr(bpy.types, item_name)
|
||||
if not hasattr(item, "bl_rna") or not isinstance(item.bl_rna, bpy.types.Panel):
|
||||
continue
|
||||
if getattr(item, "bl_context", None) != "scene":
|
||||
continue
|
||||
|
||||
# Reregister scene panel with a new poll to hide it
|
||||
item.poll = HiddenPanel.false_poll
|
||||
bpy.utils.unregister_class(item)
|
||||
bpy.utils.register_class(item)
|
||||
bpy.utils.register_class(BIM_PT_fatal_error)
|
||||
bpy.utils.register_class(CopyDebugInformation)
|
||||
bpy.utils.register_class(OpenUri)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(OpenUri)
|
||||
bpy.utils.unregister_class(CopyDebugInformation)
|
||||
bpy.utils.unregister_class(BIM_PT_fatal_error)
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import bpy
|
||||
import bpy.utils.previews
|
||||
import blenderbim
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from . import handler, ui, prop, operator, helper
|
||||
from typing import Callable, Union
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
* Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
*
|
||||
* This file is part of BlenderBIM Add-on.
|
||||
*
|
||||
* BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY, without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* You may copy this `schedule.css` template alongside your input schedule
|
||||
* document with the same filename. For example if you have a schedule called
|
||||
* `door_types.ods`, you can create a `door_types.css` in the same folder to
|
||||
* style that schedule.
|
||||
*/
|
||||
|
||||
/**
|
||||
* If you specify a font size in CSS, such as text { font-size: 5; }, all fonts
|
||||
* will be overriden to match that size.
|
||||
*
|
||||
* If your CSS, ODS, XLSX does not specify a font size, the variables below
|
||||
* will specify the default font size.
|
||||
*
|
||||
* If your ODS, XLSX does specify a font size, they will scale linearly based
|
||||
* on the variables below.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--font-size-pt: 12;
|
||||
--font-size-px: 4.13;
|
||||
--font-width: 0.45;
|
||||
}
|
||||
|
||||
text, tspan { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT', 'DejaVu Sans Condensed', 'Liberation Sans', 'Arial Narrow', 'Arial'; }
|
||||
@@ -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.);
|
||||
|
||||
@@ -29,7 +29,7 @@ from mathutils import geometry
|
||||
from mathutils import Vector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from typing import Optional, Callable, Any
|
||||
from typing import Optional, Callable, Any, Union
|
||||
|
||||
|
||||
def draw_attributes(props, layout, copy_operator=None, popup_active_attribute=None):
|
||||
@@ -91,7 +91,11 @@ def import_attributes(ifc_class, props, data, callback=None):
|
||||
|
||||
|
||||
# A more elegant attribute importer signature, intended to supersede import_attributes
|
||||
def import_attributes2(element, props, callback=None):
|
||||
def import_attributes2(
|
||||
element: Union[str, ifcopenshell.entity_instance],
|
||||
props: bpy.types.PropertyGroup,
|
||||
callback: Optional[Callable] = None,
|
||||
) -> None:
|
||||
if isinstance(element, str):
|
||||
attributes = tool.Ifc.schema().declaration_by_name(element).as_entity().all_attributes()
|
||||
info = {a.name(): None for a in attributes}
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -26,6 +26,7 @@ import bmesh
|
||||
import logging
|
||||
import mathutils
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
@@ -303,22 +304,22 @@ class IfcImporter:
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
def is_element_far_away(self, element):
|
||||
def is_element_far_away(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
try:
|
||||
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
point = placement[:, 3][0:3]
|
||||
return self.is_point_far_away(point, is_meters=False)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def is_point_far_away(self, point, is_meters=True):
|
||||
def is_point_far_away(
|
||||
self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]], is_meters: bool = True
|
||||
) -> bool:
|
||||
# Locations greater than 1km are not considered "small sites" according to the georeferencing guide
|
||||
# Users can configure this if they have to handle larger sites but beware of surveying precision
|
||||
limit = self.ifc_import_settings.distance_limit
|
||||
limit = limit if is_meters else (limit / self.unit_scale)
|
||||
coords = point
|
||||
if hasattr(point, "Coordinates"):
|
||||
coords = point.Coordinates
|
||||
coords = getattr(point, "Coordinates", point)
|
||||
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
|
||||
|
||||
def process_context_filter(self):
|
||||
@@ -608,7 +609,9 @@ class IfcImporter:
|
||||
|
||||
threshold = 10000 # Just from experience.
|
||||
|
||||
faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell")]
|
||||
# The check for CfsFaces/Faces/CoordIndex accommodates invalid data from Cadwork
|
||||
# 0 IfcClosedShell.CfsFaces
|
||||
faces = [len(faces) for e in self.file.by_type("IfcClosedShell") if (faces := e[0])]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
return
|
||||
@@ -616,12 +619,14 @@ class IfcImporter:
|
||||
if self.file.schema == "IFC2X3":
|
||||
return
|
||||
|
||||
faces = [len(e.Faces) for e in self.file.by_type("IfcPolygonalFaceSet")]
|
||||
# 2 IfcPolygonalFaceSet.Faces
|
||||
faces = [len(faces) for e in self.file.by_type("IfcPolygonalFaceSet") if (faces := e[2])]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
return
|
||||
|
||||
faces = [len(e.CoordIndex) for e in self.file.by_type("IfcTriangulatedFaceSet")]
|
||||
# 3 IfcTriangulatedFaceSet.CoordIndex
|
||||
faces = [len(index) for e in self.file.by_type("IfcTriangulatedFaceSet") if (index := e[3])]
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
|
||||
@@ -679,7 +684,7 @@ class IfcImporter:
|
||||
props.blender_orthogonal_height = str(offset_point[2])
|
||||
props.has_blender_offset = True
|
||||
|
||||
def get_offset_point(self):
|
||||
def get_offset_point(self) -> Union[npt.NDArray[np.float64], None]:
|
||||
elements_checked = 0
|
||||
# If more than these elements aren't far away, the file probably isn't absolutely positioned
|
||||
element_checking_threshold = 10
|
||||
@@ -702,17 +707,19 @@ 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):
|
||||
def does_element_likely_have_geometry_far_away(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
for representation in element.Representation.Representations:
|
||||
items = []
|
||||
for item in representation.Items:
|
||||
@@ -729,13 +736,14 @@ class IfcImporter:
|
||||
if subelement.is_a("IfcCartesianPoint"):
|
||||
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
|
||||
return True
|
||||
return False
|
||||
|
||||
def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
if obj.data and obj.data.get("has_cartesian_point_offset", None):
|
||||
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
|
||||
elif self.is_point_far_away((matrix[0, 3], matrix[1, 3], matrix[2, 3])):
|
||||
elif self.is_point_far_away((matrix[:3, 3])):
|
||||
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
matrix,
|
||||
@@ -748,7 +756,9 @@ class IfcImporter:
|
||||
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
|
||||
def find_decomposed_ifc_class(self, element, ifc_class):
|
||||
def find_decomposed_ifc_class(
|
||||
self, element: ifcopenshell.entity_instance, ifc_class: str
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if element.is_a(ifc_class):
|
||||
return element
|
||||
rel_aggregates = element.IsDecomposedBy
|
||||
@@ -1500,10 +1510,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 +1921,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 +1929,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],
|
||||
|
||||
@@ -100,7 +100,7 @@ class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
pset = ifcopenshell.util.element.get_pset(element, 'BBIM_Linked_Aggregate')
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
|
||||
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -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="")
|
||||
|
||||
|
||||
@@ -27,15 +27,12 @@ classes = (
|
||||
operator.CopyAttributeToSelection,
|
||||
prop.BIMAttributeProperties,
|
||||
ui.BIM_PT_object_attributes,
|
||||
ui.BIM_PT_material_attributes,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Object.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
|
||||
bpy.types.Material.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Object.BIMAttributeProperties
|
||||
del bpy.types.Material.BIMAttributeProperties
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.tool as tool
|
||||
|
||||
def refresh():
|
||||
AttributesData.is_loaded = False
|
||||
MaterialAttributesData.is_loaded = False
|
||||
|
||||
|
||||
class AttributesData:
|
||||
@@ -51,28 +50,3 @@ class AttributesData:
|
||||
key = "STEP ID"
|
||||
results.append({"name": key, "value": str(value)})
|
||||
return results
|
||||
|
||||
|
||||
class MaterialAttributesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"ifc_definition_id": cls.ifc_definition_id(), "attributes": cls.attributes()}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def ifc_definition_id(cls):
|
||||
return bpy.context.active_object.active_material.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
@classmethod
|
||||
def attributes(cls):
|
||||
results = []
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
|
||||
data = element.get_info()
|
||||
for key, value in data.items():
|
||||
if value is None or isinstance(value, ifcopenshell.entity_instance) or key in ["id", "type"]:
|
||||
continue
|
||||
results.append({"name": key, "value": str(value)})
|
||||
return results
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
@@ -39,14 +40,10 @@ class EnableEditingAttributes(bpy.types.Operator):
|
||||
bl_label = "Enable Editing Attributes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
if self.obj_type == "Object":
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
elif self.obj_type == "Material":
|
||||
obj = bpy.data.materials.get(self.obj)
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
props = obj.BIMAttributeProperties
|
||||
props.attributes.clear()
|
||||
|
||||
@@ -84,13 +81,9 @@ class DisableEditingAttributes(bpy.types.Operator):
|
||||
bl_label = "Disable Editing Attributes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if self.obj_type == "Object":
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
elif self.obj_type == "Material":
|
||||
obj = bpy.data.materials.get(self.obj)
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
props = obj.BIMAttributeProperties
|
||||
props.is_editing_attributes = False
|
||||
return {"FINISHED"}
|
||||
@@ -101,14 +94,10 @@ class EditAttributes(bpy.types.Operator, Operator):
|
||||
bl_label = "Edit Attributes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
if self.obj_type == "Object":
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
elif self.obj_type == "Material":
|
||||
obj = bpy.data.materials.get(self.obj)
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
props = obj.BIMAttributeProperties
|
||||
product = tool.Ifc.get_entity(obj)
|
||||
|
||||
@@ -125,7 +114,7 @@ class EditAttributes(bpy.types.Operator, Operator):
|
||||
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.attributes, callback=callback)
|
||||
ifcopenshell.api.run("attribute.edit_attributes", self.file, product=product, attributes=attributes)
|
||||
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
|
||||
bpy.ops.bim.disable_editing_attributes(obj=obj.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -19,34 +19,34 @@
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.attribute.data import AttributesData, MaterialAttributesData
|
||||
from blenderbim.bim.module.attribute.data import AttributesData
|
||||
|
||||
|
||||
def draw_ui(context, layout, obj_type, attributes):
|
||||
obj = context.active_object if obj_type == "Object" else context.active_object.active_material
|
||||
def draw_ui(context, layout, attributes):
|
||||
obj = context.active_object
|
||||
oprops = obj.BIMObjectProperties
|
||||
props = obj.BIMAttributeProperties
|
||||
|
||||
if props.is_editing_attributes:
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
|
||||
op.obj_type = obj_type
|
||||
op.obj = obj.name
|
||||
op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
|
||||
op.obj_type = obj_type
|
||||
op.obj = obj.name
|
||||
|
||||
blenderbim.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection")
|
||||
else:
|
||||
row = layout.row()
|
||||
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
|
||||
op.obj_type = obj_type
|
||||
op.obj = obj.name
|
||||
|
||||
for attribute in attributes:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=attribute["name"])
|
||||
row.label(text=attribute["value"])
|
||||
# row.label(text=attribute["value"])
|
||||
op = row.operator("bim.select_similar", text=attribute["value"], icon="NONE", emboss=False)
|
||||
op.key = attribute['name']
|
||||
|
||||
|
||||
# TODO: reimplement, see #1222
|
||||
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
|
||||
@@ -72,32 +72,4 @@ class BIM_PT_object_attributes(Panel):
|
||||
def draw(self, context):
|
||||
if not AttributesData.is_loaded:
|
||||
AttributesData.load()
|
||||
draw_ui(context, self.layout, "Object", AttributesData.data["attributes"])
|
||||
|
||||
|
||||
class BIM_PT_material_attributes(Panel):
|
||||
bl_label = "Material Attributes"
|
||||
bl_idname = "BIM_PT_material_attributes"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not IfcStore.get_file():
|
||||
return False
|
||||
try:
|
||||
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
|
||||
except:
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
if not MaterialAttributesData.is_loaded:
|
||||
MaterialAttributesData.load()
|
||||
elif (
|
||||
context.active_object.active_material.BIMObjectProperties.ifc_definition_id
|
||||
!= MaterialAttributesData.data["ifc_definition_id"]
|
||||
):
|
||||
MaterialAttributesData.load()
|
||||
|
||||
draw_ui(context, self.layout, "Material", MaterialAttributesData.data["attributes"])
|
||||
draw_ui(context, self.layout, AttributesData.data["attributes"])
|
||||
|
||||
@@ -155,7 +155,7 @@ class ReferenceUI:
|
||||
blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.add_manual_classification_reference", text="Save", icon="CHECKMARK")
|
||||
op.type = self.data.data["object_type"]
|
||||
op.obj_type = self.data.data["object_type"]
|
||||
row.operator("bim.disable_adding_manual_classification_reference", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
|
||||
@@ -82,6 +82,7 @@ classes = (
|
||||
prop.CostItem,
|
||||
prop.CostItemQuantity,
|
||||
prop.CostItemType,
|
||||
prop.ScheduleColumn,
|
||||
prop.BIMCostProperties,
|
||||
ui.BIM_PT_cost_schedules,
|
||||
ui.BIM_PT_cost_item_quantities,
|
||||
|
||||
@@ -19,10 +19,13 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||
from typing import Any
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -34,6 +37,7 @@ def refresh():
|
||||
class CostSchedulesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
_cost_values: dict[int, dict[str, Any]]
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
@@ -136,7 +140,7 @@ class CostSchedulesData:
|
||||
data["UnitBasisUnitSymbol"] = "U"
|
||||
if cost_value.Category == "*":
|
||||
is_sum = True
|
||||
cost_quantity = data["TotalCostQuantity"] or 1
|
||||
cost_quantity = 1 if data["TotalCostQuantity"] is None else data["TotalCostQuantity"]
|
||||
if has_unit_basis:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity / data["UnitBasisValueComponent"]
|
||||
else:
|
||||
@@ -155,6 +159,7 @@ class CostSchedulesData:
|
||||
data["UnitSymbol"] = "-"
|
||||
if cost_item.CostQuantities:
|
||||
quantity = cost_item.CostQuantities[0]
|
||||
data["QuantityType"] = quantity.is_a()
|
||||
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||
if unit:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
|
||||
@@ -73,7 +73,7 @@ class RemoveCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_cost_schedule(tool.Ifc, cost_schedule=tool.Ifc.get().by_id(self.cost_schedule))
|
||||
core.remove_cost_schedule(tool.Ifc, tool.Cost, cost_schedule=tool.Ifc.get().by_id(self.cost_schedule))
|
||||
|
||||
|
||||
class EnableEditingCostSchedule(bpy.types.Operator):
|
||||
@@ -223,36 +223,40 @@ class EditCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
class AssignCostItemType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_cost_item_type"
|
||||
bl_label = "Assign Cost Item Type Product"
|
||||
bl_label = "Assign Cost Item To Product Types"
|
||||
bl_description = "Assign cost item to currently selected or active product types"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
cost_item: bpy.props.IntProperty()
|
||||
prop_name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.assign_cost_item_type(
|
||||
product_types = core.assign_cost_item_type(
|
||||
tool.Ifc,
|
||||
tool.Cost,
|
||||
tool.Spatial,
|
||||
cost_item=tool.Ifc.get().by_id(self.cost_item),
|
||||
prop_name=self.prop_name, # TODO: REVIEW PROP_NAME USABILITY
|
||||
)
|
||||
self.report({"INFO"}, f"Cost item was assigned to {len(product_types)} product types.")
|
||||
|
||||
|
||||
class UnassignCostItemType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_cost_item_type"
|
||||
bl_label = "Unassign Cost Item Type"
|
||||
bl_description = "Unassign cost item from currently selected or active product types"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.unassign_cost_item_type(
|
||||
product_types = core.unassign_cost_item_type(
|
||||
tool.Ifc,
|
||||
tool.Cost,
|
||||
tool.Spatial,
|
||||
cost_item=tool.Ifc.get().by_id(self.cost_item),
|
||||
product_types=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else [],
|
||||
product_types=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else None,
|
||||
)
|
||||
self.report({"INFO"}, f"Cost item was unassigned from {len(product_types)} product types.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -287,9 +291,11 @@ class UnassignCostItemQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Ifc,
|
||||
tool.Cost,
|
||||
cost_item=tool.Ifc.get().by_id(self.cost_item),
|
||||
products=[tool.Ifc.get().by_id(self.related_object)]
|
||||
if self.related_object
|
||||
else tool.Spatial.get_selected_products(),
|
||||
products=(
|
||||
[tool.Ifc.get().by_id(self.related_object)]
|
||||
if self.related_object
|
||||
else tool.Spatial.get_selected_products()
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -509,7 +515,7 @@ class SelectCostScheduleProducts(bpy.types.Operator):
|
||||
|
||||
|
||||
class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
||||
bl_idname = "import_cost_schedule_csv.bim"
|
||||
bl_idname = "bim.import_cost_schedule_csv"
|
||||
bl_label = "Import Cost Schedule CSV"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".csv"
|
||||
@@ -527,6 +533,13 @@ class AddCostColumn(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.scene.BIMCostProperties.cost_column:
|
||||
cls.poll_message_set("Cost column name is empty")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
core.add_cost_column(tool.Cost, self.name)
|
||||
return {"FINISHED"}
|
||||
@@ -538,7 +551,7 @@ class RemoveCostColumn(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
def execute(self, context):
|
||||
core.remove_cost_column(tool.Cost, self.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -656,13 +669,17 @@ class ExportCostSchedules(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Export a cost schedule to a CSV, XSLX OR ODS file"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
format: bpy.props.EnumProperty("Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
format: bpy.props.EnumProperty(name="Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
|
||||
directory: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_folder: bpy.props.BoolProperty(
|
||||
name="Filter Folders",
|
||||
default=True,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
cost_schedule = tool.Ifc.get().by_id(self.cost_schedule) if self.cost_schedule else None
|
||||
r = core.export_cost_schedules(
|
||||
tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule
|
||||
tool.Cost, filepath=self.directory, format=self.format, cost_schedule=cost_schedule
|
||||
)
|
||||
if isinstance(r, str):
|
||||
self.report({"ERROR"}, r)
|
||||
@@ -676,6 +693,7 @@ class ExportCostSchedules(bpy.types.Operator):
|
||||
def draw(self, context):
|
||||
self.layout.label(text="Choose a format")
|
||||
self.layout.prop(self, "format")
|
||||
self.layout.label(text="Select a directory.")
|
||||
|
||||
|
||||
class ClearCostItemAssignments(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -175,6 +175,10 @@ def update_active_cost_item_resources(self, context):
|
||||
bpy.ops.bim.load_cost_item_resource_quantities()
|
||||
|
||||
|
||||
class ScheduleColumn(PropertyGroup):
|
||||
schedule_id: IntProperty()
|
||||
|
||||
|
||||
class BIMCostProperties(PropertyGroup):
|
||||
cost_schedule_predefined_types: EnumProperty(
|
||||
items=get_schedule_predefined_types, name="Predefined Type", default=None
|
||||
@@ -210,9 +214,14 @@ class BIMCostProperties(PropertyGroup):
|
||||
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
|
||||
cost_value_formula: StringProperty(name="Cost Value Formula")
|
||||
cost_column: StringProperty(name="Cost Column")
|
||||
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
||||
should_show_column_ui: BoolProperty(
|
||||
name="Should Show Column UI",
|
||||
description="Display UI for adding cost schedule columns, column names represent a category for cost item values",
|
||||
default=False,
|
||||
)
|
||||
should_show_currency_ui: BoolProperty(name="Should Show Currency UI", default=False)
|
||||
columns: CollectionProperty(name="Columns", type=StrProperty)
|
||||
columns: CollectionProperty(name="Active Schedule Columns", type=StrProperty)
|
||||
columns_storage: CollectionProperty(name="Columns", type=ScheduleColumn)
|
||||
active_column_index: IntProperty(name="Active Column Index")
|
||||
cost_item_products: CollectionProperty(name="Cost Item Products", type=CostItemQuantity)
|
||||
active_cost_item_product_index: IntProperty(name="Active Cost Item Product Index")
|
||||
|
||||
@@ -21,6 +21,7 @@ import blenderbim.bim.module.cost.prop as CostProp
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.cost.data import CostSchedulesData
|
||||
from typing import Any
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
@@ -53,7 +54,7 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row.label(text="No Cost Schedules found.", icon="TEXT")
|
||||
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="")
|
||||
row.operator("import_cost_schedule_csv.bim",icon="IMPORT",text="")
|
||||
row.operator("bim.import_cost_schedule_csv",icon="IMPORT",text="")
|
||||
|
||||
for schedule in CostSchedulesData.data["schedules"]:
|
||||
self.draw_cost_schedule_ui(schedule)
|
||||
@@ -178,24 +179,33 @@ class BIM_PT_cost_schedules(Panel):
|
||||
"active_cost_item_index",
|
||||
)
|
||||
if self.props.active_cost_item_id:
|
||||
cost_item = CostSchedulesData.data["cost_items"][ifc_definition_id]
|
||||
if self.props.cost_item_editing_type == "ATTRIBUTES":
|
||||
self.draw_editable_cost_item_attributes_ui()
|
||||
elif self.props.cost_item_editing_type == "QUANTITIES":
|
||||
self.draw_editable_cost_item_quantities_ui()
|
||||
self.draw_editable_cost_item_quantities_ui(cost_item)
|
||||
elif self.props.cost_item_editing_type == "VALUES":
|
||||
self.draw_editable_cost_item_values_ui()
|
||||
|
||||
def draw_editable_cost_item_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.cost_item_attributes, self.layout)
|
||||
|
||||
def draw_editable_cost_item_quantities_ui(self):
|
||||
def draw_editable_cost_item_quantities_ui(self, cost_item: dict[str, Any]):
|
||||
quantities = CostSchedulesData.data["cost_quantities"]
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "quantity_types", text="")
|
||||
# In IFC, all quantities of IfcCostTime should have 1 type.
|
||||
if quantities:
|
||||
quantity_class = cost_item["QuantityType"]
|
||||
row.label(text=quantity_class)
|
||||
else:
|
||||
row.prop(self.props, "quantity_types", text="")
|
||||
quantity_class = self.props.quantity_types
|
||||
|
||||
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.ifc_class = self.props.quantity_types
|
||||
op.ifc_class = quantity_class
|
||||
|
||||
for quantity in CostSchedulesData.data["cost_quantities"]:
|
||||
for quantity in quantities:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=quantity["name"])
|
||||
row.label(text=quantity["value"])
|
||||
@@ -634,7 +644,7 @@ class BIM_UL_cost_items_trait:
|
||||
layout.label(text=cost_item["UnitBasisUnitSymbol"])
|
||||
|
||||
def draw_total_quantity_column(self, layout, cost_item):
|
||||
if cost_item["TotalCostQuantity"]:
|
||||
if cost_item["TotalCostQuantity"] is not None:
|
||||
label = "{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||
layout.label(text=label)
|
||||
else:
|
||||
|
||||
@@ -23,6 +23,7 @@ import ifccsv
|
||||
import logging
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -24,7 +24,6 @@ import random
|
||||
import logging
|
||||
import platform
|
||||
import subprocess
|
||||
import addon_utils
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
@@ -34,38 +33,17 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.debug as core
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.bim.import_ifc as import_ifc
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim import get_debug_info, format_debug_info
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class CopyDebugInformation(bpy.types.Operator):
|
||||
bl_idname = "bim.copy_debug_information"
|
||||
bl_label = "Copy Debug Information"
|
||||
bl_description = "Copies debugging information to your clipboard for use in bugreports"
|
||||
bl_description = "Copies debugging information to your clipboard for use in bug reports"
|
||||
|
||||
def execute(self, context):
|
||||
version = ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
addon.bl_info.get("version", (-1, -1, -1))
|
||||
for addon in addon_utils.modules()
|
||||
if addon.bl_info["name"] == "BlenderBIM"
|
||||
][0]
|
||||
]
|
||||
)
|
||||
info = {
|
||||
"os": platform.system(),
|
||||
"os_version": platform.version(),
|
||||
"python_version": platform.python_version(),
|
||||
"architecture": platform.architecture(),
|
||||
"machine": platform.machine(),
|
||||
"processor": platform.processor(),
|
||||
"blender_version": bpy.app.version_string,
|
||||
"blenderbim_version": version,
|
||||
"ifc": False,
|
||||
}
|
||||
|
||||
info = get_debug_info()
|
||||
if tool.Ifc.get():
|
||||
info.update(
|
||||
{
|
||||
@@ -76,17 +54,15 @@ class CopyDebugInformation(bpy.types.Operator):
|
||||
}
|
||||
)
|
||||
|
||||
# Format it in a readable way
|
||||
text = "\n".join(f"{k}: {v}" for k, v in info.items())
|
||||
print(text)
|
||||
text = format_debug_info(info)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
command = "echo | set /p nul=" + text.strip()
|
||||
elif platform.system() == "Darwin": # for MacOS
|
||||
command = 'printf "' + text.strip().replace("\n", "\\n").replace('"', "") + '" | pbcopy'
|
||||
else: # Linux
|
||||
command = 'printf "' + text.strip().replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
text_with_backticks = f"```\n{text}\n```"
|
||||
|
||||
print("-" * 80)
|
||||
print(text_with_backticks)
|
||||
print("-" * 80)
|
||||
|
||||
context.window_manager.clipboard = text_with_backticks
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -159,9 +159,7 @@ class BaseDecorator:
|
||||
objecttype = "NOTDEFINED"
|
||||
|
||||
def __init__(self):
|
||||
self.font_id = blf.load(
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
|
||||
)
|
||||
self.font_id = 0 # 0 is the default font
|
||||
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
@@ -404,7 +402,6 @@ class BaseDecorator:
|
||||
line_no += 1 if multiline_to_bottom else -1
|
||||
return
|
||||
|
||||
# 0 is the default font, but we're fancier than that
|
||||
font_id = self.font_id
|
||||
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
@@ -2014,6 +2011,13 @@ class DecorationsHandler:
|
||||
for object_type in ("SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"]
|
||||
if drawing_font := bpy.context.scene.DocProperties.drawing_font:
|
||||
drawing_font_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", drawing_font)
|
||||
if os.path.exists(drawing_font_path):
|
||||
font_id = blf.load(drawing_font_path)
|
||||
for decorator in self.decorators.values():
|
||||
decorator.font_id = font_id
|
||||
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
# TODO: do it in data instead of the handler for performance?
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -315,9 +315,7 @@ class RasterStyleProperty(enum.Enum):
|
||||
SPACE_SHADING = "space.shading"
|
||||
|
||||
|
||||
RASTER_STYLE_PROPERTIES_EXCLUDE = (
|
||||
"scene.render.filepath",
|
||||
)
|
||||
RASTER_STYLE_PROPERTIES_EXCLUDE = ("scene.render.filepath",)
|
||||
|
||||
|
||||
class DocProperties(PropertyGroup):
|
||||
@@ -371,6 +369,7 @@ class DocProperties(PropertyGroup):
|
||||
default=os.path.join("drawings", "assets", "shading_styles.json"), name="Default Shading Styles"
|
||||
)
|
||||
shadingstyle_default: StringProperty(default="Blender Default", name="Default Shading Style")
|
||||
drawing_font: StringProperty(default="OpenGost Type B TT.ttf", name="Drawing Font")
|
||||
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
|
||||
@@ -16,22 +16,21 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
import string
|
||||
import svgwrite
|
||||
import openpyxl
|
||||
|
||||
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
|
||||
from odf.opendocument import load as load_ods
|
||||
from odf.table import Table, TableRow, TableColumn, TableCell
|
||||
from odf.text import P
|
||||
from odf.style import Style
|
||||
from textwrap import wrap
|
||||
from pathlib import Path
|
||||
import string
|
||||
|
||||
FONT_SIZE = 4.13
|
||||
FONT_WIDTH = lambda size: size * 0.45
|
||||
FONT_SIZE_PT = 12
|
||||
FONT_FAMILY = "OpenGost Type B TT"
|
||||
DEBUG = False
|
||||
|
||||
|
||||
@@ -63,11 +62,29 @@ class Scheduler:
|
||||
)
|
||||
self.padding = 1
|
||||
self.margin = 1
|
||||
|
||||
self.parse_css(infile)
|
||||
if infile.endswith("ods"):
|
||||
self.schedule_ods(infile, outfile)
|
||||
elif infile.endswith("xlsx"):
|
||||
self.schedule_xlsx(infile, outfile)
|
||||
|
||||
def parse_css(self, infile):
|
||||
stylesheet_path = os.path.splitext(infile)[0] + ".css"
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "schedule.css")
|
||||
with open(stylesheet_path, "r") as stylesheet:
|
||||
css = stylesheet.read()
|
||||
|
||||
matches = re.search("--font-size-pt:\s*([0-9.]+);", css)
|
||||
self.font_size_pt = float(matches.groups()[0]) if matches else 12 # Default to 12pt
|
||||
matches = re.search("--font-size-px:\s*([0-9.]+);", css)
|
||||
self.font_size_px = float(matches.groups()[0]) if matches else 4.13 # Magic number 4.13px ~= 12pt
|
||||
matches = re.search("--font-width:\s*([0-9.]+);", css)
|
||||
self.font_width = float(matches.groups()[0]) if matches else 0.45 # A magic number for OpenGost
|
||||
|
||||
self.svg.defs.add(self.svg.style(css))
|
||||
|
||||
def schedule_xlsx(self, infile, outfile):
|
||||
workbook = openpyxl.open(infile, data_only=True)
|
||||
sheet = workbook.active
|
||||
@@ -144,8 +161,8 @@ class Scheduler:
|
||||
x += unmerged_width
|
||||
continue
|
||||
|
||||
font_size = cell.font.size or 11 # 11pt default
|
||||
font_size = font_size / FONT_SIZE_PT * FONT_SIZE # Magic?
|
||||
font_size = cell.font.size or self.font_size_pt # 12pt default
|
||||
font_size = font_size / self.font_size_pt * self.font_size_px # Magic?
|
||||
|
||||
text_position = [0.0, 0.0]
|
||||
if cell.alignment.horizontal == "left":
|
||||
@@ -424,9 +441,9 @@ class Scheduler:
|
||||
italic_text = final_cell_style.get("font-style", None) == "italic"
|
||||
# NOTE: very naive since we're scaling text proportionally
|
||||
font_size = (
|
||||
float(final_cell_style.get("font-size", f"{FONT_SIZE_PT}pt")[:-2])
|
||||
/ FONT_SIZE_PT
|
||||
* FONT_SIZE
|
||||
float(final_cell_style.get("font-size", f"{self.font_size_pt}pt")[:-2])
|
||||
/ self.font_size_pt
|
||||
* self.font_size_px
|
||||
)
|
||||
|
||||
if p_tags:
|
||||
@@ -523,10 +540,7 @@ class Scheduler:
|
||||
"""
|
||||
text_lines = [str(p) for p in p_tags]
|
||||
box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
text_params = {
|
||||
"font-size": font_size,
|
||||
"font-family": FONT_FAMILY,
|
||||
}
|
||||
text_params = {"font-size": font_size}
|
||||
if bold:
|
||||
text_params["font-weight"] = "bold"
|
||||
if italic:
|
||||
@@ -546,13 +560,13 @@ class Scheduler:
|
||||
|
||||
text_tag = self.svg.text("", **(text_params | {"font-size": "0"} | box_alignment_params))
|
||||
|
||||
# TODO: should be done in less naive way
|
||||
# without using magic number for FONT_WIDTH
|
||||
# currently it might not work for all fonts and font sizes
|
||||
# TODO: Should be done without using magic number for self.font_width
|
||||
if wrap_text:
|
||||
wrapped_lines = []
|
||||
for line in text_lines:
|
||||
wrapped_line = wrap(line, width=int(cell_width // FONT_WIDTH(font_size)), break_long_words=False)
|
||||
wrapped_line = wrap(
|
||||
line, width=int(cell_width // (font_size * self.font_width)), break_long_words=False
|
||||
)
|
||||
wrapped_lines.extend(wrapped_line)
|
||||
else:
|
||||
wrapped_lines = text_lines
|
||||
|
||||
@@ -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):
|
||||
@@ -379,7 +400,11 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
representation_data["profile_set_usage"] = tool.Geometry.get_profile_set_usage(product)
|
||||
representation_data["text_literal"] = tool.Geometry.get_text_literal(old_representation)
|
||||
|
||||
# TODO: replace with core.add_representation?
|
||||
new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
|
||||
if new_representation is None:
|
||||
self.report({"ERROR"}, "Error creating representation for Blender object.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if tool.Geometry.is_body_representation(new_representation):
|
||||
[
|
||||
@@ -917,7 +942,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Linked_Aggregate")
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new[0],pset=pset)
|
||||
|
||||
if new[0].is_a("IfcElementAssembly"):
|
||||
linked_aggregate_group = [
|
||||
@@ -1029,7 +1054,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
if self.group_name in product_groups_name:
|
||||
return
|
||||
|
||||
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name)
|
||||
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), name=self.group_name)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group)
|
||||
|
||||
def custom_incremental_naming_for_element_assembly(old_to_new):
|
||||
|
||||
@@ -138,3 +138,4 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
name="IFC Interaction Mode",
|
||||
update=update_mode,
|
||||
)
|
||||
representation_from_object: PointerProperty(type=bpy.types.Object)
|
||||
|
||||
@@ -30,7 +30,6 @@ classes = (
|
||||
operator.AssignMaterial,
|
||||
operator.AssignParameterizedProfile,
|
||||
operator.ContractMaterialCategory,
|
||||
operator.CopyMaterial,
|
||||
operator.DisableEditingAssignedMaterial,
|
||||
operator.DisableEditingMaterial,
|
||||
operator.DisableEditingMaterialSetItem,
|
||||
@@ -64,7 +63,6 @@ classes = (
|
||||
prop.BIMMaterialProperties,
|
||||
prop.BIMObjectMaterialProperties,
|
||||
ui.BIM_PT_materials,
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_object_material,
|
||||
ui.BIM_UL_materials,
|
||||
)
|
||||
|
||||
@@ -36,31 +36,17 @@ class MaterialsData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"total_materials": cls.total_materials(),
|
||||
"material_types": cls.material_types(),
|
||||
"profiles": cls.profiles(),
|
||||
"styles": cls.styles(),
|
||||
"contexts": cls.contexts(),
|
||||
"active_styles": cls.active_styles(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
cls.data["material_types"] = cls.material_types()
|
||||
cls.data["total_materials"] = cls.total_materials()
|
||||
cls.data["profiles"] = cls.profiles()
|
||||
cls.data["styles"] = cls.styles()
|
||||
cls.data["contexts"] = cls.contexts()
|
||||
cls.data["active_styles"] = cls.active_styles()
|
||||
|
||||
@classmethod
|
||||
def total_materials(cls):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return (
|
||||
len(tool.Ifc.get().by_type("IfcMaterial"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialLayerSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialList"))
|
||||
)
|
||||
return (
|
||||
len(tool.Ifc.get().by_type("IfcMaterial"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialConstituentSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialLayerSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialProfileSet"))
|
||||
+ len(tool.Ifc.get().by_type("IfcMaterialList"))
|
||||
)
|
||||
return len(tool.Ifc.get().by_type(bpy.context.scene.BIMMaterialProperties.material_type))
|
||||
|
||||
@classmethod
|
||||
def material_types(cls):
|
||||
|
||||
@@ -120,6 +120,8 @@ class AddMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty(name="Material Name")
|
||||
name: bpy.props.StringProperty(default="Default")
|
||||
category: bpy.props.StringProperty(default="")
|
||||
description: bpy.props.StringProperty(default="")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
@@ -127,10 +129,14 @@ class AddMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def draw(self, context):
|
||||
row = self.layout
|
||||
row.prop(self, "name", text="Name")
|
||||
row = self.layout
|
||||
row.prop(self, "description", text="Description")
|
||||
row = self.layout
|
||||
row.prop(self, "category", text="Category")
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.materials.get(self.obj) if self.obj else None
|
||||
core.add_material(tool.Ifc, tool.Material, tool.Style, obj=obj, name=self.name)
|
||||
core.add_material(tool.Ifc, tool.Material, tool.Style, obj=obj, name=self.name, category=self.category, description=self.description)
|
||||
material_prop_purge()
|
||||
|
||||
|
||||
@@ -141,8 +147,19 @@ class DuplicateMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
material: bpy.props.IntProperty(name="Material ID")
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
tool.Material.duplicate_material(ifc_file.by_id(self.material))
|
||||
material = tool.Ifc.get().by_id(self.material)
|
||||
new = tool.Ifc.run("material.copy_material", material=material)
|
||||
|
||||
blender_material = tool.Ifc.get_object(material)
|
||||
new_blender = blender_material.copy()
|
||||
new_blender.use_fake_user = True
|
||||
tool.Ifc.link(new, new_blender)
|
||||
|
||||
if not new.is_a("IfcMaterialList"):
|
||||
name = new[0] + " Copy"
|
||||
new[0] = name
|
||||
new_blender.name = name
|
||||
|
||||
material_prop_purge()
|
||||
bpy.ops.bim.load_materials()
|
||||
|
||||
@@ -185,9 +202,10 @@ class UnlinkMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unlink_material"
|
||||
bl_label = "Unlink Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty(name="Material ID")
|
||||
|
||||
def _execute(self, context):
|
||||
core.unlink_material(tool.Ifc, obj=context.active_object.active_material)
|
||||
core.unlink_material(tool.Ifc, obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.material)))
|
||||
|
||||
|
||||
class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -679,35 +697,6 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.bim.disable_editing_material_set_item(obj=obj.name)
|
||||
|
||||
|
||||
class CopyMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.copy_material"
|
||||
bl_label = "Copy Material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
blender_material = context.active_object.active_material
|
||||
material = tool.Ifc.get_entity(blender_material)
|
||||
|
||||
if tool.Ifc.has_changed_shading(blender_material):
|
||||
blenderbim.core.style.update_style_colours(tool.Ifc, tool.Style, obj=blender_material)
|
||||
|
||||
copied_material = ifcopenshell.api.run("material.copy_material", tool.Ifc.get(), material=material)
|
||||
copied_blender_material = blender_material.copy()
|
||||
copied_style = self.get_style(copied_material)
|
||||
tool.Ifc.link(copied_material, copied_blender_material)
|
||||
if copied_style:
|
||||
tool.Ifc.link(copied_style, copied_blender_material)
|
||||
context.active_object.active_material = copied_blender_material
|
||||
|
||||
def get_style(self, material):
|
||||
for material_representation in material.HasRepresentation:
|
||||
for representation in material_representation.Representations:
|
||||
for item in representation.Items:
|
||||
for style in item.Styles:
|
||||
if style.is_a("IfcSurfaceStyle"):
|
||||
return style
|
||||
|
||||
|
||||
class ExpandMaterialCategory(bpy.types.Operator):
|
||||
bl_idname = "bim.expand_material_category"
|
||||
bl_label = "Expand Material Category"
|
||||
|
||||
@@ -97,6 +97,10 @@ def get_material_types(self, context):
|
||||
return MaterialsData.data["material_types"]
|
||||
|
||||
|
||||
def update_material_type(self, context):
|
||||
MaterialsData.data["total_materials"] = MaterialsData.total_materials()
|
||||
|
||||
|
||||
def get_profiles(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
@@ -126,7 +130,7 @@ class Material(PropertyGroup):
|
||||
|
||||
class BIMMaterialProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
material_type: EnumProperty(items=get_material_types, name="Material Type")
|
||||
material_type: EnumProperty(items=get_material_types, update=update_material_type, name="Material Type")
|
||||
materials: CollectionProperty(name="Materials", type=Material)
|
||||
active_material_index: IntProperty(name="Active Material Index")
|
||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||
|
||||
@@ -45,7 +45,7 @@ class BIM_PT_materials(Panel):
|
||||
self.props = context.scene.BIMMaterialProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Materials".format(MaterialsData.data["total_materials"]), icon="NODE_MATERIAL")
|
||||
row.label(text=f"{MaterialsData.data['total_materials']} Materials", icon="NODE_MATERIAL")
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_editing_materials", text="", icon="CANCEL")
|
||||
else:
|
||||
@@ -69,6 +69,8 @@ class BIM_PT_materials(Panel):
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material_style", text="", icon="SHADING_RENDERED")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.unlink_material", icon="UNLINKED", text="")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
|
||||
self.draw_editing_ui()
|
||||
@@ -114,29 +116,6 @@ class BIM_PT_materials(Panel):
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
bl_label = "Material"
|
||||
bl_idname = "BIM_PT_material"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.active_object and context.active_object.active_material
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
material_id = context.active_object.active_material.BIMObjectProperties.ifc_definition_id
|
||||
if bool(material_id):
|
||||
row.operator("bim.remove_material", icon="X", text="Remove IFC Material").material = material_id
|
||||
row.operator("bim.copy_material", icon="DUPLICATE", text="")
|
||||
row.operator("bim.unlink_material", icon="UNLINKED", text="")
|
||||
else:
|
||||
op = row.operator("bim.add_material", icon="ADD", text="Create IFC Material")
|
||||
op.obj = context.active_object.active_material.name
|
||||
|
||||
|
||||
class BIM_PT_object_material(Panel):
|
||||
bl_label = "Object Material"
|
||||
bl_idname = "BIM_PT_object_material"
|
||||
|
||||
@@ -23,6 +23,7 @@ import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.misc as core
|
||||
import blenderbim.core.geometry as core_geometry
|
||||
import blenderbim.core.root
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from mathutils import Vector, Matrix, Euler
|
||||
|
||||
@@ -134,6 +135,10 @@ class ResizeToStorey(bpy.types.Operator, Operator):
|
||||
class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.split_along_edge"
|
||||
bl_label = "Split Along Edge"
|
||||
bl_description = (
|
||||
"Active object is considered to be a cutting object."
|
||||
"Will unassign element from a type if type has a representation."
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
@@ -144,16 +149,25 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
cutter = context.active_object
|
||||
objs = [o for o in context.selected_objects if o != cutter]
|
||||
|
||||
objs_to_cut = []
|
||||
# Splitting only works on meshes
|
||||
for obj in objs:
|
||||
# You cannot split meshes if the representation is mapped.
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
relating_type = tool.Root.get_element_type(element)
|
||||
if relating_type and tool.Root.does_type_have_representations(relating_type):
|
||||
bpy.ops.bim.unassign_type(related_object=obj.name)
|
||||
|
||||
# refresh representation
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
|
||||
# skip empty objects that might get in the way
|
||||
if not representation:
|
||||
continue
|
||||
|
||||
core_geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -168,11 +182,13 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
if not tool.Geometry.is_meshlike(representation):
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="IfcTessellatedFaceSet")
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs, cutter)
|
||||
objs_to_cut.append(obj)
|
||||
|
||||
new_objs = tool.Misc.split_objects_with_cutter(objs_to_cut, cutter)
|
||||
for obj in new_objs:
|
||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
for obj in objs:
|
||||
for obj in objs_to_cut:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
@@ -187,6 +203,8 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
self.report({"INFO"}, f"Splitting finished, {len(new_objs)} new objects created.")
|
||||
|
||||
|
||||
class GetConnectedSystemElements(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.get_connected_system_elements"
|
||||
|
||||
@@ -193,7 +193,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
if len(data) == 1:
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
else:
|
||||
del data[self.item]
|
||||
data = tool.Ifc.get().createIfcText(json.dumps(data))
|
||||
|
||||
@@ -53,7 +53,7 @@ class AuthoringData:
|
||||
cls.data["ifc_element_type"] = cls.ifc_element_type
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["relating_type_id"] = cls.relating_type_id() # only after .ifc_classes()
|
||||
cls.data["predefined_type"] = cls.predefined_type()
|
||||
cls.data["predefined_type"] = cls.predefined_type() # only after .relating_type_id()
|
||||
cls.data["type_class"] = cls.type_class()
|
||||
|
||||
# only after .type_class()
|
||||
@@ -121,9 +121,10 @@ class AuthoringData:
|
||||
def type_thumbnail(cls):
|
||||
if not cls.data["relating_type_id"]:
|
||||
return 0
|
||||
if not tool.Blender.enum_property_has_valid_index(cls.props, "relating_type_id", cls.data["relating_type_id"]):
|
||||
relating_type_id = tool.Blender.get_enum_safe(cls.props, "relating_type_id")
|
||||
if relating_type_id is None:
|
||||
return 0
|
||||
element = tool.Ifc.get().by_id(int(cls.props.relating_type_id))
|
||||
element = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
return cls.type_thumbnails.get(element.id(), None) or 0
|
||||
|
||||
@classmethod
|
||||
@@ -251,8 +252,8 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def predefined_type(cls):
|
||||
relating_type_id = cls.props.relating_type_id
|
||||
if not relating_type_id:
|
||||
relating_type_id = tool.Blender.get_enum_safe(cls.props, "relating_type_id")
|
||||
if relating_type_id is None:
|
||||
return
|
||||
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
if not hasattr(relating_type, "PredefinedType"):
|
||||
|
||||
@@ -22,20 +22,24 @@ import bmesh
|
||||
from bmesh.types import BMVert, BMFace
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
import blenderbim
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.geometry as core
|
||||
from blenderbim.bim.module.model.window import create_bm_window, create_bm_box, update_simple_openings
|
||||
import blenderbim.core.root
|
||||
from blenderbim.bim.module.model.window import create_bm_window, create_bm_box
|
||||
|
||||
from mathutils import Vector, Matrix
|
||||
from pprint import pprint
|
||||
|
||||
import json
|
||||
import collections
|
||||
|
||||
|
||||
def update_door_modifier_representation(context, obj):
|
||||
def update_door_modifier_representation(context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
props = obj.BIMDoorProperties
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
ifc_file = tool.Ifc.get()
|
||||
@@ -159,7 +163,7 @@ def update_door_modifier_representation(context, obj):
|
||||
occurrence.OverallWidth = props.overall_width / si_conversion
|
||||
occurrence.OverallHeight = props.overall_height / si_conversion
|
||||
|
||||
update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion)
|
||||
tool.Model.update_simple_openings(element)
|
||||
|
||||
|
||||
# TODO: move it out to tools
|
||||
@@ -644,6 +648,6 @@ class RemoveDoor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.BIMDoorProperties.is_editing = False
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Door")
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -44,6 +44,7 @@ from bpy.types import SpaceView3D
|
||||
from bpy.props import FloatProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -59,9 +60,15 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
|
||||
class FilledOpeningGenerator:
|
||||
def generate(self, filling_obj, voided_obj, target=None):
|
||||
def generate(
|
||||
self,
|
||||
filling_obj: Union[bpy.types.Object, None],
|
||||
voided_obj: Union[bpy.types.Object, None],
|
||||
target: Optional[Vector] = None,
|
||||
) -> None:
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
opening_thickness_si = 0.0
|
||||
|
||||
filling = tool.Ifc.get_entity(filling_obj)
|
||||
element = tool.Ifc.get_entity(voided_obj)
|
||||
@@ -92,6 +99,7 @@ class FilledOpeningGenerator:
|
||||
# In this prototype, we assume openings are only added to axis-based elements
|
||||
layers = tool.Model.get_material_layer_parameters(element)
|
||||
if layers["layer_set_direction"] == "AXIS2":
|
||||
opening_thickness_si = layers["thickness"] * 2
|
||||
axis = tool.Model.get_wall_axis(voided_obj, layers=layers)["base"]
|
||||
new_matrix = voided_obj.matrix_world.copy()
|
||||
point_on_axis = tool.Cad.point_on_edge(target, axis)
|
||||
@@ -122,44 +130,37 @@ class FilledOpeningGenerator:
|
||||
|
||||
existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling)
|
||||
|
||||
if existing_opening_occurrence:
|
||||
opening = ifcopenshell.api.run(
|
||||
"root.create_entity",
|
||||
tool.Ifc.get(),
|
||||
ifc_class="IfcOpeningElement",
|
||||
predefined_type="OPENING",
|
||||
name="Opening",
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", tool.Ifc.get(), product=opening, matrix=filling_obj.matrix_world
|
||||
)
|
||||
opening = ifcopenshell.api.run(
|
||||
"root.create_entity",
|
||||
tool.Ifc.get(),
|
||||
ifc_class="IfcOpeningElement",
|
||||
predefined_type="OPENING",
|
||||
name="Opening",
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement",
|
||||
tool.Ifc.get(),
|
||||
product=opening,
|
||||
matrix=np.array(filling_obj.matrix_world),
|
||||
is_si=True,
|
||||
)
|
||||
|
||||
if existing_opening_occurrence:
|
||||
representation = ifcopenshell.util.representation.get_representation(
|
||||
existing_opening_occurrence, "Model", "Body", "MODEL_VIEW"
|
||||
)
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
mapped_representation = ifcopenshell.api.run(
|
||||
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||
)
|
||||
else:
|
||||
representation = self.generate_opening_from_filling(filling, filling_obj)
|
||||
opening = ifcopenshell.api.run(
|
||||
"root.create_entity", tool.Ifc.get(), ifc_class="IfcOpeningElement", predefined_type="OPENING"
|
||||
representation = self.generate_opening_from_filling(
|
||||
filling, filling_obj, opening_thickness_si=opening_thickness_si
|
||||
)
|
||||
|
||||
matrix = np.array(filling_obj.matrix_world)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement", tool.Ifc.get(), product=opening, matrix=matrix, is_si=True
|
||||
)
|
||||
mapped_representation = ifcopenshell.api.run(
|
||||
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||
)
|
||||
mapped_representation = ifcopenshell.api.run(
|
||||
"geometry.map_representation", tool.Ifc.get(), representation=representation
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=opening, representation=mapped_representation
|
||||
)
|
||||
|
||||
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=opening, element=element)
|
||||
ifcopenshell.api.run("void.add_filling", tool.Ifc.get(), opening=opening, element=filling)
|
||||
@@ -187,7 +188,7 @@ class FilledOpeningGenerator:
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
||||
def regenerate_from_type(self, usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None:
|
||||
relating_type = settings["relating_type"]
|
||||
|
||||
for related_object in settings["related_objects"]:
|
||||
@@ -252,10 +253,15 @@ class FilledOpeningGenerator:
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
def generate_opening_from_filling(self, filling, filling_obj):
|
||||
def generate_opening_from_filling(
|
||||
self,
|
||||
filling: ifcopenshell.entity_instance,
|
||||
filling_obj: bpy.types.Object,
|
||||
opening_thickness_si: float = 0.0,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
# Since openings are reused later, we give a default thickness of 1.2m
|
||||
# which should cover the majority of curved, or super thick walls.
|
||||
thickness = 1.2
|
||||
thickness = max(1.2, opening_thickness_si)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
shape_builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||
|
||||
@@ -346,7 +352,9 @@ class FilledOpeningGenerator:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_existing_opening_occurrence_if_any(self, filling):
|
||||
def get_existing_opening_occurrence_if_any(
|
||||
self, filling: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
filling_type = ifcopenshell.util.element.get_type(filling)
|
||||
if filling_type:
|
||||
filling_occurrences = ifcopenshell.util.element.get_types(filling_type)
|
||||
|
||||
@@ -138,7 +138,10 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
if not relating_type_id:
|
||||
return {"FINISHED"}
|
||||
|
||||
if self.from_invoke:
|
||||
# Check relating_type_id enum_items since it's possible
|
||||
# that we're adding e.g. IfcRoofType being in a Slab Tool
|
||||
# and roof type id won't be present in the relating_type_id enum.
|
||||
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
||||
props.relating_type_id = str(self.relating_type_id)
|
||||
|
||||
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
@@ -94,7 +94,6 @@ class DumbProfileGenerator:
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
should_add_representation=False,
|
||||
context=self.body_context,
|
||||
)
|
||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element], relating_type=self.relating_type)
|
||||
|
||||
@@ -931,13 +930,11 @@ class PatchNonParametricMepSegment(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.active_object
|
||||
|
||||
def _execute(self, context):
|
||||
styles = tool.Geometry.get_styles(context.active_object)
|
||||
blenderbim.core.material.patch_non_parametric_mep_segment(
|
||||
tool.Ifc, tool.Material, tool.Profile, obj=context.active_object
|
||||
)
|
||||
bpy.ops.bim.enable_editing_extrusion_axis()
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
styles = tool.Geometry.get_styles(context.active_object)
|
||||
|
||||
|
||||
class EnableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -60,7 +60,7 @@ def update_ifc_class(self, context):
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
|
||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
||||
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||
if not tool.Blender.enum_property_has_valid_index(self, "relating_type_id", AuthoringData.data["relating_type_id"]):
|
||||
if tool.Blender.get_enum_safe(self, "relating_type_id") is None:
|
||||
self["relating_type_id"] = 0
|
||||
|
||||
|
||||
|
||||
@@ -20,8 +20,12 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.door import bm_sort_out_geom
|
||||
@@ -535,5 +539,5 @@ class RemoveRailing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.BIMRailingProperties.is_editing = False
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Railing")
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -20,7 +20,11 @@ import bpy
|
||||
import bmesh
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import convert_property_group_from_si
|
||||
from blenderbim.bim.module.model.door import bm_sort_out_geom
|
||||
@@ -757,7 +761,7 @@ class RemoveRoof(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.BIMRoofProperties.is_editing = False
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -176,7 +176,6 @@ class DumbSlabGenerator:
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
should_add_representation=False,
|
||||
context=self.body_context,
|
||||
)
|
||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element], relating_type=self.relating_type)
|
||||
|
||||
|
||||
@@ -20,7 +20,11 @@ import bpy
|
||||
import json
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from bmesh.types import BMVert
|
||||
@@ -217,7 +221,6 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
@@ -225,7 +228,6 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj=obj,
|
||||
ifc_class="IfcStairFlight",
|
||||
should_add_representation=False,
|
||||
context=body_context,
|
||||
)
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
element.PredefinedType = "STRAIGHT"
|
||||
@@ -337,6 +339,6 @@ class RemoveStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.BIMStairProperties.is_editing = False
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Stair")
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -607,7 +607,6 @@ class DumbWallGenerator:
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
should_add_representation=False,
|
||||
context=self.body_context,
|
||||
)
|
||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element], relating_type=self.relating_type)
|
||||
if self.axis_context:
|
||||
|
||||
@@ -26,66 +26,16 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.core.geometry
|
||||
from ifcopenshell.api.geometry.add_window_representation import DEFAULT_PANEL_SCHEMAS
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape_builder
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
from bmesh.types import BMVert
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
# TODO: move to some utils helpers/tool module
|
||||
def update_simple_openings(element, opening_width, opening_height):
|
||||
ifc_file = tool.Ifc.get()
|
||||
fillings = tool.Ifc.get_all_element_occurrences(element)
|
||||
|
||||
voided_objs = set()
|
||||
has_replaced_opening_representation = False
|
||||
for filling in fillings:
|
||||
if not filling.FillsVoids:
|
||||
continue
|
||||
|
||||
opening = filling.FillsVoids[0].RelatingOpeningElement
|
||||
voided_obj = tool.Ifc.get_object(opening.VoidsElements[0].RelatingBuildingElement)
|
||||
voided_objs.add(voided_obj)
|
||||
|
||||
# we assume that the same element type (e.g. window)
|
||||
# will be used only for voiding objects of the same thickness (by y-dimension)
|
||||
# (e.g. all walls window's attached to will share the same thickness)
|
||||
# If that's not the case it will some linings will be too thick or too thin
|
||||
if has_replaced_opening_representation:
|
||||
continue
|
||||
|
||||
old_representation = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW")
|
||||
old_representation = tool.Geometry.resolve_mapped_representation(old_representation)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.unassign_representation", ifc_file, product=opening, representation=old_representation
|
||||
)
|
||||
|
||||
thickness = voided_obj.dimensions[1] + 0.1 + 0.1
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
shape_builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc_file)
|
||||
context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
extrusion = shape_builder.extrude(
|
||||
shape_builder.rectangle(size=Vector([opening_width, 0.0, opening_height]).xz),
|
||||
magnitude=thickness / unit_scale,
|
||||
position=Vector([0.0, -0.1 / unit_scale, 0.0]),
|
||||
**shape_builder.extrude_kwargs("Y")
|
||||
)
|
||||
|
||||
new_representation = shape_builder.get_representation(context, extrusion)
|
||||
|
||||
for inverse in ifc_file.get_inverse(old_representation):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation)
|
||||
|
||||
ifcopenshell.api.run("geometry.remove_representation", ifc_file, representation=old_representation)
|
||||
|
||||
has_replaced_opening_representation = True
|
||||
|
||||
tool.Model.reload_body_representation(voided_objs)
|
||||
if fillings:
|
||||
with bpy.context.temp_override(selected_objects=[tool.Ifc.get_object(f) for f in fillings]):
|
||||
bpy.ops.bim.recalculate_fill()
|
||||
|
||||
|
||||
def update_window_modifier_representation(context, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMWindowProperties
|
||||
@@ -175,7 +125,7 @@ def update_window_modifier_representation(context, obj):
|
||||
occurrence.OverallWidth = props.overall_width / si_conversion
|
||||
occurrence.OverallHeight = props.overall_height / si_conversion
|
||||
|
||||
update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion)
|
||||
tool.Model.update_simple_openings(element)
|
||||
|
||||
|
||||
def create_bm_window_frame(bm, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()):
|
||||
@@ -590,6 +540,6 @@ class RemoveWindow(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.BIMWindowProperties.is_editing = False
|
||||
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Window")
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -36,8 +36,8 @@ classes = (
|
||||
operator.EnableCulling,
|
||||
operator.EnableEditingHeader,
|
||||
operator.ExportIFC,
|
||||
operator.ExportIFCDeprecated,
|
||||
operator.FlipClippingPlane,
|
||||
operator.ImportIFC,
|
||||
operator.LinkIfc,
|
||||
operator.LoadLink,
|
||||
operator.LoadLinkedProject,
|
||||
@@ -97,7 +97,7 @@ def register():
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("export_ifc.bim", "S", "PRESS", ctrl=True)
|
||||
kmi = km.keymap_items.new("bim.save_project", "S", "PRESS", ctrl=True)
|
||||
kmi.properties.should_save_as = False
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
|
||||
@@ -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):
|
||||
@@ -1101,8 +1106,8 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
]
|
||||
|
||||
|
||||
class ExportIFC(bpy.types.Operator):
|
||||
bl_idname = "export_ifc.bim"
|
||||
class ExportIFCBase:
|
||||
bl_idname = "bim.save_project"
|
||||
bl_label = "Save IFC"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".ifc"
|
||||
@@ -1219,14 +1224,20 @@ class ExportIFC(bpy.types.Operator):
|
||||
return "Save the IFC file. Will save both .IFC/.BLEND files if synced together"
|
||||
|
||||
|
||||
class ImportIFC(bpy.types.Operator):
|
||||
bl_idname = "import_ifc.bim"
|
||||
bl_label = "Import IFC"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
class ExportIFC(ExportIFCBase, bpy.types.Operator):
|
||||
pass
|
||||
|
||||
|
||||
# TODO: remove as deprecated, better wait couple releases since
|
||||
# this operator is used for saving IFC files in user scripts.
|
||||
class ExportIFCDeprecated(ExportIFCBase, bpy.types.Operator):
|
||||
bl_idname = "export_ifc.bim"
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.bim.load_project("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
msg = f"'{ExportIFCDeprecated.bl_idname}' operator name is deprecated, use '{ExportIFC.bl_idname}'."
|
||||
self.report({"WARNING"}, msg)
|
||||
print(msg)
|
||||
return super().execute(context)
|
||||
|
||||
|
||||
class LoadLinkedProject(bpy.types.Operator):
|
||||
|
||||
@@ -68,9 +68,9 @@ def file_menu(self, context):
|
||||
op = self.layout.operator("bim.load_project", text="Open IFC Project", icon="FILEBROWSER")
|
||||
op.should_start_fresh_session = True
|
||||
self.layout.separator()
|
||||
op = self.layout.operator("export_ifc.bim", icon="FILE_TICK", text="Save IFC Project")
|
||||
op = self.layout.operator("bim.save_project", icon="FILE_TICK", text="Save IFC Project")
|
||||
op.should_save_as = False
|
||||
op = self.layout.operator("export_ifc.bim", text="Save IFC Project As...")
|
||||
op = self.layout.operator("bim.save_project", text="Save IFC Project As...")
|
||||
op.should_save_as = True
|
||||
self.layout.separator()
|
||||
self.layout.operator("bim.revert_project")
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddProposedProp,
|
||||
operator.AddPset,
|
||||
operator.AddQto,
|
||||
operator.CopyPropertyToSelection,
|
||||
@@ -39,14 +40,6 @@ classes = (
|
||||
prop.IfcPropertyEnumeratedValue,
|
||||
prop.IfcProperty,
|
||||
prop.PsetProperties,
|
||||
prop.MaterialPsetProperties,
|
||||
prop.MaterialSetPsetProperties,
|
||||
prop.MaterialSetItemPsetProperties,
|
||||
prop.TaskPsetProperties,
|
||||
prop.ResourcePsetProperties,
|
||||
prop.GroupPsetProperties,
|
||||
prop.ProfilePsetProperties,
|
||||
prop.WorkSchedulePsetProperties,
|
||||
prop.RenameProperties,
|
||||
prop.AddEditProperties,
|
||||
prop.DeletePsets,
|
||||
@@ -71,14 +64,14 @@ classes = (
|
||||
|
||||
def register():
|
||||
bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Object.MaterialSetPsetProperties = bpy.props.PointerProperty(type=prop.MaterialSetPsetProperties)
|
||||
bpy.types.Object.MaterialSetItemPsetProperties = bpy.props.PointerProperty(type=prop.MaterialSetItemPsetProperties)
|
||||
bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.MaterialPsetProperties)
|
||||
bpy.types.Scene.TaskPsetProperties = bpy.props.PointerProperty(type=prop.TaskPsetProperties)
|
||||
bpy.types.Scene.ResourcePsetProperties = bpy.props.PointerProperty(type=prop.ResourcePsetProperties)
|
||||
bpy.types.Scene.GroupPsetProperties = bpy.props.PointerProperty(type=prop.GroupPsetProperties)
|
||||
bpy.types.Scene.ProfilePsetProperties = bpy.props.PointerProperty(type=prop.ProfilePsetProperties)
|
||||
bpy.types.Scene.WorkSchedulePsetProperties = bpy.props.PointerProperty(type=prop.WorkSchedulePsetProperties)
|
||||
bpy.types.Scene.MaterialPsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Object.MaterialSetPsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Object.MaterialSetItemPsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.TaskPsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.ResourcePsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.GroupPsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.ProfilePsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.WorkSchedulePsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||
bpy.types.Scene.RenameProperties = bpy.props.CollectionProperty(type=prop.RenameProperties)
|
||||
bpy.types.Scene.AddEditProperties = bpy.props.CollectionProperty(type=prop.AddEditProperties)
|
||||
bpy.types.Scene.DeletePsets = bpy.props.CollectionProperty(type=prop.DeletePsets)
|
||||
@@ -86,7 +79,9 @@ def register():
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Object.PsetProperties
|
||||
del bpy.types.Material.PsetProperties
|
||||
del bpy.types.Object.MaterialPsetProperties
|
||||
del bpy.types.Object.MaterialSetPsetProperties
|
||||
del bpy.types.Object.MaterialSetItemPsetProperties
|
||||
del bpy.types.Scene.TaskPsetProperties
|
||||
del bpy.types.Scene.ResourcePsetProperties
|
||||
del bpy.types.Scene.GroupPsetProperties
|
||||
|
||||
@@ -157,9 +157,14 @@ class MaterialPsetsData(Data):
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
ifc_definition_id = None
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id
|
||||
|
||||
cls.data = {
|
||||
"ifc_definition_id": bpy.context.active_object.active_material.BIMObjectProperties.ifc_definition_id,
|
||||
"psets": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object.active_material)),
|
||||
"ifc_definition_id": ifc_definition_id,
|
||||
"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id)),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import blenderbim.core.qto as QtoCore
|
||||
import blenderbim.bim.module.pset.data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -41,35 +40,15 @@ class Operator:
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def get_pset_props(context, obj, obj_type):
|
||||
if obj_type == "Object":
|
||||
return bpy.data.objects.get(obj).PsetProperties
|
||||
elif obj_type == "Material":
|
||||
return bpy.data.materials.get(obj).PsetProperties
|
||||
elif obj_type == "MaterialSet":
|
||||
return bpy.data.objects.get(obj).MaterialSetPsetProperties
|
||||
elif obj_type == "MaterialSetItem":
|
||||
return bpy.data.objects.get(obj).MaterialSetItemPsetProperties
|
||||
elif obj_type == "Task":
|
||||
return context.scene.TaskPsetProperties
|
||||
elif obj_type == "Resource":
|
||||
return context.scene.ResourcePsetProperties
|
||||
elif obj_type == "Profile":
|
||||
return context.scene.ProfilePsetProperties
|
||||
elif obj_type == "WorkSchedule":
|
||||
return context.scene.WorkSchedulePsetProperties
|
||||
elif obj_type == "Group":
|
||||
return context.scene.GroupPsetProperties
|
||||
|
||||
class TogglePsetExpansion(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.toggle_pset_expansion"
|
||||
bl_label = "Toggle Pset Expansion"
|
||||
pset_id: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
blenderbim.bim.module.pset.data.is_expanded[
|
||||
self.pset_id
|
||||
] = not blenderbim.bim.module.pset.data.is_expanded.setdefault(self.pset_id, True)
|
||||
blenderbim.bim.module.pset.data.is_expanded[self.pset_id] = (
|
||||
not blenderbim.bim.module.pset.data.is_expanded.setdefault(self.pset_id, True)
|
||||
)
|
||||
|
||||
|
||||
class EnablePsetEditing(bpy.types.Operator):
|
||||
@@ -83,160 +62,15 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = get_pset_props(context, self.obj, self.obj_type)
|
||||
self.props.properties.clear()
|
||||
|
||||
if self.pset_id:
|
||||
pset = tool.Ifc.get().by_id(self.pset_id)
|
||||
self.props.active_pset_name = pset.Name
|
||||
self.props.active_pset_type = ""
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(pset.Name)
|
||||
self.pset_name = pset.Name
|
||||
else:
|
||||
pset = None
|
||||
self.props.active_pset_name = self.pset_name
|
||||
self.props.active_pset_type = self.pset_type
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(self.pset_name)
|
||||
|
||||
if pset_template:
|
||||
self.load_from_pset_template(pset_template, pset)
|
||||
else:
|
||||
self.load_from_pset_data(pset)
|
||||
|
||||
self.props.active_pset_id = self.pset_id
|
||||
core.enable_pset_editing(tool.Pset, pset, self.pset_name, self.pset_type, self.obj, self.obj_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_from_pset_template(self, pset_template, pset):
|
||||
if pset:
|
||||
data = ifcopenshell.util.element.get_property_definition(pset)
|
||||
del data["id"]
|
||||
else:
|
||||
data = {}
|
||||
for prop_template in pset_template.HasPropertyTemplates:
|
||||
if not prop_template.is_a("IfcSimplePropertyTemplate"):
|
||||
continue # Other types not yet supported
|
||||
if prop_template.TemplateType == "P_SINGLEVALUE":
|
||||
self.load_single_value(pset_template, prop_template, data)
|
||||
elif prop_template.TemplateType.startswith("Q_"):
|
||||
self.load_single_value(pset_template, prop_template, data)
|
||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
self.load_enumerated_value(prop_template, data)
|
||||
else:
|
||||
# NOTE: currently unsupported types:
|
||||
# - P_BOUNDEDVALUE
|
||||
# - P_LISTVALUE
|
||||
# - P_REFERENCEVALUE
|
||||
# - P_TABLEVALUE
|
||||
pass
|
||||
|
||||
def load_single_value(self, pset_template, prop_template, data):
|
||||
prop = self.props.properties.add()
|
||||
prop.name = prop_template.Name
|
||||
prop.value_type = "IfcPropertySingleValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
metadata.has_calculator = bool(mapper.get(pset_template.Name, {}).get(prop_template.Name, None))
|
||||
metadata.data_type = self.get_data_type(prop_template)
|
||||
|
||||
special_type = ""
|
||||
if prop_template.PrimaryMeasureType in (
|
||||
"IfcPositiveLengthMeasure",
|
||||
"IfcLengthMeasure",
|
||||
) or prop_template.TemplateType == "Q_LENGTH":
|
||||
special_type = "LENGTH"
|
||||
elif prop_template.PrimaryMeasureType == "IfcAreaMeasure" or prop_template.TemplateType == "Q_AREA":
|
||||
special_type = "AREA"
|
||||
elif prop_template.PrimaryMeasureType == "IfcVolumeMeasure" or prop_template.TemplateType == "Q_VOLUME":
|
||||
special_type = "VOLUME"
|
||||
metadata.special_type = special_type
|
||||
|
||||
if metadata.data_type == "string":
|
||||
metadata.string_value = "" if metadata.is_null else str(data[prop_template.Name])
|
||||
elif metadata.data_type == "integer":
|
||||
metadata.int_value = 0 if metadata.is_null else int(data[prop_template.Name])
|
||||
elif metadata.data_type == "float":
|
||||
metadata.float_value = 0.0 if metadata.is_null else float(data[prop_template.Name])
|
||||
elif metadata.data_type == "boolean":
|
||||
metadata.bool_value = False if metadata.is_null else bool(data[prop_template.Name])
|
||||
|
||||
metadata.ifc_class = pset_template.Name
|
||||
blenderbim.bim.helper.add_attribute_description(metadata, prop_template)
|
||||
|
||||
def get_data_type(self, prop_template):
|
||||
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||
return "float"
|
||||
elif prop_template.TemplateType == "Q_COUNT":
|
||||
return "integer"
|
||||
return ifcopenshell.util.attribute.get_primitive_type(
|
||||
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
|
||||
)
|
||||
|
||||
def load_enumerated_value(self, prop_template, data):
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
selected_enum_items = data.get(prop_template.Name, []) or []
|
||||
|
||||
prop = self.props.properties.add()
|
||||
prop.name = prop_template.Name
|
||||
prop.value_type = "IfcPropertyEnumeratedValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
|
||||
# Cute hack to abuse the metadata to find the Blender data_type
|
||||
metadata.set_value(enum_items[0])
|
||||
data_type = metadata.get_value_name()
|
||||
|
||||
for enum in enum_items:
|
||||
new = prop.enumerated_value.enumerated_values.add()
|
||||
setattr(new, data_type, enum)
|
||||
new.is_selected = enum in selected_enum_items
|
||||
|
||||
def load_from_pset_data(self, pset):
|
||||
props = []
|
||||
if pset.is_a("IfcElementQuantity"):
|
||||
props = pset.Quantities
|
||||
elif pset.is_a("IfcPropertySet"):
|
||||
props = pset.HasProperties
|
||||
elif pset.is_a("IfcMaterialProperties") or pset.is_a("IfcProfileProperties"):
|
||||
props = pset.Properties
|
||||
|
||||
for prop in props:
|
||||
if prop.is_a("IfcPropertyEnumeratedValue"):
|
||||
simple_prop = self.props.properties.add()
|
||||
simple_prop.name = prop.Name
|
||||
simple_prop.value_type = "IfcPropertyEnumeratedValue"
|
||||
metadata = simple_prop.metadata
|
||||
metadata.name = prop.Name
|
||||
metadata.is_null = len(simple_prop.enumerated_value.enumerated_values) == 0
|
||||
metadata.is_optional = True
|
||||
metadata.set_value(prop.EnumerationReference.EnumerationValues[0].wrappedValue)
|
||||
|
||||
enum_items = [v.wrappedValue for v in prop.EnumerationReference.EnumerationValues]
|
||||
selected_enum_items = [v.wrappedValue for v in prop.EnumerationValues]
|
||||
data_type = metadata.get_value_name(display_only=True)
|
||||
|
||||
for enum in enum_items:
|
||||
new = simple_prop.enumerated_value.enumerated_values.add()
|
||||
setattr(new, data_type, enum)
|
||||
new.is_selected = enum in selected_enum_items
|
||||
else:
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
value = prop.NominalValue.wrappedValue if prop.NominalValue else None
|
||||
elif prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||
value = prop[3]
|
||||
new_prop = self.props.properties.add()
|
||||
new_prop.name = prop.Name
|
||||
metadata = new_prop.metadata
|
||||
metadata.set_value(value)
|
||||
metadata.name = prop.Name
|
||||
metadata.is_null = value is None
|
||||
metadata.is_optional = True
|
||||
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
|
||||
|
||||
|
||||
class DisablePsetEditing(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.disable_pset_editing"
|
||||
@@ -246,7 +80,7 @@ class DisablePsetEditing(bpy.types.Operator, Operator):
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = get_pset_props(context, self.obj, self.obj_type)
|
||||
props = tool.Pset.get_pset_props(self.obj, self.obj_type)
|
||||
if props.active_pset_id:
|
||||
pset = tool.Ifc.get().by_id(props.active_pset_id)
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||
@@ -270,7 +104,7 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = get_pset_props(context, self.obj, self.obj_type)
|
||||
props = tool.Pset.get_pset_props(self.obj, self.obj_type)
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
properties = {}
|
||||
@@ -310,6 +144,8 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
for key, value in properties.items():
|
||||
if isinstance(value, float):
|
||||
properties[key] = round(value, 4)
|
||||
elif not isinstance(value, int):
|
||||
properties[key] = 0
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_qto",
|
||||
self.file,
|
||||
@@ -341,7 +177,7 @@ class RemovePset(bpy.types.Operator, Operator):
|
||||
objects = [self.obj]
|
||||
pset_name = tool.Ifc.get().by_id(self.pset_id).Name
|
||||
for obj in objects:
|
||||
props = get_pset_props(context, obj, self.obj_type)
|
||||
props = tool.Pset.get_pset_props(obj, self.obj_type)
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
pset = ifcopenshell.util.element.get_psets(element, should_inherit=False).get(pset_name, None)
|
||||
@@ -372,11 +208,9 @@ class AddQto(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = get_pset_props(context, self.obj, self.obj_type)
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
qto_name = tool.Pset.get_pset_name(self.obj, self.obj_type, pset_type="QTO")
|
||||
bpy.ops.bim.enable_pset_editing(
|
||||
pset_id=0, pset_name=props.qto_name, pset_type="QTO", obj=self.obj, obj_type=self.obj_type
|
||||
pset_id=0, pset_name=qto_name, pset_type="QTO", obj=self.obj, obj_type=self.obj_type
|
||||
)
|
||||
|
||||
|
||||
@@ -634,3 +468,17 @@ class BIM_OT_bulk_remove_psets(bpy.types.Operator):
|
||||
|
||||
self.report({"INFO"}, "Finished applying changes")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddProposedProp(bpy.types.Operator):
|
||||
bl_idname = "bim.add_proposed_prop"
|
||||
bl_label = "Add Proposed Prop"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
prop_name: bpy.props.StringProperty()
|
||||
prop_value: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.add_proposed_prop(tool.Pset, self.obj, self.obj_type, self.prop_name, self.prop_value)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blenderbim.bim.schema
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
@@ -58,6 +59,30 @@ def blender_formatted_enum_from_psets(psets):
|
||||
|
||||
|
||||
def get_pset_name(self, context):
|
||||
pset_type = repr(self)
|
||||
prop_type = pset_type.split(".")[-1]
|
||||
results = []
|
||||
if "bpy.data.objects" in pset_type:
|
||||
if prop_type == "PsetProperties":
|
||||
results = get_object_pset_name(self, context)
|
||||
elif prop_type == "MaterialSetPsetProperties":
|
||||
results = get_material_set_pset_names(self, context)
|
||||
elif prop_type == "MaterialSetItemPsetProperties":
|
||||
results = get_material_set_item_pset_names(self, context)
|
||||
elif prop_type == "MaterialPsetProperties":
|
||||
results = get_material_pset_names(self, context)
|
||||
elif prop_type == "ResourcePsetProperties":
|
||||
results = get_resource_pset_names(self, context)
|
||||
elif prop_type == "GroupPsetProperties":
|
||||
results = get_group_pset_names(self, context)
|
||||
elif prop_type == "ProfilePsetProperties":
|
||||
results = get_profile_pset_names(self, context)
|
||||
elif prop_type == "WorkSchedulePsetProperties":
|
||||
results = get_work_schedule_pset_names(self, context)
|
||||
return [("BBIM_CUSTOM", "Custom Pset", "Create a property set without using a template."), None] + results
|
||||
|
||||
|
||||
def get_object_pset_name(self, context):
|
||||
if not ObjectPsetsData.is_loaded:
|
||||
ObjectPsetsData.load()
|
||||
return ObjectPsetsData.data["pset_name"]
|
||||
@@ -167,6 +192,21 @@ def get_work_schedule_pset_names(self, context):
|
||||
|
||||
|
||||
def get_qto_name(self, context):
|
||||
pset_type = repr(self)
|
||||
prop_type = pset_type.split(".")[-1]
|
||||
if "bpy.data.objects" in pset_type:
|
||||
if prop_type == "PsetProperties":
|
||||
results = get_object_qto_name(self, context)
|
||||
elif prop_type == "TaskPsetProperties":
|
||||
results = get_task_qto_names(self, context)
|
||||
elif prop_type == "ResourcePsetProperties":
|
||||
results = get_resource_qto_names(self, context)
|
||||
elif prop_type == "GroupPsetProperties":
|
||||
results = get_group_qto_names(self, context)
|
||||
return [("BBIM_CUSTOM", "Custom Qto", "Create a quantity set without using a template."), None] + results
|
||||
|
||||
|
||||
def get_object_qto_name(self, context):
|
||||
if not ObjectPsetsData.is_loaded:
|
||||
ObjectPsetsData.load()
|
||||
return ObjectPsetsData.data["qto_name"]
|
||||
@@ -198,76 +238,14 @@ class IfcProperty(PropertyGroup):
|
||||
|
||||
class PsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_has_template: BoolProperty(name="Active Pset Has Template")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_pset_name, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_qto_name, name="Qto Name")
|
||||
|
||||
|
||||
class MaterialPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_material_pset_names, name="Pset Name")
|
||||
|
||||
|
||||
class MaterialSetPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_material_set_pset_names, name="Pset Name")
|
||||
|
||||
|
||||
class MaterialSetItemPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_material_set_item_pset_names, name="Pset Name")
|
||||
|
||||
|
||||
class TaskPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
qto_name: EnumProperty(items=get_task_qto_names, name="Qto Name")
|
||||
|
||||
|
||||
class ResourcePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_resource_pset_names, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_resource_qto_names, name="Qto Name")
|
||||
|
||||
|
||||
class GroupPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_group_pset_names, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_group_qto_names, name="Qto Name")
|
||||
|
||||
class ProfilePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_profile_pset_names, name="Pset Name")
|
||||
|
||||
|
||||
class WorkSchedulePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
active_pset_type: StringProperty(name="Active Pset Type")
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_work_schedule_pset_names, name="Pset Name")
|
||||
prop_name: StringProperty(name="Property Name", default="MyProperty")
|
||||
prop_value: StringProperty(name="Property Value", default="Some Value")
|
||||
|
||||
|
||||
class RenameProperties(PropertyGroup):
|
||||
|
||||
@@ -79,13 +79,7 @@ def draw_enumerated_property(prop, layout, copy_operator=None):
|
||||
|
||||
|
||||
def get_active_pset_obj_name(context, obj_type):
|
||||
if obj_type == "Object":
|
||||
return context.active_object.name
|
||||
elif obj_type == "Material":
|
||||
return context.active_object.active_material.name
|
||||
elif obj_type == "MaterialSet":
|
||||
return context.active_object.name
|
||||
elif obj_type == "MaterialSetItem":
|
||||
if obj_type in ("Object", "Material", "MaterialSet", "MaterialSetItem"):
|
||||
return context.active_object.name
|
||||
return ""
|
||||
|
||||
@@ -101,6 +95,7 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
if props.active_pset_id == pset_id:
|
||||
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
||||
op = row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
||||
op.pset_id = pset_id
|
||||
op.obj = obj_name
|
||||
op.obj_type = obj_type
|
||||
op = row.operator("bim.disable_pset_editing", icon="CANCEL", text="")
|
||||
@@ -132,6 +127,16 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
if props.active_pset_id == pset_id:
|
||||
for prop in props.properties:
|
||||
draw_psetqto_editable_ui(box, props, prop)
|
||||
|
||||
if not props.active_pset_has_template:
|
||||
row = box.row(align=True)
|
||||
row.prop(props, "prop_name", text="")
|
||||
row.prop(props, "prop_value", text="")
|
||||
op = row.operator("bim.add_proposed_prop", text="", icon="ADD")
|
||||
op.obj = obj_name
|
||||
op.obj_type = obj_type
|
||||
op.prop_name = props.prop_name
|
||||
op.prop_value = props.prop_value
|
||||
else:
|
||||
has_props_displayed = False
|
||||
for prop in pset["Properties"]:
|
||||
@@ -143,7 +148,9 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
row = box.row(align=True)
|
||||
row.scale_y = 0.8
|
||||
row.label(text=prop["Name"])
|
||||
row.label(text=str(prop["NominalValue"]))
|
||||
op = row.operator("bim.select_similar", text=str(prop["NominalValue"]), icon="NONE", emboss=False)
|
||||
op.key = pset['Name']
|
||||
|
||||
if not has_props_displayed:
|
||||
row = box.row()
|
||||
row.scale_y = 0.8
|
||||
@@ -273,36 +280,36 @@ class BIM_PT_material_psets(Panel):
|
||||
bl_idname = "BIM_PT_material_psets"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_materials"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
if not context.active_object.active_material:
|
||||
return False
|
||||
props = context.active_object.active_material.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
file = IfcStore.get_file()
|
||||
if not file or file.schema == "IFC2X3":
|
||||
ifc_file = tool.Ifc.get()
|
||||
if not ifc_file or ifc_file.schema == "IFC2X3":
|
||||
return False # We don't support material psets in IFC2X3 because they suck
|
||||
return True
|
||||
props = context.scene.BIMMaterialProperties
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
material = props.materials[props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMMaterialProperties
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id
|
||||
|
||||
if not MaterialPsetsData.is_loaded:
|
||||
MaterialPsetsData.load()
|
||||
elif (
|
||||
context.active_object.active_material.BIMObjectProperties.ifc_definition_id
|
||||
!= MaterialPsetsData.data["ifc_definition_id"]
|
||||
):
|
||||
elif ifc_definition_id != MaterialPsetsData.data["ifc_definition_id"]:
|
||||
MaterialPsetsData.load()
|
||||
|
||||
props = context.active_object.active_material.PsetProperties
|
||||
props = context.scene.MaterialPsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj = context.active_object.active_material.name
|
||||
op.obj = context.active_object.name
|
||||
op.obj_type = "Material"
|
||||
|
||||
if not props.active_pset_id and props.active_pset_name and props.active_pset_type == "PSET":
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import pathlib
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.doc
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -36,10 +37,14 @@ class PsetTemplatesData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data["pset_template_files"] = cls.pset_template_files()
|
||||
|
||||
# after pset_template_files
|
||||
cls.data["pset_templates"] = cls.pset_templates()
|
||||
|
||||
# after pset_template_files because it loads IfcStore.pset_template_file
|
||||
cls.data["primary_measure_type"] = cls.primary_measure_type()
|
||||
cls.data["property_template_type"] = cls.property_template_type()
|
||||
cls.data["pset_template_files"] = cls.pset_template_files()
|
||||
cls.data["pset_templates"] = cls.pset_templates()
|
||||
cls.data["pset_template"] = cls.pset_template()
|
||||
cls.data["prop_templates"] = cls.prop_templates()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -190,4 +190,4 @@ class BIMPsetTemplateProperties(PropertyGroup):
|
||||
active_prop_template_id: IntProperty(name="Active Prop Template Id")
|
||||
active_pset_template: PointerProperty(type=PsetTemplate)
|
||||
active_prop_template: PointerProperty(type=PropTemplate)
|
||||
new_template_filename: StringProperty("New TemplateFileName")
|
||||
new_template_filename: StringProperty(name="New TemplateFileName")
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.resource
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -28,7 +29,6 @@ def refresh():
|
||||
class ResourceData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
cost_values = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
@@ -139,7 +139,7 @@ class ResourceData:
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def active_resource_ids(cls):
|
||||
def active_resource_ids(cls) -> list[int]:
|
||||
obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
|
||||
@@ -128,9 +128,9 @@ class ContractResource(bpy.types.Operator):
|
||||
class AssignResource(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_resource"
|
||||
bl_label = "Assign Resource"
|
||||
bl_description = "Assign resource to the selected objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.assign_resource(tool.Ifc, tool.Spatial, resource=tool.Ifc.get().by_id(self.resource))
|
||||
@@ -139,9 +139,9 @@ class AssignResource(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class UnassignResource(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_resource"
|
||||
bl_label = "Unassign Resource"
|
||||
bl_description = "Unassign resource from the selected objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.unassign_resource(tool.Ifc, tool.Spatial, resource=tool.Ifc.get().by_id(self.resource))
|
||||
@@ -328,7 +328,7 @@ class EditResourceQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
|
||||
class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_idname = "import_resources.bim"
|
||||
bl_idname = "bim.import_resources"
|
||||
bl_label = "Import Resources"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".csv"
|
||||
|
||||
@@ -51,7 +51,7 @@ class BIM_PT_resources(Panel):
|
||||
row.operator("bim.disable_resource_editing_ui", text="", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.load_resources", text="", icon="GREASEPENCIL")
|
||||
row.operator("import_resources.bim", text="", icon="IMPORT")
|
||||
row.operator("bim.import_resources", text="", icon="IMPORT")
|
||||
if not self.props.is_editing:
|
||||
return
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import re
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
@@ -213,7 +215,7 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
||||
group = group[0]
|
||||
group.Description = description
|
||||
else:
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), name=self.name, description=description)
|
||||
if results:
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=list(results), group=group)
|
||||
|
||||
@@ -366,7 +368,7 @@ class SaveColourscheme(Operator, tool.Ifc.Operator):
|
||||
description = json.dumps(
|
||||
{"type": "BBIM_Search", "colourscheme": colourscheme, "colourscheme_query": query}
|
||||
)
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), name=self.name, description=description)
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
@@ -633,12 +635,14 @@ class SelectSimilar(Operator, tool.Ifc.Operator):
|
||||
bl_label = "Select Similar"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
key: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
key = props.element_key
|
||||
if props.element_key == "PredefinedType":
|
||||
key = self.key
|
||||
if key == "PredefinedType":
|
||||
key = "predefined_type"
|
||||
value = ifcopenshell.util.selector.get_element_value(element, key)
|
||||
for obj in context.visible_objects:
|
||||
|
||||
@@ -113,7 +113,8 @@ class BIM_PT_select_similar(Panel):
|
||||
if SelectSimilarData.data["element_key"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "element_key", text="")
|
||||
row.operator("bim.select_similar", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op = row.operator("bim.select_similar", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.key = props.element_key
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text=f"No Active Element")
|
||||
|
||||
@@ -628,7 +628,7 @@ class DisableEditingWorkCalendar(bpy.types.Operator):
|
||||
|
||||
|
||||
class ImportCSV(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_idname = "import_csv.bim"
|
||||
bl_idname = "bim.import_csv"
|
||||
bl_label = "Import CSV"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".csv"
|
||||
@@ -653,7 +653,7 @@ class ImportCSV(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ImportP6(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "import_p6.bim"
|
||||
bl_idname = "bim.import_p6"
|
||||
bl_label = "Import P6"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".xml"
|
||||
@@ -679,7 +679,7 @@ class ImportP6(bpy.types.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ImportP6XER(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "import_p6xer.bim"
|
||||
bl_idname = "bim.import_p6xer"
|
||||
bl_label = "Import P6 XER"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".xer"
|
||||
@@ -705,7 +705,7 @@ class ImportP6XER(bpy.types.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ImportPP(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "import_pp.bim"
|
||||
bl_idname = "bim.import_pp"
|
||||
bl_label = "Import Powerproject .pp"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".pp"
|
||||
@@ -731,7 +731,7 @@ class ImportPP(bpy.types.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ImportMSP(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "import_msp.bim"
|
||||
bl_idname = "bim.import_msp"
|
||||
bl_label = "Import MSP"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".xml"
|
||||
@@ -757,7 +757,7 @@ class ImportMSP(bpy.types.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ExportMSP(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "export_msp.bim"
|
||||
bl_idname = "bim.export_msp"
|
||||
bl_label = "Export MSP"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".xml"
|
||||
@@ -787,7 +787,7 @@ class ExportMSP(bpy.types.Operator, ImportHelper):
|
||||
|
||||
|
||||
class ExportP6(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "export_p6.bim"
|
||||
bl_idname = "bim.export_p6"
|
||||
bl_label = "Export P6"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".xml"
|
||||
|
||||
@@ -44,6 +44,8 @@ def update_elevation(self, context):
|
||||
|
||||
|
||||
def update_active_container_index(self, context):
|
||||
if self.active_container_index < 0:
|
||||
return
|
||||
self.active_container_id = self.containers[self.active_container_index].ifc_definition_id
|
||||
self.container_name = self.containers[self.active_container_index].name
|
||||
self.elevation = self.containers[self.active_container_index].elevation
|
||||
|
||||
@@ -116,7 +116,7 @@ class BIM_PT_SpatialManager(Panel):
|
||||
self.props = context.scene.BIMSpatialManagerProperties
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure")
|
||||
if self.props.active_container_index < len(self.props.containers):
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
@@ -134,7 +134,7 @@ class BIM_PT_SpatialManager(Panel):
|
||||
"active_container_index",
|
||||
)
|
||||
row = self.layout.row()
|
||||
if self.props.active_container_index < len(self.props.containers):
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
row.prop(self.props, "container_name", text="")
|
||||
row.prop(self.props, "elevation", text="")
|
||||
op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply")
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.core.structural as core
|
||||
|
||||
@@ -50,8 +50,6 @@ classes = (
|
||||
prop.BIMStylesProperties,
|
||||
prop.BIMStyleProperties,
|
||||
ui.BIM_PT_styles,
|
||||
ui.BIM_PT_style,
|
||||
ui.BIM_PT_style_attributes,
|
||||
ui.BIM_UL_styles,
|
||||
)
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ from ifcopenshell.util.doc import get_entity_doc
|
||||
|
||||
def refresh():
|
||||
StylesData.is_loaded = False
|
||||
StyleAttributesData.is_loaded = False
|
||||
|
||||
|
||||
class StylesData:
|
||||
@@ -68,30 +67,3 @@ class StylesData:
|
||||
@classmethod
|
||||
def total_styles(cls):
|
||||
return len(tool.Ifc.get().by_type("IfcPresentationStyle"))
|
||||
|
||||
|
||||
class StyleAttributesData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"ifc_style_id": cls.ifc_style_id(),
|
||||
"attributes": cls.attributes(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def ifc_style_id(cls):
|
||||
return bpy.context.active_object.active_material.BIMMaterialProperties.ifc_style_id
|
||||
|
||||
@classmethod
|
||||
def attributes(cls):
|
||||
style = tool.Ifc.get().by_id(bpy.context.active_object.active_material.BIMMaterialProperties.ifc_style_id)
|
||||
results = []
|
||||
for name, value in style.get_info().items():
|
||||
if name in ["id", "type", "Styles"]:
|
||||
continue
|
||||
results.append({"name": name, "value": str(value)})
|
||||
return results
|
||||
|
||||
@@ -28,6 +28,7 @@ from pathlib import Path
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
# TODO: is this still relevant or can it be deleted?
|
||||
class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.update_style_colours"
|
||||
bl_label = "Save Current Shading Style"
|
||||
@@ -53,6 +54,7 @@ class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, "Check the system console to see saved style properties")
|
||||
|
||||
|
||||
# TODO: is this still relevant or can it be deleted?
|
||||
class UpdateStyleTextures(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.update_style_textures"
|
||||
bl_label = "Update Style Textures"
|
||||
@@ -93,9 +95,10 @@ class UnlinkStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unlink_style"
|
||||
bl_label = "Unlink Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
style: bpy.props.IntProperty(default=0)
|
||||
|
||||
def _execute(self, context):
|
||||
core.unlink_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
|
||||
core.unlink_style(tool.Ifc, style=tool.Ifc.get().by_id(self.style))
|
||||
|
||||
|
||||
class EnableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -122,7 +122,7 @@ class ColourRgb(PropertyGroup):
|
||||
name: StringProperty()
|
||||
color_value: FloatVectorProperty(size=3, subtype="COLOR", default=(1, 1, 1))
|
||||
# not exposed in the UI, here just to preserve the data
|
||||
color_name: StringProperty("Color Name")
|
||||
color_name: StringProperty(name="Color Name")
|
||||
|
||||
# to fit blender.bim.helper.export_attributes
|
||||
def get_value(self):
|
||||
|
||||
@@ -21,7 +21,7 @@ import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.style.data import StylesData, StyleAttributesData
|
||||
from blenderbim.bim.module.style.data import StylesData
|
||||
from bl_ui.properties_material import MaterialButtonsPanel
|
||||
|
||||
|
||||
@@ -62,11 +62,11 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.enable_adding_presentation_style", text="", icon="ADD")
|
||||
if active_style:
|
||||
style = self.props.styles[self.props.active_style_index]
|
||||
material_name = StylesData.data["styles_to_blender_material_names"][self.props.active_style_index]
|
||||
material = bpy.data.materials[material_name]
|
||||
|
||||
row.operator("bim.duplicate_style", text="", icon="DUPLICATE").style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
op = row.operator("bim.unlink_style", text="", icon="UNLINKED")
|
||||
op.style = style.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_style", text="", icon="GREASEPENCIL")
|
||||
op.style = style.ifc_definition_id
|
||||
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
|
||||
@@ -93,9 +93,12 @@ class BIM_PT_styles(Panel):
|
||||
# style ui tools
|
||||
if active_style:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(material.BIMStyleProperties, "active_style_type", icon="SHADING_RENDERED", text="")
|
||||
op = row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
|
||||
op.style_id = style.ifc_definition_id
|
||||
material_name = StylesData.data["styles_to_blender_material_names"][self.props.active_style_index]
|
||||
if material_name: # The user may have unlinked the style, so the material may not exist
|
||||
material = bpy.data.materials[material_name]
|
||||
row.prop(material.BIMStyleProperties, "active_style_type", icon="SHADING_RENDERED", text="")
|
||||
op = row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
|
||||
op.style_id = style.ifc_definition_id
|
||||
|
||||
if self.props.style_type == "IfcSurfaceStyle":
|
||||
self.layout.label(text="Surface Style Element:")
|
||||
@@ -259,80 +262,6 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.disable_editing_style", text="", icon="CANCEL")
|
||||
|
||||
|
||||
class BIM_PT_style(MaterialButtonsPanel, Panel):
|
||||
bl_label = "Style"
|
||||
bl_idname = "BIM_PT_style"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
IfcStore.get_file()
|
||||
and context.active_object is not None
|
||||
and context.active_object.active_material is not None
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
mat = context.material
|
||||
props = mat.BIMMaterialProperties
|
||||
row = self.layout.row(align=True)
|
||||
if not props.ifc_style_id:
|
||||
row.operator("bim.add_style", icon="ADD")
|
||||
return
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.update_style_colours", icon="GREASEPENCIL")
|
||||
row.operator("bim.update_style_textures", icon="TEXTURE", text="")
|
||||
row.operator("bim.unlink_style", icon="UNLINKED", text="")
|
||||
row.operator("bim.remove_style", icon="X", text="").style = props.ifc_style_id
|
||||
|
||||
|
||||
class BIM_PT_style_attributes(Panel):
|
||||
bl_label = "Style Attributes"
|
||||
bl_idname = "BIM_PT_style_attributes"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
bl_parent_id = "BIM_PT_style"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not IfcStore.get_file():
|
||||
return False
|
||||
try:
|
||||
return bool(context.active_object.active_material.BIMMaterialProperties.ifc_style_id)
|
||||
except:
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
if not StyleAttributesData.is_loaded:
|
||||
StyleAttributesData.load()
|
||||
elif (
|
||||
context.active_object.active_material.BIMMaterialProperties.ifc_style_id
|
||||
!= StyleAttributesData.data["ifc_style_id"]
|
||||
):
|
||||
StyleAttributesData.load()
|
||||
|
||||
obj = context.active_object.active_material
|
||||
mprops = obj.BIMMaterialProperties
|
||||
props = obj.BIMStyleProperties
|
||||
if props.is_editing:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_style", icon="CANCEL", text="")
|
||||
blenderbim.bim.helper.draw_attributes(props.attributes, self.layout)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="STEP ID")
|
||||
row.label(text=str(mprops.ifc_style_id))
|
||||
|
||||
for attribute in StyleAttributesData.data["attributes"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=attribute["name"])
|
||||
row.label(text=attribute["value"])
|
||||
|
||||
|
||||
class BIM_UL_styles(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
|
||||
@@ -22,6 +22,7 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.api
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
@@ -454,7 +455,6 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
predefined_type=predefined_type,
|
||||
ifc_class="IfcStairFlightType",
|
||||
should_add_representation=False,
|
||||
context=body,
|
||||
)
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
bpy.ops.bim.add_stair()
|
||||
@@ -512,10 +512,7 @@ class RemoveType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
element = tool.Ifc.get().by_id(self.element)
|
||||
obj = tool.Ifc.get_object(element)
|
||||
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
|
||||
if obj:
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
bpy.data.objects.remove(obj)
|
||||
tool.Geometry.delete_ifc_object(obj)
|
||||
|
||||
|
||||
class RenameType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.root
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
|
||||
@@ -77,6 +78,10 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, "You can't add an opening to another opening.")
|
||||
continue
|
||||
|
||||
if not hasattr(element1, "HasOpenings"):
|
||||
self.report({"INFO"}, f"An {element1.is_a()} is not allowed to have an opening.")
|
||||
continue
|
||||
|
||||
if tool.Ifc.is_moved(obj1):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj1)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import os
|
||||
import bpy
|
||||
import addon_utils
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from bpy.types import Panel
|
||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||
@@ -29,8 +30,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
|
||||
|
||||
@@ -289,8 +291,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.prop(self, "spatial_elements_unselectable")
|
||||
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
||||
row = layout.row()
|
||||
@@ -319,28 +319,30 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(context.scene.BIMProperties, "data_dir")
|
||||
row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.BIMProperties, "pset_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "sheets_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "layouts_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "titleblocks_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawings_dir")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "stylesheet_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "markers_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "symbols_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "patterns_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "shadingstyles_path")
|
||||
row = self.layout.row(align=True)
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "shadingstyle_default")
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawing_font")
|
||||
|
||||
|
||||
# Scene panel groups
|
||||
@@ -402,6 +404,21 @@ class BIM_PT_tabs(Panel):
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(aprops, "tab", text="")
|
||||
|
||||
if blenderbim.last_error:
|
||||
box = self.layout.box()
|
||||
box.alert=True
|
||||
row = box.row(align=True)
|
||||
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
|
||||
row.operator("bim.close_error", text="", icon="CANCEL")
|
||||
if platform.system() == "Windows":
|
||||
box.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
|
||||
else:
|
||||
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
|
||||
|
||||
|
||||
@@ -1,183 +1,232 @@
|
||||
def add_cost_schedule(ifc, name, predefined_type):
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def add_cost_schedule(ifc: tool.Ifc, name, predefined_type):
|
||||
ifc.run("cost.add_cost_schedule", name=name, predefined_type=predefined_type)
|
||||
|
||||
|
||||
def edit_cost_schedule(ifc, cost, cost_schedule):
|
||||
def edit_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance):
|
||||
attributes = cost.get_cost_schedule_attributes()
|
||||
ifc.run("cost.edit_cost_schedule", cost_schedule=cost_schedule, attributes=attributes)
|
||||
cost.disable_editing_cost_schedule()
|
||||
|
||||
|
||||
def disable_editing_cost_schedule(cost):
|
||||
def disable_editing_cost_schedule(cost: tool.Cost):
|
||||
cost.disable_editing_cost_schedule()
|
||||
|
||||
|
||||
def remove_cost_schedule(ifc, cost_schedule):
|
||||
def remove_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("cost.remove_cost_schedule", cost_schedule=cost_schedule)
|
||||
cost.remove_stored_schedule_columns(cost_schedule)
|
||||
|
||||
|
||||
def enable_editing_cost_schedule_attributes(cost, cost_schedule):
|
||||
def enable_editing_cost_schedule_attributes(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance):
|
||||
cost.load_cost_schedule_attributes(cost_schedule)
|
||||
cost.enable_editing_cost_schedule_attributes(cost_schedule)
|
||||
|
||||
|
||||
def enable_editing_cost_items(cost, cost_schedule):
|
||||
def enable_editing_cost_items(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance):
|
||||
cost.enable_editing_cost_items(cost_schedule)
|
||||
cost.load_active_schedule_columns()
|
||||
cost.load_cost_schedule_tree()
|
||||
cost.play_sound()
|
||||
|
||||
|
||||
def add_summary_cost_item(ifc, cost, cost_schedule):
|
||||
def add_summary_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance):
|
||||
ifc.run("cost.add_cost_item", cost_schedule=cost_schedule)
|
||||
cost.load_cost_schedule_tree()
|
||||
# cost.play_sound()
|
||||
|
||||
|
||||
def add_cost_item(ifc, cost, cost_item):
|
||||
def add_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
ifc.run("cost.add_cost_item", cost_item=cost_item)
|
||||
cost.load_cost_schedule_tree()
|
||||
# cost.enable_editing_cost_schedule_attributes(cost_schedule)
|
||||
|
||||
|
||||
def expand_cost_item(cost, cost_item):
|
||||
def expand_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.expand_cost_item(cost_item)
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def expand_cost_items(cost):
|
||||
def expand_cost_items(cost: tool.Cost):
|
||||
cost.expand_cost_items()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def contract_cost_item(cost, cost_item):
|
||||
def contract_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.contract_cost_item(cost_item)
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def contract_cost_items(cost):
|
||||
def contract_cost_items(cost: tool.Cost):
|
||||
cost.contract_cost_items()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def remove_cost_item(ifc, cost, cost_item_id):
|
||||
def remove_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item_id: int):
|
||||
cost_item = ifc.get().by_id(cost_item_id)
|
||||
ifc.run("cost.remove_cost_item", cost_item=cost_item)
|
||||
cost.clean_up_cost_item_tree(cost_item_id)
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def enable_editing_cost_item_attributes(cost, cost_item):
|
||||
def enable_editing_cost_item_attributes(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.enable_editing_cost_item_attributes(cost_item)
|
||||
cost.load_cost_item_attributes(cost_item)
|
||||
|
||||
|
||||
def disable_editing_cost_item(cost):
|
||||
def disable_editing_cost_item(cost: tool.Cost):
|
||||
cost.disable_editing_cost_item()
|
||||
|
||||
|
||||
def edit_cost_item(ifc, cost):
|
||||
def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost):
|
||||
attributes = cost.get_cost_item_attributes()
|
||||
ifc.run("cost.edit_cost_item", cost_item=cost.get_active_cost_item(), attributes=attributes)
|
||||
cost.disable_editing_cost_item()
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def assign_cost_item_type(ifc, cost, spatial, cost_item, prop_name):
|
||||
product_types = spatial.get_selected_product_types()
|
||||
[
|
||||
def assign_cost_item_type(
|
||||
ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance, prop_name
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Returns:
|
||||
List of found product types.
|
||||
"""
|
||||
product_types = list(spatial.get_selected_product_types())
|
||||
rels = [
|
||||
ifc.run("control.assign_control", relating_control=cost_item, related_object=product_type)
|
||||
for product_type in product_types
|
||||
]
|
||||
cost.load_cost_item_types(cost_item)
|
||||
return product_types
|
||||
|
||||
|
||||
def unassign_cost_item_type(ifc, cost, spatial, cost_item, product_types):
|
||||
def unassign_cost_item_type(
|
||||
ifc: tool.Ifc,
|
||||
cost: tool.Cost,
|
||||
spatial: tool.Spatial,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
product_types: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Returns:
|
||||
List of found product types.
|
||||
"""
|
||||
if not product_types:
|
||||
product_types = spatial.get_selected_product_types()
|
||||
product_types = list(spatial.get_selected_product_types())
|
||||
[
|
||||
ifc.run("control.unassign_control", relating_control=cost_item, related_object=product_type)
|
||||
for product_type in product_types
|
||||
]
|
||||
cost.load_cost_item_types(cost_item)
|
||||
return product_types
|
||||
|
||||
|
||||
def load_cost_item_types(cost):
|
||||
def load_cost_item_types(cost: tool.Cost):
|
||||
cost_item = cost.get_active_cost_item()
|
||||
cost.load_cost_item_types(cost_item)
|
||||
|
||||
|
||||
def assign_cost_item_quantity(ifc, cost, cost_item, related_object_type, prop_name):
|
||||
def assign_cost_item_quantity(
|
||||
ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type, prop_name
|
||||
):
|
||||
products = cost.get_products(related_object_type)
|
||||
if products:
|
||||
ifc.run("cost.assign_cost_item_quantity", cost_item=cost_item, products=products, prop_name=prop_name)
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type=related_object_type)
|
||||
|
||||
|
||||
def load_cost_item_quantities(cost):
|
||||
def load_cost_item_quantities(cost: tool.Cost):
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def load_cost_item_element_quantities(cost):
|
||||
def load_cost_item_element_quantities(cost: tool.Cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT")
|
||||
|
||||
|
||||
def load_cost_item_task_quantities(cost):
|
||||
def load_cost_item_task_quantities(cost: tool.Cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PROCESS")
|
||||
|
||||
|
||||
def load_cost_item_resource_quantities(cost):
|
||||
def load_cost_item_resource_quantities(cost: tool.Cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE")
|
||||
|
||||
|
||||
def assign_cost_value(ifc, cost_item, cost_rate):
|
||||
def assign_cost_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, cost_rate):
|
||||
ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate)
|
||||
|
||||
|
||||
def load_schedule_of_rates(cost, schedule_of_rates):
|
||||
def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates):
|
||||
cost.load_schedule_of_rates_tree(schedule_of_rates)
|
||||
|
||||
|
||||
def unassign_cost_item_quantity(ifc, cost, cost_item, products):
|
||||
def unassign_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, products):
|
||||
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def enable_editing_cost_item_quantities(cost, cost_item):
|
||||
def enable_editing_cost_item_quantities(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.enable_editing_cost_item_quantities(cost_item)
|
||||
|
||||
|
||||
def enable_editing_cost_item_values(cost, cost_item):
|
||||
def enable_editing_cost_item_values(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.enable_editing_cost_item_values(cost_item)
|
||||
|
||||
|
||||
def add_cost_item_quantity(ifc, cost_item, ifc_class):
|
||||
def add_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, ifc_class):
|
||||
ifc.run("cost.add_cost_item_quantity", cost_item=cost_item, ifc_class=ifc_class)
|
||||
|
||||
|
||||
def remove_cost_item_quantity(ifc, cost_item, physical_quantity):
|
||||
def remove_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, physical_quantity):
|
||||
ifc.run("cost.remove_cost_item_quantity", cost_item=cost_item, physical_quantity=physical_quantity)
|
||||
|
||||
|
||||
def enable_editing_cost_item_quantity(cost, physical_quantity):
|
||||
def enable_editing_cost_item_quantity(cost: tool.Cost, physical_quantity):
|
||||
cost.load_cost_item_quantity_attributes(physical_quantity)
|
||||
cost.enable_editing_cost_item_quantity(physical_quantity)
|
||||
|
||||
|
||||
def disable_editing_cost_item_quantity(cost):
|
||||
def disable_editing_cost_item_quantity(cost: tool.Cost):
|
||||
cost.disable_editing_cost_item_quantity()
|
||||
|
||||
|
||||
def edit_cost_item_quantity(ifc, cost, physical_quantity):
|
||||
def edit_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, physical_quantity):
|
||||
attributes = cost.get_cost_item_quantity_attributes()
|
||||
ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
|
||||
cost.disable_editing_cost_item_quantity()
|
||||
cost.load_cost_item_quantities()
|
||||
|
||||
|
||||
def add_cost_value(ifc, cost, parent, cost_type, cost_category):
|
||||
def add_cost_value(ifc: tool.Ifc, cost: tool.Cost, parent, cost_type, cost_category):
|
||||
value = ifc.run("cost.add_cost_value", parent=parent)
|
||||
ifc.run(
|
||||
"cost.edit_cost_value",
|
||||
@@ -186,82 +235,84 @@ def add_cost_value(ifc, cost, parent, cost_type, cost_category):
|
||||
)
|
||||
|
||||
|
||||
def remove_cost_value(ifc, parent, cost_value):
|
||||
def remove_cost_value(ifc: tool.Ifc, parent, cost_value):
|
||||
ifc.run("cost.remove_cost_value", parent=parent, cost_value=cost_value)
|
||||
|
||||
|
||||
def enable_editing_cost_item_value(cost, cost_value):
|
||||
def enable_editing_cost_item_value(cost: tool.Cost, cost_value):
|
||||
cost.load_cost_item_value_attributes(cost_value)
|
||||
cost.enable_editing_cost_item_value(cost_value)
|
||||
|
||||
|
||||
def disable_editing_cost_item_value(cost):
|
||||
def disable_editing_cost_item_value(cost: tool.Cost):
|
||||
cost.disable_editing_cost_item_value()
|
||||
|
||||
|
||||
def enable_editing_cost_item_value_formula(cost, cost_value):
|
||||
def enable_editing_cost_item_value_formula(cost: tool.Cost, cost_value):
|
||||
cost.load_cost_item_value_formula_attributes(cost_value)
|
||||
cost.enable_editing_cost_item_value_formula(cost_value)
|
||||
|
||||
|
||||
def edit_cost_item_value_formula(ifc, cost, cost_value):
|
||||
def edit_cost_item_value_formula(ifc: tool.Ifc, cost: tool.Cost, cost_value):
|
||||
formula = cost.get_cost_item_value_formula()
|
||||
ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula)
|
||||
cost.disable_editing_cost_item_value()
|
||||
|
||||
|
||||
def edit_cost_value(ifc, cost, cost_value):
|
||||
def edit_cost_value(ifc: tool.Ifc, cost: tool.Cost, cost_value):
|
||||
attributes = cost.get_cost_value_attributes()
|
||||
ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes)
|
||||
cost.disable_editing_cost_item_value()
|
||||
# cost.load_cost_item_values(cost.get_highlighted_cost_item())
|
||||
|
||||
|
||||
def copy_cost_item_values(ifc, cost, source, destination):
|
||||
def copy_cost_item_values(ifc: tool.Ifc, cost: tool.Cost, source, destination):
|
||||
ifc.run("cost.copy_cost_item_values", source=source, destination=destination)
|
||||
|
||||
|
||||
def select_cost_item_products(cost, spatial, cost_item):
|
||||
def select_cost_item_products(cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance):
|
||||
is_deep = cost.show_nested_cost_item_elements()
|
||||
products = cost.get_cost_item_products(cost_item, is_deep)
|
||||
spatial.select_products(products)
|
||||
|
||||
|
||||
def select_cost_schedule_products(cost, spatial, cost_schedule):
|
||||
def select_cost_schedule_products(cost: tool.Cost, spatial: tool.Spatial, cost_schedule: ifcopenshell.entity_instance):
|
||||
products = cost.get_cost_schedule_products(cost_schedule)
|
||||
spatial.select_products(products)
|
||||
|
||||
|
||||
def import_cost_schedule_csv(cost, file_path, is_schedule_of_rates):
|
||||
def import_cost_schedule_csv(cost: tool.Cost, file_path, is_schedule_of_rates):
|
||||
cost.import_cost_schedule_csv(file_path, is_schedule_of_rates)
|
||||
|
||||
|
||||
def add_cost_column(cost, name):
|
||||
def add_cost_column(cost: tool.Cost, name):
|
||||
cost.add_cost_column(name)
|
||||
|
||||
|
||||
def remove_cost_column(cost, name):
|
||||
def remove_cost_column(cost: tool.Cost, name):
|
||||
cost.remove_cost_column(name)
|
||||
|
||||
|
||||
def expand_cost_item_rate(cost, cost_item):
|
||||
def expand_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.expand_cost_item_rate(cost_item)
|
||||
|
||||
|
||||
def contract_cost_item_rate(cost, cost_item):
|
||||
def contract_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost.contract_cost_item_rate(cost_item)
|
||||
|
||||
|
||||
def calculate_cost_item_resource_value(ifc, cost_item):
|
||||
def calculate_cost_item_resource_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance):
|
||||
ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item)
|
||||
|
||||
|
||||
def export_cost_schedules(cost, filepath, format, cost_schedule=None):
|
||||
def export_cost_schedules(cost: tool.Cost, filepath: str, format: str, cost_schedule=None):
|
||||
cost.play_sound()
|
||||
return cost.export_cost_schedules(filepath, format, cost_schedule)
|
||||
|
||||
|
||||
def clear_cost_item_assignments(ifc, cost, cost_item, related_object_type):
|
||||
def clear_cost_item_assignments(
|
||||
ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type
|
||||
):
|
||||
products = cost.get_cost_item_assignments(cost_item, filter_by_type=related_object_type)
|
||||
if products:
|
||||
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
|
||||
@@ -269,7 +320,7 @@ def clear_cost_item_assignments(ifc, cost, cost_item, related_object_type):
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def select_unassigned_products(ifc, cost, spatial):
|
||||
def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial):
|
||||
spatial.deselect_objects()
|
||||
products = ifc.get().by_type("IfcElement")
|
||||
cost_schedule = cost.get_active_cost_schedule()
|
||||
@@ -277,11 +328,11 @@ def select_unassigned_products(ifc, cost, spatial):
|
||||
spatial.select_products(selection)
|
||||
|
||||
|
||||
def load_product_cost_items(cost, product):
|
||||
def load_product_cost_items(cost: tool.Cost, product):
|
||||
cost.load_product_cost_items(product)
|
||||
|
||||
|
||||
def highlight_product_cost_item(spatial, cost, cost_item):
|
||||
def highlight_product_cost_item(spatial: tool.Spatial, cost: tool.Cost, cost_item: ifcopenshell.entity_instance):
|
||||
cost_schedule = cost.get_cost_schedule(cost_item)
|
||||
is_cost_schedule_active = cost.is_cost_schedule_active(cost_schedule)
|
||||
if is_cost_schedule_active:
|
||||
@@ -290,7 +341,7 @@ def highlight_product_cost_item(spatial, cost, cost_item):
|
||||
return "Cost schedule is not active"
|
||||
|
||||
|
||||
def change_parent_cost_item(ifc, cost, new_parent):
|
||||
def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent):
|
||||
cost_item = cost.get_active_cost_item()
|
||||
if cost_item and cost.is_root_cost_item(cost_item):
|
||||
return "Cannot change root cost item"
|
||||
@@ -300,7 +351,7 @@ def change_parent_cost_item(ifc, cost, new_parent):
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def copy_cost_item(ifc, cost):
|
||||
def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost):
|
||||
cost_item = cost.get_highlighted_cost_item()
|
||||
if cost_item:
|
||||
cost_item = ifc.run("cost.copy_cost_item", cost_item=cost_item)
|
||||
@@ -308,7 +359,7 @@ def copy_cost_item(ifc, cost):
|
||||
cost.load_cost_schedule_tree()
|
||||
|
||||
|
||||
def add_currency(ifc, cost):
|
||||
def add_currency(ifc: tool.Ifc, cost: tool.Cost):
|
||||
unit = ifc.run("unit.add_monetary_unit")
|
||||
attributes = cost.get_currency_attributes()
|
||||
ifc.run("unit.edit_monetary_unit", unit=unit, attributes=attributes)
|
||||
|
||||
@@ -16,36 +16,44 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
import ifcopenshell
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def enable_editing_text(drawing, obj=None):
|
||||
def enable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None:
|
||||
drawing.enable_editing_text(obj)
|
||||
drawing.import_text_attributes(obj)
|
||||
|
||||
|
||||
def disable_editing_text(drawing, obj=None):
|
||||
def disable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None:
|
||||
drawing.disable_editing_text(obj)
|
||||
|
||||
|
||||
def edit_text(drawing, obj=None):
|
||||
def edit_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None:
|
||||
drawing.synchronise_ifc_and_text_attributes(obj)
|
||||
drawing.update_text_size_pset(obj)
|
||||
drawing.update_text_value(obj)
|
||||
drawing.disable_editing_text(obj)
|
||||
|
||||
|
||||
def enable_editing_assigned_product(drawing, obj=None):
|
||||
def enable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None:
|
||||
drawing.enable_editing_assigned_product(obj)
|
||||
drawing.import_assigned_product(obj)
|
||||
|
||||
|
||||
def disable_editing_assigned_product(drawing, obj=None):
|
||||
def disable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None:
|
||||
drawing.disable_editing_assigned_product(obj)
|
||||
|
||||
|
||||
def edit_assigned_product(ifc, drawing, obj=None, product=None):
|
||||
def edit_assigned_product(
|
||||
ifc: tool.Ifc, drawing: tool.Drawing, obj: bpy.types.Object, product: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> None:
|
||||
element = ifc.get_entity(obj)
|
||||
existing_product = drawing.get_assigned_product(element)
|
||||
if existing_product != product:
|
||||
@@ -58,16 +66,16 @@ def edit_assigned_product(ifc, drawing, obj=None, product=None):
|
||||
drawing.disable_editing_assigned_product(obj)
|
||||
|
||||
|
||||
def load_sheets(drawing):
|
||||
def load_sheets(drawing: tool.Drawing) -> None:
|
||||
drawing.import_sheets()
|
||||
drawing.enable_editing_sheets()
|
||||
|
||||
|
||||
def disable_editing_sheets(drawing):
|
||||
def disable_editing_sheets(drawing: tool.Drawing) -> None:
|
||||
drawing.disable_editing_sheets()
|
||||
|
||||
|
||||
def add_sheet(ifc, drawing, titleblock: ifcopenshell.entity_instance):
|
||||
def add_sheet(ifc: tool.Ifc, drawing, titleblock: ifcopenshell.entity_instance) -> None:
|
||||
sheet = ifc.run("document.add_information")
|
||||
layout = ifc.run("document.add_reference", information=sheet)
|
||||
titleblock_reference = ifc.run("document.add_reference", information=sheet)
|
||||
@@ -93,7 +101,7 @@ def add_sheet(ifc, drawing, titleblock: ifcopenshell.entity_instance):
|
||||
drawing.import_sheets()
|
||||
|
||||
|
||||
def regenerate_sheet(drawing, sheet=None):
|
||||
def regenerate_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None:
|
||||
titleblock_uri = drawing.get_document_uri(sheet, "TITLEBLOCK")
|
||||
drawing.create_svg_sheet(sheet, drawing.sanitise_filename(Path(titleblock_uri).stem))
|
||||
try:
|
||||
@@ -104,11 +112,11 @@ def regenerate_sheet(drawing, sheet=None):
|
||||
drawing.delete_file(path_layout)
|
||||
|
||||
|
||||
def open_sheet(drawing, sheet=None):
|
||||
def open_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None:
|
||||
drawing.open_layout_svg(drawing.get_document_uri(sheet, "LAYOUT"))
|
||||
|
||||
|
||||
def remove_sheet(ifc, drawing, sheet=None):
|
||||
def remove_sheet(ifc: tool.Ifc, drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None:
|
||||
for reference in drawing.get_document_references(sheet):
|
||||
if drawing.get_reference_description(reference) in ("LAYOUT", "SHEET", "REVISION", "RASTER"):
|
||||
uri = ifc.resolve_uri(drawing.get_document_uri(reference))
|
||||
@@ -118,7 +126,7 @@ def remove_sheet(ifc, drawing, sheet=None):
|
||||
drawing.import_sheets()
|
||||
|
||||
|
||||
def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identification: str, name: str) -> None:
|
||||
def rename_sheet(ifc: tool.Ifc, drawing, sheet: ifcopenshell.entity_instance, identification: str, name: str) -> None:
|
||||
if ifc.get_schema() == "IFC2X3":
|
||||
attributes = {"DocumentId": identification, "Name": name}
|
||||
else:
|
||||
@@ -144,30 +152,32 @@ def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identificati
|
||||
drawing.move_file(old_location, ifc.resolve_uri(new_location))
|
||||
|
||||
|
||||
def rename_reference(ifc, drawing, reference=None, identification=None):
|
||||
def rename_reference(
|
||||
ifc: tool.Ifc, drawing: tool.Drawing, reference: ifcopenshell.entity_instance, identification: str
|
||||
) -> None:
|
||||
attributes = drawing.generate_reference_attributes(reference, Identification=identification)
|
||||
ifc.run("document.edit_reference", reference=reference, attributes=attributes)
|
||||
|
||||
|
||||
def load_schedules(drawing):
|
||||
def load_schedules(drawing: tool.Drawing) -> None:
|
||||
drawing.import_documents("SCHEDULE")
|
||||
drawing.enable_editing_schedules()
|
||||
|
||||
|
||||
def load_references(drawing):
|
||||
def load_references(drawing: tool.Drawing) -> None:
|
||||
drawing.import_documents("REFERENCE")
|
||||
drawing.enable_editing_references()
|
||||
|
||||
|
||||
def disable_editing_schedules(drawing):
|
||||
def disable_editing_schedules(drawing: tool.Drawing) -> None:
|
||||
drawing.disable_editing_schedules()
|
||||
|
||||
|
||||
def disable_editing_references(drawing):
|
||||
def disable_editing_references(drawing: tool.Drawing) -> None:
|
||||
drawing.disable_editing_references()
|
||||
|
||||
|
||||
def add_document(ifc, drawing, document_type, uri=None):
|
||||
def add_document(ifc: tool.Ifc, drawing: tool.Drawing, document_type: tool.Drawing.DOCUMENT_TYPE, uri: str) -> None:
|
||||
document = ifc.run("document.add_information")
|
||||
reference = ifc.run("document.add_reference", information=document)
|
||||
name = drawing.get_path_filename(uri)
|
||||
@@ -180,34 +190,43 @@ def add_document(ifc, drawing, document_type, uri=None):
|
||||
drawing.import_documents(document_type)
|
||||
|
||||
|
||||
def remove_document(ifc, drawing, document_type, document=None):
|
||||
def remove_document(
|
||||
ifc: tool.Ifc,
|
||||
drawing: tool.Drawing,
|
||||
document_type: tool.Drawing.DOCUMENT_TYPE,
|
||||
document: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
ifc.run("document.remove_information", information=document)
|
||||
drawing.import_documents(document_type)
|
||||
|
||||
|
||||
def open_schedule(drawing, schedule=None):
|
||||
def open_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None:
|
||||
drawing.open_spreadsheet(drawing.get_document_uri(schedule))
|
||||
|
||||
|
||||
def open_reference(drawing, reference=None):
|
||||
def open_reference(drawing: tool.Drawing, reference: ifcopenshell.entity_instance) -> None:
|
||||
drawing.open_svg(drawing.get_document_uri(reference))
|
||||
|
||||
|
||||
def update_document_name(ifc, drawing, document=None, name=None):
|
||||
def update_document_name(
|
||||
ifc: tool.Ifc, drawing: tool.Drawing, document: ifcopenshell.entity_instance, name=None
|
||||
) -> None:
|
||||
if drawing.get_name(document) != name:
|
||||
ifc.run("document.edit_information", information=document, attributes={"Name": name})
|
||||
|
||||
|
||||
def load_drawings(drawing):
|
||||
def load_drawings(drawing: tool.Drawing) -> None:
|
||||
drawing.import_drawings()
|
||||
drawing.enable_editing_drawings()
|
||||
|
||||
|
||||
def disable_editing_drawings(drawing):
|
||||
def disable_editing_drawings(drawing: tool.Drawing) -> None:
|
||||
drawing.disable_editing_drawings()
|
||||
|
||||
|
||||
def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
||||
def add_drawing(
|
||||
ifc: tool.Ifc, collector: tool.Collector, drawing: tool.Drawing, target_view=None, location_hint=None
|
||||
) -> None:
|
||||
drawing_name = drawing.ensure_unique_drawing_name(drawing.generate_drawing_name(target_view, location_hint))
|
||||
drawing_matrix = drawing.generate_drawing_matrix(target_view, location_hint)
|
||||
camera = drawing.create_camera(drawing_name, drawing_matrix, location_hint)
|
||||
@@ -265,7 +284,12 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
||||
drawing.import_drawings()
|
||||
|
||||
|
||||
def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotations=False):
|
||||
def duplicate_drawing(
|
||||
ifc: tool.Ifc,
|
||||
drawing_tool: tool.Drawing,
|
||||
drawing: ifcopenshell.entity_instance,
|
||||
should_duplicate_annotations: bool = False,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
drawing_name = drawing_tool.ensure_unique_drawing_name(drawing_tool.get_name(drawing))
|
||||
new_drawing = ifc.run("root.copy_class", product=drawing)
|
||||
drawing_tool.copy_representation(drawing, new_drawing)
|
||||
@@ -302,7 +326,7 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati
|
||||
return new_drawing
|
||||
|
||||
|
||||
def remove_drawing(ifc, drawing_tool, drawing=None):
|
||||
def remove_drawing(ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance) -> None:
|
||||
if drawing_tool.is_active_drawing(drawing):
|
||||
drawing_tool.run_drawing_activate_model()
|
||||
|
||||
@@ -330,7 +354,9 @@ def remove_drawing(ifc, drawing_tool, drawing=None):
|
||||
drawing_tool.import_drawings()
|
||||
|
||||
|
||||
def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
|
||||
def update_drawing_name(
|
||||
ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance, name=None
|
||||
) -> None:
|
||||
if drawing_tool.get_name(drawing) != name:
|
||||
ifc.run("attribute.edit_attributes", product=drawing, attributes={"Name": name})
|
||||
group = drawing_tool.get_drawing_group(drawing)
|
||||
@@ -364,7 +390,13 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
|
||||
drawing_tool.import_sheets()
|
||||
|
||||
|
||||
def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None):
|
||||
def add_annotation(
|
||||
ifc: tool.Ifc,
|
||||
collector: tool.Collector,
|
||||
drawing_tool: tool.Drawing,
|
||||
drawing: ifcopenshell.entity_instance,
|
||||
object_type: str,
|
||||
) -> None:
|
||||
target_view = drawing_tool.get_drawing_target_view(drawing)
|
||||
context = drawing_tool.get_annotation_context(target_view, object_type)
|
||||
if not context:
|
||||
@@ -387,12 +419,14 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
|
||||
drawing_tool.enable_editing(obj)
|
||||
|
||||
|
||||
def build_schedule(drawing, schedule=None):
|
||||
def build_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None:
|
||||
drawing.create_svg_schedule(schedule)
|
||||
drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg"))
|
||||
|
||||
|
||||
def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
def sync_references(
|
||||
ifc: tool.Ifc, collector: tool.Collector, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
if not drawing_tool.has_annotation(drawing):
|
||||
return
|
||||
|
||||
@@ -437,13 +471,25 @@ def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
drawing_tool.sync_object_representation(reference_obj)
|
||||
|
||||
|
||||
def select_assigned_product(drawing, context):
|
||||
def select_assigned_product(drawing: tool.Drawing, context: bpy.types.Context) -> None:
|
||||
drawing.select_assigned_product(context)
|
||||
|
||||
|
||||
def activate_drawing_view(ifc, drawing_tool, drawing):
|
||||
def activate_drawing_view(
|
||||
ifc: tool.Ifc, blender: tool.Blender, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
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
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
import blenderbim.core.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def edit_object_placement(
|
||||
@@ -37,8 +38,15 @@ def edit_object_placement(
|
||||
|
||||
|
||||
def add_representation(
|
||||
ifc, geometry, style, surveyor, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
geometry: tool.Geometry,
|
||||
style: tool.Style,
|
||||
surveyor: tool.Surveyor,
|
||||
obj: bpy.types.Object,
|
||||
context: ifcopenshell.entity_instance,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -63,6 +71,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(
|
||||
@@ -86,15 +97,15 @@ def add_representation(
|
||||
|
||||
|
||||
def switch_representation(
|
||||
ifc,
|
||||
geometry,
|
||||
obj=None,
|
||||
representation=None,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
geometry: tool.Geometry,
|
||||
obj: bpy.types.Object,
|
||||
representation: ifcopenshell.entity_instance,
|
||||
should_reload: bool = True,
|
||||
is_global: bool = True,
|
||||
should_sync_changes_first: bool = False,
|
||||
apply_openings: bool = True,
|
||||
) -> None:
|
||||
"""Function can switch to representation that wasn't yet assigned to that object. See #2766.
|
||||
|
||||
`should_sync_changes_first` - sync ifc representation with current state of `obj.data`;
|
||||
@@ -221,3 +232,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
|
||||
|
||||
@@ -21,10 +21,10 @@ def unlink_material(ifc, obj=None):
|
||||
ifc.unlink(obj=obj)
|
||||
|
||||
|
||||
def add_material(ifc, material, style, obj=None, name=None):
|
||||
def add_material(ifc, material, style, obj=None, name=None, category=None, description=None):
|
||||
if not obj:
|
||||
obj = material.add_default_material_object(name)
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj))
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj), category=category, description=description)
|
||||
ifc.link(ifc_material, obj)
|
||||
ifc_style = style.get_style(obj)
|
||||
if ifc_style:
|
||||
@@ -85,6 +85,7 @@ def enable_editing_material(material_tool, material):
|
||||
def edit_material(ifc, material_tool, material):
|
||||
attributes = material_tool.get_material_attributes()
|
||||
ifc.run("material.edit_material", material=material, attributes=attributes)
|
||||
material_tool.sync_blender_material_name(material)
|
||||
material_tool.disable_editing_material()
|
||||
material_type = material_tool.get_active_material_type()
|
||||
material_tool.import_material_definitions(material_type)
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
def create_project(ifc, project, schema=None, template=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def create_project(ifc: tool.Ifc, project: tool.Project, schema: str, template: Optional[str] = None) -> None:
|
||||
if ifc.get():
|
||||
return
|
||||
|
||||
@@ -34,7 +42,7 @@ def create_project(ifc, project, schema=None, template=None):
|
||||
building = project.create_empty("My Building")
|
||||
storey = project.create_empty("My Storey")
|
||||
|
||||
project.run_root_assign_class(obj=project_obj, ifc_class="IfcProject")
|
||||
project.run_root_assign_class(obj=project_obj, ifc_class="IfcProject", should_add_representation=False)
|
||||
project.run_unit_assign_scene_units()
|
||||
|
||||
model = project.run_context_add_context(context_type="Model", context_identifier="", target_view="", parent=0)
|
||||
|
||||
@@ -31,7 +31,7 @@ def copy_property_to_selection(ifc, pset, is_pset=True, obj=None, pset_name=None
|
||||
|
||||
|
||||
def add_pset(ifc, pset, blender, obj_name, obj_type):
|
||||
pset_name = pset.get_pset_name(obj_name, obj_type)
|
||||
pset_name = pset.get_pset_name(obj_name, obj_type, pset_type="PSET")
|
||||
if obj_type == "Object":
|
||||
elements = [ifc.get_entity(obj) for obj in blender.get_selected_objects()]
|
||||
else:
|
||||
@@ -39,6 +39,30 @@ def add_pset(ifc, pset, blender, obj_name, obj_type):
|
||||
for element in elements:
|
||||
if not element:
|
||||
continue
|
||||
if not pset.is_pset_applicable(element, pset_name):
|
||||
if pset_name and not pset.is_pset_applicable(element, pset_name):
|
||||
continue
|
||||
pset.enable_pset_editing(pset_id=0, pset_name=pset_name, pset_type="PSET", obj=obj_name, obj_type=obj_type)
|
||||
|
||||
|
||||
def enable_pset_editing(pset_tool, pset, pset_name, pset_type, obj_name, obj_type):
|
||||
props = pset_tool.get_pset_props(obj_name, obj_type)
|
||||
pset_tool.clear_blender_pset_properties(props)
|
||||
|
||||
pset_template = pset_tool.get_pset_template(pset_name)
|
||||
|
||||
if pset_template:
|
||||
pset_tool.import_pset_from_template(pset_template, pset, props)
|
||||
has_template = True
|
||||
else:
|
||||
has_template = False
|
||||
|
||||
if pset:
|
||||
pset_tool.import_pset_from_existing(pset, props)
|
||||
pset_tool.set_active_pset(props, pset, has_template)
|
||||
else:
|
||||
pset_tool.enable_proposed_pset(props, pset_name, pset_type, has_template)
|
||||
|
||||
|
||||
def add_proposed_prop(pset, obj_name, obj_type, name, value):
|
||||
props = pset.get_pset_props(obj_name, obj_type)
|
||||
pset.add_proposed_property(name, pset.cast_string_to_primitive(value), props)
|
||||
|
||||
@@ -18,42 +18,57 @@
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union, Iterable
|
||||
|
||||
def load_resources(resource):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def load_resources(resource: tool.Resource) -> None:
|
||||
resource.load_resources()
|
||||
|
||||
|
||||
def add_resource(tool_ifc, resource_tool, ifc_class, parent_resource=None):
|
||||
def add_resource(
|
||||
tool_ifc: tool.Ifc,
|
||||
resource_tool: tool.Resource,
|
||||
ifc_class,
|
||||
parent_resource: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> None:
|
||||
tool_ifc.run("resource.add_resource", ifc_class=ifc_class, parent_resource=parent_resource)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def disable_editing_resource(resource_tool):
|
||||
def disable_editing_resource(resource_tool: tool.Resource) -> None:
|
||||
resource_tool.disable_editing_resource()
|
||||
|
||||
|
||||
def disable_resource_editing_ui(resource_tool):
|
||||
def disable_resource_editing_ui(resource_tool: tool.Resource) -> None:
|
||||
resource_tool.disable_resource_editing_ui()
|
||||
|
||||
|
||||
def enable_editing_resource(resource_tool, resource):
|
||||
def enable_editing_resource(resource_tool: tool.Resource, resource) -> None:
|
||||
resource_tool.enable_editing_resource(resource)
|
||||
resource_tool.load_resource_attributes(resource)
|
||||
|
||||
|
||||
def edit_resource(ifc, resource_tool, resource):
|
||||
def edit_resource(ifc: tool.Ifc, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
attributes = resource_tool.get_resource_attributes()
|
||||
ifc.run("resource.edit_resource", resource=resource, attributes=attributes)
|
||||
resource_tool.load_resource_properties()
|
||||
resource_tool.disable_editing_resource()
|
||||
|
||||
|
||||
def remove_resource(ifc, resource_tool, resource=None):
|
||||
def remove_resource(ifc: tool.Ifc, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("resource.remove_resource", resource=resource)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def enable_editing_resource_time(ifc_tool, resource_tool, resource):
|
||||
def enable_editing_resource_time(
|
||||
ifc_tool, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
resource_time = resource_tool.get_resource_time(resource)
|
||||
if resource_time is None:
|
||||
resource_time = ifc_tool.run("resource.add_resource_time", resource=resource)
|
||||
@@ -61,17 +76,21 @@ def enable_editing_resource_time(ifc_tool, resource_tool, resource):
|
||||
resource_tool.load_resource_time_attributes(resource_time)
|
||||
|
||||
|
||||
def edit_resource_time(ifc, resource_tool, resource_time):
|
||||
def edit_resource_time(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, resource_time: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
attributes = resource_tool.get_resource_time_attributes()
|
||||
ifc.run("resource.edit_resource_time", resource_time=resource_time, attributes=attributes)
|
||||
resource_tool.disable_editing_resource()
|
||||
|
||||
|
||||
def disable_editing_resource_time(resource_tool):
|
||||
def disable_editing_resource_time(resource_tool: tool.Resource) -> None:
|
||||
resource_tool.disable_editing_resource()
|
||||
|
||||
|
||||
def calculate_resource_work(ifc, resource_tool, resource):
|
||||
def calculate_resource_work(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
if resource_tool.get_task_assignments(resource):
|
||||
ifc.run("resource.calculate_resource_work", resource=resource)
|
||||
else:
|
||||
@@ -81,92 +100,112 @@ def calculate_resource_work(ifc, resource_tool, resource):
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def enable_editing_resource_costs(resource_tool, resource):
|
||||
def enable_editing_resource_costs(resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.enable_editing_resource_costs(resource)
|
||||
resource_tool.disable_editing_resource_cost_value()
|
||||
|
||||
|
||||
def disable_editing_resource_cost_value(resource_tool):
|
||||
def disable_editing_resource_cost_value(resource_tool: tool.Resource) -> None:
|
||||
resource_tool.disable_editing_resource_cost_value()
|
||||
|
||||
|
||||
def enable_editing_resource_cost_value(resource_tool, cost_value):
|
||||
def enable_editing_resource_cost_value(resource_tool: tool.Resource, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.enable_editing_cost_value_attributes(cost_value)
|
||||
resource_tool.load_cost_value_attributes(cost_value)
|
||||
|
||||
|
||||
def enable_editing_resource_cost_value_formula(resource_tool, cost_value):
|
||||
def enable_editing_resource_cost_value_formula(
|
||||
resource_tool: tool.Resource, cost_value: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
resource_tool.enable_editing_resource_cost_value_formula(cost_value)
|
||||
|
||||
|
||||
def edit_resource_cost_value_formula(ifc, resource_tool, cost_value):
|
||||
def edit_resource_cost_value_formula(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, cost_value: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
formula = resource_tool.get_resource_cost_value_formula()
|
||||
ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula)
|
||||
resource_tool.disable_editing_resource_cost_value()
|
||||
|
||||
|
||||
def edit_resource_cost_value(ifc, resource_tool, cost_value):
|
||||
def edit_resource_cost_value(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, cost_value: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
attributes = resource_tool.get_resource_cost_value_attributes()
|
||||
ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes)
|
||||
resource_tool.disable_editing_resource_cost_value()
|
||||
|
||||
|
||||
def enable_editing_resource_base_quantity(resource_tool, resource):
|
||||
def enable_editing_resource_base_quantity(resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.enable_editing_resource_base_quantity(resource)
|
||||
|
||||
|
||||
def add_resource_quantity(ifc, ifc_class, resource):
|
||||
def add_resource_quantity(ifc: tool.Ifc, ifc_class: str, resource: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("resource.add_resource_quantity", resource=resource, ifc_class=ifc_class)
|
||||
|
||||
|
||||
def remove_resource_quantity(ifc, resource):
|
||||
def remove_resource_quantity(ifc: tool.Ifc, resource: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("resource.remove_resource_quantity", resource=resource)
|
||||
|
||||
|
||||
def enable_editing_resource_quantity(resource_tool, resource_quantity=None):
|
||||
def enable_editing_resource_quantity(
|
||||
resource_tool: tool.Resource, resource_quantity: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
resource_tool.enable_editing_resource_quantity(resource_quantity)
|
||||
|
||||
|
||||
def disable_editing_resource_quantity(resource_tool):
|
||||
def disable_editing_resource_quantity(resource_tool: tool.Resource) -> None:
|
||||
resource_tool.disable_editing_resource_quantity()
|
||||
|
||||
|
||||
def edit_resource_quantity(resource_tool, ifc, physical_quantity=None):
|
||||
def edit_resource_quantity(
|
||||
resource_tool: tool.Resource, ifc: tool.Ifc, physical_quantity: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
attributes = resource_tool.get_resource_quantity_attributes()
|
||||
ifc.run("resource.edit_resource_quantity", physical_quantity=physical_quantity, attributes=attributes)
|
||||
resource_tool.disable_editing_resource_quantity()
|
||||
|
||||
|
||||
def import_resources(resource_tool, file_path):
|
||||
def import_resources(resource_tool: tool.Resource, file_path: str) -> None:
|
||||
resource_tool.import_resources(file_path)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def expand_resource(resource_tool, resource):
|
||||
def expand_resource(resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.expand_resource(resource)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def contract_resource(resource_tool, resource):
|
||||
def contract_resource(resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.contract_resource(resource)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def assign_resource(ifc, spatial, resource=None, products=None):
|
||||
def assign_resource(
|
||||
ifc: tool.Ifc,
|
||||
spatial: tool.Spatial,
|
||||
resource: ifcopenshell.entity_instance,
|
||||
products: Optional[Iterable[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
if not products:
|
||||
products = spatial.get_selected_products()
|
||||
for product in products:
|
||||
rel = ifc.run("resource.assign_resource", relating_resource=resource, related_object=product)
|
||||
|
||||
|
||||
def unassign_resource(ifc, spatial, resource=None, products=None):
|
||||
def unassign_resource(
|
||||
ifc: tool.Ifc,
|
||||
spatial: tool.Spatial,
|
||||
resource: ifcopenshell.entity_instance,
|
||||
products: Optional[Iterable[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
if not products:
|
||||
products = spatial.get_selected_products()
|
||||
for product in products:
|
||||
ifc.run("resource.unassign_resource", relating_resource=resource, related_object=product)
|
||||
|
||||
|
||||
def edit_productivity_pset(ifc, resource_tool):
|
||||
def edit_productivity_pset(ifc: tool.Ifc, resource_tool: tool.Resource) -> None:
|
||||
resource = resource_tool.get_highlighted_resource()
|
||||
if resource is None:
|
||||
return
|
||||
@@ -178,7 +217,9 @@ def edit_productivity_pset(ifc, resource_tool):
|
||||
ifc.run("pset.edit_pset", pset=pset, properties=resource_tool.get_productivity_attributes())
|
||||
|
||||
|
||||
def add_usage_constraint(ifc, resource_tool, resource=None, reference_path=None):
|
||||
def add_usage_constraint(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance, reference_path: str
|
||||
) -> None:
|
||||
metric = resource_tool.has_metric_constraint(resource, "Usage")
|
||||
if metric:
|
||||
return print("Must remove existing metric first")
|
||||
@@ -198,7 +239,9 @@ def add_usage_constraint(ifc, resource_tool, resource=None, reference_path=None)
|
||||
ifc.run("constraint.assign_constraint", products=[resource], constraint=objective)
|
||||
|
||||
|
||||
def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
||||
def remove_usage_constraint(
|
||||
ifc: tool.Ifc, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance, reference_path: str
|
||||
) -> None:
|
||||
constraints = resource_tool.get_constraints(resource)
|
||||
for constraint in constraints:
|
||||
metrics = resource_tool.get_metrics(constraint)
|
||||
@@ -210,10 +253,12 @@ def remove_usage_constraint(ifc, resource_tool, resource, reference_path):
|
||||
ifc.run("constraint.remove_constraint", constraint=constraint)
|
||||
|
||||
|
||||
def go_to_resource(resource_tool, resource):
|
||||
def go_to_resource(resource_tool: tool.Resource, resource: ifcopenshell.entity_instance) -> None:
|
||||
resource_tool.go_to_resource(resource)
|
||||
|
||||
|
||||
def calculate_resource_usage(ifc, resource_tool, resource):
|
||||
def calculate_resource_usage(
|
||||
ifc: tool.Resource, resource_tool: tool.Resource, resource: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
ifc.run("resource.calculate_resource_usage", resource=resource)
|
||||
resource_tool.load_resources()
|
||||
|
||||
@@ -16,8 +16,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
def copy_class(ifc, collector, geometry, root, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def copy_class(
|
||||
ifc: tool.Ifc, collector: tool.Collector, geometry: tool.Geometry, root: tool.Root, obj: bpy.types.Object
|
||||
) -> ifcopenshell.entity_instance:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -48,16 +58,20 @@ def copy_class(ifc, collector, geometry, root, obj=None):
|
||||
|
||||
|
||||
def assign_class(
|
||||
ifc,
|
||||
collector,
|
||||
root,
|
||||
obj=None,
|
||||
ifc_class=None,
|
||||
predefined_type=None,
|
||||
should_add_representation=True,
|
||||
context=None,
|
||||
ifc_representation_class=None,
|
||||
):
|
||||
ifc: tool.Ifc,
|
||||
collector: tool.Collector,
|
||||
root: tool.Root,
|
||||
obj: bpy.types.Object,
|
||||
ifc_class: str,
|
||||
context: Optional[ifcopenshell.entity_instance] = None,
|
||||
predefined_type: Optional[str] = None,
|
||||
should_add_representation: bool = True,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""
|
||||
Args:
|
||||
context: is not optional if `should_add_representation` is True
|
||||
"""
|
||||
if ifc.get_entity(obj):
|
||||
return
|
||||
|
||||
@@ -67,6 +81,7 @@ def assign_class(
|
||||
ifc.link(element, obj)
|
||||
|
||||
if should_add_representation:
|
||||
assert context, "Context is required for adding a representation"
|
||||
root.run_geometry_add_representation(
|
||||
obj=obj, context=context, ifc_representation_class=ifc_representation_class, profile_set_usage=None
|
||||
)
|
||||
|
||||
@@ -16,103 +16,115 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
def add_work_plan(ifc):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def add_work_plan(ifc: tool.Ifc) -> ifcopenshell.entity_instance:
|
||||
return ifc.run("sequence.add_work_plan")
|
||||
|
||||
|
||||
def remove_work_plan(ifc, work_plan=None):
|
||||
def remove_work_plan(ifc: tool.Ifc, work_plan: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.remove_work_plan", work_plan=work_plan)
|
||||
|
||||
|
||||
def enable_editing_work_plan(sequence, work_plan=None):
|
||||
def enable_editing_work_plan(sequence: tool.Sequence, work_plan: ifcopenshell.entity_instance) -> None:
|
||||
sequence.load_work_plan_attributes(work_plan)
|
||||
sequence.enable_editing_work_plan(work_plan)
|
||||
|
||||
|
||||
def disable_editing_work_plan(sequence):
|
||||
def disable_editing_work_plan(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_work_plan()
|
||||
|
||||
|
||||
def edit_work_plan(ifc, sequence, work_plan=None):
|
||||
def edit_work_plan(ifc: tool.Ifc, sequence: tool.Sequence, work_plan: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_work_plan_attributes()
|
||||
ifc.run("sequence.edit_work_plan", work_plan=work_plan, attributes=attributes)
|
||||
sequence.disable_editing_work_plan()
|
||||
|
||||
|
||||
def edit_work_schedule(ifc, sequence, work_schedule=None):
|
||||
def edit_work_schedule(ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_work_schedule_attributes()
|
||||
ifc.run("sequence.edit_work_schedule", work_schedule=work_schedule, attributes=attributes)
|
||||
sequence.disable_editing_work_schedule()
|
||||
|
||||
|
||||
def enable_editing_work_plan_schedules(sequence, work_plan=None):
|
||||
def enable_editing_work_plan_schedules(
|
||||
sequence: tool.Sequence, work_plan: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> None:
|
||||
sequence.enable_editing_work_plan_schedules(work_plan)
|
||||
|
||||
|
||||
def add_work_schedule(ifc, sequence, name=None):
|
||||
def add_work_schedule(ifc: tool.Ifc, sequence: tool.Sequence, name: str) -> ifcopenshell.entity_instance:
|
||||
predefined_type, object_type = sequence.get_user_predefined_type()
|
||||
return ifc.run("sequence.add_work_schedule", name=name, predefined_type=predefined_type, object_type=object_type)
|
||||
|
||||
|
||||
def remove_work_schedule(ifc, work_schedule=None):
|
||||
def remove_work_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.remove_work_schedule", work_schedule=work_schedule)
|
||||
|
||||
|
||||
def assign_work_schedule(ifc, work_plan=None, work_schedule=None):
|
||||
def assign_work_schedule(
|
||||
ifc: tool.Ifc, work_plan: ifcopenshell.entity_instance, work_schedule: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if work_schedule:
|
||||
return ifc.run("aggregate.assign_object", relating_object=work_plan, products=[work_schedule])
|
||||
|
||||
|
||||
def unassign_work_schedule(ifc, work_schedule=None):
|
||||
def unassign_work_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("aggregate.unassign_object", products=[work_schedule])
|
||||
|
||||
|
||||
def enable_editing_work_schedule(sequence, work_schedule=None):
|
||||
def enable_editing_work_schedule(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
sequence.load_work_schedule_attributes(work_schedule)
|
||||
sequence.enable_editing_work_schedule(work_schedule)
|
||||
|
||||
|
||||
def enable_editing_work_schedule_tasks(sequence, work_schedule=None):
|
||||
def enable_editing_work_schedule_tasks(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
sequence.enable_editing_work_schedule_tasks(work_schedule)
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def load_task_tree(sequence, work_schedule):
|
||||
def load_task_tree(sequence: tool.Sequence, work_schedule) -> None:
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def expand_task(sequence, task=None):
|
||||
def expand_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
sequence.expand_task(task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def expand_all_tasks(sequence):
|
||||
def expand_all_tasks(sequence: tool.Sequence) -> None:
|
||||
sequence.expand_all_tasks()
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def contract_task(sequence, task=None):
|
||||
def contract_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
sequence.contract_task(task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def contract_all_tasks(sequence):
|
||||
def contract_all_tasks(sequence: tool.Sequence) -> None:
|
||||
sequence.contract_all_tasks()
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def remove_task(ifc, sequence, task=None):
|
||||
def remove_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.remove_task", task=task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
@@ -120,44 +132,42 @@ def remove_task(ifc, sequence, task=None):
|
||||
sequence.disable_selecting_deleted_task()
|
||||
|
||||
|
||||
def load_task_properties(sequence):
|
||||
def load_task_properties(sequence: tool.Sequence) -> None:
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def disable_editing_work_schedule(sequence):
|
||||
def disable_editing_work_schedule(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_work_schedule()
|
||||
|
||||
|
||||
def add_summary_task(ifc, sequence, work_schedule=None):
|
||||
def add_summary_task(ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.add_task", work_schedule=work_schedule)
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def add_task(ifc, sequence, parent_task=None):
|
||||
def add_task(
|
||||
ifc: tool.Ifc, sequence: tool.Sequence, parent_task: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> None:
|
||||
ifc.run("sequence.add_task", parent_task=parent_task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def enable_editing_task(sequence, task=None):
|
||||
sequence.enable_editing_task(task)
|
||||
|
||||
|
||||
def enable_editing_task_attributes(sequence, task=None):
|
||||
def enable_editing_task_attributes(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
sequence.load_task_attributes(task)
|
||||
sequence.enable_editing_task_attributes(task)
|
||||
|
||||
|
||||
def edit_task(ifc, sequence, task=None):
|
||||
def edit_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_task_attributes()
|
||||
ifc.run("sequence.edit_task", task=task, attributes=attributes)
|
||||
sequence.load_task_properties(task=task)
|
||||
sequence.disable_editing_task()
|
||||
|
||||
|
||||
def copy_task_attribute(ifc, sequence, attribute_name=None):
|
||||
def copy_task_attribute(ifc: tool.Ifc, sequence: tool.Sequence, attribute_name: str) -> None:
|
||||
for task in sequence.get_checked_tasks():
|
||||
ifc.run(
|
||||
"sequence.edit_task",
|
||||
@@ -167,18 +177,18 @@ def copy_task_attribute(ifc, sequence, attribute_name=None):
|
||||
sequence.load_task_properties(task)
|
||||
|
||||
|
||||
def duplicate_task(ifc, sequence, task=None):
|
||||
def duplicate_task(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.duplicate_task", task=task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
sequence.load_task_tree(work_schedule)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def disable_editing_task(sequence):
|
||||
def disable_editing_task(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_task()
|
||||
|
||||
|
||||
def enable_editing_task_time(ifc, sequence, task=None):
|
||||
def enable_editing_task_time(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
task_time = sequence.get_task_time(task)
|
||||
if task_time is None:
|
||||
task_time = ifc.run("sequence.add_task_time", task=task)
|
||||
@@ -186,7 +196,7 @@ def enable_editing_task_time(ifc, sequence, task=None):
|
||||
sequence.enable_editing_task_time(task)
|
||||
|
||||
|
||||
def edit_task_time(ifc, sequence, resource, task_time=None):
|
||||
def edit_task_time(ifc: tool.Ifc, sequence: tool.Sequence, resource, task_time: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_task_time_attributes()
|
||||
# TODO: nasty loop goes on when calendar props are messed up
|
||||
ifc.run("sequence.edit_task_time", task_time=task_time, attributes=attributes)
|
||||
@@ -196,59 +206,83 @@ def edit_task_time(ifc, sequence, resource, task_time=None):
|
||||
resource.load_resource_properties()
|
||||
|
||||
|
||||
def assign_predecessor(ifc, sequence, task=None):
|
||||
def assign_predecessor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
predecessor_task = sequence.get_highlighted_task()
|
||||
ifc.run("sequence.assign_sequence", relating_process=task, related_process=predecessor_task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def unassign_predecessor(ifc, sequence, task=None):
|
||||
def unassign_predecessor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
predecessor_task = sequence.get_highlighted_task()
|
||||
ifc.run("sequence.unassign_sequence", relating_process=task, related_process=predecessor_task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def assign_successor(ifc, sequence, task=None):
|
||||
def assign_successor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
successor_task = sequence.get_highlighted_task()
|
||||
ifc.run("sequence.assign_sequence", relating_process=successor_task, related_process=task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def unassign_successor(ifc, sequence, task=None):
|
||||
def unassign_successor(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
successor_task = sequence.get_highlighted_task()
|
||||
ifc.run("sequence.unassign_sequence", relating_process=successor_task, related_process=task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def assign_products(ifc, sequence, spatial, task=None, products=None):
|
||||
def assign_products(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
spatial: tool.Spatial,
|
||||
task: ifcopenshell.entity_instance,
|
||||
products: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
for product in products or spatial.get_selected_products() or []:
|
||||
ifc.run("sequence.assign_product", relating_product=product, related_object=task)
|
||||
outputs = sequence.get_task_outputs(task)
|
||||
sequence.load_task_outputs(outputs)
|
||||
|
||||
|
||||
def unassign_products(ifc, sequence, spatial, task=None, products=None):
|
||||
def unassign_products(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
spatial: tool.Spatial,
|
||||
task: ifcopenshell.entity_instance,
|
||||
products: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
for product in products or spatial.get_selected_products() or []:
|
||||
ifc.run("sequence.unassign_product", relating_product=product, related_object=task)
|
||||
outputs = sequence.get_task_outputs(task)
|
||||
sequence.load_task_outputs(outputs)
|
||||
|
||||
|
||||
def assign_input_products(ifc, sequence, spatial, task=None, products=None):
|
||||
def assign_input_products(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
spatial: tool.Spatial,
|
||||
task: ifcopenshell.entity_instance,
|
||||
products: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
for product in products or spatial.get_selected_products() or []:
|
||||
ifc.run("sequence.assign_process", relating_process=task, related_object=product)
|
||||
inputs = sequence.get_task_inputs(task)
|
||||
sequence.load_task_inputs(inputs)
|
||||
|
||||
|
||||
def unassign_input_products(ifc, sequence, spatial, task=None, products=None):
|
||||
def unassign_input_products(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
spatial: tool.Spatial,
|
||||
task: ifcopenshell.entity_instance,
|
||||
products: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
for product in products or spatial.get_selected_products() or []:
|
||||
ifc.run("sequence.unassign_process", relating_process=task, related_object=product)
|
||||
inputs = sequence.get_task_inputs(task)
|
||||
sequence.load_task_inputs(inputs)
|
||||
|
||||
|
||||
def assign_resource(ifc, sequence, resource_tool, task=None):
|
||||
def assign_resource(ifc: tool.Ifc, sequence: tool.Sequence, resource_tool, task: ifcopenshell.entity_instance) -> None:
|
||||
resource = resource_tool.get_highlighted_resource()
|
||||
sub_resource = ifc.run(
|
||||
"resource.add_resource",
|
||||
@@ -261,59 +295,67 @@ def assign_resource(ifc, sequence, resource_tool, task=None):
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def unassign_resource(ifc, sequence, resource_tool, task=None, resource=None):
|
||||
def unassign_resource(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
resource_tool,
|
||||
task: ifcopenshell.entity_instance,
|
||||
resource: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
ifc.run("sequence.unassign_process", relating_process=task, related_object=resource)
|
||||
ifc.run("resource.remove_resource", resource=resource)
|
||||
sequence.load_task_resources(task)
|
||||
resource_tool.load_resources()
|
||||
|
||||
|
||||
def remove_work_calendar(ifc, work_calendar=None):
|
||||
def remove_work_calendar(ifc: tool.Ifc, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.remove_work_calendar", work_calendar=work_calendar)
|
||||
|
||||
|
||||
def add_work_calendar(ifc):
|
||||
def add_work_calendar(ifc: tool.Ifc) -> ifcopenshell.entity_instance:
|
||||
return ifc.run("sequence.add_work_calendar")
|
||||
|
||||
|
||||
def edit_work_calendar(ifc, sequence, work_calendar=None):
|
||||
def edit_work_calendar(ifc: tool.Ifc, sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_work_calendar_attributes()
|
||||
ifc.run("sequence.edit_work_calendar", work_calendar=work_calendar, attributes=attributes)
|
||||
sequence.disable_editing_work_calendar()
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def enable_editing_work_calendar(sequence, work_calendar=None):
|
||||
def enable_editing_work_calendar(sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
sequence.load_work_calendar_attributes(work_calendar)
|
||||
sequence.enable_editing_work_calendar(work_calendar)
|
||||
|
||||
|
||||
def disable_editing_work_calendar(sequence):
|
||||
def disable_editing_work_calendar(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_work_calendar()
|
||||
|
||||
|
||||
def enable_editing_work_calendar_times(sequence, work_calendar=None):
|
||||
def enable_editing_work_calendar_times(sequence: tool.Sequence, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
sequence.enable_editing_work_calendar_times(work_calendar)
|
||||
|
||||
|
||||
def add_work_time(ifc, work_calendar=None, time_type=None):
|
||||
def add_work_time(
|
||||
ifc: tool.Ifc, work_calendar: ifcopenshell.entity_instance, time_type: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
return ifc.run("sequence.add_work_time", work_calendar=work_calendar, time_type=time_type)
|
||||
|
||||
|
||||
def enable_editing_work_time(sequence, work_time=None):
|
||||
def enable_editing_work_time(sequence: tool.Sequence, work_time: ifcopenshell.entity_instance) -> None:
|
||||
sequence.load_work_time_attributes(work_time)
|
||||
sequence.enable_editing_work_time(work_time)
|
||||
|
||||
|
||||
def disable_editing_work_time(sequence):
|
||||
def disable_editing_work_time(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_work_time()
|
||||
|
||||
|
||||
def remove_work_time(ifc, work_time=None):
|
||||
def remove_work_time(ifc: tool.Ifc, work_time=None) -> None:
|
||||
ifc.run("sequence.remove_work_time", work_time=work_time)
|
||||
|
||||
|
||||
def edit_work_time(ifc, sequence):
|
||||
def edit_work_time(ifc: tool.Ifc, sequence: tool.Sequence) -> None:
|
||||
work_time = sequence.get_active_work_time()
|
||||
ifc.run("sequence.edit_work_time", work_time=work_time, attributes=sequence.get_work_time_attributes())
|
||||
recurrence_pattern = work_time.RecurrencePattern
|
||||
@@ -326,100 +368,118 @@ def edit_work_time(ifc, sequence):
|
||||
sequence.disable_editing_work_time()
|
||||
|
||||
|
||||
def assign_recurrence_pattern(ifc, work_time=None, recurrence_type=None):
|
||||
def assign_recurrence_pattern(
|
||||
ifc: tool.Ifc, work_time: ifcopenshell.entity_instance, recurrence_type: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
return ifc.run("sequence.assign_recurrence_pattern", parent=work_time, recurrence_type=recurrence_type)
|
||||
|
||||
|
||||
def unassign_recurrence_pattern(ifc, recurrence_pattern=None):
|
||||
def unassign_recurrence_pattern(ifc: tool.Ifc, recurrence_pattern: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.unassign_recurrence_pattern", recurrence_pattern=recurrence_pattern)
|
||||
|
||||
|
||||
def add_time_period(ifc, sequence, recurrence_pattern=None):
|
||||
def add_time_period(ifc: tool.Ifc, sequence: tool.Sequence, recurrence_pattern: ifcopenshell.entity_instance) -> None:
|
||||
start_time, end_time = sequence.get_recurrence_pattern_times()
|
||||
ifc.run("sequence.add_time_period", recurrence_pattern=recurrence_pattern, start_time=start_time, end_time=end_time)
|
||||
sequence.reset_time_period()
|
||||
|
||||
|
||||
def remove_time_period(ifc, time_period=None):
|
||||
def remove_time_period(ifc: tool.Ifc, time_period: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.remove_time_period", time_period=time_period)
|
||||
|
||||
|
||||
def enable_editing_task_calendar(sequence, task=None):
|
||||
def enable_editing_task_calendar(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
sequence.enable_editing_task_calendar(task)
|
||||
|
||||
|
||||
def edit_task_calendar(ifc, sequence, task=None, work_calendar=None):
|
||||
def edit_task_calendar(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
task: ifcopenshell.entity_instance,
|
||||
work_calendar: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
ifc.run("control.assign_control", relating_control=work_calendar, related_object=task)
|
||||
ifc.run("sequence.cascade_schedule", task=task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def remove_task_calendar(ifc, sequence, task=None, work_calendar=None):
|
||||
def remove_task_calendar(
|
||||
ifc: tool.Ifc,
|
||||
sequence: tool.Sequence,
|
||||
task: ifcopenshell.entity_instance,
|
||||
work_calendar: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
ifc.run("control.unassign_control", relating_control=work_calendar, related_object=task)
|
||||
ifc.run("sequence.cascade_schedule", task=task)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def enable_editing_task_sequence(sequence):
|
||||
def enable_editing_task_sequence(sequence: tool.Sequence) -> None:
|
||||
sequence.enable_editing_task_sequence()
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def disable_editing_task_time(sequence):
|
||||
def disable_editing_task_time(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_task_time()
|
||||
|
||||
|
||||
def enable_editing_sequence_attributes(sequence, rel_sequence=None):
|
||||
def enable_editing_sequence_attributes(sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
sequence.enable_editing_rel_sequence_attributes(rel_sequence)
|
||||
sequence.load_rel_sequence_attributes(rel_sequence)
|
||||
|
||||
|
||||
def enable_editing_sequence_lag_time(sequence, rel_sequence=None, lag_time=None):
|
||||
def enable_editing_sequence_lag_time(
|
||||
sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance, lag_time: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
sequence.load_lag_time_attributes(lag_time)
|
||||
sequence.enable_editing_sequence_lag_time(rel_sequence)
|
||||
|
||||
|
||||
def unassign_lag_time(ifc, sequence, rel_sequence=None):
|
||||
def unassign_lag_time(ifc: tool.Ifc, sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.unassign_lag_time", rel_sequence=rel_sequence)
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def assign_lag_time(ifc, rel_sequence=None):
|
||||
def assign_lag_time(ifc: tool.Ifc, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.assign_lag_time", rel_sequence=rel_sequence, lag_value="P1D")
|
||||
|
||||
|
||||
def edit_sequence_attributes(ifc, sequence, rel_sequence=None):
|
||||
def edit_sequence_attributes(
|
||||
ifc: tool.Ifc, sequence: tool.Sequence, rel_sequence: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
attributes = sequence.get_rel_sequence_attributes()
|
||||
ifc.run("sequence.edit_sequence", rel_sequence=rel_sequence, attributes=attributes)
|
||||
sequence.disable_editing_rel_sequence()
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def edit_sequence_lag_time(ifc, sequence, lag_time=None):
|
||||
def edit_sequence_lag_time(ifc: tool.Ifc, sequence: tool.Sequence, lag_time: ifcopenshell.entity_instance) -> None:
|
||||
attributes = sequence.get_lag_time_attributes()
|
||||
ifc.run("sequence.edit_lag_time", lag_time=lag_time, attributes=attributes)
|
||||
sequence.disable_editing_rel_sequence()
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def disable_editing_rel_sequence(sequence):
|
||||
def disable_editing_rel_sequence(sequence: tool.Sequence) -> None:
|
||||
sequence.disable_editing_rel_sequence()
|
||||
|
||||
|
||||
def select_task_outputs(sequence, spatial, task=None):
|
||||
def select_task_outputs(sequence: tool.Sequence, spatial: tool.Spatial, task: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(products=sequence.get_task_outputs(task))
|
||||
|
||||
|
||||
def select_task_inputs(sequence, spatial, task=None):
|
||||
def select_task_inputs(sequence: tool.Sequence, spatial: tool.Spatial, task: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(products=sequence.get_task_inputs(task))
|
||||
|
||||
|
||||
def select_work_schedule_products(sequence, spatial, work_schedule=None):
|
||||
def select_work_schedule_products(
|
||||
sequence: tool.Sequence, spatial: tool.Spatial, work_schedule: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
products = sequence.get_work_schedule_products(work_schedule)
|
||||
spatial.select_products(products)
|
||||
|
||||
|
||||
def select_unassigned_work_schedule_products(ifc, sequence, spatial):
|
||||
def select_unassigned_work_schedule_products(ifc: tool.Ifc, sequence: tool.Sequence, spatial: tool.Spatial) -> None:
|
||||
spatial.deselect_objects()
|
||||
products = ifc.get().by_type("IfcElement")
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
@@ -428,11 +488,11 @@ def select_unassigned_work_schedule_products(ifc, sequence, spatial):
|
||||
spatial.select_products(selection)
|
||||
|
||||
|
||||
def recalculate_schedule(ifc, work_schedule=None):
|
||||
def recalculate_schedule(ifc: tool.Ifc, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.recalculate_schedule", work_schedule=work_schedule)
|
||||
|
||||
|
||||
def add_task_column(sequence, column_type=None, name=None, data_type=None):
|
||||
def add_task_column(sequence: tool.Sequence, column_type: str, name: str, data_type: str) -> None:
|
||||
sequence.add_task_column(column_type, name, data_type)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
if work_schedule:
|
||||
@@ -440,11 +500,11 @@ def add_task_column(sequence, column_type=None, name=None, data_type=None):
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def remove_task_column(sequence, name=None):
|
||||
def remove_task_column(sequence: tool.Sequence, name: str) -> None:
|
||||
sequence.remove_task_column(name)
|
||||
|
||||
|
||||
def set_task_sort_column(sequence, column=None):
|
||||
def set_task_sort_column(sequence: tool.Sequence, column: str) -> None:
|
||||
sequence.set_task_sort_column(column)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
if work_schedule:
|
||||
@@ -452,7 +512,7 @@ def set_task_sort_column(sequence, column=None):
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def calculate_task_duration(ifc, sequence, task=None):
|
||||
def calculate_task_duration(ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("sequence.calculate_task_duration", task=task)
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
if work_schedule:
|
||||
@@ -460,11 +520,11 @@ def calculate_task_duration(ifc, sequence, task=None):
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def load_animation_color_scheme(sequence, scheme):
|
||||
def load_animation_color_scheme(sequence: tool.Sequence, scheme: Union[ifcopenshell.entity_instance, None]) -> None:
|
||||
sequence.load_animation_color_scheme(scheme)
|
||||
|
||||
|
||||
def go_to_task(sequence, task=None):
|
||||
def go_to_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> Union[None, str]:
|
||||
work_schedule = sequence.get_work_schedule(task)
|
||||
is_work_schedule_active = sequence.is_work_schedule_active(work_schedule)
|
||||
if is_work_schedule_active:
|
||||
@@ -473,7 +533,7 @@ def go_to_task(sequence, task=None):
|
||||
return "Work schedule is not active"
|
||||
|
||||
|
||||
def highlight_product_related_task(sequence, spatial, product_type=None):
|
||||
def highlight_product_related_task(sequence: tool.Sequence, spatial: tool.Spatial, product_type=None) -> None:
|
||||
products = spatial.get_selected_products()
|
||||
if products:
|
||||
if product_type == "Output":
|
||||
@@ -487,26 +547,26 @@ def highlight_product_related_task(sequence, spatial, product_type=None):
|
||||
sequence.go_to_task(task)
|
||||
|
||||
|
||||
def guess_date_range(sequence, work_schedule=None):
|
||||
def guess_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
start, finish = sequence.guess_date_range(work_schedule)
|
||||
sequence.update_visualisation_date(start, finish)
|
||||
|
||||
|
||||
def setup_default_task_columns(sequence):
|
||||
def setup_default_task_columns(sequence: tool.Sequence) -> None:
|
||||
sequence.setup_default_task_columns()
|
||||
|
||||
|
||||
def add_task_bars(sequence):
|
||||
def add_task_bars(sequence: tool.Sequence) -> None:
|
||||
tasks = sequence.get_animation_bar_tasks()
|
||||
if tasks:
|
||||
sequence.create_bars(tasks)
|
||||
|
||||
|
||||
def load_default_animation_color_scheme(sequence):
|
||||
def load_default_animation_color_scheme(sequence: tool.Sequence) -> None:
|
||||
sequence.load_default_animation_color_scheme()
|
||||
|
||||
|
||||
def visualise_work_schedule_date_range(sequence, work_schedule=None):
|
||||
def visualise_work_schedule_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
sequence.clear_objects_animation(include_blender_objects=False)
|
||||
settings = sequence.get_animation_settings()
|
||||
if settings:
|
||||
@@ -520,7 +580,7 @@ def visualise_work_schedule_date_range(sequence, work_schedule=None):
|
||||
sequence.set_object_shading()
|
||||
|
||||
|
||||
def visualise_work_schedule_date(sequence, work_schedule=None):
|
||||
def visualise_work_schedule_date(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
sequence.clear_objects_animation(include_blender_objects=False)
|
||||
start_date = sequence.get_start_date()
|
||||
product_states = sequence.process_construction_state(work_schedule, start_date)
|
||||
@@ -528,12 +588,12 @@ def visualise_work_schedule_date(sequence, work_schedule=None):
|
||||
sequence.set_object_shading()
|
||||
|
||||
|
||||
def generate_gantt_chart(sequence, work_schedule):
|
||||
def generate_gantt_chart(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
json = sequence.create_tasks_json(work_schedule)
|
||||
sequence.generate_gantt_browser_chart(json, work_schedule)
|
||||
|
||||
|
||||
def load_product_related_tasks(sequence, product=None):
|
||||
def load_product_related_tasks(sequence: tool.Sequence, product: ifcopenshell.entity_instance) -> Union[None, str]:
|
||||
filter_by_schedule = sequence.is_filter_by_active_schedule()
|
||||
if filter_by_schedule:
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
@@ -546,7 +606,9 @@ def load_product_related_tasks(sequence, product=None):
|
||||
sequence.load_product_related_tasks(task_inputs, task_ouputs)
|
||||
|
||||
|
||||
def reorder_task_nesting(ifc, sequence, task, new_index):
|
||||
def reorder_task_nesting(
|
||||
ifc: tool.Ifc, sequence: tool.Sequence, task: ifcopenshell.entity_instance, new_index: int
|
||||
) -> Union[None, str]:
|
||||
is_sorting_enabled = sequence.is_sorting_enabled()
|
||||
is_sort_reversed = sequence.is_sort_reversed()
|
||||
if is_sorting_enabled or is_sort_reversed:
|
||||
@@ -558,17 +620,19 @@ def reorder_task_nesting(ifc, sequence, task, new_index):
|
||||
sequence.load_task_properties()
|
||||
|
||||
|
||||
def create_baseline(ifc, sequence, work_schedule, name):
|
||||
def create_baseline(
|
||||
ifc: tool.Ifc, sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance, name: Optional[str] = None
|
||||
) -> None:
|
||||
ifc.run("sequence.create_baseline", work_schedule=work_schedule, name=name)
|
||||
|
||||
|
||||
def clear_previous_animation(sequence):
|
||||
def clear_previous_animation(sequence: tool.Sequence) -> None:
|
||||
sequence.clear_objects_animation(include_blender_objects=False)
|
||||
|
||||
|
||||
def add_animation_camera(sequence):
|
||||
def add_animation_camera(sequence: tool.Sequence) -> None:
|
||||
sequence.add_animation_camera()
|
||||
|
||||
|
||||
def save_animation_color_scheme(sequence, name):
|
||||
def save_animation_color_scheme(sequence: tool.Sequence, name: str) -> None:
|
||||
sequence.save_animation_color_scheme(name)
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
import blenderbim.core.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def reference_structure(
|
||||
|
||||
@@ -111,8 +111,9 @@ def update_style_textures(ifc, style, obj=None, representation=None):
|
||||
ifc.run("style.remove_surface_style", style=texture_style)
|
||||
|
||||
|
||||
def unlink_style(ifc, style, obj=None):
|
||||
ifc.unlink(obj=obj, element=style.get_style(obj))
|
||||
def unlink_style(ifc, style=None):
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
|
||||
|
||||
def enable_editing_style(style, obj=None):
|
||||
|
||||
@@ -94,7 +94,7 @@ def hide_ports(ifc, system, element=None):
|
||||
def add_port(ifc, system, element=None):
|
||||
system.load_ports(element, system.get_ports(element))
|
||||
obj = system.create_empty_at_cursor_with_element_orientation(element)
|
||||
port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort")
|
||||
port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort", should_add_representation=False)
|
||||
ifc.run("system.assign_port", element=element, port=port)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -485,12 +487,12 @@ class Material:
|
||||
def disable_editing_materials(cls): pass
|
||||
def enable_editing_material(cls, material): pass
|
||||
def enable_editing_materials(cls): pass
|
||||
def get_active_material_type(cls): pass
|
||||
def get_active_material(cls): pass
|
||||
def get_active_material_type(cls): pass
|
||||
def get_active_object_material(cls, obj): pass
|
||||
def get_elements_by_material(cls, material): pass
|
||||
def get_material_attributes(cls): pass
|
||||
def get_material(cls, element, should_inherit): pass
|
||||
def get_material_attributes(cls): pass
|
||||
def get_name(cls, obj): pass
|
||||
def get_type(cls, element): pass
|
||||
def has_material_profile(cls, element): pass
|
||||
@@ -501,6 +503,7 @@ class Material:
|
||||
def is_material_used_in_sets(cls, material): pass
|
||||
def load_material_attributes(cls, material): pass
|
||||
def replace_material_with_material_profile(cls, element): pass
|
||||
def sync_blender_material_name(cls, material): pass
|
||||
|
||||
|
||||
@interface
|
||||
@@ -615,9 +618,20 @@ class Profile:
|
||||
|
||||
@interface
|
||||
class Pset:
|
||||
def add_proposed_property(cls, name, value, props): pass
|
||||
def cast_string_to_primitive(cls, value: str): pass
|
||||
def clear_blender_pset_properties(cls, props): pass
|
||||
def enable_proposed_pset(cls, props, pset_name, pset_type, has_template): pass
|
||||
def get_element_pset(cls, element, pset_name): pass
|
||||
def get_pset_name(cls, obj, obj_type): pass
|
||||
def get_prop_template_primitive_type(cls, prop_template): pass
|
||||
def get_pset_name(cls, obj, obj_type, pset_type): pass
|
||||
def get_pset_template(cls, name): pass
|
||||
def import_enumerated_value_from_template(cls, prop_template, data, props): pass
|
||||
def import_pset_from_existing(cls, pset, props): pass
|
||||
def import_pset_from_template(cls, pset_template, pset, props): pass
|
||||
def import_single_value_from_template(cls, pset_template, prop_template, data, props): pass
|
||||
def is_pset_applicable(cls,element, pset_name): pass
|
||||
def set_active_pset(cls, props, pset, has_template): pass
|
||||
|
||||
|
||||
@interface
|
||||
|
||||
@@ -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:
|
||||
@@ -127,7 +144,9 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
if obj_type == "Object":
|
||||
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
|
||||
elif obj_type == "Material":
|
||||
return bpy.data.materials.get(obj).BIMObjectProperties.ifc_definition_id
|
||||
return context.scene.BIMMaterialProperties.materials[
|
||||
context.scene.BIMMaterialProperties.active_material_index
|
||||
].ifc_definition_id
|
||||
elif obj_type == "MaterialSet":
|
||||
return ifcopenshell.util.element.get_material(
|
||||
tool.Ifc.get_entity(bpy.data.objects.get(obj)), should_skip_usage=True
|
||||
@@ -451,15 +470,41 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
active_object.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def enum_property_has_valid_index(cls, props, prop_name: str, enum_items: tuple) -> bool:
|
||||
def get_enum_safe(cls, props: bpy.types.PropertyGroup, prop_name: str) -> Union[str, None]:
|
||||
"""method created for readibility and to avoid console warnings like
|
||||
`pyrna_enum_to_py: current value '17' matches no enum in 'BIMModelProperties', '', 'relating_type_id'`
|
||||
"""
|
||||
current_value_index = props.get(prop_name, None)
|
||||
# assuming the default value is fine
|
||||
if current_value_index is None:
|
||||
return True
|
||||
return current_value_index < len(enum_items)
|
||||
# Yes, accessing items through annotations is a bit hacky
|
||||
# but it's the only way to get the dynamic enum items
|
||||
# besides providing them to get_enum_safe explicitly.
|
||||
prop_keywords = props.__annotations__[prop_name].keywords
|
||||
items = prop_keywords.get("items")
|
||||
if items is None:
|
||||
return None
|
||||
if not isinstance(items, (list, tuple)):
|
||||
# items are retrieved through a callback, not a static list / tuple :
|
||||
items = items(props, bpy.context)
|
||||
|
||||
items_amount = len(items)
|
||||
# If enum has no items it seems to always produce a warning.
|
||||
# E.g. if you try to get it's value directly: `BIMModelProperties.relating_type_id`.
|
||||
if items_amount == 0:
|
||||
return None
|
||||
|
||||
index = props.get(prop_name)
|
||||
# If value was never changed (still default), we can just retrieve it from the enum.
|
||||
if index is None:
|
||||
default_value = prop_keywords.get("default", 0)
|
||||
if isinstance(default_value, int):
|
||||
index = default_value
|
||||
else:
|
||||
# If default value is a string then it's a static enum
|
||||
# and we can just return it.
|
||||
return default_value
|
||||
# Ensure index is valid.
|
||||
if items_amount > index >= 0:
|
||||
return items[index][0]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def append_data_block(cls, filepath: str, data_block_type: str, name: str, link=False, relative=False) -> dict:
|
||||
@@ -845,9 +890,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
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.brick
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
@@ -604,4 +605,4 @@ class BrickStore:
|
||||
def set_last_saved(cls):
|
||||
save = os.path.getmtime(BrickStore.path)
|
||||
save = datetime.datetime.fromtimestamp(save)
|
||||
BrickStore.last_saved = f"{save.year}-{save.month}-{save.day} {save.hour}:{save.minute}"
|
||||
BrickStore.last_saved = f"{save.year}-{save.month}-{save.day} {save.hour}:{save.minute}"
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import os
|
||||
import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.bim.helper
|
||||
import json
|
||||
from typing import Optional, Any, Generator, Union
|
||||
|
||||
|
||||
class Cost(blenderbim.core.tool.Cost):
|
||||
@@ -16,9 +19,59 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
|
||||
@classmethod
|
||||
def disable_editing_cost_schedule(cls):
|
||||
cls.store_active_schedule_columns()
|
||||
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = 0
|
||||
cls.disable_editing_cost_item()
|
||||
|
||||
@classmethod
|
||||
def load_active_schedule_columns(cls) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
active_columns = props.columns
|
||||
storage = props.columns_storage
|
||||
active_cost_schedule_id = cls.get_active_cost_schedule().id()
|
||||
|
||||
# store column names to keep the original order
|
||||
cols_to_add = []
|
||||
|
||||
# collection property only support removal by index
|
||||
for storage_col_i, storage_col in reversed(list(enumerate(storage[:]))):
|
||||
if storage_col.schedule_id != active_cost_schedule_id:
|
||||
continue
|
||||
cols_to_add.insert(0, storage_col.name)
|
||||
# We don't store active schedule columns in storage
|
||||
# so it will be easy to edit them.
|
||||
storage.remove(storage_col_i)
|
||||
|
||||
for col_name in cols_to_add:
|
||||
col = active_columns.add()
|
||||
col.name = col_name
|
||||
|
||||
@classmethod
|
||||
def store_active_schedule_columns(cls) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
active_columns = props.columns
|
||||
storage = props.columns_storage
|
||||
active_cost_schedule_id = cls.get_active_cost_schedule().id()
|
||||
|
||||
for col in active_columns:
|
||||
storage_col = storage.add()
|
||||
storage_col.name = col.name
|
||||
storage_col.schedule_id = active_cost_schedule_id
|
||||
|
||||
props.columns.clear()
|
||||
|
||||
@classmethod
|
||||
def remove_stored_schedule_columns(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
storage = props.columns_storage
|
||||
active_cost_schedule_id = cost_schedule.id()
|
||||
|
||||
# collection property only support removal by index
|
||||
for storage_col_i, storage_col in reversed(list(enumerate(storage[:]))):
|
||||
if storage_col.schedule_id != active_cost_schedule_id:
|
||||
continue
|
||||
storage.remove(storage_col_i)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_schedule_attributes(cls, cost_schedule):
|
||||
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id()
|
||||
@@ -36,7 +89,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
blenderbim.bim.helper.import_attributes2(cost_schedule, props.cost_schedule_attributes, callback=special_import)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_items(cls, cost_schedule):
|
||||
def enable_editing_cost_items(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.active_cost_schedule_id = cost_schedule.id()
|
||||
props.is_editing = "COST_ITEMS"
|
||||
@@ -147,7 +200,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return blenderbim.bim.helper.export_attributes(props.cost_item_attributes)
|
||||
|
||||
@classmethod
|
||||
def get_active_cost_item(cls):
|
||||
def get_active_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
if not props.active_cost_item_id:
|
||||
return None
|
||||
@@ -163,7 +216,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_types(cls, cost_item=None):
|
||||
def load_cost_item_types(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
|
||||
if not cost_item:
|
||||
cost_item = cls.get_highlighted_cost_item()
|
||||
if not cost_item:
|
||||
@@ -186,7 +239,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
new.name = related_object.Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_quantity_assignments(cls, cost_item, related_object_type):
|
||||
def load_cost_item_quantity_assignments(cls, cost_item: ifcopenshell.entity_instance, related_object_type):
|
||||
def create_list_items(collection, cost_item, is_deep):
|
||||
products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False)
|
||||
for product in products:
|
||||
@@ -215,12 +268,16 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
create_list_items(props.cost_item_resources, cost_item, is_deep)
|
||||
|
||||
@classmethod
|
||||
def calculate_parametric_quantity(cls, cost_item=None, product=None):
|
||||
def calculate_parametric_quantity(
|
||||
cls, cost_item: ifcopenshell.entity_instance, product: ifcopenshell.entity_instance
|
||||
) -> tuple[float, Union[str, None]]:
|
||||
quantities, unit = cls.get_assigned_quantities(cost_item, product)
|
||||
return sum(quantity[3] for quantity in quantities), unit
|
||||
|
||||
@classmethod
|
||||
def get_assigned_quantities(cls, cost_item, product):
|
||||
def get_assigned_quantities(
|
||||
cls, cost_item: ifcopenshell.entity_instance, product: ifcopenshell.entity_instance
|
||||
) -> tuple[list[ifcopenshell.entity_instance], Union[str, None]]:
|
||||
selected_quantitites = []
|
||||
unit = ""
|
||||
for quantities in ifcopenshell.util.element.get_psets(product, qtos_only=True).values():
|
||||
@@ -259,7 +316,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
blenderbim.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_item_values(cls, cost_item=None):
|
||||
def enable_editing_cost_item_values(cls, cost_item: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.active_cost_item_id = cost_item.id()
|
||||
props.cost_item_editing_type = "VALUES"
|
||||
@@ -287,7 +344,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_value_attributes(cls, cost_value=None):
|
||||
def load_cost_item_value_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
def import_attributes(name, prop, data, cost_value, is_rates, props_collection):
|
||||
if name == "AppliedValue":
|
||||
# TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure)
|
||||
@@ -334,36 +391,38 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
blenderbim.bim.helper.import_attributes2(cost_value, props.cost_value_attributes, callback=callback)
|
||||
|
||||
@classmethod
|
||||
def calculate_applied_value(cls, cost_item, cost_value):
|
||||
def calculate_applied_value(
|
||||
cls, cost_item: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance
|
||||
) -> float:
|
||||
return ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value)
|
||||
|
||||
@classmethod
|
||||
def is_active_schedule_of_rates(cls):
|
||||
def is_active_schedule_of_rates(cls) -> bool:
|
||||
return (
|
||||
tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id).PredefinedType
|
||||
== "SCHEDULEOFRATES"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_item_value(cls, cost_value=None):
|
||||
def enable_editing_cost_item_value(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.active_cost_value_id = cost_value.id()
|
||||
props.cost_value_editing_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_cost_item_value(cls):
|
||||
def disable_editing_cost_item_value(cls) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.active_cost_value_id = 0
|
||||
props.cost_value_editing_type = ""
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_value_formula_attributes(cls, cost_value=None):
|
||||
def load_cost_item_value_formula_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.cost_value_attributes.clear()
|
||||
bpy.context.scene.BIMCostProperties.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_item_value_formula(cls, cost_value=None):
|
||||
def enable_editing_cost_item_value_formula(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.active_cost_value_id = cost_value.id()
|
||||
props.cost_value_editing_type = "FORMULA"
|
||||
@@ -373,7 +432,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return bpy.context.scene.BIMCostProperties.cost_value_formula
|
||||
|
||||
@classmethod
|
||||
def get_cost_value_attributes(cls):
|
||||
def get_cost_value_attributes(cls) -> dict[str, Any]:
|
||||
def export_attributes(attributes, prop):
|
||||
if prop.name == "UnitBasisValue":
|
||||
if prop.is_null:
|
||||
@@ -392,13 +451,18 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return blenderbim.bim.helper.export_attributes(props.cost_value_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def get_cost_value_unit_component(cls):
|
||||
def get_cost_value_unit_component(cls) -> ifcopenshell.entity_instance:
|
||||
return tool.Ifc.get().by_id(
|
||||
int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_cost_item_assignments(cls, cost_item, filter_by_type=None, is_deep=False):
|
||||
def get_cost_item_assignments(
|
||||
cls,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
filter_by_type: Optional[ifcopenshell.util.cost.FILTER_BY_TYPE] = None,
|
||||
is_deep: bool = False,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.cost.get_cost_item_assignments(
|
||||
cost_item, filter_by_type=filter_by_type, is_deep=is_deep
|
||||
)
|
||||
@@ -408,30 +472,40 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return bpy.context.scene.BIMCostProperties.show_nested_elements
|
||||
|
||||
@classmethod
|
||||
def get_cost_item_products(cls, cost_item, is_deep=False):
|
||||
def get_cost_item_products(
|
||||
cls, cost_item: ifcopenshell.entity_instance, is_deep: bool = False
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
return cls.get_cost_item_assignments(cost_item, filter_by_type="PRODUCT", is_deep=is_deep)
|
||||
|
||||
@classmethod
|
||||
def get_cost_item_resources(cls, cost_item, is_deep=False):
|
||||
def get_cost_item_resources(
|
||||
cls, cost_item: ifcopenshell.entity_instance, is_deep: bool = False
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
return cls.get_cost_item_assignments(cost_item, filter_by_type="RESOURCE", is_deep=is_deep)
|
||||
|
||||
@classmethod
|
||||
def get_cost_item_processes(cls, cost_item, is_deep=False):
|
||||
def get_cost_item_processes(
|
||||
cls, cost_item: ifcopenshell.entity_instance, is_deep: bool = False
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
return cls.get_cost_item_assignments(cost_item, filter_by_type="PROCESS", is_deep=is_deep)
|
||||
|
||||
@classmethod
|
||||
def get_schedule_cost_items(cls, cost_schedule):
|
||||
def get_schedule_cost_items(
|
||||
cls, cost_schedule: ifcopenshell.entity_instance
|
||||
) -> Generator[ifcopenshell.entity_instance, None, None]:
|
||||
return ifcopenshell.util.cost.get_schedule_cost_items(cost_schedule)
|
||||
|
||||
@classmethod
|
||||
def get_cost_schedule_products(cls, cost_schedule):
|
||||
def get_cost_schedule_products(
|
||||
cls, cost_schedule: ifcopenshell.entity_instance
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
products = []
|
||||
for cost_item in ifcopenshell.util.cost.get_schedule_cost_items(cost_schedule):
|
||||
products.extend(cls.get_cost_item_products(cost_item))
|
||||
return products
|
||||
|
||||
@classmethod
|
||||
def import_cost_schedule_csv(cls, file_path=None, is_schedule_of_rates=False):
|
||||
def import_cost_schedule_csv(cls, file_path: Optional[str] = None, is_schedule_of_rates: bool = False) -> None:
|
||||
if not file_path:
|
||||
return
|
||||
from ifc5d.csv2ifc import Csv2Ifc
|
||||
@@ -446,13 +520,13 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||
|
||||
@classmethod
|
||||
def add_cost_column(cls, name):
|
||||
def add_cost_column(cls, name: str) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
new = props.columns.add()
|
||||
new.name = name
|
||||
|
||||
@classmethod
|
||||
def remove_cost_column(cls, name):
|
||||
def remove_cost_column(cls, name: str) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.columns.remove(props.columns.find(name))
|
||||
|
||||
@@ -473,7 +547,9 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
|
||||
|
||||
@classmethod
|
||||
def create_new_cost_item_li(cls, props_collection, cost_item, level_index, type="cost_rate"):
|
||||
def create_new_cost_item_li(
|
||||
cls, props_collection, cost_item: ifcopenshell.entity_instance, level_index: int, type: str = "cost_rate"
|
||||
) -> None:
|
||||
new = props_collection.add()
|
||||
new.ifc_definition_id = cost_item.id()
|
||||
new.name = cost_item.Name or "Unnamed"
|
||||
@@ -508,7 +584,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
props.is_cost_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def export_cost_schedules(cls, filepath, format=None, cost_schedule=None):
|
||||
def export_cost_schedules(cls, filepath: str, format: str, cost_schedule=None):
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
@@ -621,7 +697,7 @@ class Cost(blenderbim.core.tool.Cost):
|
||||
return bool(cost_items)
|
||||
|
||||
@classmethod
|
||||
def load_product_cost_items(cls, product):
|
||||
def load_product_cost_items(cls, product: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMCostProperties
|
||||
props.is_cost_update_enabled = False
|
||||
props.product_cost_items.clear()
|
||||
|
||||
@@ -51,11 +51,14 @@ from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT
|
||||
from lxml import etree
|
||||
from mathutils import Vector, Matrix
|
||||
from fractions import Fraction
|
||||
from typing import Optional, Union, Iterable, Any
|
||||
from typing import Optional, Union, Iterable, Any, Literal
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class Drawing(blenderbim.core.tool.Drawing):
|
||||
ANNOTATION_DATA_TYPE = Literal["empty", "curve", "mesh"]
|
||||
DOCUMENT_TYPE = Literal["SCHEDULE", "REFERENCE"]
|
||||
|
||||
@classmethod
|
||||
def canonicalise_class_name(cls, name):
|
||||
return re.sub("[^0-9a-zA-Z]+", "", name)
|
||||
@@ -68,11 +71,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_annotation_data_type(cls, object_type):
|
||||
def get_annotation_data_type(cls, object_type: str) -> ANNOTATION_DATA_TYPE:
|
||||
return ANNOTATION_TYPES_DATA[object_type][3]
|
||||
|
||||
@classmethod
|
||||
def create_annotation_object(cls, drawing, object_type):
|
||||
def create_annotation_object(cls, drawing: ifcopenshell.entity_instance, object_type: str) -> bpy.types.Object:
|
||||
data_type = cls.get_annotation_data_type(object_type)
|
||||
obj = annotation.Annotator.get_annotation_obj(drawing, object_type, data_type)
|
||||
if object_type == "FILL_AREA":
|
||||
@@ -298,15 +301,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_text(cls, obj):
|
||||
def disable_editing_text(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMTextProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_assigned_product(cls, obj):
|
||||
def disable_editing_assigned_product(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMAssignedProductProperties.is_editing_product = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
def enable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
@@ -330,11 +333,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_text(cls, obj):
|
||||
def enable_editing_text(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMTextProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_assigned_product(cls, obj):
|
||||
def enable_editing_assigned_product(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMAssignedProductProperties.is_editing_product = True
|
||||
|
||||
@classmethod
|
||||
@@ -428,7 +431,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return location
|
||||
|
||||
@classmethod
|
||||
def get_path_filename(cls, path):
|
||||
def get_path_filename(cls, path: str) -> str:
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
@classmethod
|
||||
@@ -483,7 +486,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, element):
|
||||
def get_name(cls, element: ifcopenshell.entity_instance) -> Union[str, None]:
|
||||
return element.Name
|
||||
|
||||
@classmethod
|
||||
@@ -563,7 +566,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
ifcopenshell.util.element.remove_deep2(ifc_file, literal)
|
||||
|
||||
@classmethod
|
||||
def synchronise_ifc_and_text_attributes(cls, obj):
|
||||
def synchronise_ifc_and_text_attributes(cls, obj: bpy.types.Object) -> None:
|
||||
literals = cls.get_text_literal(obj, return_list=True)
|
||||
literals_attributes = cls.export_text_literal_attributes(obj)
|
||||
defined_ifc_ids = [l.ifc_definition_id for l in obj.BIMTextProperties.literals]
|
||||
@@ -784,7 +787,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
new.ifc_definition_id = drawing.id() # Last, to prevent unnecessary prop callbacks
|
||||
|
||||
@classmethod
|
||||
def import_documents(cls, document_type):
|
||||
def import_documents(cls, document_type: DOCUMENT_TYPE) -> None:
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
if document_type == "SCHEDULE":
|
||||
documents_collection = dprops.schedules
|
||||
@@ -845,7 +848,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return next(s for s in props.sheets[: props.active_sheet_index + 1][::-1] if s.is_sheet)
|
||||
|
||||
@classmethod
|
||||
def import_text_attributes(cls, obj):
|
||||
def import_text_attributes(cls, obj: bpy.types.Object) -> None:
|
||||
props = obj.BIMTextProperties
|
||||
props.literals.clear()
|
||||
|
||||
@@ -863,7 +866,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
props.font_size = str(text_data["FontSize"])
|
||||
|
||||
@classmethod
|
||||
def import_assigned_product(cls, obj):
|
||||
def import_assigned_product(cls, obj: bpy.types.Object) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
product = cls.get_assigned_product(element)
|
||||
if product:
|
||||
@@ -934,38 +937,16 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = True
|
||||
|
||||
@classmethod
|
||||
def update_text_value(cls, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
objs = [obj]
|
||||
for occurrence in ifcopenshell.util.element.get_types(element):
|
||||
obj = tool.Ifc.get_object(occurrence)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
else:
|
||||
objs = []
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type and element_type.RepresentationMaps:
|
||||
obj = tool.Ifc.get_object(element_type)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
for occurrence in ifcopenshell.util.element.get_types(element_type):
|
||||
obj = tool.Ifc.get_object(occurrence)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
else:
|
||||
objs = [obj]
|
||||
|
||||
for obj in objs:
|
||||
props = obj.BIMTextProperties
|
||||
literals = cls.get_text_literal(obj, return_list=True)
|
||||
cls.import_text_attributes(obj)
|
||||
for i, literal in enumerate(literals):
|
||||
product = cls.get_assigned_product(tool.Ifc.get_entity(obj)) or tool.Ifc.get_entity(obj)
|
||||
props.literals[i].value = cls.replace_text_literal_variables(literal.Literal, product)
|
||||
def update_text_value(cls, obj: bpy.types.Object) -> None:
|
||||
props = obj.BIMTextProperties
|
||||
literals = cls.get_text_literal(obj, return_list=True)
|
||||
cls.import_text_attributes(obj)
|
||||
for i, literal in enumerate(literals):
|
||||
product = cls.get_assigned_product(tool.Ifc.get_entity(obj)) or tool.Ifc.get_entity(obj)
|
||||
props.literals[i].value = cls.replace_text_literal_variables(literal.Literal, product)
|
||||
|
||||
@classmethod
|
||||
def update_text_size_pset(cls, obj):
|
||||
def update_text_size_pset(cls, obj: bpy.types.Object) -> None:
|
||||
"""updates pset `EPset_Annotation.Classes` value
|
||||
based on current font size from `obj.BIMTextProperties.font_size`
|
||||
"""
|
||||
@@ -1544,7 +1525,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
@classmethod
|
||||
def get_document_references(cls, document):
|
||||
def get_document_references(cls, document: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return document.DocumentReferences or []
|
||||
return document.HasDocumentReferences or []
|
||||
@@ -1575,7 +1556,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return reference.Description
|
||||
|
||||
@classmethod
|
||||
def generate_reference_attributes(cls, reference: ifcopenshell.entity_instance, **attributes: Any) -> dict[str, Any]:
|
||||
def generate_reference_attributes(
|
||||
cls, reference: ifcopenshell.entity_instance, **attributes: Any
|
||||
) -> dict[str, Any]:
|
||||
"""will automatically convert attributes below for IFC2X3 compatibility:
|
||||
|
||||
- Identification -> ItemReference
|
||||
@@ -1657,23 +1640,22 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
base_elements = set(ifc_file.by_type("IfcElement") + ifc_file.by_type("IfcSpatialElement"))
|
||||
elements = {e for e in (elements & base_elements) if e.is_a() != "IfcSpace"}
|
||||
|
||||
|
||||
updated_set = set()
|
||||
|
||||
for i in elements:
|
||||
# exclude annotations to avoid including annotations from other drawings
|
||||
if not i.is_a("IfcAnnotation"):
|
||||
if not i.is_a("IfcAnnotation"):
|
||||
updated_set.add(i)
|
||||
#add aggregate too, if element is host by one
|
||||
# add aggregate too, if element is host by one
|
||||
if i.Decomposes:
|
||||
aggregate = i.Decomposes[0].RelatingObject
|
||||
#remove IfcProject for class iterator. See https://github.com/IfcOpenShell/IfcOpenShell/issues/4361#issuecomment-2081223615
|
||||
if not aggregate.is_a("IfcProject"):
|
||||
# remove IfcProject for class iterator. See https://github.com/IfcOpenShell/IfcOpenShell/issues/4361#issuecomment-2081223615
|
||||
if not aggregate.is_a("IfcProject"):
|
||||
updated_set.add(aggregate)
|
||||
|
||||
# After the iteration is complete, update elements with updated set
|
||||
# After the iteration is complete, update elements with updated set
|
||||
elements.update(updated_set)
|
||||
|
||||
|
||||
# add annotations from the current drawing
|
||||
annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing))
|
||||
elements.update(annotations)
|
||||
@@ -1716,7 +1698,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return reference.ReferencedDocument
|
||||
|
||||
@classmethod
|
||||
def select_assigned_product(cls, context):
|
||||
def select_assigned_product(cls, context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
product = cls.get_assigned_product(element)
|
||||
@@ -1735,7 +1717,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return True if (camera and camera.data.type == "ORTHO") else False
|
||||
|
||||
@classmethod
|
||||
def is_active_drawing(cls, drawing):
|
||||
def is_active_drawing(cls, drawing: ifcopenshell.entity_instance) -> bool:
|
||||
return drawing.id() == bpy.context.scene.DocProperties.active_drawing_id
|
||||
|
||||
@classmethod
|
||||
@@ -1743,21 +1725,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 +1743,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)
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ import logging
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.system
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.drawing
|
||||
@@ -87,7 +90,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
bpy.data.meshes.remove(data)
|
||||
|
||||
@classmethod
|
||||
def delete_ifc_object(cls, obj):
|
||||
def delete_ifc_object(cls, obj: bpy.types.Object) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -318,7 +321,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return new_mesh
|
||||
|
||||
@classmethod
|
||||
def get_active_representation(cls, obj):
|
||||
def get_active_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""< IfcShapeRepresentation or None"""
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
@@ -458,7 +461,9 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return f"{representation.ContextOfItems.id()}/{representation.id()}"
|
||||
|
||||
@classmethod
|
||||
def get_styles(cls, obj, only_assigned_to_faces=False):
|
||||
def get_styles(
|
||||
cls, obj: bpy.types.Object, only_assigned_to_faces: bool = False
|
||||
) -> list[Union[ifcopenshell.entity_instance, None]]:
|
||||
styles = [tool.Style.get_style(s.material) for s in obj.material_slots if s.material]
|
||||
if not only_assigned_to_faces:
|
||||
return styles
|
||||
@@ -466,8 +471,15 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
usage_count = [0] * len(obj.material_slots)
|
||||
if not usage_count: # if there are no materials, polygons will still use index 0
|
||||
return []
|
||||
|
||||
for poly in obj.data.polygons:
|
||||
usage_count[poly.material_index] += 1
|
||||
|
||||
# remove usages for empty material slots
|
||||
for i, slot in reversed(list(enumerate(obj.material_slots))):
|
||||
if not slot.material:
|
||||
del usage_count[i]
|
||||
|
||||
styles = [style for style, usage in zip(styles, usage_count, strict=True) if usage > 0]
|
||||
return styles
|
||||
|
||||
@@ -559,11 +571,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
new.value = element[i]
|
||||
|
||||
@classmethod
|
||||
def is_body_representation(cls, representation):
|
||||
def is_body_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.ContextOfItems.ContextIdentifier == "Body"
|
||||
|
||||
@classmethod
|
||||
def is_box_representation(cls, representation):
|
||||
def is_box_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.ContextOfItems.ContextIdentifier == "Box"
|
||||
|
||||
@classmethod
|
||||
@@ -571,11 +583,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return not all([tool.Cad.is_x(o, 1.0) for o in obj.scale]) or obj in IfcStore.edited_objs
|
||||
|
||||
@classmethod
|
||||
def is_mapped_representation(cls, representation):
|
||||
def is_mapped_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
return representation.RepresentationType == "MappedRepresentation"
|
||||
|
||||
@classmethod
|
||||
def is_meshlike(cls, representation):
|
||||
def is_meshlike(cls, representation: ifcopenshell.entity_instance) -> bool:
|
||||
if ifcopenshell.util.representation.resolve_representation(representation).RepresentationType in (
|
||||
"AdvancedBrep",
|
||||
"Annotation2D",
|
||||
@@ -655,7 +667,9 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def resolve_mapped_representation(cls, representation):
|
||||
def resolve_mapped_representation(
|
||||
cls, representation: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if representation.RepresentationType == "MappedRepresentation":
|
||||
return cls.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
|
||||
return representation
|
||||
|
||||
@@ -38,12 +38,6 @@ class Material(blenderbim.core.tool.Material):
|
||||
def disable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def duplicate_material(cls, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
new_material = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), material)
|
||||
new_material.Name = material.Name + "_copy"
|
||||
return new_material
|
||||
|
||||
@classmethod
|
||||
def enable_editing_materials(cls):
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
@@ -242,3 +236,25 @@ class Material(blenderbim.core.tool.Material):
|
||||
meshes_to_objects[mesh] = obj
|
||||
for obj in meshes_to_objects.values():
|
||||
tool.Geometry.reload_representation(obj)
|
||||
|
||||
@classmethod
|
||||
def sync_blender_material_name(cls, material):
|
||||
name = material.Name or "Unnamed"
|
||||
obj = tool.Ifc.get_object(material)
|
||||
if obj:
|
||||
obj.name = name
|
||||
style = tool.Style.get_style(obj)
|
||||
if style:
|
||||
style.Name = name
|
||||
obj = tool.Ifc.get_object(style)
|
||||
if obj:
|
||||
obj.name = name
|
||||
|
||||
@classmethod
|
||||
def get_style(cls, material):
|
||||
for material_representation in material.HasRepresentation:
|
||||
for representation in material_representation.Representations:
|
||||
for item in representation.Items:
|
||||
for style in item.Styles:
|
||||
if style.is_a("IfcSurfaceStyle"):
|
||||
return style
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user