mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 12:36:00 +00:00
Compare commits
126 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 | |||
| 1110f0cffb | |||
| 66e829a8aa | |||
| 04172a87cb |
@@ -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)
|
||||
|
||||
+6
-29
@@ -277,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
|
||||
@@ -366,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,15 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import bpy
|
||||
|
||||
# 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
|
||||
@@ -78,7 +86,7 @@ def format_debug_info(info: dict):
|
||||
return text.strip()
|
||||
|
||||
|
||||
if sys.modules.get("bpy", None):
|
||||
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"))
|
||||
@@ -120,11 +128,16 @@ if sys.modules.get("bpy", None):
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text="BlenderBIM could not load.", icon="ERROR")
|
||||
layout.label(text="View the console for full logs.", icon="CONSOLE")
|
||||
box = layout.box()
|
||||
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")
|
||||
@@ -133,6 +146,21 @@ if sys.modules.get("bpy", None):
|
||||
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"
|
||||
@@ -149,14 +177,7 @@ if sys.modules.get("bpy", None):
|
||||
|
||||
def execute(self, context):
|
||||
info = format_debug_info(get_debug_info())
|
||||
|
||||
if platform.system() == "Windows":
|
||||
command = "echo | set /p nul=" + info
|
||||
elif platform.system() == "Darwin": # for MacOS
|
||||
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
|
||||
else: # Linux
|
||||
command = 'printf "' + info.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
context.window_manager.clipboard = info
|
||||
return {"FINISHED"}
|
||||
|
||||
class HiddenPanel:
|
||||
|
||||
@@ -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'; }
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
@@ -714,7 +719,7 @@ class IfcImporter:
|
||||
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:
|
||||
@@ -731,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,
|
||||
@@ -750,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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -144,7 +144,7 @@ class BIM_PT_linked_aggregate(Panel):
|
||||
row.label(text="Not a Linked Aggregate")
|
||||
else:
|
||||
row.label(text=f"{Number_Linked_Aggregates} Linked Aggregates")
|
||||
op = row.operator("bim.object_duplicate_move_linked_aggregate", text="", icon="DUPLICATE")
|
||||
op = row.operator("bim.duplicate_linked_aggregate_to_3d_cursor", text="", icon="DUPLICATE")
|
||||
if type(Number_Linked_Aggregates) is int:
|
||||
if Number_Linked_Aggregates > 0:
|
||||
op = row.operator("bim.select_linked_aggregates", text="", icon="OUTLINER_DATA_POINTCLOUD")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,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()
|
||||
|
||||
@@ -85,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"}
|
||||
@@ -102,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)
|
||||
|
||||
@@ -126,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
|
||||
|
||||
@@ -40,7 +40,7 @@ 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):
|
||||
info = get_debug_info()
|
||||
@@ -56,17 +56,13 @@ class CopyDebugInformation(bpy.types.Operator):
|
||||
|
||||
text = format_debug_info(info)
|
||||
|
||||
text_with_backticks = f"```\n{text}\n```"
|
||||
|
||||
print("-" * 80)
|
||||
print(text)
|
||||
print(text_with_backticks)
|
||||
print("-" * 80)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
command = "echo | set /p nul=" + text
|
||||
elif platform.system() == "Darwin": # for MacOS
|
||||
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | pbcopy'
|
||||
else: # Linux
|
||||
command = 'printf "' + text.replace("\n", "\\n").replace('"', "") + '" | xclip -selection clipboard'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
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?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,6 +29,7 @@ classes = (
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.DuplicateLinkedAggregateTo3dCursor,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
operator.OverrideDuplicateMoveLinked,
|
||||
|
||||
@@ -400,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):
|
||||
[
|
||||
@@ -938,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 = [
|
||||
@@ -973,15 +977,15 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
|
||||
|
||||
|
||||
class DuplicateMoveLinkedAggregateMacro(bpy.types.Macro):
|
||||
bl_description = "Create a new linked aggregate"
|
||||
bl_description = "Create and move a new linked aggregate"
|
||||
bl_idname = "bim.object_duplicate_move_linked_aggregate_macro"
|
||||
bl_label = "IFC Duplicate Linked Aggregate"
|
||||
bl_label = "IFC Duplicate and Move Linked Aggregate"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
|
||||
class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
bl_idname = "bim.object_duplicate_move_linked_aggregate"
|
||||
bl_label = "IFC Duplicate Linked Aggregate"
|
||||
bl_label = "IFC Duplicate and Move Linked Aggregate"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
is_interactive: bpy.props.BoolProperty(name="Is Interactive", default=True)
|
||||
|
||||
@@ -996,7 +1000,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
return DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context)
|
||||
|
||||
@staticmethod
|
||||
def execute_ifc_duplicate_linked_aggregate_operator(self, context):
|
||||
def execute_ifc_duplicate_linked_aggregate_operator(self, context, location_from_3d_cursor=False):
|
||||
self.new_active_obj = None
|
||||
self.group_name = "BBIM_Linked_Aggregate"
|
||||
self.pset_name = "BBIM_Linked_Aggregate"
|
||||
@@ -1050,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):
|
||||
@@ -1109,6 +1113,16 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
]
|
||||
tool.Ifc.run("group.assign_group", group=linked_aggregate_group[0], products=new)
|
||||
|
||||
def get_location_from_3d_cursor(old_to_new, aggregate):
|
||||
base_obj = tool.Ifc.get_object(aggregate)
|
||||
base_obj_location = base_obj.location.copy()
|
||||
|
||||
for new in old_to_new.values():
|
||||
new_obj = tool.Ifc.get_object(new[0])
|
||||
location_diff = new_obj.location - base_obj_location
|
||||
new_obj.location = context.scene.cursor.location + location_diff
|
||||
|
||||
|
||||
if len(context.selected_objects) != 1:
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1135,11 +1149,29 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
|
||||
custom_incremental_naming_for_element_assembly(old_to_new)
|
||||
|
||||
if location_from_3d_cursor:
|
||||
get_location_from_3d_cursor(old_to_new, selected_element)
|
||||
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
return old_to_new
|
||||
|
||||
|
||||
class DuplicateLinkedAggregateTo3dCursor(bpy.types.Operator):
|
||||
bl_idname = "bim.duplicate_linked_aggregate_to_3d_cursor"
|
||||
bl_label = "IFC Duplicate Linked Aggregate to 3d Cursor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_objects) > 0
|
||||
|
||||
def execute(self, context):
|
||||
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=False)
|
||||
|
||||
def _execute(self, context):
|
||||
return DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context, location_from_3d_cursor=True)
|
||||
|
||||
class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
bl_idname = "bim.refresh_linked_aggregate"
|
||||
bl_label = "IFC Refresh Linked Aggregate"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -83,17 +83,8 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props = obj.BIMArrayProperties
|
||||
|
||||
relating_obj = props.relating_array_object
|
||||
|
||||
if relating_obj:
|
||||
element = tool.Ifc.get_entity(relating_obj)
|
||||
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
|
||||
parent_element = tool.Ifc.get().by_guid(parent_globalid)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
|
||||
else:
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props.count = data["count"]
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props.x = data["x"] * si_conversion
|
||||
@@ -102,9 +93,7 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.use_local_space = data.get("use_local_space", False)
|
||||
props.sync_children = data.get("sync_children", False)
|
||||
props.method = data.get("method", "OFFSET")
|
||||
|
||||
props.is_editing = self.item
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -148,10 +137,6 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
#clears the relating_array_object so it doesn't show again next time
|
||||
props.relating_array_object = None
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -208,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
|
||||
|
||||
|
||||
@@ -87,21 +87,6 @@ def update_type_page(self, context):
|
||||
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
|
||||
|
||||
|
||||
def update_relating_array_from_object(self, context):
|
||||
bpy.ops.bim.enable_editing_array(item=self.is_editing)
|
||||
return
|
||||
|
||||
|
||||
def is_object_array_applicable(self, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return False
|
||||
return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMModelProperties(PropertyGroup):
|
||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||
relating_type_id: bpy.props.EnumProperty(
|
||||
@@ -218,14 +203,6 @@ class BIMArrayProperties(PropertyGroup):
|
||||
description="Regenerate all children based on the parent object",
|
||||
default=False,
|
||||
)
|
||||
relating_array_object: bpy.props.PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Array Properties",
|
||||
update=update_relating_array_from_object,
|
||||
poll=is_object_array_applicable,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMStairProperties(PropertyGroup):
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -223,8 +223,6 @@ class BIM_PT_array(bpy.types.Panel):
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "z")
|
||||
row.operator("bim.input_cursor_z_array", icon="CURSOR", text="")
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "relating_array_object", icon="COPYDOWN")
|
||||
else:
|
||||
row = box.row(align=True)
|
||||
name = f"{array['count']} Items ({array.get('method', 'OFFSET').capitalize()})"
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -1106,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"
|
||||
@@ -1224,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()
|
||||
|
||||
|
||||
@@ -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,7 @@ import re
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.selector
|
||||
@@ -214,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)
|
||||
|
||||
@@ -367,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)
|
||||
@@ -634,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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -290,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()
|
||||
@@ -320,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
|
||||
@@ -406,10 +407,14 @@ class BIM_PT_tabs(Panel):
|
||||
|
||||
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")
|
||||
box.label(text="View the console for full logs.", icon="CONSOLE")
|
||||
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"
|
||||
|
||||
@@ -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,11 +471,13 @@ 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, blender, 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)
|
||||
|
||||
@@ -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
|
||||
@@ -47,7 +55,7 @@ def add_representation(
|
||||
data = geometry.get_object_data(obj)
|
||||
|
||||
if not data and ifc_representation_class != "IfcTextLiteral":
|
||||
raise IncompatibleRepresentationError()
|
||||
return
|
||||
|
||||
representation = ifc.run(
|
||||
"geometry.add_representation",
|
||||
@@ -89,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`;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -487,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
|
||||
@@ -503,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
|
||||
@@ -617,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
|
||||
|
||||
@@ -144,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
|
||||
@@ -468,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,8 +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
|
||||
@@ -88,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
|
||||
@@ -319,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)
|
||||
@@ -459,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
|
||||
@@ -467,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
|
||||
|
||||
@@ -560,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
|
||||
@@ -572,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",
|
||||
@@ -656,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
|
||||
|
||||
@@ -97,7 +97,9 @@ class Misc(blenderbim.core.tool.Misc):
|
||||
IfcStore.edited_objs.add(obj)
|
||||
|
||||
@classmethod
|
||||
def split_objects_with_cutter(cls, objs, cutter):
|
||||
def split_objects_with_cutter(
|
||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||
) -> list[bpy.types.Object]:
|
||||
cutter_mesh = cutter.data
|
||||
|
||||
bm = bmesh.new()
|
||||
|
||||
@@ -23,9 +23,11 @@ import collections
|
||||
import collections.abc
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry as geometry
|
||||
@@ -36,8 +38,9 @@ from functools import partial
|
||||
from blenderbim.bim import import_ifc
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from ifcopenshell.util.shape_builder import V, ShapeBuilder
|
||||
from typing import Optional, Union, TypeVar, Any
|
||||
from typing import Optional, Union, TypeVar, Any, Iterable
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -468,7 +471,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
has_deleted_opening = True
|
||||
|
||||
@classmethod
|
||||
def get_material_layer_parameters(cls, element):
|
||||
def get_material_layer_parameters(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
layer_set_direction = "AXIS2"
|
||||
offset = 0.0
|
||||
@@ -551,7 +554,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
data = tool.Ifc.get().createIfcText(json.dumps(data))
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||
else:
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
@classmethod
|
||||
def get_flow_segment_axis(cls, obj):
|
||||
@@ -902,7 +905,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
return Matrix(placement)
|
||||
|
||||
@classmethod
|
||||
def reload_body_representation(cls, obj_or_objects):
|
||||
def reload_body_representation(cls, obj_or_objects: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None:
|
||||
"""Update body representation including all decomposed objects"""
|
||||
if isinstance(obj_or_objects, collections.abc.Iterable):
|
||||
objects = set(obj_or_objects)
|
||||
@@ -1203,3 +1206,49 @@ class Model(blenderbim.core.tool.Model):
|
||||
|
||||
vertices = (v.to_3d().xzy for v in vertices)
|
||||
return (vertices, edges, faces)
|
||||
|
||||
@classmethod
|
||||
def update_simple_openings(cls, element: ifcopenshell.entity_instance) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
fillings = {e: tool.Ifc.get_object(e) for e in 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 all occurrences of the same element type (e.g. a window)
|
||||
# will use openings of the same thickness.
|
||||
# Generator we use by default will create a really thick opening representation
|
||||
# to make sure it will fit for walls with different thickness.
|
||||
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
|
||||
)
|
||||
|
||||
new_representation = FilledOpeningGenerator().generate_opening_from_filling(
|
||||
filling, fillings[filling], voided_obj.dimensions[1]
|
||||
)
|
||||
|
||||
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=list(fillings.values())):
|
||||
bpy.ops.bim.recalculate_fill()
|
||||
|
||||
@@ -19,17 +19,24 @@
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.context
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.core.unit
|
||||
import blenderbim.core.owner
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Project(blenderbim.core.tool.Project):
|
||||
@classmethod
|
||||
def append_all_types_from_template(cls, template):
|
||||
def append_all_types_from_template(cls, template: str) -> None:
|
||||
# TODO refactor
|
||||
filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "templates", "projects", template)
|
||||
bpy.ops.bim.select_library_file(filepath=filepath)
|
||||
@@ -95,13 +102,13 @@ class Project(blenderbim.core.tool.Project):
|
||||
@classmethod
|
||||
def run_root_assign_class(
|
||||
cls,
|
||||
obj=None,
|
||||
ifc_class=None,
|
||||
predefined_type=None,
|
||||
should_add_representation=True,
|
||||
context=None,
|
||||
ifc_representation_class=None,
|
||||
):
|
||||
obj: bpy.types.Object,
|
||||
ifc_class: str,
|
||||
predefined_type: Optional[str] = None,
|
||||
should_add_representation: bool = True,
|
||||
context: Optional[ifcopenshell.entity_instance] = None,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
return blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
|
||||
@@ -23,6 +23,7 @@ import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
from typing import Union
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
|
||||
|
||||
class Pset(blenderbim.core.tool.Pset):
|
||||
@@ -39,7 +40,7 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
if obj_type == "Object":
|
||||
return bpy.data.objects.get(obj).PsetProperties
|
||||
elif obj_type == "Material":
|
||||
return bpy.data.materials.get(obj).PsetProperties
|
||||
return bpy.context.scene.MaterialPsetProperties
|
||||
elif obj_type == "MaterialSet":
|
||||
return bpy.data.objects.get(obj).MaterialSetPsetProperties
|
||||
elif obj_type == "MaterialSetItem":
|
||||
@@ -56,9 +57,12 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
return bpy.context.scene.GroupPsetProperties
|
||||
|
||||
@classmethod
|
||||
def get_pset_name(cls, obj, obj_type):
|
||||
pset = cls.get_pset_props(obj, obj_type)
|
||||
return pset.pset_name
|
||||
def get_pset_name(cls, obj, obj_type, pset_type="PSET"):
|
||||
props = cls.get_pset_props(obj, obj_type)
|
||||
name = props.pset_name if pset_type == "PSET" else props.qto_name
|
||||
if name == "BBIM_CUSTOM":
|
||||
return ""
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def is_pset_applicable(cls, element: ifcopenshell.entity_instance, pset_name: str) -> bool:
|
||||
@@ -82,5 +86,202 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
def enable_pset_editing(cls, pset_id=None, pset_name=None, pset_type=None, obj=None, obj_type=None):
|
||||
# TODO REFACTOR ONCE toll/CORE functions are available
|
||||
bpy.ops.bim.enable_pset_editing(
|
||||
pset_id=0, pset_name=tool.Pset.get_pset_name(obj, obj_type), pset_type="PSET", obj=obj, obj_type=obj_type
|
||||
pset_id=0, pset_name=cls.get_pset_name(obj, obj_type), pset_type="PSET", obj=obj, obj_type=obj_type
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def import_pset_from_existing(cls, pset, props):
|
||||
pset_props = []
|
||||
if pset.is_a("IfcElementQuantity"):
|
||||
pset_props = pset.Quantities
|
||||
elif pset.is_a("IfcPropertySet"):
|
||||
pset_props = pset.HasProperties
|
||||
elif pset.is_a("IfcMaterialProperties") or pset.is_a("IfcProfileProperties"):
|
||||
pset_props = pset.Properties
|
||||
|
||||
for prop in pset_props:
|
||||
if props.properties.get(prop.Name):
|
||||
continue # This property has already been added from a template
|
||||
if prop.is_a("IfcPropertyEnumeratedValue"):
|
||||
simple_prop = 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 = 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)
|
||||
|
||||
@classmethod
|
||||
def get_prop_template_primitive_type(cls, 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(
|
||||
tool.Ifc.schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def import_enumerated_value_from_template(cls, prop_template, data, props):
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
selected_enum_items = data.get(prop_template.Name, []) or []
|
||||
|
||||
prop = 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
|
||||
|
||||
@classmethod
|
||||
def import_single_value_from_template(cls, pset_template, prop_template, data, props):
|
||||
prop = 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 = cls.get_prop_template_primitive_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)
|
||||
|
||||
@classmethod
|
||||
def import_pset_from_template(cls, pset_template, pset, props):
|
||||
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":
|
||||
cls.import_single_value_from_template(pset_template, prop_template, data, props)
|
||||
elif prop_template.TemplateType.startswith("Q_"):
|
||||
cls.import_single_value_from_template(pset_template, prop_template, data, props)
|
||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
cls.import_enumerated_value_from_template(prop_template, data, props)
|
||||
else:
|
||||
# NOTE: currently unsupported types:
|
||||
# - P_BOUNDEDVALUE
|
||||
# - P_LISTVALUE
|
||||
# - P_REFERENCEVALUE
|
||||
# - P_TABLEVALUE
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def clear_blender_pset_properties(cls, props):
|
||||
props.properties.clear()
|
||||
|
||||
@classmethod
|
||||
def set_active_pset(cls, props, pset, has_template):
|
||||
props.active_pset_id = pset.id()
|
||||
props.active_pset_name = pset.Name
|
||||
props.active_pset_has_template = has_template
|
||||
|
||||
@classmethod
|
||||
def enable_proposed_pset(cls, props, pset_name, pset_type, has_template):
|
||||
props.active_pset_id = 0
|
||||
props.active_pset_name = pset_name or "My_Data"
|
||||
props.active_pset_type = pset_type
|
||||
props.active_pset_has_template = has_template
|
||||
|
||||
@classmethod
|
||||
def get_pset_template(cls, name):
|
||||
return blenderbim.bim.schema.ifc.psetqto.get_by_name(name)
|
||||
|
||||
@classmethod
|
||||
def add_proposed_property(cls, name, value, props):
|
||||
if props.properties.get(name):
|
||||
return
|
||||
prop = props.properties.add()
|
||||
prop.name = name
|
||||
metadata = prop.metadata
|
||||
metadata.set_value(value)
|
||||
metadata.name = name
|
||||
metadata.is_null = value is None
|
||||
metadata.is_optional = True
|
||||
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
|
||||
|
||||
@classmethod
|
||||
def cast_string_to_primitive(cls, value: str):
|
||||
value = value.strip()
|
||||
if value.lower() == "true":
|
||||
return True
|
||||
elif value.lower() == "false":
|
||||
return False
|
||||
elif value.lower() == "null" or value == "":
|
||||
return None
|
||||
try:
|
||||
value = int(value)
|
||||
return value
|
||||
except:
|
||||
try:
|
||||
value = float(value)
|
||||
return value
|
||||
except:
|
||||
return value
|
||||
return value
|
||||
|
||||
@@ -33,10 +33,12 @@ import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.resource
|
||||
import blenderbim.bim.schema
|
||||
import ifcopenshell.util.constraint
|
||||
from typing import Any, Union
|
||||
|
||||
|
||||
class Resource(blenderbim.core.tool.Resource):
|
||||
@classmethod
|
||||
def load_resources(cls):
|
||||
def load_resources(cls) -> None:
|
||||
def create_new_resource_li(resource, level_index):
|
||||
new = bpy.context.scene.BIMResourceTreeProperties.resources.add()
|
||||
new.ifc_definition_id = resource.id()
|
||||
@@ -66,42 +68,44 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def load_resource_properties(cls):
|
||||
def load_resource_properties(cls) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
tprops = bpy.context.scene.BIMResourceTreeProperties
|
||||
props.is_resource_update_enabled = False
|
||||
for item in tprops.resources:
|
||||
resource = tool.Ifc.get().by_id(item.ifc_definition_id)
|
||||
item.name = resource.Name if resource.Name else "Unnamed"
|
||||
item.schedule_usage = resource.Usage.ScheduleUsage if (resource.Usage and resource.Usage.ScheduleUsage) else 0
|
||||
item.schedule_usage = (
|
||||
resource.Usage.ScheduleUsage if (resource.Usage and resource.Usage.ScheduleUsage) else 0
|
||||
)
|
||||
props.is_resource_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def disable_editing_resource(cls):
|
||||
def disable_editing_resource(cls) -> None:
|
||||
bpy.context.scene.BIMResourceProperties.active_resource_id = 0
|
||||
bpy.context.scene.BIMResourceProperties.active_resource_time_id = 0
|
||||
|
||||
@classmethod
|
||||
def disable_resource_editing_ui(cls):
|
||||
def disable_resource_editing_ui(cls) -> None:
|
||||
bpy.context.scene.BIMResourceProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def load_resource_attributes(cls, resource):
|
||||
def load_resource_attributes(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
blenderbim.bim.helper.import_attributes2(resource, bpy.context.scene.BIMResourceProperties.resource_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource(cls, resource):
|
||||
def enable_editing_resource(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_resource_id = resource.id()
|
||||
props.resource_attributes.clear()
|
||||
props.editing_resource_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def get_resource_attributes(cls):
|
||||
def get_resource_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMResourceProperties.resource_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_time(cls, resource):
|
||||
def enable_editing_resource_time(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.resource_time_attributes.clear()
|
||||
props.active_resource_time_id = resource.Usage.id()
|
||||
@@ -109,11 +113,11 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props.editing_resource_type = "USAGE"
|
||||
|
||||
@classmethod
|
||||
def get_resource_time(cls, resource):
|
||||
return resource.Usage if resource.Usage else None
|
||||
def get_resource_time(cls, resource: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return resource.Usage or None
|
||||
|
||||
@classmethod
|
||||
def load_resource_time_attributes(cls, resource_time):
|
||||
def load_resource_time_attributes(cls, resource_time: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if prop.data_type == "string":
|
||||
if isinstance(data[name], datetime):
|
||||
@@ -128,7 +132,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_resource_time_attributes(cls):
|
||||
def get_resource_time_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
||||
if prop.is_null:
|
||||
@@ -147,20 +151,19 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
return blenderbim.bim.helper.export_attributes(props.resource_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_costs(cls, resource):
|
||||
def enable_editing_resource_costs(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_resource_id = resource.id()
|
||||
props.editing_resource_type = "COSTS"
|
||||
resource
|
||||
|
||||
@classmethod
|
||||
def disable_editing_resource_cost_value(cls):
|
||||
def disable_editing_resource_cost_value(cls) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_cost_value_id = 0
|
||||
props.cost_value_editing_type = ""
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_cost_value_formula(cls, cost_value):
|
||||
def enable_editing_resource_cost_value_formula(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.cost_value_attributes.clear()
|
||||
props.active_cost_value_id = cost_value.id()
|
||||
@@ -168,7 +171,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value) if cost_value else ""
|
||||
|
||||
@classmethod
|
||||
def load_cost_value_attributes(cls, cost_value):
|
||||
def load_cost_value_attributes(cls, cost_value: ifcopenshell.entity_instance):
|
||||
def callback(name, prop, data):
|
||||
if name == "AppliedValue":
|
||||
# TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure)
|
||||
@@ -221,7 +224,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
blenderbim.bim.helper.import_attributes2(cost_value, props.cost_value_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_cost_value_attributes(cls, cost_value):
|
||||
def enable_editing_cost_value_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.cost_value_attributes.clear()
|
||||
props.active_cost_value_id = cost_value.id()
|
||||
@@ -232,7 +235,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
return bpy.context.scene.BIMResourceProperties.cost_value_formula
|
||||
|
||||
@classmethod
|
||||
def get_resource_cost_value_attributes(cls):
|
||||
def get_resource_cost_value_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if prop.name == "UnitBasisValue":
|
||||
if prop.is_null:
|
||||
@@ -257,28 +260,28 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_base_quantity(cls, resource):
|
||||
def enable_editing_resource_base_quantity(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_resource_id = resource.id()
|
||||
props.editing_resource_type = "QUANTITY"
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_quantity(cls, resource_quantity):
|
||||
def enable_editing_resource_quantity(cls, resource_quantity: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.quantity_attributes.clear()
|
||||
props.is_editing_quantity = True
|
||||
blenderbim.bim.helper.import_attributes2(resource_quantity, props.quantity_attributes)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_resource_quantity(cls):
|
||||
def disable_editing_resource_quantity(cls) -> None:
|
||||
bpy.context.scene.BIMResourceProperties.is_editing_quantity = False
|
||||
|
||||
@classmethod
|
||||
def get_resource_quantity_attributes(cls):
|
||||
def get_resource_quantity_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMResourceProperties.quantity_attributes)
|
||||
|
||||
@classmethod
|
||||
def expand_resource(cls, resource):
|
||||
def expand_resource(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
contracted_resources = json.loads(props.contracted_resources)
|
||||
if not resource.id() in contracted_resources:
|
||||
@@ -287,14 +290,14 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props.contracted_resources = json.dumps(contracted_resources)
|
||||
|
||||
@classmethod
|
||||
def contract_resource(cls, resource):
|
||||
def contract_resource(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
contracted_resources = json.loads(props.contracted_resources)
|
||||
contracted_resources.append(resource.id())
|
||||
props.contracted_resources = json.dumps(contracted_resources)
|
||||
|
||||
@classmethod
|
||||
def import_resources(cls, file_path):
|
||||
def import_resources(cls, file_path: str) -> None:
|
||||
from ifc4d.csv2ifc import Csv2Ifc
|
||||
|
||||
start = time.time()
|
||||
@@ -305,7 +308,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
print("Importing Resources CSV finished in {:.2f} seconds".format(time.time() - start))
|
||||
|
||||
@classmethod
|
||||
def get_highlighted_resource(cls):
|
||||
def get_highlighted_resource(cls) -> Union[ifcopenshell.entity_instance, None]:
|
||||
resources = len(bpy.context.scene.BIMResourceTreeProperties.resources)
|
||||
if resources and resources > bpy.context.scene.BIMResourceProperties.active_resource_index:
|
||||
return tool.Ifc.get().by_id(
|
||||
@@ -315,7 +318,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def clear_productivity_data(cls, props):
|
||||
def clear_productivity_data(cls, props: bpy.types.PropertyGroup) -> None:
|
||||
for duration_prop in props.quantity_consumed or []:
|
||||
if duration_prop.name == "BaseQuantityConsumed":
|
||||
duration_prop.years = 0
|
||||
@@ -328,7 +331,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props.quantity_produced_name = ""
|
||||
|
||||
@classmethod
|
||||
def load_productivity_data(cls):
|
||||
def load_productivity_data(cls) -> None:
|
||||
duration_props = None
|
||||
for collection_prop in bpy.context.scene.BIMResourceProductivity.quantity_consumed:
|
||||
duration_props = collection_prop if collection_prop.name == "BaseQuantityConsumed" else None
|
||||
@@ -358,7 +361,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
duration_props.seconds = durations_attributes["seconds"]
|
||||
|
||||
@classmethod
|
||||
def get_productivity_attributes(cls):
|
||||
def get_productivity_attributes(cls) -> dict[str, Any]:
|
||||
props = bpy.context.scene.BIMResourceProductivity
|
||||
productivity = {}
|
||||
if props.quantity_consumed:
|
||||
@@ -370,7 +373,9 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
return productivity
|
||||
|
||||
@classmethod
|
||||
def get_productivity(cls, resource, should_inherit=False):
|
||||
def get_productivity(
|
||||
cls, resource: ifcopenshell.entity_instance, should_inherit: bool = False
|
||||
) -> ifcopenshell.util.resource.PRODUCTIVITY_PSET_DATA:
|
||||
return ifcopenshell.util.resource.get_productivity(resource, should_inherit=should_inherit)
|
||||
|
||||
@classmethod
|
||||
@@ -380,29 +385,29 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
return
|
||||
return tool.Ifc.run(
|
||||
"pset.edit_pset",
|
||||
pset= tool.Ifc.get().by_id(productivity["id"]),
|
||||
pset=tool.Ifc.get().by_id(productivity["id"]),
|
||||
properties=attributes,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_constraints(cls, resource):
|
||||
def get_constraints(cls, resource: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.constraint.get_constraints(product=resource)
|
||||
|
||||
@classmethod
|
||||
def get_metrics(cls, constraint):
|
||||
def get_metrics(cls, constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.constraint.get_metrics(constraint)
|
||||
|
||||
@classmethod
|
||||
def get_metric_reference(cls, metric, is_deep=True):
|
||||
def get_metric_reference(cls, metric: ifcopenshell.entity_instance, is_deep: bool = True):
|
||||
return ifcopenshell.util.constraint.get_metric_reference(metric, is_deep=is_deep)
|
||||
|
||||
@classmethod
|
||||
def has_metric_constraint(cls, resource, attribute):
|
||||
def has_metric_constraint(cls, resource: ifcopenshell.entity_instance, attribute):
|
||||
metrics = ifcopenshell.util.constraint.get_metric_constraints(resource, attribute)
|
||||
return True if metrics else False
|
||||
|
||||
@classmethod
|
||||
def run_edit_resource_time(cls, resource, attributes):
|
||||
def run_edit_resource_time(cls, resource: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
if not resource.Usage:
|
||||
tool.Ifc.run(
|
||||
"resource.add_resource_time",
|
||||
@@ -411,7 +416,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
tool.Ifc.run("resource.edit_resource_time", resource_time=resource.Usage, attributes=attributes)
|
||||
|
||||
@classmethod
|
||||
def go_to_resource(cls, resource):
|
||||
def go_to_resource(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
def get_ancestors_ids(resource):
|
||||
ids = []
|
||||
for rel in resource.Nests or []:
|
||||
@@ -427,24 +432,22 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
bpy.context.scene.BIMResourceProperties.contracted_resources = json.dumps(contracted_resources)
|
||||
cls.load_resources()
|
||||
|
||||
|
||||
resource_props = bpy.context.scene.BIMResourceTreeProperties
|
||||
expanded_resources = [item.ifc_definition_id for item in resource_props.resources]
|
||||
bpy.context.scene.BIMResourceProperties.active_resource_index = expanded_resources.index(resource.id())
|
||||
|
||||
|
||||
@classmethod
|
||||
def run_calculate_resource_usage(cls, resource):
|
||||
def run_calculate_resource_usage(cls, resource: ifcopenshell.entity_instance) -> None:
|
||||
tool.Ifc.run("resource.calculate_resource_usage", resource=resource)
|
||||
|
||||
@classmethod
|
||||
def get_task_assignments(cls, resource):
|
||||
def get_task_assignments(cls, resource: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.resource.get_task_assignments(resource)
|
||||
|
||||
@classmethod
|
||||
def get_nested_resources(cls, resource):
|
||||
def get_nested_resources(cls, resource: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.resource.get_nested_resources(resource)
|
||||
|
||||
@classmethod
|
||||
def is_attribute_locked(cls, resource, attribute):
|
||||
return ifcopenshell.util.constraint.is_attribute_locked(resource, attribute)
|
||||
def is_attribute_locked(cls, resource: ifcopenshell.entity_instance, attribute) -> bool:
|
||||
return ifcopenshell.util.constraint.is_attribute_locked(resource, attribute)
|
||||
|
||||
@@ -29,6 +29,7 @@ import blenderbim.core.style
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
class Root(blenderbim.core.tool.Root):
|
||||
@@ -129,7 +130,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
return ifcopenshell.util.representation.get_representation(element, context=context.ContextType)
|
||||
|
||||
@classmethod
|
||||
def get_element_type(cls, element):
|
||||
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
@@ -282,8 +283,12 @@ class Root(blenderbim.core.tool.Root):
|
||||
|
||||
@classmethod
|
||||
def run_geometry_add_representation(
|
||||
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||
):
|
||||
cls,
|
||||
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:
|
||||
return blenderbim.core.geometry.add_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
|
||||
@@ -36,11 +36,12 @@ import blenderbim.bim.helper
|
||||
import blenderbim.bim.module.sequence.helper as helper
|
||||
from dateutil import parser
|
||||
from datetime import datetime
|
||||
from typing import Optional, Any, Union
|
||||
|
||||
|
||||
class Sequence(blenderbim.core.tool.Sequence):
|
||||
@classmethod
|
||||
def get_work_plan_attributes(cls):
|
||||
def get_work_plan_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if "Date" in prop.name or "Time" in prop.name:
|
||||
if prop.is_null:
|
||||
@@ -59,7 +60,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return blenderbim.bim.helper.export_attributes(props.work_plan_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def load_work_plan_attributes(cls, work_plan):
|
||||
def load_work_plan_attributes(cls, work_plan: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if name in ["CreationDate", "StartTime", "FinishTime"]:
|
||||
prop.string_value = "" if prop.is_null else data[name]
|
||||
@@ -70,23 +71,23 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
blenderbim.bim.helper.import_attributes2(work_plan, props.work_plan_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_plan(cls, work_plan):
|
||||
def enable_editing_work_plan(cls, work_plan: Union[ifcopenshell.entity_instance, None]) -> None:
|
||||
if work_plan:
|
||||
bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
|
||||
bpy.context.scene.BIMWorkPlanProperties.editing_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_work_plan(cls):
|
||||
def disable_editing_work_plan(cls) -> None:
|
||||
bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = 0
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_plan_schedules(cls, work_plan):
|
||||
def enable_editing_work_plan_schedules(cls, work_plan: Union[ifcopenshell.entity_instance, None]) -> None:
|
||||
if work_plan:
|
||||
bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
|
||||
bpy.context.scene.BIMWorkPlanProperties.editing_type = "SCHEDULES"
|
||||
|
||||
@classmethod
|
||||
def get_work_schedule_attributes(cls):
|
||||
def get_work_schedule_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if "Date" in prop.name or "Time" in prop.name:
|
||||
if prop.is_null:
|
||||
@@ -105,7 +106,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def load_work_schedule_attributes(cls, work_schedule):
|
||||
def load_work_schedule_attributes(cls, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if name in ["CreationDate", "StartTime", "FinishTime"]:
|
||||
prop.string_value = "" if prop.is_null else data[name]
|
||||
@@ -116,23 +117,23 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
blenderbim.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_schedule(cls, work_schedule):
|
||||
def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = work_schedule.id()
|
||||
bpy.context.scene.BIMWorkScheduleProperties.editing_type = "WORK_SCHEDULE"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_work_schedule(cls):
|
||||
def disable_editing_work_schedule(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_schedule_tasks(cls, work_schedule):
|
||||
def enable_editing_work_schedule_tasks(cls, work_schedule: Union[ifcopenshell.entity_instance, None]) -> None:
|
||||
if work_schedule:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_work_schedule_id = work_schedule.id()
|
||||
props.editing_type = "TASKS"
|
||||
|
||||
@classmethod
|
||||
def load_task_tree(cls, work_schedule):
|
||||
def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
bpy.context.scene.BIMTaskTreeProperties.tasks.clear()
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
cls.contracted_tasks = json.loads(props.contracted_tasks)
|
||||
@@ -142,7 +143,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
cls.create_new_task_li(related_object_id, 0)
|
||||
|
||||
@classmethod
|
||||
def get_sorted_tasks_ids(cls, tasks):
|
||||
def get_sorted_tasks_ids(cls, tasks: list[ifcopenshell.entity_instance]) -> list[int]:
|
||||
def get_sort_key(task):
|
||||
# Sorting only applies to actual tasks, not the WBS
|
||||
# for rel in task.IsNestedBy:
|
||||
@@ -170,7 +171,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return related_object_ids
|
||||
|
||||
@classmethod
|
||||
def create_new_task_li(cls, related_object_id, level_index):
|
||||
def create_new_task_li(cls, related_object_id: int, level_index: int) -> None:
|
||||
task = tool.Ifc.get().by_id(related_object_id)
|
||||
new = bpy.context.scene.BIMTaskTreeProperties.tasks.add()
|
||||
new.ifc_definition_id = related_object_id
|
||||
@@ -182,8 +183,9 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
for related_object_id in cls.get_sorted_tasks_ids(ifcopenshell.util.sequence.get_nested_tasks(task)):
|
||||
cls.create_new_task_li(related_object_id, level_index + 1)
|
||||
|
||||
# TODO: task argument is never used?
|
||||
@classmethod
|
||||
def load_task_properties(cls, task=None):
|
||||
def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
task_props = bpy.context.scene.BIMTaskTreeProperties
|
||||
tasks_with_visual_bar = cls.get_task_bar_list()
|
||||
@@ -248,24 +250,24 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
props.is_task_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def get_active_work_schedule(cls):
|
||||
def get_active_work_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
|
||||
return None
|
||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id)
|
||||
|
||||
@classmethod
|
||||
def expand_task(cls, task):
|
||||
def expand_task(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
contracted_tasks = json.loads(props.contracted_tasks)
|
||||
contracted_tasks.remove(task.id())
|
||||
props.contracted_tasks = json.dumps(contracted_tasks)
|
||||
|
||||
@classmethod
|
||||
def expand_all_tasks(cls):
|
||||
def expand_all_tasks(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps([])
|
||||
|
||||
@classmethod
|
||||
def contract_all_tasks(cls):
|
||||
def contract_all_tasks(cls) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
contracted_tasks = json.loads(props.contracted_tasks)
|
||||
for task_item in bpy.context.scene.BIMTaskTreeProperties.tasks:
|
||||
@@ -274,18 +276,18 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
props.contracted_tasks = json.dumps(contracted_tasks)
|
||||
|
||||
@classmethod
|
||||
def contract_task(cls, task):
|
||||
def contract_task(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
contracted_tasks = json.loads(props.contracted_tasks)
|
||||
contracted_tasks.append(task.id())
|
||||
props.contracted_tasks = json.dumps(contracted_tasks)
|
||||
|
||||
@classmethod
|
||||
def disable_work_schedule(cls):
|
||||
def disable_work_schedule(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
|
||||
|
||||
@classmethod
|
||||
def disable_selecting_deleted_task(cls):
|
||||
def disable_selecting_deleted_task(cls) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
if props.active_task_id not in [
|
||||
task.ifc_definition_id for task in bpy.context.scene.BIMTaskTreeProperties.tasks
|
||||
@@ -294,7 +296,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
|
||||
|
||||
@classmethod
|
||||
def get_checked_tasks(cls):
|
||||
def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]:
|
||||
return [
|
||||
tool.Ifc.get().by_id(task.ifc_definition_id)
|
||||
for task in bpy.context.scene.BIMTaskTreeProperties.tasks
|
||||
@@ -302,39 +304,39 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
] or []
|
||||
|
||||
@classmethod
|
||||
def get_task_attribute_value(cls, attribute_name):
|
||||
def get_task_attribute_value(cls, attribute_name: str) -> Any:
|
||||
return bpy.context.scene.BIMWorkScheduleProperties.task_attributes.get(attribute_name).get_value()
|
||||
|
||||
@classmethod
|
||||
def get_active_task(cls):
|
||||
def get_active_task(cls) -> ifcopenshell.entity_instance:
|
||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_task_id)
|
||||
|
||||
@classmethod
|
||||
def get_active_work_time(cls):
|
||||
def get_active_work_time(cls) -> ifcopenshell.entity_instance:
|
||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id)
|
||||
|
||||
@classmethod
|
||||
def get_task_time(cls, task):
|
||||
return task.TaskTime if task.TaskTime else None
|
||||
def get_task_time(cls, task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return task.TaskTime or None
|
||||
|
||||
@classmethod
|
||||
def load_task_attributes(cls, task):
|
||||
def load_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.task_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(task, props.task_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_task_attributes(cls, task):
|
||||
def enable_editing_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_task_id = task.id()
|
||||
props.editing_task_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def get_task_attributes(cls):
|
||||
def get_task_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.task_attributes)
|
||||
|
||||
@classmethod
|
||||
def load_task_time_attributes(cls, task_time):
|
||||
def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if prop and prop.data_type == "string":
|
||||
duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add()
|
||||
@@ -358,20 +360,20 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
blenderbim.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_task_time(cls, task):
|
||||
def enable_editing_task_time(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_task_id = task.id()
|
||||
props.active_task_time_id = task.TaskTime.id()
|
||||
props.editing_task_type = "TASKTIME"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_task(cls):
|
||||
def disable_editing_task(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
|
||||
bpy.context.scene.BIMWorkScheduleProperties.editing_task_type = ""
|
||||
|
||||
@classmethod
|
||||
def get_task_time_attributes(cls):
|
||||
def get_task_time_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
||||
if prop.is_null:
|
||||
@@ -400,7 +402,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return blenderbim.bim.helper.export_attributes(props.task_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def load_task_resources(cls, task):
|
||||
def load_task_resources(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
rprops = bpy.context.scene.BIMResourceProperties
|
||||
props.task_resources.clear()
|
||||
@@ -413,36 +415,38 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
rprops.is_resource_update_enabled = True
|
||||
|
||||
@classmethod
|
||||
def get_task_inputs(cls, task):
|
||||
def get_task_inputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_inputs
|
||||
return ifcopenshell.util.sequence.get_task_inputs(task, is_deep)
|
||||
|
||||
@classmethod
|
||||
def get_task_outputs(cls, task):
|
||||
def get_task_outputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_outputs
|
||||
return ifcopenshell.util.sequence.get_task_outputs(task, is_deep)
|
||||
|
||||
@classmethod
|
||||
def are_entities_same_class(cls, entities):
|
||||
def are_entities_same_class(cls, entities: list[ifcopenshell.entity_instance]) -> bool:
|
||||
if not entities:
|
||||
return False
|
||||
if len(entities) == 1:
|
||||
return True
|
||||
first = entities[0]
|
||||
first_class = entities[0].is_a()
|
||||
for entity in entities:
|
||||
if entity.is_a() != first.is_a():
|
||||
if entity.is_a() != first_class:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_task_resources(cls, task):
|
||||
def get_task_resources(
|
||||
cls, task: Union[ifcopenshell.entity_instance, None]
|
||||
) -> Union[list[ifcopenshell.entity_instance], None]:
|
||||
if not task:
|
||||
return
|
||||
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_resources
|
||||
return ifcopenshell.util.sequence.get_task_resources(task, is_deep)
|
||||
|
||||
@classmethod
|
||||
def load_task_inputs(cls, inputs):
|
||||
def load_task_inputs(cls, inputs: list[ifcopenshell.entity_instance]) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.task_inputs.clear()
|
||||
for input in inputs:
|
||||
@@ -451,7 +455,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
new.name = input.Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def load_task_outputs(cls, outputs):
|
||||
def load_task_outputs(cls, outputs: list[ifcopenshell.entity_instance]) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.task_outputs.clear()
|
||||
if outputs:
|
||||
@@ -461,7 +465,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
new.name = output.Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def get_highlighted_task(cls):
|
||||
def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]:
|
||||
tasks = bpy.context.scene.BIMTaskTreeProperties.tasks
|
||||
if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index:
|
||||
return tool.Ifc.get().by_id(
|
||||
@@ -469,47 +473,42 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_direct_nested_tasks(cls, task):
|
||||
def get_direct_nested_tasks(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.sequence.get_nested_tasks(task)
|
||||
|
||||
@classmethod
|
||||
def get_direct_task_outputs(cls, task):
|
||||
def get_direct_task_outputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.sequence.get_direct_task_outputs(task)
|
||||
|
||||
@classmethod
|
||||
def get_task_outputs(cls, task):
|
||||
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_outputs
|
||||
return ifcopenshell.util.sequence.get_task_outputs(task, is_deep)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_calendar_times(cls, work_calendar):
|
||||
def enable_editing_work_calendar_times(cls, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkCalendarProperties
|
||||
props.active_work_calendar_id = work_calendar.id()
|
||||
props.editing_type = "WORKTIMES"
|
||||
|
||||
@classmethod
|
||||
def load_work_calendar_attributes(cls, work_calendar):
|
||||
def load_work_calendar_attributes(cls, work_calendar: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
props = bpy.context.scene.BIMWorkCalendarProperties
|
||||
props.work_calendar_attributes.clear()
|
||||
return blenderbim.bim.helper.import_attributes2(work_calendar, props.work_calendar_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_calendar(cls, work_calendar):
|
||||
def enable_editing_work_calendar(cls, work_calendar: ifcopenshell.entity_instance) -> None:
|
||||
bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = work_calendar.id()
|
||||
bpy.context.scene.BIMWorkCalendarProperties.editing_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_work_calendar(cls):
|
||||
def disable_editing_work_calendar(cls) -> None:
|
||||
bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0
|
||||
|
||||
@classmethod
|
||||
def get_work_calendar_attributes(cls):
|
||||
def get_work_calendar_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(
|
||||
bpy.context.scene.BIMWorkCalendarProperties.work_calendar_attributes
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def load_work_time_attributes(cls, work_time):
|
||||
def load_work_time_attributes(cls, work_time: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if name in ["Start", "Finish"]:
|
||||
prop.string_value = "" if prop.is_null else data[name]
|
||||
@@ -521,7 +520,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
blenderbim.bim.helper.import_attributes2(work_time, props.work_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_time(cls, work_time):
|
||||
def enable_editing_work_time(cls, work_time: ifcopenshell.entity_instance) -> None:
|
||||
def initialise_recurrence_components(props):
|
||||
if len(props.day_components) == 0:
|
||||
for i in range(0, 31):
|
||||
@@ -568,7 +567,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
props.editing_type = "WORKTIMES"
|
||||
|
||||
@classmethod
|
||||
def get_work_time_attributes(cls):
|
||||
def get_work_time_attributes(cls) -> dict[str, Any]:
|
||||
def callback(attributes, prop):
|
||||
if "Start" in prop.name or "Finish" in prop.name:
|
||||
if prop.is_null:
|
||||
@@ -608,11 +607,11 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def disable_editing_work_time(cls):
|
||||
def disable_editing_work_time(cls) -> None:
|
||||
bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id = 0
|
||||
|
||||
@classmethod
|
||||
def get_recurrence_pattern_times(cls):
|
||||
def get_recurrence_pattern_times(cls) -> Union[tuple[datetime, datetime], None]:
|
||||
props = bpy.context.scene.BIMWorkCalendarProperties
|
||||
try:
|
||||
start_time = parser.parse(props.start_time)
|
||||
@@ -622,40 +621,40 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return # improve UI / refactor to add user hints
|
||||
|
||||
@classmethod
|
||||
def reset_time_period(cls):
|
||||
def reset_time_period(cls) -> None:
|
||||
bpy.context.scene.BIMWorkCalendarProperties.start_time = ""
|
||||
bpy.context.scene.BIMWorkCalendarProperties.end_time = ""
|
||||
|
||||
@classmethod
|
||||
def enable_editing_task_calendar(cls, task):
|
||||
def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_task_id = task.id()
|
||||
props.editing_task_type = "CALENDAR"
|
||||
|
||||
@classmethod
|
||||
def enable_editing_task_sequence(cls):
|
||||
def enable_editing_task_sequence(cls) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.editing_task_type = "SEQUENCE"
|
||||
|
||||
@classmethod
|
||||
def disable_editing_task_time(cls):
|
||||
def disable_editing_task_time(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
|
||||
|
||||
@classmethod
|
||||
def load_rel_sequence_attributes(cls, rel_sequence):
|
||||
def load_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.sequence_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_rel_sequence_attributes(cls, rel_sequence):
|
||||
def enable_editing_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_sequence_id = rel_sequence.id()
|
||||
props.editing_sequence_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def load_lag_time_attributes(cls, lag_time):
|
||||
def load_lag_time_attributes(cls, lag_time: ifcopenshell.entity_instance) -> None:
|
||||
def callback(name, prop, data):
|
||||
if name == "LagValue":
|
||||
prop = bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes.add()
|
||||
@@ -673,21 +672,21 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
blenderbim.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_sequence_lag_time(cls, rel_sequence):
|
||||
def enable_editing_sequence_lag_time(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.active_sequence_id = rel_sequence.id()
|
||||
props.editing_sequence_type = "LAG_TIME"
|
||||
|
||||
@classmethod
|
||||
def get_rel_sequence_attributes(cls):
|
||||
def get_rel_sequence_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.sequence_attributes)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_rel_sequence(cls):
|
||||
def disable_editing_rel_sequence(cls) -> None:
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_sequence_id = 0
|
||||
|
||||
@classmethod
|
||||
def get_lag_time_attributes(cls):
|
||||
def get_lag_time_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes)
|
||||
|
||||
@classmethod
|
||||
@@ -698,14 +697,14 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
obj.select_set(True) if obj else None
|
||||
|
||||
@classmethod
|
||||
def add_task_column(cls, column_type, name, data_type):
|
||||
def add_task_column(cls, column_type: str, name: str, data_type: str):
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
new = props.columns.add()
|
||||
new.name = f"{column_type}.{name}"
|
||||
new.data_type = data_type
|
||||
|
||||
@classmethod
|
||||
def setup_default_task_columns(cls):
|
||||
def setup_default_task_columns(cls) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.columns.clear()
|
||||
default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]
|
||||
@@ -715,14 +714,14 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
new.data_type = "string"
|
||||
|
||||
@classmethod
|
||||
def remove_task_column(cls, name):
|
||||
def remove_task_column(cls, name: str) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.columns.remove(props.columns.find(name))
|
||||
if props.sort_column == name:
|
||||
props.sort_column = ""
|
||||
|
||||
@classmethod
|
||||
def set_task_sort_column(cls, column):
|
||||
def set_task_sort_column(cls, column: str) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.sort_column = column
|
||||
|
||||
@@ -743,7 +742,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
return related_tasks
|
||||
|
||||
@classmethod
|
||||
def get_work_schedule(cls, task):
|
||||
def get_work_schedule(cls, task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for rel in task.HasAssignments or []:
|
||||
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"):
|
||||
return rel.RelatingControl
|
||||
@@ -779,8 +778,9 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
expanded_tasks = [item.ifc_definition_id for item in task_props.tasks]
|
||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_index = expanded_tasks.index(task.id()) or 0
|
||||
|
||||
# TODO: proper typing
|
||||
@classmethod
|
||||
def guess_date_range(cls, work_schedule):
|
||||
def guess_date_range(cls, work_schedule: ifcopenshell.entity_instance) -> tuple[Any, Any]:
|
||||
return ifcopenshell.util.sequence.guess_date_range(work_schedule)
|
||||
|
||||
@classmethod
|
||||
@@ -792,7 +792,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
props.visualisation_finish = ifcopenshell.util.date.canonicalise_time(finish_date)
|
||||
|
||||
@classmethod
|
||||
def get_animation_bar_tasks(cls):
|
||||
def get_animation_bar_tasks(cls) -> list[ifcopenshell.entity_instance]:
|
||||
return [tool.Ifc.get().by_id(task_id) for task_id in cls.get_task_bar_list()]
|
||||
|
||||
@classmethod
|
||||
@@ -830,12 +830,16 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
def create_task_bar_data(tasks, vertical_increment, collection):
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
settings = {
|
||||
"viz_start": parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
|
||||
if props.visualisation_start
|
||||
else None,
|
||||
"viz_finish": parser.parse(props.visualisation_finish, dayfirst=True, fuzzy=True)
|
||||
if props.visualisation_finish
|
||||
else None,
|
||||
"viz_start": (
|
||||
parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
|
||||
if props.visualisation_start
|
||||
else None
|
||||
),
|
||||
"viz_finish": (
|
||||
parser.parse(props.visualisation_finish, dayfirst=True, fuzzy=True)
|
||||
if props.visualisation_finish
|
||||
else None
|
||||
),
|
||||
"start_frame": bpy.context.scene.frame_start,
|
||||
"end_frame": bpy.context.scene.frame_end,
|
||||
}
|
||||
@@ -1076,19 +1080,19 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
predefined_type_item.color = data["Color"]
|
||||
|
||||
@classmethod
|
||||
def get_start_date(cls):
|
||||
def get_start_date(cls) -> Union[datetime, None]:
|
||||
start = parser.parse(bpy.context.scene.BIMWorkScheduleProperties.visualisation_start, dayfirst=True, fuzzy=True)
|
||||
return start if start else None
|
||||
return start or None
|
||||
|
||||
@classmethod
|
||||
def get_finish_date(cls):
|
||||
def get_finish_date(cls) -> Union[datetime, None]:
|
||||
finish = parser.parse(
|
||||
bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish, dayfirst=True, fuzzy=True
|
||||
)
|
||||
return finish if finish else None
|
||||
return finish or None
|
||||
|
||||
@classmethod
|
||||
def process_construction_state(cls, work_schedule, date):
|
||||
def process_construction_state(cls, work_schedule: ifcopenshell.entity_instance, date: datetime) -> dict[str, Any]:
|
||||
cls.to_build = set()
|
||||
cls.in_construction = set()
|
||||
cls.completed = set()
|
||||
@@ -1109,7 +1113,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def process_task_status(cls, task, date):
|
||||
def process_task_status(cls, task: ifcopenshell.entity_instance, date: datetime) -> None:
|
||||
for rel in task.IsNestedBy or []:
|
||||
[cls.process_task_status(related_object, date) for related_object in rel.RelatedObjects]
|
||||
start = ifcopenshell.util.sequence.derive_date(task, "ScheduleStart", is_earliest=True)
|
||||
@@ -1234,7 +1238,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_animation_product_frames(cls, work_schedule, settings):
|
||||
def get_animation_product_frames(cls, work_schedule: ifcopenshell.entity_instance, settings: dict[str, Any]):
|
||||
def preprocess_task(task):
|
||||
for subtask in ifcopenshell.util.sequence.get_nested_tasks(task):
|
||||
preprocess_task(subtask)
|
||||
@@ -1445,7 +1449,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
append_handler(animate_text_handler)
|
||||
|
||||
@classmethod
|
||||
def create_tasks_json(cls, work_schedule=None):
|
||||
def create_tasks_json(cls, work_schedule: ifcopenshell.entity_instance) -> list[dict[str, Any]]:
|
||||
sequence_type_map = {
|
||||
None: "FS",
|
||||
"START_START": "SS",
|
||||
@@ -1513,9 +1517,11 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
"pParent": task.Nests[0].RelatingObject.id() if task.Nests else 0,
|
||||
"pOpen": 1,
|
||||
"pCost": 1,
|
||||
"ifcduration": str(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleDuration))
|
||||
if (task_time and task_time.ScheduleDuration)
|
||||
else "",
|
||||
"ifcduration": (
|
||||
str(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleDuration))
|
||||
if (task_time and task_time.ScheduleDuration)
|
||||
else ""
|
||||
),
|
||||
"resourceUsage": resources_usage,
|
||||
}
|
||||
if task_time and task_time.IsCritical:
|
||||
@@ -1535,7 +1541,9 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
cls.create_new_task_json(nested_task, json, type_map, baseline_schedule)
|
||||
|
||||
@classmethod
|
||||
def generate_gantt_browser_chart(cls, task_json, work_schedule):
|
||||
def generate_gantt_browser_chart(
|
||||
cls, task_json: list[dict[str, Any]], work_schedule: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"), "w") as f:
|
||||
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.mustache"), "r") as t:
|
||||
task_b64 = base64.b64encode(bytes(json.dumps(task_json), "utf-8")).decode("utf-8")
|
||||
@@ -1545,15 +1553,19 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
webbrowser.open("file://" + os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"))
|
||||
|
||||
@classmethod
|
||||
def is_filter_by_active_schedule(cls):
|
||||
def is_filter_by_active_schedule(cls) -> bool:
|
||||
return bpy.context.scene.BIMWorkScheduleProperties.filter_by_active_schedule
|
||||
|
||||
@classmethod
|
||||
def get_tasks_for_product(cls, product, work_schedule=None):
|
||||
def get_tasks_for_product(
|
||||
cls, product: ifcopenshell.entity_instance, work_schedule: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
|
||||
return ifcopenshell.util.sequence.get_tasks_for_product(product, work_schedule)
|
||||
|
||||
@classmethod
|
||||
def load_product_related_tasks(cls, task_inputs, task_ouputs):
|
||||
def load_product_related_tasks(
|
||||
cls, task_inputs: list[ifcopenshell.entity_instance], task_ouputs: list[ifcopenshell.entity_instance]
|
||||
) -> None:
|
||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||
props.product_input_tasks.clear()
|
||||
props.product_output_tasks.clear()
|
||||
@@ -1567,7 +1579,9 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
new.ifc_definition_id = task.id()
|
||||
|
||||
@classmethod
|
||||
def get_work_schedule_products(cls, work_schedule):
|
||||
def get_work_schedule_products(
|
||||
cls, work_schedule: ifcopenshell.entity_instance
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
products = []
|
||||
for task in ifcopenshell.util.sequence.get_root_tasks(work_schedule):
|
||||
products.extend(ifcopenshell.util.sequence.get_task_inputs(task, is_deep=True))
|
||||
@@ -1615,7 +1629,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
bpy.ops.view3d.camera_to_view_selected()
|
||||
|
||||
@classmethod
|
||||
def save_animation_color_scheme(cls, name):
|
||||
def save_animation_color_scheme(cls, name: str) -> ifcopenshell.entity_instance:
|
||||
props = bpy.context.scene.BIMAnimationProperties
|
||||
colour_scheme = {
|
||||
"Inputs": {cs.name: cs.color[0:3] for cs in props.task_input_colors},
|
||||
@@ -1630,11 +1644,11 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
group.Description = json.dumps(description)
|
||||
else:
|
||||
description = json.dumps({"type": "BBIM_AnimationColorScheme", "colourscheme": colour_scheme})
|
||||
group = tool.Ifc.run("group.add_group", Name=name, Description=description)
|
||||
group = tool.Ifc.run("group.add_group", name=name, description=description)
|
||||
return group[0]
|
||||
|
||||
@classmethod
|
||||
def load_animation_color_scheme(cls, scheme):
|
||||
def load_animation_color_scheme(cls, scheme: Optional[ifcopenshell.entity_instance]) -> None:
|
||||
if not scheme:
|
||||
return
|
||||
data = json.loads(scheme.Description)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user