mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60ae4e80ce | |||
| 05ec36a37d | |||
| dc8ea31138 | |||
| faaea7db49 | |||
| b14ff94303 | |||
| b3dcfbf0dc | |||
| b68109d32b | |||
| 2ea3fe4ab9 | |||
| 94c159ccd0 | |||
| e4eead6662 | |||
| ee4e92dfa3 | |||
| f4ce2965f9 | |||
| 5fb7e6e375 | |||
| c399eb259a | |||
| 5ccdf187d5 | |||
| d53351d218 | |||
| 08a82c7b57 | |||
| 499cdbb9b8 | |||
| 4a5d1eba0b | |||
| 70091aa28b | |||
| 3d566cc401 | |||
| 6eb3c48144 | |||
| 44d180007e | |||
| 7e8060b224 | |||
| d408568d01 | |||
| 47704b1625 | |||
| 23fee9c8c8 | |||
| c5d1801e1e | |||
| e2dc5f4a48 | |||
| d28f62d327 | |||
| 70064359c3 | |||
| d8b74cf125 | |||
| 00c13c5ea4 | |||
| e26dae67aa | |||
| 6e30cd2ba7 | |||
| 0f9ea1e1e7 | |||
| 128ffc8fd7 | |||
| f67911902f | |||
| 64a1a3eb29 | |||
| fcf70ad160 | |||
| 116c9a5fd9 | |||
| 61b10a19c7 | |||
| 666f8e2459 | |||
| c303f216ad | |||
| 162e66b775 | |||
| c05abc4d5b | |||
| dacb3b101c | |||
| 696c6b09b0 | |||
| 6e738a0e3a | |||
| ba6cdc967c | |||
| 468baef5c8 | |||
| bda9dfd4cc | |||
| 930feb1d52 | |||
| e0b11aa66e | |||
| b2e7044373 | |||
| 77abe9a0a5 | |||
| e2c67c2f37 | |||
| a5d123fe83 | |||
| b7b99b6db7 | |||
| 2be0cc697a | |||
| dc92b973a0 | |||
| 5466be9b7b | |||
| b86dab2a76 | |||
| a57e063c14 | |||
| c7830e9e5b | |||
| d533ba1e6b | |||
| 45a1655485 | |||
| 3679fccfbd | |||
| cf9f792a3b | |||
| d87665de7e | |||
| 95daf7efa3 | |||
| 435ce8d58a | |||
| f92a2b3c8e | |||
| 5ea1c94822 | |||
| ab1bf7592e | |||
| f4247c3527 | |||
| 8c3d10a2bd | |||
| 1bda47f76e | |||
| 9001aa6383 | |||
| 097c5cee3c | |||
| 9d789425ce | |||
| c0b86ea016 | |||
| 6806eb0a05 | |||
| ce26d931c1 | |||
| eeaa0a5530 | |||
| a09cb5dee4 | |||
| 6035683492 | |||
| f27dbacd28 | |||
| 8817a7e6d5 | |||
| 80c4c13998 | |||
| 5159e2daee | |||
| 54951d2102 | |||
| 8403c32e76 | |||
| 316934de97 | |||
| dd48fbcb0f | |||
| 54978a9868 | |||
| 02fd78df14 | |||
| ed1202e1f2 | |||
| d9de967197 | |||
| e57076c84f | |||
| 75e6495999 | |||
| fcf07bebfb | |||
| f190ddcd92 | |||
| 39a841fb4e | |||
| bdfd0c4aa7 | |||
| 37b14f2a3d | |||
| 70a4af2605 | |||
| 7142f3856c |
@@ -39,7 +39,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py39, py310, py311]
|
||||
pyver: [py39, py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
|
||||
+29
-183
@@ -55,70 +55,28 @@ PYLIBDIR:=python3.11
|
||||
PYNUMBER:=311
|
||||
PYPI_VERSION:=3.11
|
||||
endif
|
||||
ifeq ($(PYVERSION), py312)
|
||||
PYLIBDIR:=python3.12
|
||||
PYNUMBER:=312
|
||||
PYPI_VERSION:=3.12
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
|
||||
LXML_ARCH:=manylinux_2_28_x86_64
|
||||
SHAPELY_ARCH:=manylinux_2_17_x86_64.manylinux2014_x86_64
|
||||
PILLOW_ARCH:=manylinux_2_28_x86_64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
PYPI_PLATFORM:=--platform macosx_10_9_x86_64
|
||||
SHAPELY_ARCH:=macosx_10_9_x86_64
|
||||
PILLOW_ARCH:=macosx_10_10_x86_64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
LXML_ARCH:=macosx_11_0_x86_64
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
LXML_ARCH:=macosx_11_0_x86_64
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
LXML_ARCH:=macosx_11_0_universal2
|
||||
endif
|
||||
PYPI_PLATFORM:=--platform macosx_10_10_x86_64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
PYPI_PLATFORM:=--platform macosx_11_0_arm64
|
||||
LXML_ARCH:=macosx_11_0_universal2
|
||||
SHAPELY_ARCH:=macosx_11_0_arm64
|
||||
PILLOW_ARCH:=macosx_11_0_arm64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), win)
|
||||
PYPI_PLATFORM:=--platform win_amd64
|
||||
LXML_ARCH:=win_amd64
|
||||
SHAPELY_ARCH:=win_amd64
|
||||
PILLOW_ARCH:=win_amd64
|
||||
endif
|
||||
|
||||
LXML_NAME:=lxml
|
||||
LXML_VER:=4.9.4
|
||||
SHAPELY_NAME:=shapely
|
||||
SHAPELY_VER:=2.0.2
|
||||
PILLOW_NAME:=pillow
|
||||
PILLOW_PKG_NAME:=Pillow
|
||||
PILLOW_VER:=9.5.0
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/s/$(SHAPELY_NAME)/$(SHAPELY_NAME)-$(SHAPELY_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(SHAPELY_ARCH).whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/p/$(PILLOW_NAME)/$(PILLOW_PKG_NAME)-$(PILLOW_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(PILLOW_ARCH).whl
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
ifeq ($(PYVERSION), py39)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h0520ce3_1.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py311h246f609_1.tar.bz2
|
||||
endif
|
||||
else
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/l/$(LXML_NAME)/$(LXML_NAME)-$(LXML_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(LXML_ARCH).whl
|
||||
endif
|
||||
$(info $$LXML_URL is [${LXML_URL}])
|
||||
$(info $$SHAPELY_URL is [${SHAPELY_URL}])
|
||||
$(info $$PILLOW_URL is [${PILLOW_URL}])
|
||||
|
||||
.PHONY: bump
|
||||
bump:
|
||||
cd . && $(SED) "s/$(OLD)/$(NEW)/" Makefile
|
||||
@@ -137,7 +95,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-f7c03db-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-c7830e9-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
@@ -192,49 +150,6 @@ endif
|
||||
cp -r dist/working/site-packages/git dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides Mustache templating in construction documentation
|
||||
# TODO: remove this dependency, it seems overkill and we seem to get by with str replaces
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/3f/e7/8750ba6c6101d6aa5ceeb20c013adf2c6f3554a12c71d75654b468404bfa/pystache-0.6.0.tar.gz
|
||||
cd dist/working && tar -xzvf pystache*
|
||||
cd dist/working/pystache-0.6.0/ && $(PYTHON) setup.py build && cp -r build/lib/pystache ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides SVG export in construction documentation
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/79/e8/7eb2ba188eda14a4b47e33b51f3b4978985f4116655c699bcd18c79279b5/svgwrite-1.3.1.zip
|
||||
cd dist/working && unzip svgwrite*
|
||||
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides fuzzy date parsing for construction sequencing
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
|
||||
cd dist/working && tar -xzvf python-dateutil*
|
||||
cp -r dist/working/python-dateutil-2.8.1/dateutil dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides duration parsing for construction sequencing
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz
|
||||
cd dist/working && tar -xzvf isodate*
|
||||
cp -r dist/working/isodate-0.6.0/src/isodate dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides networkx graph analysis for project dependency calculations
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/b0/21/adfbf6168631e28577e4af9eb9f26d75fe72b2bb1d33762a5f2c425e6c2a/networkx-2.5.1.tar.gz
|
||||
cd dist/working && tar -xzvf networkx*
|
||||
cp -r dist/working/networkx-2.5.1/networkx dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by networkx
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz
|
||||
cd dist/working && tar -xzvf decorator*
|
||||
cp -r dist/working/decorator-5.0.9/src/decorator.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides audio playback for costing
|
||||
mkdir -p dist/working
|
||||
git clone https://github.com/Andrej730/aud.git --branch master-reduced-size --depth 1 dist/working/aud
|
||||
@@ -249,79 +164,6 @@ endif
|
||||
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCDiff
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/0f/ca/caead2949fbb824c7142e3774fa841aa853bb4d4331b440da8c8514dfc6f/deepdiff-5.8.1.tar.gz
|
||||
cd dist/working && tar -xzvf deepdiff*
|
||||
cp -r dist/working/deepdiff-5.8.1/deepdiff dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by deepdiff
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/00/55/ce2cbc6d64034b30cad81a29ba61bdba456f190f5e83c09831304bf68d6b/jsonpickle-1.2.tar.gz
|
||||
cd dist/working && tar -xzvf jsonpickle*
|
||||
cp -r dist/working/jsonpickle-1.2/jsonpickle dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by deepdiff
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/a3/b7/d4d69641cbe707a45c23b190f2d717466ba5accc4c70b5f7a8a450387895/ordered-set-3.1.1.tar.gz
|
||||
cd dist/working && tar -xzvf ordered-set*
|
||||
cp -r dist/working/ordered-set-3.1.1/ordered_set.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by lark
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/00/32/8076fa13e832bb4dcff379f18f228e5a53412be0631808b9ca2610c0f566/pyparsing-2.4.5.tar.gz
|
||||
cd dist/working && tar -xzvf pyparsing*
|
||||
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/18/4d/8d522136c37d9e1ea74062b41b8d5e1318ebf45063ae46ce72ed60af223b/lark-parser-0.8.5.tar.gz
|
||||
cd dist/working && tar -xzvf lark-parser*
|
||||
cp -r dist/working/lark-parser-0.8.5/lark dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFC4D
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/b0/bb/9c4dddd3ca173cb56241cfb2eddfae24690dc676d357ac4cab17d0a36d9d/PyP6Xer-1.13.0.tar.gz
|
||||
cd dist/working && tar -xzvf PyP6Xer*
|
||||
cp -r dist/working/PyP6Xer-1.13.0/xerparser dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by QTOCalculator
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(SHAPELY_URL)
|
||||
cd dist/working && cp *.whl shapely.zip && unzip shapely.zip
|
||||
cp -r dist/working/shapely dist/blenderbim/libs/site/packages/
|
||||
ifeq ($(PLATFORM), win)
|
||||
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
|
||||
endif
|
||||
ifeq ($(PLATFORM), linux)
|
||||
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by the BIM tool thumbnail generator
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(PILLOW_URL)
|
||||
cd dist/working && cp *.whl pillow.zip && unzip pillow.zip
|
||||
cp -r dist/working/PIL dist/blenderbim/libs/site/packages/
|
||||
ifeq ($(PLATFORM), linux)
|
||||
cp -r dist/working/Pillow.libs dist/blenderbim/libs/site/packages/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by xerparser and IFC4D
|
||||
# TODO: remove this dependency. It's only used to show a progress bar.
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/9f/7b/76c4e5ef1a1b528fcaada4133f972e77d33c252831676cf414119ca6093d/tqdm-4.50.0.tar.gz
|
||||
cd dist/working && tar -xzvf tqdm*
|
||||
cp -r dist/working/tqdm-4.50.0/tqdm dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
mkdir dist/working
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && mkdir site-packages
|
||||
@@ -344,6 +186,28 @@ endif
|
||||
cd dist/working && . env/bin/activate && $(PIP) install xmlschema --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install elementpath --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install six --target=./site-packages
|
||||
# Required by drawing module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install lxml $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Required by qto and drawing module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install shapely $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Required by the BIM type manager thumbnail generator
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pillow $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Provides mustache templating in construction docs and web UI data
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pystache --target=./site-packages
|
||||
# Provides SVG export in construction documentation
|
||||
cd dist/working && . env/bin/activate && $(PIP) install svgwrite --target=./site-packages
|
||||
# Provides fuzzy date parsing for construction sequencing
|
||||
cd dist/working && . env/bin/activate && $(PIP) install python-dateutil --target=./site-packages
|
||||
# Provides duration parsing for construction sequencing
|
||||
cd dist/working && . env/bin/activate && $(PIP) install isodate --target=./site-packages
|
||||
# Provides networkx graph analysis for project dependency calculations
|
||||
cd dist/working && . env/bin/activate && $(PIP) install networkx --target=./site-packages
|
||||
# Required by IFCDiff
|
||||
cd dist/working && . env/bin/activate && $(PIP) install deepdiff --target=./site-packages
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
cd dist/working && . env/bin/activate && $(PIP) install lark --target=./site-packages
|
||||
# Required by IFC4D
|
||||
cd dist/working && . env/bin/activate && $(PIP) install PyP6Xer --target=./site-packages
|
||||
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
@@ -362,24 +226,6 @@ endif
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/reporter/junit.py < junit.patch
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by ids - uh, not any more, but I'm trying it out in the drawing module
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
# No wheels available for MacOS M1, but turns out this Anaconda build works
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(LXML_URL)
|
||||
cd dist/working && tar -xf lxml*
|
||||
cp -r dist/working/lib/$(PYLIBDIR)/site-packages/lxml dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
else
|
||||
# Wheels are preferred for other OSes. A first attempt at using Anaconda
|
||||
# builds for everything shows it breaks on windows. See #2422.
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(LXML_URL)
|
||||
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
|
||||
cp -r dist/working/lxml dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
endif
|
||||
|
||||
# Required by behave
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
|
||||
|
||||
@@ -142,6 +142,7 @@ classes = [
|
||||
ui.BIM_PT_tabs,
|
||||
# Project overview
|
||||
ui.BIM_PT_tab_project_info,
|
||||
ui.BIM_PT_tab_project_tree,
|
||||
ui.BIM_PT_tab_project_setup,
|
||||
ui.BIM_PT_tab_geometry,
|
||||
ui.BIM_PT_tab_stakeholders,
|
||||
@@ -170,6 +171,7 @@ classes = [
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
ui.BIM_PT_tab_status,
|
||||
ui.BIM_PT_tab_qto,
|
||||
ui.BIM_PT_tab_resources,
|
||||
ui.BIM_PT_tab_cost,
|
||||
ui.BIM_PT_tab_sequence,
|
||||
|
||||
@@ -5,6 +5,7 @@ FILE_NAME('EQto_BodyGeometryValidation.ifc','2020-01-01T00:00:00',(),(),'EQto_Bo
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
/* This quantity set is Qto_BodyGeometryValidation backport from IFC4X3 to support IFC4. */
|
||||
#1=IFCPROPERTYSETTEMPLATE('2KS9su6r517uLXTGKwwDdn',$,'EQto_BodyGeometryValidation','Quantities supplied for validating the correct interpretation of the body shape representation at import. In case of multiple representation items, the quantities are summed for each of the items (irrespective of any overlap). Choosing a suitable tolerance value for comparing the supplied numbers to the numbers calculated from the reconstructed geometry is at the discretion of the importing application.',.QTO_OCCURRENCEDRIVEN.,'IfcProduct',(#2,#3,#4,#5,#6,#7));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('3iCdOOLRjCwQ_HRTB41Xh8',$,'GrossSurfaceArea','Total gross area of the object, normally generated as perimeter * length + 2 * cross section area. It is the sum of OuterSurfaceArea + (2 x CrossSectionArea) and shall only be given, if the OuterSurfaceArea and CrossSectionArea cannot be established separately.\X2\000A000A\X0\Total gross surface area of the element before applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('0juemEqF5889vg7HwR87Fv',$,'NetSurfaceArea','Net surface area of the object, normally generated as perimeter * length + 2 * cross section area taking into account possible processing features (cut-out''s, etc.) or openings and recesses.\X2\000A000A\X0\Total net surface area of the element after applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
|
||||
|
||||
@@ -301,7 +301,8 @@ def load_post(scene):
|
||||
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
|
||||
if "BIM" in bpy.data.workspaces:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.activate_workspace:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
else:
|
||||
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
|
||||
|
||||
|
||||
@@ -225,15 +225,6 @@ def get_enum_items(data, prop_name, context=None):
|
||||
return items
|
||||
|
||||
|
||||
# hack to close popup
|
||||
# https://blender.stackexchange.com/a/202576/130742
|
||||
def close_operator_panel(event):
|
||||
x, y = event.mouse_x, event.mouse_y
|
||||
bpy.context.window.cursor_warp(10, 10)
|
||||
move_back = lambda: bpy.context.window.cursor_warp(x, y)
|
||||
bpy.app.timers.register(move_back, first_interval=0.01)
|
||||
|
||||
|
||||
def convert_property_group_from_si(property_group, skip_props=()):
|
||||
"""Method converts property group values from si to current ifc project units
|
||||
|
||||
@@ -313,6 +304,9 @@ def draw_filter(layout, filter_groups, data, module):
|
||||
elif ifc_filter.type == "group":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER_COLLECTION")
|
||||
elif ifc_filter.type == "parent":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_PARENT")
|
||||
elif ifc_filter.type == "query":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
|
||||
|
||||
@@ -222,7 +222,7 @@ class IfcStore:
|
||||
elif existing_obj:
|
||||
try:
|
||||
existing_obj.name
|
||||
IfcStore.unlink_element(obj=existing_obj)
|
||||
IfcStore.unlink_element(element=element, obj=existing_obj)
|
||||
except:
|
||||
pass
|
||||
IfcStore.id_map[element.id()] = obj
|
||||
|
||||
@@ -1702,19 +1702,20 @@ class IfcImporter:
|
||||
if aggregate["element"].is_a("IfcElementType"):
|
||||
self.type_collection.children.link(aggregate["collection"])
|
||||
continue
|
||||
self.project["blender"].children.link(aggregate["collection"])
|
||||
|
||||
def create_materials(self):
|
||||
def create_materials(self) -> None:
|
||||
for material in self.file.by_type("IfcMaterial"):
|
||||
self.create_material(material)
|
||||
|
||||
def create_material(self, material):
|
||||
def create_material(self, material: ifcopenshell.entity_instance) -> bpy.types.Material:
|
||||
blender_material = bpy.data.materials.new(material.Name)
|
||||
self.link_element(material, blender_material)
|
||||
self.material_creator.materials[material.id()] = blender_material
|
||||
blender_material.use_fake_user = True
|
||||
return blender_material
|
||||
|
||||
def create_styles(self):
|
||||
def create_styles(self) -> None:
|
||||
parsed_styles = set()
|
||||
|
||||
for material_definition_representation in self.file.by_type("IfcMaterialDefinitionRepresentation"):
|
||||
|
||||
@@ -40,36 +40,39 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
|
||||
bl_label = "Assign Object To Aggregation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
relating_object: bpy.props.IntProperty()
|
||||
related_object: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
relating_obj = None
|
||||
if self.relating_object:
|
||||
element = tool.Ifc.get().by_id(self.relating_object)
|
||||
if element.IsDecomposedBy:
|
||||
relating_obj = tool.Ifc.get_object(element)
|
||||
else:
|
||||
assembly = element.Decomposes[0].RelatingObject
|
||||
relating_obj = tool.Ifc.get_object(assembly)
|
||||
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
|
||||
elif self.related_object:
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get().by_id(self.related_object))
|
||||
if aggregate:
|
||||
relating_obj = tool.Ifc.get_object(aggregate)
|
||||
elif context.active_object:
|
||||
relating_obj = context.active_object
|
||||
if not relating_obj:
|
||||
return
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
if obj == relating_obj:
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
result = core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=relating_obj,
|
||||
related_obj=obj,
|
||||
)
|
||||
if not result:
|
||||
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
try:
|
||||
core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=relating_obj,
|
||||
related_obj=obj,
|
||||
)
|
||||
except core.IncompatibleAggregateError:
|
||||
self.report({"ERROR"}, f"Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
except core.AggregateRepresentationError:
|
||||
self.report({"ERROR"}, f"Cannot aggregate to {relating_obj.name} with a body representation")
|
||||
|
||||
|
||||
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -34,15 +34,27 @@ from bpy.props import (
|
||||
|
||||
def update_relating_object(self, context):
|
||||
def message(self, context):
|
||||
self.layout.label(text="Please select a valid Ifc Element")
|
||||
self.layout.label(text="Please select a valid IFC Element")
|
||||
|
||||
if self.relating_object is None:
|
||||
return
|
||||
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
|
||||
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
|
||||
if self.relating_object:
|
||||
self.related_object = None
|
||||
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
|
||||
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
|
||||
self.relating_object = None
|
||||
|
||||
|
||||
def update_related_object(self, context):
|
||||
def message(self, context):
|
||||
self.layout.label(text="Please select a valid IFC Element")
|
||||
|
||||
if self.related_object:
|
||||
self.relating_object = None
|
||||
if not self.related_object.BIMObjectProperties.ifc_definition_id:
|
||||
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
|
||||
self.related_object = None
|
||||
|
||||
|
||||
class BIMObjectAggregateProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object, update=update_relating_object)
|
||||
relating_object: PointerProperty(name="Relating Whole", type=bpy.types.Object, update=update_relating_object)
|
||||
related_object: PointerProperty(name="Related Part", type=bpy.types.Object, update=update_related_object)
|
||||
|
||||
@@ -53,12 +53,21 @@ class BIM_PT_aggregate(Panel):
|
||||
props = context.active_object.BIMObjectAggregateProperties
|
||||
|
||||
if props.is_editing:
|
||||
row = layout.row()
|
||||
row.prop(props, "relating_object", text="Whole")
|
||||
row = layout.row()
|
||||
row.prop(props, "related_object", text="Or Part")
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "relating_object", text="")
|
||||
col = row.column(align=True)
|
||||
if not props.relating_object and not props.related_object:
|
||||
col.enabled = False
|
||||
op = col.operator("bim.aggregate_assign_object", icon="CHECKMARK")
|
||||
if props.relating_object:
|
||||
op = row.operator("bim.aggregate_assign_object", icon="CHECKMARK", text="")
|
||||
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
|
||||
elif props.related_object:
|
||||
op.related_object = props.related_object.BIMObjectProperties.ifc_definition_id
|
||||
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
|
||||
return
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
if AggregateData.data["has_relating_object"]:
|
||||
@@ -73,7 +82,7 @@ class BIM_PT_aggregate(Panel):
|
||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||
op = row.operator("bim.aggregate_unassign_object", icon="X", text="")
|
||||
else:
|
||||
row.label(text="No Whole relation defined", icon="TRIA_UP")
|
||||
row.label(text="No Whole Relationship Found", icon="TRIA_UP")
|
||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||
|
||||
|
||||
@@ -198,10 +198,16 @@ class CostSchedulesData:
|
||||
# data["DerivedUnitSymbol"] = "?"
|
||||
# print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name)
|
||||
|
||||
# TODO: dead code?
|
||||
@classmethod
|
||||
def _get_object_quantities(cls, cost_item, element):
|
||||
def _get_object_quantities(
|
||||
cls, cost_item: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
|
||||
) -> list[int]:
|
||||
if not element.is_a("IfcObject"):
|
||||
return []
|
||||
cost_quantities = cost_item.CostQuantities
|
||||
if not cost_quantities:
|
||||
return []
|
||||
results = []
|
||||
for relationship in element.IsDefinedBy:
|
||||
if not relationship.is_a("IfcRelDefinesByProperties"):
|
||||
@@ -210,7 +216,7 @@ class CostSchedulesData:
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
continue
|
||||
for prop in qto.Quantities:
|
||||
if prop in cost_item.CostQuantities or []:
|
||||
if prop in cost_quantities:
|
||||
results.append(prop.id())
|
||||
return results
|
||||
|
||||
|
||||
@@ -755,9 +755,11 @@ class BIM_UL_product_cost_items(UIList):
|
||||
class BIM_PT_Costing_Tools(Panel):
|
||||
bl_label = "5D Tools"
|
||||
bl_idname = "BIM_PT_Costing_Tools"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "4D/5D Toolkit"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_cost"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
|
||||
@@ -29,13 +29,9 @@ def register():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.register_tool(workspace.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False)
|
||||
bpy.types.Scene.BIMCoveringProperties = bpy.props.PointerProperty(type=prop.BIMCoveringProperties)
|
||||
# bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
|
||||
# bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.unregister_tool(workspace.CoveringTool)
|
||||
del bpy.types.Scene.BIMCoveringProperties
|
||||
# del bpy.types.Object.BIMObjectSpatialProperties
|
||||
# del bpy.types.Scene.BIMSpatialManagerProperties
|
||||
|
||||
@@ -93,7 +93,8 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
if obj.data:
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
material.BIMMaterialProperties.ifc_style_id = False
|
||||
material.BIMObjectProperties.ifc_definition_id = 0
|
||||
material.BIMMaterialProperties.ifc_style_id = 0
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
IfcStore.purge()
|
||||
|
||||
@@ -29,6 +29,7 @@ import subprocess
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.ifcopenshell_wrapper
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.selector
|
||||
@@ -1537,14 +1538,7 @@ class ActivateDrawing(bpy.types.Operator):
|
||||
if not self.camera_view_point:
|
||||
viewport_position = tool.Blender.get_viewport_position()
|
||||
|
||||
try:
|
||||
core.activate_drawing_view(tool.Ifc, tool.Blender, tool.Drawing, drawing=drawing)
|
||||
except core.CameraNotAvailableError:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"The drawing view is not available. Ensure you have not excluded it in the active view layer.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
core.activate_drawing_view(tool.Ifc, tool.Blender, tool.Drawing, drawing=drawing)
|
||||
|
||||
if not self.camera_view_point:
|
||||
tool.Blender.set_viewport_position(viewport_position)
|
||||
@@ -1638,7 +1632,7 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
camera_props = context.active_object.data.BIMCameraProperties
|
||||
camera_props = context.scene.camera.data.BIMCameraProperties
|
||||
|
||||
# added this part as a temporary fallback
|
||||
# TODO: should remove it a bit later when projects get more accommodated
|
||||
|
||||
@@ -22,7 +22,6 @@ import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import shutil
|
||||
import pystache
|
||||
import mathutils
|
||||
import xml.etree.ElementTree as ET
|
||||
import svgwrite
|
||||
|
||||
@@ -22,14 +22,20 @@ from . import ui, prop, operator
|
||||
classes = (
|
||||
operator.AddRepresentation,
|
||||
operator.CopyRepresentation,
|
||||
operator.DisableEditingRepresentationItemShapeAspect,
|
||||
operator.DisableEditingRepresentationItemStyle,
|
||||
operator.DisableEditingRepresentationItems,
|
||||
operator.DuplicateLinkedAggregateTo3dCursor,
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.EditObjectPlacement,
|
||||
operator.EditRepresentationItemShapeAspect,
|
||||
operator.EditRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItemShapeAspect,
|
||||
operator.EnableEditingRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItems,
|
||||
operator.FlipObject,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.DuplicateLinkedAggregateTo3dCursor,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
operator.OverrideDuplicateMoveLinked,
|
||||
@@ -46,19 +52,13 @@ classes = (
|
||||
operator.RefreshLinkedAggregate,
|
||||
operator.RemoveConnection,
|
||||
operator.RemoveRepresentation,
|
||||
operator.RemoveRepresentationItem,
|
||||
operator.RemoveRepresentationItemFromShapeAspect,
|
||||
operator.SelectConnection,
|
||||
operator.SwitchRepresentation,
|
||||
operator.UnassignRepresentationItemStyle,
|
||||
operator.UpdateParametricRepresentation,
|
||||
operator.UpdateRepresentation,
|
||||
operator.RemoveRepresentationItem,
|
||||
operator.EnableEditingRepresentationItemStyle,
|
||||
operator.EditRepresentationItemStyle,
|
||||
operator.DisableEditingRepresentationItemStyle,
|
||||
operator.UnassignRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItemShapeAspect,
|
||||
operator.EditRepresentationItemShapeAspect,
|
||||
operator.DisableEditingRepresentationItemShapeAspect,
|
||||
operator.RemoveRepresentationItemFromShapeAspect,
|
||||
prop.RepresentationItem,
|
||||
prop.ShapeAspect,
|
||||
prop.BIMObjectGeometryProperties,
|
||||
@@ -120,6 +120,7 @@ def register():
|
||||
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Mesh", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("bim.override_mode_set_object", "TAB", "PRESS")
|
||||
kmi.properties.should_save = True
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("wm.call_menu", "P", "PRESS")
|
||||
kmi.properties.name = ui.BIM_MT_hotkey_separate.bl_idname
|
||||
|
||||
@@ -28,6 +28,7 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.geometry as core
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.style
|
||||
@@ -39,6 +40,7 @@ from mathutils import Vector, Matrix
|
||||
from time import time
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -713,6 +715,10 @@ class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
else:
|
||||
bpy.data.objects.remove(obj)
|
||||
for collection in collections_to_delete:
|
||||
# Removing an aggregate object would also remove it's collection
|
||||
# making the collection data-block invalid.
|
||||
if not tool.Blender.is_valid_data_block(collection):
|
||||
continue
|
||||
bpy.data.collections.remove(collection)
|
||||
if self.is_batch:
|
||||
old_file = tool.Ifc.get()
|
||||
@@ -724,7 +730,7 @@ class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
IfcStore.add_transaction_operation(self)
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_collection_objects_and_children(self, collection):
|
||||
def get_collection_objects_and_children(self, collection: bpy.types.Collection) -> dict[str, Any]:
|
||||
objects = set()
|
||||
children = set()
|
||||
queue = [collection]
|
||||
@@ -1683,20 +1689,11 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if self.is_valid:
|
||||
row = self.layout.row()
|
||||
row.prop(self, "should_save")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Geometry Found: Object will revert to previous state.")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
|
||||
|
||||
def _invoke(self, context, event):
|
||||
self.is_valid = True
|
||||
self.should_save = True
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
|
||||
|
||||
@@ -1758,8 +1755,6 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
|
||||
if self.edited_objs:
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ from blenderbim.bim.module.layer.data import LayersData
|
||||
|
||||
def mode_menu(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.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=blenderbim.bim.icons["IFC"].icon_id)
|
||||
|
||||
|
||||
|
||||
@@ -247,9 +247,11 @@ class IFCGIT_PT_revision_inspector(bpy.types.Panel):
|
||||
bl_idname = "IFCGIT_PT_revision_inspector"
|
||||
bl_label = "Git History"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "IFCGIT_PT_panel"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
|
||||
@@ -562,6 +562,7 @@ 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
|
||||
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
|
||||
blenderbim.bim.helper.import_attributes2(profile, self.props.material_set_item_profile_attributes)
|
||||
@@ -577,6 +578,7 @@ 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
|
||||
self.props.active_material_set_item_id = 0
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -594,6 +596,7 @@ class EditMaterialSetItemProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
attributes = blenderbim.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)
|
||||
self.props.active_material_set_item_id = 0
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
|
||||
|
||||
@@ -700,13 +703,28 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class ExpandMaterialCategory(bpy.types.Operator):
|
||||
bl_idname = "bim.expand_material_category"
|
||||
bl_label = "Expand Material Category"
|
||||
bl_description = "SHIFT+CLICK to expand all material categories"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
category: bpy.props.StringProperty()
|
||||
expand_all: bpy.props.BoolProperty(name="Expand All", default=False, options={"SKIP_SAVE"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# Expanding all categories on shift+click.
|
||||
# Make sure to use SKIP_SAVE on property, otherwise it might get stuck.
|
||||
if event.type == "LEFTMOUSE" and event.shift:
|
||||
self.expand_all = True
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMMaterialProperties
|
||||
for category in [c for c in props.materials if c.is_category and c.name == self.category]:
|
||||
for index, category in [
|
||||
(i, c)
|
||||
for i, c in enumerate(props.materials)
|
||||
if c.is_category and (self.expand_all or c.name == self.category)
|
||||
]:
|
||||
category.is_expanded = True
|
||||
if category.name == self.category:
|
||||
props.active_material_index = index
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -714,13 +732,28 @@ class ExpandMaterialCategory(bpy.types.Operator):
|
||||
class ContractMaterialCategory(bpy.types.Operator):
|
||||
bl_idname = "bim.contract_material_category"
|
||||
bl_label = "Contract Material Category"
|
||||
bl_description = "SHIFT+CLICK to contract all material categories"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
category: bpy.props.StringProperty()
|
||||
contract_all: bpy.props.BoolProperty(name="Contract All", default=False, options={"SKIP_SAVE"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# Contracting all categories on shift+click.
|
||||
# Make sure to use SKIP_SAVE on property, otherwise it might get stuck.
|
||||
if event.type == "LEFTMOUSE" and event.shift:
|
||||
self.contract_all = True
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMMaterialProperties
|
||||
for category in [c for c in props.materials if c.is_category and c.name == self.category]:
|
||||
for index, category in [
|
||||
(i, c)
|
||||
for i, c in enumerate(props.materials)
|
||||
if c.is_category and (self.contract_all or c.name == self.category)
|
||||
]:
|
||||
category.is_expanded = False
|
||||
if category.name == self.category:
|
||||
props.active_material_index = index
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ class Material(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
is_category: BoolProperty(name="Is Category", default=False)
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
has_style: BoolProperty(name="Has Style", default=True)
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ class BIM_PT_object_material(Panel):
|
||||
|
||||
total_items = len(ObjectMaterialData.data["set_items"])
|
||||
for index, set_item in enumerate(ObjectMaterialData.data["set_items"]):
|
||||
if len(self.props.material_set_item_profile_attributes):
|
||||
if len(self.props.material_set_item_profile_attributes) and self.props.active_material_set_item_id == set_item["id"]:
|
||||
self.draw_editable_set_item_profile_ui(set_item)
|
||||
elif self.props.active_material_set_item_id == set_item["id"]:
|
||||
self.draw_editable_set_item_ui(set_item)
|
||||
@@ -343,7 +343,7 @@ class BIM_UL_materials(UIList):
|
||||
row.operator(
|
||||
"bim.expand_material_category", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).category = item.name
|
||||
row.label(text=item.name or "Uncategorised")
|
||||
row.label(text=item.name)
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text=item.name, icon="MATERIAL")
|
||||
|
||||
@@ -22,10 +22,11 @@ import bpy
|
||||
class BIM_PT_misc_utilities(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_misc_utilities"
|
||||
bl_label = "Miscellaneous"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "output"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_parent_id = "BIM_PT_tab_sandbox"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
@@ -83,8 +83,17 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props = obj.BIMArrayProperties
|
||||
|
||||
relating_obj = props.relating_array_object
|
||||
|
||||
if relating_obj:
|
||||
element = tool.Ifc.get_entity(relating_obj)
|
||||
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
|
||||
parent_element = tool.Ifc.get().by_guid(parent_globalid)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
|
||||
else:
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props.count = data["count"]
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props.x = data["x"] * si_conversion
|
||||
@@ -93,7 +102,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.use_local_space = data.get("use_local_space", False)
|
||||
props.sync_children = data.get("sync_children", False)
|
||||
props.method = data.get("method", "OFFSET")
|
||||
|
||||
props.is_editing = self.item
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -137,6 +148,10 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
#clears the relating_array_object so it doesn't show again next time
|
||||
props.relating_array_object = None
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,21 @@ def update_type_page(self, context):
|
||||
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
|
||||
|
||||
|
||||
def update_relating_array_from_object(self, context):
|
||||
bpy.ops.bim.enable_editing_array(item=self.is_editing)
|
||||
return
|
||||
|
||||
|
||||
def is_object_array_applicable(self, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return False
|
||||
return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMModelProperties(PropertyGroup):
|
||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||
relating_type_id: bpy.props.EnumProperty(
|
||||
@@ -203,6 +218,14 @@ class BIMArrayProperties(PropertyGroup):
|
||||
description="Regenerate all children based on the parent object",
|
||||
default=False,
|
||||
)
|
||||
relating_array_object: bpy.props.PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Array Properties",
|
||||
update=update_relating_array_from_object,
|
||||
poll=is_object_array_applicable,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMStairProperties(PropertyGroup):
|
||||
|
||||
@@ -223,6 +223,8 @@ class BIM_PT_array(bpy.types.Panel):
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "z")
|
||||
row.operator("bim.input_cursor_z_array", icon="CURSOR", text="")
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "relating_array_object", icon="COPYDOWN")
|
||||
else:
|
||||
row = box.row(align=True)
|
||||
name = f"{array['count']} Items ({array.get('method', 'OFFSET').capitalize()})"
|
||||
|
||||
@@ -22,7 +22,7 @@ import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.type.prop as type_prop
|
||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData
|
||||
@@ -485,7 +485,7 @@ class BimToolUI:
|
||||
|
||||
cls.layout.separator()
|
||||
add_layout_hotkey_operator(
|
||||
cls.layout, "Calculate All Quantities", "S_Q", bpy.ops.bim.calculate_all_quantities.__doc__
|
||||
cls.layout, "Perform Quantity Take-off", "S_Q", bpy.ops.bim.perform_quantity_take_off.__doc__
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -614,7 +614,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
|
||||
bpy.ops.bim.calculate_all_quantities()
|
||||
bpy.ops.bim.perform_quantity_take_off()
|
||||
|
||||
def hotkey_C_P(self):
|
||||
if not bpy.context.selected_objects:
|
||||
|
||||
@@ -121,12 +121,16 @@ class UpdateIfcPatchArguments(bpy.types.Operator):
|
||||
for arg_name in inputs:
|
||||
arg_info = inputs[arg_name]
|
||||
new_attr = patch_args.add()
|
||||
data_type = arg_info.get("type", "str")
|
||||
if isinstance(data_type, list):
|
||||
data_type = [dt for dt in data_type if dt != "NoneType"][0]
|
||||
new_attr.data_type = {
|
||||
"Literal": "string",
|
||||
"str": "string",
|
||||
"float": "float",
|
||||
"int": "integer",
|
||||
"bool": "boolean",
|
||||
}[arg_info.get("type", "str")]
|
||||
}[data_type]
|
||||
new_attr.name = arg_name
|
||||
new_attr.set_value(arg_info.get("default", new_attr.get_value_default()))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -76,6 +76,11 @@ classes = (
|
||||
gizmo.ClippingPlane,
|
||||
)
|
||||
|
||||
if bpy.app.version >= (4, 1, 0):
|
||||
classes += (
|
||||
operator.IFCFileHandlerOperator,
|
||||
operator.BIM_FH_import_ifc,
|
||||
)
|
||||
|
||||
addon_keymaps = []
|
||||
|
||||
|
||||
@@ -2085,3 +2085,46 @@ class BIM_OT_load_clipping_planes(bpy.types.Operator):
|
||||
obj.location = values["location"]
|
||||
obj.rotation_euler = values["rotation"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
if bpy.app.version >= (4, 1, 0):
|
||||
|
||||
class IFCFileHandlerOperator(bpy.types.Operator):
|
||||
bl_idname = "bim.load_project_file_handler"
|
||||
bl_label = "Import .ifc file"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# Keeping code in .invoke() as we'll probably add some
|
||||
# popup windows later.
|
||||
|
||||
# `files` contain only .ifc files.
|
||||
filepath = Path(self.directory)
|
||||
# If user is just drag'n'dropping a single file -> load it as a new project,
|
||||
# if they're holding ALT -> link the file/files to the current project.
|
||||
if event.alt:
|
||||
# Passing self.files directly results in TypeError.
|
||||
serialized_files = [{"name": f.name} for f in self.files]
|
||||
return bpy.ops.bim.link_ifc(directory=self.directory, files=serialized_files)
|
||||
else:
|
||||
if len(self.files) == 1:
|
||||
return bpy.ops.bim.load_project(filepath=(filepath / self.files[0].name).as_posix())
|
||||
else:
|
||||
self.report(
|
||||
{"INFO"},
|
||||
"To link multiple IFC files hold ALT while drag'n'dropping them.",
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
class BIM_FH_import_ifc(bpy.types.FileHandler):
|
||||
bl_label = "IFC File Handler"
|
||||
bl_import_operator = IFCFileHandlerOperator.bl_idname
|
||||
bl_file_extensions = ".ifc"
|
||||
|
||||
# FileHandler won't work without poll_drop defined.
|
||||
@classmethod
|
||||
def poll_drop(cls, context):
|
||||
return True
|
||||
|
||||
@@ -27,8 +27,6 @@ classes = (
|
||||
operator.DisablePsetEditing,
|
||||
operator.EditPset,
|
||||
operator.EnablePsetEditing,
|
||||
operator.GuessQuantity,
|
||||
operator.CalculateQuantity,
|
||||
operator.RemovePset,
|
||||
operator.TogglePsetExpansion,
|
||||
operator.BIM_OT_add_property_to_edit,
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
mapper = {
|
||||
'Qto_AudioVisualApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_PlateBaseQuantities' : {
|
||||
'Width' : "get_height",
|
||||
'Perimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_OpeningElementBaseQuantities' : {
|
||||
'Width' : "get_length",
|
||||
'Height' : "get_opening_height",
|
||||
'Depth' : "get_opening_depth",
|
||||
'Area' : "get_opening_mapping_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_MarineFacilityBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ChillerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_PileBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_VibrationIsolatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LampBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_VehicleBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_PipeFittingBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_CableCarrierFittingBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_HeatExchangerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DoorBaseQuantities' : {
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Height' : "get_height",
|
||||
'Perimeter' : "get_rectangular_perimeter",
|
||||
'Area' : "get_net_side_area",
|
||||
},
|
||||
'Qto_DuctSegmentBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_TransformerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FacilityPartBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ProjectionElementBaseQuantities' : {
|
||||
'Area' : "get_net_side_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SignBaseQuantities' : {
|
||||
'Height' : "get_height",
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Thickness' : "get_width",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_CableSegmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
},
|
||||
'Qto_BuildingBaseQuantities' : {
|
||||
'Height' : None,
|
||||
'EavesHeight' : None,
|
||||
'FootPrintArea' : None,
|
||||
'GrossFloorArea' : None,
|
||||
'NetFloorArea' : None,
|
||||
'GrossVolume' : None,
|
||||
'NetVolume' : None,
|
||||
},
|
||||
'Qto_ElectricFlowStorageDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CommunicationsApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RailBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Volume' : "get_net_volume",
|
||||
'Weight' : "get_net_weight",
|
||||
},
|
||||
'Qto_PictorialSignQuantities' : {
|
||||
'Area' : "get_net_side_area",
|
||||
'SignArea' : None,
|
||||
},
|
||||
'Qto_SpaceHeaterBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_CoveringBaseQuantities' : {
|
||||
'Width' : "get_covering_width",
|
||||
'GrossArea' : "get_covering_gross_area",
|
||||
'NetArea' : "get_covering_net_area",
|
||||
},
|
||||
'Qto_PipeSegmentBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_HumidifierBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ConstructionEquipmentResourceBaseQuantities' : {
|
||||
'UsageTime' : None,
|
||||
'OperatingTime' : None,
|
||||
},
|
||||
'Qto_AlarmBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_JunctionBoxBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NumberOfGangs' : None,
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ArealStratumBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Length' : "get_length",
|
||||
'PlanLength' : None,
|
||||
},
|
||||
'Qto_SiteBaseQuantities' : {
|
||||
'GrossPerimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
},
|
||||
'Qto_MotorConnectionBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RoofBaseQuantities' : {
|
||||
'GrossArea' : "get_gross_top_area",
|
||||
'NetArea' : "get_net_top_area",
|
||||
'ProjectedArea' : None,
|
||||
},
|
||||
'Qto_ChimneyBaseQuantities' : {
|
||||
'Length' : "get_height",
|
||||
},
|
||||
'Qto_ElectricTimeControlBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ElectricMotorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_EarthworksFillBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Width' : None,
|
||||
'Depth' : None,
|
||||
'CompactedVolume' : None,
|
||||
'LooseVolume' : None,
|
||||
},
|
||||
'Qto_ConduitSegmentBaseQuantities' : {
|
||||
'InnerDiameter' : None,
|
||||
'OuterDiameter' : None,
|
||||
},
|
||||
'Qto_SignalBaseQuantities' : {
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_DuctFittingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_UnitaryControlElementBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ActuatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CurtainWallQuantities' : {
|
||||
'Length' : None,
|
||||
'Height' : None,
|
||||
'Width' : None,
|
||||
'GrossSideArea' : None,
|
||||
'NetSideArea' : None,
|
||||
},
|
||||
'Qto_BoilerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
'TotalSurfaceArea' : None,
|
||||
},
|
||||
'Qto_FlowMeterBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_AirTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Perimeter' : None,
|
||||
'TotalSurfaceArea' : None,
|
||||
},
|
||||
'Qto_DuctSilencerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_WasteTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SlabBaseQuantities' : {
|
||||
'Width' : "get_width",
|
||||
'Length' : "get_length",
|
||||
'Depth' : "get_height",
|
||||
'Perimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_ImpactProtectionDeviceBaseQuantities' : {
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_LightFixtureBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FlowInstrumentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BuildingStoreyBaseQuantities' : {
|
||||
'GrossHeight' : None,
|
||||
'NetHeight' : None,
|
||||
'GrossPerimeter' : None,
|
||||
'GrossFloorArea' : None,
|
||||
'NetFloorArea' : None,
|
||||
'GrossVolume' : None,
|
||||
'NetVolume' : None,
|
||||
},
|
||||
'Qto_ReinforcedSoilBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Width' : None,
|
||||
'Depth' : None,
|
||||
'Area' : None,
|
||||
'Volume' : None,
|
||||
},
|
||||
'Qto_DistributionBoardBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NumberOfCircuits' : None,
|
||||
},
|
||||
'Qto_FootingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_PumpBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CableCarrierSegmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Length' : None,
|
||||
'CrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
},
|
||||
'Qto_InterceptorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ColumnBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_EarthworksCutBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Depth' : "get_height",
|
||||
'UndisturbedVolume' : "get_net_volume",
|
||||
'LooseVolume' : None,
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_StackTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CoilBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_KerbBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Depth' : None,
|
||||
'Volume' : "get_net_volume",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_PavementBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Depth' : "get_height",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ControllerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SolarDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'GrossArea' : None,
|
||||
},
|
||||
'Qto_RampFlightBaseQuantities' : {
|
||||
'Length' : "get_stair_length",
|
||||
'Width' : "get_width",
|
||||
'GrossArea' : "get_gross_stair_area",
|
||||
'NetArea' : "get_net_stair_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ElectricApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ValveBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DamperBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SurfaceFeatureBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Length' : "get_length",
|
||||
},
|
||||
'Qto_WallBaseQuantities' : {
|
||||
'Length' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'GrossFootprintArea' : "get_gross_footprint_area",
|
||||
'NetFootprintArea' : "get_net_footprint_area",
|
||||
'GrossSideArea' : "get_gross_side_area",
|
||||
'NetSideArea' : "get_net_side_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_StairFlightBaseQuantities' : {
|
||||
'Length' : "get_stair_length",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SwitchingDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BurnerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ConstructionMaterialResourceBaseQuantities' : {
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_ElectricGeneratorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LinearStratumBaseQuantities' : {
|
||||
'Diameter' : None,
|
||||
'Length' : None,
|
||||
},
|
||||
'Qto_CableFittingBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DistributionChamberElementBaseQuantities' : {
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'Depth' : "get_length",
|
||||
},
|
||||
'Qto_CompressorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ProtectiveDeviceTrippingUnitBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_EvaporatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SpaceBaseQuantities' : {
|
||||
'Height' : "get_height",
|
||||
'FinishCeilingHeight' : "get_finish_ceiling_height",
|
||||
'FinishFloorHeight' : "get_finish_floor_height",
|
||||
'GrossPerimeter' : "get_gross_perimeter",
|
||||
'NetPerimeter' : None,
|
||||
'GrossFloorArea' : "get_gross_footprint_area",
|
||||
'NetFloorArea' : "get_net_floor_area",
|
||||
'GrossWallArea' : None,
|
||||
'NetWallArea' : None,
|
||||
'GrossCeilingArea' : "get_gross_ceiling_area",
|
||||
'NetCeilingArea' : "get_net_ceiling_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_space_net_volume",
|
||||
},
|
||||
'Qto_CourseBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Thickness' : "get_height",
|
||||
'Volume' : "get_net_volume",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_CondenserBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FireSuppressionTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RailingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
},
|
||||
'Qto_TubeBundleBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_BeamBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_SleeperBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ProtectiveDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CooledBeamBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_TankBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
'TotalSurfaceArea' : "get_outer_surface_area",
|
||||
},
|
||||
'Qto_AirToAirHeatRecoveryBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CoolingTowerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SensorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_WindowBaseQuantities' : {
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Height' : "get_height",
|
||||
'Perimeter' : "get_rectangular_perimeter",
|
||||
'Area' : "get_net_side_area",
|
||||
},
|
||||
'Qto_SanitaryTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BuildingElementProxyQuantities' : {
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_FanBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_OutletBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_UnitaryEquipmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FilterBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_MemberBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_BodyGeometryValidation' : {
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'SurfaceGenusBeforeFeatures' : None,
|
||||
'SurfaceGenusAfterFeatures' : None,
|
||||
},
|
||||
'Qto_VolumetricStratumBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Mass' : None,
|
||||
'PlanArea' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SpatialZoneBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ReinforcingElementBaseQuantities' : {
|
||||
'Count' : None,
|
||||
'Length' : "get_length",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_EvaporativeCoolerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_AirTerminalBoxTypeBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LaborResourceBaseQuantities' : {
|
||||
'StandardWork' : None,
|
||||
'OvertimeWork' : None,
|
||||
},
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.doc
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
|
||||
@@ -297,7 +298,6 @@ class WorkSchedulePsetsData(Data):
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
props = bpy.context.scene.WorkSchedulePsetProperties
|
||||
ifc_definition_id = bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
||||
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -27,10 +27,8 @@ import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.pset as core
|
||||
import blenderbim.core.qto as QtoCore
|
||||
import blenderbim.bim.module.pset.data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -142,6 +140,8 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
)
|
||||
else:
|
||||
for key, value in properties.items():
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, float):
|
||||
properties[key] = round(value, 4)
|
||||
elif not isinstance(value, int):
|
||||
@@ -214,55 +214,6 @@ class AddQto(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class CalculateQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.calculate_quantity"
|
||||
bl_label = "Calculate Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Calculates the quantity with a defined formula for this exact entity and quantity"
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.qto_calculator = QtoCalculator()
|
||||
obj = context.active_object
|
||||
prop = obj.PsetProperties.properties.get(self.prop)
|
||||
quantity = self.calculate_quantity(obj, context)
|
||||
|
||||
if quantity is None:
|
||||
self.report({"ERROR"}, "Could not calculate quantity")
|
||||
return {"CANCELLED"}
|
||||
|
||||
prop.metadata.float_value = quantity
|
||||
return {"FINISHED"}
|
||||
|
||||
def calculate_quantity(self, obj, context):
|
||||
quantity = self.qto_calculator.calculate_quantity(obj.PsetProperties.active_pset_name, self.prop, obj)
|
||||
if quantity is None:
|
||||
return
|
||||
return round(quantity, 3)
|
||||
|
||||
|
||||
class GuessQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.guess_quantity"
|
||||
bl_label = "Guess Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Calculate the quantity by guessing the formula from the quantity name. "
|
||||
"Less reliable than Calculate Quantity"
|
||||
)
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.qto_calculator = QtoCalculator()
|
||||
obj = context.active_object
|
||||
prop = obj.PsetProperties.properties.get(self.prop)
|
||||
prop.metadata.float_value = self.guess_quantity(obj, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def guess_quantity(self, obj, context):
|
||||
quantity = self.qto_calculator.guess_quantity(self.prop, [p.name for p in obj.PsetProperties.properties], obj)
|
||||
return round(quantity, 3) if quantity is not None else None
|
||||
|
||||
|
||||
class CopyPropertyToSelection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.copy_property_to_selection"
|
||||
bl_label = "Copy Property To Selection"
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import blenderbim.bim.schema
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.doc
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -103,8 +103,6 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
op.obj_type = obj_type
|
||||
elif not props.active_pset_id:
|
||||
row.label(text=pset["Name"], icon="COPY_ID")
|
||||
if "Qto" in pset["Name"] and "Base" in pset["Name"]:
|
||||
op = row.operator("bim.calculate_all_quantities", icon="MOD_EDGESPLIT", text="")
|
||||
op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="")
|
||||
op.pset_id = pset_id
|
||||
op.obj = obj_name
|
||||
@@ -160,25 +158,6 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
def draw_psetqto_editable_ui(box, props, prop):
|
||||
row = box.row(align=True)
|
||||
draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
if prop.metadata.has_calculator:
|
||||
op = row.operator("bim.calculate_quantity", icon="MOD_EDGESPLIT", text="")
|
||||
op.prop = prop.name
|
||||
# Old "guess quantity" feature to be removed once new calculator is comprehensive
|
||||
if (
|
||||
"length" in prop.name.lower()
|
||||
or "width" in prop.name.lower()
|
||||
or "height" in prop.name.lower()
|
||||
or "depth" in prop.name.lower()
|
||||
or "perimeter" in prop.name.lower()
|
||||
):
|
||||
op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="")
|
||||
op.prop = prop.name
|
||||
elif "area" in prop.name.lower():
|
||||
op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="")
|
||||
op.prop = prop.name
|
||||
elif "volume" in prop.name.lower():
|
||||
op = row.operator("bim.guess_quantity", icon="SPHERE", text="")
|
||||
op.prop = prop.name
|
||||
|
||||
|
||||
class BIM_PT_object_psets(Panel):
|
||||
|
||||
@@ -23,13 +23,15 @@ classes = (
|
||||
operator.CalculateCircleRadius,
|
||||
operator.CalculateEdgeLengths,
|
||||
operator.CalculateFaceAreas,
|
||||
operator.CalculateFormworkArea,
|
||||
operator.CalculateObjectVolumes,
|
||||
operator.ExecuteQtoMethod,
|
||||
operator.QuantifyObjects,
|
||||
operator.AssignBaseQto,
|
||||
operator.CalculateAllQuantities,
|
||||
operator.CalculateSingleQuantity,
|
||||
operator.PerformQuantityTakeOff,
|
||||
prop.BIMQtoProperties,
|
||||
ui.BIM_PT_qto_utilities,
|
||||
ui.BIM_PT_qto,
|
||||
ui.BIM_PT_qto_manual,
|
||||
ui.BIM_PT_qto_simple,
|
||||
ui.BIM_PT_qto_cost,
|
||||
)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,11 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def refresh():
|
||||
QtoData.is_loaded = False
|
||||
|
||||
|
||||
class QtoData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
@@ -29,9 +31,9 @@ class QtoData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"has_cost_item" : cls.has_cost_item(),
|
||||
"relating_cost_items" : cls.relating_cost_items(),
|
||||
}
|
||||
"has_cost_item": cls.has_cost_item(),
|
||||
"relating_cost_items": cls.relating_cost_items(),
|
||||
}
|
||||
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -53,23 +55,13 @@ class QtoData:
|
||||
for relating_cost_item in relating_cost_items:
|
||||
results.append(
|
||||
{
|
||||
'cost_item_id' : relating_cost_item['cost_item_id'],
|
||||
'cost_item_name' : relating_cost_item['cost_item_name'],
|
||||
'quantity_id' : relating_cost_item['quantity_id'],
|
||||
'quantity_name' : relating_cost_item['quantity_name'],
|
||||
'quantity_value' : relating_cost_item['quantity_value'],
|
||||
'quantity_type' : relating_cost_item['quantity_type'],
|
||||
"cost_item_id": relating_cost_item["cost_item_id"],
|
||||
"cost_item_name": relating_cost_item["cost_item_name"],
|
||||
"quantity_id": relating_cost_item["quantity_id"],
|
||||
"quantity_name": relating_cost_item["quantity_name"],
|
||||
"quantity_value": relating_cost_item["quantity_value"],
|
||||
"quantity_type": relating_cost_item["quantity_type"],
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.qto as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.qto import helper
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class CalculateCircleRadius(bpy.types.Operator):
|
||||
@@ -85,115 +84,82 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExecuteQtoMethod(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_qto_method"
|
||||
bl_label = "Execute Qto Method"
|
||||
class CalculateFormworkArea(bpy.types.Operator):
|
||||
bl_idname = "bim.calculate_formwork_area"
|
||||
bl_label = "Calculate Formwork Area"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
return context.selected_objects and context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
selected_mesh_objects = [o for o in context.selected_objects if o.type == "MESH"]
|
||||
props = context.scene.BIMQtoProperties
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
for obj in selected_mesh_objects:
|
||||
result += helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
result = helper.calculate_volumes(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "SIDE_FORMWORK":
|
||||
result = helper.calculate_side_formwork_area(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "NetFootprintArea":
|
||||
result = QtoCalculator().get_net_footprint_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "NetRoofprintArea":
|
||||
result = QtoCalculator().get_net_roofprint_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "LateralArea":
|
||||
result = QtoCalculator().get_lateral_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "TotalSurfaceArea":
|
||||
result = QtoCalculator().get_total_surface_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "OpeningArea":
|
||||
result = QtoCalculator().get_opening_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "GrossTopArea":
|
||||
result = QtoCalculator().get_gross_top_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "NetTopArea":
|
||||
result = QtoCalculator().get_net_top_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "ProjectedArea":
|
||||
result = QtoCalculator().get_projected_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "TotalContactArea":
|
||||
result = QtoCalculator().get_total_contact_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "ContactArea":
|
||||
result = QtoCalculator().get_contact_area(selected_mesh_objects[0], selected_mesh_objects[1])
|
||||
props.qto_result = str(round(result, 3))
|
||||
result = helper.calculate_formwork_area([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class QuantifyObjects(bpy.types.Operator):
|
||||
bl_idname = "bim.quantify_objects"
|
||||
bl_label = "Quantify Objects"
|
||||
class CalculateSingleQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_single_quantity"
|
||||
bl_label = "Calculate Single Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMQtoProperties
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in (o for o in context.selected_objects if o.type == "MESH"):
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
result = helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
result = helper.calculate_volumes([obj], context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area([obj], context)
|
||||
elif props.qto_methods == "SIDE_FORMWORK":
|
||||
result = helper.calculate_side_formwork_area([obj], context)
|
||||
if not result:
|
||||
continue
|
||||
result = round(result, 3)
|
||||
qto = ifcopenshell.api.run(
|
||||
"pset.add_qto",
|
||||
self.file,
|
||||
product=self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
name=props.qto_name,
|
||||
)
|
||||
ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={props.prop_name: result})
|
||||
return {"FINISHED"}
|
||||
|
||||
class AssignBaseQto(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_objects_base_qto"
|
||||
bl_label = "Assign IFC Object Quantity Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Assign IFC quantity set to selected object"
|
||||
bl_description = "Calculate a single quantity using a function on the selected objects"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
core.assign_objects_base_qto(tool.Ifc, tool.Qto, selected_objects = context.selected_objects)
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
elements = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
elements.add(element)
|
||||
|
||||
rules = {
|
||||
"calculators": {
|
||||
props.calculator: {
|
||||
"IfcProduct": {props.qto_name: {props.prop_name: props.calculator_function}},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, elements, rules)
|
||||
ifc5d.qto.edit_qtos(ifc_file, results)
|
||||
return {"FINISHED"}
|
||||
|
||||
class CalculateAllQuantities(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_all_quantities"
|
||||
bl_label = "Calculate All Quantities"
|
||||
|
||||
class PerformQuantityTakeOff(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.perform_quantity_take_off"
|
||||
bl_label = "Perform Quantity Take-off"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Calculate all possible quantities and assign them to selected object"
|
||||
bl_description = "Perform a quantity take off based of a QTO rule configuration"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and context.selected_objects
|
||||
return tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
core.calculate_objects_base_quantities(tool.Ifc, tool.Cost, tool.Qto, QtoCalculator(), selected_objects = context.selected_objects)
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
if context.selected_objects:
|
||||
elements = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
elements.add(element)
|
||||
else:
|
||||
elements = set(tool.Ifc.get().by_type("IfcElement"))
|
||||
|
||||
rules = ifc5d.qto.rules[props.qto_rule]
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, elements, rules)
|
||||
ifc5d.qto.edit_qtos(ifc_file, results)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifc5d.qto
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -31,34 +32,37 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
def get_qto_rule(self, context):
|
||||
results = []
|
||||
for rule_id, rule in ifc5d.qto.rules.items():
|
||||
results.append((rule_id, rule["name"], rule["description"]))
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator(self, context):
|
||||
results = []
|
||||
for name, calculator in ifc5d.qto.calculators.items():
|
||||
results.append((name, name, calculator.__doc__))
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator_function(self, context):
|
||||
calculator = ifc5d.qto.calculators[self.calculator]
|
||||
results = []
|
||||
previous_measure = None
|
||||
for function_id, function in calculator.functions.items():
|
||||
measure = function.measure.split("Measure")[0][3:]
|
||||
if previous_measure is not None and measure != previous_measure:
|
||||
results.append(None)
|
||||
results.append((function_id, f"{measure}: {function.name}", function.description))
|
||||
previous_measure = measure
|
||||
return results
|
||||
|
||||
|
||||
class BIMQtoProperties(PropertyGroup):
|
||||
qto_rule: EnumProperty(items=get_qto_rule, name="Qto Rule")
|
||||
calculator: EnumProperty(items=get_calculator, name="Calculator")
|
||||
calculator_function: EnumProperty(items=get_calculator_function, name="Calculator Function")
|
||||
qto_result: StringProperty(default="", name="Qto Result")
|
||||
qto_methods: EnumProperty(
|
||||
items=[
|
||||
("HEIGHT", "Height", "Calculate the Z height of an object"),
|
||||
("VOLUME", "Volume", "Calculate the volume of an object"),
|
||||
(
|
||||
"FORMWORK",
|
||||
"Formwork",
|
||||
"Calculate the exposed formwork for all bottoms and sides (e.g. for beams and slabs) of one or more objects",
|
||||
),
|
||||
(
|
||||
"SIDE_FORMWORK",
|
||||
"Side Formwork",
|
||||
"Calculate the exposed formwork for all sides only (e.g. for columns) of one or more objects",
|
||||
),
|
||||
("NetFootprintArea", "Net footprint area", "Calculate the net footprint area"),
|
||||
("NetRoofprintArea", "Net roofprint area", "Calculate the net roofprint area"),
|
||||
("LateralArea", "Lateral area", "Calculate the lateral area"),
|
||||
("TotalSurfaceArea", "Total surface area", "Calculate the total surface area"),
|
||||
("OpeningArea", "Opening area", "Calculate the opening area"),
|
||||
("GrossTopArea", "Gross top area", "Calculate the gross top area"),
|
||||
("NetTopArea", "Net top area", "Calculate the net top area"),
|
||||
("ProjectedArea", "Projected area", "Calculate the projected area"),
|
||||
("TotalContactArea", "Total contact area", "Get the total contact area"),
|
||||
("ContactArea", "Contact area between two objects", "Get the contact area")
|
||||
],
|
||||
name="Qto Methods",
|
||||
)
|
||||
qto_name: StringProperty(name="Qto Name")
|
||||
prop_name: StringProperty(name="Prop Name")
|
||||
qto_name: StringProperty(name="Qto Name", default="My_Qto")
|
||||
prop_name: StringProperty(name="Prop Name", default="MyDimension")
|
||||
|
||||
@@ -20,69 +20,122 @@ import bpy
|
||||
from blenderbim.bim.module.qto.data import QtoData
|
||||
|
||||
|
||||
class BIM_PT_qto_utilities(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_utilities"
|
||||
class BIM_PT_qto(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto"
|
||||
bl_label = "Quantity Take-off"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
def draw(self, context):
|
||||
if not QtoData.is_loaded:
|
||||
QtoData.load()
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
row = layout.row()
|
||||
if context.selected_objects:
|
||||
row.label(text=f"Quantifying {len(context.selected_objects)} Selected Objects", icon="MOD_EDGESPLIT")
|
||||
else:
|
||||
row.label(text="Quantifying All Objects", icon="MOD_EDGESPLIT")
|
||||
row = layout.row()
|
||||
row.prop(props, "qto_rule", text="")
|
||||
row = layout.row()
|
||||
row.operator("bim.perform_quantity_take_off")
|
||||
|
||||
|
||||
class BIM_PT_qto_manual(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_manual"
|
||||
bl_label = "Manual Quantification"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "calculator")
|
||||
row = layout.row()
|
||||
row.prop(props, "calculator_function", text="Function")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_name", text="")
|
||||
row.prop(props, "prop_name", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_single_quantity")
|
||||
|
||||
|
||||
class BIM_PT_qto_simple(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_simple"
|
||||
bl_label = "Simple Quantity Calculator"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "qto_result", text="Results")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_circle_radius")
|
||||
row = layout.row(align=True)
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_edge_lengths")
|
||||
row = layout.row(align=True)
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_face_areas")
|
||||
row = layout.row(align=True)
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_object_volumes")
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_formwork_area")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_methods", text="")
|
||||
row.operator("bim.execute_qto_method", icon="PROPERTIES", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_name", text="")
|
||||
row.prop(props, "prop_name", text="")
|
||||
row.operator("bim.quantify_objects", icon="COPYDOWN", text="")
|
||||
class BIM_PT_qto_cost(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_cost"
|
||||
bl_label = "Parametric Cost Relationships"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.assign_objects_base_qto")
|
||||
def draw(self, context):
|
||||
if not QtoData.is_loaded:
|
||||
QtoData.load()
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_all_quantities", icon="MOD_EDGESPLIT")
|
||||
if not context.selected_objects:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Selected Object")
|
||||
return
|
||||
|
||||
if context.selected_objects:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Relating Cost Item:")
|
||||
|
||||
if QtoData.data['has_cost_item']:
|
||||
for relating_cost_item in QtoData.data['relating_cost_items']:
|
||||
row.label(text=f"\n")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Cost item name:")
|
||||
row.label(text=f"{relating_cost_item['cost_item_name']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity name:")
|
||||
row.label(text=f"{relating_cost_item['quantity_name']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity value:")
|
||||
row.label(text=f"{relating_cost_item['quantity_value']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity type:")
|
||||
row.label(text=f"{relating_cost_item['quantity_type']}")
|
||||
row = layout.row(align=True)
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.label(text = f"No cost item related")
|
||||
if not QtoData.data["has_cost_item"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Related Cost Item")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Relating Cost Item:")
|
||||
for relating_cost_item in QtoData.data["relating_cost_items"]:
|
||||
row.label(text="\n")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Cost item name:")
|
||||
row.label(text=f"{relating_cost_item['cost_item_name']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity name:")
|
||||
row.label(text=f"{relating_cost_item['quantity_name']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity value:")
|
||||
row.label(text=f"{relating_cost_item['quantity_value']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity type:")
|
||||
row.label(text=f"{relating_cost_item['quantity_type']}")
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -37,13 +37,16 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
quantitytypes_enum = []
|
||||
quantitytypes_enum = {}
|
||||
|
||||
|
||||
def purge():
|
||||
global quantitytypes_enum
|
||||
quantitytypes_enum = []
|
||||
def setup_quantity_types_enum():
|
||||
resources = ifcopenshell.util.resource.RESOURCES_TO_QUANTITIES
|
||||
for resource, quantities in resources.items():
|
||||
quantitytypes_enum[resource] = [(q, q, "") for q in quantities]
|
||||
|
||||
|
||||
setup_quantity_types_enum()
|
||||
|
||||
|
||||
def updateResourceName(self, context):
|
||||
@@ -63,15 +66,7 @@ def updateResourceName(self, context):
|
||||
|
||||
|
||||
def get_quantity_types(self, context):
|
||||
global quantitytypes_enum
|
||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||
quantitytypes_enum.extend(
|
||||
[
|
||||
(t.name(), t.name(), "")
|
||||
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
|
||||
]
|
||||
)
|
||||
return quantitytypes_enum
|
||||
return quantitytypes_enum[self.active_resource_class]
|
||||
|
||||
|
||||
def update_active_resource_index(self, context):
|
||||
@@ -88,9 +83,7 @@ def updateResourceUsage(self, context):
|
||||
resource = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if resource.Usage and resource.Usage.ScheduleUsage == self.schedule_usage:
|
||||
return
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={
|
||||
"ScheduleUsage": self.schedule_usage
|
||||
})
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={"ScheduleUsage": self.schedule_usage})
|
||||
tool.Sequence.load_task_properties()
|
||||
tool.Resource.load_resource_properties()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
@@ -127,12 +120,21 @@ class BIMResourceProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
active_resource_index: IntProperty(name="Active Resource Index", update=update_active_resource_index)
|
||||
active_resource_id: IntProperty(name="Active Resource Id")
|
||||
active_resource_class: StringProperty(name="Active Resource Type")
|
||||
contracted_resources: StringProperty(name="Contracted Resources", default="[]")
|
||||
is_resource_update_enabled: BoolProperty(name="Is Resource Update Enabled", default=True)
|
||||
is_loaded: BoolProperty(name="Is Editing")
|
||||
active_resource_time_id: IntProperty(name="Active Resource Usage Id")
|
||||
resource_time_attributes: CollectionProperty(name="Resource Usage Attributes", type=Attribute)
|
||||
editing_resource_type: StringProperty(name="Editing Resource Type")
|
||||
editing_resource_type: EnumProperty(
|
||||
name="Editing Resource Type",
|
||||
items=(
|
||||
("ATTRIBUTES", "", ""),
|
||||
("USAGE", "", ""),
|
||||
("COSTS", "", ""),
|
||||
("QUANTITY", "", ""),
|
||||
),
|
||||
)
|
||||
cost_types: EnumProperty(
|
||||
items=[
|
||||
("FIXED", "Fixed", "The cost value is a fixed number"),
|
||||
|
||||
@@ -27,7 +27,6 @@ import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import close_operator_panel
|
||||
from blenderbim.bim.module.group import ui
|
||||
import blenderbim.core.search as core
|
||||
from itertools import cycle
|
||||
@@ -122,14 +121,12 @@ class SelectFilterElements(bpy.types.Operator):
|
||||
filter_groups = tool.Search.get_filter_groups(self.module)
|
||||
global_ids = []
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if global_id:
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
if global_id := getattr(element, "GlobalId", None):
|
||||
global_ids.append(global_id)
|
||||
if len(global_ids) > 50:
|
||||
# Too much to store in a string property
|
||||
name = "globalid-filter-" + ifcopenshell.guid.new()
|
||||
name = f"globalid-filter-{ifcopenshell.guid.new()}"
|
||||
text_data = bpy.data.texts.new(name)
|
||||
text_data.from_string(",".join(global_ids))
|
||||
filter_groups[self.group_index].filters[self.index].value = f"bpy.data.texts['{name}']"
|
||||
@@ -182,8 +179,7 @@ class Search(Operator):
|
||||
|
||||
total_selected = 0
|
||||
for element in results:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
if obj := tool.Ifc.get_object(element):
|
||||
obj.select_set(True)
|
||||
self.report({"INFO"}, f"{len(results)} Results")
|
||||
return {"FINISHED"}
|
||||
@@ -285,8 +281,7 @@ class ColourByProperty(Operator):
|
||||
else:
|
||||
colourscheme[value] = {"colour": next(colours)[0:3], "total": 1}
|
||||
obj.color = (*colourscheme[value]["colour"], 1)
|
||||
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
||||
if areas:
|
||||
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
|
||||
areas[0].spaces[0].shading.color_type = "OBJECT"
|
||||
|
||||
props.colourscheme.clear()
|
||||
@@ -299,8 +294,7 @@ class ColourByProperty(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def store_state(self, context):
|
||||
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
||||
if areas:
|
||||
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
|
||||
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
|
||||
|
||||
def rollback(self, data):
|
||||
@@ -450,8 +444,7 @@ class SelectIfcClass(Operator):
|
||||
classes = set()
|
||||
predefined_types = set()
|
||||
for obj in objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
classes.add(element.is_a())
|
||||
predefined_types.add(ifcopenshell.util.element.get_predefined_type(element))
|
||||
for cls in classes:
|
||||
@@ -461,9 +454,8 @@ class SelectIfcClass(Operator):
|
||||
and ifcopenshell.util.element.get_predefined_type(element) not in predefined_types
|
||||
):
|
||||
continue
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
if obj := tool.Ifc.get_object(element):
|
||||
tool.Blender.select_object(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -489,10 +481,7 @@ class ToggleFilterSelection(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
if self.action == "SELECT":
|
||||
self.selecting_actionbool = True
|
||||
else:
|
||||
self.selecting_actionbool = False
|
||||
self.selecting_actionbool = self.action == "SELECT"
|
||||
if props.filter_type == "CLASSES":
|
||||
for ifc_class in props.filter_classes:
|
||||
ifc_class.is_selected = self.selecting_actionbool
|
||||
@@ -546,11 +535,7 @@ class ActivateIfcClassFilter(Operator):
|
||||
"filter_classes",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_classes_index",
|
||||
rows=(
|
||||
20
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_classes) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_classes)
|
||||
),
|
||||
rows=min(len(bpy.context.scene.BIMSearchProperties.filter_classes), 20),
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
@@ -605,11 +590,7 @@ class ActivateContainerFilter(Operator):
|
||||
"filter_container",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_container_index",
|
||||
rows=(
|
||||
20
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_container) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_container)
|
||||
),
|
||||
rows=min(len(bpy.context.scene.BIMSearchProperties.filter_container), 20),
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
|
||||
@@ -118,8 +118,9 @@ class BIMSearchProperties(PropertyGroup):
|
||||
("location", "Location", "", "PACKAGE", 5),
|
||||
("type", "Type", "", "FILE_VOLUME", 6),
|
||||
("group", "Group", "", "OUTLINER_COLLECTION", 7),
|
||||
("query", "Query", "", "POINTCLOUD_DATA", 8),
|
||||
("instance", "GlobalId", "", "GRIP", 9),
|
||||
("parent", "Parent", "", "FILE_PARENT", 8),
|
||||
("query", "Query", "", "POINTCLOUD_DATA", 9),
|
||||
("instance", "GlobalId", "", "GRIP", 10),
|
||||
],
|
||||
)
|
||||
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
|
||||
|
||||
@@ -22,7 +22,6 @@ import json
|
||||
import time
|
||||
import calendar
|
||||
import isodate
|
||||
import pystache
|
||||
import blenderbim.core.sequence as core
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.sequence.helper as helper
|
||||
@@ -1409,9 +1408,13 @@ class LoadProductTasks(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
core.load_product_related_tasks(
|
||||
result = core.load_product_related_tasks(
|
||||
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
if isinstance(result, str):
|
||||
self.report({"INFO"}, result)
|
||||
else:
|
||||
self.report({"INFO"}, f"{len(result)} product tasks loaded.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -1084,9 +1084,11 @@ class BIM_PT_work_calendars(Panel):
|
||||
class BIM_PT_4D_Tools(Panel):
|
||||
bl_label = "4D Tools"
|
||||
bl_idname = "BIM_PT_4D_Tools"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "4D/5D Toolkit"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
|
||||
@@ -22,31 +22,32 @@ from . import ui, prop, operator, workspace
|
||||
classes = (
|
||||
operator.AssignContainer,
|
||||
operator.ChangeSpatialLevel,
|
||||
operator.ContractContainer,
|
||||
operator.CopyToContainer,
|
||||
operator.DeleteContainer,
|
||||
operator.DereferenceStructure,
|
||||
operator.DisableEditingContainer,
|
||||
operator.EditContainerAttributes,
|
||||
operator.EnableEditingContainer,
|
||||
operator.ExpandContainer,
|
||||
operator.LoadContainerManager,
|
||||
operator.ReferenceStructure,
|
||||
operator.RemoveContainer,
|
||||
operator.SelectContainer,
|
||||
operator.SelectSimilarContainer,
|
||||
operator.SelectProduct,
|
||||
operator.LoadContainerManager,
|
||||
operator.EditContainerAttributes,
|
||||
operator.AddBuildingStorey,
|
||||
operator.ContractContainer,
|
||||
operator.ExpandContainer,
|
||||
operator.DeleteContainer,
|
||||
operator.SelectDecomposedElements,
|
||||
operator.SelectProduct,
|
||||
operator.SelectSimilarContainer,
|
||||
prop.SpatialElement,
|
||||
prop.Element,
|
||||
prop.BIMSpatialProperties,
|
||||
prop.BIMObjectSpatialProperties,
|
||||
prop.BIMContainer,
|
||||
prop.BIMSpatialManagerProperties,
|
||||
prop.BIMProjectTreeProperties,
|
||||
ui.BIM_PT_spatial,
|
||||
ui.BIM_UL_containers,
|
||||
ui.BIM_UL_containers_manager,
|
||||
ui.BIM_PT_SpatialManager,
|
||||
ui.BIM_UL_elements,
|
||||
ui.BIM_PT_project_tree,
|
||||
workspace.Hotkey,
|
||||
)
|
||||
|
||||
@@ -56,7 +57,7 @@ def register():
|
||||
bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False)
|
||||
bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties)
|
||||
bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
|
||||
bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
|
||||
bpy.types.Scene.BIMProjectTreeProperties = bpy.props.PointerProperty(type=prop.BIMProjectTreeProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -64,4 +65,4 @@ def unregister():
|
||||
bpy.utils.unregister_tool(workspace.SpatialTool)
|
||||
del bpy.types.Scene.BIMSpatialProperties
|
||||
del bpy.types.Object.BIMObjectSpatialProperties
|
||||
del bpy.types.Scene.BIMSpatialManagerProperties
|
||||
del bpy.types.Scene.BIMProjectTreeProperties
|
||||
|
||||
@@ -23,6 +23,7 @@ import ifcopenshell.util.element
|
||||
|
||||
def refresh():
|
||||
SpatialData.is_loaded = False
|
||||
ProjectTreeData.is_loaded = False
|
||||
|
||||
|
||||
class SpatialData:
|
||||
@@ -31,28 +32,28 @@ class SpatialData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"parent_container_id": cls.parent_container_id(),
|
||||
"is_directly_contained": cls.is_directly_contained(),
|
||||
"label": cls.label(),
|
||||
"references": cls.references(),
|
||||
"containers": cls.containers(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
cls.data["poll"] = cls.poll()
|
||||
if cls.data["poll"]:
|
||||
cls.data.update(
|
||||
{
|
||||
"parent_container_id": cls.parent_container_id(),
|
||||
"is_directly_contained": cls.is_directly_contained(),
|
||||
"label": cls.label(),
|
||||
"references": cls.references(),
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def containers(cls):
|
||||
results = {}
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||
else:
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialElement")
|
||||
for container in spatial_elements:
|
||||
results[container.id()] = {
|
||||
"type": container.is_a(),
|
||||
"id": container.id(),
|
||||
}
|
||||
return results
|
||||
def poll(cls):
|
||||
if not bpy.context.active_object:
|
||||
return False
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if not element:
|
||||
return False
|
||||
if element.is_a("IfcElement") or element.is_a("IfcAnnotation") or element.is_a("IfcGrid"):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def parent_container_id(cls):
|
||||
@@ -81,3 +82,29 @@ class SpatialData:
|
||||
@classmethod
|
||||
def is_directly_contained(cls):
|
||||
return bool(getattr(tool.Ifc.get_entity(bpy.context.active_object), "ContainedInStructure", False))
|
||||
|
||||
|
||||
class ProjectTreeData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {
|
||||
"containers": cls.containers(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def containers(cls):
|
||||
results = {}
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||
else:
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialElement")
|
||||
for container in spatial_elements:
|
||||
results[container.id()] = {
|
||||
"type": container.is_a(),
|
||||
"id": container.id(),
|
||||
}
|
||||
return results
|
||||
|
||||
@@ -227,26 +227,6 @@ class DeleteContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
core.delete_container(tool.Ifc, tool.Spatial, tool.Geometry, container=tool.Ifc.get().by_id(self.container))
|
||||
|
||||
class AddBuildingStorey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_building_storey"
|
||||
bl_label = "Add Storey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
part_class: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
active_container = tool.Spatial.get_active_container()
|
||||
obj = tool.Ifc.get_object(active_container)
|
||||
blenderbim.core.aggregate.add_part_to_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
tool.Blender,
|
||||
obj=obj,
|
||||
part_class=self.part_class,
|
||||
part_name="Unnamed",
|
||||
)
|
||||
core.load_container_manager(tool.Spatial)
|
||||
|
||||
|
||||
class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_decomposed_elements"
|
||||
|
||||
@@ -36,27 +36,21 @@ import ifcopenshell
|
||||
|
||||
|
||||
def update_elevation(self, context):
|
||||
entity = tool.Ifc.get().by_id(self.active_container_id)
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if not obj:
|
||||
return
|
||||
obj.location.z = self.elevation
|
||||
if ifc_definition_id := self.ifc_definition_id:
|
||||
entity = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if not obj:
|
||||
return
|
||||
obj.location.z = self.elevation
|
||||
|
||||
|
||||
def update_name(self, context):
|
||||
if ifc_definition_id := self.ifc_definition_id:
|
||||
tool.Spatial.edit_container_name(tool.Ifc.get().by_id(ifc_definition_id), self.name)
|
||||
|
||||
|
||||
def update_active_container_index(self, context):
|
||||
if self.active_container_index < 0:
|
||||
return
|
||||
self.active_container_id = self.containers[self.active_container_index].ifc_definition_id
|
||||
self.container_name = self.containers[self.active_container_index].name
|
||||
self.elevation = self.containers[self.active_container_index].elevation
|
||||
|
||||
|
||||
def updateContainerName(self, context):
|
||||
props = context.scene.BIMSpatialManagerProperties
|
||||
if not props.is_container_update_enabled or self.name == "Unnamed":
|
||||
return
|
||||
tool.Spatial.edit_container_name(tool.Ifc.get().by_id(props.active_container_id), self.name)
|
||||
props.container_name = self.name
|
||||
tool.Spatial.load_contained_elements()
|
||||
|
||||
|
||||
def update_relating_container_from_object(self, context):
|
||||
@@ -104,20 +98,34 @@ class BIMObjectSpatialProperties(PropertyGroup):
|
||||
|
||||
|
||||
class BIMContainer(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=updateContainerName)
|
||||
elevation: FloatProperty(name="Elevation", subtype="DISTANCE")
|
||||
name: StringProperty(name="Name", update=update_name)
|
||||
ifc_class: StringProperty(name="IFC Class")
|
||||
description: StringProperty(name="Description")
|
||||
long_name: StringProperty(name="Long Name")
|
||||
elevation: FloatProperty(name="Elevation", subtype="DISTANCE", update=update_elevation)
|
||||
level_index: IntProperty(name="Level Index")
|
||||
has_children: BoolProperty(name="Has Children")
|
||||
is_expanded: BoolProperty(name="Is Expanded")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class BIMSpatialManagerProperties(PropertyGroup):
|
||||
class Element(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_class: BoolProperty(name="Is Class", default=False)
|
||||
is_type: BoolProperty(name="Is Type", default=False)
|
||||
total: IntProperty(name="Total")
|
||||
|
||||
|
||||
class BIMProjectTreeProperties(PropertyGroup):
|
||||
containers: CollectionProperty(name="Containers", type=BIMContainer)
|
||||
contracted_containers: StringProperty(name="Contracted containers", default="[]")
|
||||
expanded_containers: StringProperty(name="Expanded containers", default="[]")
|
||||
active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index)
|
||||
active_container_id: IntProperty(name="Active Container Id")
|
||||
container_name: StringProperty(name="Container Name")
|
||||
elevation: FloatProperty(name="Elevation", update=update_elevation, subtype="DISTANCE")
|
||||
is_container_update_enabled: BoolProperty(name="Is Container Update Enabled", default=True) # TODO:review
|
||||
elements: CollectionProperty(name="Elements", type=Element)
|
||||
active_element_index: IntProperty(name="Active Element Index")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
@property
|
||||
def active_container(self):
|
||||
if self.containers and self.active_container_index < len(self.containers):
|
||||
return self.containers[self.active_container_index]
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.spatial.data import SpatialData
|
||||
from blenderbim.bim.module.spatial.data import SpatialData, ProjectTreeData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
@@ -32,14 +31,9 @@ class BIM_PT_spatial(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
if not oprops.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(oprops.ifc_definition_id):
|
||||
return False
|
||||
return True
|
||||
if not SpatialData.is_loaded:
|
||||
SpatialData.load()
|
||||
return SpatialData.data["poll"]
|
||||
|
||||
def draw(self, context):
|
||||
if not SpatialData.is_loaded:
|
||||
@@ -97,34 +91,41 @@ class BIM_UL_containers(UIList):
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_SpatialManager(Panel):
|
||||
bl_label = "Spatial Manager"
|
||||
bl_idname = "BIM_PT_SpatialManager"
|
||||
class BIM_PT_project_tree(Panel):
|
||||
bl_label = "Project Tree"
|
||||
bl_idname = "BIM_PT_project_tree"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_tree"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and tool.Ifc.schema().name() != "IFC2X3"
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not SpatialData.is_loaded:
|
||||
SpatialData.load()
|
||||
self.props = context.scene.BIMSpatialManagerProperties
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure")
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="Select Children")
|
||||
spatial_data = SpatialData.data["containers"].get(ifc_definition_id, None)
|
||||
if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]:
|
||||
row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey"
|
||||
row.operator("bim.delete_container", icon="X", text="Delete").container = ifc_definition_id
|
||||
if not ProjectTreeData.is_loaded:
|
||||
ProjectTreeData.load()
|
||||
self.props = context.scene.BIMProjectTreeProperties
|
||||
|
||||
if self.props.active_container:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
text=f"Active {self.props.active_container.ifc_class}: {self.props.active_container.name}",
|
||||
icon="OUTLINER_COLLECTION",
|
||||
)
|
||||
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text="")
|
||||
row.operator("bim.delete_container", icon="X", text="").container = (
|
||||
self.props.active_container.ifc_definition_id
|
||||
)
|
||||
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="")
|
||||
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Warning: No Active Container", icon="ERROR")
|
||||
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_containers_manager",
|
||||
"",
|
||||
@@ -132,13 +133,33 @@ class BIM_PT_SpatialManager(Panel):
|
||||
"containers",
|
||||
self.props,
|
||||
"active_container_index",
|
||||
rows=10,
|
||||
)
|
||||
|
||||
if not self.props.active_container:
|
||||
return
|
||||
|
||||
# spatial_data = ProjectTreeData.data["containers"].get(ifc_definition_id, None)
|
||||
# if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]:
|
||||
# row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey"
|
||||
|
||||
if not self.props.total_elements:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Contained Elements", icon="FILE_3D")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{self.props.total_elements} Contained Elements", icon="FILE_3D")
|
||||
row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_elements",
|
||||
"",
|
||||
self.props,
|
||||
"elements",
|
||||
self.props,
|
||||
"active_element_index",
|
||||
)
|
||||
row = self.layout.row()
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
row.prop(self.props, "container_name", text="")
|
||||
row.prop(self.props, "elevation", text="")
|
||||
op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply")
|
||||
op.container = self.props.containers[self.props.active_container_index].ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_containers_manager(UIList):
|
||||
@@ -146,22 +167,52 @@ class BIM_UL_containers_manager(UIList):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
self.draw_hierarchy(row, item)
|
||||
split1 = row.split(factor=0.7)
|
||||
split1.prop(item, "name", emboss=False, text="")
|
||||
split2 = row.split(factor=1)
|
||||
split2.label(icon="BLANK1", text=tool.Unit.blender_format_unit(item.elevation))
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
row.prop(item, "long_name", emboss=False, text="")
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.prop(item, "elevation", emboss=False, text="")
|
||||
|
||||
def draw_hierarchy(self, row, item):
|
||||
for i in range(0, item.level_index):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
if item.is_expanded:
|
||||
row.operator(
|
||||
"bim.contract_container", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
|
||||
).container = item.ifc_definition_id
|
||||
row.operator("bim.contract_container", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN").container = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
else:
|
||||
row.operator(
|
||||
"bim.expand_container", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).container = item.ifc_definition_id
|
||||
row.operator("bim.expand_container", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT").container = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
else:
|
||||
row.label(text="", icon="DOT")
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.ifc_class == "IfcSite":
|
||||
row.label(text="", icon="WORLD")
|
||||
elif item.ifc_class == "IfcBuilding":
|
||||
row.label(text="", icon="HOME")
|
||||
elif item.ifc_class == "IfcBuildingStorey":
|
||||
row.label(text="", icon="LINENUMBERS_OFF")
|
||||
elif item.ifc_class == "IfcSpace":
|
||||
row.label(text="", icon="ANTIALIASED")
|
||||
else:
|
||||
row.label(text="", icon="META_PLANE")
|
||||
|
||||
|
||||
class BIM_UL_elements(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if item.is_class:
|
||||
row.label(text="", icon="DISCLOSURE_TRI_DOWN")
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
elif item.is_type:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text="", icon="DISCLOSURE_TRI_DOWN")
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
|
||||
@@ -64,16 +64,10 @@ def add_layout_hotkey(layout, text, hotkey, description):
|
||||
tool.Blender.add_layout_hotkey_operator(*args)
|
||||
|
||||
|
||||
# NOTES before adding new operators:
|
||||
# - add scene.BIMSpatialProperties
|
||||
# - add SpatialData
|
||||
|
||||
|
||||
class SpatialToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
# cls.props = context.scene.BIMSpatialProperties
|
||||
cls.model_props = context.scene.BIMModelProperties
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
@@ -151,12 +145,10 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMSpatialProperties
|
||||
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):
|
||||
|
||||
@@ -24,6 +24,7 @@ classes = (
|
||||
operator.AddPresentationStyle,
|
||||
operator.AddStyle,
|
||||
operator.AddSurfaceTexture,
|
||||
operator.AssignStyleToSelected,
|
||||
operator.BrowseExternalStyle,
|
||||
operator.RemoveTextureMap,
|
||||
operator.ChooseTextureMapPath,
|
||||
|
||||
@@ -22,6 +22,8 @@ import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.style as core
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.representation
|
||||
from blenderbim.bim.module.style.prop import switch_shading
|
||||
from pathlib import Path
|
||||
@@ -160,13 +162,6 @@ class UpdateCurrentStyle(bpy.types.Operator):
|
||||
update_all: bpy.props.BoolProperty(name="Update All", default=False, options={"SKIP_SAVE"})
|
||||
style_id: bpy.props.IntProperty(default=0, options={"SKIP_SAVE"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.selected_objects:
|
||||
cls.poll_message_set("No objects selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
# updating all styles on shift+click
|
||||
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
|
||||
@@ -957,3 +952,49 @@ class SaveUVToStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, f"UV saved to the style {style.Name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignStyleToSelected(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_style_to_selected"
|
||||
bl_label = "Assign Style To Selected"
|
||||
bl_description = "Assign style to the selected objects' active representations"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
style_id: bpy.props.IntProperty(name="Style ID")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.selected_objects:
|
||||
cls.poll_message_set("No objects selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
if self.style_id == 0:
|
||||
self.report({"ERROR"}, "No style provided")
|
||||
return {"CANCELLED"}
|
||||
ifc_file = tool.Ifc.get()
|
||||
style = ifc_file.by_id(self.style_id)
|
||||
|
||||
representations: dict[ifcopenshell.entity_instance, bpy.types.Object] = {}
|
||||
for obj in context.selected_objects:
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
representations.setdefault(representation, obj)
|
||||
|
||||
if not representations:
|
||||
self.report({"INFO"}, "No IFC objects with representations selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
for representation in representations:
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
ifc_file,
|
||||
shape_representation=representation,
|
||||
styles=[style],
|
||||
should_use_presentation_style_assignment=tool.Geometry.should_use_presentation_style_assignment(),
|
||||
)
|
||||
|
||||
tool.Geometry.reload_representation(representations.values())
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -65,6 +65,8 @@ class BIM_PT_styles(Panel):
|
||||
|
||||
row.operator("bim.duplicate_style", text="", icon="DUPLICATE").style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
op = row.operator("bim.assign_style_to_selected", text="", icon="BRUSH_DATA")
|
||||
op.style_id = style.ifc_definition_id
|
||||
op = row.operator("bim.unlink_style", text="", icon="UNLINKED")
|
||||
op.style = style.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_style", text="", icon="GREASEPENCIL")
|
||||
|
||||
@@ -263,7 +263,6 @@ class Attribute(PropertyGroup):
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
is_uri: BoolProperty(name="Is Uri", default=False)
|
||||
is_selected: BoolProperty(name="Is Selected", default=False)
|
||||
has_calculator: BoolProperty(name="Has Calculator", default=False)
|
||||
value_min: FloatProperty(description="This is used to validate int_value and float_value")
|
||||
value_min_constraint: BoolProperty(default=False, description="True if the numerical value has a lower bound")
|
||||
value_max: FloatProperty(description="This is used to validate int_value and float_value")
|
||||
|
||||
@@ -194,19 +194,17 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
layout_svg_command: StringProperty(name="Layout SVG Command", description='E.g. [["firefox", "path"]]')
|
||||
pdf_command: StringProperty(name="PDF Command", description='E.g. [["firefox", "path"]]')
|
||||
spreadsheet_command: StringProperty(name="Spreadsheet Command", description='E.g. [["libreoffice", "path"]]')
|
||||
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
||||
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
|
||||
should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True)
|
||||
should_hide_empty_props: BoolProperty(name="Hide Empty Properties", default=True)
|
||||
should_setup_workspace: BoolProperty(name="Setup Workspace Layout for BIM", default=True)
|
||||
activate_workspace: BoolProperty(name="Activate BIM Workspace on Startup", default=True)
|
||||
should_setup_toolbar: BoolProperty(
|
||||
name="Always Show Toolbar In 3D Viewport",
|
||||
default=True,
|
||||
description="If disabled, the toolbar will only load when an IFC model is active",
|
||||
)
|
||||
should_play_chaching_sound: BoolProperty(
|
||||
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
||||
)
|
||||
lock_grids_on_import: BoolProperty(name="Should Lock Grids By Default", default=True)
|
||||
spatial_elements_unselectable: BoolProperty(name="Should Make Spatial Elements Unselectable By Default", default=True)
|
||||
should_play_chaching_sound: BoolProperty(name="Play A Cha-Ching Sound When Project Costs Updates", default=False)
|
||||
lock_grids_on_import: BoolProperty(name="Lock Grids By Default", default=True)
|
||||
spatial_elements_unselectable: BoolProperty(name="Make Spatial Elements Unselectable By Default", default=True)
|
||||
decorations_colour: bpy.props.FloatVectorProperty(
|
||||
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
|
||||
)
|
||||
@@ -277,12 +275,12 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.prop(self, "spreadsheet_command")
|
||||
row = layout.row()
|
||||
row.prop(self, "openlca_port")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_hide_empty_props")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_setup_workspace")
|
||||
row = layout.row()
|
||||
row.prop(self, "activate_workspace")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_setup_toolbar")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_play_chaching_sound")
|
||||
@@ -407,7 +405,7 @@ class BIM_PT_tabs(Panel):
|
||||
|
||||
if blenderbim.last_error:
|
||||
box = self.layout.box()
|
||||
box.alert=True
|
||||
box.alert = True
|
||||
row = box.row(align=True)
|
||||
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
|
||||
row.operator("bim.close_error", text="", icon="CANCEL")
|
||||
@@ -442,6 +440,20 @@ class BIM_PT_tab_project_info(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_project_tree(Panel):
|
||||
bl_label = "Project Tree"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "PROJECT")
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_project_setup(Panel):
|
||||
bl_label = "Project Setup"
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -490,7 +502,7 @@ class BIM_PT_tab_grouping_and_filtering(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED","HEADER_LAYOUT_EXPAND"}
|
||||
bl_options = {"DEFAULT_CLOSED", "HEADER_LAYOUT_EXPAND"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -503,8 +515,9 @@ class BIM_PT_tab_grouping_and_filtering(Panel):
|
||||
# Draws help button on the right
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="") # empty text occupies the left of the row
|
||||
row.operator("bim.open_uri", text="", icon="HELP").uri = \
|
||||
row.operator("bim.open_uri", text="", icon="HELP").uri = (
|
||||
"https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html"
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_tab_geometry(Panel):
|
||||
@@ -535,6 +548,20 @@ class BIM_PT_tab_status(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_qto(Panel):
|
||||
bl_label = "Quantity Take-off"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_resources(Panel):
|
||||
bl_label = "Resources"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -16,20 +16,37 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
def enable_editing_aggregate(aggregator, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def enable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None:
|
||||
aggregator.enable_editing(obj)
|
||||
|
||||
|
||||
def disable_editing_aggregate(aggregator, obj=None):
|
||||
def disable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None:
|
||||
aggregator.disable_editing(obj)
|
||||
|
||||
|
||||
def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=None):
|
||||
def assign_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregator: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
relating_obj: Optional[bpy.types.Object] = None,
|
||||
related_obj: Optional[bpy.types.Object] = None,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not aggregator.can_aggregate(relating_obj, related_obj):
|
||||
return
|
||||
raise IncompatibleAggregateError
|
||||
relating_object = ifc.get_entity(relating_obj)
|
||||
if aggregator.has_physical_body_representation(relating_object):
|
||||
raise AggregateRepresentationError
|
||||
rel = ifc.run(
|
||||
"aggregate.assign_object", products=[ifc.get_entity(related_obj)], relating_object=ifc.get_entity(relating_obj)
|
||||
"aggregate.assign_object", products=[ifc.get_entity(related_obj)], relating_object=relating_object
|
||||
)
|
||||
collector.assign(relating_obj)
|
||||
collector.assign(related_obj)
|
||||
@@ -37,7 +54,13 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
|
||||
return rel
|
||||
|
||||
|
||||
def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None):
|
||||
def unassign_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregate: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
relating_obj: Optional[bpy.types.Object] = None,
|
||||
related_obj: Optional[bpy.types.Object] = None,
|
||||
) -> None:
|
||||
related_element = ifc.get_entity(related_obj)
|
||||
container = aggregate.get_container(related_element)
|
||||
if not relating_obj:
|
||||
@@ -52,7 +75,23 @@ def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=No
|
||||
collector.assign(related_obj)
|
||||
|
||||
|
||||
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
|
||||
def add_part_to_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregator: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
blender: tool.Blender,
|
||||
obj: bpy.types.Object,
|
||||
part_class: str,
|
||||
part_name: Optional[str] = None,
|
||||
) -> None:
|
||||
part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name)
|
||||
assign_object(ifc, aggregator, collector, relating_obj=obj, related_obj=part_obj)
|
||||
blender.set_active_object(obj)
|
||||
|
||||
|
||||
class IncompatibleAggregateError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AggregateRepresentationError(Exception):
|
||||
pass
|
||||
|
||||
@@ -484,12 +484,4 @@ def activate_drawing_view(
|
||||
drawing_tool.import_annotations_in_group(drawing_tool.get_drawing_group(drawing))
|
||||
blender.activate_camera(camera)
|
||||
drawing_tool.isolate_camera_collection(camera)
|
||||
try:
|
||||
blender.set_active_object(camera)
|
||||
except:
|
||||
raise CameraNotAvailableError()
|
||||
drawing_tool.activate_drawing(camera)
|
||||
|
||||
|
||||
class CameraNotAvailableError(Exception):
|
||||
pass
|
||||
|
||||
@@ -16,15 +16,33 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
def unlink_material(ifc, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def unlink_material(ifc: tool.Ifc, obj: bpy.types.Material) -> None:
|
||||
ifc.unlink(obj=obj)
|
||||
|
||||
|
||||
def add_material(ifc, material, style, obj=None, name=None, category=None, description=None):
|
||||
def add_material(
|
||||
ifc: tool.Ifc,
|
||||
material: tool.Material,
|
||||
style: tool.Style,
|
||||
obj: Optional[bpy.types.Material] = None,
|
||||
name: Optional[str] = None,
|
||||
category: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if not obj:
|
||||
obj = material.add_default_material_object(name)
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj), category=category, description=description)
|
||||
ifc_material = ifc.run(
|
||||
"material.add_material", name=material.get_name(obj), category=category, description=description
|
||||
)
|
||||
ifc.link(ifc_material, obj)
|
||||
ifc_style = style.get_style(obj)
|
||||
if ifc_style:
|
||||
@@ -36,14 +54,16 @@ def add_material(ifc, material, style, obj=None, name=None, category=None, descr
|
||||
return ifc_material
|
||||
|
||||
|
||||
def add_material_set(ifc, material, set_type=None):
|
||||
def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> ifcopenshell.entity_instance:
|
||||
ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type)
|
||||
if material.is_editing_materials():
|
||||
material.import_material_definitions(material.get_active_material_type())
|
||||
return ifc_material
|
||||
|
||||
|
||||
def remove_material(ifc, material_tool, style, material=None) -> bool:
|
||||
def remove_material(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, style: tool.Style, material: ifcopenshell.entity_instance
|
||||
) -> bool:
|
||||
"""returns True after deleting False,\n
|
||||
returns False if material used in material sets and cannot be removed"""
|
||||
if material_tool.is_material_used_in_sets(material):
|
||||
@@ -58,31 +78,33 @@ def remove_material(ifc, material_tool, style, material=None) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def remove_material_set(ifc, material_tool, material=None):
|
||||
def remove_material_set(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("material.remove_material_set", material=material)
|
||||
if material_tool.is_editing_materials():
|
||||
material_tool.import_material_definitions(material_tool.get_active_material_type())
|
||||
|
||||
|
||||
def load_materials(material, material_type=None):
|
||||
def load_materials(material: tool.Material, material_type: str) -> None:
|
||||
material.import_material_definitions(material_type)
|
||||
material.enable_editing_materials()
|
||||
|
||||
|
||||
def disable_editing_materials(material):
|
||||
def disable_editing_materials(material: tool.Material) -> None:
|
||||
material.disable_editing_materials()
|
||||
|
||||
|
||||
def select_by_material(material_tool, spatial, material=None):
|
||||
def select_by_material(
|
||||
material_tool: tool.Material, spatial: tool.Spatial, material: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
spatial.select_products(material_tool.get_elements_by_material(material))
|
||||
|
||||
|
||||
def enable_editing_material(material_tool, material):
|
||||
def enable_editing_material(material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
material_tool.load_material_attributes(material)
|
||||
material_tool.enable_editing_material(material)
|
||||
|
||||
|
||||
def edit_material(ifc, material_tool, material):
|
||||
def edit_material(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
attributes = material_tool.get_material_attributes()
|
||||
ifc.run("material.edit_material", material=material, attributes=attributes)
|
||||
material_tool.sync_blender_material_name(material)
|
||||
@@ -92,11 +114,13 @@ def edit_material(ifc, material_tool, material):
|
||||
material_tool.enable_editing_materials()
|
||||
|
||||
|
||||
def disable_editing_material(material_tool):
|
||||
def disable_editing_material(material_tool: tool.Material) -> None:
|
||||
material_tool.disable_editing_material()
|
||||
|
||||
|
||||
def assign_material(ifc, material_tool, material_type, objects):
|
||||
def assign_material(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, material_type: Union[str, None], objects: list[bpy.types.Object]
|
||||
) -> None:
|
||||
material_type = material_type or material_tool.get_active_object_material()
|
||||
material = material_tool.get_active_material()
|
||||
for obj in objects:
|
||||
@@ -109,7 +133,7 @@ def assign_material(ifc, material_tool, material_type, objects):
|
||||
material_tool.add_material_to_set(material_set=assigned_material, material=material)
|
||||
|
||||
|
||||
def unassign_material(ifc, material_tool, objects):
|
||||
def unassign_material(ifc: tool.Ifc, material_tool: tool.Material, objects: list[bpy.types.Object]) -> None:
|
||||
for obj in objects:
|
||||
element = ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -125,7 +149,9 @@ def unassign_material(ifc, material_tool, objects):
|
||||
ifc.run("material.unassign_material", products=[element])
|
||||
|
||||
|
||||
def patch_non_parametric_mep_segment(ifc, material_tool, profile_tool, obj):
|
||||
def patch_non_parametric_mep_segment(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, profile_tool: tool.Profile, obj: bpy.types.Object
|
||||
) -> None:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
@@ -16,53 +16,15 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
def calculate_circle_radius(qto, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def calculate_circle_radius(qto: tool.Qto, obj: bpy.types.Object) -> float:
|
||||
result = qto.get_radius_of_selected_vertices(obj)
|
||||
qto.set_qto_result(result)
|
||||
return result
|
||||
|
||||
|
||||
def assign_objects_base_qto(ifc, qto, selected_objects):
|
||||
for obj in selected_objects:
|
||||
assign_object_base_qto(ifc, qto, obj)
|
||||
|
||||
|
||||
def assign_object_base_qto(ifc, qto, obj):
|
||||
product = ifc.get_entity(obj)
|
||||
if not product:
|
||||
return
|
||||
base_quantity_name = qto.get_applicable_base_quantity_name(product)
|
||||
if not base_quantity_name:
|
||||
return
|
||||
ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
|
||||
|
||||
def calculate_objects_base_quantities(ifc, cost, qto, calculator, selected_objects):
|
||||
if selected_objects:
|
||||
for obj in selected_objects:
|
||||
calculate_object_base_quantities(ifc, cost, qto, calculator, obj)
|
||||
|
||||
|
||||
def calculate_object_base_quantities(ifc, cost, qto, calculator, obj):
|
||||
product = ifc.get_entity(obj)
|
||||
if not product:
|
||||
return
|
||||
base_quantity_name = qto.get_applicable_base_quantity_name(product)
|
||||
if not base_quantity_name:
|
||||
print(f"There is no base quantity")
|
||||
return
|
||||
base_qto = qto.get_base_qto(product)
|
||||
if not base_qto:
|
||||
base_qto = ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
calculated_quantities = qto.get_calculated_object_quantities(calculator, base_quantity_name, obj)
|
||||
ifc.run("pset.edit_qto", qto=base_qto, properties=calculated_quantities)
|
||||
cost.update_cost_items(product=product)
|
||||
@@ -533,20 +533,6 @@ def go_to_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> U
|
||||
return "Work schedule is not active"
|
||||
|
||||
|
||||
def highlight_product_related_task(sequence: tool.Sequence, spatial: tool.Spatial, product_type=None) -> None:
|
||||
products = spatial.get_selected_products()
|
||||
if products:
|
||||
if product_type == "Output":
|
||||
tasks = sequence.find_related_output_tasks(products[0])
|
||||
elif product_type == "Input":
|
||||
tasks = sequence.find_related_input_tasks(products[0])
|
||||
for task in tasks:
|
||||
work_schedule = sequence.get_work_schedule(task)
|
||||
is_work_schedule_active = sequence.is_work_schedule_active(work_schedule)
|
||||
if is_work_schedule_active:
|
||||
sequence.go_to_task(task)
|
||||
|
||||
|
||||
def guess_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
start, finish = sequence.guess_date_range(work_schedule)
|
||||
sequence.update_visualisation_date(start, finish)
|
||||
@@ -593,17 +579,20 @@ def generate_gantt_chart(sequence: tool.Sequence, work_schedule: ifcopenshell.en
|
||||
sequence.generate_gantt_browser_chart(json, work_schedule)
|
||||
|
||||
|
||||
def load_product_related_tasks(sequence: tool.Sequence, product: ifcopenshell.entity_instance) -> Union[None, str]:
|
||||
def load_product_related_tasks(
|
||||
sequence: tool.Sequence, product: ifcopenshell.entity_instance
|
||||
) -> Union[list[ifcopenshell.entity_instance], str]:
|
||||
filter_by_schedule = sequence.is_filter_by_active_schedule()
|
||||
if filter_by_schedule:
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
if work_schedule:
|
||||
task_inputs, task_ouputs = sequence.get_tasks_for_product(product, work_schedule)
|
||||
else:
|
||||
return "No active work schedule"
|
||||
return "No active work schedule."
|
||||
else:
|
||||
task_inputs, task_ouputs = sequence.get_tasks_for_product(product)
|
||||
sequence.load_product_related_tasks(task_inputs, task_ouputs)
|
||||
return task_inputs + task_ouputs
|
||||
|
||||
|
||||
def reorder_task_nesting(
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Any
|
||||
|
||||
def add_style(ifc, style, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def add_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> ifcopenshell.entity_instance:
|
||||
element = ifc.run("style.add_style", name=style.get_name(obj))
|
||||
ifc.link(element, obj)
|
||||
if style.can_support_rendering_style(obj):
|
||||
@@ -33,18 +41,26 @@ def add_style(ifc, style, obj=None):
|
||||
return element
|
||||
|
||||
|
||||
def add_external_style(ifc, style, obj, attributes):
|
||||
def add_external_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, attributes: dict[str, Any]) -> None:
|
||||
element = style.get_style(obj)
|
||||
ifc.run(
|
||||
"style.add_surface_style", style=element, ifc_class="IfcExternallyDefinedSurfaceStyle", attributes=attributes
|
||||
)
|
||||
|
||||
|
||||
def update_external_style(ifc, style, external_style, attributes):
|
||||
# TODO: unused `style` argument?
|
||||
def update_external_style(
|
||||
ifc: tool.Ifc,
|
||||
style: ifcopenshell.entity_instance,
|
||||
external_style: ifcopenshell.entity_instance,
|
||||
attributes: dict[str, Any],
|
||||
) -> None:
|
||||
ifc.run("style.edit_surface_style", style=external_style, attributes=attributes)
|
||||
|
||||
|
||||
def remove_style(ifc, material, style_tool, style=None):
|
||||
def remove_style(
|
||||
ifc: tool.Ifc, material: tool.Material, style_tool: tool.Style, style: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
ifc.run("style.remove_style", style=style)
|
||||
@@ -54,7 +70,7 @@ def remove_style(ifc, material, style_tool, style=None):
|
||||
style_tool.import_presentation_styles(style_tool.get_active_style_type())
|
||||
|
||||
|
||||
def update_style_colours(ifc, style, obj=None, verbose=False):
|
||||
def update_style_colours(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, verbose: bool = False) -> None:
|
||||
element = style.get_style(obj)
|
||||
|
||||
if style.can_support_rendering_style(obj):
|
||||
@@ -91,7 +107,9 @@ def update_style_colours(ifc, style, obj=None, verbose=False):
|
||||
style.record_shading(obj)
|
||||
|
||||
|
||||
def update_style_textures(ifc, style, obj=None, representation=None):
|
||||
def update_style_textures(
|
||||
ifc: tool.Ifc, style: tool.Style, obj: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
element = style.get_style(obj)
|
||||
|
||||
uv_maps = style.get_uv_maps(representation)
|
||||
@@ -111,34 +129,34 @@ def update_style_textures(ifc, style, obj=None, representation=None):
|
||||
ifc.run("style.remove_surface_style", style=texture_style)
|
||||
|
||||
|
||||
def unlink_style(ifc, style=None):
|
||||
def unlink_style(ifc: tool.Ifc, style: ifcopenshell.entity_instance) -> None:
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
|
||||
|
||||
def enable_editing_style(style, obj=None):
|
||||
def enable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
style.enable_editing(obj)
|
||||
style.import_surface_attributes(style.get_style(obj), obj)
|
||||
|
||||
|
||||
def disable_editing_style(style, obj=None):
|
||||
def disable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
style.disable_editing(obj)
|
||||
|
||||
|
||||
def edit_style(ifc, style, obj=None):
|
||||
def edit_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
attributes = style.export_surface_attributes(obj)
|
||||
ifc.run("style.edit_presentation_style", style=style.get_style(obj), attributes=attributes)
|
||||
style.disable_editing(obj)
|
||||
|
||||
|
||||
def load_styles(style, style_type=None):
|
||||
def load_styles(style: tool.Style, style_type: str) -> None:
|
||||
style.import_presentation_styles(style_type)
|
||||
style.enable_editing_styles()
|
||||
|
||||
|
||||
def disable_editing_styles(style):
|
||||
def disable_editing_styles(style: tool.Style) -> None:
|
||||
style.disable_editing_styles()
|
||||
|
||||
|
||||
def select_by_style(style_tool, spatial, style=None):
|
||||
def select_by_style(style_tool: tool.Style, spatial: tool.Spatial, style: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(style_tool.get_elements_by_style(style))
|
||||
|
||||
@@ -637,16 +637,9 @@ class Pset:
|
||||
@interface
|
||||
class Qto:
|
||||
def get_radius_of_selected_vertices(cls, obj): pass
|
||||
def set_qto_result(cls, result): pass
|
||||
def get_applicable_quantity_names(cls, qto_name): pass
|
||||
def get_applicable_base_quantity_name(cls, product): pass
|
||||
def get_rounded_value(cls, new_quantity): pass
|
||||
def get_calculated_object_quantities(cls, calculator, baste_qto, object): pass
|
||||
def add_object_base_qto(cls, object): pass
|
||||
def add_product_base_qto(cls, product): pass
|
||||
def get_new_calculated_quantity(cls, qto_name, quantity_name, object): pass
|
||||
def get_new_guessed_quantity(cls, object, qto_name, quantity_name, ): pass
|
||||
def get_related_cost_item_quantities(cls, product): pass
|
||||
def get_rounded_value(cls, new_quantity): pass
|
||||
def set_qto_result(cls, result): pass
|
||||
|
||||
|
||||
@interface
|
||||
@@ -838,7 +831,7 @@ class Spatial:
|
||||
def can_reference(cls, structure, element): pass
|
||||
def contract_container(cls, container): pass
|
||||
def copy_xy(cls, src_obj, destination_obj): pass
|
||||
def create_new_storey_li(cls, element, level_index): pass
|
||||
def import_spatial_structure(cls, element, level_index): pass
|
||||
def deselect_objects(cls): pass
|
||||
def disable_editing(cls, obj): pass
|
||||
def duplicate_object_and_data(cls, obj): pass
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
@@ -29,9 +30,9 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
related_object = tool.Ifc.get_entity(related_obj)
|
||||
if not relating_object or not related_object:
|
||||
return False
|
||||
if (relating_object.is_a("IfcElement") or relating_object.is_a("IfcElementType")) and related_object.is_a("IfcElement"):
|
||||
if relating_obj.data: # See #3973
|
||||
return False
|
||||
if (relating_object.is_a("IfcElement") or relating_object.is_a("IfcElementType")) and related_object.is_a(
|
||||
"IfcElement"
|
||||
):
|
||||
return True
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
if relating_object.is_a("IfcSpatialStructureElement") and related_object.is_a("IfcSpatialStructureElement"):
|
||||
@@ -46,19 +47,28 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj):
|
||||
def has_physical_body_representation(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
if element.is_a("IfcElement") or element.is_a("IfcElementType"): # See 3973
|
||||
if ifcopenshell.util.representation.get_representation(element, "Model", "Body"):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMObjectAggregateProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
def enable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMObjectAggregateProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def get_container(cls, element):
|
||||
def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_container(element)
|
||||
|
||||
@classmethod
|
||||
def get_relating_object(cls, related_element):
|
||||
def get_relating_object(
|
||||
cls, related_element: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for rel in related_element.Decomposes:
|
||||
if rel.is_a("IfcRelAggregates"):
|
||||
return rel.RelatingObject
|
||||
|
||||
@@ -21,6 +21,7 @@ import bmesh
|
||||
import json
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim
|
||||
import addon_utils
|
||||
@@ -196,6 +197,21 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
return False
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def is_valid_data_block(cls, data_block: bpy.types.ID) -> bool:
|
||||
"""Check if Blender data-block is still valid.
|
||||
|
||||
If Blender data-block (e.g. an Object) is removed then it's
|
||||
python object gets invalidated and accessing any of it's attributes
|
||||
leads to ReferenceError: StructRNA of type Object has been removed.
|
||||
This method helps avoiding try / except ReferenceError constructions.
|
||||
"""
|
||||
try:
|
||||
data_block.bl_rna
|
||||
return True
|
||||
except ReferenceError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def show_info_message(cls, text: str, message_type: Literal["INFO", "ERROR"] = "INFO") -> None:
|
||||
"""useful for showing error messages outside blender operators
|
||||
@@ -459,6 +475,13 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
obj.select_set(False)
|
||||
context.view_layer.objects.active = active_object
|
||||
active_object.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def select_object(cls, obj: bpy.types.Object):
|
||||
try:
|
||||
obj.select_set(True)
|
||||
except RuntimeError: # Trying to select a hidden object throws an error
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def set_objects_selection(
|
||||
|
||||
@@ -95,9 +95,12 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
# NOTE: won't allow assigning IfcElements to the IfcProject directly
|
||||
# and some elements might get missing in other viewers if they're don't support displaying
|
||||
# elements without hierarchy
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj
|
||||
)
|
||||
try:
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj
|
||||
)
|
||||
except blenderbim.core.aggregate.IncompatibleAggregateError:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def assign(cls, obj: bpy.types.Object) -> None:
|
||||
|
||||
@@ -1752,6 +1752,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def activate_drawing(cls, camera: bpy.types.Object) -> None:
|
||||
selected_objects_before = bpy.context.selected_objects
|
||||
|
||||
# Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
|
||||
drawing = tool.Ifc.get_entity(camera)
|
||||
|
||||
@@ -1839,6 +1841,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
cls.import_camera_props(drawing, camera)
|
||||
|
||||
for obj in selected_objects_before:
|
||||
obj.hide_set(False)
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def get_elements_in_camera_view(
|
||||
cls, camera: bpy.types.Object, objs: list[ifcopenshell.entity_instance]
|
||||
|
||||
@@ -40,7 +40,7 @@ import blenderbim.bim.import_ifc
|
||||
from math import radians, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from typing import Union
|
||||
from typing import Union, Iterable
|
||||
|
||||
|
||||
class Geometry(blenderbim.core.tool.Geometry):
|
||||
@@ -725,11 +725,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def should_use_presentation_style_assignment(cls):
|
||||
def should_use_presentation_style_assignment(cls) -> bool:
|
||||
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
||||
|
||||
@classmethod
|
||||
def get_model_representations(cls):
|
||||
def get_model_representations(cls) -> list[ifcopenshell.entity_instance]:
|
||||
return tool.Ifc.get().by_type("IfcShapeRepresentation")
|
||||
|
||||
@classmethod
|
||||
@@ -755,21 +755,44 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@classmethod
|
||||
def reload_representation(cls, obj):
|
||||
"""reload `obj` active representation"""
|
||||
if not obj.data:
|
||||
return
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
def reload_representation(cls, obj_or_objs: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None:
|
||||
"""Reload object/objects active representation.
|
||||
|
||||
Ensures that same representations won't be reloaded multiple times.
|
||||
"""
|
||||
objs = obj_or_objs if isinstance(obj_or_objs, Iterable) else [obj_or_objs]
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
# Find all objects that use the same representation
|
||||
# as there are possibility that some of them have openings
|
||||
# (each representation with opening has a unique Mesh)
|
||||
# and therefore reloading Mesh of it's type or occurrence
|
||||
# might not be enough.
|
||||
elements = set()
|
||||
for obj in objs:
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
elements.update(ifcopenshell.util.element.get_elements_by_representation(ifc_file, representation))
|
||||
|
||||
# Filter out unique meshes to avoid
|
||||
# reloading the same representation multiple times.
|
||||
meshes_to_objects: dict[bpy.types.Mesh, bpy.types.Object]
|
||||
meshes_to_objects = {(obj:=tool.Ifc.get_object(element)).data: obj for element in elements}
|
||||
|
||||
for obj in meshes_to_objects.values():
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def remove_representation_item(cls, representation_item):
|
||||
|
||||
@@ -16,48 +16,72 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.material
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
from collections import defaultdict
|
||||
from typing import Union, Any, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# Avoid circular imports.
|
||||
from blenderbim.bim.module.material.prop import Material as MaterialItem
|
||||
|
||||
|
||||
class Material(blenderbim.core.tool.Material):
|
||||
@classmethod
|
||||
def add_default_material_object(cls, name):
|
||||
def add_default_material_object(cls, name: Union[str, None]) -> bpy.types.Material:
|
||||
return bpy.data.materials.new(name or "Default")
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
def delete_object(cls, obj: bpy.types.Material) -> None:
|
||||
bpy.data.materials.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_materials(cls):
|
||||
def disable_editing_materials(cls) -> None:
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_materials(cls):
|
||||
def enable_editing_materials(cls) -> None:
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def get_active_material_type(cls):
|
||||
def get_active_material_type(cls) -> str:
|
||||
return bpy.context.scene.BIMMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_material(cls, material):
|
||||
def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, obj):
|
||||
def get_name(cls, obj: bpy.types.Material) -> str:
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
def import_material_definitions(cls, material_type):
|
||||
def get_active_material_item(cls) -> Union[MaterialItem, None]:
|
||||
"""Get active material props item if index is valid, otherwise, return None."""
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
expanded_categories = {m.name for m in props.materials if m.is_expanded}
|
||||
if 0 <= props.active_material_index < len(props.materials):
|
||||
return props.materials[props.active_material_index]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def import_material_definitions(cls, material_type: str) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
|
||||
# Store active category name to reselect it later.
|
||||
# Occurs when we expand/contract all categories.
|
||||
active_item = cls.get_active_material_item()
|
||||
previously_selected_category = None
|
||||
if active_item and active_item.is_category:
|
||||
previously_selected_category = active_item.name
|
||||
|
||||
expanded_categories = {m.name for m in props.materials if m.is_category and m.is_expanded}
|
||||
props.materials.clear()
|
||||
get_name = lambda x: x.Name or "Unnamed"
|
||||
if material_type == "IfcMaterialLayerSet":
|
||||
@@ -65,14 +89,25 @@ class Material(blenderbim.core.tool.Material):
|
||||
elif material_type == "IfcMaterialList":
|
||||
get_name = lambda x: "Unnamed"
|
||||
materials = sorted(tool.Ifc.get().by_type(material_type), key=get_name)
|
||||
categories = {}
|
||||
categories = defaultdict(list)
|
||||
if material_type == "IfcMaterial":
|
||||
[categories.setdefault(getattr(m, "Category", "Uncategorised"), []).append(m) for m in materials]
|
||||
category_index_to_reselect = None
|
||||
|
||||
for m in materials:
|
||||
# IfcMaterial has Category since IFC4.
|
||||
category = getattr(m, "Category", None)
|
||||
category = category or "Uncategorised"
|
||||
categories[category].append(m)
|
||||
|
||||
for category, mats in categories.items():
|
||||
cat = props.materials.add()
|
||||
cat.name = category or ""
|
||||
cat.name = category
|
||||
cat.is_category = True
|
||||
cat.is_expanded = cat.name in expanded_categories
|
||||
|
||||
if previously_selected_category == category:
|
||||
category_index_to_reselect = len(props.materials) - 1
|
||||
|
||||
for material in mats if cat.is_expanded else []:
|
||||
new = props.materials.add()
|
||||
new.ifc_definition_id = material.id()
|
||||
@@ -81,6 +116,9 @@ class Material(blenderbim.core.tool.Material):
|
||||
ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
)
|
||||
new.has_style = bool(material.HasRepresentation)
|
||||
|
||||
if category_index_to_reselect is not None:
|
||||
props.active_material_index = category_index_to_reselect
|
||||
return
|
||||
for material in materials:
|
||||
new = props.materials.add()
|
||||
@@ -89,11 +127,11 @@ class Material(blenderbim.core.tool.Material):
|
||||
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
|
||||
|
||||
@classmethod
|
||||
def is_editing_materials(cls):
|
||||
def is_editing_materials(cls) -> bool:
|
||||
return bpy.context.scene.BIMMaterialProperties.is_editing
|
||||
|
||||
@classmethod
|
||||
def is_material_used_in_sets(cls, material):
|
||||
def is_material_used_in_sets(cls, material: ifcopenshell.entity_instance) -> bool:
|
||||
for inverse in tool.Ifc.get().get_inverse(material):
|
||||
if inverse.is_a() in [
|
||||
"IfcMaterialProfile",
|
||||
@@ -105,48 +143,50 @@ class Material(blenderbim.core.tool.Material):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def load_material_attributes(cls, material):
|
||||
def load_material_attributes(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.material_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(material, props.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_material(cls, material):
|
||||
def enable_editing_material(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = material.id()
|
||||
props.editing_material_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def get_material_attributes(cls):
|
||||
def get_material_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMMaterialProperties.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_material(cls):
|
||||
def disable_editing_material(cls) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = 0
|
||||
props.editing_material_type = ""
|
||||
|
||||
@classmethod
|
||||
def get_type(cls, element):
|
||||
def get_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
def get_active_object_material(cls):
|
||||
def get_active_object_material(cls) -> Union[str, None]:
|
||||
active_obj = bpy.context.active_object
|
||||
if not active_obj:
|
||||
return
|
||||
return active_obj.BIMObjectMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def get_active_material(cls):
|
||||
def get_active_material(cls) -> ifcopenshell.entity_instance:
|
||||
return tool.Ifc.get().by_id(int(bpy.context.active_object.BIMObjectMaterialProperties.material))
|
||||
|
||||
@classmethod
|
||||
def get_material(cls, element, should_inherit=False):
|
||||
def get_material(
|
||||
cls, element: ifcopenshell.entity_instance, should_inherit: bool = False
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_material(element, should_inherit=should_inherit)
|
||||
|
||||
@classmethod
|
||||
def is_a_material_set(cls, material):
|
||||
def is_a_material_set(cls, material: ifcopenshell.entity_instance) -> bool:
|
||||
return material.is_a() in [
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet",
|
||||
@@ -154,7 +194,9 @@ class Material(blenderbim.core.tool.Material):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def add_material_to_set(cls, material_set, material):
|
||||
def add_material_to_set(
|
||||
cls, material_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
if material_set.is_a("IfcMaterialConstituentSet"):
|
||||
if not material_set.MaterialConstituents:
|
||||
tool.Ifc.run(
|
||||
@@ -195,7 +237,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def has_material_profile(cls, element):
|
||||
def has_material_profile(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
material = cls.get_material(element, should_inherit=False)
|
||||
inherited_material = cls.get_material(element, should_inherit=True)
|
||||
if material and "Profile" in material.is_a():
|
||||
@@ -205,11 +247,13 @@ class Material(blenderbim.core.tool.Material):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def is_a_flow_segment(cls, element):
|
||||
def is_a_flow_segment(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return element.is_a("IfcFlowSegment")
|
||||
|
||||
@classmethod
|
||||
def replace_material_with_material_profile(cls, element):
|
||||
def replace_material_with_material_profile(
|
||||
cls, element: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
old_material = cls.get_material(element, should_inherit=False)
|
||||
old_inherited_material = cls.get_material(element, should_inherit=True)
|
||||
material = old_material if old_material and old_material.is_a("IfcMaterial") else None
|
||||
@@ -225,20 +269,14 @@ class Material(blenderbim.core.tool.Material):
|
||||
return material_profile
|
||||
|
||||
@classmethod
|
||||
def update_elements_using_material(cls, material):
|
||||
def update_elements_using_material(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
# update elements that are using this material
|
||||
elements = ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
# filter only unique representations to avoid reloading same representations multiple times
|
||||
meshes_to_objects = dict()
|
||||
for e in elements:
|
||||
obj = tool.Ifc.get_object(e)
|
||||
mesh = obj.data
|
||||
meshes_to_objects[mesh] = obj
|
||||
for obj in meshes_to_objects.values():
|
||||
tool.Geometry.reload_representation(obj)
|
||||
objects = [tool.Ifc.get_object(e) for e in elements]
|
||||
tool.Geometry.reload_representation(objects)
|
||||
|
||||
@classmethod
|
||||
def sync_blender_material_name(cls, material):
|
||||
def sync_blender_material_name(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
name = material.Name or "Unnamed"
|
||||
obj = tool.Ifc.get_object(material)
|
||||
if obj:
|
||||
@@ -251,7 +289,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
obj.name = name
|
||||
|
||||
@classmethod
|
||||
def get_style(cls, material):
|
||||
def get_style(cls, material: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for material_representation in material.HasRepresentation:
|
||||
for representation in material_representation.Representations:
|
||||
for item in representation.Items:
|
||||
|
||||
@@ -17,18 +17,23 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import PIL.ImageDraw
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Profile(blenderbim.core.tool.Profile):
|
||||
@classmethod
|
||||
def draw_image_for_ifc_profile(cls, draw, profile, size):
|
||||
def draw_image_for_ifc_profile(
|
||||
cls, draw: PIL.ImageDraw.ImageDraw, profile: ifcopenshell.entity_instance, size: float
|
||||
) -> None:
|
||||
"""generates image based on `profile` using `PIL.ImageDraw`"""
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
@@ -57,11 +62,11 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
draw.line((tuple(grouped_verts[e[0]]), tuple(grouped_verts[e[1]])), fill="white", width=2)
|
||||
|
||||
@classmethod
|
||||
def is_editing_profile(cls):
|
||||
return ProfileDecorator.installed
|
||||
def is_editing_profile(cls) -> bool:
|
||||
return bool(ProfileDecorator.installed)
|
||||
|
||||
@classmethod
|
||||
def get_profile(cls, element):
|
||||
def get_profile(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
representations = element.Representation
|
||||
for representation in representations.Representations:
|
||||
if not representation.is_a("IfcShapeRepresentation"):
|
||||
@@ -74,7 +79,7 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_model_profiles(cls):
|
||||
def get_model_profiles(cls) -> list[ifcopenshell.entity_instance]:
|
||||
return tool.Ifc.get().by_type("IfcProfileDef")
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
from typing import Union
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
|
||||
|
||||
class Pset(blenderbim.core.tool.Pset):
|
||||
@@ -177,7 +176,6 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
metadata.has_calculator = bool(mapper.get(pset_template.Name, {}).get(prop_template.Name, None))
|
||||
metadata.data_type = cls.get_prop_template_primitive_type(prop_template)
|
||||
|
||||
special_type = ""
|
||||
|
||||
@@ -16,20 +16,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from types import ClassMethodDescriptorType
|
||||
import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
from mathutils import Vector
|
||||
from ifcopenshell import util
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator, QuanityTypes
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
import blenderbim.bim.schema
|
||||
from mathutils import Vector
|
||||
from typing import Optional, Union, Literal
|
||||
|
||||
QuantityTypes = Literal["Q_LENGTH", "Q_AREA", "Q_VOLUME"]
|
||||
|
||||
class Qto(blenderbim.core.tool.Qto):
|
||||
@classmethod
|
||||
@@ -45,77 +42,17 @@ class Qto(blenderbim.core.tool.Qto):
|
||||
def set_qto_result(cls, result: float) -> None:
|
||||
bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
|
||||
@classmethod
|
||||
def add_object_base_qto(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
product = tool.Ifc.get_entity(obj)
|
||||
return cls.add_product_base_qto(product)
|
||||
|
||||
@classmethod
|
||||
def add_product_base_qto(cls, product: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
base_quantity_name = cls.get_applicable_base_quantity_name(product)
|
||||
if base_quantity_name:
|
||||
return tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_applicable_quantity_names(cls, qto_name: str) -> list[str]:
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(qto_name)
|
||||
return (
|
||||
[property.Name for property in pset_template.HasPropertyTemplates]
|
||||
if hasattr(pset_template, "HasPropertyTemplates")
|
||||
else []
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_applicable_base_quantity_name(
|
||||
cls, product: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> Union[str, None]:
|
||||
if not product:
|
||||
return
|
||||
applicable_qto_names = blenderbim.bim.schema.ifc.psetqto.get_applicable_names(
|
||||
product.is_a(), ifcopenshell.util.element.get_predefined_type(product), qto_only=True
|
||||
)
|
||||
return next((qto_name for qto_name in applicable_qto_names if "Qto_" in qto_name and "Base" in qto_name), None)
|
||||
|
||||
@classmethod
|
||||
def get_new_calculated_quantity(cls, qto_name: str, quantity_name: str, obj: bpy.types.Object) -> float:
|
||||
return QtoCalculator().calculate_quantity(qto_name, quantity_name, obj)
|
||||
|
||||
@classmethod
|
||||
def get_new_guessed_quantity(
|
||||
cls, obj: bpy.types.Object, quantity_name: str, alternative_prop_names: list[str]
|
||||
) -> Union[float, None]:
|
||||
return QtoCalculator().guess_quantity(quantity_name, alternative_prop_names, obj)
|
||||
|
||||
@classmethod
|
||||
def get_rounded_value(cls, new_quantity: float) -> float:
|
||||
return round(new_quantity, 3)
|
||||
|
||||
@classmethod
|
||||
def get_calculated_object_quantities(
|
||||
cls, calculator: QtoCalculator, qto_name: str, obj: bpy.types.Object
|
||||
) -> dict[str, float]:
|
||||
return {
|
||||
quantity_name: cls.get_rounded_value(value)
|
||||
for quantity_name in cls.get_applicable_quantity_names(qto_name) or []
|
||||
if cls.has_calculator(qto_name, quantity_name)
|
||||
and (value := calculator.calculate_quantity(qto_name, quantity_name, obj)) is not None
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def has_calculator(cls, qto_name: str, quantity_name: str) -> bool:
|
||||
return bool(mapper.get(qto_name, {}).get(quantity_name, None))
|
||||
|
||||
@classmethod
|
||||
def convert_to_project_units(
|
||||
cls,
|
||||
value: float,
|
||||
qto_name: Optional[str] = None,
|
||||
quantity_name: Optional[str] = None,
|
||||
quantity_type: Optional[QuanityTypes] = None,
|
||||
quantity_type: Optional[QuantityTypes] = None,
|
||||
) -> Union[float, None]:
|
||||
"""You can either specify `quantity_type` or provide `qto_name/quantity_name`
|
||||
to let method figure the `quantity_type` from the templates
|
||||
@@ -147,38 +84,27 @@ class Qto(blenderbim.core.tool.Qto):
|
||||
)
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def get_guessed_quantities(
|
||||
cls, obj: bpy.types.Object, pset_qto_properties: list[ifcopenshell.entity_instance]
|
||||
) -> dict[str, float]:
|
||||
calculated_quantities = {}
|
||||
for pset_qto_property in pset_qto_properties:
|
||||
quantity_name = pset_qto_property.get_info()["Name"]
|
||||
alternative_prop_names = [p.get_info()["Name"] for p in pset_qto_properties]
|
||||
|
||||
new_quantity = cls.get_new_guessed_quantity(obj, quantity_name, alternative_prop_names)
|
||||
|
||||
if not new_quantity:
|
||||
new_quantity = 0
|
||||
else:
|
||||
new_quantity = cls.get_rounded_value(new_quantity)
|
||||
|
||||
calculated_quantities[quantity_name] = new_quantity
|
||||
|
||||
return calculated_quantities
|
||||
|
||||
@classmethod
|
||||
def get_base_qto(cls, product: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not hasattr(product, "IsDefinedBy"):
|
||||
return
|
||||
base_qto_definition = None
|
||||
base_qto_definition_name: Union[str, None] = None
|
||||
for rel in product.IsDefinedBy or []:
|
||||
if not (
|
||||
rel.is_a("IfcRelDefinesByProperties")
|
||||
and "Base" in rel.RelatingPropertyDefinition.Name
|
||||
and "Qto_" in rel.RelatingPropertyDefinition.Name
|
||||
):
|
||||
definition = rel.RelatingPropertyDefinition
|
||||
if not rel.is_a("IfcRelDefinesByProperties"):
|
||||
continue
|
||||
return rel.RelatingPropertyDefinition
|
||||
definition = rel.RelatingPropertyDefinition
|
||||
definition_name = definition.Name
|
||||
if "Qto_" not in definition_name:
|
||||
continue
|
||||
if "Base" in definition_name:
|
||||
return definition
|
||||
if base_qto_definition and "BodyGeometryValidation" not in base_qto_definition_name:
|
||||
continue
|
||||
base_qto_definition = definition
|
||||
base_qto_definition_name = definition_name
|
||||
return base_qto_definition
|
||||
|
||||
@classmethod
|
||||
def get_related_cost_item_quantities(cls, product: ifcopenshell.entity_instance) -> list[dict]:
|
||||
|
||||
@@ -264,6 +264,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_resource_id = resource.id()
|
||||
props.editing_resource_type = "QUANTITY"
|
||||
props.active_resource_class = resource.is_a()
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_quantity(cls, resource_quantity: ifcopenshell.entity_instance) -> None:
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.geometry
|
||||
@@ -29,17 +30,17 @@ import blenderbim.core.style
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Union, Optional
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
class Root(blenderbim.core.tool.Root):
|
||||
@classmethod
|
||||
def add_tracked_opening(cls, obj):
|
||||
def add_tracked_opening(cls, obj: bpy.types.Object) -> None:
|
||||
new = bpy.context.scene.BIMModelProperties.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
@classmethod
|
||||
def assign_body_styles(cls, element, obj):
|
||||
def assign_body_styles(cls, element: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None:
|
||||
# Should this even be here? Should it be in the geometry tool?
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if body:
|
||||
@@ -56,7 +57,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def copy_representation(cls, source, dest):
|
||||
def copy_representation(cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance) -> None:
|
||||
def exclude_callback(attribute):
|
||||
return attribute.is_a("IfcProfileDef") and attribute.ProfileName
|
||||
|
||||
@@ -80,11 +81,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def does_type_have_representations(cls, element):
|
||||
def does_type_have_representations(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return bool(element.RepresentationMaps)
|
||||
|
||||
@classmethod
|
||||
def get_decomposition_relationships(cls, objs):
|
||||
def get_decomposition_relationships(
|
||||
cls, objs: list[bpy.types.Object]
|
||||
) -> dict[ifcopenshell.entity_instance, dict[str, Any]]:
|
||||
relationships = {}
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -96,7 +99,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
return relationships
|
||||
|
||||
@classmethod
|
||||
def get_connection_relationships(cls, objs):
|
||||
def get_connection_relationships(
|
||||
cls, objs: list[bpy.types.Object]
|
||||
) -> dict[ifcopenshell.entity_instance, dict[str, Any]]:
|
||||
relationships = {}
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -119,7 +124,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
return relationships
|
||||
|
||||
@classmethod
|
||||
def get_element_representation(cls, element, context):
|
||||
def get_element_representation(
|
||||
cls, element: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if context.is_a("IfcGeometricRepresentationSubContext"):
|
||||
return ifcopenshell.util.representation.get_representation(
|
||||
element,
|
||||
@@ -134,13 +141,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
def get_object_name(cls, obj):
|
||||
def get_object_name(cls, obj: bpy.types.Object) -> None:
|
||||
if "." in obj.name and obj.name.split(".")[-1].isnumeric():
|
||||
return ".".join(obj.name.split(".")[:-1])
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
def get_object_representation(cls, obj):
|
||||
def get_object_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.data and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -152,19 +159,21 @@ class Root(blenderbim.core.tool.Root):
|
||||
return element.Representation.Representations[0]
|
||||
|
||||
@classmethod
|
||||
def get_representation_context(cls, representation):
|
||||
def get_representation_context(cls, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return representation.ContextOfItems
|
||||
|
||||
@classmethod
|
||||
def is_element_a(cls, element, ifc_class):
|
||||
def is_element_a(cls, element: ifcopenshell.entity_instance, ifc_class: str) -> bool:
|
||||
return element.is_a(ifc_class)
|
||||
|
||||
@classmethod
|
||||
def link_object_data(cls, source_obj, destination_obj):
|
||||
def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
|
||||
destination_obj.data = source_obj.data
|
||||
|
||||
@classmethod
|
||||
def recreate_decompositions(cls, relationships, old_to_new):
|
||||
def recreate_decompositions(
|
||||
cls, relationships, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
) -> None:
|
||||
for subelement, data in relationships.items():
|
||||
new_subelements = old_to_new.get(subelement)
|
||||
new_elements = old_to_new.get(data["element"])
|
||||
@@ -227,7 +236,11 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def recreate_connections(cls, relationship, old_to_new):
|
||||
def recreate_connections(
|
||||
cls,
|
||||
relationship: dict[ifcopenshell.entity_instance, dict[str, Any]],
|
||||
old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||
) -> None:
|
||||
for element, data in relationship.items():
|
||||
try:
|
||||
new_relating_element = old_to_new.get(data["relating_element"])[0]
|
||||
@@ -244,7 +257,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def recreate_aggregate(cls, old_to_new):
|
||||
def recreate_aggregate(
|
||||
cls, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
) -> None:
|
||||
for old, new in old_to_new.items():
|
||||
old_aggregate = ifcopenshell.util.element.get_aggregate(old)
|
||||
if old_aggregate:
|
||||
@@ -301,7 +316,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def set_object_name(cls, obj, element):
|
||||
def set_object_name(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None:
|
||||
# This disables the Blender name event handler
|
||||
obj.BIMObjectProperties.is_renaming = True
|
||||
name = getattr(element, "Name", getattr(element, "AxisTag", None))
|
||||
@@ -309,13 +324,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
obj.BIMObjectProperties.is_renaming = False
|
||||
|
||||
@classmethod
|
||||
def unlink_object(cls, obj):
|
||||
def unlink_object(cls, obj: bpy.types.Object) -> None:
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
if hasattr(obj.data, "BIMMeshProperties"):
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
for material_slot in obj.material_slots:
|
||||
if material_slot.material:
|
||||
blenderbim.core.style.unlink_style(tool.Ifc, tool.Style, obj=material_slot.material)
|
||||
blenderbim.core.style.unlink_style(tool.Ifc, style=material_slot.material)
|
||||
blenderbim.core.material.unlink_material(tool.Ifc, obj=material_slot.material)
|
||||
if "Ifc" in obj.name and "/" in obj.name:
|
||||
obj.name = obj.name.split("/", 1)[1]
|
||||
|
||||
@@ -86,6 +86,9 @@ class Search(blenderbim.core.tool.Search):
|
||||
elif ifc_filter.type == "group":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"group{comparison}{value}")
|
||||
elif ifc_filter.type == "parent":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"parent{comparison}{value}")
|
||||
elif ifc_filter.type == "query":
|
||||
keys = cls.wrap_value(ifc_filter, ifc_filter.name)
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
@@ -201,6 +204,10 @@ class ImportFilterQueryTransformer(lark.Transformer):
|
||||
comparison, value = args
|
||||
return {"type": "group", "value": f"{comparison}{value}"}
|
||||
|
||||
def parent(self, args):
|
||||
comparison, value = args
|
||||
return {"type": "parent", "value": f"{comparison}{value}"}
|
||||
|
||||
def query(self, args):
|
||||
keys, comparison, value = args
|
||||
return {"type": "query", "name": keys, "value": f"{comparison}{value}"}
|
||||
|
||||
@@ -207,45 +207,79 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
src_obj.location = (destination_obj.location[0], destination_obj.location[1], z)
|
||||
|
||||
@classmethod
|
||||
def load_container_manager(cls):
|
||||
cls.props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
previous_container_index = cls.props.active_container_index
|
||||
cls.props.containers.clear()
|
||||
cls.contracted_containers = json.loads(cls.props.contracted_containers)
|
||||
cls.props.is_container_update_enabled = False
|
||||
parent = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
def load_contained_elements(cls):
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
props.elements.clear()
|
||||
if not (container := props.active_container):
|
||||
return
|
||||
|
||||
for object in ifcopenshell.util.element.get_parts(parent) or []:
|
||||
if object.is_a("IfcSpatialElement") or object.is_a("IfcSpatialStructureElement"):
|
||||
cls.create_new_storey_li(object, 0)
|
||||
cls.props.is_container_update_enabled = True
|
||||
# triggers spatial manager props setup
|
||||
cls.props.active_container_index = min(previous_container_index, len(cls.props.containers) - 1)
|
||||
container = tool.Ifc.get().by_id(container.ifc_definition_id)
|
||||
|
||||
results = {}
|
||||
for element in ifcopenshell.util.element.get_contained(container):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
ifc_class = element.is_a()
|
||||
type_name = element_type.Name or "Unnamed" if element_type else "Untyped"
|
||||
results.setdefault(ifc_class, {}).setdefault(type_name, 0)
|
||||
results[ifc_class][type_name] += 1
|
||||
|
||||
total_elements = 0
|
||||
for ifc_class in sorted(results.keys()):
|
||||
new = props.elements.add()
|
||||
new.name = ifc_class
|
||||
new.is_class = True
|
||||
total = 0
|
||||
for type_name in sorted(results[ifc_class].keys()):
|
||||
new2 = props.elements.add()
|
||||
new2.name = type_name
|
||||
new2.is_type = True
|
||||
new2.total = results[ifc_class][type_name]
|
||||
total += new2.total
|
||||
new.total = total
|
||||
total_elements += total
|
||||
|
||||
props.total_elements = total_elements
|
||||
|
||||
@classmethod
|
||||
def create_new_storey_li(cls, element, level_index):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
new = cls.props.containers.add()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.long_name = element.LongName or ""
|
||||
new.has_decomposition = bool(element.IsDecomposedBy)
|
||||
new.ifc_definition_id = element.id()
|
||||
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element) * si_conversion
|
||||
def load_container_manager(cls):
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
previous_container_index = props.active_container_index
|
||||
props.containers.clear()
|
||||
cls.contracted_containers = json.loads(props.contracted_containers)
|
||||
props.is_container_update_enabled = False
|
||||
parent = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
|
||||
for subelement in ifcopenshell.util.element.get_parts(parent) or []:
|
||||
if subelement.is_a("IfcSpatialElement") or subelement.is_a("IfcSpatialStructureElement"):
|
||||
cls.import_spatial_structure(subelement, 0)
|
||||
props.is_container_update_enabled = True
|
||||
# triggers spatial manager props setup
|
||||
props.active_container_index = min(previous_container_index, len(props.containers) - 1)
|
||||
|
||||
@classmethod
|
||||
def import_spatial_structure(cls, element, level_index):
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
new = props.containers.add()
|
||||
new.ifc_class = element.is_a()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.description = element.Description or ""
|
||||
new.long_name = element.LongName or ""
|
||||
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element)
|
||||
new.is_expanded = element.id() not in cls.contracted_containers
|
||||
new.level_index = level_index
|
||||
if new.has_decomposition:
|
||||
new.has_children = True
|
||||
if new.is_expanded:
|
||||
for related_object in ifcopenshell.util.element.get_parts(element) or []:
|
||||
if related_object.is_a("IfcSpatialElement") or related_object.is_a("IfcSpatialStructureElement"):
|
||||
cls.create_new_storey_li(related_object, level_index + 1)
|
||||
children = ifcopenshell.util.element.get_parts(element)
|
||||
new.has_children = bool(children)
|
||||
new.ifc_definition_id = element.id()
|
||||
if new.is_expanded:
|
||||
for child in children or []:
|
||||
cls.import_spatial_structure(child, level_index + 1)
|
||||
|
||||
@classmethod
|
||||
def edit_container_attributes(cls, entity):
|
||||
# TODO
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
name = bpy.context.scene.BIMSpatialManagerProperties.container_name
|
||||
name = bpy.context.scene.BIMProjectTreeProperties.container_name
|
||||
if name != entity.Name:
|
||||
cls.edit_container_name(entity, name)
|
||||
|
||||
@@ -255,21 +289,21 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
|
||||
@classmethod
|
||||
def get_active_container(cls):
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
if props.active_container_index < len(props.containers):
|
||||
container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
|
||||
return container
|
||||
|
||||
@classmethod
|
||||
def contract_container(cls, container):
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
contracted_containers = json.loads(props.contracted_containers)
|
||||
contracted_containers.append(container.id())
|
||||
props.contracted_containers = json.dumps(contracted_containers)
|
||||
|
||||
@classmethod
|
||||
def expand_container(cls, container):
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
props = bpy.context.scene.BIMProjectTreeProperties
|
||||
contracted_containers = json.loads(props.contracted_containers)
|
||||
contracted_containers.remove(container.id())
|
||||
props.contracted_containers = json.dumps(contracted_containers)
|
||||
|
||||
@@ -20,11 +20,12 @@ import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
from mathutils import Color
|
||||
from typing import Union
|
||||
from typing import Union, Any, Optional
|
||||
|
||||
# fmt: off
|
||||
TEXTURE_MAPS_BY_METHODS = {
|
||||
@@ -45,19 +46,19 @@ STYLE_PROPS_MAP = {
|
||||
|
||||
class Style(blenderbim.core.tool.Style):
|
||||
@classmethod
|
||||
def can_support_rendering_style(cls, obj):
|
||||
def can_support_rendering_style(cls, obj: bpy.types.Material) -> bool:
|
||||
return obj.use_nodes and hasattr(obj.node_tree, "nodes")
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj):
|
||||
def disable_editing(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_external_style(cls, obj):
|
||||
def disable_editing_external_style(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing_external_style = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_styles(cls):
|
||||
def disable_editing_styles(cls) -> None:
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
@@ -67,39 +68,40 @@ class Style(blenderbim.core.tool.Style):
|
||||
return new_style
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
def enable_editing(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_external_style(cls, obj):
|
||||
def enable_editing_external_style(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing_external_style = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_styles(cls):
|
||||
def enable_editing_styles(cls) -> None:
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def export_surface_attributes(cls, obj):
|
||||
def export_surface_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.attributes)
|
||||
|
||||
@classmethod
|
||||
def export_external_style_attributes(cls, obj):
|
||||
def export_external_style_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.external_style_attributes)
|
||||
|
||||
@classmethod
|
||||
def get_active_style_type(cls):
|
||||
def get_active_style_type(cls) -> str:
|
||||
return bpy.context.scene.BIMStylesProperties.style_type
|
||||
|
||||
# TODO: `obj` argument is unused?
|
||||
@classmethod
|
||||
def get_context(cls, obj):
|
||||
def get_context(cls, obj) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_style(cls, style):
|
||||
def get_elements_by_style(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, obj):
|
||||
def get_name(cls, obj: bpy.types.Material) -> str:
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
@@ -126,7 +128,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return style_elements
|
||||
|
||||
@classmethod
|
||||
def get_shading_style_data_from_props(cls) -> dict:
|
||||
def get_shading_style_data_from_props(cls) -> dict[str, Any]:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_style_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_rendering`"""
|
||||
surface_style_data = dict()
|
||||
@@ -154,7 +156,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return surface_style_data
|
||||
|
||||
@classmethod
|
||||
def get_texture_style_data_from_props(cls) -> list[dict]:
|
||||
def get_texture_style_data_from_props(cls) -> list[dict[str, Any]]:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_texture_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_with_textures`"""
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
@@ -174,7 +176,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return textures
|
||||
|
||||
@classmethod
|
||||
def set_surface_style_props(cls):
|
||||
def set_surface_style_props(cls) -> None:
|
||||
"""set blender style props based on currently edited IfcSurfaceStyle,
|
||||
reset unrelated props to default values"""
|
||||
|
||||
@@ -241,7 +243,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
props["update_graph"] = prev_update_graph_value
|
||||
|
||||
@classmethod
|
||||
def get_surface_rendering_attributes(cls, obj, verbose=False):
|
||||
def get_surface_rendering_attributes(cls, obj: bpy.types.Material, verbose: bool = False) -> dict[str, Any]:
|
||||
report = (lambda *x: print(*x)) if verbose else (lambda *x: None)
|
||||
|
||||
def color_to_ifc_format(color):
|
||||
@@ -403,22 +405,22 @@ class Style(blenderbim.core.tool.Style):
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_surface_rendering_style(cls, obj):
|
||||
def get_surface_rendering_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcSurfaceStyleRendering", None)
|
||||
|
||||
@classmethod
|
||||
def get_texture_style(cls, obj):
|
||||
def get_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcSurfaceStyleWithTextures", None)
|
||||
|
||||
@classmethod
|
||||
def get_external_style(cls, obj):
|
||||
def get_external_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcExternallyDefinedSurfaceStyle", None)
|
||||
|
||||
@classmethod
|
||||
def get_surface_shading_attributes(cls, obj):
|
||||
def get_surface_shading_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
data = {
|
||||
"SurfaceColour": {
|
||||
"Name": None,
|
||||
@@ -433,7 +435,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def get_surface_shading_style(cls, obj):
|
||||
def get_surface_shading_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
|
||||
items = [s for s in style.Styles if s.is_a() == "IfcSurfaceStyleShading"]
|
||||
@@ -441,7 +443,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items[0]
|
||||
|
||||
@classmethod
|
||||
def get_surface_texture_style(cls, obj):
|
||||
def get_surface_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
|
||||
items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleWithTextures")]
|
||||
@@ -449,7 +451,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items[0]
|
||||
|
||||
@classmethod
|
||||
def get_uv_maps(cls, representation):
|
||||
def get_uv_maps(cls, representation: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
items = []
|
||||
for item in representation.Items:
|
||||
if item.is_a("IfcMappedItem"):
|
||||
@@ -464,7 +466,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def get_style_ui_props_attributes(cls, style_type):
|
||||
def get_style_ui_props_attributes(cls, style_type: str) -> Union[bpy.types.PropertyGroup, None]:
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
if style_type == "IfcExternallyDefinedSurfaceStyle":
|
||||
return props.external_style_attributes
|
||||
@@ -474,7 +476,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return props.lighting_style_colours
|
||||
|
||||
@classmethod
|
||||
def import_presentation_styles(cls, style_type):
|
||||
def import_presentation_styles(cls, style_type: str) -> None:
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
props.styles.clear()
|
||||
@@ -497,13 +499,13 @@ class Style(blenderbim.core.tool.Style):
|
||||
new.total_elements = len(ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style))
|
||||
|
||||
@classmethod
|
||||
def import_surface_attributes(cls, style, obj):
|
||||
def import_surface_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None:
|
||||
attributes = obj.BIMStyleProperties.attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(style, attributes)
|
||||
|
||||
@classmethod
|
||||
def import_external_style_attributes(cls, style, obj):
|
||||
def import_external_style_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None:
|
||||
attributes = obj.BIMStyleProperties.external_style_attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(style, attributes)
|
||||
@@ -514,26 +516,26 @@ class Style(blenderbim.core.tool.Style):
|
||||
return bool(external_style and external_style.Location and external_style.Location.endswith(".blend"))
|
||||
|
||||
@classmethod
|
||||
def is_editing_styles(cls):
|
||||
def is_editing_styles(cls) -> bool:
|
||||
return bpy.context.scene.BIMStylesProperties.is_editing
|
||||
|
||||
@classmethod
|
||||
def record_shading(cls, obj):
|
||||
def record_shading(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMMaterialProperties.shading_checksum = repr(np.array(obj.diffuse_color).tobytes())
|
||||
|
||||
@classmethod
|
||||
def select_elements(cls, elements):
|
||||
def select_elements(cls, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def change_current_style_type(cls, blender_material, style_type):
|
||||
def change_current_style_type(cls, blender_material: bpy.types.Material, style_type: str) -> None:
|
||||
blender_material.BIMStyleProperties.active_style_type = style_type
|
||||
|
||||
@classmethod
|
||||
def get_styled_items(cls, style):
|
||||
def get_styled_items(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
inverses = list(ifc_file.get_inverse(style))
|
||||
@@ -554,13 +556,15 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items
|
||||
|
||||
@classmethod
|
||||
def assign_style_to_object(cls, style, obj):
|
||||
def assign_style_to_object(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None:
|
||||
"""assigns `style` to `object` current representation"""
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
tool.Ifc.run("style.assign_representation_styles", shape_representation=representation, styles=[style])
|
||||
|
||||
@classmethod
|
||||
def assign_style_to_representation_item(cls, representation_item, style=None):
|
||||
def assign_style_to_representation_item(
|
||||
cls, representation_item: ifcopenshell.entity_instance, style: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
if not representation_item.StyledByItem:
|
||||
if style is None:
|
||||
@@ -574,12 +578,14 @@ class Style(blenderbim.core.tool.Style):
|
||||
styled_item.Styles = (style,)
|
||||
|
||||
@classmethod
|
||||
def get_representation_item_style(cls, representation_item):
|
||||
def get_representation_item_style(
|
||||
cls, representation_item: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for inverse in tool.Ifc.get().get_inverse(representation_item):
|
||||
if inverse.is_a("IfcStyledItem"):
|
||||
for style in inverse.Styles:
|
||||
return style
|
||||
|
||||
@classmethod
|
||||
def reload_material_from_ifc(cls, blender_material):
|
||||
def reload_material_from_ifc(cls, blender_material: bpy.types.Material) -> None:
|
||||
blender_material.BIMStyleProperties.active_style_type = blender_material.BIMStyleProperties.active_style_type
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ section img {
|
||||
background-color: var(--color-admonition-title-background);
|
||||
border-radius: 5px;
|
||||
font-style: italic;
|
||||
color: var(--color-admonition-title);
|
||||
color: var(--color-admonition-text);
|
||||
}
|
||||
img.icon {
|
||||
width: auto;
|
||||
|
||||
@@ -99,6 +99,7 @@ html_theme_options = {
|
||||
"color-link--visited": "#39b54a",
|
||||
"color-link--hover": "#d98014",
|
||||
"color-link--visited--hover": "#d98014",
|
||||
"color-admonition-text": "#651fff",
|
||||
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji"
|
||||
},
|
||||
"dark_css_variables": {
|
||||
@@ -113,6 +114,7 @@ html_theme_options = {
|
||||
"color-link--visited": "#39b54a",
|
||||
"color-link--hover": "#d98014",
|
||||
"color-link--visited--hover": "#d98014",
|
||||
"color-admonition-text": "#EEEEEC",
|
||||
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji"
|
||||
},
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ You will need to choose which build to download.
|
||||
- Choose ``linux``, ``macos`` (Apple Intel), ``macosm1`` (Apple Silicon), or
|
||||
``win`` depending on your operating system
|
||||
|
||||
For users who don't follow the `VFX Platform <https://vfxplatform.com/>`_
|
||||
standard, we also provide py312 builds.
|
||||
|
||||
Sometimes, a build may be delayed, or contain broken code. We try to avoid this,
|
||||
but it happens.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Some of these add-ons are not shipped with Blender:
|
||||
import GIS data, grab elevation data from the web, and generate TINs from
|
||||
survey points and contours.
|
||||
- `Ladybug Tools for Blender
|
||||
<https://github.com/Andrej730/ladybug-blender/releases/download/ladybug-blender-240419/ladybug-blender-240419.zip>`__ - Ladybug Tools
|
||||
<https://github.com/ladybug-tools/ladybug-blender/releases/download/ladybug-blender-240529/ladybug-blender-240529.zip>`__ - Ladybug Tools
|
||||
is an extension of Sverchok for environmental analysis and building physics
|
||||
simulation. It allows analysis of solar, daylight, energy, and CFD.
|
||||
- `Topologic <https://topologic.app/>`__ - Perform spatial and topological
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import json
|
||||
import ifcopenshell.util.pset
|
||||
import ifcopenshell.util.type
|
||||
|
||||
|
||||
def order_dict(dictionary):
|
||||
# https://stackoverflow.com/questions/22721579/sorting-a-nested-ordereddict-by-key-recursively
|
||||
return {k: order_dict(v) if isinstance(v, dict) else v for k, v in sorted(dictionary.items())}
|
||||
|
||||
|
||||
results = {}
|
||||
|
||||
psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
for template in psetqto.templates:
|
||||
for pset_template in template.by_type("IfcPropertySetTemplate"):
|
||||
if not pset_template.Name.startswith("Qto_"):
|
||||
continue
|
||||
query = pset_template.ApplicableEntity
|
||||
results.setdefault(query, {})
|
||||
results[query].setdefault(pset_template.Name, {})
|
||||
for quantity in pset_template.HasPropertyTemplates:
|
||||
results[query][pset_template.Name][quantity.Name] = None
|
||||
|
||||
results = order_dict(results)
|
||||
print(results)
|
||||
with open("results.json", "w") as f:
|
||||
json.dump(results, f, indent=4)
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.unit
|
||||
import ifcopenshell.api.context
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
from test.bim.bootstrap import NewFile
|
||||
@@ -92,16 +95,24 @@ class TestCanAggregate(NewFile):
|
||||
subelement_obj = bpy.data.objects.new("Object", None)
|
||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||
|
||||
def test_aggregates_with_meshes_are_invalid(self):
|
||||
|
||||
class TestHasPhysicalBodyRepresentation(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
element = ifc.createIfcElementAssembly()
|
||||
element_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
tool.Ifc.link(element, element_obj)
|
||||
subelement = ifc.createIfcBeam()
|
||||
subelement_obj = bpy.data.objects.new("Object", None)
|
||||
tool.Ifc.link(subelement, subelement_obj)
|
||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||
assert subject.has_physical_body_representation(element) is False
|
||||
ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcProject")
|
||||
ifcopenshell.api.unit.assign_unit(ifc)
|
||||
context = ifcopenshell.api.context.add_context(ifc, context_type="Model")
|
||||
body = ifcopenshell.api.context.add_context(
|
||||
ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
|
||||
)
|
||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc)
|
||||
origin = builder.create_axis2_placement_3d()
|
||||
block = ifc.createIfcCsgSolid(ifc.createIfcBlock(origin, 200, 200, 200))
|
||||
rep = builder.get_representation(context=body, items=[block])
|
||||
ifcopenshell.api.geometry.assign_representation(ifc, product=element, representation=rep)
|
||||
assert subject.has_physical_body_representation(element) is True
|
||||
|
||||
|
||||
class TestDisableEditing(NewFile):
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.pset
|
||||
import test.bim.bootstrap
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.qto.calculator as calculator
|
||||
from blenderbim.tool.qto import Qto as subject
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class TestImplementsTool(test.bim.bootstrap.NewFile):
|
||||
@@ -44,35 +45,6 @@ class TestSetQtoResult(test.bim.bootstrap.NewFile):
|
||||
assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457"
|
||||
|
||||
|
||||
class TestGetApplicableQuantityNames(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
schema = ifc.schema
|
||||
properties_templates = (
|
||||
ifcopenshell.util.pset.PsetQto(schema)
|
||||
.get_by_name("Qto_WallBaseQuantities")
|
||||
.get_info()["HasPropertyTemplates"]
|
||||
)
|
||||
applicable_quantity_names = [a.Name for a in properties_templates]
|
||||
assert subject.get_applicable_quantity_names("Qto_WallBaseQuantities") == applicable_quantity_names
|
||||
|
||||
|
||||
class TestGetApplicableBaseQuantityName(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
assert subject.get_applicable_base_quantity_name(wall) == "Qto_WallBaseQuantities"
|
||||
|
||||
def test_no_base_quantity(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
product = ifc.by_type("IfcProject")[0]
|
||||
assert subject.get_applicable_base_quantity_name(product) == None
|
||||
|
||||
|
||||
class TestGetRoundedValue(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
quantity = 1.2345
|
||||
@@ -83,12 +55,14 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
def setup_file(self):
|
||||
self.ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(self.ifc)
|
||||
project = ifcopenshell.api.run("root.create_entity", self.ifc, ifc_class="IfcProject", name="My Project")
|
||||
ifcopenshell.api.run("root.create_entity", self.ifc, ifc_class="IfcProject", name="My Project")
|
||||
|
||||
def setup_units(self, units):
|
||||
ifcopenshell.api.run("unit.assign_unit", self.ifc, **units)
|
||||
|
||||
def calculate_quantities(self, obj):
|
||||
import ifc5d.qto
|
||||
|
||||
context = ifcopenshell.api.run("context.add_context", self.ifc, context_type="Model")
|
||||
bpy.ops.mesh.primitive_cube_add(location=(0.0, 0.0, 0.0), size=2)
|
||||
obj = bpy.context.active_object
|
||||
@@ -101,12 +75,32 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
predefined_type="ELEMENTEDWALL",
|
||||
context=context,
|
||||
)
|
||||
calculator = QtoCalculator()
|
||||
base_qto = ifcopenshell.api.run("pset.add_qto", self.ifc, product=element, name="Qto_WallBaseQuantities")
|
||||
quantities = subject.get_calculated_object_quantities(
|
||||
calculator=calculator, qto_name="Qto_WallBaseQuantities", obj=obj
|
||||
)
|
||||
return quantities
|
||||
|
||||
rules = {
|
||||
"calculators": {
|
||||
"Blender": {
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"GrossFootprintArea": "get_gross_footprint_area",
|
||||
"GrossSideArea": "get_gross_side_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Height": "get_height",
|
||||
"Length": "get_length",
|
||||
"NetFootprintArea": "get_net_footprint_area",
|
||||
"NetSideArea": "get_net_side_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Width": "get_width",
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, {element}, rules)
|
||||
return {k: round(v, 3) for k, v in results[element]["Qto_WallBaseQuantities"].items() if v is not None}
|
||||
|
||||
def test_meters_project_unit(self):
|
||||
self.setup_file()
|
||||
@@ -172,35 +166,6 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
assert quantities["NetVolume"] == 282.517
|
||||
|
||||
|
||||
class TestAddObjectBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
project = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject", name="My Project")
|
||||
context = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
bpy.ops.mesh.primitive_cube_add(location=(0.0, 0.0, 0.0), size=2)
|
||||
obj = bpy.context.active_object
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class="IfcWall",
|
||||
predefined_type="ELEMENTEDWALL",
|
||||
context=context,
|
||||
)
|
||||
assert subject.add_object_base_qto(obj).Name == "Qto_WallBaseQuantities"
|
||||
|
||||
|
||||
class TestAddProductBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
base_qto = subject.add_product_base_qto(wall)
|
||||
assert base_qto.Name == "Qto_WallBaseQuantities"
|
||||
|
||||
|
||||
class TestGetBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -213,7 +178,7 @@ class TestGetBaseQto(test.bim.bootstrap.NewFile):
|
||||
assert subject.get_base_qto(product).id() == pset_qto.get_info()["id"]
|
||||
assert subject.get_base_qto(product).Name == pset_qto.Name
|
||||
|
||||
def test_isempty(self):
|
||||
def test_no_quantities(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
|
||||
@@ -0,0 +1,637 @@
|
||||
{
|
||||
"name": "IFC4 Base Quantities - IfcOpenShell",
|
||||
"description": "This ruleset quantifies every single possible standardised base quantity in IFC4 using only IfcOpenShell as a geometry processor.",
|
||||
"calculators": {
|
||||
"IfcOpenShell": {
|
||||
"IfcActuator": {
|
||||
"Qto_ActuatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminal": {
|
||||
"Qto_AirTerminalBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Perimeter": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminalBox": {
|
||||
"Qto_AirTerminalBoxTypeBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirToAirHeatRecovery": {
|
||||
"Qto_AirToAirHeatRecoveryBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAlarm": {
|
||||
"Qto_AlarmBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAudioVisualAppliance": {
|
||||
"Qto_AudioVisualApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcBeam": {
|
||||
"Qto_BeamBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": "gross_get_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"Length": "net_get_z",
|
||||
"NetSurfaceArea": "net_get_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": "net_get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcBoiler": {
|
||||
"Qto_BoilerBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcBuilding": {
|
||||
"Qto_BuildingBaseQuantities": {
|
||||
"EavesHeight": null,
|
||||
"FootprintArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossVolume": null,
|
||||
"Height": null,
|
||||
"NetFloorArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingElementProxy": {
|
||||
"Qto_BuildingElementProxyQuantities": {
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingStorey": {
|
||||
"Qto_BuildingStoreyBaseQuantities": {
|
||||
"GrossFloorArea": null,
|
||||
"GrossHeight": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"NetFloorArea": null,
|
||||
"NetHeigtht": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBurner": {
|
||||
"Qto_BurnerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierFitting": {
|
||||
"Qto_CableCarrierFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierSegment": {
|
||||
"Qto_CableCarrierSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcCableFitting": {
|
||||
"Qto_CableFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableSegment": {
|
||||
"Qto_CableSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcChiller": {
|
||||
"Qto_ChillerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcChimney": {
|
||||
"Qto_ChimneyBaseQuantities": {
|
||||
"Length": null
|
||||
}
|
||||
},
|
||||
"IfcCoil": {
|
||||
"Qto_CoilBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcColumn": {
|
||||
"Qto_ColumnBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": "gross_get_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"Length": "net_get_z",
|
||||
"NetSurfaceArea": "net_get_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": "net_get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcCommunicationsAppliance": {
|
||||
"Qto_CommunicationsApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCompressor": {
|
||||
"Qto_CompressorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCondenser": {
|
||||
"Qto_CondenserBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionEquipmentResource": {
|
||||
"Qto_ConstructionEquipmentResourceBaseQuantities": {
|
||||
"OperatingTime": null,
|
||||
"UsageTime": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionMaterialResource": {
|
||||
"Qto_ConstructionMaterialResourceBaseQuantities": {
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcController": {
|
||||
"Qto_ControllerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCooledBeam": {
|
||||
"Qto_CooledBeamBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCoolingTower": {
|
||||
"Qto_CoolingTowerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCovering": {
|
||||
"Qto_CoveringBaseQuantities": {
|
||||
"GrossArea": "gross_get_max_side_area",
|
||||
"NetArea": "net_get_max_side_area",
|
||||
"Width": "gross_get_min_xyz"
|
||||
}
|
||||
},
|
||||
"IfcCurtainWall": {
|
||||
"Qto_CurtainWallQuantities": {
|
||||
"GrossSideArea": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetSideArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDamper": {
|
||||
"Qto_DamperBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcDistributionChamberElement": {
|
||||
"Qto_DistributionChamberElementBaseQuantities": {
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcDoor": {
|
||||
"Qto_DoorBaseQuantities": {
|
||||
"Area": null,
|
||||
"Height": null,
|
||||
"Perimeter": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDuctFitting": {
|
||||
"Qto_DuctFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcDuctSegment": {
|
||||
"Qto_DuctSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcDuctSilencer": {
|
||||
"Qto_DuctSilencerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricAppliance": {
|
||||
"Qto_ElectricApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricDistributionBoard": {
|
||||
"Qto_ElectricDistributionBoardBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfCircuits": null
|
||||
}
|
||||
},
|
||||
"IfcElectricFlowStorageDevice": {
|
||||
"Qto_ElectricFlowStorageDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricGenerator": {
|
||||
"Qto_ElectricGeneratorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricMotor": {
|
||||
"Qto_ElectricMotorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricTimeControl": {
|
||||
"Qto_ElectricTimeControlBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporativeCooler": {
|
||||
"Qto_EvaporativeCoolerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporator": {
|
||||
"Qto_EvaporatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFan": {
|
||||
"Qto_FanBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFilter": {
|
||||
"Qto_FilterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFireSuppressionTerminal": {
|
||||
"Qto_FireSuppressionTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowInstrument": {
|
||||
"Qto_FlowInstrumentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowMeter": {
|
||||
"Qto_FlowMeterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFooting": {
|
||||
"Qto_FootingBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcHeatExchanger": {
|
||||
"Qto_HeatExchangerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcHumidifier": {
|
||||
"Qto_HumidifierBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcInterceptor": {
|
||||
"Qto_InterceptorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcJunctionBox": {
|
||||
"Qto_JunctionBoxBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfGangs": null
|
||||
}
|
||||
},
|
||||
"IfcLaborResource": {
|
||||
"Qto_LaborResourceBaseQuantities": {
|
||||
"OvertimeWork": null,
|
||||
"StandardWork": null
|
||||
}
|
||||
},
|
||||
"IfcLamp": {
|
||||
"Qto_LampBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcLightFixture": {
|
||||
"Qto_LightFixtureBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcMember": {
|
||||
"Qto_MemberBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": "gross_get_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"Length": "net_get_z",
|
||||
"NetSurfaceArea": "net_get_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": "net_get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcMotorConnection": {
|
||||
"Qto_MotorConnectionBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcOpeningElement": {
|
||||
"Qto_OpeningElementBaseQuantities": {
|
||||
"Area": "gross_get_max_side_area",
|
||||
"Depth": "gross_get_z",
|
||||
"Height": "gross_get_y",
|
||||
"Volume": "gross_get_volume",
|
||||
"Width": "gross_get_x"
|
||||
}
|
||||
},
|
||||
"IfcOutlet": {
|
||||
"Qto_OutletBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPile": {
|
||||
"Qto_PileBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeFitting": {
|
||||
"Qto_PipeFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeSegment": {
|
||||
"Qto_PipeSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPlate": {
|
||||
"Qto_PlateBaseQuantities": {
|
||||
"GrossArea": "gross_get_max_side_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"NetArea": "net_get_max_side_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"Perimeter": null,
|
||||
"Width": "net_get_min_xyz"
|
||||
}
|
||||
},
|
||||
"IfcProjectionElement": {
|
||||
"Qto_ProjectionElementBaseQuantities": {
|
||||
"Area": null,
|
||||
"Volume": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDevice": {
|
||||
"Qto_ProtectiveDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDeviceTrippingUnit": {
|
||||
"Qto_ProtectiveDeviceTrippingUnitBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPump": {
|
||||
"Qto_PumpBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcRailing": {
|
||||
"Qto_RailingBaseQuantities": {
|
||||
"Length": null
|
||||
}
|
||||
},
|
||||
"IfcRampFlight": {
|
||||
"Qto_RampFlightBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossVolume": null,
|
||||
"Length": null,
|
||||
"NetArea": null,
|
||||
"NetVolume": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcReinforcingElement": {
|
||||
"Qto_ReinforcingElementBaseQuantities": {
|
||||
"Count": null,
|
||||
"Length": null,
|
||||
"Weight": null
|
||||
}
|
||||
},
|
||||
"IfcRoof": {
|
||||
"Qto_RoofBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"NetArea": null,
|
||||
"ProjectedArea": null
|
||||
}
|
||||
},
|
||||
"IfcSanitaryTerminal": {
|
||||
"Qto_SanitaryTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSensor": {
|
||||
"Qto_SensorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSite": {
|
||||
"Qto_SiteBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossPerimeter": null
|
||||
}
|
||||
},
|
||||
"IfcSlab": {
|
||||
"Qto_SlabBaseQuantities": {
|
||||
"Depth": "net_get_z",
|
||||
"GrossArea": "gross_get_footprint_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"Length": "net_get_x",
|
||||
"NetArea": "net_get_footprint_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"Perimeter": "net_get_footprint_perimeter",
|
||||
"Width": "net_get_y"
|
||||
}
|
||||
},
|
||||
"IfcSolarDevice": {
|
||||
"Qto_SolarDeviceBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSpace": {
|
||||
"Qto_SpaceBaseQuantities": {
|
||||
"FinishCeilingHeight": null,
|
||||
"FinishFloorHeight": null,
|
||||
"GrossCeilingArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWallArea": null,
|
||||
"Height": null,
|
||||
"NetCeilingArea": null,
|
||||
"NetFloorArea": null,
|
||||
"NetPerimeter": null,
|
||||
"NetVolume": null,
|
||||
"NetWallArea": null
|
||||
}
|
||||
},
|
||||
"IfcSpaceHeater": {
|
||||
"Qto_SpaceHeaterBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStackTerminal": {
|
||||
"Qto_StackTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStairFlight": {
|
||||
"Qto_StairFlightBaseQuantities": {
|
||||
"GrossVolume": null,
|
||||
"Length": "net_get_max_xy",
|
||||
"NetVolume": "net_get_volume"
|
||||
}
|
||||
},
|
||||
"IfcSwitchingDevice": {
|
||||
"Qto_SwitchingDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTank": {
|
||||
"Qto_TankBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcTransformer": {
|
||||
"Qto_TransformerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTubeBundle": {
|
||||
"Qto_TubeBundleBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryControlElement": {
|
||||
"Qto_UnitaryControlElementBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryEquipment": {
|
||||
"Qto_UnitaryEquipmentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcValve": {
|
||||
"Qto_ValveBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcVibrationIsolator": {
|
||||
"Qto_VibrationIsolatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"GrossFootprintArea": null,
|
||||
"GrossSideArea": "gross_get_side_area",
|
||||
"GrossVolume": "gross_get_volume",
|
||||
"GrossWeight": null,
|
||||
"Height": "net_get_z",
|
||||
"Length": "net_get_x",
|
||||
"NetFootprintArea": null,
|
||||
"NetSideArea": "net_get_side_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"Width": "net_get_y"
|
||||
}
|
||||
},
|
||||
"IfcWasteTerminal": {
|
||||
"Qto_WasteTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWindow": {
|
||||
"Qto_WindowBaseQuantities": {
|
||||
"Area": "net_get_max_side_area",
|
||||
"Height": "net_get_z",
|
||||
"Perimeter": null,
|
||||
"Width": "net_get_x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
{
|
||||
"name": "IFC4 Base Quantities - Blender",
|
||||
"description": "This ruleset quantifies every single possible standardised base quantity in IFC4 using Blender.",
|
||||
"calculators": {
|
||||
"Blender": {
|
||||
"IfcActuator": {
|
||||
"Qto_ActuatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminal": {
|
||||
"Qto_AirTerminalBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Perimeter": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminalBox": {
|
||||
"Qto_AirTerminalBoxTypeBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirToAirHeatRecovery": {
|
||||
"Qto_AirToAirHeatRecoveryBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAlarm": {
|
||||
"Qto_AlarmBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAudioVisualAppliance": {
|
||||
"Qto_AudioVisualApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcBeam": {
|
||||
"Qto_BeamBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcBoiler": {
|
||||
"Qto_BoilerBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcBuilding": {
|
||||
"Qto_BuildingBaseQuantities": {
|
||||
"EavesHeight": null,
|
||||
"FootprintArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossVolume": null,
|
||||
"Height": null,
|
||||
"NetFloorArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingElementProxy": {
|
||||
"Qto_BuildingElementProxyQuantities": {
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcBuildingStorey": {
|
||||
"Qto_BuildingStoreyBaseQuantities": {
|
||||
"GrossFloorArea": null,
|
||||
"GrossHeight": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"NetFloorArea": null,
|
||||
"NetHeigtht": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBurner": {
|
||||
"Qto_BurnerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierFitting": {
|
||||
"Qto_CableCarrierFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierSegment": {
|
||||
"Qto_CableCarrierSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcCableFitting": {
|
||||
"Qto_CableFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableSegment": {
|
||||
"Qto_CableSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcChiller": {
|
||||
"Qto_ChillerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcChimney": {
|
||||
"Qto_ChimneyBaseQuantities": {
|
||||
"Length": "get_height"
|
||||
}
|
||||
},
|
||||
"IfcCoil": {
|
||||
"Qto_CoilBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcColumn": {
|
||||
"Qto_ColumnBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcCommunicationsAppliance": {
|
||||
"Qto_CommunicationsApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCompressor": {
|
||||
"Qto_CompressorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCondenser": {
|
||||
"Qto_CondenserBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionEquipmentResource": {
|
||||
"Qto_ConstructionEquipmentResourceBaseQuantities": {
|
||||
"OperatingTime": null,
|
||||
"UsageTime": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionMaterialResource": {
|
||||
"Qto_ConstructionMaterialResourceBaseQuantities": {
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": null,
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcController": {
|
||||
"Qto_ControllerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCooledBeam": {
|
||||
"Qto_CooledBeamBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCoolingTower": {
|
||||
"Qto_CoolingTowerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCovering": {
|
||||
"Qto_CoveringBaseQuantities": {
|
||||
"GrossArea": "get_covering_gross_area",
|
||||
"NetArea": "get_covering_net_area",
|
||||
"Width": "get_covering_width"
|
||||
}
|
||||
},
|
||||
"IfcCurtainWall": {
|
||||
"Qto_CurtainWallQuantities": {
|
||||
"GrossSideArea": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetSideArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDamper": {
|
||||
"Qto_DamperBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcDistributionChamberElement": {
|
||||
"Qto_DistributionChamberElementBaseQuantities": {
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcDoor": {
|
||||
"Qto_DoorBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Height": "get_height",
|
||||
"Perimeter": "get_rectangular_perimeter",
|
||||
"Width": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcDuctFitting": {
|
||||
"Qto_DuctFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcDuctSegment": {
|
||||
"Qto_DuctSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcDuctSilencer": {
|
||||
"Qto_DuctSilencerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricAppliance": {
|
||||
"Qto_ElectricApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricDistributionBoard": {
|
||||
"Qto_ElectricDistributionBoardBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfCircuits": null
|
||||
}
|
||||
},
|
||||
"IfcElectricFlowStorageDevice": {
|
||||
"Qto_ElectricFlowStorageDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricGenerator": {
|
||||
"Qto_ElectricGeneratorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricMotor": {
|
||||
"Qto_ElectricMotorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricTimeControl": {
|
||||
"Qto_ElectricTimeControlBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporativeCooler": {
|
||||
"Qto_EvaporativeCoolerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporator": {
|
||||
"Qto_EvaporatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFan": {
|
||||
"Qto_FanBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFilter": {
|
||||
"Qto_FilterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFireSuppressionTerminal": {
|
||||
"Qto_FireSuppressionTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowInstrument": {
|
||||
"Qto_FlowInstrumentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowMeter": {
|
||||
"Qto_FlowMeterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFooting": {
|
||||
"Qto_FootingBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Height": "get_height",
|
||||
"Length": "get_length",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcHeatExchanger": {
|
||||
"Qto_HeatExchangerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcHumidifier": {
|
||||
"Qto_HumidifierBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcInterceptor": {
|
||||
"Qto_InterceptorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcJunctionBox": {
|
||||
"Qto_JunctionBoxBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfGangs": null
|
||||
}
|
||||
},
|
||||
"IfcLaborResource": {
|
||||
"Qto_LaborResourceBaseQuantities": {
|
||||
"OvertimeWork": null,
|
||||
"StandardWork": null
|
||||
}
|
||||
},
|
||||
"IfcLamp": {
|
||||
"Qto_LampBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcLightFixture": {
|
||||
"Qto_LightFixtureBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcMember": {
|
||||
"Qto_MemberBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcMotorConnection": {
|
||||
"Qto_MotorConnectionBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcOpeningElement": {
|
||||
"Qto_OpeningElementBaseQuantities": {
|
||||
"Area": "get_opening_mapping_area",
|
||||
"Depth": "get_opening_depth",
|
||||
"Height": "get_opening_height",
|
||||
"Volume": "get_net_volume",
|
||||
"Width": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcOutlet": {
|
||||
"Qto_OutletBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPile": {
|
||||
"Qto_PileBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcPipeFitting": {
|
||||
"Qto_PipeFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeSegment": {
|
||||
"Qto_PipeSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": "get_cross_section_area",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcPlate": {
|
||||
"Qto_PlateBaseQuantities": {
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"NetArea": "get_net_footprint_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Perimeter": "get_gross_perimeter",
|
||||
"Width": "get_height"
|
||||
}
|
||||
},
|
||||
"IfcProjectionElement": {
|
||||
"Qto_ProjectionElementBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Volume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDevice": {
|
||||
"Qto_ProtectiveDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDeviceTrippingUnit": {
|
||||
"Qto_ProtectiveDeviceTrippingUnitBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPump": {
|
||||
"Qto_PumpBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcRailing": {
|
||||
"Qto_RailingBaseQuantities": {
|
||||
"Length": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcRampFlight": {
|
||||
"Qto_RampFlightBaseQuantities": {
|
||||
"GrossArea": "get_gross_stair_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"Length": "get_stair_length",
|
||||
"NetArea": "get_net_stair_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcReinforcingElement": {
|
||||
"Qto_ReinforcingElementBaseQuantities": {
|
||||
"Count": null,
|
||||
"Length": "get_length",
|
||||
"Weight": null
|
||||
}
|
||||
},
|
||||
"IfcRoof": {
|
||||
"Qto_RoofBaseQuantities": {
|
||||
"GrossArea": "get_gross_top_area",
|
||||
"NetArea": "get_net_top_area",
|
||||
"ProjectedArea": null
|
||||
}
|
||||
},
|
||||
"IfcSanitaryTerminal": {
|
||||
"Qto_SanitaryTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSensor": {
|
||||
"Qto_SensorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSite": {
|
||||
"Qto_SiteBaseQuantities": {
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossPerimeter": "get_gross_perimeter"
|
||||
}
|
||||
},
|
||||
"IfcSlab": {
|
||||
"Qto_SlabBaseQuantities": {
|
||||
"Depth": "get_height",
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetArea": "get_net_footprint_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Perimeter": "get_gross_perimeter",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcSolarDevice": {
|
||||
"Qto_SolarDeviceBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSpace": {
|
||||
"Qto_SpaceBaseQuantities": {
|
||||
"FinishCeilingHeight": "get_finish_ceiling_height",
|
||||
"FinishFloorHeight": "get_finish_floor_height",
|
||||
"GrossCeilingArea": "get_gross_ceiling_area",
|
||||
"GrossFloorArea": "get_gross_footprint_area",
|
||||
"GrossPerimeter": "get_gross_perimeter",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWallArea": null,
|
||||
"Height": "get_height",
|
||||
"NetCeilingArea": "get_net_ceiling_area",
|
||||
"NetFloorArea": "get_net_floor_area",
|
||||
"NetPerimeter": null,
|
||||
"NetVolume": "get_space_net_volume",
|
||||
"NetWallArea": null
|
||||
}
|
||||
},
|
||||
"IfcSpaceHeater": {
|
||||
"Qto_SpaceHeaterBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStackTerminal": {
|
||||
"Qto_StackTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStairFlight": {
|
||||
"Qto_StairFlightBaseQuantities": {
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"Length": "get_stair_length",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcSwitchingDevice": {
|
||||
"Qto_SwitchingDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTank": {
|
||||
"Qto_TankBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcTransformer": {
|
||||
"Qto_TransformerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTubeBundle": {
|
||||
"Qto_TubeBundleBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryControlElement": {
|
||||
"Qto_UnitaryControlElementBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryEquipment": {
|
||||
"Qto_UnitaryEquipmentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcValve": {
|
||||
"Qto_ValveBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcVibrationIsolator": {
|
||||
"Qto_VibrationIsolatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"GrossFootprintArea": "get_gross_footprint_area",
|
||||
"GrossSideArea": "get_gross_side_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Height": "get_height",
|
||||
"Length": "get_length",
|
||||
"NetFootprintArea": "get_net_footprint_area",
|
||||
"NetSideArea": "get_net_side_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcWasteTerminal": {
|
||||
"Qto_WasteTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWindow": {
|
||||
"Qto_WindowBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Height": "get_height",
|
||||
"Perimeter": "get_rectangular_perimeter",
|
||||
"Width": "get_length"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class Csv2Ifc:
|
||||
"cost.add_cost_item_quantity", self.file, cost_item=cost_item["ifc"], ifc_class=quantity_class
|
||||
)
|
||||
# 3 IfcPhysicalSimpleQuantity Value
|
||||
quantity[3] = cost_item["Quantity"]
|
||||
quantity[3] = int(cost_item["Quantity"]) if quantity_class == "IfcQuantityCount" else cost_item["Quantity"]
|
||||
if prop_name:
|
||||
quantity.Name = prop_name
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.date
|
||||
from typing import Union, Optional
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
class IfcDataGetter:
|
||||
@@ -41,17 +41,17 @@ class IfcDataGetter:
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def canonicalise_time(time):
|
||||
def canonicalise_time(time: Union[datetime.datetime, None]) -> str:
|
||||
if not time:
|
||||
return "-"
|
||||
return time.strftime("%d/%m/%y")
|
||||
|
||||
@staticmethod
|
||||
def get_root_costs(cost_schedule):
|
||||
def get_root_costs(cost_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return [obj for rel in cost_schedule.Controls or [] for obj in rel.RelatedObjects or []]
|
||||
|
||||
@staticmethod
|
||||
def get_cost_item_values(cost_item=None):
|
||||
def get_cost_item_values(cost_item: Union[ifcopenshell.entity_instance, None]) -> Union[list[dict[str, Any]], None]:
|
||||
if not cost_item:
|
||||
return None
|
||||
values = []
|
||||
@@ -71,14 +71,14 @@ class IfcDataGetter:
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def process_categories(cost_item, categories):
|
||||
def process_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]:
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
if cost_value.Category:
|
||||
categories.add("{}{}".format(cost_value.Category, " Cost"))
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def process_cost_item_categories(cost_item, categories):
|
||||
def process_cost_item_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]:
|
||||
IfcDataGetter.process_categories(cost_item, categories)
|
||||
for rel in cost_item.IsNestedBy or []:
|
||||
for child in rel.RelatedObjects or []:
|
||||
@@ -86,14 +86,20 @@ class IfcDataGetter:
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def get_cost_rates_categories(schedule):
|
||||
def get_cost_rates_categories(schedule: ifcopenshell.entity_instance) -> set[str]:
|
||||
categories = set()
|
||||
for cost_item in IfcDataGetter.get_root_costs(schedule):
|
||||
IfcDataGetter.process_cost_item_categories(cost_item, categories)
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def process_cost_data(file, cost_item, cost_items_data, index, hierarchy="1"):
|
||||
def process_cost_data(
|
||||
file: ifcopenshell.file,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
cost_items_data: list[dict[str, Any]],
|
||||
index: int,
|
||||
hierarchy: str = "1",
|
||||
) -> None:
|
||||
def listToString(s):
|
||||
return ", ".join([str(i) for i in s])
|
||||
|
||||
@@ -134,7 +140,7 @@ class IfcDataGetter:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_cost_items_data(file, schedule):
|
||||
def get_cost_items_data(file: ifcopenshell.file, schedule: ifcopenshell.entity_instance) -> list[dict[str, Any]]:
|
||||
cost_items_data = []
|
||||
index = 0
|
||||
for cost_item in IfcDataGetter.get_root_costs(schedule):
|
||||
@@ -142,7 +148,7 @@ class IfcDataGetter:
|
||||
return cost_items_data
|
||||
|
||||
@staticmethod
|
||||
def format_unit(unit):
|
||||
def format_unit(unit: ifcopenshell.entity_instance) -> str:
|
||||
if unit.is_a("IfcContextDependentUnit"):
|
||||
return f"{unit.UnitType} / {unit.Name}"
|
||||
else:
|
||||
@@ -152,7 +158,7 @@ class IfcDataGetter:
|
||||
return f"{unit.UnitType} / {name}"
|
||||
|
||||
@staticmethod
|
||||
def get_cost_value_unit(cost_value=None):
|
||||
def get_cost_value_unit(cost_value: Optional[ifcopenshell.entity_instance] = None) -> Union[str, None]:
|
||||
if not cost_value:
|
||||
return None
|
||||
unit = cost_value.UnitBasis
|
||||
@@ -161,9 +167,9 @@ class IfcDataGetter:
|
||||
return IfcDataGetter.format_unit(unit.UnitComponent)
|
||||
|
||||
@staticmethod
|
||||
def get_cost_item_quantity(file, cost_item=None):
|
||||
def get_cost_item_quantity(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
# TODO: handle multiple quantities, THOSE WHHICH ARE JUYST ASSIGNED TO THE COST ITEM DIRECTLY, NOT THROUGH OBJECTS.
|
||||
def add_quantity(quantity, take_off_name):
|
||||
def add_quantity(quantity: ifcopenshell.entity_instance, take_off_name: str) -> float:
|
||||
accounted_for.append(quantity)
|
||||
if take_off_name == "":
|
||||
take_off_name = quantity[0]
|
||||
@@ -171,26 +177,25 @@ class IfcDataGetter:
|
||||
take_off_name = "mixed-takeoff-quantities"
|
||||
return quantity[3]
|
||||
|
||||
if not cost_item:
|
||||
return None
|
||||
take_off_name = ""
|
||||
has_changed_name = False
|
||||
total_cost_quantity = 0
|
||||
accounted_for = []
|
||||
for rel in cost_item.Controls or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
qtos = ifcopenshell.util.element.get_psets(related_object, qtos_only=True)
|
||||
for quantities in qtos.values() or []:
|
||||
qto = file.by_id(quantities["id"])
|
||||
for quantity in qto.Quantities:
|
||||
if not quantity in cost_item.CostQuantities:
|
||||
continue
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
accounted_for.append(quantity)
|
||||
cost_item_quantities = cost_item.CostQuantities
|
||||
if cost_item_quantities:
|
||||
for rel in cost_item.Controls or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
qtos = ifcopenshell.util.element.get_psets(related_object, qtos_only=True)
|
||||
for quantities in qtos.values() or []:
|
||||
qto = file.by_id(quantities["id"])
|
||||
for quantity in qto.Quantities:
|
||||
if quantity not in cost_item_quantities:
|
||||
continue
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
accounted_for.append(quantity)
|
||||
|
||||
for quantity in cost_item.CostQuantities or []:
|
||||
if not quantity in accounted_for:
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
for quantity in cost_item_quantities:
|
||||
if not quantity in accounted_for:
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
|
||||
return {
|
||||
"id": cost_item.id(),
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# Ifc5D - IFC costing utility
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Ifc5D.
|
||||
#
|
||||
# Ifc5D is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ifc5D is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Ifc5D. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.selector
|
||||
import multiprocessing
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
Function = namedtuple("Function", ["measure", "name", "description"])
|
||||
rules = {}
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
for name in ("IFC4QtoBaseQuantities", "IFC4QtoBaseQuantitiesBlender"):
|
||||
with open(os.path.join(cwd, name + ".json"), "r") as f:
|
||||
rules[name] = json.load(f)
|
||||
|
||||
|
||||
def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], rules: dict) -> dict:
|
||||
results = {}
|
||||
for calculator, queries in rules["calculators"].items():
|
||||
calculator = calculators[calculator]
|
||||
for query, qtos in queries.items():
|
||||
filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements)
|
||||
if filtered_elements:
|
||||
calculator.calculate(ifc_file, filtered_elements, qtos, results)
|
||||
return results
|
||||
|
||||
|
||||
def edit_qtos(ifc_file, results) -> None:
|
||||
for element, qtos in results.items():
|
||||
for name, quantities in qtos.items():
|
||||
qto = ifcopenshell.util.element.get_pset(element, name, should_inherit=False)
|
||||
if qto:
|
||||
qto = ifc_file.by_id(qto["id"])
|
||||
else:
|
||||
qto = ifcopenshell.api.pset.add_qto(ifc_file, element, name)
|
||||
ifcopenshell.api.pset.edit_qto(ifc_file, qto=qto, properties=quantities)
|
||||
|
||||
|
||||
class SI2ProjectUnitConverter:
|
||||
def __init__(self, ifc_file):
|
||||
self.project_units = {
|
||||
"IfcAreaMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "AREAUNIT"),
|
||||
"IfcLengthMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "LENGTHUNIT"),
|
||||
"IfcMassMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "MASSUNIT"),
|
||||
"IfcTimeMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "TIMEUNIT"),
|
||||
"IfcVolumeMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "VOLUMEUNIT"),
|
||||
}
|
||||
for key, value in self.project_units.items():
|
||||
if value:
|
||||
self.project_units[key] = (getattr(value, "Prefix", "None"), value.Name)
|
||||
|
||||
self.si_names = {
|
||||
"IfcAreaMeasure": "SQUARE_METRE",
|
||||
"IfcLengthMeasure": "METRE",
|
||||
"IfcMassMeasure": "GRAM",
|
||||
"IfcTimeMeasure": "SECOND",
|
||||
"IfcVolumeMeasure": "CUBIC_METRE",
|
||||
}
|
||||
|
||||
def convert(self, value, measure):
|
||||
if measure_unit := self.project_units.get(measure, None):
|
||||
return ifcopenshell.util.unit.convert(value, None, self.si_names[measure], *measure_unit)
|
||||
return value
|
||||
|
||||
|
||||
class IfcOpenShell:
|
||||
"""Calculates Model body context geometry using the default IfcOpenShell
|
||||
iterator on triangulation elements."""
|
||||
|
||||
raw_functions = {
|
||||
# IfcLengthMeasure
|
||||
"get_x": Function("IfcLengthMeasure", "X", "Calculates the length along the local X axis"),
|
||||
"get_y": Function("IfcLengthMeasure", "Y", "Calculates the length along the local Y axis"),
|
||||
"get_z": Function("IfcLengthMeasure", "Z", "Calculates the length along the local Z axis"),
|
||||
"get_max_xyz": Function("IfcLengthMeasure", "Max XYZ", "The maximum X, Y, or Z local dimension"),
|
||||
"get_min_xyz": Function("IfcLengthMeasure", "Min XYZ", "The minimum X, Y, or Z local dimension"),
|
||||
"get_top_elevation": Function("IfcLengthMeasure", "Top elevation", "The local maximum Z ordinate"),
|
||||
"get_bottom_elevation": Function("IfcLengthMeasure", "Bottom Elevation", "The local minimum Z ordinate"),
|
||||
"get_footprint_perimeter": Function(
|
||||
"IfcLengthMeasure",
|
||||
"Footprint Perimeter",
|
||||
"The perimeter if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
# IfcAreaMeasure
|
||||
"get_area": Function("IfcAreaMeasure", "Area", "The total surface area of the element"),
|
||||
"get_footprint_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Footprint Area",
|
||||
"The area if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
"get_max_side_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Max Side Area",
|
||||
"The maximum side area when seen from either X, Y, or Z directions",
|
||||
),
|
||||
"get_outer_surface_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Outer Surface Area",
|
||||
"The total surface area except for the top or bottom, such as the ends of columns or beams",
|
||||
),
|
||||
"get_side_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Side area",
|
||||
"The side (non-projected) are of the shape as seen from the local Y-axis",
|
||||
),
|
||||
# IfcVolumeMeasure
|
||||
"get_volume": Function("IfcVolumeMeasure", "Volume", "Calculates the volume of a manifold shape"),
|
||||
}
|
||||
|
||||
functions = {}
|
||||
for k, v in raw_functions.items():
|
||||
functions[f"gross_{k}"] = Function(v.measure, f"Gross {v.name}", v.description)
|
||||
functions[f"net_{k}"] = Function(v.measure, f"Net {v.name}", v.description)
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict,
|
||||
results: dict,
|
||||
) -> None:
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.shape
|
||||
|
||||
formula_functions = {}
|
||||
|
||||
gross_settings = ifcopenshell.geom.settings()
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
net_settings = ifcopenshell.geom.settings()
|
||||
|
||||
gross_qtos = {}
|
||||
net_qtos = {}
|
||||
|
||||
for name, quantities in qtos.items():
|
||||
for quantity, formula in quantities.items():
|
||||
if not formula:
|
||||
continue
|
||||
if formula.startswith("gross_"):
|
||||
formula = formula[6:]
|
||||
gross_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
elif formula.startswith("net_"):
|
||||
formula = formula[4:]
|
||||
net_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
|
||||
tasks = []
|
||||
|
||||
if gross_qtos:
|
||||
tasks.append((IfcOpenShell.create_iterator(ifc_file, gross_settings, list(elements)), gross_qtos))
|
||||
|
||||
if net_qtos:
|
||||
tasks.append((IfcOpenShell.create_iterator(ifc_file, net_settings, list(elements)), net_qtos))
|
||||
|
||||
unit_converter = SI2ProjectUnitConverter(ifc_file)
|
||||
|
||||
for iterator, qtos in tasks:
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
shape = iterator.get()
|
||||
element = ifc_file.by_id(shape.id)
|
||||
results.setdefault(element, {})
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
for quantity, formula in quantities.items():
|
||||
results[element][name][quantity] = unit_converter.convert(
|
||||
formula_functions[formula](shape.geometry), IfcOpenShell.raw_functions[formula].measure
|
||||
)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
@staticmethod
|
||||
def create_iterator(
|
||||
ifc_file: ifcopenshell.file, settings: ifcopenshell.geom.settings, elements: list[ifcopenshell.entity_instance]
|
||||
) -> ifcopenshell.geom.iterator:
|
||||
return ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
|
||||
|
||||
class Blender:
|
||||
"""Calculates geometry based on currently loaded Blender objects."""
|
||||
|
||||
functions = {
|
||||
# IfcLengthMeasure
|
||||
"get_covering_width": Function("IfcLengthMeasure", "Covering Width", ""),
|
||||
"get_finish_ceiling_height": Function("IfcLengthMeasure", "Finish Ceiling Height", ""),
|
||||
"get_finish_floor_height": Function("IfcLengthMeasure", "Finish Floor Height", ""),
|
||||
"get_gross_perimeter": Function("IfcLengthMeasure", "Gross Perimeter", ""),
|
||||
"get_height": Function("IfcLengthMeasure", "Height", ""),
|
||||
"get_length": Function("IfcLengthMeasure", "Length", ""),
|
||||
"get_opening_depth": Function("IfcLengthMeasure", "Opening Depth", ""),
|
||||
"get_opening_height": Function("IfcLengthMeasure", "Opening Height", ""),
|
||||
"get_rectangular_perimeter": Function("IfcLengthMeasure", "Rectangular Perimeter", ""),
|
||||
"get_stair_length": Function("IfcLengthMeasure", "Stair Length", ""),
|
||||
"get_width": Function("IfcLengthMeasure", "Width", ""),
|
||||
# IfcAreaMeasure
|
||||
"get_covering_gross_area": Function("IfcAreaMeasure", "Covering Gross Area", ""),
|
||||
"get_covering_net_area": Function("IfcAreaMeasure", "Covering Net Area", ""),
|
||||
"get_cross_section_area": Function("IfcAreaMeasure", "Cross Section Area", ""),
|
||||
"get_gross_ceiling_area": Function("IfcAreaMeasure", "Gross Ceiling Area", ""),
|
||||
"get_gross_footprint_area": Function("IfcAreaMeasure", "Gross Footprint Area", ""),
|
||||
"get_gross_side_area": Function("IfcAreaMeasure", "Gross Side Area", ""),
|
||||
"get_gross_stair_area": Function("IfcAreaMeasure", "Gross Stair Area", ""),
|
||||
"get_gross_surface_area": Function("IfcAreaMeasure", "Gross Surface Area", ""),
|
||||
"get_gross_top_area": Function("IfcAreaMeasure", "Gross Top Area", ""),
|
||||
"get_net_ceiling_area": Function("IfcAreaMeasure", "Net Ceiling Area", ""),
|
||||
"get_net_floor_area": Function("IfcAreaMeasure", "Net Floor Area", ""),
|
||||
"get_net_footprint_area": Function("IfcAreaMeasure", "Net Footprint Area", ""),
|
||||
"get_net_side_area": Function("IfcAreaMeasure", "Net Side Area", ""),
|
||||
"get_net_stair_area": Function("IfcAreaMeasure", "Net Stair Area", ""),
|
||||
"get_net_surface_area": Function("IfcAreaMeasure", "Net Surface Area", ""),
|
||||
"get_net_top_area": Function("IfcAreaMeasure", "Net Top Area", ""),
|
||||
"get_opening_mapping_area": Function("IfcAreaMeasure", "Opening Mapping Area", ""),
|
||||
"get_outer_surface_area": Function("IfcAreaMeasure", "Outer Surface Area", ""),
|
||||
# IfcVolumeMeasure
|
||||
"get_gross_volume": Function("IfcVolumeMeasure", "Gross Volume", ""),
|
||||
"get_net_volume": Function("IfcVolumeMeasure", "Net Volume", ""),
|
||||
"get_space_net_volume": Function("IfcVolumeMeasure", "Space Net Volume", ""),
|
||||
# IfcMassMeasure
|
||||
"get_gross_weight": Function("IfcMassMeasure", "Gross Weight", ""),
|
||||
"get_net_weight": Function("IfcMassMeasure", "Net Weight", ""),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], qtos: dict, results: dict
|
||||
) -> None:
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.qto.calculator as calculator
|
||||
|
||||
unit_converter = SI2ProjectUnitConverter(ifc_file)
|
||||
formula_functions = {}
|
||||
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj or obj.type != "MESH":
|
||||
continue
|
||||
results.setdefault(element, {})
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
for quantity, formula in quantities.items():
|
||||
if not formula:
|
||||
continue
|
||||
if not (formula_function := formula_functions.get(formula)):
|
||||
formula_function = formula_functions[formula] = getattr(calculator, formula)
|
||||
results[element][name][quantity] = unit_converter.convert(
|
||||
formula_function(obj), Blender.functions[formula].measure
|
||||
)
|
||||
|
||||
|
||||
calculators = {"Blender": Blender, "IfcOpenShell": IfcOpenShell}
|
||||
@@ -1,35 +1,4 @@
|
||||
# IFCCityJSON
|
||||
Converter for CityJSON files and IFC. Currently only supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
## Dependencies
|
||||
- [IfcOpenShell](https://github.com/IfcOpenShell/IfcOpenShell) (also IfcOpenShell api is needed)
|
||||
- [CJIO](https://github.com/cityjson/cjio) (>=0.8, <1.0)
|
||||
|
||||
## Usage of IFCCityJSON
|
||||
An extended ifccityjson tutorial can be found on [the OSARCH wiki](https://wiki.osarch.org/index.php?title=Ifccityjson)
|
||||
Following command will execute a conversion from CityJSON to IFC
|
||||
|
||||
python ifccityjson.py [-i input file] [-o output file] [-n name of identification attribute]
|
||||
|
||||
The example file that could be used is example/3D_BAG_example.json
|
||||
|
||||
python ifccityjson.py -i example/3DBAG_example.json -o example/3DBAG_example.ifc -n identificatie
|
||||
|
||||
## Implemented geometries
|
||||
- [x] "MultiPoint"
|
||||
- [x] "MultiLineString"
|
||||
- [x] "MultiSurface"
|
||||
- [x] "CompositeSurface"
|
||||
- [x] "Solid": exterior shell
|
||||
- [ ] "Solid": interior shell
|
||||
- [x] "MultiSolid"
|
||||
- [x] "CompositeSolid"
|
||||
- [ ] "GeometryInstance"
|
||||
|
||||
## TODO
|
||||
- [x] CityJSON Attributes as IFC properties in 'CityJSON_attributes' pset
|
||||
- [x] Implement georeferencing
|
||||
- [x] Do not use template IFC for new IFC file, but make IFC file from scratch
|
||||
- [x] Create mapping to IFC for all CityJSON object types & semantic surfaces
|
||||
- [ ] Implement conversion of all CitYJSON geometries
|
||||
- [x] Implement conversion of all LODs instead of only the most detailed
|
||||
IfcCityJSON is a converter for CityJSON files and IFC. It currently only
|
||||
supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
|
||||
import argparse
|
||||
from cjio import cityjson
|
||||
from cityjson2ifc.cityjson2ifc import Cityjson2ifc
|
||||
from .cityjson2ifc import Cityjson2ifc
|
||||
|
||||
def cmdline():
|
||||
# Example:
|
||||
@@ -51,4 +51,4 @@ def cmdline():
|
||||
converter.convert(city_model)
|
||||
|
||||
if __name__ == '__main__':
|
||||
cmdline()
|
||||
cmdline()
|
||||
@@ -146,7 +146,7 @@ class Cityjson2ifc:
|
||||
# Georeferencing
|
||||
self.properties["local_translation"] = None
|
||||
self.properties["local_scale"] = None
|
||||
if not self.city_model.is_transformed:
|
||||
if self.city_model.is_transformed:
|
||||
self.properties["local_scale"] = self.city_model.transform["scale"]
|
||||
local_translation = self.city_model.transform["translate"]
|
||||
self.properties["local_translation"] = {
|
||||
|
||||
@@ -265,7 +265,7 @@ config = {
|
||||
"AreaUnit",
|
||||
"Phase",
|
||||
],
|
||||
"colours": "ppppreeeeesss",
|
||||
"colours": "ppprrreeeeesss",
|
||||
"sort": [{"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_facilities,
|
||||
"get_element_data": get_facility_data,
|
||||
@@ -283,7 +283,7 @@ config = {
|
||||
"ModelID",
|
||||
"Elevation",
|
||||
],
|
||||
"colours": "ppreeees",
|
||||
"colours": "prrreeees",
|
||||
"sort": [{"name": "Elevation", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_storeys,
|
||||
"get_element_data": get_storey_data,
|
||||
@@ -304,7 +304,7 @@ config = {
|
||||
"GrossFloorArea",
|
||||
"NetFloorArea",
|
||||
],
|
||||
"colours": "ppprreeess",
|
||||
"colours": "pprrrreeesss",
|
||||
"sort": [{"name": "StoreyName", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_spaces,
|
||||
"get_element_data": get_space_data,
|
||||
@@ -336,7 +336,7 @@ config = {
|
||||
"PointOfContact",
|
||||
"WarrantyPeriod",
|
||||
],
|
||||
"colours": "pppreeeeesssss",
|
||||
"colours": "pprrreeeeesssss",
|
||||
"sort": [{"name": "ModelObject", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_element_types,
|
||||
"get_element_data": get_element_type_data,
|
||||
@@ -379,7 +379,7 @@ config = {
|
||||
"ModelSoftware",
|
||||
"ModelID",
|
||||
],
|
||||
"colours": "pppreee",
|
||||
"colours": "pprrreee",
|
||||
"sort": [{"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_systems,
|
||||
"get_element_data": get_system_data,
|
||||
|
||||
@@ -89,7 +89,7 @@ endif
|
||||
mkdir -p dist/ifcopenshell
|
||||
cp -r ifcopenshell/* dist/ifcopenshell/
|
||||
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-f7c03db-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-c7830e9-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell/ifcopenshell_wrapper.py dist/ifcopenshell/
|
||||
ifeq ($(PLATFORM), win)
|
||||
|
||||
@@ -3,3 +3,62 @@ IfcCityJSON
|
||||
|
||||
IfcCityJSON is a converter for CityJSON files and IFC. It currently only
|
||||
supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
Source installation
|
||||
-------------------
|
||||
|
||||
1. :doc:`Install IfcOpenShell <ifcopenshell-python/installation>`
|
||||
2. `Clone the source code <https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/ifccityjson>`_.
|
||||
3. ``cd /path/to/IfcOpenShell/src/ifccityjson``
|
||||
4. ``pip install .``
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
An extended ifccityjson tutorial can be found on `the OSARCH wiki
|
||||
<https://wiki.osarch.org/index.php?title=Ifccityjson>`_. Here's the built-in
|
||||
documentation:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python -m ifccityjson -h
|
||||
|
||||
usage: __main__.py [-h] -i INPUT [-o OUTPUT] [-n NAME] [--split-lod] [--no-split-lod] [--lod LOD]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-i INPUT, --input INPUT
|
||||
input CityJSON file
|
||||
-o OUTPUT, --output OUTPUT
|
||||
output IFC file. Standard is output.ifc
|
||||
-n NAME, --name NAME Attribute containing the name
|
||||
--split-lod Split the file in multiple LoDs
|
||||
--no-split-lod Do not split the file in multiple LoDs
|
||||
--lod LOD extract LOD value (example: 1.2)
|
||||
|
||||
The example file that could be used is example/3D_BAG_example.json
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python ifccityjson.py -i example/geometries.json -o output.ifc -n identificatie
|
||||
|
||||
The following geometries are implemented:
|
||||
|
||||
- [x] "MultiPoint"
|
||||
- [x] "MultiLineString"
|
||||
- [x] "MultiSurface"
|
||||
- [x] "CompositeSurface"
|
||||
- [x] "Solid": exterior shell
|
||||
- [ ] "Solid": interior shell
|
||||
- [x] "MultiSolid"
|
||||
- [x] "CompositeSolid"
|
||||
- [ ] "GeometryInstance"
|
||||
|
||||
TODO:
|
||||
|
||||
- [x] CityJSON Attributes as IFC properties in 'CityJSON_attributes' pset
|
||||
- [x] Implement georeferencing
|
||||
- [x] Do not use template IFC for new IFC file, but make IFC file from scratch
|
||||
- [x] Create mapping to IFC for all CityJSON object types & semantic surfaces
|
||||
- [ ] Implement conversion of all CitYJSON geometries
|
||||
- [x] Implement conversion of all LODs instead of only the most detailed
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user