mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c9a657a8c | |||
| 48acfad8bb | |||
| 01b63d87d0 | |||
| 36744d391e | |||
| f0e5430daa | |||
| 197281eb5a | |||
| 7b370e6142 | |||
| 71a7d0c086 | |||
| b86e735530 | |||
| 46104f5bf5 | |||
| d68068a84b | |||
| fc7dd3b234 | |||
| 85fc97eb84 | |||
| 9076deddbc | |||
| eb0d3c38b2 | |||
| 590288496e | |||
| 269e137ea2 | |||
| ed722c6f13 | |||
| 2b9e3d2a2e | |||
| 7dee888ecf | |||
| a67927ae67 | |||
| 1271e4b7e3 | |||
| a44d0e713a | |||
| a358cff1b7 | |||
| 188ca3e402 | |||
| b5260d3005 | |||
| 79940641b2 | |||
| 7458040949 | |||
| 14eb543662 | |||
| e786687882 | |||
| 37e131193b | |||
| dfb00f27bb | |||
| 53dea65d8b | |||
| 2c16839de7 | |||
| c4f2d556f1 | |||
| 74900193ee | |||
| 2987d38a31 | |||
| 08b50bb9ea | |||
| 1257eb0872 | |||
| 1c8ef176b1 | |||
| e1e51cbf26 | |||
| 4c97cc8a41 | |||
| 7c6e124fe9 | |||
| 3fc312752b | |||
| d0cb5456b3 | |||
| 86072079de | |||
| 9440096106 | |||
| 649a913bbc | |||
| 53a3afaded | |||
| 33d3984990 | |||
| 5f86c6d1c2 | |||
| 10d5378ee3 | |||
| 7f27785927 | |||
| fed063a0c6 | |||
| 855062e36b | |||
| 7b74b6c68f | |||
| a01278aa8d | |||
| 99c49736fa | |||
| c1efdbb8ea | |||
| e240ae2669 | |||
| 945099b817 | |||
| f4b0fe6560 | |||
| efc6842a9d | |||
| 43931e951e | |||
| 134a480b03 | |||
| 0eb32ed7b7 | |||
| 1855ac421c | |||
| 7a69d6f20e | |||
| 2609a8293c | |||
| e68c47a897 | |||
| 06fa2b05d6 | |||
| db52e70121 | |||
| c81fe97cc2 | |||
| b9f4503206 | |||
| bd4dc501a2 | |||
| 8d1255f0bc | |||
| cc7abe6a8d | |||
| edfcc3f74b | |||
| b1c87b1eb4 | |||
| d37bda6871 | |||
| bb0d156bbf | |||
| b89ffcb6d0 | |||
| 323967b9fd | |||
| 4dd10b16e6 | |||
| 15a0d43180 | |||
| 5a1f7000ec | |||
| 277aaca6c5 | |||
| 9187f2d2b5 | |||
| 62d53ba62d | |||
| 9055f14bb9 | |||
| 0bd608b6cb | |||
| 497542d7a0 | |||
| 26b2a7d584 | |||
| 2d0bd9782a | |||
| 746a61a199 | |||
| 24df52e2f7 | |||
| 257f0b11f5 | |||
| b4f2039367 | |||
| cfb7d026d1 | |||
| 34acd6de35 | |||
| 0f3de599a6 | |||
| 0104a2cc9f | |||
| 81f4b08b6d | |||
| 10776b7de8 | |||
| b0efb648f2 | |||
| a20a8a68a6 | |||
| f8ec8402fe | |||
| aaf008f007 | |||
| f63d6b80c5 | |||
| 2f6ae1745f | |||
| 5f4098e5d4 | |||
| b49cc6c5e9 | |||
| dcc681ac74 | |||
| 240c78c12a | |||
| 6de409031e | |||
| ea274588b0 | |||
| e46e1512fa | |||
| 70182d943d | |||
| 0bd4451ce9 | |||
| 899ada0019 | |||
| f6ae711f6c | |||
| 1d815cdb67 | |||
| ce6339869d | |||
| 5eb9cb2ee4 | |||
| 6eda4e389c | |||
| 0783f8082e | |||
| 3854afb34d | |||
| 0b2321f478 | |||
| 5510fb1e3c | |||
| 7331f7361f | |||
| e06684e274 | |||
| 57d6e9b544 | |||
| 5fa0aa03e4 | |||
| 1d2ac9426d | |||
| e79a85a4fd | |||
| 076a6c152d | |||
| 76277c35d2 | |||
| a81b4a7dbb | |||
| ee8a3927b4 | |||
| 365393aa07 | |||
| 9289e1d0cc | |||
| c30b5cbc08 | |||
| 9736345d79 | |||
| 5599e52184 | |||
| ce26af5a7e | |||
| 973c9e2ec9 | |||
| 29a890662e | |||
| b42c3ba8c2 | |||
| a7509dbe29 | |||
| 23158b1b7e | |||
| 8c16f394c1 | |||
| 12f8a1f769 | |||
| 7ffd2742d6 | |||
| 6429fe34ed | |||
| d38f56e82d | |||
| c549dea943 | |||
| 894c05e138 | |||
| 92c2fc0b7f | |||
| ee2fa739bb | |||
| f7efbe5924 | |||
| 6e20ee9fea | |||
| e6d3396630 | |||
| cf1647a451 | |||
| 69535c53e0 | |||
| fed8c21625 | |||
| 0f92c2d26b | |||
| c11060b46a | |||
| 9f0d408e79 | |||
| d37baa4e0c | |||
| ae4f125371 | |||
| 25d8fff5fd | |||
| 331f491d50 | |||
| b19ece18c6 | |||
| abddc739fe | |||
| 174f909137 | |||
| a826115803 | |||
| 79abbad796 | |||
| 04a20b5c80 | |||
| f4d101788d | |||
| 4bb32b252d | |||
| 8b92b28dfc | |||
| 43cc7a0304 | |||
| 67bac441ad | |||
| 6c511f6b1b | |||
| deb0a46acb | |||
| ee0599e263 | |||
| a8efd53d6b | |||
| 02f0c92a41 | |||
| f11e09c7d8 | |||
| bb1e00e474 | |||
| 4efb79e472 | |||
| 527e0f3dbf | |||
| 8ea3142451 | |||
| a68cb74699 | |||
| 17642ca4e9 | |||
| 83a351b1c7 | |||
| 37baa7a57b | |||
| 7ca2c043b5 | |||
| 1e3ddce5c2 | |||
| c79020aeab | |||
| 645298e9d6 | |||
| ae76d3253d | |||
| 693f7c5397 | |||
| a8960957c0 | |||
| c609aea1de | |||
| 247bff4075 | |||
| 5a3a032fe6 | |||
| 5d8c2f2ec2 | |||
| 93ee92c401 | |||
| 8c2d71b306 | |||
| 8f1446bfa4 | |||
| 32608ff194 | |||
| b0fbea60ae | |||
| 8f261be338 | |||
| 42e184c936 | |||
| e0a93626a5 | |||
| aaa4be1a94 | |||
| 9f453d54d8 | |||
| b226a79ee8 | |||
| 774a770c48 | |||
| aff90e89f7 | |||
| aad4dad9a7 | |||
| 6ac6f13018 | |||
| d23cfcb2cf | |||
| 71668fbaf1 | |||
| 81b7f3dc30 | |||
| a0fa861b92 | |||
| a63c8e1d70 | |||
| 3e1d1bf7fa | |||
| 20e1fc2618 | |||
| 1014487d82 | |||
| 3832077d0f | |||
| 768bf17b05 | |||
| fb4ad5290f | |||
| f20bdd0ab6 | |||
| 5fe87bf5aa | |||
| c23a20df84 | |||
| b87cd33b12 | |||
| 8acd5321b0 | |||
| 1165ef3f33 | |||
| 2b7108a1d8 | |||
| 33d9e050ec | |||
| b88fd964bc | |||
| 0b32c89c26 | |||
| 9db1783062 | |||
| 2d54a48f8e | |||
| 39a30f2577 | |||
| 1e946fd98e | |||
| 79046e2970 | |||
| f6c6cc9a1b | |||
| af03953ef7 | |||
| cdcbc2ad3b | |||
| ba2456ad36 | |||
| 299b5bcf6b | |||
| cb95539cb0 | |||
| 7b78ebc0f3 | |||
| f39e6ccb54 | |||
| 3e1710c6fc | |||
| fac5104bd8 | |||
| 51c4059771 | |||
| f24de9d384 | |||
| 1e08a71c5a | |||
| 24b4be3ea1 | |||
| d2420c2e45 | |||
| d093d80698 | |||
| aef972a518 | |||
| 06c6fa5dff | |||
| f6087613f0 | |||
| 45e36590eb | |||
| 7f37acfd38 | |||
| de2329c352 | |||
| 09abf0c7ab | |||
| 77c2e626b3 | |||
| 9ea6a1fc75 | |||
| 5b7964f04c | |||
| 57d7d83ebb | |||
| 2f4c3de92d | |||
| 136270b566 | |||
| 557a5f03d2 | |||
| 4e1ff8ea6a | |||
| 08680d8726 | |||
| 53cf1ee0a1 | |||
| ddf7d1b0b9 | |||
| eeac0a2e30 | |||
| 1f83b4f7a0 | |||
| ada8cc90ef | |||
| 21562d7e7d | |||
| 77f9dbf4f0 | |||
| 875dd37579 | |||
| e1c49ad317 | |||
| 8e9d4be55d | |||
| 65d65972ac | |||
| b2d66bbb89 | |||
| d8a250f153 | |||
| 4c0a7e1ca9 | |||
| 11c6b55f7c | |||
| 1d2214efd9 | |||
| cb027c80f9 | |||
| 44c7c6d14e | |||
| 3e7f244100 | |||
| da1c92c42b | |||
| 17682498da |
@@ -0,0 +1,21 @@
|
||||
name: Dispatch Build IfcOpenShell
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
trigger-workflows:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
workflow:
|
||||
- 'Build IfcOpenShell Linux'
|
||||
- 'Build IfcOpenShell Linux ARM'
|
||||
- 'Build IfcOpenShell OSX'
|
||||
- 'Build IfcOpenShell WASM / Pyodide'
|
||||
- 'Build IfcOpenShell Windows'
|
||||
steps:
|
||||
- name: Trigger binary build workflows
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
workflow: ${{ matrix.workflow }}
|
||||
@@ -128,10 +128,8 @@ jobs:
|
||||
#
|
||||
# To force the link and overwrite all conflicting files:
|
||||
# brew link --overwrite python@3.13
|
||||
brew link --overwrite python@3.12
|
||||
brew link --overwrite python@3.13
|
||||
# https://github.com/rust-lang/rustup/pull/3989/files
|
||||
brew install --overwrite awscli
|
||||
brew install --overwrite awscli | true
|
||||
|
||||
- name: Upload .zip archives to S3
|
||||
run: |
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_ifcopenshell:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_ifcopenshell:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
container: rockylinux:8
|
||||
|
||||
steps:
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
# Ensure Bonsai and ifcsverchok enable/disable works before uploading to extensions repo.
|
||||
|
||||
# Download Blender.
|
||||
wget -q -O blender.tar.xz https://ftp.nluug.nl/pub/graphics/blender/release/Blender4.3/blender-4.3.0-linux-x64.tar.xz
|
||||
wget -q -O blender.tar.xz https://download.blender.org/release/Blender4.3/blender-4.3.2-linux-x64.tar.xz
|
||||
tar -xf blender.tar.xz
|
||||
|
||||
# Setup Blender.
|
||||
|
||||
@@ -24,10 +24,6 @@ jobs:
|
||||
name: "Windows 64bit",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Windows 32bit",
|
||||
short_name: win32,
|
||||
}
|
||||
- {
|
||||
name: "Linux 64bit",
|
||||
short_name: linux64
|
||||
|
||||
@@ -25,10 +25,6 @@ jobs:
|
||||
name: "Windows 64bit",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Windows 32bit",
|
||||
short_name: win32,
|
||||
}
|
||||
- {
|
||||
name: "Linux 64bit",
|
||||
short_name: linux64
|
||||
|
||||
@@ -48,7 +48,6 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil shapely
|
||||
pip install mathutils
|
||||
pip install src/bcf --no-deps
|
||||
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
|
||||
|
||||
+1
-1
@@ -350,7 +350,7 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
||||
run([git, "clone", "--recursive", clone_url, target_dir])
|
||||
else:
|
||||
logger.info(f"directory '{target_dir}' already cloned. Pulling latest changes.")
|
||||
run([git, "-C", target_dir, "fetch", "--all", "--tags"])
|
||||
run([git, "-C", target_dir, "fetch", "--all", "--tags", "--force"])
|
||||
|
||||
# detect whether we are on a branch and pull
|
||||
if run([git, "rev-parse", "--abbrev-ref", "HEAD"], cwd=target_dir) != "HEAD":
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ BLENDER_PLATFORM:=windows-x64
|
||||
endif
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=463289e
|
||||
OLD:=c49ca69
|
||||
.PHONY: bump
|
||||
bump:
|
||||
ifndef NEW
|
||||
|
||||
@@ -115,10 +115,8 @@ classes = [
|
||||
operator.ReloadIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
operator.RevertClippingPlaneCut,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectCacheDir,
|
||||
operator.SelectDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectURIAttribute,
|
||||
operator.SetTab,
|
||||
operator.SwitchTab,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 420 B |
Binary file not shown.
|
After Width: | Height: | Size: 415 B |
Binary file not shown.
|
After Width: | Height: | Size: 519 B |
Binary file not shown.
|
After Width: | Height: | Size: 513 B |
@@ -132,11 +132,12 @@ class IfcExporter:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
def has_changed_materials(self, obj: bpy.types.Object) -> bool:
|
||||
checksum = obj.data.BIMMeshProperties.material_checksum
|
||||
mprops = tool.Geometry.get_mesh_props(obj.data)
|
||||
checksum = mprops.material_checksum
|
||||
return checksum != tool.Geometry.get_material_checksum(obj)
|
||||
|
||||
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = self.file.by_id(tool.Blender.get_object_bim_props(obj).ifc_definition_id)
|
||||
if tool.Geometry.is_scaled(obj):
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
# update_representation might not apply scale if the object has openings
|
||||
|
||||
@@ -29,7 +29,6 @@ from bpy.app.handlers import persistent
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bonsai.bim.module.model.workspace import LIST_OF_TOOLS, TOOLS_TO_CLASSES_MAP
|
||||
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
|
||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator
|
||||
@@ -56,23 +55,24 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
|
||||
return
|
||||
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
props = obj.BIMStyleProperties
|
||||
props = tool.Style.get_material_style_props(obj)
|
||||
if ifc_definition_id := props.ifc_definition_id:
|
||||
if props.is_renaming:
|
||||
props.is_renmaing = False
|
||||
props.is_renaming = False
|
||||
return
|
||||
IfcStore.get_file().by_id(ifc_definition_id).Name = obj.name
|
||||
tool.Ifc.get().by_id(ifc_definition_id).Name = obj.name
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
return
|
||||
|
||||
if obj.BIMObjectProperties.is_renaming:
|
||||
obj.BIMObjectProperties.is_renaming = False
|
||||
if props.is_renaming:
|
||||
props.is_renaming = False
|
||||
return
|
||||
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
if "/" in obj.name:
|
||||
object_name = obj.name
|
||||
element_name = obj.name.split("/", 1)[1]
|
||||
@@ -88,8 +88,8 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
|
||||
if not element.is_a("IfcRoot"):
|
||||
return
|
||||
element.Name = element_name
|
||||
if obj.BIMObjectProperties.collection:
|
||||
obj.BIMObjectProperties.collection.name = object_name
|
||||
if props.collection:
|
||||
props.collection.name = object_name
|
||||
refresh_ui_data()
|
||||
|
||||
|
||||
@@ -108,29 +108,44 @@ def update_bim_tool_props():
|
||||
return
|
||||
mode = bpy.context.mode
|
||||
current_tool = bpy.context.workspace.tools.from_space_view3d_mode(mode)
|
||||
if not current_tool or current_tool.idname not in LIST_OF_TOOLS:
|
||||
if not current_tool or current_tool.idname not in tool.Blender.get_list_of_tools():
|
||||
return
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
props = tool.Model.get_model_props()
|
||||
aprops = tool.Drawing.get_annotation_props()
|
||||
TOOLS_TO_CLASSES_MAP = tool.Blender.get_tools_to_classes_map()
|
||||
is_annotation_tool = current_tool.idname == "bim.annotation_tool"
|
||||
if element.is_a("IfcTypeProduct") or element.is_a("IfcProduct"):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type:
|
||||
is_bim_tool = current_tool.idname == "bim.bim_tool"
|
||||
|
||||
if is_annotation_tool and (object_type := tool.Drawing.get_annotation_type_object_type(element_type)):
|
||||
aprops.object_type = object_type
|
||||
aprops.relating_type_id = str(element_type.id())
|
||||
return
|
||||
|
||||
if is_bim_tool:
|
||||
props.ifc_class = element_type.is_a()
|
||||
|
||||
if is_bim_tool or TOOLS_TO_CLASSES_MAP.get(current_tool.idname) == element_type.is_a():
|
||||
props.relating_type_id = str(element_type.id())
|
||||
|
||||
if is_annotation_tool:
|
||||
return
|
||||
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if element.is_a("IfcElementType") or element.is_a("IfcElement"):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type:
|
||||
is_bim_tool = current_tool.idname == "bim.bim_tool"
|
||||
if is_bim_tool:
|
||||
props.ifc_class = element_type.is_a()
|
||||
if is_bim_tool or TOOLS_TO_CLASSES_MAP.get(current_tool.idname) == element_type.is_a():
|
||||
props.relating_type_id = str(element_type.id())
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if not extrusion:
|
||||
return
|
||||
|
||||
def get_x_angle(extrusion):
|
||||
def get_x_angle(extrusion: ifcopenshell.entity_instance) -> float:
|
||||
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
|
||||
x_angle = Vector((0, 1)).angle_signed(Vector((y, z)))
|
||||
return x_angle
|
||||
@@ -207,35 +222,38 @@ def refresh_ui_data():
|
||||
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = bpy.context.scene.DocProperties.should_draw_decorations
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = props.should_draw_decorations
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
tool.Web.send_webui_data()
|
||||
|
||||
|
||||
@persistent
|
||||
def loadIfcStore(scene):
|
||||
def loadIfcStore(scene: bpy.types.Scene) -> None:
|
||||
IfcStore.purge()
|
||||
refresh_ui_data()
|
||||
if not IfcStore.get_file():
|
||||
if not tool.Ifc.get():
|
||||
return
|
||||
IfcStore.get_schema()
|
||||
tool.Ifc.schema()
|
||||
IfcStore.relink_all_objects()
|
||||
|
||||
|
||||
@persistent
|
||||
def undo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.undo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
def undo_post(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Blender.get_bim_props()
|
||||
if IfcStore.last_transaction != props.last_transaction:
|
||||
IfcStore.last_transaction = props.last_transaction
|
||||
IfcStore.undo(until_key=props.last_transaction)
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
|
||||
@persistent
|
||||
def redo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.redo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
def redo_post(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Blender.get_bim_props()
|
||||
if IfcStore.last_transaction != props.last_transaction:
|
||||
IfcStore.last_transaction = props.last_transaction
|
||||
IfcStore.redo(until_key=props.last_transaction)
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
@@ -268,10 +286,10 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
||||
return pao
|
||||
|
||||
|
||||
def viewport_shading_changed_callback(area):
|
||||
def viewport_shading_changed_callback(area: bpy.types.Area) -> None:
|
||||
shading = area.spaces.active.shading.type
|
||||
if shading == "RENDERED":
|
||||
bpy.context.scene.BIMStylesProperties.active_style_type = "External"
|
||||
tool.Style.get_style_props().active_style_type = "Internal"
|
||||
|
||||
|
||||
def subscribe_to_viewport_shading_changes():
|
||||
@@ -326,13 +344,14 @@ def load_post(scene):
|
||||
tool.Blender.setup_tabs()
|
||||
|
||||
if tool.Ifc.get() and bpy.data.is_saved:
|
||||
bpy.context.scene.BIMProperties.has_blend_warning = True
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.has_blend_warning = True
|
||||
|
||||
# Bonsai overlays
|
||||
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
georeference_props = tool.Georeference.get_georeference_props()
|
||||
aggregate_props = bpy.context.scene.BIMAggregateProperties
|
||||
nest_props = bpy.context.scene.BIMNestProperties
|
||||
model_props = bpy.context.scene.BIMModelProperties
|
||||
model_props = tool.Model.get_model_props()
|
||||
if georeference_props.should_visualise:
|
||||
GeoreferenceDecorator.install(bpy.context)
|
||||
if aggregate_props.aggregate_decorator:
|
||||
|
||||
@@ -27,12 +27,12 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.doc import get_attribute_doc, get_predefined_type_doc, get_property_doc
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from typing import Optional, Callable, Any, Union, Iterable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bonsai.bim.prop
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.module.search.prop import BIMFilterGroup
|
||||
|
||||
# ImportCallback return values:
|
||||
# - None - property should be imported by default workflow
|
||||
@@ -119,7 +119,8 @@ def import_attributes(
|
||||
data: dict[str, Any],
|
||||
callback: Optional[ImportCallback] = None,
|
||||
) -> None:
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||
schema = tool.Ifc.schema()
|
||||
for attribute in schema.declaration_by_name(ifc_class).all_attributes():
|
||||
import_attribute(attribute, props, data, callback=callback)
|
||||
|
||||
|
||||
@@ -292,7 +293,10 @@ def prop_with_search(
|
||||
should_click_ok: bool = False,
|
||||
original_operator_path: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
) -> bpy.types.UILayout:
|
||||
"""
|
||||
:return: Added row.
|
||||
"""
|
||||
# kwargs are layout.prop arguments (text, icon, etc.)
|
||||
row = layout.row(align=True)
|
||||
row.prop(data, prop_name, **kwargs)
|
||||
@@ -306,6 +310,7 @@ def prop_with_search(
|
||||
op.original_operator_path = original_operator_path or ""
|
||||
except TypeError: # Prop is not iterable
|
||||
pass
|
||||
return row
|
||||
|
||||
|
||||
def get_enum_items(
|
||||
@@ -372,11 +377,16 @@ def convert_property_group_from_si(property_group: bpy.types.PropertyGroup, skip
|
||||
setattr(property_group, prop_name, prop_value)
|
||||
|
||||
|
||||
def draw_filter(layout: bpy.types.UILayout, filter_groups, data, module: str) -> None:
|
||||
def draw_filter(
|
||||
layout: bpy.types.UILayout,
|
||||
filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup],
|
||||
data,
|
||||
module: str,
|
||||
) -> None:
|
||||
if not data.is_loaded:
|
||||
data.load()
|
||||
|
||||
sprops = bpy.context.scene.BIMSearchProperties
|
||||
sprops = tool.Search.get_search_props()
|
||||
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -105,7 +105,8 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_file():
|
||||
if IfcStore.file is None:
|
||||
IfcStore.path = cast(str, bpy.context.scene.BIMProperties.ifc_file)
|
||||
props = tool.Blender.get_bim_props()
|
||||
IfcStore.path = props.ifc_file
|
||||
# Interpret relative paths as relative to .blend file.
|
||||
if IfcStore.path and not os.path.isabs(IfcStore.path):
|
||||
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
|
||||
@@ -119,10 +120,11 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_cache():
|
||||
if IfcStore.cache is None and IfcStore.path:
|
||||
props = tool.Blender.get_bim_props()
|
||||
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
os.makedirs(bpy.context.scene.BIMProperties.cache_dir, exist_ok=True)
|
||||
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
os.makedirs(props.cache_dir, exist_ok=True)
|
||||
IfcStore.cache_path = os.path.join(props.cache_dir, f"{ifc_hash}.h5")
|
||||
cache_path = Path(IfcStore.cache_path)
|
||||
cache_settings = ifcopenshell.geom.settings()
|
||||
serializer_settings = ifcopenshell.geom.serializer_settings()
|
||||
@@ -162,7 +164,8 @@ class IfcStore:
|
||||
assert IfcStore.file
|
||||
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
props = tool.Blender.get_bim_props()
|
||||
new_cache_path = os.path.join(props.cache_dir, f"{ifc_hash}.h5")
|
||||
IfcStore.cache = None
|
||||
try:
|
||||
shutil.move(IfcStore.cache_path, new_cache_path)
|
||||
@@ -178,6 +181,7 @@ class IfcStore:
|
||||
if not os.path.isfile(path):
|
||||
return
|
||||
extension = path.split(".")[-1]
|
||||
props = tool.Project.get_project_props()
|
||||
if extension.lower() == "ifczip":
|
||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||
with zipfile.ZipFile(path, "r") as zip_ref:
|
||||
@@ -187,7 +191,7 @@ class IfcStore:
|
||||
return
|
||||
elif extension.lower() == "ifcxml":
|
||||
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
|
||||
elif bpy.context.scene.BIMProjectProperties.should_stream:
|
||||
elif props.should_stream:
|
||||
IfcStore.file = ifcopenshell.open(path, should_stream=True)
|
||||
else:
|
||||
IfcStore.file = ifcopenshell.open(path)
|
||||
@@ -195,9 +199,8 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
|
||||
if IfcStore.file is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
|
||||
bpy.context.scene.BIMProjectProperties.export_schema
|
||||
)
|
||||
props = tool.Project.get_project_props()
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(props.export_schema)
|
||||
elif IfcStore.schema is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema_identifier)
|
||||
return IfcStore.schema
|
||||
@@ -247,7 +250,7 @@ class IfcStore:
|
||||
# refactor this class and deprecate usage of IfcStore in favour of
|
||||
# tools.
|
||||
if not isinstance(obj, (bpy.types.Object, bpy.types.Material)):
|
||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||
tool.Geometry.get_mesh_props(obj).ifc_definition_id = element.id()
|
||||
return
|
||||
|
||||
existing_obj = IfcStore.id_map.get(element.id(), None)
|
||||
@@ -262,9 +265,11 @@ class IfcStore:
|
||||
IfcStore.guid_map[global_id] = obj
|
||||
|
||||
if element.is_a("IfcSurfaceStyle"):
|
||||
obj.BIMStyleProperties.ifc_definition_id = element.id()
|
||||
props = tool.Style.get_material_style_props(obj)
|
||||
props.ifc_definition_id = element.id()
|
||||
else:
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
props.ifc_definition_id = element.id()
|
||||
|
||||
tool.Ifc.setup_listeners(obj)
|
||||
|
||||
@@ -402,15 +407,24 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def purge_blender_ifc_data(obj: IFC_CONNECTED_TYPE) -> None:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
obj.BIMStyleProperties.ifc_definition_id = 0
|
||||
props = tool.Style.get_material_style_props(obj)
|
||||
props.ifc_definition_id = 0
|
||||
else: # bpy.types.Object
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
props.ifc_definition_id = 0
|
||||
|
||||
@staticmethod
|
||||
def execute_ifc_operator(operator: tool.Ifc.Operator, context: bpy.types.Context, is_invoke=False) -> set[str]:
|
||||
def execute_ifc_operator(
|
||||
operator: tool.Ifc.Operator,
|
||||
context: bpy.types.Context,
|
||||
event=None,
|
||||
method: Literal["EXECUTE", "INVOKE", "MODAL"] = "EXECUTE",
|
||||
) -> set[str]:
|
||||
bonsai.last_actions.append({"type": "operator", "name": operator.bl_idname})
|
||||
bpy.context.scene.BIMProperties.is_dirty = True
|
||||
is_top_level_operator = not bool(IfcStore.current_transaction)
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.is_dirty = True
|
||||
# Modals don't nest, and Blender handles the loop that continuously calls modal()
|
||||
is_top_level_operator = not bool(IfcStore.current_transaction) or (method == "MODAL")
|
||||
|
||||
if is_top_level_operator:
|
||||
IfcStore.begin_transaction(operator)
|
||||
@@ -436,10 +450,12 @@ class IfcStore:
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
|
||||
try:
|
||||
if is_invoke:
|
||||
result = getattr(operator, "_invoke")(context, None)
|
||||
else:
|
||||
if method == "EXECUTE":
|
||||
result = getattr(operator, "_execute")(context)
|
||||
elif method == "INVOKE":
|
||||
result = getattr(operator, "_invoke")(context, event)
|
||||
elif method == "MODAL":
|
||||
result = getattr(operator, "_modal")(context, event)
|
||||
except:
|
||||
bonsai.last_error = traceback.format_exc()
|
||||
# Try to ensure undo will work since Blender undo does work in case of errors.
|
||||
@@ -451,12 +467,27 @@ class IfcStore:
|
||||
end_top_level_operator()
|
||||
raise
|
||||
|
||||
end_top_level_operator()
|
||||
if method == "MODAL":
|
||||
if result == {"FINISHED"}:
|
||||
end_top_level_operator()
|
||||
elif result == {"CANCELLED"}:
|
||||
# Please read the docs: https://docs.blender.org/api/current/bpy.types.Operator.html
|
||||
# > "when an operator returns {'CANCELLED'}, no undo step will be created".
|
||||
# This means that if your modal edits IFC data, then the user
|
||||
# cancels it, Blender's undo history will not be in sync with
|
||||
# Bonsai / IfcOpenShell's undo history. Instead of hoping for
|
||||
# Bonsai devs to remember to handle the "cancel" state (i.e.
|
||||
# detect escape keypress) and return {"FINISHED"}, we instead
|
||||
# always enforce an undo step.
|
||||
bpy.ops.ed.undo_push(message=f"Cancel {operator.bl_idname}")
|
||||
end_top_level_operator()
|
||||
else:
|
||||
end_top_level_operator()
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def begin_transaction(operator: tool.Ifc.Operator) -> None:
|
||||
IfcStore.current_transaction = str(uuid.uuid4())
|
||||
IfcStore.current_transaction = str(uuid.uuid4()) + operator.__class__.__name__
|
||||
operator.transaction_key = IfcStore.current_transaction
|
||||
|
||||
@staticmethod
|
||||
@@ -470,7 +501,8 @@ class IfcStore:
|
||||
) -> None:
|
||||
key = getattr(operator, "transaction_key", None)
|
||||
data = getattr(operator, "transaction_data", None)
|
||||
bpy.context.scene.BIMProperties.last_transaction = key
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.last_transaction = key
|
||||
IfcStore.last_transaction = key
|
||||
rollback = rollback or getattr(operator, "rollback", lambda data: True)
|
||||
commit = commit or getattr(operator, "commit", lambda data: True)
|
||||
|
||||
@@ -20,10 +20,12 @@ from __future__ import annotations
|
||||
import bpy
|
||||
import time
|
||||
import json
|
||||
import ifcpatch
|
||||
import logging
|
||||
import traceback
|
||||
import mathutils
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
@@ -36,7 +38,8 @@ import ifcopenshell.util.shape
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
|
||||
from bonsai.tool.loader import OBJECT_DATA_TYPE
|
||||
from typing import Dict, Union, Optional, Any
|
||||
from typing import Dict, Union, Optional, Any, Literal
|
||||
from ifcopenshell.util.shape import MatrixType
|
||||
|
||||
|
||||
class MaterialCreator:
|
||||
@@ -68,6 +71,9 @@ class MaterialCreator:
|
||||
if isinstance(mesh, bpy.types.Curve):
|
||||
return
|
||||
|
||||
if mesh.get("has_layer_styles", None) == True:
|
||||
return
|
||||
|
||||
self.mesh = mesh
|
||||
self.obj = obj
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
@@ -81,15 +87,16 @@ class MaterialCreator:
|
||||
|
||||
def load_existing_materials(self) -> None:
|
||||
for material in bpy.data.materials:
|
||||
if ifc_definition_id := material.BIMStyleProperties.ifc_definition_id:
|
||||
if ifc_definition_id := tool.Blender.get_ifc_definition_id(material):
|
||||
self.styles[ifc_definition_id] = material
|
||||
|
||||
def parse_element_type_material_styles(self, element: ifcopenshell.entity_instance) -> None:
|
||||
if self.mesh["ios_materials"]:
|
||||
return # Already has materials assign to the representation itself
|
||||
return # Already has materials assign to the representation itself
|
||||
# Otherwise, we need to check for material styles on the element, since
|
||||
# create_shape on types only works on representations.
|
||||
context = tool.Ifc.get().by_id(self.mesh.BIMMeshProperties.ifc_definition_id).ContextOfItems
|
||||
mprops = tool.Geometry.get_mesh_props(self.mesh)
|
||||
context = tool.Ifc.get().by_id(mprops.ifc_definition_id).ContextOfItems
|
||||
for material in ifcopenshell.util.element.get_materials(element):
|
||||
if style := ifcopenshell.util.representation.get_material_style(material, context):
|
||||
self.mesh["ios_materials"] = (style.id(),)
|
||||
@@ -167,7 +174,7 @@ class MaterialCreator:
|
||||
if -1 in self.mesh["ios_material_ids"]:
|
||||
material_to_slot[-1] = get_empty_slot_index()
|
||||
|
||||
material_index = [material_to_slot[mat_id] for mat_id in self.mesh["ios_material_ids"]]
|
||||
material_index = np.array([material_to_slot[mat_id] for mat_id in self.mesh["ios_material_ids"]], dtype="I")
|
||||
self.mesh.polygons.foreach_set("material_index", material_index)
|
||||
|
||||
def resolve_all_stylable_representation_items(
|
||||
@@ -217,7 +224,7 @@ class IfcImporter:
|
||||
self.progress = 0
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
classes_to_wireframe_str = bpy.context.scene.DocProperties.classes_to_wireframe
|
||||
classes_to_wireframe_str = tool.Drawing.get_document_props().classes_to_wireframe
|
||||
self.classes_to_wireframe_list = [word.strip() for word in classes_to_wireframe_str.split(",")]
|
||||
|
||||
def profile_code(self, message: str) -> None:
|
||||
@@ -433,7 +440,7 @@ class IfcImporter:
|
||||
return False
|
||||
|
||||
def calculate_model_offset(self) -> None:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if self.ifc_import_settings.false_origin_mode == "MANUAL":
|
||||
tool.Loader.set_manual_blender_offset(self.file)
|
||||
elif self.ifc_import_settings.false_origin_mode == "AUTOMATIC":
|
||||
@@ -474,14 +481,16 @@ class IfcImporter:
|
||||
if grid.WAxes:
|
||||
self.create_grid_axes(grid.WAxes, grid_obj, grid_placement)
|
||||
|
||||
def create_grid_axes(self, axes, grid_obj, grid_placement):
|
||||
def create_grid_axes(
|
||||
self, axes: list[ifcopenshell.entity_instance], grid_obj: bpy.types.Object, grid_placement: MatrixType
|
||||
) -> None:
|
||||
for axis in axes:
|
||||
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
||||
mesh = self.create_mesh(axis, shape)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh)
|
||||
obj.show_in_front = True
|
||||
self.link_element(axis, obj)
|
||||
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, grid_placement.copy()))
|
||||
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, grid_placement))
|
||||
|
||||
def create_element_types(self):
|
||||
for element_type in self.element_types:
|
||||
@@ -506,8 +515,9 @@ class IfcImporter:
|
||||
pass
|
||||
elif shape:
|
||||
mesh = self.create_mesh(element, shape)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
if mesh is not None:
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
else:
|
||||
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
||||
break
|
||||
@@ -588,6 +598,7 @@ class IfcImporter:
|
||||
obj.hide_select = True
|
||||
|
||||
def create_generic_sqlite_elements(self, elements: set[ifcopenshell.entity_instance]) -> None:
|
||||
assert isinstance(self.file, ifcopenshell.sql.sqlite)
|
||||
self.geometry_cache = self.file.get_geometry([e.id() for e in elements])
|
||||
for geometry_id, geometry in self.geometry_cache["geometry"].items():
|
||||
mesh_name = tool.Loader.get_mesh_name_from_shape(type("Geometry", (), {"id": geometry_id}))
|
||||
@@ -597,21 +608,9 @@ class IfcImporter:
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
if geometry["faces"]:
|
||||
num_vertices = len(verts) // 3
|
||||
total_faces = len(geometry["faces"])
|
||||
loop_start = range(0, total_faces, 3)
|
||||
num_loops = total_faces // 3
|
||||
loop_total = [3] * num_loops
|
||||
num_vertex_indices = len(geometry["faces"])
|
||||
|
||||
mesh.vertices.add(num_vertices)
|
||||
mesh.vertices.foreach_set("co", verts)
|
||||
mesh.loops.add(num_vertex_indices)
|
||||
mesh.loops.foreach_set("vertex_index", geometry["faces"])
|
||||
mesh.polygons.add(num_loops)
|
||||
mesh.polygons.foreach_set("loop_start", loop_start)
|
||||
mesh.polygons.foreach_set("loop_total", loop_total)
|
||||
mesh.update()
|
||||
mesh = tool.Loader.create_mesh_from_shape(
|
||||
mesh=mesh, faces=geometry["faces"].reshape(-1, 3), verts=verts.reshape(-1, 3)
|
||||
)
|
||||
else:
|
||||
e = geometry["edges"]
|
||||
v = verts
|
||||
@@ -801,8 +800,9 @@ class IfcImporter:
|
||||
materials_updated = bool(mesh)
|
||||
if mesh is None:
|
||||
mesh = self.create_mesh(element, shape)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
if mesh is not None:
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
self.meshes[mesh_name] = mesh
|
||||
else:
|
||||
mesh = None
|
||||
|
||||
@@ -813,11 +813,10 @@ class IfcImporter:
|
||||
if element.is_a(ifcclass):
|
||||
obj.display_type = "WIRE"
|
||||
|
||||
if shape:
|
||||
if shape and mesh:
|
||||
# We use numpy here because Blender mathutils.Matrix is not accurate enough
|
||||
mat = np.array(shape.transformation.matrix).reshape((4, 4), order="F")
|
||||
mat = ifcopenshell.util.shape.get_shape_matrix(shape)
|
||||
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, mat))
|
||||
assert mesh # Type checker.
|
||||
if not materials_updated:
|
||||
self.material_creator.create(element, obj, mesh, tool.Geometry.does_shape_has_openings(shape))
|
||||
elif mesh: # When does this occur?
|
||||
@@ -888,17 +887,19 @@ class IfcImporter:
|
||||
|
||||
def load_file(self):
|
||||
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
|
||||
if not bpy.context.scene.BIMProperties.ifc_file:
|
||||
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
|
||||
self.file = IfcStore.get_file()
|
||||
props = tool.Blender.get_bim_props()
|
||||
if not props.ifc_file:
|
||||
props.ifc_file = self.ifc_import_settings.input_file
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
def calculate_unit_scale(self):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
tool.Loader.set_unit_scale(self.unit_scale)
|
||||
|
||||
def set_units(self):
|
||||
def set_units(self) -> None:
|
||||
if not (assignment := self.file.by_type("IfcProject")[0].UnitsInContext):
|
||||
return # Geometry is optional in IFC
|
||||
props = tool.Blender.get_bim_props()
|
||||
for unit in assignment.Units:
|
||||
if unit.is_a("IfcNamedUnit") and unit.UnitType == "LENGTHUNIT":
|
||||
if unit.is_a("IfcSIUnit"):
|
||||
@@ -918,26 +919,27 @@ class IfcImporter:
|
||||
elif unit.is_a("IfcNamedUnit") and unit.UnitType == "AREAUNIT":
|
||||
name = unit.Name if unit.is_a("IfcSIUnit") else unit.Name.lower()
|
||||
try:
|
||||
bpy.context.scene.BIMProperties.area_unit = "{}{}".format(
|
||||
props.area_unit = "{}{}".format(
|
||||
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
|
||||
)
|
||||
except: # Probably an invalid unit.
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
props.area_unit = "SQUARE_METRE"
|
||||
elif unit.is_a("IfcNamedUnit") and unit.UnitType == "VOLUMEUNIT":
|
||||
name = unit.Name if unit.is_a("IfcSIUnit") else unit.Name.lower()
|
||||
try:
|
||||
bpy.context.scene.BIMProperties.volume_unit = "{}{}".format(
|
||||
props.volume_unit = "{}{}".format(
|
||||
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
|
||||
)
|
||||
except: # Probably an invalid unit.
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
props.volume_unit = "CUBIC_METRE"
|
||||
|
||||
def create_project(self):
|
||||
project = self.file.by_type("IfcProject")[0]
|
||||
self.project = {"ifc": project}
|
||||
obj = tool.Ifc.get_object(project)
|
||||
if obj:
|
||||
self.project["blender"] = obj.BIMObjectProperties.collection
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
self.project["blender"] = props.collection
|
||||
self.has_existing_project = True
|
||||
return
|
||||
self.project["blender"] = bpy.data.collections.new(
|
||||
@@ -947,10 +949,16 @@ class IfcImporter:
|
||||
obj.hide_select = True
|
||||
self.project["blender"].objects.link(obj)
|
||||
self.project["blender"].BIMCollectionProperties.obj = obj
|
||||
obj.BIMObjectProperties.collection = self.collections[project.GlobalId] = self.project["blender"]
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
props.collection = self.collections[project.GlobalId] = self.project["blender"]
|
||||
|
||||
def create_styles(self) -> None:
|
||||
for style in self.file.by_type("IfcSurfaceStyle"):
|
||||
styles = self.file.by_type("IfcSurfaceStyle")
|
||||
if len(styles) > self.ifc_import_settings.style_limit: # Probably something strange happening
|
||||
print("Warning! Excessive styles were found and merged where applicable.")
|
||||
ifcpatch.execute({"file": self.file, "recipe": "MergeStyles", "arguments": []})
|
||||
styles = self.file.by_type("IfcSurfaceStyle")
|
||||
for style in styles:
|
||||
self.create_style(style)
|
||||
|
||||
def create_style(self, style: ifcopenshell.entity_instance) -> None:
|
||||
@@ -963,10 +971,11 @@ class IfcImporter:
|
||||
self.material_creator.styles[style.id()] = blender_material
|
||||
|
||||
style_elements = tool.Style.get_style_elements(blender_material)
|
||||
props = tool.Style.get_material_style_props(blender_material)
|
||||
if tool.Style.has_blender_external_style(style_elements):
|
||||
blender_material.BIMStyleProperties.active_style_type = "External"
|
||||
props.active_style_type = "External"
|
||||
else:
|
||||
blender_material.BIMStyleProperties.active_style_type = "Shading"
|
||||
props.active_style_type = "Shading"
|
||||
|
||||
def place_objects_in_collections(self) -> None:
|
||||
for ifc_definition_id, obj in self.added_data.items():
|
||||
@@ -974,10 +983,10 @@ class IfcImporter:
|
||||
tool.Collector.assign(obj, should_clean_users_collection=False)
|
||||
|
||||
def is_curve_annotation(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
object_type = element.ObjectType
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
return (
|
||||
object_type in tool.Drawing.ANNOTATION_TYPES_DATA
|
||||
and tool.Drawing.ANNOTATION_TYPES_DATA[object_type][3] == "curve"
|
||||
and tool.Drawing.ANNOTATION_TYPES_DATA[object_type].data_type == "curve"
|
||||
)
|
||||
|
||||
def get_drawing_group(self, element):
|
||||
@@ -1030,8 +1039,8 @@ class IfcImporter:
|
||||
self,
|
||||
element: ifcopenshell.entity_instance,
|
||||
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
|
||||
cartesian_point_offset=None,
|
||||
) -> bpy.types.Mesh:
|
||||
cartesian_point_offset: Union[npt.NDArray[np.float64], Literal[False]] = None,
|
||||
) -> Union[bpy.types.Mesh, None]:
|
||||
try:
|
||||
if hasattr(shape, "geometry"):
|
||||
# shape is ShapeElementType
|
||||
@@ -1046,40 +1055,33 @@ class IfcImporter:
|
||||
old_mesh.name = mesh_name + ".old"
|
||||
mesh = bpy.data.meshes.new(mesh_name)
|
||||
|
||||
verts = ifcopenshell.util.shape.get_vertices(geometry)
|
||||
if cartesian_point_offset is False:
|
||||
verts = geometry.verts
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
elif cartesian_point_offset is not None:
|
||||
verts_array = np.array(geometry.verts)
|
||||
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
verts = offset_verts.tolist()
|
||||
verts = verts - cartesian_point_offset
|
||||
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = (
|
||||
f"{cartesian_point_offset[0]},{cartesian_point_offset[1]},{cartesian_point_offset[2]}"
|
||||
)
|
||||
elif geometry.verts and tool.Loader.is_point_far_away(
|
||||
(geometry.verts[0], geometry.verts[1], geometry.verts[2]), is_meters=True
|
||||
):
|
||||
elif verts.size and tool.Loader.is_point_far_away(verts[0], is_meters=True):
|
||||
# Shift geometry close to the origin based off that first vert it found
|
||||
verts_array = np.array(geometry.verts)
|
||||
offset = np.array([-geometry.verts[0], -geometry.verts[1], -geometry.verts[2]])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
verts = offset_verts.tolist()
|
||||
offset = verts[0]
|
||||
verts = verts - offset
|
||||
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = f"{geometry.verts[0]},{geometry.verts[1]},{geometry.verts[2]}"
|
||||
mesh["cartesian_point_offset"] = f"{offset[0]},{offset[1]},{offset[2]}"
|
||||
else:
|
||||
verts = geometry.verts
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
return tool.Loader.convert_geometry_to_mesh(
|
||||
mesh = tool.Loader.convert_geometry_to_mesh(
|
||||
geometry,
|
||||
mesh,
|
||||
verts=verts,
|
||||
load_indexed_maps=self.ifc_import_settings.load_indexed_maps,
|
||||
)
|
||||
return tool.Loader.slice_layerset_mesh(element, mesh)
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
|
||||
import traceback
|
||||
@@ -1087,9 +1089,10 @@ class IfcImporter:
|
||||
print(traceback.format_exc())
|
||||
|
||||
def set_default_context(self):
|
||||
rprops = tool.Root.get_root_props()
|
||||
for subcontext in self.file.by_type("IfcGeometricRepresentationSubContext"):
|
||||
if subcontext.ContextIdentifier == "Body":
|
||||
bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id())
|
||||
rprops.contexts = str(subcontext.id())
|
||||
break
|
||||
|
||||
def link_element(self, element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
|
||||
@@ -1135,6 +1138,7 @@ class IfcImportSettings:
|
||||
self.deflection_tolerance = 0.001
|
||||
self.angular_tolerance = 0.5
|
||||
self.void_limit = 30
|
||||
self.style_limit = 300
|
||||
# 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
|
||||
self.distance_limit = 1000
|
||||
@@ -1172,6 +1176,7 @@ class IfcImportSettings:
|
||||
settings.deflection_tolerance = props.deflection_tolerance
|
||||
settings.angular_tolerance = props.angular_tolerance
|
||||
settings.void_limit = props.void_limit
|
||||
settings.style_limit = props.style_limit
|
||||
settings.distance_limit = props.distance_limit
|
||||
settings.false_origin_mode = props.false_origin_mode
|
||||
try:
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell.util.element
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.aggregate as core
|
||||
import bonsai.core.spatial
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -34,12 +34,12 @@ class BIM_PT_aggregate(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
props = context.active_object.BIMObjectProperties
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
@@ -66,9 +66,9 @@ class BIM_PT_aggregate(Panel):
|
||||
col.enabled = False
|
||||
op = col.operator("bim.aggregate_assign_object", icon="CHECKMARK")
|
||||
if props.relating_object:
|
||||
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
|
||||
op.relating_object = tool.Blender.get_object_bim_props(props.relating_object).ifc_definition_id
|
||||
elif props.related_object:
|
||||
op.related_object = props.related_object.BIMObjectProperties.ifc_definition_id
|
||||
op.related_object = tool.Blender.get_object_bim_props(props.related_object).ifc_definition_id
|
||||
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
|
||||
return
|
||||
else:
|
||||
@@ -115,14 +115,14 @@ class BIM_PT_linked_aggregate(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
props = context.active_object.BIMObjectProperties
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.attribute as core
|
||||
import bonsai.core.spatial
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def get_objs_for_operation(operator_properties, context):
|
||||
@@ -117,7 +116,7 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = tool.Blender.get_active_object(is_selected=False)
|
||||
if not (element := tool.Ifc.get_entity(obj)):
|
||||
return
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.bim.helper
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.attribute.data import AttributesData
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class AuginCreateNewModel(bpy.types.Operator):
|
||||
context.scene.collection.objects.link(cam_obj)
|
||||
context.scene.camera = cam_obj
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
tmpdir = tempfile.mkdtemp(dir=tool.Blender.get_addon_preferences().tmp_dir or None)
|
||||
thumb_path = os.path.join(tmpdir, "thumb.png")
|
||||
context.scene.render.image_settings.file_format = "PNG"
|
||||
context.scene.render.filepath = thumb_path
|
||||
@@ -122,15 +122,16 @@ class AuginCreateNewModel(bpy.types.Operator):
|
||||
context.scene.render.image_settings.file_format = old_file_format
|
||||
context.scene.render.filepath = old_filepath
|
||||
|
||||
client.upload_file(context.scene.BIMProperties.ifc_file, result["s3_bucket"], result["model_path"])
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
client.upload_file(bim_props.ifc_file, result["s3_bucket"], result["model_path"])
|
||||
client.upload_file(thumb_path, result["s3_bucket"], result["thumb_path"])
|
||||
|
||||
# Notify done
|
||||
url = "https://server.auge.pro.br/API/v3/augin_rest.php/files_uploaded"
|
||||
payload = {
|
||||
"user_token": props.token,
|
||||
"ifc_filesize": os.path.getsize(context.scene.BIMProperties.ifc_file),
|
||||
"model_filesize": os.path.getsize(context.scene.BIMProperties.ifc_file),
|
||||
"ifc_filesize": os.path.getsize(bim_props.ifc_file),
|
||||
"model_filesize": os.path.getsize(bim_props.ifc_file),
|
||||
"thumb_filesize": os.path.getsize(thumb_path),
|
||||
"model_upload_path": result["model_path"],
|
||||
"thumb_upload_path": result["thumb_path"],
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy.types
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_augin(bpy.types.Panel):
|
||||
@@ -47,7 +48,8 @@ class BIM_PT_augin(bpy.types.Panel):
|
||||
row = layout.row()
|
||||
row.label(text="Logged in as " + props.username)
|
||||
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not bim_props.ifc_file:
|
||||
row = layout.row()
|
||||
row.label(text="No IFC Found")
|
||||
return
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import bcf
|
||||
import bcf.bcfxml
|
||||
import bcf.v2.bcfxml
|
||||
import bonsai.tool as tool
|
||||
from typing import Union
|
||||
|
||||
|
||||
@@ -30,7 +31,8 @@ class BcfStore:
|
||||
@classmethod
|
||||
def get_bcfxml(cls) -> Union[bcf.bcfxml.BcfXml, None]:
|
||||
if not cls.bcfxml:
|
||||
bcf_filepath = bpy.context.scene.BCFProperties.bcf_file
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
bcf_filepath = props.bcf_file
|
||||
if not os.path.isabs(bcf_filepath):
|
||||
bcf_filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), bcf_filepath))
|
||||
if bcf_filepath:
|
||||
@@ -46,7 +48,7 @@ class BcfStore:
|
||||
@classmethod
|
||||
def set(cls, bcfxml: Union[bcf.bcfxml.BcfXml, None], filepath: str) -> None:
|
||||
cls.bcfxml = bcfxml
|
||||
props = bpy.context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.bcf_file = filepath
|
||||
|
||||
# Set bcf_version prop on load.
|
||||
|
||||
@@ -43,7 +43,6 @@ import bonsai.tool as tool
|
||||
import bonsai.bim.module.bcf.prop as bcf_prop
|
||||
import bonsai.bim.module.bcf.bcfstore as bcfstore
|
||||
from pathlib import Path
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import radians, degrees, atan, tan, cos, sin
|
||||
from mathutils import Vector, Matrix, Euler, geometry
|
||||
from xsdata.models.datatype import XmlDateTime
|
||||
@@ -55,7 +54,7 @@ class NewBcfProject(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
bcf_v2 = props.bcf_version == "2"
|
||||
bcf_class = bcf.v2.bcfxml.BcfXml if bcf_v2 else bcf.v3.bcfxml.BcfXml
|
||||
bcfxml = bcf_class.create_new("New Project")
|
||||
@@ -103,7 +102,8 @@ class LoadBcfProject(bpy.types.Operator):
|
||||
assert bcfxml.project
|
||||
if bcfxml.project.name is None:
|
||||
bcfxml.project.name = nameless
|
||||
context.scene.BCFProperties.name = bcfxml.project.name
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.name = bcfxml.project.name
|
||||
bpy.ops.bim.load_bcf_topics()
|
||||
self.report({"INFO"}, f"BCF Project '{Path(self.filepath).name}' is loaded.")
|
||||
return {"FINISHED"}
|
||||
@@ -131,7 +131,7 @@ class LoadBcfTopics(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.topics.clear()
|
||||
# workaround, one non standard topic would break reading entire bcf
|
||||
# ignored these topics ATM
|
||||
@@ -163,7 +163,8 @@ class LoadBcfTopic(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
topic = bcfxml.topics[self.topic_guid]
|
||||
bcfxml.get_header(self.topic_guid)
|
||||
new = context.scene.BCFProperties.topics[self.topic_index]
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
new = props.topics[self.topic_index]
|
||||
data_map = {
|
||||
"name": topic.guid,
|
||||
"title": topic.topic.title,
|
||||
@@ -244,7 +245,8 @@ class LoadBcfComments(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
blender_topic = context.scene.BCFProperties.topics.get(self.topic_guid)
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.topics.get(self.topic_guid)
|
||||
blender_topic.comments.clear()
|
||||
for comment in bcfxml.topics[self.topic_guid].comments:
|
||||
new = blender_topic.comments.add()
|
||||
@@ -274,7 +276,9 @@ class EditBcfProjectName(bpy.types.Operator):
|
||||
|
||||
# Bonsai creates default project on load.
|
||||
assert bcfxml.project
|
||||
bcfxml.project.name = context.scene.BCFProperties.name
|
||||
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
bcfxml.project.name = props.name
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -284,7 +288,7 @@ class EditBcfTopicName(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
@@ -300,7 +304,7 @@ class EditBcfTopic(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
@@ -371,13 +375,15 @@ class AddBcfTopic(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.author
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return props.author
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
bcfxml.add_topic("New Topic", "", context.scene.BCFProperties.author)
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
bcfxml.add_topic("New Topic", "", props.author)
|
||||
bpy.ops.bim.load_bcf_topics()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -389,7 +395,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props_are_filled = all(
|
||||
(getattr(props, attr) for attr in ("bim_snippet_reference", "bim_snippet_schema", "bim_snippet_type"))
|
||||
)
|
||||
@@ -403,7 +409,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
is_external = "://" in props.bim_snippet_reference
|
||||
@@ -435,7 +441,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
related_topics = tool.Bcf.get_topic_related_topics(topic)
|
||||
@@ -462,14 +468,15 @@ class AddBcfHeaderFile(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.file_reference
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return props.file_reference
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
|
||||
@@ -519,9 +526,10 @@ class ViewBcfTopic(bpy.types.Operator):
|
||||
topic_guid: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
for index, topic in enumerate(context.scene.BCFProperties.topics):
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
for index, topic in enumerate(props.topics):
|
||||
if topic.name.lower() == self.topic_guid.lower():
|
||||
context.scene.BCFProperties.active_topic_index = index
|
||||
props.active_topic_index = index
|
||||
break
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -546,7 +554,7 @@ class AddBcfViewpoint(bpy.types.Operator):
|
||||
blender_camera = context.scene.camera
|
||||
assert blender_camera
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
|
||||
@@ -675,7 +683,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
if not bcfxml:
|
||||
return False
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
topic = props.active_topic
|
||||
if not topic:
|
||||
return False
|
||||
@@ -690,7 +698,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
del topic.viewpoints[blender_topic.viewpoints]
|
||||
@@ -715,7 +723,7 @@ class RemoveBcfFile(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
header_files = tool.Bcf.get_topic_header_files(topic)
|
||||
@@ -733,13 +741,14 @@ class RemoveBcfTopic(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.topics
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return props.topics
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
del bcfxml.topics[props.active_topic.name]
|
||||
bpy.ops.bim.load_bcf_topics()
|
||||
return {"FINISHED"}
|
||||
@@ -752,13 +761,14 @@ class AddBcfReferenceLink(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.reference_link
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return bool(props.reference_link)
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
reference_links = tool.Bcf.get_topic_reference_links(topic)
|
||||
@@ -776,14 +786,15 @@ class AddBcfDocumentReference(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.document_reference
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return bool(props.document_reference)
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
|
||||
@@ -855,13 +866,14 @@ class AddBcfLabel(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BCFProperties.label
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
return bool(props.label)
|
||||
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
new = blender_topic.labels.add()
|
||||
@@ -883,7 +895,7 @@ class EditBcfReferenceLinks(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
reference_links = [r.name for r in blender_topic.reference_links]
|
||||
@@ -900,7 +912,7 @@ class EditBcfLabels(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
labels = [l.name for l in blender_topic.labels]
|
||||
@@ -918,7 +930,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
reference_links = tool.Bcf.get_topic_reference_links(topic)
|
||||
@@ -938,7 +950,7 @@ class RemoveBcfLabel(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
labels = tool.Bcf.get_topic_labels(topic)
|
||||
@@ -957,7 +969,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
tool.Bcf.set_topic_bim_snippet(topic, None)
|
||||
@@ -978,7 +990,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
|
||||
@@ -1043,7 +1055,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
related_topics = tool.Bcf.get_topic_related_topics(topic)
|
||||
@@ -1063,7 +1075,7 @@ class RemoveBcfComment(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
comments = topic.comments
|
||||
@@ -1084,7 +1096,7 @@ class EditBcfComment(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
blender_comment = blender_topic.comments.get(self.comment_guid)
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
@@ -1092,7 +1104,7 @@ class EditBcfComment(bpy.types.Operator):
|
||||
if comment.guid == self.comment_guid:
|
||||
comment.comment = blender_comment.comment
|
||||
comment.modified_date = XmlDateTime.now()
|
||||
comment.modified_author = context.scene.BCFProperties.author
|
||||
comment.modified_author = props.author
|
||||
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1105,7 +1117,7 @@ class AddBcfComment(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
if not props.comment:
|
||||
cls.poll_message_set("No comment to add.")
|
||||
return False
|
||||
@@ -1128,7 +1140,7 @@ class AddBcfComment(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
comments = topic.comments
|
||||
@@ -1136,7 +1148,7 @@ class AddBcfComment(bpy.types.Operator):
|
||||
if bcf_v2:
|
||||
comment = bcf.v2.model.Comment(
|
||||
date=XmlDateTime.now(),
|
||||
author=context.scene.BCFProperties.author,
|
||||
author=props.author,
|
||||
comment=props.comment,
|
||||
guid=str(uuid.uuid4()),
|
||||
)
|
||||
@@ -1149,7 +1161,7 @@ class AddBcfComment(bpy.types.Operator):
|
||||
else:
|
||||
comment = bcf.v3.model.Comment(
|
||||
date=XmlDateTime.now(),
|
||||
author=context.scene.BCFProperties.author,
|
||||
author=props.author,
|
||||
comment=props.comment,
|
||||
guid=str(uuid.uuid4()),
|
||||
)
|
||||
@@ -1179,7 +1191,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
if blender_topic is None:
|
||||
cls.poll_message_set("No topic is active.")
|
||||
@@ -1193,11 +1205,11 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
blender_topic = props.active_topic
|
||||
topic = bcfxml.topics[blender_topic.name]
|
||||
if self.viewpoint_guid:
|
||||
@@ -1303,7 +1315,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
@@ -1342,7 +1354,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
|
||||
objs: list[bpy.types.Object] = []
|
||||
for global_id in exception_global_ids:
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj and context.view_layer.objects.get(obj.name):
|
||||
assert isinstance(obj, bpy.types.Object)
|
||||
objs.append(obj)
|
||||
@@ -1401,7 +1413,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
return
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for global_id in selected_global_ids:
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
obj.hide_set(False)
|
||||
@@ -1414,7 +1426,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
for acomponent in acoloring.component:
|
||||
global_id_colours.setdefault(acomponent.ifc_guid, acoloring.color)
|
||||
for global_id, color in global_id_colours.items():
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj:
|
||||
obj.color = self.hex_to_rgb(color)
|
||||
|
||||
@@ -1517,7 +1529,8 @@ class OpenBcfReferenceLink(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open(context.scene.BCFProperties.topic_links[self.index].name)
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
webbrowser.open(props.topic_links[self.index].name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1530,7 +1543,8 @@ class SelectBcfHeaderFile(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
if self.filepath:
|
||||
context.scene.BCFProperties.file_reference = self.filepath
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.file_reference = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -1546,7 +1560,8 @@ class SelectBcfBimSnippetReference(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
if self.filepath:
|
||||
context.scene.BCFProperties.bim_snippet_reference = self.filepath
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.bim_snippet_reference = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -1562,7 +1577,8 @@ class SelectBcfDocumentReference(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
if self.filepath:
|
||||
context.scene.BCFProperties.document_reference = self.filepath
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
props.document_reference = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -1585,7 +1601,8 @@ class LoadBcfHeaderIfcFile(bpy.types.Operator):
|
||||
assert bcfxml
|
||||
|
||||
bcf_path = tool.Bcf.get_path()
|
||||
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
topic = bcfxml.topics[props.active_topic.name]
|
||||
entity = tool.Bcf.get_topic_header_files(topic)[self.index]
|
||||
ifc_path = bcf.agnostic.topic.extract_file(topic, entity)
|
||||
bpy.ops.bim.load_project(filepath=ifc_path)
|
||||
@@ -1605,7 +1622,8 @@ class ExtractBcfFile(bpy.types.Operator):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
topic = bcfxml.topics[props.active_topic.name]
|
||||
|
||||
if self.entity_type == "HEADER_FILE":
|
||||
entity = tool.Bcf.get_topic_header_files(topic)[self.index]
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from . import bcfstore
|
||||
from bonsai.bim.prop import StrProperty
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -34,6 +35,7 @@ from functools import partial
|
||||
from typing import Literal
|
||||
from typing_extensions import assert_never
|
||||
from bcf.agnostic.extensions import get_extensions_attributes
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
|
||||
bcfviewpoints_enum = None
|
||||
@@ -95,7 +97,7 @@ def getBcfViewpoints(self, context, force_update=False):
|
||||
global bcfviewpoints_enum
|
||||
if bcfviewpoints_enum is None or force_update: # Retrieving Viewpoints is slow. Make sure we only do when needed
|
||||
bcfviewpoints_enum = []
|
||||
props = context.scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
topic = props.active_topic
|
||||
@@ -110,6 +112,12 @@ class BcfBimSnippet(PropertyGroup):
|
||||
type: StringProperty(name="Type")
|
||||
is_external: BoolProperty(name="Is External")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
schema: str
|
||||
reference: str
|
||||
type: str
|
||||
is_external: bool
|
||||
|
||||
|
||||
class BcfDocumentReference(PropertyGroup):
|
||||
reference: StringProperty(name="Reference")
|
||||
@@ -117,6 +125,12 @@ class BcfDocumentReference(PropertyGroup):
|
||||
guid: StringProperty(name="GUID")
|
||||
is_external: BoolProperty(name="Is External")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
reference: str
|
||||
description: str
|
||||
guid: str
|
||||
is_external: bool
|
||||
|
||||
|
||||
class BcfComment(PropertyGroup):
|
||||
name: StringProperty(name="GUID")
|
||||
@@ -128,6 +142,16 @@ class BcfComment(PropertyGroup):
|
||||
modified_author: StringProperty(name="Modified Author")
|
||||
is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfCommentIsEditable)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
date: str
|
||||
author: str
|
||||
comment: str
|
||||
viewpoint: str
|
||||
modified_date: str
|
||||
modified_author: str
|
||||
is_editable: bool
|
||||
|
||||
|
||||
def get_extensions_items(
|
||||
self: "BCFProperties", context: bpy.types.Context, edit_text: str, extensions_attr: str
|
||||
@@ -183,6 +207,30 @@ class BcfTopic(PropertyGroup):
|
||||
comments: CollectionProperty(name="Comments", type=BcfComment)
|
||||
is_editable: BoolProperty(name="Edit Topic Attributes", default=False, update=updateBcfTopicIsEditable)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
title: str
|
||||
type: str
|
||||
status: str
|
||||
priority: str
|
||||
stage: str
|
||||
creation_date: str
|
||||
creation_author: str
|
||||
modified_date: str
|
||||
modified_author: str
|
||||
assigned_to: str
|
||||
due_date: str
|
||||
description: str
|
||||
viewpoints: str
|
||||
files: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
reference_links: bpy.types.bpy_prop_collection_idprop[BcfReferenceLink]
|
||||
labels: bpy.types.bpy_prop_collection_idprop[BcfLabel]
|
||||
bim_snippet: BcfBimSnippet
|
||||
document_references: bpy.types.bpy_prop_collection_idprop[BcfDocumentReference]
|
||||
related_topics: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
comments: bpy.types.bpy_prop_collection_idprop[BcfComment]
|
||||
is_editable: bool
|
||||
|
||||
|
||||
def get_related_topics(self: "BCFProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
global RELATED_TOPICS_ENUM_ITEMS
|
||||
@@ -235,7 +283,30 @@ class BCFProperties(PropertyGroup):
|
||||
comment: StringProperty(default="", name="Comment")
|
||||
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
|
||||
|
||||
def clear_input_fields(self):
|
||||
if TYPE_CHECKING:
|
||||
bcf_file: str
|
||||
bcf_version: str
|
||||
comment_text_width: int
|
||||
name: str
|
||||
author: str
|
||||
topics: bpy.types.bpy_prop_collection_idprop[BcfTopic]
|
||||
active_topic_index: int
|
||||
file_reference: str
|
||||
file_ifc_project: str
|
||||
file_ifc_spatial_structure_element: str
|
||||
reference_link: str
|
||||
label: str
|
||||
bim_snippet_reference: str
|
||||
bim_snippet_type: str
|
||||
bim_snippet_schema: str
|
||||
document_reference: str
|
||||
document_reference_description: str
|
||||
document_description: str
|
||||
related_topic: str
|
||||
comment: str
|
||||
has_related_viewpoint: bool
|
||||
|
||||
def clear_input_fields(self) -> None:
|
||||
self.file_reference = ""
|
||||
self.file_ifc_project = ""
|
||||
self.file_ifc_spatial_structure_element = ""
|
||||
@@ -250,7 +321,7 @@ class BCFProperties(PropertyGroup):
|
||||
self.has_related_viewpoint = False
|
||||
|
||||
@property
|
||||
def active_topic(self):
|
||||
def active_topic(self) -> Union[BcfTopic, None]:
|
||||
if len(self.topics) == 0:
|
||||
return None
|
||||
if self.active_topic_index < 0:
|
||||
@@ -259,5 +330,5 @@ class BCFProperties(PropertyGroup):
|
||||
self.active_topic_index = len(self.topics) - 1
|
||||
return self.topics[self.active_topic_index]
|
||||
|
||||
def refresh_topic(self, context):
|
||||
def refresh_topic(self, context: bpy.types.Context) -> None:
|
||||
refreshBcfTopic(self, context)
|
||||
|
||||
@@ -37,8 +37,7 @@ class BIM_PT_bcf(Panel):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
scene = context.scene
|
||||
props = scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
|
||||
if not bcfstore.BcfStore.get_bcfxml():
|
||||
row = layout.row(align=True)
|
||||
@@ -63,18 +62,17 @@ class BIM_PT_bcf(Panel):
|
||||
row = layout.row()
|
||||
row.prop(props, "author")
|
||||
|
||||
props = context.scene.BCFProperties
|
||||
row = layout.row()
|
||||
row.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
|
||||
col = row.column(align=True)
|
||||
col.operator("bim.add_bcf_topic", icon="ADD", text="")
|
||||
col.operator("bim.remove_bcf_topic", icon="REMOVE", text="")
|
||||
if props.active_topic_index < len(props.topics):
|
||||
topic = props.active_topic
|
||||
|
||||
topic = props.active_topic
|
||||
if topic is not None:
|
||||
is_editable = topic.is_editable
|
||||
col.prop(topic, "is_editable", icon="CHECKMARK" if topic.is_editable else "GREASEPENCIL", icon_only=True)
|
||||
|
||||
topic = props.active_topic
|
||||
row = layout.row()
|
||||
row.enabled = is_editable
|
||||
row.prop(topic, "description", text="")
|
||||
@@ -131,8 +129,7 @@ class BIM_PT_bcf_metadata(Panel):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
scene = context.scene
|
||||
props = scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
if not bcfxml or props.active_topic_index >= len(props.topics):
|
||||
@@ -296,8 +293,7 @@ class BIM_PT_bcf_comments(Panel):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
scene = context.scene
|
||||
props = scene.BCFProperties
|
||||
props = tool.Bcf.get_bcf_props()
|
||||
|
||||
if props.active_topic_index >= len(props.topics):
|
||||
layout.label(text="No BCF project is loaded")
|
||||
|
||||
@@ -25,6 +25,7 @@ import mathutils
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.boundary
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.shape
|
||||
@@ -40,6 +41,7 @@ from bonsai.bim.module.model.decorator import ProfileDecorator
|
||||
from bonsai.bim.module.boundary.decorator import BoundaryDecorator
|
||||
import bonsai.core
|
||||
import bonsai.core.geometry
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
def disable_editing_boundary_geometry(context):
|
||||
@@ -57,7 +59,7 @@ def disable_editing_boundary_geometry(context):
|
||||
|
||||
|
||||
class Loader:
|
||||
def __init__(self, operator=None):
|
||||
def __init__(self, operator: Optional[bpy.types.Operator] = None):
|
||||
self.operator = operator
|
||||
self.ifc_file = None
|
||||
self.logger = None
|
||||
@@ -66,7 +68,7 @@ class Loader:
|
||||
self.fallback_settings = self.load_fallback_settings()
|
||||
self.load_importer()
|
||||
|
||||
def create_mesh(self, boundary):
|
||||
def create_mesh(self, boundary: ifcopenshell.entity_instance) -> Union[bpy.types.Mesh, None]:
|
||||
# ConnectionGeometry is optional in IFC schema for some reasons.
|
||||
if not boundary.ConnectionGeometry:
|
||||
return None
|
||||
@@ -113,7 +115,7 @@ class Loader:
|
||||
bm.edges.new((verts[-1], verts[0]))
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
mesh.BIMMeshProperties.ifc_definition_id = surface.id()
|
||||
tool.Ifc.link(surface, mesh)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
matrix = mathutils.Matrix(
|
||||
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
|
||||
@@ -130,7 +132,7 @@ class Loader:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
return settings
|
||||
|
||||
def load_importer(self):
|
||||
def load_importer(self) -> None:
|
||||
self.ifc_file = tool.Ifc.get()
|
||||
self.logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, IfcStore.path, self.logger)
|
||||
@@ -279,7 +281,7 @@ class SelectProjectBoundaries(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def get_colour(ifc_boundary):
|
||||
def get_colour(ifc_boundary: ifcopenshell.entity_instance) -> tuple[float, float, float, float]:
|
||||
"""Return a color depending on IfcClass given"""
|
||||
product_colors = {
|
||||
"IfcWall": (0.7, 0.3, 0, 1),
|
||||
@@ -315,7 +317,8 @@ class ColourByRelatedBuildingElement(bpy.types.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element.is_a("IfcRelSpaceBoundary"):
|
||||
@@ -416,7 +419,7 @@ class UpdateBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
tool.Boundary.move_origin_to_space_origin(context.active_object)
|
||||
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
|
||||
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
||||
ifcopenshell.api.boundary.assign_connection_geometry(tool.Ifc.get(), **settings)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -660,6 +663,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.select_set(True)
|
||||
|
||||
def auto_generate_boundaries(self, space, space_obj):
|
||||
props = tool.Model.get_model_props()
|
||||
# Identify all potential building elements
|
||||
# TODO: don't select everything, use AABB culling in Blender
|
||||
building_elements = (
|
||||
@@ -775,9 +779,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# we cheat by using the exterior boundary to mean "gross".
|
||||
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
|
||||
|
||||
parent_boundary = tool.Ifc.run(
|
||||
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
|
||||
)
|
||||
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=props.boundary_class)
|
||||
if building_element.is_a("IfcVirtualElement"):
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
|
||||
else:
|
||||
@@ -854,9 +856,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
|
||||
continue
|
||||
|
||||
boundary = tool.Ifc.run(
|
||||
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
|
||||
)
|
||||
boundary = tool.Ifc.run("root.create_entity", ifc_class=props.boundary_class)
|
||||
boundary.RelatingSpace = space
|
||||
boundary.RelatedBuildingElement = filling or opening
|
||||
boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
|
||||
@@ -877,6 +877,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not relating_space or not related_building_element:
|
||||
return
|
||||
|
||||
props = tool.Model.get_model_props()
|
||||
# Find which face on space should be bounded to related building element
|
||||
# TODO: Handle round wall where multiple faces need to be bound to the same related building element
|
||||
bm = bmesh.new()
|
||||
@@ -924,7 +925,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
):
|
||||
return
|
||||
|
||||
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
|
||||
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=props.boundary_class)
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
# Set to EXTERNAL by default and turn later to internal if there is a corresponding boundary relating to an
|
||||
# internal space
|
||||
@@ -959,7 +960,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
|
||||
connection_geometry = self.create_connection_geometry_from_polygon(opening_polygon, target_face_matrix)
|
||||
boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
|
||||
boundary = tool.Ifc.run("root.create_entity", ifc_class=props.boundary_class)
|
||||
boundary.RelatingSpace = relating_space
|
||||
boundary.RelatedBuildingElement = filling
|
||||
boundary.ConnectionGeometry = connection_geometry
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.boundary.data import SpaceBoundariesData
|
||||
|
||||
@@ -34,7 +33,7 @@ class BIM_PT_SceneBoundaries(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -53,20 +52,20 @@ class BIM_PT_Boundary(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
props = context.active_object.BIMObjectProperties
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
|
||||
entity = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
return entity.is_a("IfcRelSpaceBoundary")
|
||||
|
||||
def draw(self, context):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = obj.BIMObjectProperties
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
ifc_file = tool.Ifc.get()
|
||||
boundary = ifc_file.by_id(props.ifc_definition_id)
|
||||
self.bprops = tool.Boundary.get_object_boundary_props(obj)
|
||||
@@ -129,12 +128,12 @@ class BIM_PT_SpaceBoundaries(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
props = context.active_object.BIMObjectProperties
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
for ifc_class in ("IfcSpace", "IfcExternalSpatialElement"):
|
||||
@@ -146,7 +145,9 @@ class BIM_PT_SpaceBoundaries(Panel):
|
||||
if not SpaceBoundariesData.is_loaded:
|
||||
SpaceBoundariesData.load()
|
||||
|
||||
self.props = context.active_object.BIMObjectProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.props = tool.Blender.get_object_bim_props(obj)
|
||||
self.ifc_file = tool.Ifc.get()
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_space_boundaries")
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_bsdd(Panel):
|
||||
|
||||
@@ -128,9 +128,10 @@ class CadMitre(bpy.types.Operator):
|
||||
class CadFillet(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_fillet"
|
||||
bl_label = "CAD Fillet"
|
||||
bl_description = "Add fillet to the 2 selected edges."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resolution: bpy.props.IntProperty(name="Arc Resolution", min=0, default=1)
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.1)
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.1, subtype="DISTANCE")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -143,18 +144,15 @@ class CadFillet(bpy.types.Operator):
|
||||
layout.prop(self, prop)
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
obj = bpy.context.active_object
|
||||
cursor = obj.matrix_world.inverted() @ bpy.context.scene.cursor.location
|
||||
mesh = obj.data
|
||||
assert obj and isinstance((mesh := obj.data), bpy.types.Mesh)
|
||||
bm = bmesh.from_edit_mesh(mesh)
|
||||
selected_edges = [e for e in bm.edges if e.select]
|
||||
if len(selected_edges) != 2:
|
||||
self.report({"ERROR"}, "Exactly 2 edges should be selected.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Assume the user has selected two edges sharing a vert, but merge verts
|
||||
@@ -219,6 +217,7 @@ class CadFillet(bpy.types.Operator):
|
||||
class CadArcFrom2Points(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_arc_from_2_points"
|
||||
bl_label = "CAD Arc from 2 Points"
|
||||
bl_description = "Add an arc to the active mesh based on 2 selected vertices."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
|
||||
should_flip: bpy.props.BoolProperty(name="Flip", description="Flip arc", default=False)
|
||||
@@ -235,6 +234,7 @@ class CadArcFrom2Points(bpy.types.Operator):
|
||||
if bpy.context.mode != "EDIT_MESH":
|
||||
return {"CANCELLED"}
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
region = bpy.context.region
|
||||
region_3d = bpy.context.area.spaces.active.region_3d
|
||||
cursor = bpy.context.scene.cursor.location
|
||||
@@ -242,10 +242,12 @@ class CadArcFrom2Points(bpy.types.Operator):
|
||||
if not center:
|
||||
return {"CANCELLED"}
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
mw = obj.matrix_world
|
||||
bm = bmesh.from_edit_mesh(mesh)
|
||||
selected_verts = [v for v in bm.verts if v.select]
|
||||
if len(selected_verts) != 2:
|
||||
self.report({"ERROR"}, "Exactly 2 vertices should be selected.")
|
||||
return {"CANCELLED"}
|
||||
v1 = bpy_extras.view3d_utils.location_3d_to_region_2d(region, region_3d, mw @ selected_verts[0].co)
|
||||
v2 = bpy_extras.view3d_utils.location_3d_to_region_2d(region, region_3d, mw @ selected_verts[1].co)
|
||||
@@ -342,11 +344,12 @@ class CadArcFrom3Points(bpy.types.Operator):
|
||||
class CadOffset(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_offset"
|
||||
bl_label = "CAD Offset"
|
||||
bl_description = "Copy selected mesh geometry at provided offset. Mesh copied based on the current viewport angle."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
distance: bpy.props.FloatProperty(name="Distance", default=0.1)
|
||||
distance: bpy.props.FloatProperty(name="Distance", default=0.1, subtype="DISTANCE")
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
def poll(cls, context):
|
||||
return context.mode == "EDIT_MESH"
|
||||
|
||||
def draw(self, context):
|
||||
@@ -369,9 +372,6 @@ class CadOffset(bpy.types.Operator):
|
||||
# effect of converting an unclosed loop into a closed loop which is also
|
||||
# not what users expect.
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.distance = self.distance * si_conversion
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@@ -543,7 +543,8 @@ class CadOffset(bpy.types.Operator):
|
||||
class AddIfcCircle(bpy.types.Operator):
|
||||
bl_idname = "bim.add_ifccircle"
|
||||
bl_label = "Add IfcCircle"
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.5)
|
||||
bl_description = "Add IfcCircle to the currently active mesh."
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.5, subtype="DISTANCE")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -551,18 +552,19 @@ class AddIfcCircle(bpy.types.Operator):
|
||||
return bool(obj) and obj.type == "MESH"
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
obj = context.active_object
|
||||
assert obj and isinstance((mesh := obj.data), bpy.types.Mesh)
|
||||
self.obj = obj
|
||||
self.mesh = mesh
|
||||
if self.has_selected_existing_circle(context):
|
||||
self.change_radius(context)
|
||||
else:
|
||||
self.create_circle(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def has_selected_existing_circle(self, context):
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
def has_selected_existing_circle(self, context: bpy.types.Context) -> bool:
|
||||
obj = self.obj
|
||||
bm = bmesh.from_edit_mesh(self, mesh)
|
||||
verts = [v for v in bm.verts if v.select and not v.hide]
|
||||
if len(verts) != 2:
|
||||
return False
|
||||
@@ -575,32 +577,32 @@ class AddIfcCircle(bpy.types.Operator):
|
||||
for group in groups:
|
||||
if group in verts[0][deform_layer] and group in verts[1][deform_layer]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def change_radius(self, context):
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
def change_radius(self, context: bpy.types.Object) -> None:
|
||||
bm = bmesh.from_edit_mesh(self.mesh)
|
||||
verts = [v for v in bm.verts if v.select and not v.hide]
|
||||
center = verts[0].co.lerp(verts[1].co, 0.5)
|
||||
verts[0].co = center + ((verts[0].co - center).normalized() * self.radius)
|
||||
verts[1].co = center + ((verts[1].co - center).normalized() * self.radius)
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
bmesh.update_edit_mesh(self.mesh)
|
||||
|
||||
def create_circle(self, context):
|
||||
obj = context.active_object
|
||||
def create_circle(self, context: bpy.types.Context) -> None:
|
||||
obj = self.obj
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
# The last group may be the result of a prior run of this operator.
|
||||
# I tried looping through all groups but Blender group indices seem to behave unpredictably.
|
||||
if len(obj.vertex_groups):
|
||||
last_group = obj.vertex_groups[-1]
|
||||
verts_in_group = [v for v in obj.data.vertices if last_group.index in [vg.group for vg in v.groups]]
|
||||
verts_in_group = [v for v in self.mesh.vertices if last_group.index in [vg.group for vg in v.groups]]
|
||||
if "IFCCIRCLE" in last_group.name and len(verts_in_group) != 2:
|
||||
obj.vertex_groups.remove(last_group)
|
||||
group = obj.vertex_groups.new(name="IFCCIRCLE")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
bm = bmesh.from_edit_mesh(self.mesh)
|
||||
|
||||
bm.verts.layers.deform.verify()
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
@@ -617,7 +619,7 @@ class AddIfcCircle(bpy.types.Operator):
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
bmesh.update_edit_mesh(self.mesh)
|
||||
|
||||
|
||||
class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
@@ -638,18 +640,19 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
layout.prop(self, prop)
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
obj = context.active_object
|
||||
assert obj and isinstance((mesh := obj.data), bpy.types.Mesh)
|
||||
self.obj = obj
|
||||
self.mesh = mesh
|
||||
if self.has_selected_existing_arc(context):
|
||||
self.change_radius(context)
|
||||
else:
|
||||
self.create_arc(context)
|
||||
return self.create_arc(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def has_selected_existing_arc(self, context: bpy.types.Context) -> bool:
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
obj = self.obj
|
||||
bm = bmesh.from_edit_mesh(self.mesh)
|
||||
verts = [v for v in bm.verts if v.select and not v.hide]
|
||||
if len(verts) != 3:
|
||||
return False
|
||||
@@ -668,13 +671,14 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
return False
|
||||
|
||||
def change_radius(self, context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
obj = self.obj
|
||||
bm = bmesh.from_edit_mesh(self.mesh)
|
||||
edges = [e for e in bm.edges if e.select and not e.hide]
|
||||
|
||||
mid = list(set(edges[0].verts) & set(edges[1].verts))[0]
|
||||
v1 = edges[0].other_vert(mid)
|
||||
v2 = edges[1].other_vert(mid)
|
||||
assert v1 and v2
|
||||
center = tool.Cad.get_center_of_arc([v1.co, mid.co, v2.co])
|
||||
|
||||
if len(v1.link_edges) != 2 or len(v2.link_edges) != 2:
|
||||
@@ -682,6 +686,7 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
|
||||
v3 = v1.link_edges[1].other_vert(v1) if mid in v1.link_edges[0].verts else v1.link_edges[0].other_vert(v1)
|
||||
v4 = v2.link_edges[1].other_vert(v2) if mid in v2.link_edges[0].verts else v2.link_edges[0].other_vert(v2)
|
||||
assert v3 and v4
|
||||
dir1 = (v3.co - v1.co).normalized()
|
||||
dir2 = (v4.co - v2.co).normalized()
|
||||
|
||||
@@ -702,11 +707,10 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
|
||||
def create_arc(self, context):
|
||||
obj = bpy.context.active_object
|
||||
bmesh.update_edit_mesh(self.mesh)
|
||||
|
||||
def create_arc(self, context: bpy.types.Context) -> set[str]:
|
||||
obj = self.obj
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
# The last group may be the result of a prior run of this operator.
|
||||
# I tried looping through all groups but Blender group indices seem to behave unpredictably.
|
||||
@@ -718,7 +722,7 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
group = obj.vertex_groups.new(name="IFCARCINDEX")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
bm = bmesh.from_edit_mesh(self.mesh)
|
||||
|
||||
bm.verts.layers.deform.verify()
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
@@ -737,6 +741,7 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
shared_vert = list(set(selected_edges[0].verts) & set(selected_edges[1].verts))[0]
|
||||
v1 = selected_edges[0].other_vert(shared_vert)
|
||||
v2 = selected_edges[1].other_vert(shared_vert)
|
||||
assert v1 and v2
|
||||
dir1 = (v1.co - shared_vert.co).normalized()
|
||||
dir2 = (v2.co - shared_vert.co).normalized()
|
||||
edge_angle = dir1.angle(dir2)
|
||||
@@ -770,7 +775,8 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
bmesh.update_edit_mesh(self.mesh)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AlignViewToProfile(bpy.types.Operator):
|
||||
@@ -806,8 +812,9 @@ class AlignViewToProfile(bpy.types.Operator):
|
||||
class AddRectangle(bpy.types.Operator):
|
||||
bl_idname = "bim.add_rectangle"
|
||||
bl_label = "Add Rectangle"
|
||||
x: bpy.props.FloatProperty(name="X", default=1)
|
||||
y: bpy.props.FloatProperty(name="Y", default=1)
|
||||
bl_description = "Add rectangle shape to the active mesh."
|
||||
x: bpy.props.FloatProperty(name="X", default=1, subtype="DISTANCE")
|
||||
y: bpy.props.FloatProperty(name="Y", default=1, subtype="DISTANCE")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -815,11 +822,8 @@ class AddRectangle(bpy.types.Operator):
|
||||
return bool(obj) and obj.type == "MESH"
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.x = self.x * si_conversion
|
||||
self.y = self.y * si_conversion
|
||||
|
||||
obj = context.active_object
|
||||
assert obj and isinstance(obj.data, bpy.types.Mesh)
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
cursor = obj.matrix_world.inverted() @ context.scene.cursor.location
|
||||
new_verts = [
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bpy.types import PropertyGroup
|
||||
from math import pi
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
class BIMCadProperties(PropertyGroup):
|
||||
@@ -31,3 +32,11 @@ class BIMCadProperties(PropertyGroup):
|
||||
gable_roof_edge_angle: bpy.props.FloatProperty(
|
||||
name="Gable Roof Edge Angle", default=pi / 2, soft_min=0, soft_max=pi / 2, subtype="ANGLE"
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resolution: int
|
||||
radius: float
|
||||
distance: float
|
||||
x: float
|
||||
y: float
|
||||
gable_roof_edge_angle: float
|
||||
|
||||
@@ -23,7 +23,7 @@ import bonsai.bim.module.type.prop as type_prop
|
||||
import ifcopenshell.util.unit
|
||||
from bpy.types import WorkSpaceTool
|
||||
from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData
|
||||
from typing import Union
|
||||
from functools import partial
|
||||
|
||||
|
||||
def load_custom_icons():
|
||||
@@ -69,10 +69,12 @@ class CadTool(WorkSpaceTool):
|
||||
("bim.cad_hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, workspace_tool):
|
||||
def draw_settings(
|
||||
context: bpy.types.Context, layout: bpy.types.UILayout, workspace_tool: bpy.types.WorkSpaceTool
|
||||
) -> None:
|
||||
ui_context = str(context.region.type)
|
||||
obj = context.active_object
|
||||
if not obj or not obj.data:
|
||||
if not obj or not (data := obj.data):
|
||||
return
|
||||
is_profile = tool.Geometry.is_profile_object_active()
|
||||
if is_profile:
|
||||
@@ -112,17 +114,20 @@ class CadTool(WorkSpaceTool):
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Rectangle", "S_R", "Rectangle", ui_context)
|
||||
add_layout_hotkey_operator(row, "Rectangle", "S_R", bpy.ops.bim.add_rectangle.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Circle", "S_C", "Circle", ui_context)
|
||||
add_layout_hotkey_operator(row, "Circle", "S_C", bpy.ops.bim.add_ifccircle.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "3-Point Arc", "S_V", bpy.ops.bim.set_arc_index.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Reset Vertex", "S_X", bpy.ops.bim.reset_vertex.__doc__, ui_context)
|
||||
|
||||
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif (
|
||||
isinstance(data, tool.Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and tool.Geometry.get_mesh_props(data).subshape_type == "AXIS"
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Axis", "bim.edit_extrusion_axis", "bim.disable_editing_extrusion_axis", ui_context
|
||||
)
|
||||
@@ -133,15 +138,15 @@ class CadTool(WorkSpaceTool):
|
||||
row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context
|
||||
)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", "Fillet", ui_context)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.cad_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
|
||||
else:
|
||||
if (
|
||||
(RailingData.is_loaded or not RailingData.load())
|
||||
and RailingData.data["pset_data"]
|
||||
and context.active_object.BIMRailingProperties.is_editing_path
|
||||
and tool.Model.get_railing_props(obj).is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout,
|
||||
@@ -154,7 +159,7 @@ class CadTool(WorkSpaceTool):
|
||||
elif (
|
||||
(RoofData.is_loaded or not RoofData.load())
|
||||
and RoofData.data["pset_data"]
|
||||
and context.active_object.BIMRoofProperties.is_editing_path
|
||||
and tool.Model.get_roof_props(obj).is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context
|
||||
@@ -171,17 +176,17 @@ class CadTool(WorkSpaceTool):
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", "Offset", ui_context)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "2-Point Arc", "S_C", "2-Point Arc", ui_context)
|
||||
add_layout_hotkey_operator(row, "2-Point Arc", "S_C", bpy.ops.bim.cad_arc_from_2_points.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "3-Point Arc", "S_V", bpy.ops.bim.cad_arc_from_3_points.__doc__, ui_context)
|
||||
|
||||
|
||||
class CadHotkey(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_hotkey"
|
||||
bl_label = "CAD Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = ""
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
hotkey: bpy.props.StringProperty()
|
||||
description: bpy.props.StringProperty()
|
||||
|
||||
@@ -190,12 +195,14 @@ class CadHotkey(bpy.types.Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMCadProperties
|
||||
self.props = tool.Cad.get_cad_props()
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMCadProperties
|
||||
props = tool.Cad.get_cad_props()
|
||||
obj = context.active_object
|
||||
|
||||
if self.hotkey == "S_C":
|
||||
if tool.Geometry.is_profile_object_active():
|
||||
row = self.layout.row()
|
||||
@@ -221,7 +228,7 @@ class CadHotkey(bpy.types.Operator):
|
||||
elif (
|
||||
(RoofData.is_loaded or not RoofData.load())
|
||||
and RoofData.data["pset_data"]
|
||||
and bpy.context.active_object.BIMRoofProperties.is_editing_path
|
||||
and tool.Model.get_roof_props(obj).is_editing_path
|
||||
):
|
||||
self.layout.row().prop(props, "gable_roof_edge_angle")
|
||||
|
||||
@@ -231,9 +238,8 @@ class CadHotkey(bpy.types.Operator):
|
||||
row.prop(props, "resolution")
|
||||
|
||||
def hotkey_S_C(self):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
if tool.Geometry.is_profile_object_active():
|
||||
bpy.ops.bim.add_ifccircle(radius=self.props.radius / si_conversion)
|
||||
bpy.ops.bim.add_ifccircle(radius=self.props.radius)
|
||||
else:
|
||||
bpy.ops.bim.cad_arc_from_2_points()
|
||||
|
||||
@@ -241,36 +247,49 @@ class CadHotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.cad_trim_extend()
|
||||
|
||||
def hotkey_S_F(self):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
if tool.Geometry.is_profile_object_active():
|
||||
bpy.ops.bim.add_ifcarcindex_fillet(radius=self.props.radius / si_conversion)
|
||||
bpy.ops.bim.add_ifcarcindex_fillet(radius=self.props.radius)
|
||||
else:
|
||||
bpy.ops.bim.cad_fillet(resolution=self.props.resolution, radius=self.props.radius / si_conversion)
|
||||
bpy.ops.bim.cad_fillet(resolution=self.props.resolution, radius=self.props.radius)
|
||||
|
||||
def hotkey_S_O(self):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
bpy.ops.bim.cad_offset(distance=self.props.distance / si_conversion)
|
||||
bpy.ops.bim.cad_offset(distance=self.props.distance)
|
||||
|
||||
def hotkey_S_Q(self):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if bpy.context.active_object.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
obj = bpy.context.active_object
|
||||
|
||||
if not obj:
|
||||
return
|
||||
|
||||
if not tool.Geometry.has_mesh_properties(data := obj.data):
|
||||
return
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
if mprops.subshape_type == "PROFILE":
|
||||
if element.is_a("IfcProfileDef"):
|
||||
bpy.ops.bim.edit_arbitrary_profile()
|
||||
elif element.is_a("IfcRelSpaceBoundary"):
|
||||
bpy.ops.bim.edit_boundary_geometry()
|
||||
else:
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif mprops.subshape_type == "AXIS":
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
|
||||
def hotkey_S_R(self):
|
||||
obj = bpy.context.active_object
|
||||
if not obj:
|
||||
return
|
||||
|
||||
if tool.Geometry.is_profile_object_active():
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
bpy.ops.bim.add_rectangle(x=self.props.x / si_conversion, y=self.props.y / si_conversion)
|
||||
bpy.ops.bim.add_rectangle(x=self.props.x, y=self.props.y)
|
||||
elif (
|
||||
(RoofData.is_loaded or not RoofData.load())
|
||||
and RoofData.data["pset_data"]
|
||||
and bpy.context.active_object.BIMRoofProperties.is_editing_path
|
||||
and tool.Model.get_roof_props(obj).is_editing_path
|
||||
):
|
||||
bpy.ops.bim.set_gable_roof_edge_angle(angle=self.props.gable_roof_edge_angle)
|
||||
|
||||
@@ -288,7 +307,9 @@ class CadHotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.reset_vertex()
|
||||
|
||||
|
||||
def add_header_apply_button(layout, text, apply_operator, cancel_operator, ui_context=""):
|
||||
def add_header_apply_button(
|
||||
layout: bpy.types.UILayout, text: str, apply_operator: str, cancel_operator: str, ui_context: str = ""
|
||||
) -> None:
|
||||
custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{text} Mode", icon_value=custom_icon)
|
||||
@@ -307,34 +328,8 @@ def add_header_apply_button(layout, text, apply_operator, cancel_operator, ui_co
|
||||
row.label(text="Tools")
|
||||
|
||||
|
||||
def add_layout_hotkey_operator(
|
||||
layout: bpy.types.UILayout, text: str, hotkey: str, description: Union[str, None], ui_context: str = ""
|
||||
) -> bpy.types.OperatorProperties:
|
||||
parts = hotkey.split("_")
|
||||
modifier, key = parts
|
||||
op_text = "" if ui_context == "TOOL_HEADER" else text
|
||||
custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id
|
||||
modifier_icon, modifier_str = MODIFIERS.get(modifier, ("NONE", ""))
|
||||
|
||||
row = layout if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
op = row.operator("bim.cad_hotkey", text=op_text, icon_value=custom_icon)
|
||||
|
||||
if ui_context != "TOOL_HEADER" and len(parts) == 2:
|
||||
layout = layout.row(align=True) # Create a new line for hotkey display
|
||||
layout.label(text="", icon=modifier_icon)
|
||||
layout.label(text="", icon=f"EVENT_{key}")
|
||||
|
||||
hotkey_description = f"Hotkey: {modifier_str} {key}"
|
||||
description = "\n\n".join(filter(None, [hotkey_description if description else ""]))
|
||||
op.hotkey = hotkey
|
||||
op.description = description or hotkey_description
|
||||
|
||||
return op
|
||||
add_layout_hotkey_operator = partial(tool.Blender.add_layout_hotkey_operator, tool_name="cad", module_name=__name__)
|
||||
|
||||
|
||||
custom_icon_previews = None
|
||||
display_mode = None
|
||||
|
||||
MODIFIERS = {
|
||||
"S": ("EVENT_SHIFT", "⇧"),
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
# TODO refactor into new clash results system
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
@@ -316,13 +316,15 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
|
||||
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
if not props.ifc_definition_id:
|
||||
continue
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
@@ -334,7 +336,7 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
element_file = self.file
|
||||
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = element_file.by_id(props.ifc_definition_id)
|
||||
except:
|
||||
continue
|
||||
|
||||
@@ -478,7 +480,7 @@ class SelectSmartGroup(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
return tool.Ifc.get() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
def execute(self, context):
|
||||
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
@@ -118,7 +118,7 @@ class MaterialClassificationsData(ReferencesData):
|
||||
def references(cls):
|
||||
results = []
|
||||
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
material = props.materials[props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
|
||||
@@ -201,7 +201,8 @@ class EnableEditingClassification(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
def callback(name, prop, data):
|
||||
if name == "ReferenceTokens":
|
||||
new = bpy.context.scene.BIMGeoreferenceProperties.projected_crs.add()
|
||||
geo_props = tool.Georeference.get_georeference_props()
|
||||
new = geo_props.projected_crs.add()
|
||||
new.name = name
|
||||
new.data_type = "string"
|
||||
new.is_null = data[name] is None
|
||||
@@ -239,7 +240,7 @@ class RemoveClassification(bpy.types.Operator, tool.Ifc.Operator):
|
||||
classification: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"classification.remove_classification",
|
||||
tool.Ifc.get(),
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.classification.prop as classification_prop
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.classification.data import (
|
||||
ClassificationsData,
|
||||
ClassificationReferencesData,
|
||||
@@ -124,7 +123,7 @@ class ReferenceUI:
|
||||
self.sprops = context.scene.BIMClassificationProperties
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
self.props = context.scene.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui(context)
|
||||
|
||||
@@ -312,7 +311,7 @@ class BIM_PT_material_classifications(Panel, ReferenceUI):
|
||||
def poll(cls, context):
|
||||
if not tool.Ifc.get():
|
||||
return False
|
||||
props = context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
if props.is_editing and (material := props.active_material) and material.ifc_definition_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell.api.constraint
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadObjectives(bpy.types.Operator):
|
||||
@@ -84,7 +83,7 @@ class AddObjective(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
|
||||
result = ifcopenshell.api.run("constraint.add_objective", tool.Ifc.get())
|
||||
bpy.ops.bim.load_objectives()
|
||||
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
||||
return {"FINISHED"}
|
||||
@@ -116,7 +115,7 @@ class RemoveConstraint(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
|
||||
)
|
||||
@@ -162,8 +161,12 @@ class AssignConstraint(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
|
||||
objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects
|
||||
products = [
|
||||
self.file.by_id(obj_id)
|
||||
for obj in objs
|
||||
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
|
||||
]
|
||||
if products:
|
||||
ifcopenshell.api.run(
|
||||
"constraint.assign_constraint",
|
||||
@@ -185,8 +188,12 @@ class UnassignConstraint(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
|
||||
objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects
|
||||
products = [
|
||||
self.file.by_id(obj_id)
|
||||
for obj in objs
|
||||
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
|
||||
]
|
||||
if products:
|
||||
ifcopenshell.api.run(
|
||||
"constraint.unassign_constraint",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.constraint.data import ConstraintsData, ObjectConstraintsData
|
||||
|
||||
@@ -33,7 +33,7 @@ class BIM_PT_constraints(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not ConstraintsData.is_loaded:
|
||||
@@ -79,21 +79,21 @@ class BIM_PT_object_constraints(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
return False
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
return bool(tool.Ifc.get_object_by_identifier(props.ifc_definition_id))
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectConstraintsData.is_loaded:
|
||||
ObjectConstraintsData.load()
|
||||
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
assert obj
|
||||
self.oprops = tool.Blender.get_object_bim_props(obj)
|
||||
self.sprops = context.scene.BIMConstraintProperties
|
||||
self.props = obj.BIMObjectConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
|
||||
@@ -740,15 +740,14 @@ class LoadProductCostItems(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not tool.Ifc.get() or not (obj := context.active_object) or not (obj.BIMObjectProperties.ifc_definition_id):
|
||||
if not tool.Ifc.get() or not (obj := context.active_object) or not (tool.Blender.get_ifc_definition_id(obj)):
|
||||
cls.poll_message_set("No IFC object is active.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
core.load_product_cost_items(
|
||||
tool.Cost, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
obj = context.active_object
|
||||
core.load_product_cost_items(tool.Cost, product=tool.Ifc.get_entity(obj))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.classification.data import CostClassificationsData
|
||||
from bonsai.bim.module.cost.data import CostSchedulesData, CostItemRatesData, CostItemQuantitiesData
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
@@ -83,7 +82,7 @@ def update_cost_item_identification(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
if not props.is_cost_update_enabled or self.identification == "XXX":
|
||||
return
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
@@ -98,7 +97,7 @@ def update_cost_item_name(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
if not props.is_cost_update_enabled or self.name == "Unnamed":
|
||||
return
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.bim.module.cost.prop as CostProp
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.cost.data import CostSchedulesData
|
||||
from typing import Any
|
||||
|
||||
@@ -37,7 +36,7 @@ class BIM_PT_cost_schedules(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
class BIMCoveringProperties(PropertyGroup):
|
||||
ceiling_height: bpy.props.FloatProperty(
|
||||
name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height"
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ceiling_height: float
|
||||
|
||||
@@ -24,6 +24,7 @@ import bonsai.tool as tool
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bpy.types import WorkSpaceTool
|
||||
from functools import partial
|
||||
|
||||
|
||||
class CoveringTool(WorkSpaceTool):
|
||||
@@ -45,17 +46,15 @@ class CoveringTool(WorkSpaceTool):
|
||||
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
|
||||
args = ("covering", layout, text, hotkey, description)
|
||||
tool.Blender.add_layout_hotkey_operator(*args)
|
||||
add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="covering", module_name=__name__)
|
||||
|
||||
|
||||
class CoveringToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout, ifc_element_type=None):
|
||||
cls.layout = layout
|
||||
cls.props = context.scene.BIMModelProperties
|
||||
cls.covering_props = context.scene.BIMCoveringProperties
|
||||
cls.props = tool.Model.get_model_props()
|
||||
cls.covering_props = tool.Covering.get_covering_props()
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
if not tool.Ifc.get():
|
||||
@@ -136,8 +135,8 @@ class CoveringToolUI:
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
if cls.props.ifc_class:
|
||||
box = cls.layout.box()
|
||||
if AuthoringData.data["type_thumbnail"]:
|
||||
box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5)
|
||||
if thumbnail := AuthoringData.data["relating_type_data"].get("thumbnail"):
|
||||
box.template_icon(icon_value=thumbnail, scale=5)
|
||||
else:
|
||||
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
|
||||
op.ifc_class = cls.props.ifc_class
|
||||
@@ -145,8 +144,8 @@ class CoveringToolUI:
|
||||
|
||||
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.covering_hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = ""
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
hotkey: bpy.props.StringProperty()
|
||||
description: bpy.props.StringProperty()
|
||||
|
||||
@@ -159,12 +158,11 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMCoveringProperties
|
||||
self.props = tool.Covering.get_covering_props()
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
|
||||
def invoke(self, context, event):
|
||||
# https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
|
||||
# self.props = context.scene.BIMSpatialProperties
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -175,14 +173,14 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
container = tool.Root.get_default_container()
|
||||
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
if AuthoringData.data["relating_type_data"].get("predefined_type") == "FLOORING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
|
||||
elif container:
|
||||
bpy.ops.bim.add_instance_flooring_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
elif AuthoringData.data["relating_type_data"].get("predefined_type") == "CEILING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_ceiling_coverings_from_walls()
|
||||
elif container:
|
||||
|
||||
@@ -20,8 +20,8 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.tool as tool
|
||||
from math import degrees, atan2
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
from .api import Api
|
||||
|
||||
@@ -92,7 +92,7 @@ class RunAnalysis(bpy.types.Operator):
|
||||
bl_label = "Run Analysis"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.inputs = {
|
||||
"floors": [],
|
||||
"walls": [],
|
||||
@@ -181,12 +181,12 @@ class RunAnalysis(bpy.types.Operator):
|
||||
if modifier.type == "TRIANGULATE":
|
||||
return True
|
||||
|
||||
def get_covetool_category(self, obj):
|
||||
def get_covetool_category(self, obj: bpy.types.Object):
|
||||
if not hasattr(obj, "data") or not isinstance(obj.data, bpy.types.Mesh):
|
||||
return
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
|
||||
return
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = self.file.by_id(ifc_id)
|
||||
ifc_class = element.is_a()
|
||||
if "IfcSlab" in ifc_class:
|
||||
return "floors"
|
||||
|
||||
@@ -27,7 +27,6 @@ import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.drawing.scheduler as scheduler
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.handler import refresh_ui_data
|
||||
from typing import TYPE_CHECKING
|
||||
from collections import Counter
|
||||
@@ -212,7 +211,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
props = context.scene.CsvProperties
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, f".{props.format}")
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
ifc_file = tool.Ifc.get()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
results = ifcopenshell.util.selector.filter_elements(
|
||||
@@ -314,7 +313,7 @@ class ImportIfcCsv(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props = context.scene.CsvProperties
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
ifc_file = tool.Ifc.get()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.search.data import SearchData
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
scene = context.scene
|
||||
props = scene.CsvProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "should_load_from_memory")
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
@@ -49,7 +49,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
row.prop(props, "should_show_settings", icon="PREFERENCES", text="")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_ifc_file")
|
||||
row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
@@ -78,10 +78,10 @@ class PrintIfcFile(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
print(IfcStore.get_file().wrapped_data.to_string())
|
||||
print(tool.Ifc.get().wrapped_data.to_string())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -101,13 +101,14 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
if tool.Geometry.has_mesh_properties(data):
|
||||
if data.library:
|
||||
continue
|
||||
data.BIMMeshProperties.ifc_definition_id = 0
|
||||
tool.Geometry.get_mesh_props(data).ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
if material.library:
|
||||
continue
|
||||
tool.Ifc.unlink(obj=material)
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
bim_props.ifc_file = ""
|
||||
tool.Debug.get_debug_props().attributes.clear()
|
||||
IfcStore.purge()
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
@@ -160,7 +161,8 @@ class ProfileImportIFC(bpy.types.Operator):
|
||||
if not tool.Ifc.get():
|
||||
cls.poll_message_set("No IFC file loaded.")
|
||||
return False
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not bim_props.ifc_file:
|
||||
cls.poll_message_set("Current IFC file is not saved.")
|
||||
return False
|
||||
return True
|
||||
@@ -184,10 +186,10 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace")
|
||||
|
||||
total = len(elements)
|
||||
@@ -256,7 +258,7 @@ class CreateShapeFromStepId(bpy.types.Operator):
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
self.file = tool.Ifc.get()
|
||||
element = self.file.by_id(self.step_id or int(context.scene.BIMDebugProperties.step_id))
|
||||
element = self.file.by_id(self.step_id or int(tool.Debug.get_debug_props().step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("keep-bounding-boxes", True)
|
||||
if self.should_include_curves:
|
||||
@@ -309,7 +311,7 @@ class RewindInspector(bpy.types.Operator):
|
||||
bl_description = "Rewind the Inspector to the previously inspected element"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
total_breadcrumbs = len(props.step_id_breadcrumb)
|
||||
if total_breadcrumbs < 2:
|
||||
return {"FINISHED"}
|
||||
@@ -328,13 +330,13 @@ class InspectFromStepId(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
debug_props = context.scene.BIMDebugProperties
|
||||
self.file = tool.Ifc.get()
|
||||
debug_props = tool.Debug.get_debug_props()
|
||||
debug_props.active_step_id = self.step_id
|
||||
crumb = context.scene.BIMDebugProperties.step_id_breadcrumb.add()
|
||||
crumb = debug_props.step_id_breadcrumb.add()
|
||||
crumb.name = str(self.step_id)
|
||||
element = self.file.by_id(self.step_id)
|
||||
debug_props.attributes.clear()
|
||||
@@ -380,12 +382,12 @@ class InspectFromObject(bpy.types.Operator):
|
||||
def get_active_object_ifc_definition(cls, context: bpy.types.Context) -> Union[int, None]:
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
if ifc_id := obj.BIMObjectProperties.ifc_definition_id:
|
||||
if ifc_id := tool.Blender.get_ifc_definition_id(obj):
|
||||
return ifc_id
|
||||
if (
|
||||
(data := obj.data)
|
||||
and tool.Geometry.has_mesh_properties(data)
|
||||
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
):
|
||||
return ifc_id
|
||||
|
||||
@@ -422,7 +424,7 @@ class PrintObjectPlacement(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
placement = ifcopenshell.util.placement.get_local_placement(IfcStore.get_file().by_id(self.step_id))
|
||||
placement = ifcopenshell.util.placement.get_local_placement(tool.Ifc.get().by_id(self.step_id))
|
||||
if self.create_empty_object:
|
||||
bpy.ops.object.empty_add(type="ARROWS")
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
@@ -438,7 +440,8 @@ class ParseExpress(bpy.types.Operator):
|
||||
bl_label = "Parse Express"
|
||||
|
||||
def execute(self, context):
|
||||
core.parse_express(tool.Debug, context.scene.BIMDebugProperties.express_file)
|
||||
props = tool.Debug.get_debug_props()
|
||||
core.parse_express(tool.Debug, props.express_file)
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -452,8 +455,9 @@ class SelectExpressFile(bpy.types.Operator):
|
||||
filter_glob: bpy.props.StringProperty(default="*.exp", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Debug.get_debug_props()
|
||||
if os.path.exists(self.filepath) and "exp" in os.path.splitext(self.filepath)[1]:
|
||||
context.scene.BIMDebugProperties.express_file = self.filepath
|
||||
props.express_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -497,7 +501,7 @@ class PrintUnusedElementStats(bpy.types.Operator):
|
||||
ignore_styled_items: bpy.props.BoolProperty(name="Ignore Styled Items", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
# ignore some classes that could have zero 0 inverse references by their nature
|
||||
ignore_classes = []
|
||||
if self.ignore_contexts:
|
||||
@@ -509,6 +513,7 @@ class PrintUnusedElementStats(bpy.types.Operator):
|
||||
if self.ignore_styled_items:
|
||||
ignore_classes += ["IfcStyledItem"]
|
||||
ignore_classes += [
|
||||
"IfcDocumentReference", # Document references for sheet elements (drawings, schedules, etc).
|
||||
"IfcIndexedColourMap", # Only referenced by inverse attributes.
|
||||
"IfcIndexedTextureMap", # Only referenced by inverse attributes.
|
||||
]
|
||||
@@ -523,7 +528,7 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Purge Unused Elements By Class"
|
||||
bl_description = (
|
||||
"Will find all elements of class that have no inverse references and will remove them, use very carefully.\n"
|
||||
"If IFC class is provided in neighbour field, will purge only elemnts of the provided class. Otherwise will purge all white-listed elements.\n"
|
||||
"If IFC class is provided in neighbour field, will purge only elements of the provided class. Otherwise will purge all white-listed elements.\n"
|
||||
"ALT+CLICK to provide a path where to save the IFC file with the removed elements (note changes will be applied to the current IFC too)"
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -543,7 +548,7 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
if props.ifc_class_purge:
|
||||
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
|
||||
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
|
||||
@@ -609,18 +614,30 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"IfcUnitAssignment",
|
||||
"IfcVirtualGridIntersection",
|
||||
]
|
||||
whitelist_exceptions = {
|
||||
# Document references for sheet elements (drawings, schedules, etc).
|
||||
"IfcExternalReference": ("IfcDocumentReference",),
|
||||
}
|
||||
|
||||
total_purged = 0
|
||||
schema = tool.Ifc.schema()
|
||||
while True:
|
||||
total_batches = 0
|
||||
print("*" * 100)
|
||||
total_batch_purged = 0
|
||||
for ifc_class in whitelisted_classes:
|
||||
total_class_purged = 0
|
||||
|
||||
# Ensure class is present in the schema.
|
||||
try:
|
||||
elements = tool.Ifc.get().by_type(ifc_class)
|
||||
except:
|
||||
continue # Probably not in this schema?
|
||||
schema.declaration_by_name(ifc_class)
|
||||
except RuntimeError:
|
||||
continue
|
||||
|
||||
elements = tool.Ifc.get().by_type(ifc_class)
|
||||
if ifc_class in whitelist_exceptions:
|
||||
elements = [e for e in elements if not any(e.is_a(c) for c in whitelist_exceptions[ifc_class])]
|
||||
|
||||
to_purge = set()
|
||||
for element in elements:
|
||||
try:
|
||||
@@ -687,15 +704,17 @@ class PurgeUnusedObjects(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if purged == 0:
|
||||
return
|
||||
|
||||
scene = context.scene
|
||||
if object_type == "PROFILE":
|
||||
if scene.BIMProfileProperties.is_editing:
|
||||
props = tool.Profile.get_profile_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_profiles()
|
||||
elif object_type == "STYLE":
|
||||
if scene.BIMStylesProperties.is_editing:
|
||||
props = tool.Style.get_style_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_styles()
|
||||
elif object_type == "MATERIAL":
|
||||
if scene.BIMMaterialProperties.is_editing:
|
||||
props = tool.Material.get_material_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_materials()
|
||||
|
||||
|
||||
@@ -735,15 +754,17 @@ class MergeIdenticalObjects(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if merged == 0:
|
||||
return
|
||||
|
||||
scene = context.scene
|
||||
if object_type == "PROFILE":
|
||||
if scene.BIMProfileProperties.is_editing:
|
||||
props = tool.Profile.get_profile_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_profiles()
|
||||
elif object_type == "STYLE":
|
||||
if scene.BIMStylesProperties.is_editing:
|
||||
props = tool.Style.get_style_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_styles()
|
||||
elif object_type == "MATERIAL":
|
||||
if scene.BIMMaterialProperties.is_editing:
|
||||
props = tool.Material.get_material_props()
|
||||
if props.is_editing:
|
||||
bpy.ops.bim.load_materials()
|
||||
|
||||
|
||||
@@ -799,7 +820,7 @@ class DebugActiveDrawing(bpy.types.Operator):
|
||||
)
|
||||
|
||||
def execute(self, context: bpy.types.Context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_item = props.drawings[props.active_drawing_index]
|
||||
drawing = tool.Ifc.get().by_id(drawing_item.ifc_definition_id)
|
||||
|
||||
@@ -934,6 +955,11 @@ class RestartBlender(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
# Save preferences manually since we're restarting Blender using .execv
|
||||
# and it doens't have a chance to save them on exit.
|
||||
if context.preferences.use_preferences_save:
|
||||
bpy.ops.wm.save_userpref()
|
||||
|
||||
ms_store_app_id = tool.Blender.get_microsoft_store_app_id()
|
||||
if not ms_store_app_id:
|
||||
path = bpy.app.binary_path
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -28,6 +29,9 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal, get_args
|
||||
|
||||
DisplayType = Literal["BOUNDS", "WIRE", "SOLID", "TEXTURED"]
|
||||
|
||||
|
||||
class BIMDebugProperties(PropertyGroup):
|
||||
@@ -41,14 +45,23 @@ class BIMDebugProperties(PropertyGroup):
|
||||
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
|
||||
express_file: StringProperty(name="Express File")
|
||||
display_type: EnumProperty(
|
||||
items=[
|
||||
("BOUNDS", "Bounds", ""),
|
||||
("WIRE", "Wire", ""),
|
||||
("SOLID", "Solid", ""),
|
||||
("TEXTURED", "Textured", ""),
|
||||
],
|
||||
items=[(display_type, display_type.capitalize(), "") for display_type in get_args(DisplayType)],
|
||||
name="Display Type",
|
||||
default="BOUNDS",
|
||||
)
|
||||
ifc_class_purge: StringProperty(name="Unused Elements IFC Class", default="")
|
||||
package_name: StringProperty(name="Package Name", default="")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
step_id: int
|
||||
number_of_polygons: int
|
||||
percentile_of_polygons: int
|
||||
active_step_id: int
|
||||
step_id_breadcrumb: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_references: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
express_file: str
|
||||
display_type: str
|
||||
ifc_class_purge: str
|
||||
package_name: str
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
@@ -32,10 +33,11 @@ class BIM_PT_debug(Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(context.scene.BIMProperties, "ifc_file", text="")
|
||||
row.prop(bim_props, "ifc_file", text="")
|
||||
row.operator("bim.validate_ifc_file", icon="CHECKMARK", text="")
|
||||
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
@@ -87,27 +89,25 @@ class BIM_PT_debug(Panel):
|
||||
row.prop(props, "step_id", text="")
|
||||
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = context.scene.BIMDebugProperties.number_of_polygons
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = props.number_of_polygons
|
||||
row.prop(props, "number_of_polygons", text="")
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = (
|
||||
context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = props.percentile_of_polygons
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.prop(props, "display_type", text="")
|
||||
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
|
||||
row.operator("bim.override_display_type").display = props.display_type
|
||||
|
||||
layout.operator("bim.purge_unused_representations")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
|
||||
row.prop(props, "ifc_class_purge", text="")
|
||||
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
|
||||
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if context.active_object and (data := context.active_object.data):
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -123,7 +123,7 @@ class BIM_PT_debug(Panel):
|
||||
row.operator("bim.rewind_inspector", icon="FRAME_PREV", text="")
|
||||
row.prop(props, "active_step_id", text="")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.inspect_from_step_id").step_id = context.scene.BIMDebugProperties.active_step_id
|
||||
row.operator("bim.inspect_from_step_id").step_id = props.active_step_id
|
||||
row.operator("bim.inspect_from_object")
|
||||
|
||||
if props.attributes:
|
||||
|
||||
@@ -65,13 +65,14 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
obj.color = (0.0, 0.0, 0.7, 1.0)
|
||||
continue
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
|
||||
continue
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
@@ -83,7 +84,7 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
element_file = ifc_file
|
||||
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = element_file.by_id(ifc_id)
|
||||
except:
|
||||
continue
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
@@ -252,13 +253,14 @@ class SelectDiffObjects(bpy.types.Operator):
|
||||
obj.select_set(True)
|
||||
continue
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
|
||||
continue
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
@@ -270,7 +272,7 @@ class SelectDiffObjects(bpy.types.Operator):
|
||||
element_file = ifc_file
|
||||
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = element_file.by_id(ifc_id)
|
||||
except:
|
||||
continue
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.diff.data import DiffData
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -52,7 +52,7 @@ class DocumentData:
|
||||
|
||||
@classmethod
|
||||
def parent_document(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props = tool.Document.get_document_props()
|
||||
if len(props.breadcrumbs):
|
||||
parent = tool.Ifc.get().by_id(int(props.breadcrumbs[-1].name))
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
|
||||
@@ -24,7 +24,6 @@ import ifcopenshell.util.element
|
||||
import bonsai.bim.handler
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.document as core
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadProjectDocuments(bpy.types.Operator):
|
||||
@@ -116,7 +115,7 @@ class EditDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
props = tool.Document.get_document_props()
|
||||
core.edit_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(props.active_document_id))
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
def update_document_name(self: "Document", context: bpy.types.Context) -> None:
|
||||
@@ -57,6 +58,11 @@ class Document(PropertyGroup):
|
||||
)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
identification: str
|
||||
is_information: bool
|
||||
ifc_definition_id: int
|
||||
|
||||
|
||||
class BIMDocumentProperties(PropertyGroup):
|
||||
document_attributes: CollectionProperty(name="Document Attributes", type=Attribute)
|
||||
@@ -65,3 +71,11 @@ class BIMDocumentProperties(PropertyGroup):
|
||||
breadcrumbs: CollectionProperty(name="Breadcrumbs", type=StrProperty)
|
||||
active_document_index: IntProperty(name="Active Document Index")
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
document_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_document_id: int
|
||||
documents: bpy.types.bpy_prop_collection_idprop[Document]
|
||||
breadcrumbs: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
active_document_index: int
|
||||
is_editing: bool
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.document.data import DocumentData, ObjectDocumentData
|
||||
|
||||
@@ -33,13 +33,13 @@ class BIM_PT_documents(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not DocumentData.is_loaded:
|
||||
DocumentData.load()
|
||||
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
self.props = tool.Document.get_document_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE")
|
||||
@@ -90,20 +90,22 @@ class BIM_PT_object_documents(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
|
||||
return False
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
if not tool.Ifc.get_object_by_identifier(ifc_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectDocumentData.is_loaded:
|
||||
ObjectDocumentData.load()
|
||||
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.oprops = tool.Blender.get_object_bim_props(obj)
|
||||
self.props = tool.Document.get_document_props()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import bpy
|
||||
import math
|
||||
@@ -37,7 +38,7 @@ class Annotator:
|
||||
if related_element is None:
|
||||
location, _, _, _ = Annotator.get_placeholder_coords(bpy.context)
|
||||
else:
|
||||
obj.data.BIMAssignedProductProperties.related_element = related_element
|
||||
curve.BIMAssignedProductProperties.related_element = related_element
|
||||
location = related_element.location
|
||||
obj.location = location
|
||||
obj.hide_render = True
|
||||
@@ -47,9 +48,10 @@ class Annotator:
|
||||
tool.Blender.get_data_dir_path(Path("fonts") / "OpenGost Type B TT.ttf").__str__()
|
||||
)
|
||||
font.name = "OpenGost Type B TT"
|
||||
obj.data.font = font
|
||||
obj.data.BIMTextProperties.font_size = "2.5"
|
||||
collection = bpy.context.scene.camera.BIMObjectProperties.collection
|
||||
curve.font = font
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
props.font_size = "2.5"
|
||||
collection = tool.Blender.get_object_bim_props(bpy.context.scene.camera).collection
|
||||
collection.objects.link(obj)
|
||||
Annotator.resize_text(obj)
|
||||
return obj
|
||||
@@ -66,7 +68,8 @@ class Annotator:
|
||||
return
|
||||
# This is a magic number for OpenGost
|
||||
font_size = 1.6 / 1000
|
||||
font_size *= float(text_obj.data.BIMTextProperties.font_size)
|
||||
props = tool.Drawing.get_text_props(text_obj)
|
||||
font_size *= float(props.font_size)
|
||||
|
||||
font_size /= tool.Drawing.get_scale_ratio(tool.Drawing.get_diagram_scale(camera)["Scale"])
|
||||
|
||||
@@ -123,12 +126,14 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def get_annotation_obj(drawing: ifcopenshell.entity_instance, object_type: str, data_type: str) -> bpy.types.Object:
|
||||
def get_annotation_obj(
|
||||
drawing: ifcopenshell.entity_instance, object_type: str, data_type: tool.Drawing.ANNOTATION_DATA_TYPE
|
||||
) -> bpy.types.Object:
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
|
||||
matrix_world = tool.Drawing.get_camera_matrix(camera)
|
||||
matrix_world.translation = co1
|
||||
collection = camera.BIMObjectProperties.collection
|
||||
collection = tool.Blender.get_object_bim_props(camera).collection
|
||||
|
||||
if object_type == "TEXT":
|
||||
obj = bpy.data.objects.new(object_type, None)
|
||||
@@ -145,11 +150,17 @@ class Annotator:
|
||||
collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
if object_type != "ANGLE":
|
||||
for obj in collection.objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element and element.ObjectType == object_type and obj.type == object_type.upper():
|
||||
return obj
|
||||
# TODO: remove as outdated?
|
||||
# Is reusing the same objects preventing the creation of new annotations.
|
||||
# if object_type != "ANGLE":
|
||||
# for obj in collection.objects:
|
||||
# element = tool.Ifc.get_entity(obj)
|
||||
# if (
|
||||
# element
|
||||
# and ifcopenshell.util.element.get_predefined_type(element) == object_type
|
||||
# and obj.type == data_type.upper()
|
||||
# ):
|
||||
# return obj
|
||||
|
||||
if data_type == "mesh":
|
||||
data = bpy.data.meshes.new(object_type)
|
||||
|
||||
@@ -88,7 +88,8 @@ class SheetsData:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
titleblocks_dir = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir")
|
||||
if not titleblocks_dir:
|
||||
titleblocks_dir = bpy.context.scene.DocProperties.titleblocks_dir
|
||||
props = tool.Drawing.get_document_props()
|
||||
titleblocks_dir = props.titleblocks_dir
|
||||
titleblocks_dir = tool.Ifc.resolve_uri(titleblocks_dir)
|
||||
if os.path.exists(titleblocks_dir):
|
||||
files.extend([str(f.stem) for f in Path(titleblocks_dir).glob("*.svg")])
|
||||
@@ -120,23 +121,25 @@ class DrawingsData:
|
||||
|
||||
@classmethod
|
||||
def location_hint(cls):
|
||||
if bpy.context.scene.DocProperties.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
results = [("0", "Origin", "")]
|
||||
results.extend(
|
||||
[(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcBuildingStorey")]
|
||||
)
|
||||
return results
|
||||
elif bpy.context.scene.DocProperties.target_view in ["MODEL_VIEW"]:
|
||||
elif props.target_view in ["MODEL_VIEW"]:
|
||||
return [(h.upper(), h, "") for h in ["Orthographic", "Perspective"]]
|
||||
return [(h.upper(), h, "") for h in ["North", "South", "East", "West"]]
|
||||
|
||||
@classmethod
|
||||
def active_drawing_pset_data(cls):
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing_id = bpy.context.scene.DocProperties.active_drawing_id
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_id = props.active_drawing_id
|
||||
if drawing_id == 0:
|
||||
return {}
|
||||
drawing = ifc_file.by_id(bpy.context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(drawing_id)
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing")
|
||||
|
||||
|
||||
@@ -303,7 +306,7 @@ class DecoratorData:
|
||||
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
|
||||
return None
|
||||
|
||||
props = obj.BIMTextProperties
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
# getting font size
|
||||
pset_data = ifcopenshell.util.element.get_pset(element, "EPset_Annotation") or {}
|
||||
# use `regular` as default
|
||||
@@ -361,7 +364,11 @@ class DecoratorData:
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
supported_object_types = ("DIMENSION", "DIAMETER", "SECTION_LEVEL", "PLAN_LEVEL", "RADIUS")
|
||||
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in supported_object_types:
|
||||
if (
|
||||
not element
|
||||
or not element.is_a("IfcAnnotation")
|
||||
or ifcopenshell.util.element.get_predefined_type(element) not in supported_object_types
|
||||
):
|
||||
return None
|
||||
|
||||
dimension_style = "arrow"
|
||||
@@ -399,7 +406,7 @@ class AnnotationData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.props = bpy.context.scene.BIMAnnotationProperties
|
||||
cls.props = tool.Drawing.get_annotation_props()
|
||||
cls.data["relating_type_id"] = cls.relating_type_id()
|
||||
cls.data["relating_types"] = cls.relating_types()
|
||||
|
||||
|
||||
@@ -423,7 +423,8 @@ class BaseDecorator:
|
||||
# font_size = 16 <-- this is a good default
|
||||
# TODO: need to synchronize it better with svg
|
||||
|
||||
magic_font_scale = bpy.context.scene.DocProperties.magic_font_scale
|
||||
props = tool.Drawing.get_document_props()
|
||||
magic_font_scale = props.magic_font_scale
|
||||
font_size_px = int(magic_font_scale * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
@@ -589,7 +590,7 @@ class BaseDecorator:
|
||||
text_dir = self.get_annotation_direction(context, obj)
|
||||
|
||||
pos = location_3d_to_region_2d(region, region3d, text_world_position)
|
||||
props = obj.BIMTextProperties
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
text_data = DecoratorData.get_ifc_text_data(obj)
|
||||
if props.is_editing:
|
||||
text_data = text_data | props.get_text_edited_data()
|
||||
@@ -976,6 +977,7 @@ class FallDecorator(BaseDecorator):
|
||||
# same function as in svgwriter.py
|
||||
def get_label_text():
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
B, A = [v.co.xyz for v in spline_points[:2]]
|
||||
rise = abs(A.z - B.z)
|
||||
O = A.copy()
|
||||
@@ -988,13 +990,14 @@ class FallDecorator(BaseDecorator):
|
||||
angle = 90
|
||||
|
||||
# ues SLOPE_ANGLE as default
|
||||
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type in ("FALL", "SLOPE_ANGLE"):
|
||||
return f"{angle}°"
|
||||
elif element.ObjectType == "SLOPE_FRACTION":
|
||||
elif object_type == "SLOPE_FRACTION":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{self.format_value(context, rise)} / {self.format_value(context, run)}"
|
||||
elif element.ObjectType == "SLOPE_PERCENT":
|
||||
elif object_type == "SLOPE_PERCENT":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{round(angle_tg * 100)} %"
|
||||
@@ -2022,7 +2025,8 @@ 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:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if drawing_font := props.drawing_font:
|
||||
drawing_font_path = tool.Blender.get_data_dir_path(Path("fonts") / drawing_font)
|
||||
if drawing_font_path.is_file():
|
||||
font_id = blf.load(drawing_font_path.__str__())
|
||||
@@ -2045,7 +2049,7 @@ class DecorationsHandler:
|
||||
if not element.is_a("IfcAnnotation"):
|
||||
continue
|
||||
|
||||
object_type: Union[str, None] = element.ObjectType
|
||||
object_type: Union[str, None] = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type == "DRAWING":
|
||||
continue
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
import bonsai.tool as tool
|
||||
from bpy import types
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
@@ -478,21 +479,25 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
bl_options = {"3D", "PERSISTENT", "SHOW_MODAL_ALL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, ctx):
|
||||
obj = ctx.object
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return (
|
||||
obj
|
||||
and obj.type == "MESH"
|
||||
and obj.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
and (data := obj.data)
|
||||
and isinstance(data, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(data).ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
)
|
||||
|
||||
def setup(self, ctx):
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
def setup(self, context: bpy.types.Context) -> None:
|
||||
target = context.object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
|
||||
basis = target.matrix_world.normalized()
|
||||
theme = ctx.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(ctx.scene.unit_settings.system, ctx.scene.unit_settings.length_unit)
|
||||
theme = context.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(context.scene.unit_settings.system, context.scene.unit_settings.length_unit)
|
||||
|
||||
# setup handle
|
||||
gz = self.handle = self.gizmos.new("BIM_GT_uglydot_3d")
|
||||
@@ -521,23 +526,26 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
# gz.use_draw_modal = True
|
||||
# gz.target_set_prop('value', target.demo, 'depth')
|
||||
|
||||
def refresh(self, ctx):
|
||||
def refresh(self, context: bpy.types.Context) -> None:
|
||||
"""updating gizmos"""
|
||||
target = ctx.object
|
||||
target = context.active_object
|
||||
basis = target.matrix_world.normalized()
|
||||
self.handle.matrix_basis = basis
|
||||
self.guides.matrix_basis = basis
|
||||
|
||||
def update(self, ctx):
|
||||
def update(self, context: bpy.types.Context) -> None:
|
||||
"""updating object"""
|
||||
bpy.ops.bim.update_parametric_representation()
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
target = context.active_object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
self.handle.target_set_prop("offset", prop, "value")
|
||||
self.guides.target_set_prop("depth", prop, "value")
|
||||
|
||||
@staticmethod
|
||||
def get_scale_value(system, length_unit):
|
||||
def get_scale_value(system: str, length_unit: str) -> float:
|
||||
scale_value = 1
|
||||
if system == "METRIC":
|
||||
if length_unit == "KILOMETERS":
|
||||
|
||||
@@ -24,7 +24,8 @@ from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def load_post(*args):
|
||||
if bpy.context.scene.DocProperties.should_draw_decorations:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.should_draw_decorations:
|
||||
decoration.DecorationsHandler.install(bpy.context)
|
||||
else:
|
||||
decoration.DecorationsHandler.uninstall()
|
||||
@@ -35,9 +36,11 @@ def depsgraph_update_pre_handler(scene):
|
||||
set_active_camera_resolution(scene)
|
||||
|
||||
|
||||
def set_active_camera_resolution(scene):
|
||||
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
|
||||
def set_active_camera_resolution(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not scene.camera or "/" not in scene.camera.name or not props.drawings:
|
||||
return
|
||||
assert isinstance(scene.camera.data, bpy.types.Camera)
|
||||
props = scene.camera.data.BIMCameraProperties
|
||||
ortho_scale = max((props.width, props.height))
|
||||
aspect_ratio = props.width / props.height
|
||||
@@ -60,5 +63,3 @@ def set_active_camera_resolution(scene):
|
||||
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x)
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y)
|
||||
|
||||
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
import bpy
|
||||
import math
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
from mathutils import Vector
|
||||
from typing import Union
|
||||
|
||||
# Code taken and updated from https://blenderartists.org/t/detecting-intersection-of-bounding-boxes/457520/2
|
||||
|
||||
@@ -135,6 +137,10 @@ def format_distance(
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
if area_unit := ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "AREAUNIT"):
|
||||
area_unit_symbol = " " + ifcopenshell.util.unit.get_unit_symbol(area_unit)
|
||||
else:
|
||||
area_unit_symbol = ""
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
@@ -225,7 +231,24 @@ def format_distance(
|
||||
if add_inches or frac:
|
||||
tx_dist += '"'
|
||||
else:
|
||||
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
|
||||
fmt = "%1.3f"
|
||||
sq_feet = round(value * toInches / inPerFoot, 4)
|
||||
tx_dist = ""
|
||||
if area_unit_symbol == " ft2":
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_feet
|
||||
if area_unit_symbol == " in2":
|
||||
sq_inch = sq_feet * 144
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_inch
|
||||
if area_unit_symbol == " yd2":
|
||||
sq_yard = sq_feet / 9
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_yard
|
||||
if area_unit_symbol == " mi2":
|
||||
sq_mile = sq_feet / 27878400
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_mile
|
||||
|
||||
# METRIC FORMATTING
|
||||
elif unit_system == "METRIC":
|
||||
@@ -287,19 +310,41 @@ def format_distance(
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
if isArea:
|
||||
tx_dist += s_code
|
||||
if area_unit_symbol == " m2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % value
|
||||
if area_unit_symbol == " cm2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
d_cm = value * (10000)
|
||||
tx_dist = fmt % d_cm
|
||||
if area_unit_symbol == " mm2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
d_cm = value * (1000000)
|
||||
tx_dist = fmt % d_cm
|
||||
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
|
||||
|
||||
def get_active_drawing(scene):
|
||||
def get_active_drawing(
|
||||
scene: bpy.types.Scene,
|
||||
) -> Union[tuple[bpy.types.Collection, bpy.types.Camera], tuple[None, None]]:
|
||||
"""Get active drawing collection and camera"""
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
try:
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||
return camera.BIMObjectProperties.collection, camera
|
||||
return tool.Blender.get_object_bim_props(camera).collection, camera
|
||||
except:
|
||||
return None, None
|
||||
|
||||
@@ -416,7 +461,7 @@ def elevate_segment(bounds, segm):
|
||||
return [Vector((x, ymin, zmin)), Vector((x, ymax, zmin))]
|
||||
|
||||
|
||||
def add_newline_between_words(text, newline_at):
|
||||
def add_newline_between_words(text: str, newline_at: int) -> str:
|
||||
result = []
|
||||
start = 0
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class AddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMAnnotationProperties
|
||||
props = tool.Drawing.get_annotation_props()
|
||||
object_type = props.object_type
|
||||
has_representation = props.create_representation_for_type
|
||||
drawing = tool.Ifc.get_entity(bpy.context.scene.camera)
|
||||
@@ -115,7 +115,7 @@ class EnableAddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
bpy.context.scene.BIMAnnotationProperties.is_adding_type = True
|
||||
tool.Drawing.get_annotation_props().is_adding_type = True
|
||||
|
||||
|
||||
class DisableAddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -124,7 +124,7 @@ class DisableAddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
bpy.context.scene.BIMAnnotationProperties.is_adding_type = False
|
||||
tool.Drawing.get_annotation_props().is_adding_type = False
|
||||
|
||||
|
||||
class AddDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -134,19 +134,19 @@ class AddDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a drawing view to the IFC project"
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
hint = self.props.location_hint
|
||||
if self.props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
hint = props.location_hint
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
hint = int(hint)
|
||||
core.add_drawing(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Drawing,
|
||||
target_view=self.props.target_view,
|
||||
target_view=props.target_view,
|
||||
location_hint=hint,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -162,7 +162,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -176,7 +176,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "should_duplicate_annotations")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.duplicate_drawing(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -184,7 +184,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
should_duplicate_annotations=self.should_duplicate_annotations,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -244,9 +244,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id
|
||||
active_drawing_id = tool.Blender.get_ifc_definition_id(context.scene.camera)
|
||||
if self.print_all:
|
||||
original_drawing_id = active_drawing_id
|
||||
drawings_to_print = [d.ifc_definition_id for d in self.props.drawings if d.is_selected and d.is_drawing]
|
||||
@@ -261,7 +261,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.camera = context.scene.camera
|
||||
self.camera_element = tool.Ifc.get_entity(self.camera)
|
||||
self.camera_document = tool.Drawing.get_drawing_document(self.camera_element)
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
with profile("Drawing generation process"):
|
||||
with profile("Initialize drawing generation process"):
|
||||
@@ -385,13 +385,13 @@ class CreateDrawing(bpy.types.Operator):
|
||||
obj.hide_render = obj.name not in visible_object_names
|
||||
|
||||
context.scene.render.filepath = str(Path(svg_path).with_suffix(".png"))
|
||||
drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
drawing_style = self.props.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
|
||||
if drawing_style.render_type == "DEFAULT":
|
||||
bpy.ops.render.render(write_still=True)
|
||||
else:
|
||||
previous_visibility = {}
|
||||
for obj in self.camera.BIMObjectProperties.collection.objects:
|
||||
for obj in tool.Blender.get_object_bim_props(self.camera).collection.objects:
|
||||
if bpy.context.view_layer.objects.get(obj.name):
|
||||
previous_visibility[obj.name] = obj.hide_get()
|
||||
obj.hide_set(True)
|
||||
@@ -712,9 +712,11 @@ class CreateDrawing(bpy.types.Operator):
|
||||
}
|
||||
cached_linework -= edited_guids
|
||||
|
||||
files = {context.scene.BIMProperties.ifc_file: tool.Ifc.get()}
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
files = {bim_props.ifc_file: tool.Ifc.get()}
|
||||
|
||||
for link in context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
files[link.name] = IfcStore.session_files[link.name]
|
||||
@@ -729,7 +731,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# Don't use draw.main() just whilst we're prototyping and experimenting
|
||||
# TODO: hash paths are never used
|
||||
ifc_hash = hashlib.md5(ifc_path.encode("utf-8")).hexdigest()
|
||||
ifc_cache_path = os.path.join(context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
ifc_cache_path = os.path.join(bim_props.cache_dir, f"{ifc_hash}.h5")
|
||||
|
||||
self.serialiser.setFile(ifc)
|
||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element, ifc_file=ifc)
|
||||
@@ -1141,7 +1143,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
try:
|
||||
return tool.Ifc.get().by_guid(guid)
|
||||
except:
|
||||
for link in bpy.context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
try:
|
||||
@@ -1367,7 +1370,11 @@ class CreateDrawing(bpy.types.Operator):
|
||||
elements = list(elements | filtered_drawing_annotations)
|
||||
|
||||
annotations = sorted(
|
||||
elements, key=lambda a: (tool.Drawing.get_annotation_z_index(a), 1 if a.ObjectType == "TEXT" else 0)
|
||||
elements,
|
||||
key=lambda a: (
|
||||
tool.Drawing.get_annotation_z_index(a),
|
||||
1 if ifcopenshell.util.element.get_predefined_type(a) == "TEXT" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
precision = ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "MetricPrecision")
|
||||
@@ -1433,7 +1440,7 @@ class AddAnnotation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMAnnotationProperties
|
||||
props = tool.Drawing.get_annotation_props()
|
||||
if not (drawing := tool.Ifc.get_entity(context.scene.camera)):
|
||||
self.report({"WARNING"}, "Not a BIM camera")
|
||||
return
|
||||
@@ -1458,7 +1465,8 @@ class AddSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a sheet to the project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=context.scene.DocProperties.titleblock)
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=props.titleblock)
|
||||
|
||||
|
||||
class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -1474,7 +1482,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
cls.poll_message_set("Not implemented yet.")
|
||||
return False
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1483,7 +1491,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
pass
|
||||
"""
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
core.duplicate_sheet(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -1508,7 +1516,7 @@ class OpenLayout(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.update_sheet_drawing_sizes(sheet)
|
||||
@@ -1530,7 +1538,8 @@ class SelectAllSheets(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for sheet in context.scene.DocProperties.sheets:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in props.sheets:
|
||||
if sheet.is_selected != self.select_all:
|
||||
sheet.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1550,7 +1559,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
@@ -1564,7 +1573,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
|
||||
if self.open_all:
|
||||
@@ -1612,9 +1621,10 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
# Won't be visible in UI anyway.
|
||||
if not props.sheets or not context.scene.BIMProperties.data_dir:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not props.sheets or not bim_props.data_dir:
|
||||
return False
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
@@ -1622,8 +1632,8 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
active_drawing = tool.Drawing.get_active_drawing_item()
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_drawing = props.drawings[props.active_drawing_index]
|
||||
assert active_drawing
|
||||
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
@@ -1680,7 +1690,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_item = tool.Drawing.get_active_sheet_item()
|
||||
if active_item is None:
|
||||
return False
|
||||
@@ -1714,11 +1724,12 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
return props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.sheets and bim_props.data_dir
|
||||
|
||||
def invoke(self, context, event):
|
||||
# opening all sheets on shift+click
|
||||
@@ -1731,7 +1742,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
scene = context.scene
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
svg2dxf_command = tool.Blender.get_addon_preferences().svg2dxf_command
|
||||
|
||||
@@ -1838,7 +1849,8 @@ class SelectAllDrawings(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for drawing in context.scene.DocProperties.drawings:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in props.drawings:
|
||||
if drawing.is_selected != self.select_all:
|
||||
drawing.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1857,7 +1869,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1871,7 +1883,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if self.open_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in self.props.drawings if d.is_drawing and d.is_selected
|
||||
@@ -1907,7 +1919,7 @@ class ActivateModel(bpy.types.Operator):
|
||||
bl_description = "Activate the model view, hide all annotations"
|
||||
|
||||
def execute(self, context):
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.active_drawing_id = 0
|
||||
|
||||
CutDecorator.uninstall()
|
||||
@@ -1971,11 +1983,12 @@ class ActivateDrawingBase:
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
if bpy.context.scene.DocProperties.is_editing_drawings == False:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.is_editing_drawings == False:
|
||||
bpy.ops.bim.load_drawings()
|
||||
|
||||
drawing = tool.Ifc.get().by_id(self.drawing)
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
|
||||
if self.use_quick_preview:
|
||||
tool.Blender.activate_camera(tool.Drawing.import_temporary_drawing_camera(drawing))
|
||||
@@ -2027,7 +2040,7 @@ class ActivateDrawing(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2050,7 +2063,7 @@ class ActivateDrawingFromSheet(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(reference_type="DRAWING"):
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2066,7 +2079,8 @@ class SelectDocIfcFile(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.ifc_files[self.index].name = self.filepath
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.ifc_files[self.index].name = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -2081,7 +2095,7 @@ class ResizeText(bpy.types.Operator):
|
||||
# TODO: check undo redo
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.scene.camera.BIMObjectProperties.collection.objects:
|
||||
for obj in tool.Blender.get_object_bim_props(context.scene.camera).collection.objects:
|
||||
if isinstance(obj.data, bpy.types.TextCurve):
|
||||
annotation.Annotator.resize_text(obj)
|
||||
return {"FINISHED"}
|
||||
@@ -2098,7 +2112,7 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2112,12 +2126,10 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.remove_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
@@ -2187,7 +2199,8 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
with open(json_path, "r") as fi:
|
||||
shading_styles_json = json.load(fi)
|
||||
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
drawing_styles.clear()
|
||||
styles = [style for style in shading_styles_json]
|
||||
for style_name in styles:
|
||||
@@ -2212,7 +2225,8 @@ class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
new = drawing_styles.add()
|
||||
# drawing style is saved to ifc on rename
|
||||
new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles)
|
||||
@@ -2227,7 +2241,8 @@ class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.drawing_styles.remove(self.index)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles.remove(self.index)
|
||||
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = max(self.index - 1, 0)
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2283,7 +2298,8 @@ class SaveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index = int(self.index)
|
||||
else:
|
||||
index = context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles[index].raster_style = json.dumps(style)
|
||||
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2309,7 +2325,8 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
|
||||
rel_path = drawing_pset_data["ShadingStyles"]
|
||||
current_style = drawing_pset_data.get("CurrentShadingStyle", None)
|
||||
@@ -2338,7 +2355,7 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new_style_name = None
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing = ifc_file.by_id(context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": new_style_name}
|
||||
@@ -2357,17 +2374,18 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
scene = context.scene
|
||||
ifc_file = tool.Ifc.get()
|
||||
active_drawing_style_index = scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if active_drawing_style_index >= len(scene.DocProperties.drawing_styles):
|
||||
if active_drawing_style_index >= len(props.drawing_styles):
|
||||
self.report({"ERROR"}, "Could not find active drawing style")
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.drawing_style = scene.DocProperties.drawing_styles[active_drawing_style_index]
|
||||
self.drawing_style = props.drawing_styles[active_drawing_style_index]
|
||||
|
||||
self.set_raster_style(context)
|
||||
self.set_query(context)
|
||||
|
||||
drawing = ifc_file.by_id(scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": self.drawing_style.name}
|
||||
@@ -2392,7 +2410,8 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def set_query(self, context: bpy.types.Context) -> None:
|
||||
self.include_global_ids = []
|
||||
self.exclude_global_ids = []
|
||||
for ifc_file in context.scene.DocProperties.ifc_files:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for ifc_file in props.ifc_files:
|
||||
try:
|
||||
ifc = ifcopenshell.open(ifc_file.name)
|
||||
except:
|
||||
@@ -2507,14 +2526,15 @@ class AddScheduleToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.schedules:
|
||||
cls.poll_message_set("No schedule selected.")
|
||||
return False
|
||||
return props.schedules and props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.schedules and props.sheets and bim_props.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_schedule = props.schedules[props.active_schedule_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
schedule = tool.Ifc.get().by_id(active_schedule.ifc_definition_id)
|
||||
@@ -2573,14 +2593,15 @@ class AddReferenceToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.references:
|
||||
cls.poll_message_set("No reference selected.")
|
||||
return False
|
||||
return props.references and props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.references and props.sheets and bim_props.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_reference = props.references[props.active_reference_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
extref = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
|
||||
@@ -2682,7 +2703,8 @@ class AddDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2695,7 +2717,8 @@ class RemoveDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2725,7 +2748,9 @@ class EditTextPopup(bpy.types.Operator):
|
||||
# need to keep them in sync or move to some common function
|
||||
# NOTE: that `popup_active_attribute` is used here when it's not used in `BIM_PT_text.draw()`
|
||||
|
||||
props = context.active_object.BIMTextProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_text_literal", icon="ADD", text="Add Literal")
|
||||
@@ -2826,9 +2851,11 @@ class AddTextLiteral(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
|
||||
# similar to `tool.Drawing.import_text_attributes`
|
||||
literal_props = obj.BIMTextProperties.literals.add()
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
literal_props = props.literals.add()
|
||||
literal_attributes = literal_props.attributes
|
||||
literal_attr_values = {
|
||||
"Literal": "Literal",
|
||||
@@ -2865,7 +2892,9 @@ class RemoveTextLiteral(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
obj.BIMTextProperties.literals.remove(self.literal_prop_id)
|
||||
assert obj
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
props.literals.remove(self.literal_prop_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2880,7 +2909,9 @@ class OrderTextLiteralUp(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
obj.BIMTextProperties.literals.move(self.literal_prop_id, self.literal_prop_id - 1)
|
||||
assert obj
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
props.literals.move(self.literal_prop_id, self.literal_prop_id - 1)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2895,7 +2926,9 @@ class OrderTextLiteralDown(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
obj.BIMTextProperties.literals.move(self.literal_prop_id, self.literal_prop_id + 1)
|
||||
assert obj
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
props.literals.move(self.literal_prop_id, self.literal_prop_id + 1)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2969,7 +3002,7 @@ class LoadSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
core.load_sheets(tool.Drawing)
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
sheets_not_found = []
|
||||
for sheet_prop in props.sheets:
|
||||
if not sheet_prop.is_sheet:
|
||||
@@ -2999,7 +3032,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
document_type: Literal["SHEET", "TITLEBLOCK", "EMBEDDED"]
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if sheet.is_a("IfcDocumentInformation"):
|
||||
self.document_type = "SHEET"
|
||||
@@ -3013,6 +3046,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.document_type == "SHEET":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
@@ -3020,13 +3054,13 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "name", text="Name")
|
||||
elif self.document_type == "TITLEBLOCK":
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "titleblock", text="Titleblock")
|
||||
row.prop(props, "titleblock", text="Titleblock")
|
||||
elif self.document_type == "EMBEDDED":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if self.document_type == "SHEET":
|
||||
core.rename_sheet(tool.Ifc, tool.Drawing, sheet=sheet, identification=self.identification, name=self.name)
|
||||
@@ -3124,7 +3158,7 @@ class ExpandTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = True
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3140,7 +3174,7 @@ class ContractTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = False
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3156,7 +3190,7 @@ class ExpandSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = True
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3172,7 +3206,7 @@ class ContractSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = False
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3319,7 +3353,8 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.material_slots[0].material = material
|
||||
bpy.ops.bim.add_style()
|
||||
|
||||
style = ifc_file.by_id(material.BIMStyleProperties.ifc_definition_id)
|
||||
style = tool.Ifc.get_entity(material)
|
||||
assert style
|
||||
tool.Style.assign_style_to_object(style, obj)
|
||||
|
||||
# TODO: IfcSurfaceStyleRendering is unnecessary here, added it only because
|
||||
@@ -3363,7 +3398,7 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -3377,14 +3412,13 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.convert_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
drawings = [tool.Ifc.get().by_id(context.scene.DocProperties.drawings.get(self.view).ifc_definition_id)]
|
||||
drawings = [tool.Ifc.get().by_id(props.drawings.get(self.view).ifc_definition_id)]
|
||||
|
||||
drawing_uris: list[Path] = []
|
||||
drawings_not_found: list[str] = []
|
||||
|
||||
@@ -44,7 +44,7 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
BoolVectorProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
from typing import TYPE_CHECKING, Literal, Any
|
||||
|
||||
|
||||
diagram_scales_enum = []
|
||||
@@ -76,7 +76,7 @@ def update_diagram_scale(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -93,7 +93,7 @@ def update_diagram_scale(self, context):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=diagram_scale)
|
||||
|
||||
|
||||
def update_is_nts(self, context):
|
||||
def update_is_nts(self: "BIMCameraProperties", context: bpy.types.Context) -> None:
|
||||
if not self.update_props:
|
||||
return
|
||||
if not context.scene.camera or context.scene.camera.data != self.id_data:
|
||||
@@ -104,7 +104,7 @@ def update_is_nts(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -192,8 +192,8 @@ def get_drawing_style_name(self: "DrawingStyle"):
|
||||
|
||||
def set_drawing_style_name(self: "DrawingStyle", new_value: str) -> None:
|
||||
"""ensure the name is unique"""
|
||||
scene = bpy.context.scene
|
||||
drawing_styles = [s.name for s in scene.DocProperties.drawing_styles if s.name != self.name]
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = [s.name for s in props.drawing_styles if s.name != self.name]
|
||||
new_value = tool.Blender.ensure_unique_name(new_value, drawing_styles)
|
||||
old_value = self.name
|
||||
self["name"] = new_value
|
||||
@@ -266,7 +266,7 @@ def update_titleblocks(self, context):
|
||||
def update_should_draw_decorations(self, context: bpy.types.Context) -> None:
|
||||
if self.should_draw_decorations:
|
||||
# TODO: design a proper text variable templating renderer
|
||||
collection = context.scene.camera.BIMObjectProperties.collection
|
||||
collection = tool.Blender.get_object_bim_props(context.scene.camera).collection
|
||||
for obj in collection.objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
|
||||
@@ -613,7 +613,13 @@ class BIMTextProperties(PropertyGroup):
|
||||
)
|
||||
newline_at: IntProperty(name="Newline At")
|
||||
|
||||
def get_text_edited_data(self):
|
||||
if TYPE_CHECKING:
|
||||
is_editing: bool
|
||||
literals: bpy.types.bpy_prop_collection_idprop[LiteralProps]
|
||||
font_size: str
|
||||
newline_at: int
|
||||
|
||||
def get_text_edited_data(self) -> dict[str, Any]:
|
||||
"""should be called only if `is_editing`
|
||||
otherwise should use `DecoratorData.get_ifc_text_data(obj)` instead
|
||||
because this data could be out of date
|
||||
@@ -676,3 +682,10 @@ class BIMAnnotationProperties(PropertyGroup):
|
||||
)
|
||||
is_adding_type: bpy.props.BoolProperty(default=False)
|
||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
object_type: str
|
||||
relating_type_id: str
|
||||
create_representation_for_type: bool
|
||||
is_adding_type: bool
|
||||
type_name: str
|
||||
|
||||
@@ -56,7 +56,7 @@ def a1_to_rc(cell):
|
||||
|
||||
|
||||
class Scheduler:
|
||||
def schedule(self, infile, outfile):
|
||||
def schedule(self, infile: str, outfile: str) -> None:
|
||||
self.svg = svgwrite.Drawing(
|
||||
outfile,
|
||||
debug=False,
|
||||
@@ -71,11 +71,12 @@ class Scheduler:
|
||||
elif infile.endswith("xlsx"):
|
||||
self.schedule_xlsx(infile, outfile)
|
||||
|
||||
def parse_css(self, infile):
|
||||
def parse_css(self, infile: str) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
stylesheet_path = os.path.splitext(infile)[0] + ".css"
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_rel_path = getattr(bpy.context.scene.DocProperties, "schedules_stylesheet_path")
|
||||
ifc_file_path = os.path.dirname(IfcStore.path)
|
||||
stylesheet_rel_path = props.schedules_stylesheet_path
|
||||
ifc_file_path = os.path.dirname(tool.Ifc.get_path())
|
||||
stylesheet_path = ifc_file_path + "\\" + stylesheet_rel_path
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_path = tool.Blender.get_data_dir_path(Path("assets") / "schedule.css")
|
||||
@@ -91,7 +92,7 @@ class Scheduler:
|
||||
|
||||
self.svg.defs.add(self.svg.style(css))
|
||||
|
||||
def schedule_xlsx(self, infile, outfile):
|
||||
def schedule_xlsx(self, infile: str, outfile: str) -> None:
|
||||
workbook = openpyxl.open(infile, data_only=True)
|
||||
sheet = workbook.active
|
||||
|
||||
@@ -236,7 +237,7 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def schedule_ods(self, infile, outfile):
|
||||
def schedule_ods(self, infile: str, outfile: str) -> None:
|
||||
doc = load_ods(infile)
|
||||
|
||||
# useful for debugging ods
|
||||
@@ -495,11 +496,11 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def get_style(self, style_name, styles):
|
||||
def get_style(self, style_name: str, styles: dict) -> dict:
|
||||
style = styles[style_name] if style_name else {}
|
||||
return style
|
||||
|
||||
def get_box_alignment(self, style):
|
||||
def get_box_alignment(self, style: dict) -> str:
|
||||
if style and "vertical-align" in style and style["vertical-align"] != "automatic":
|
||||
vertical_align = style["vertical-align"]
|
||||
else:
|
||||
|
||||
@@ -181,35 +181,35 @@ class SvgWriter:
|
||||
self.decimal_places = decimal_places
|
||||
for element in annotations:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj or element.ObjectType == "DRAWING":
|
||||
if not obj or (object_type := ifcopenshell.util.element.get_predefined_type(element)) == "DRAWING":
|
||||
continue
|
||||
elif element.ObjectType == "GRID":
|
||||
elif object_type == "GRID":
|
||||
self.draw_grid_annotation(obj)
|
||||
elif element.ObjectType == "TEXT_LEADER":
|
||||
elif object_type == "TEXT_LEADER":
|
||||
self.draw_leader_annotation(obj)
|
||||
elif element.ObjectType == "STAIR_ARROW":
|
||||
elif object_type == "STAIR_ARROW":
|
||||
self.draw_stair_annotation(obj)
|
||||
elif element.ObjectType == "DIMENSION":
|
||||
elif object_type == "DIMENSION":
|
||||
self.draw_dimension_annotations(obj)
|
||||
elif element.ObjectType == "ANGLE":
|
||||
elif object_type == "ANGLE":
|
||||
self.draw_angle_annotations(obj)
|
||||
elif element.ObjectType == "RADIUS":
|
||||
elif object_type == "RADIUS":
|
||||
self.draw_radius_annotations(obj)
|
||||
elif element.ObjectType == "DIAMETER":
|
||||
elif object_type == "DIAMETER":
|
||||
self.draw_diameter_annotations(obj)
|
||||
elif element.ObjectType == "ELEVATION":
|
||||
elif object_type == "ELEVATION":
|
||||
self.draw_elevation_annotation(obj)
|
||||
elif element.ObjectType == "SECTION":
|
||||
elif object_type == "SECTION":
|
||||
self.draw_section_annotation(obj)
|
||||
elif element.ObjectType == "BREAKLINE":
|
||||
elif object_type == "BREAKLINE":
|
||||
self.draw_break_annotations(obj)
|
||||
elif element.ObjectType == "PLAN_LEVEL":
|
||||
elif object_type == "PLAN_LEVEL":
|
||||
self.draw_plan_level_annotation(obj)
|
||||
elif element.ObjectType == "SECTION_LEVEL":
|
||||
elif object_type == "SECTION_LEVEL":
|
||||
self.draw_section_level_annotation(obj)
|
||||
elif element.ObjectType == "TEXT":
|
||||
elif object_type == "TEXT":
|
||||
self.draw_text_annotation(obj, obj.location)
|
||||
elif element.ObjectType in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
elif object_type in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.draw_fall_annotations(obj)
|
||||
else:
|
||||
self.draw_misc_annotation(obj)
|
||||
@@ -1207,13 +1207,14 @@ class SvgWriter:
|
||||
angle = 90
|
||||
|
||||
# ues SLOPE_ANGLE as default
|
||||
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type in ("FALL", "SLOPE_ANGLE"):
|
||||
return f"{angle}°"
|
||||
elif element.ObjectType == "SLOPE_FRACTION":
|
||||
elif object_type == "SLOPE_FRACTION":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{helper.format_distance(rise, precision=self.precision, decimal_places=self.decimal_places)} / {helper.format_distance(run, precision=self.precision, decimal_places=self.decimal_places)}"
|
||||
elif element.ObjectType == "SLOPE_PERCENT":
|
||||
elif object_type == "SLOPE_PERCENT":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{round(angle_tg * 100)} %"
|
||||
|
||||
@@ -49,7 +49,7 @@ class BIM_PT_camera(Panel):
|
||||
return
|
||||
|
||||
self.layout.use_property_split = True
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
|
||||
col = self.layout.column(align=True)
|
||||
@@ -161,7 +161,7 @@ class BIM_PT_drawing_underlay(Panel):
|
||||
layout.use_property_split = True
|
||||
camera = context.scene.camera
|
||||
assert camera
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = camera.data.BIMCameraProperties
|
||||
drawing_index_is_valid = props.active_drawing_style_index < len(dprops.drawing_styles)
|
||||
|
||||
@@ -229,7 +229,7 @@ class BIM_PT_drawings(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_drawings:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -302,7 +302,7 @@ class BIM_PT_schedules(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_schedules:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -352,7 +352,7 @@ class BIM_PT_references(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_references:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -394,7 +394,7 @@ class BIM_PT_sheets(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_sheets:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -520,7 +520,8 @@ class BIM_PT_text(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMTextProperties
|
||||
assert obj
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
|
||||
if props.is_editing:
|
||||
# shares most of the code with EditTextPopup.draw()
|
||||
@@ -600,7 +601,7 @@ class BIM_UL_drawinglist(bpy.types.UIList):
|
||||
selected_icon = "CHECKBOX_HLT" if item.is_selected else "CHECKBOX_DEHLT"
|
||||
row.prop(item, "is_selected", text="", icon=selected_icon, emboss=False)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if (
|
||||
self.props.drawings
|
||||
and self.props.active_drawing_id
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.core.type
|
||||
import bonsai.core.drawing as core
|
||||
import bonsai.tool as tool
|
||||
@@ -26,7 +27,7 @@ import ifcopenshell.util.representation
|
||||
from bonsai.bim.module.drawing.data import DecoratorData, AnnotationData
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bpy.types import WorkSpaceTool
|
||||
from typing import Union
|
||||
from functools import partial
|
||||
|
||||
|
||||
class LaunchAnnotationTypeManager(bpy.types.Operator):
|
||||
@@ -45,7 +46,7 @@ class LaunchAnnotationTypeManager(bpy.types.Operator):
|
||||
if not AnnotationData.is_loaded:
|
||||
AnnotationData.load()
|
||||
|
||||
props = context.scene.BIMAnnotationProperties
|
||||
props = tool.Drawing.get_annotation_props()
|
||||
|
||||
columns = self.layout.column_flow(columns=3)
|
||||
row = columns.row()
|
||||
@@ -134,28 +135,14 @@ class AnnotationTool(WorkSpaceTool):
|
||||
AnnotationToolUI.draw(context, layout)
|
||||
|
||||
|
||||
def add_layout_hotkey_operator(
|
||||
layout: bpy.types.UILayout, text: str, hotkey: str, description: Union[str, None]
|
||||
) -> tuple[bpy.types.OperatorProperties, bpy.types.UILayout]:
|
||||
modifiers = {
|
||||
"A": "EVENT_ALT",
|
||||
"S": "EVENT_SHIFT",
|
||||
}
|
||||
modifier, key = hotkey.split("_")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon=modifiers[modifier])
|
||||
row.label(text="", icon=f"EVENT_{key}")
|
||||
|
||||
op = row.operator("bim.annotation_hotkey", text=text)
|
||||
op.hotkey = hotkey
|
||||
op.description = description
|
||||
return op, row
|
||||
add_layout_hotkey_operator = partial(
|
||||
tool.Blender.add_layout_hotkey_operator, tool_name="annotation", module_name=__name__
|
||||
)
|
||||
|
||||
|
||||
# TODO: move to operator
|
||||
def create_annotation_occurrence(context):
|
||||
props = context.scene.BIMAnnotationProperties
|
||||
props = tool.Drawing.get_annotation_props()
|
||||
relating_type = tool.Ifc.get().by_id(int(props.relating_type_id))
|
||||
object_type = props.object_type
|
||||
|
||||
@@ -197,10 +184,12 @@ def create_annotation_occurrence(context):
|
||||
|
||||
|
||||
class AnnotationToolUI:
|
||||
layout: bpy.types.UILayout
|
||||
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
cls.props = context.scene.BIMAnnotationProperties
|
||||
cls.props = tool.Drawing.get_annotation_props()
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
if not tool.Ifc.get():
|
||||
@@ -224,7 +213,8 @@ class AnnotationToolUI:
|
||||
@classmethod
|
||||
def draw_create_object_interface(cls):
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(bpy.context.scene.DocProperties, "should_draw_decorations", text="Viewport Annotations")
|
||||
props = tool.Drawing.get_document_props()
|
||||
row.prop(props, "should_draw_decorations", text="Viewport Annotations")
|
||||
|
||||
@classmethod
|
||||
def draw_edit_object_interface(cls, context):
|
||||
@@ -261,8 +251,8 @@ class AnnotationToolUI:
|
||||
|
||||
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.annotation_hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = ""
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
hotkey: bpy.props.StringProperty()
|
||||
description: bpy.props.StringProperty()
|
||||
|
||||
@@ -279,19 +269,19 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"ERROR"}, "No drawing active for annotation hotkeys.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.props = context.scene.BIMAnnotationProperties
|
||||
self.props = tool.Drawing.get_annotation_props()
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
|
||||
def invoke(self, context, event):
|
||||
# https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
|
||||
self.props = context.scene.BIMAnnotationProperties
|
||||
self.props = tool.Drawing.get_annotation_props()
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
def hotkey_S_T(self):
|
||||
props = bpy.context.scene.BIMAnnotationProperties
|
||||
props = tool.Drawing.get_annotation_props()
|
||||
object_type = props.object_type
|
||||
|
||||
if object_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP:
|
||||
@@ -330,7 +320,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not element or not element.is_a("IfcAnnotation"):
|
||||
continue
|
||||
|
||||
annotation_type = element.ObjectType
|
||||
annotation_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if annotation_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP:
|
||||
self.report({"ERROR"}, f"Annotation type {annotation_type} is not supported for readjustment.")
|
||||
continue
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.fm.data import FMData
|
||||
|
||||
|
||||
@@ -40,11 +39,11 @@ class BIM_PT_fm(Panel):
|
||||
scene = context.scene
|
||||
props = scene.BIMFMProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
row = layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = layout.row()
|
||||
props.ifc_files.layout_file_select(row, "*.ifc;*.ifczip;*.ifcxml", "IFC File(s)")
|
||||
|
||||
|
||||
@@ -98,12 +98,14 @@ addon_keymaps = []
|
||||
|
||||
|
||||
@persistent
|
||||
def block_scale(scene):
|
||||
def block_scale(scene: bpy.types.Scene) -> None:
|
||||
import bonsai.tool as tool
|
||||
|
||||
if obj := (getattr(bpy.context, "active_object", None) or bpy.context.view_layer.objects.active):
|
||||
if isinstance(obj, bpy.types.Object) and obj.BIMObjectProperties.ifc_definition_id:
|
||||
if isinstance(obj, bpy.types.Object) and tool.Blender.get_ifc_definition_id(obj):
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
elif isinstance(obj, bpy.types.Mesh) and obj.BIMMeshProperties.ifc_definition_id:
|
||||
elif isinstance(obj, bpy.types.Mesh) and tool.Geometry.get_mesh_props(obj).ifc_definition_id:
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
|
||||
|
||||
@@ -53,15 +53,16 @@ class ViewportData:
|
||||
obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
modes = [obj_mode]
|
||||
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj:
|
||||
modes: list[tuple[str, str, str, str, int]] = [obj_mode]
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.representation_obj:
|
||||
modes.append(item_mode)
|
||||
|
||||
if not obj:
|
||||
return modes
|
||||
|
||||
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
pprops = tool.Project.get_project_props()
|
||||
if obj in pprops.clipping_planes_objs:
|
||||
pass
|
||||
elif element:
|
||||
if tool.Geometry.is_locked(element):
|
||||
@@ -107,8 +108,9 @@ class RepresentationsData:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
active_representation_id = None
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties"):
|
||||
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
if active_representation:
|
||||
active_representation_id = active_representation.id()
|
||||
|
||||
for representation in tool.Geometry.get_representations_iter(element):
|
||||
representation_type = representation.RepresentationType
|
||||
@@ -158,9 +160,9 @@ class RepresentationsData:
|
||||
if not obj.data:
|
||||
return []
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not (active_representation_id := obj.data.BIMMeshProperties.ifc_definition_id):
|
||||
base_representation = tool.Geometry.get_active_representation(obj)
|
||||
if not base_representation:
|
||||
return [] # Maybe in profile editing mode
|
||||
base_representation = tool.Ifc.get().by_id(active_representation_id)
|
||||
|
||||
# shape aspects matching context of the active representation
|
||||
matching_shape_aspects = []
|
||||
@@ -390,7 +392,7 @@ class PlacementData:
|
||||
def load(cls):
|
||||
cls.data = {"has_placement": cls.has_placement()}
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
obj = bpy.context.active_object
|
||||
if obj and props.has_blender_offset:
|
||||
xyz = cls.original_xyz(obj)
|
||||
@@ -413,7 +415,7 @@ class PlacementData:
|
||||
@classmethod
|
||||
def original_xyz(cls, obj):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
xyz = ifcopenshell.util.geolocation.xyz2enh(
|
||||
obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][3],
|
||||
|
||||
@@ -46,12 +46,14 @@ class ItemDecorator:
|
||||
obj_is_boolean: dict[str, list[ifcopenshell.entity_instance]] = {}
|
||||
objs: dict[str, dict[str, list]] = {}
|
||||
obj_matrix: dict[str, Matrix] = {}
|
||||
for item_obj in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item_obj in props.item_objs:
|
||||
if obj := item_obj.obj:
|
||||
obj: bpy.types.Object
|
||||
objs[obj.name] = cls.get_obj_data(obj)
|
||||
obj_is_selected[obj.name] = obj.select_get()
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
obj_is_boolean[obj.name] = [i for i in tool.Ifc.get().get_inverse(item) if i.is_a("IfcBooleanResult")]
|
||||
obj_matrix[obj.name] = obj.matrix_world.copy()
|
||||
|
||||
@@ -143,7 +145,8 @@ class ItemDecorator:
|
||||
color = selected_elements_color
|
||||
blf.color(font_id, *color)
|
||||
|
||||
for item in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item in props.item_objs:
|
||||
if (obj := item.obj) and obj.hide_get() == False:
|
||||
if obj.select_get():
|
||||
centroid = obj.matrix_world @ Vector(obj.bound_box[0]).lerp(Vector(obj.bound_box[6]), 0.5)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
from typing import Optional, TYPE_CHECKING, Union, Literal
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
@@ -270,6 +270,9 @@ class BIMObjectGeometryProperties(PropertyGroup):
|
||||
representation_item_layer: str
|
||||
|
||||
|
||||
GeometryMode = Literal["OBJECT", "ITEM", "EDIT"]
|
||||
|
||||
|
||||
class BIMGeometryProperties(PropertyGroup):
|
||||
# Revit workaround
|
||||
should_use_presentation_style_assignment: BoolProperty(name="Force Presentation Style Assignment", default=False)
|
||||
@@ -308,7 +311,7 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
should_force_faceted_brep: bool
|
||||
should_force_triangulation: bool
|
||||
is_changing_mode: bool
|
||||
mode: str
|
||||
mode: GeometryMode
|
||||
representation_obj: Union[bpy.types.Object, None]
|
||||
item_objs: bpy.types.bpy_prop_collection_idprop[RepresentationItemObject]
|
||||
representation_from_object: Union[bpy.types.Object, None]
|
||||
|
||||
@@ -20,7 +20,6 @@ import bpy
|
||||
import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, Menu, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.geometry.data import (
|
||||
RepresentationsData,
|
||||
@@ -32,13 +31,31 @@ from bonsai.bim.module.geometry.data import (
|
||||
from bonsai.bim.module.layer.data import LayersData
|
||||
|
||||
|
||||
class UIData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"menu_icon_color_mode": cls.icon_color_mode("user_interface.wcol_menu.text")}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def icon_color_mode(cls, color_path):
|
||||
return tool.Blender.detect_icon_color_mode(color_path)
|
||||
|
||||
|
||||
def mode_menu(self, context):
|
||||
if not tool.Ifc.get():
|
||||
return
|
||||
if not UIData.is_loaded:
|
||||
UIData.load()
|
||||
ifc_icon = f"{UIData.data['menu_icon_color_mode']}_ifc"
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "EDIT":
|
||||
row.operator("bim.override_mode_set_object", icon="CANCEL", text="Discard Changes").should_save = False
|
||||
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=bonsai.bim.icons["IFC"].icon_id)
|
||||
row.prop(props, "mode", text="", icon_value=bonsai.bim.icons[ifc_icon].icon_id)
|
||||
|
||||
|
||||
def object_menu(self, context):
|
||||
@@ -105,6 +122,7 @@ class BIM_MT_object_set_origin(Menu):
|
||||
def outliner_menu(self, context):
|
||||
self.layout.separator()
|
||||
self.layout.operator("bim.override_outliner_delete", icon="X")
|
||||
self.layout.operator("bim.override_paste_buffer", icon="PLUGIN")
|
||||
|
||||
|
||||
class BIM_PT_representations(Panel):
|
||||
@@ -206,7 +224,9 @@ class BIM_PT_representation_items(Panel):
|
||||
if not RepresentationItemsData.is_loaded:
|
||||
RepresentationItemsData.load()
|
||||
|
||||
obj = context.scene.BIMGeometryProperties.representation_obj or tool.Blender.get_active_object()
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
obj = props.representation_obj or tool.Blender.get_active_object()
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
@@ -314,18 +334,17 @@ class BIM_PT_connections(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(tool.Blender.get_ifc_definition_id(obj)):
|
||||
return False
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not ConnectionsData.is_loaded:
|
||||
ConnectionsData.load()
|
||||
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMObjectProperties
|
||||
|
||||
if not ConnectionsData.data["connections"] and not ConnectionsData.data["is_connection_realization"]:
|
||||
layout.label(text="No connections found")
|
||||
@@ -391,15 +410,18 @@ class BIM_PT_mesh(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if not context.active_object.data:
|
||||
return
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Advanced Users Only", icon="ERROR")
|
||||
|
||||
@@ -407,7 +429,7 @@ class BIM_PT_mesh(Panel):
|
||||
|
||||
row = layout.row()
|
||||
text = "Manually Save Representation"
|
||||
if tool.Ifc.is_edited(context.active_object):
|
||||
if tool.Ifc.is_edited(obj):
|
||||
text += "*"
|
||||
row.operator("bim.update_representation", text=text)
|
||||
|
||||
@@ -434,8 +456,8 @@ class BIM_PT_mesh(Panel):
|
||||
op = row.operator("bim.update_representation", text="Convert To Arbitrary Extrusion With Voids")
|
||||
op.ifc_representation_class = "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if True:
|
||||
mprops = tool.Geometry.get_mesh_props(mesh)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -457,12 +479,16 @@ class BIM_PT_placement(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
return (obj := context.active_object) and tool.Blender.get_ifc_definition_id(obj)
|
||||
|
||||
def draw(self, context):
|
||||
if not PlacementData.is_loaded:
|
||||
PlacementData.load()
|
||||
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
|
||||
if not PlacementData.data["has_placement"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Object Placement Found")
|
||||
@@ -473,12 +499,12 @@ class BIM_PT_placement(Panel):
|
||||
row = self.layout.row()
|
||||
row.prop(context.active_object, "rotation_euler", text="Rotation")
|
||||
|
||||
if context.active_object.BIMObjectProperties.blender_offset_type != "NONE":
|
||||
if props.blender_offset_type != "NONE":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
||||
row.label(text=context.active_object.BIMObjectProperties.blender_offset_type)
|
||||
row.label(text=props.blender_offset_type)
|
||||
|
||||
if context.active_object.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE":
|
||||
if props.blender_offset_type != "NOT_APPLICABLE":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=PlacementData.data["original_x"], icon="EMPTY_AXIS")
|
||||
row.label(text=PlacementData.data["original_y"])
|
||||
@@ -551,14 +577,14 @@ class BIM_PT_workarounds(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMGeometryProperties
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_force_faceted_brep")
|
||||
row = self.layout.row()
|
||||
@@ -568,7 +594,7 @@ class BIM_PT_workarounds(Panel):
|
||||
|
||||
|
||||
class BIM_UL_representation_items(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(self, context, layout: bpy.types.UILayout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
icon = "MATERIAL" if item.surface_style else "MESH_UVSPHERE"
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -167,7 +167,7 @@ class GeoreferenceData:
|
||||
result["rotation"] = str(round(ifcopenshell.util.geolocation.yaxis2angle(*wcs[:, 1][:2]), 3))
|
||||
result["x"], result["y"], result["z"] = wcs[:, 3][:3]
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_xyz = ifcopenshell.util.geolocation.enh2xyz(
|
||||
result["x"],
|
||||
@@ -193,7 +193,7 @@ class GeoreferenceData:
|
||||
|
||||
@classmethod
|
||||
def local_origin(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.has_blender_offset:
|
||||
return
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
@@ -21,6 +21,7 @@ import blf
|
||||
import gpu
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.geolocation
|
||||
import bonsai.tool as tool
|
||||
from math import radians
|
||||
from bpy.types import SpaceView3D
|
||||
@@ -53,7 +54,8 @@ class GeoreferenceDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
self.scale = bpy.context.scene.BIMGeoreferenceProperties.visualization_scale
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
self.scale = props.visualization_scale
|
||||
content_pos = [v * self.scale for v in content_pos]
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
@@ -64,7 +66,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -177,7 +179,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -350,7 +352,7 @@ class GeoreferenceDecorator:
|
||||
self.gn_angle = float(GeoreferenceData.data["map_derived_angle"] or 0)
|
||||
self.tn_angle = float(GeoreferenceData.data["true_derived_angle"] or 0)
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_angle = ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
|
||||
|
||||
@@ -33,15 +33,18 @@ from bpy.props import (
|
||||
)
|
||||
from bonsai.bim.module.georeference.data import GeoreferenceData
|
||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
def get_coordinate_operation_class(self, context):
|
||||
def get_coordinate_operation_class(
|
||||
self: "BIMGeoreferenceProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
return GeoreferenceData.data["coordinate_operation_class"]
|
||||
|
||||
|
||||
def update_true_north_angle(self, context):
|
||||
def update_true_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -54,7 +57,7 @@ def update_true_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_true_north_vector(self, context):
|
||||
def update_true_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -67,7 +70,7 @@ def update_true_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_angle(self, context):
|
||||
def update_grid_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -81,7 +84,7 @@ def update_grid_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_vector(self, context):
|
||||
def update_grid_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -95,15 +98,15 @@ def update_grid_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_should_visualise(self, context):
|
||||
def update_should_visualise(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.should_visualise:
|
||||
GeoreferenceDecorator.install(bpy.context)
|
||||
else:
|
||||
GeoreferenceDecorator.uninstall()
|
||||
|
||||
|
||||
def update_blender_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_blender_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -123,8 +126,8 @@ def update_blender_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_local_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_local_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -147,8 +150,8 @@ def update_local_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_map_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_map_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -248,3 +251,45 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
||||
wcs_y: StringProperty(name="WCS Y", default="0")
|
||||
wcs_z: StringProperty(name="WCS Z", default="0")
|
||||
wcs_rotation: StringProperty(name="WCS Rotation", default="0")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
coordinate_operation_class: str
|
||||
is_changing_angle: bool
|
||||
is_editing: bool
|
||||
is_editing_wcs: bool
|
||||
is_editing_true_north: bool
|
||||
coordinate_operation: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
projected_crs: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
is_updating_coordinates: bool
|
||||
blender_coordinates: str
|
||||
local_coordinates: str
|
||||
map_coordinates: str
|
||||
should_visualise: bool
|
||||
visualization_scale: float
|
||||
grid_north_angle: str
|
||||
x_axis_abscissa: str
|
||||
x_axis_ordinate: str
|
||||
x_axis_is_null: bool
|
||||
|
||||
host_model_origin: str
|
||||
host_model_origin_si: str
|
||||
host_model_project_north: str
|
||||
|
||||
model_origin: str
|
||||
model_origin_si: str
|
||||
model_project_north: str
|
||||
|
||||
has_blender_offset: bool
|
||||
blender_offset_x: str
|
||||
blender_offset_y: str
|
||||
blender_offset_z: str
|
||||
blender_x_axis_abscissa: str
|
||||
blender_x_axis_ordinate: str
|
||||
|
||||
true_north_angle: str
|
||||
true_north_abscissa: str
|
||||
true_north_ordinate: str
|
||||
wcs_x: str
|
||||
wcs_y: str
|
||||
wcs_z: str
|
||||
wcs_rotation: str
|
||||
|
||||
@@ -32,7 +32,7 @@ class BIM_PT_gis(Panel):
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
def draw_header(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
icon = "HIDE_OFF" if props.should_visualise else "HIDE_ON"
|
||||
row.label(text="") # empty text occupies the left of the row
|
||||
@@ -43,7 +43,7 @@ class BIM_PT_gis(Panel):
|
||||
def draw(self, context):
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
@@ -54,7 +54,7 @@ class BIM_PT_gis(Panel):
|
||||
self.draw_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||
@@ -87,7 +87,7 @@ class BIM_PT_gis(Panel):
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
def draw_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
row = self.layout.row()
|
||||
@@ -149,7 +149,7 @@ class BIM_PT_gis_true_north(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
self.props = context.scene.BIMGeoreferenceProperties
|
||||
self.props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if self.props.is_editing_true_north:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -200,7 +200,7 @@ class BIM_PT_gis_blender(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row()
|
||||
@@ -233,7 +233,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.is_editing_wcs:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -263,7 +263,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
row.operator("bim.enable_editing_wcs", icon="GREASEPENCIL", text="")
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="World Coordinate System", icon="EMPTY_ARROWS")
|
||||
@@ -293,7 +293,7 @@ class BIM_PT_gis_calculator(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -22,7 +22,6 @@ import ifcopenshell.api.group
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
import json
|
||||
|
||||
|
||||
@@ -129,8 +128,7 @@ class RemoveGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
group: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMGroupProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run("group.remove_group", self.file, **{"group": self.file.by_id(self.group)})
|
||||
bpy.ops.bim.load_groups()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.group.data import GroupsData, ObjectGroupsData
|
||||
|
||||
@@ -34,7 +34,7 @@ class BIM_PT_groups(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not GroupsData.is_loaded:
|
||||
@@ -91,9 +91,10 @@ class BIM_PT_object_groups(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
return tool.Ifc.get() and props.ifc_definition_id
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectGroupsData.is_loaded:
|
||||
|
||||
@@ -374,16 +374,17 @@ class ObjectLog(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
cls.poll_message_set("No Active Object")
|
||||
elif not context.active_object.BIMObjectProperties.ifc_definition_id:
|
||||
elif not tool.Blender.get_ifc_definition_id(obj):
|
||||
cls.poll_message_set("Active Object doesn't have an IFC definition")
|
||||
else:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
step_id = context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
step_id = tool.Blender.get_ifc_definition_id(obj)
|
||||
core.entity_log(tool.IfcGit, tool.Ifc, step_id, self)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadLayers(bpy.types.Operator):
|
||||
@@ -33,7 +32,7 @@ class LoadLayers(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.BIMLayerProperties
|
||||
props.layers.clear()
|
||||
for layer in tool.Ifc.get().by_type("IfcPresentationLayerAssignment"):
|
||||
@@ -126,7 +125,7 @@ class RemovePresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMLayerProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run("layer.remove_layer", self.file, **{"layer": self.file.by_id(self.layer)})
|
||||
bpy.ops.bim.load_layers()
|
||||
return {"FINISHED"}
|
||||
@@ -142,12 +141,12 @@ class AssignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"layer.assign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"items": [self.file.by_id(tool.Geometry.get_mesh_props(item).ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
@@ -164,15 +163,10 @@ class UnassignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"layer.unassign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
ifc_file = tool.Ifc.get()
|
||||
representation = tool.Geometry.get_data_representation(item)
|
||||
assert representation
|
||||
ifcopenshell.api.layer.unassign_layer(ifc_file, items=[representation], layer=ifc_file.by_id(self.layer))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList, Mesh
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.layer.data import LayersData
|
||||
|
||||
@@ -34,7 +34,7 @@ class BIM_PT_layers(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not LayersData.is_loaded:
|
||||
@@ -77,7 +77,6 @@ class BIM_UL_layers(UIList):
|
||||
row.label(text=item.name)
|
||||
|
||||
if context.active_object and isinstance(context.active_object.data, Mesh):
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if item.ifc_definition_id in LayersData.data["active_layers"]:
|
||||
op = row.operator("bim.unassign_presentation_layer", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
op.layer = item.ifc_definition_id
|
||||
|
||||
@@ -47,7 +47,8 @@ class LibrariesData:
|
||||
|
||||
@classmethod
|
||||
def library_attributes(cls):
|
||||
library_id = bpy.context.scene.BIMLibraryProperties.active_library_id
|
||||
props = tool.Library.get_library_props()
|
||||
library_id = props.active_library_id
|
||||
if not library_id:
|
||||
return []
|
||||
results = []
|
||||
@@ -63,7 +64,7 @@ class LibrariesData:
|
||||
|
||||
@classmethod
|
||||
def reference_attributes(cls):
|
||||
props = bpy.context.scene.BIMLibraryProperties
|
||||
props = tool.Library.get_library_props()
|
||||
try:
|
||||
reference_id = props.references[props.active_reference_index].ifc_definition_id
|
||||
except:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bonsai.bim.module.library.data import LibrariesData
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -30,16 +31,31 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
|
||||
def update_active_reference_index(self, context):
|
||||
LibrariesData.is_loaded = False
|
||||
|
||||
|
||||
def update_library_element_name(self: "LibraryReference", context: bpy.types.Context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
element = ifc_file.by_id(self.ifc_definition_id)
|
||||
previous_name = element.Name
|
||||
if self.name == previous_name:
|
||||
return
|
||||
element.Name = self.name
|
||||
LibrariesData.is_loaded = False
|
||||
|
||||
|
||||
class LibraryReference(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
name: StringProperty(name="Name", update=update_library_element_name)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
ifc_definition_id: int
|
||||
|
||||
|
||||
class BIMLibraryProperties(PropertyGroup):
|
||||
editing_mode: EnumProperty(
|
||||
@@ -58,3 +74,12 @@ class BIMLibraryProperties(PropertyGroup):
|
||||
active_reference_id: IntProperty(name="Active Reference Id")
|
||||
references: CollectionProperty(type=LibraryReference, name="References")
|
||||
active_reference_index: IntProperty(name="Active Reference Index", update=update_active_reference_index)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
editing_mode: Literal["NONE", "LIBRARY", "REFERENCES", "REFERENCE"]
|
||||
library_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_library_id: int
|
||||
reference_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_reference_id: int
|
||||
references: bpy.types.bpy_prop_collection_idprop[LibraryReference]
|
||||
active_reference_index: int
|
||||
|
||||
@@ -16,10 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.module.library.data import LibrariesData, LibraryReferencesData
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.library.prop import BIMLibraryProperties, LibraryReference
|
||||
|
||||
|
||||
class BIM_PT_libraries(Panel):
|
||||
@@ -38,7 +44,7 @@ class BIM_PT_libraries(Panel):
|
||||
def draw(self, context):
|
||||
if not LibrariesData.is_loaded:
|
||||
LibrariesData.load()
|
||||
self.props = context.scene.BIMLibraryProperties
|
||||
self.props = tool.Library.get_library_props()
|
||||
|
||||
if self.props.editing_mode == "LIBRARY":
|
||||
self.draw_editable_library_ui()
|
||||
@@ -110,7 +116,7 @@ class BIM_PT_library_references(Panel):
|
||||
def draw(self, context):
|
||||
if not LibraryReferencesData.is_loaded:
|
||||
LibraryReferencesData.load()
|
||||
self.props = context.scene.BIMLibraryProperties
|
||||
self.props = tool.Library.get_library_props()
|
||||
|
||||
if self.props.editing_mode == "REFERENCES":
|
||||
self.layout.template_list(
|
||||
@@ -129,10 +135,12 @@ class BIM_PT_library_references(Panel):
|
||||
|
||||
|
||||
class BIM_UL_library_references(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self, context, layout: bpy.types.UILayout, data, item: LibraryReference, icon, active_data, active_propname
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
op = row.operator("bim.enable_editing_library_reference", text="", icon="GREASEPENCIL")
|
||||
op.reference = item.ifc_definition_id
|
||||
op = row.operator("bim.remove_library_reference", text="", icon="X")
|
||||
@@ -140,9 +148,11 @@ class BIM_UL_library_references(UIList):
|
||||
|
||||
|
||||
class BIM_UL_object_library_references(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self, context, layout: bpy.types.UILayout, data, item: LibraryReference, icon, active_data, active_propname
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
op = row.operator("bim.assign_library_reference", text="", icon="ADD")
|
||||
op.reference = item.ifc_definition_id
|
||||
|
||||
@@ -48,7 +48,8 @@ class MaterialsData:
|
||||
|
||||
@classmethod
|
||||
def total_materials(cls):
|
||||
return len(tool.Ifc.get().by_type(bpy.context.scene.BIMMaterialProperties.material_type))
|
||||
props = tool.Material.get_material_props()
|
||||
return len(tool.Ifc.get().by_type(props.material_type))
|
||||
|
||||
@classmethod
|
||||
def material_types(cls):
|
||||
@@ -101,7 +102,7 @@ class MaterialsData:
|
||||
|
||||
@classmethod
|
||||
def material_styles_data(cls) -> dict[int, list[dict[str, Any]]]:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
material_styles_data: dict[int, list[dict[str, Any]]] = {}
|
||||
|
||||
for material_item in props.materials:
|
||||
@@ -273,6 +274,8 @@ class ObjectMaterialData:
|
||||
"icon": icon,
|
||||
"material_id": material_id,
|
||||
}
|
||||
if item.is_a("IfcMaterialProfile"):
|
||||
data["profile_id"] = item.Profile.id()
|
||||
if item.is_a("IfcMaterialProfile") and not item.Name:
|
||||
if item.Profile:
|
||||
data["name"] = item.Profile.ProfileName or "Unnamed"
|
||||
@@ -281,8 +284,9 @@ class ObjectMaterialData:
|
||||
if item.is_a("IfcMaterialLayer"):
|
||||
total_thickness = item.LayerThickness
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.material
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.representation
|
||||
@@ -28,7 +29,6 @@ import bonsai.tool as tool
|
||||
import bonsai.core.style
|
||||
import bonsai.core.material as core
|
||||
import bonsai.bim.module.model.profile as model_profile
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from typing import Any, Union, TYPE_CHECKING
|
||||
from bonsai.bim.module.model import wall, slab
|
||||
|
||||
@@ -40,7 +40,8 @@ class LoadMaterials(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.load_materials(tool.Material, context.scene.BIMMaterialProperties.material_type)
|
||||
props = tool.Material.get_material_props()
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -66,7 +67,10 @@ class SelectByMaterial(bpy.types.Operator):
|
||||
core.select_by_material(tool.Material, tool.Spatial, material=material)
|
||||
|
||||
# copy selection query to clipboard
|
||||
material_name = material.Name
|
||||
if material.is_a("IfcMaterialLayerSet"):
|
||||
material_name = material.LayerSetName
|
||||
else:
|
||||
material_name = material.Name
|
||||
result = f'material="{material_name}"'
|
||||
bpy.context.window_manager.clipboard = result
|
||||
self.report({"INFO"}, f"({result}) was copied to the clipboard.")
|
||||
@@ -116,7 +120,7 @@ class AssignParameterizedProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
profile = ifcopenshell.api.run(
|
||||
"profile.add_parameterized_profile",
|
||||
self.file,
|
||||
@@ -258,7 +262,8 @@ class AssignMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not (material_type := properties.material_type):
|
||||
if not (obj := context.active_object):
|
||||
return ""
|
||||
material_type = obj.BIMObjectMaterialProperties.material_type
|
||||
omprops = tool.Material.get_object_material_props(obj)
|
||||
material_type = omprops.material_type
|
||||
|
||||
description = "Assign current IfcMaterial to the selected objects"
|
||||
if material_type != "IfcMaterial":
|
||||
@@ -290,14 +295,12 @@ class AddConstituent(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_constituent",
|
||||
omprops = tool.Material.get_object_material_props(obj)
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.material.add_constituent(
|
||||
self.file,
|
||||
**{
|
||||
"constituent_set": self.file.by_id(self.constituent_set),
|
||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
},
|
||||
constituent_set=self.file.by_id(self.constituent_set),
|
||||
material=self.file.by_id(int(omprops.material)),
|
||||
)
|
||||
|
||||
|
||||
@@ -314,7 +317,7 @@ class RemoveConstituent(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if len(material_set.MaterialConstituents) == 1:
|
||||
self.report({"ERROR"}, "At least one constituent must exist")
|
||||
return {"CANCELLED"}
|
||||
ifcopenshell.api.run("material.remove_constituent", tool.Ifc.get(), constituent=constituent)
|
||||
ifcopenshell.api.material.remove_constituent(tool.Ifc.get(), constituent=constituent)
|
||||
|
||||
|
||||
class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -326,13 +329,15 @@ class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_profile",
|
||||
assert obj
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Material.get_material_props()
|
||||
omprops = tool.Material.get_object_material_props(obj)
|
||||
ifcopenshell.api.material.add_profile(
|
||||
self.file,
|
||||
profile_set=self.file.by_id(self.profile_set),
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
profile=self.file.by_id(int(context.scene.BIMMaterialProperties.profiles)),
|
||||
material=self.file.by_id(int(omprops.material)),
|
||||
profile=self.file.by_id(int(props.profiles)),
|
||||
)
|
||||
|
||||
|
||||
@@ -349,7 +354,7 @@ class RemoveProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if len(material_set.MaterialProfiles) == 1:
|
||||
self.report({"ERROR"}, "At least one profile must exist")
|
||||
return {"CANCELLED"}
|
||||
ifcopenshell.api.run("material.remove_profile", tool.Ifc.get(), profile=profile)
|
||||
ifcopenshell.api.material.remove_profile(tool.Ifc.get(), profile=profile)
|
||||
|
||||
|
||||
class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -362,11 +367,13 @@ class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
assert obj
|
||||
omprops = tool.Material.get_object_material_props(obj)
|
||||
layer_set = tool.Ifc.get().by_id(self.layer_set)
|
||||
ifcopenshell.api.material.add_layer(
|
||||
tool.Ifc.get(),
|
||||
layer_set=layer_set,
|
||||
material=tool.Ifc.get().by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
material=tool.Ifc.get().by_id(int(omprops.material)),
|
||||
)
|
||||
slab.DumbSlabPlaner().regenerate_from_layer_set(layer_set)
|
||||
wall.DumbWallPlaner().regenerate_from_layer_set(layer_set)
|
||||
@@ -384,7 +391,7 @@ class ReorderMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
material_set = self.file.by_id(self.material_set)
|
||||
ifcopenshell.api.run(
|
||||
"material.reorder_set_item",
|
||||
@@ -410,7 +417,7 @@ class RemoveLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if len(material_set.MaterialLayers) == 1:
|
||||
self.report({"ERROR"}, "At least one layer must exist")
|
||||
return {"CANCELLED"}
|
||||
ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=layer)
|
||||
ifcopenshell.api.material.remove_layer(tool.Ifc.get(), layer=layer)
|
||||
for material_set in material_sets:
|
||||
slab.DumbSlabPlaner().regenerate_from_layer_set(material_set)
|
||||
wall.DumbWallPlaner().regenerate_from_layer_set(material_set)
|
||||
@@ -440,12 +447,13 @@ class AddListItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_list_item",
|
||||
assert obj
|
||||
omprops = tool.Material.get_object_material_props(obj)
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.material.add_list_item(
|
||||
self.file,
|
||||
material_list=self.file.by_id(self.list_item_set),
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
material=self.file.by_id(int(omprops.material)),
|
||||
)
|
||||
|
||||
|
||||
@@ -460,7 +468,7 @@ class RemoveListItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"material.remove_list_item",
|
||||
self.file,
|
||||
@@ -479,10 +487,12 @@ class EnableEditingAssignedMaterial(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
props = tool.Material.get_object_material_props(obj)
|
||||
props.is_editing = True
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
assert material
|
||||
|
||||
if material.is_a("IfcMaterial"):
|
||||
props.material = str(material.id())
|
||||
@@ -540,7 +550,8 @@ class DisableEditingAssignedMaterial(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
bpy.ops.bim.disable_editing_material_set_item()
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
props = tool.Material.get_object_material_props(obj)
|
||||
props.is_editing = False
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -554,11 +565,14 @@ class EditAssignedMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
material_set_usage: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
active_obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = active_obj.BIMObjectMaterialProperties
|
||||
assert active_obj
|
||||
props = tool.Material.get_object_material_props(active_obj)
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
assert element
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
assert material
|
||||
|
||||
objects = tool.Blender.get_selected_objects()
|
||||
|
||||
@@ -574,32 +588,40 @@ class EditAssignedMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
material_set = self.file.by_id(self.material_set)
|
||||
attributes = bonsai.bim.helper.export_attributes(props.material_set_attributes)
|
||||
ifcopenshell.api.run(
|
||||
"material.edit_assigned_material",
|
||||
ifcopenshell.api.material.edit_assigned_material(
|
||||
self.file,
|
||||
**{"element": material_set, "attributes": attributes},
|
||||
element=material_set,
|
||||
attributes=attributes,
|
||||
)
|
||||
|
||||
if self.material_set_usage:
|
||||
material_set_usage = self.file.by_id(self.material_set_usage)
|
||||
attributes = bonsai.bim.helper.export_attributes(props.material_set_usage_attributes)
|
||||
|
||||
if material_set_usage.is_a("IfcMaterialLayerSetUsage"):
|
||||
ifcopenshell.api.run(
|
||||
"material.edit_layer_usage",
|
||||
ifcopenshell.api.material.edit_layer_usage(
|
||||
self.file,
|
||||
**{"usage": material_set_usage, "attributes": attributes},
|
||||
)
|
||||
slab.DumbSlabPlaner().regenerate_from_layer_set(material_set_usage.ForLayerSet)
|
||||
wall.DumbWallPlaner().regenerate_from_layer_set(material_set_usage.ForLayerSet)
|
||||
elif material_set_usage.is_a("IfcMaterialProfileSetUsage"):
|
||||
if attributes.get("CardinalPoint", None):
|
||||
attributes["CardinalPoint"] = int(attributes["CardinalPoint"])
|
||||
ifcopenshell.api.run(
|
||||
"material.edit_profile_usage",
|
||||
self.file,
|
||||
**{"usage": material_set_usage, "attributes": attributes},
|
||||
usage=material_set_usage,
|
||||
attributes=attributes,
|
||||
)
|
||||
|
||||
layer_sets_to_regenerate = set()
|
||||
|
||||
for obj in objects:
|
||||
obj_element = tool.Ifc.get_entity(obj)
|
||||
obj_material_usage = ifcopenshell.util.element.get_material(obj_element)
|
||||
|
||||
if obj_material_usage and obj_material_usage.is_a("IfcMaterialLayerSetUsage"):
|
||||
obj_material_usage.OffsetFromReferenceLine = material.OffsetFromReferenceLine
|
||||
obj_material_usage.DirectionSense = material.DirectionSense
|
||||
obj_material_usage.ReferenceExtent = material.ReferenceExtent
|
||||
|
||||
layer_sets_to_regenerate.add(obj_material_usage.ForLayerSet)
|
||||
|
||||
for layer_set in layer_sets_to_regenerate:
|
||||
wall.DumbWallPlaner().regenerate_from_layer_set(layer_set)
|
||||
slab.DumbSlabPlaner().regenerate_from_layer_set(layer_set)
|
||||
|
||||
bpy.ops.bim.disable_editing_assigned_material(obj=active_obj.name)
|
||||
|
||||
|
||||
@@ -612,7 +634,8 @@ class EnableEditingMaterialSetItemProfile(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
self.props = tool.Material.get_object_material_props(obj)
|
||||
self.props.active_material_set_item_id = self.material_set_item
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
profile = tool.Ifc.get().by_id(self.material_set_item).Profile
|
||||
@@ -628,7 +651,8 @@ class DisableEditingMaterialSetItemProfile(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
self.props = tool.Material.get_object_material_props(obj)
|
||||
self.props.active_material_set_item_id = 0
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
return {"FINISHED"}
|
||||
@@ -643,7 +667,8 @@ class EditMaterialSetItemProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
self.props = tool.Material.get_object_material_props(obj)
|
||||
attributes = bonsai.bim.helper.export_attributes(self.props.material_set_item_profile_attributes)
|
||||
profile = tool.Ifc.get().by_id(self.material_set_item).Profile
|
||||
ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes)
|
||||
@@ -660,13 +685,15 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
material_set_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.mprops = context.scene.BIMMaterialProperties
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
self.mprops = tool.Material.get_material_props()
|
||||
self.props = tool.Material.get_object_material_props(obj)
|
||||
self.props.active_material_set_item_id = self.material_set_item
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
material_set_item = self.file.by_id(self.material_set_item)
|
||||
|
||||
@@ -690,7 +717,8 @@ class DisableEditingMaterialSetItem(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
assert obj
|
||||
props = tool.Material.get_object_material_props(obj)
|
||||
props.active_material_set_item_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -704,11 +732,14 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
mprops = context.scene.BIMMaterialProperties
|
||||
self.file = tool.Ifc.get()
|
||||
assert obj
|
||||
props = tool.Material.get_object_material_props(obj)
|
||||
mprops = tool.Material.get_material_props()
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
assert material
|
||||
|
||||
attributes = bonsai.bim.helper.export_attributes(props.material_set_item_attributes)
|
||||
|
||||
@@ -717,7 +748,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.file,
|
||||
constituent=self.file.by_id(self.material_set_item),
|
||||
attributes=attributes,
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||
material=self.file.by_id(int(props.material_set_item_material)),
|
||||
)
|
||||
elif material.is_a("IfcMaterialLayerSet"):
|
||||
layer = self.file.by_id(self.material_set_item)
|
||||
@@ -725,7 +756,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.file,
|
||||
layer=layer,
|
||||
attributes=attributes,
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||
material=self.file.by_id(int(props.material_set_item_material)),
|
||||
)
|
||||
slab.DumbSlabPlaner().regenerate_from_layer(layer)
|
||||
wall.DumbWallPlaner().regenerate_from_layer(layer)
|
||||
@@ -739,7 +770,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
profile=self.file.by_id(self.material_set_item),
|
||||
attributes=attributes,
|
||||
profile_def=profile_def,
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||
material=self.file.by_id(int(props.material_set_item_material)),
|
||||
)
|
||||
else:
|
||||
pass
|
||||
@@ -763,7 +794,7 @@ class ExpandMaterialCategory(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
for index, category in (
|
||||
(i, c)
|
||||
for i, c in enumerate(props.materials)
|
||||
@@ -792,7 +823,7 @@ class ContractMaterialCategory(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
for index, category in (
|
||||
(i, c)
|
||||
for i, c in enumerate(props.materials)
|
||||
@@ -812,7 +843,7 @@ class EnableEditingMaterialStyle(bpy.types.Operator):
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
props.active_material_id = self.material
|
||||
props.editing_material_type = "STYLE"
|
||||
|
||||
@@ -843,7 +874,7 @@ class EditMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
ifc_file = tool.Ifc.get()
|
||||
material = ifc_file.by_id(props.active_material_id)
|
||||
style = ifc_file.by_id(int(props.styles))
|
||||
@@ -867,7 +898,7 @@ class UnassignMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
context: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
material = tool.Ifc.get().by_id(props.materials[props.active_material_index].ifc_definition_id)
|
||||
style = tool.Ifc.get().by_id(self.style)
|
||||
context = tool.Ifc.get().by_id(self.context)
|
||||
@@ -888,7 +919,7 @@ class SelectMaterialInMaterialsUI(bpy.types.Operator):
|
||||
material_id: int
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
ifc_file = tool.Ifc.get()
|
||||
material_id = self.material_id
|
||||
material = ifc_file.by_id(material_id)
|
||||
|
||||
@@ -22,7 +22,6 @@ from ifcopenshell.util.doc import get_entity_doc
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
from bonsai.bim.module.profile.data import ProfileData
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -121,7 +120,7 @@ def set_material_name(self: "Material", new_category_name: str) -> None:
|
||||
material.Category = new_category_name
|
||||
|
||||
# Reload UI elements if necessary.
|
||||
props: "BIMMaterialProperties" = self.id_data.BIMMaterialProperties
|
||||
props = tool.Material.get_material_props()
|
||||
new_category_name_already_in_use = bool(
|
||||
next((m for m in props.materials if m.is_category and m.name == new_category_name), None)
|
||||
)
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
@@ -29,7 +28,7 @@ from bonsai.bim.module.drawing.helper import format_distance
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.material.prop import Material
|
||||
from bonsai.bim.module.material.prop import Material, BIMMaterialProperties
|
||||
|
||||
|
||||
class BIM_PT_materials(Panel):
|
||||
@@ -43,13 +42,13 @@ class BIM_PT_materials(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
|
||||
self.props = context.scene.BIMMaterialProperties
|
||||
self.props = tool.Material.get_material_props()
|
||||
material = tool.Material.get_active_material_item()
|
||||
material_id = material.ifc_definition_id if material else None
|
||||
|
||||
@@ -138,14 +137,14 @@ class BIM_PT_object_material(Panel):
|
||||
def poll(cls, context):
|
||||
if not tool.Blender.is_tab(context, "GEOMETRY"):
|
||||
return False
|
||||
if not context.active_object:
|
||||
if not (obj := context.active_object):
|
||||
return False
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
ifc_id = tool.Blender.get_ifc_definition_id(obj)
|
||||
if not ifc_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(ifc_id):
|
||||
return False
|
||||
if not hasattr(IfcStore.get_file().by_id(props.ifc_definition_id), "HasAssociations"):
|
||||
if not hasattr(tool.Ifc.get().by_id(ifc_id), "HasAssociations"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -153,10 +152,12 @@ class BIM_PT_object_material(Panel):
|
||||
if not ObjectMaterialData.is_loaded:
|
||||
ObjectMaterialData.load()
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.oprops = context.active_object.BIMObjectProperties
|
||||
self.props = context.active_object.BIMObjectMaterialProperties
|
||||
self.mprops = context.scene.BIMMaterialProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.file = tool.Ifc.get()
|
||||
self.oprops = tool.Blender.get_object_bim_props(obj)
|
||||
self.props = tool.Material.get_object_material_props(obj)
|
||||
self.mprops = tool.Material.get_material_props()
|
||||
|
||||
if not ObjectMaterialData.data["materials"]:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -317,7 +318,7 @@ class BIM_PT_object_material(Panel):
|
||||
):
|
||||
if "Profile" in ObjectMaterialData.data["material_class"]:
|
||||
op = row.operator("bim.profiles_ui_select", icon="ZOOM_SELECTED", text="")
|
||||
op.profile_id = set_item["id"]
|
||||
op.profile_id = set_item["profile_id"]
|
||||
op = row.operator("bim.enable_editing_material_set_item_profile", icon="ITALIC", text="")
|
||||
op.material_set_item = set_item["id"]
|
||||
op = row.operator("bim.enable_editing_material_set_item", icon="GREASEPENCIL", text="")
|
||||
@@ -351,9 +352,10 @@ class BIM_PT_object_material(Panel):
|
||||
if ObjectMaterialData.data["total_thickness"]:
|
||||
total_thickness = ObjectMaterialData.data["total_thickness"]
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
@@ -401,10 +403,16 @@ class BIM_PT_object_material(Panel):
|
||||
|
||||
class BIM_UL_materials(UIList):
|
||||
def draw_item(
|
||||
self, context, layout: bpy.types.UILayout, data, item: Material, icon, active_data, active_propname
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMMaterialProperties,
|
||||
item: Material,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
mprops = context.scene.BIMMaterialProperties
|
||||
material_type = mprops.material_type
|
||||
material_type = data.material_type
|
||||
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
@@ -425,6 +433,8 @@ class BIM_UL_materials(UIList):
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.ifc_definition_id == data.active_material_id:
|
||||
row.label(text="", icon="GREASEPENCIL")
|
||||
if material_type == "IfcMaterialList":
|
||||
row.label(text=item.name, icon="MATERIAL")
|
||||
else:
|
||||
|
||||
@@ -257,7 +257,7 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
|
||||
sources = []
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
if not tool.Blender.get_ifc_definition_id(obj):
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -299,7 +299,7 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Blender.select_and_activate_single_object(context, curve)
|
||||
|
||||
def get_absolute_matrix(self, matrix):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.global2local(
|
||||
|
||||
@@ -72,7 +72,9 @@ class DisableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
context.active_object.BIMArrayProperties.is_editing = -1
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
tool.Model.get_array_props(obj).is_editing = -1
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -84,8 +86,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMArrayProperties
|
||||
props = tool.Model.get_array_props(obj)
|
||||
|
||||
relating_obj = props.relating_array_object
|
||||
|
||||
@@ -119,7 +122,7 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMArrayProperties
|
||||
props = tool.Model.get_array_props(obj)
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
@@ -182,7 +185,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMArrayProperties
|
||||
props = tool.Model.get_array_props(obj)
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
data = json.loads(pset["Data"])
|
||||
@@ -302,7 +305,8 @@ class Input3DCursorXArray(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMArrayProperties
|
||||
assert obj
|
||||
props = tool.Model.get_array_props(obj)
|
||||
cursor = context.scene.cursor
|
||||
if props.use_local_space:
|
||||
props.x = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).x
|
||||
@@ -318,7 +322,8 @@ class Input3DCursorYArray(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMArrayProperties
|
||||
assert obj
|
||||
props = tool.Model.get_array_props(obj)
|
||||
cursor = context.scene.cursor
|
||||
if props.use_local_space:
|
||||
props.y = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).y
|
||||
@@ -334,7 +339,8 @@ class Input3DCursorZArray(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMArrayProperties
|
||||
assert obj
|
||||
props = tool.Model.get_array_props(obj)
|
||||
cursor = context.scene.cursor
|
||||
if props.use_local_space:
|
||||
props.z = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).z
|
||||
|
||||
@@ -26,7 +26,7 @@ from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||
import bonsai.tool as tool
|
||||
from math import degrees
|
||||
from natsort import natsorted
|
||||
from typing import Union
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -43,31 +43,25 @@ def refresh():
|
||||
|
||||
class AuthoringData:
|
||||
data = {}
|
||||
type_thumbnails = {}
|
||||
type_thumbnails: dict[int, int] = {}
|
||||
types_per_page = 9
|
||||
ifc_element_type = None
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls, ifc_element_type=None):
|
||||
def load(cls, ifc_element_type: Optional[str] = None):
|
||||
cls.is_loaded = True
|
||||
cls.props = bpy.context.scene.BIMModelProperties
|
||||
if ifc_element_type:
|
||||
cls.ifc_element_type = None if ifc_element_type == "all" else ifc_element_type
|
||||
cls.props = tool.Model.get_model_props()
|
||||
cls.data["default_container"] = cls.default_container()
|
||||
cls.data["ifc_element_type"] = cls.ifc_element_type
|
||||
cls.data["ifc_element_type"] = ifc_element_type
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["ifc_class_current"] = cls.ifc_class_current()
|
||||
# Make sure .ifc_classes() was run before next lines
|
||||
cls.data["type_elements"] = cls.type_elements()
|
||||
cls.data["type_elements_filtered"] = cls.type_elements_filtered()
|
||||
# After .type_elements().
|
||||
cls.data["relating_type_id"] = cls.relating_type_id()
|
||||
# Make sure .relating_type_id() was run before next lines
|
||||
cls.data["relating_type_id_current"] = cls.relating_type_id_current()
|
||||
cls.data["relating_type_name"] = cls.relating_type_name()
|
||||
cls.data["relating_type_description"] = cls.relating_type_description()
|
||||
cls.data["relating_type_material_usage"] = cls.relating_type_material_usage()
|
||||
cls.data["predefined_type"] = cls.predefined_type()
|
||||
cls.data["relating_type_data"] = cls.relating_type_data()
|
||||
# Make sure .type_elements_filtered() was run before next lines
|
||||
cls.data["total_types"] = cls.total_types()
|
||||
cls.data["total_pages"] = cls.total_pages() # Only after .total_types()
|
||||
@@ -75,7 +69,6 @@ class AuthoringData:
|
||||
cls.data["prev_page"] = cls.prev_page()
|
||||
cls.data["paginated_relating_types"] = cls.paginated_relating_types()
|
||||
|
||||
cls.data["type_thumbnail"] = cls.type_thumbnail() # Only after .relating_type_id_current()
|
||||
cls.data["materials"] = cls.materials()
|
||||
cls.data["is_voidable_element"] = cls.is_voidable_element()
|
||||
cls.data["has_visible_openings"] = cls.has_visible_openings()
|
||||
@@ -97,7 +90,7 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def default_container(cls) -> str | None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props = tool.Spatial.get_spatial_props()
|
||||
if props.default_container:
|
||||
try:
|
||||
return tool.Ifc.get().by_id(props.default_container).Name
|
||||
@@ -112,10 +105,6 @@ class AuthoringData:
|
||||
version = tool.Ifc.get_schema()
|
||||
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def type_thumbnail(cls):
|
||||
return cls.type_thumbnails.get(int(cls.data["relating_type_id_current"] or 0), 0)
|
||||
|
||||
@classmethod
|
||||
def materials(cls):
|
||||
results = [("0", "None", "No material")]
|
||||
@@ -153,7 +142,7 @@ class AuthoringData:
|
||||
def type_elements_filtered(cls):
|
||||
search_query = cls.props.search_name.lower()
|
||||
|
||||
def filter_element(element):
|
||||
def filter_element(element: ifcopenshell.entity_instance) -> bool:
|
||||
if search_query in (element.Name or "Unnamed").lower():
|
||||
return True
|
||||
if search_query in (element.Description or "").lower():
|
||||
@@ -173,21 +162,24 @@ class AuthoringData:
|
||||
elements = cls.data["type_elements_filtered"]
|
||||
elements = elements[(cls.props.type_page - 1) * cls.types_per_page : cls.props.type_page * cls.types_per_page]
|
||||
for element in elements:
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if predefined_type == "NOTDEFINED":
|
||||
predefined_type = None
|
||||
results.append(
|
||||
{
|
||||
"id": element.id(),
|
||||
"ifc_class": element.is_a(),
|
||||
"name": element.Name or "Unnamed",
|
||||
"description": element.Description or "No Description",
|
||||
"predefined_type": predefined_type,
|
||||
"icon_id": cls.type_thumbnails.get(element.id(), None) or 0,
|
||||
}
|
||||
)
|
||||
results.append(cls.get_type_data(element))
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def get_type_data(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if predefined_type == "NOTDEFINED":
|
||||
predefined_type = None
|
||||
data = {
|
||||
"id": element.id(),
|
||||
"ifc_class": element.is_a(),
|
||||
"name": element.Name or "Unnamed",
|
||||
"description": element.Description or "No Description",
|
||||
"predefined_type": predefined_type,
|
||||
"icon_id": cls.type_thumbnails.get(element.id(), 0),
|
||||
}
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def is_voidable_element(cls):
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
@@ -316,38 +308,16 @@ class AuthoringData:
|
||||
return [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in elements]
|
||||
|
||||
@classmethod
|
||||
def relating_type_id_current(cls):
|
||||
def relating_type_data(cls) -> dict[str, Any]:
|
||||
relating_type_id = tool.Blender.get_enum_safe(cls.props, "relating_type_id")
|
||||
relating_type_id_data = cls.data["relating_type_id"]
|
||||
if not relating_type_id and relating_type_id_data:
|
||||
relating_type_id = relating_type_id_data[0][0]
|
||||
return relating_type_id
|
||||
|
||||
@classmethod
|
||||
def relating_type_name(cls):
|
||||
if relating_type_id := cls.data["relating_type_id_current"]:
|
||||
return tool.Ifc.get().by_id(int(relating_type_id)).Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def relating_type_description(cls):
|
||||
if relating_type_id := cls.data["relating_type_id_current"]:
|
||||
return tool.Ifc.get().by_id(int(relating_type_id)).Description or "No description"
|
||||
|
||||
@classmethod
|
||||
def relating_type_material_usage(cls):
|
||||
if relating_type_id := cls.data["relating_type_id_current"]:
|
||||
return tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id)))
|
||||
|
||||
@classmethod
|
||||
def predefined_type(cls):
|
||||
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"):
|
||||
return
|
||||
predefined_type = relating_type.PredefinedType
|
||||
return predefined_type
|
||||
if not relating_type_id_data:
|
||||
return {}
|
||||
relating_type_id = relating_type_id_data[0][0]
|
||||
ifc_file = tool.Ifc.get()
|
||||
relating_type = ifc_file.by_id(int(relating_type_id))
|
||||
return cls.get_type_data(relating_type)
|
||||
|
||||
@classmethod
|
||||
def selected_material_usages(cls):
|
||||
@@ -415,7 +385,9 @@ class StairData:
|
||||
|
||||
@classmethod
|
||||
def general_params(cls):
|
||||
props = bpy.context.active_object.BIMStairProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_stair_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
general_params = {}
|
||||
general_props = props.get_props_kwargs(stair_type=data["stair_type"])
|
||||
@@ -481,7 +453,9 @@ class WindowData:
|
||||
|
||||
@classmethod
|
||||
def general_params(cls):
|
||||
props = bpy.context.active_object.BIMWindowProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_window_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
general_params = {}
|
||||
general_props = props.get_general_kwargs()
|
||||
@@ -492,7 +466,9 @@ class WindowData:
|
||||
|
||||
@classmethod
|
||||
def lining_params(cls):
|
||||
props = bpy.context.active_object.BIMWindowProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_window_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
lining_data = data["lining_properties"]
|
||||
lining_params = {}
|
||||
@@ -504,7 +480,9 @@ class WindowData:
|
||||
|
||||
@classmethod
|
||||
def panel_params(cls):
|
||||
props = bpy.context.active_object.BIMWindowProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_window_props(obj)
|
||||
panel_data = cls.data["pset_data"]["data_dict"]["panel_properties"]
|
||||
panel_params = {}
|
||||
panel_props = props.get_panel_kwargs()
|
||||
@@ -535,7 +513,9 @@ class DoorData:
|
||||
|
||||
@classmethod
|
||||
def general_params(cls):
|
||||
props = bpy.context.active_object.BIMDoorProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_door_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
general_params = {}
|
||||
general_props = props.get_general_kwargs()
|
||||
@@ -546,7 +526,9 @@ class DoorData:
|
||||
|
||||
@classmethod
|
||||
def lining_params(cls):
|
||||
props = bpy.context.active_object.BIMDoorProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_door_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
lining_data = data["lining_properties"]
|
||||
lining_params = {}
|
||||
@@ -558,7 +540,9 @@ class DoorData:
|
||||
|
||||
@classmethod
|
||||
def panel_params(cls):
|
||||
props = bpy.context.active_object.BIMDoorProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_door_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
panel_data = cls.data["pset_data"]["data_dict"]["panel_properties"]
|
||||
panel_params = {}
|
||||
@@ -589,7 +573,9 @@ class RailingData:
|
||||
|
||||
@classmethod
|
||||
def general_params(cls):
|
||||
props = bpy.context.active_object.BIMRailingProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_railing_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
general_params = {}
|
||||
general_props = props.get_general_kwargs(railing_type=data["railing_type"])
|
||||
@@ -623,7 +609,9 @@ class RoofData:
|
||||
|
||||
@classmethod
|
||||
def general_params(cls):
|
||||
props = bpy.context.active_object.BIMRoofProperties
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
props = tool.Model.get_roof_props(obj)
|
||||
data = cls.data["pset_data"]["data_dict"]
|
||||
general_params = {}
|
||||
general_props = props.get_general_kwargs(generation_method=data["generation_method"])
|
||||
@@ -656,19 +644,19 @@ class ItemData:
|
||||
|
||||
@classmethod
|
||||
def representation_identifier(cls):
|
||||
props = bpy.context.scene.BIMGeometryProperties
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
rep = tool.Geometry.get_active_representation(props.representation_obj)
|
||||
return rep.RepresentationIdentifier
|
||||
|
||||
@classmethod
|
||||
def representation_type(cls):
|
||||
props = bpy.context.scene.BIMGeometryProperties
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
rep = tool.Geometry.get_active_representation(props.representation_obj)
|
||||
return rep.RepresentationType
|
||||
|
||||
@classmethod
|
||||
def representation_usage(cls):
|
||||
props = bpy.context.scene.BIMGeometryProperties
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
return tool.Model.get_usage_type(tool.Ifc.get_entity(props.representation_obj))
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -695,6 +695,8 @@ class PolylineDecorator:
|
||||
self.draw_batch("LINES", mouse_point + projection_point, decorator_color_unselected, edges)
|
||||
|
||||
if axis1 and axis2:
|
||||
axis1 = [tuple(tool.Polyline.use_transform_orientations(Vector(v))) for v in axis1]
|
||||
axis2 = [tuple(tool.Polyline.use_transform_orientations(Vector(v))) for v in axis2]
|
||||
self.line_shader.uniform_float("lineWidth", 1.5)
|
||||
self.draw_batch("LINES", axis1, highlight_color(axis_color1), [(0, 1)])
|
||||
self.draw_batch("LINES", axis2, highlight_color(axis_color2), [(0, 1)])
|
||||
@@ -829,7 +831,7 @@ class ProductDecorator:
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
|
||||
self.relating_type = None
|
||||
props = context.scene.BIMModelProperties
|
||||
props = tool.Model.get_model_props()
|
||||
relating_type_id = props.relating_type_id
|
||||
if relating_type_id:
|
||||
self.relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user