Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0a5c9c8f3 | |||
| 22e087c82a | |||
| 57396e1bb9 | |||
| 389076bff4 | |||
| bfcc067257 | |||
| f88ac5de62 | |||
| cdd456bede | |||
| 7041d17c16 | |||
| de842f7aba | |||
| 6107c39c1c | |||
| 9584687fcf | |||
| b231e9051b | |||
| 40f9875930 | |||
| 328773550d | |||
| 303544dc5c | |||
| 1c66f986cc | |||
| e801629f86 | |||
| 74bbd6d116 | |||
| e06794ef3d | |||
| cd7062c905 | |||
| 42d476cbb0 | |||
| cd0d3908d3 | |||
| cb70f97789 | |||
| 572ed0db0f | |||
| 43d78af767 | |||
| c7aa42bfd5 | |||
| 18d7193efc | |||
| 8da92557cc | |||
| 761b4c035f | |||
| ee0f7f1677 | |||
| dca2e58586 | |||
| 3810255f04 | |||
| d3ca647cd5 | |||
| 8b35a3aadd | |||
| 78314b1ec7 | |||
| ef88b30639 | |||
| 5e815e7832 | |||
| e7b153cbf6 | |||
| 9b855bed2d | |||
| 3143f8c9a5 | |||
| 8dd781aea2 | |||
| 0506475f26 | |||
| ac092fb6f8 | |||
| 9beddca2f7 | |||
| 6d9770542f | |||
| 544d6ee695 | |||
| b8963b3d6e | |||
| f2bd46d699 | |||
| 8ac8ad33b3 | |||
| 44bbd17d97 | |||
| 8f0e67a35b | |||
| d2d4112724 | |||
| 209eea6a18 | |||
| 85426b3fef | |||
| c602f0f233 | |||
| bb6312e174 | |||
| d6d70af5a0 | |||
| 8187c75183 | |||
| 01cfcebb09 | |||
| 75040b6ffb | |||
| 6c8858e51d | |||
| 6bb63817af | |||
| 1e299a0ddd | |||
| 3201c234fa | |||
| f5d27621e7 | |||
| 3e1eb9648f | |||
| 5f9474acb7 | |||
| f4e6e924b1 | |||
| bcfc97037c | |||
| 65d0abd072 | |||
| 776c68e2b0 | |||
| dab2d5ce04 | |||
| 29b33661d7 | |||
| 5dec636584 | |||
| d11b91f152 | |||
| b0fc98e384 | |||
| 56f09d2541 | |||
| 4484b77232 | |||
| f6c054066b | |||
| d5c70e51f2 | |||
| cab9e4d892 | |||
| 14cd8289ea | |||
| 4202c08487 | |||
| a70fb41652 | |||
| df437fd3e3 | |||
| 3102dbcd44 | |||
| 9d838db096 | |||
| 0c9d555693 | |||
| 4b637c7049 | |||
| fd3a873bbd | |||
| 5cea988911 | |||
| 70fcbfa990 | |||
| c541aa78e5 | |||
| b821e62391 | |||
| 41141b8fc9 | |||
| ae0789e4ec | |||
| 13d4a99358 | |||
| becb375ec2 | |||
| a777a558bd | |||
| de37aeb515 | |||
| 23be1a6939 | |||
| 8edced5a0c | |||
| 21937e88e8 | |||
| f7da83737a | |||
| 27663d80ba | |||
| 4ec5d4fc65 | |||
| 2b34ca9eb7 | |||
| 2bb9810072 | |||
| c7e87324de | |||
| ec62d8fbb9 | |||
| 7b5aae9487 | |||
| 12c94fafab | |||
| 09be170820 | |||
| 966af5a757 | |||
| 89557460c8 | |||
| 2bad6b2722 | |||
| e5cd30c6d0 | |||
| 6f2da2896e | |||
| 0387c778f4 | |||
| 76568ea67c | |||
| 8e2a1a1649 | |||
| cb5f57d68a | |||
| e7c6f28312 | |||
| 1820f72bc4 | |||
| 1631dc39f5 | |||
| d2f19e7c28 | |||
| 736703336c | |||
| 44df1f676d | |||
| f1ffb81c86 | |||
| 08ec3fc73f | |||
| f1d7cf1dfb | |||
| 1ee488b148 | |||
| ecb9827e7f | |||
| 88f26eaead | |||
| ce03742aab | |||
| 62540f0b00 | |||
| 406b8b4540 | |||
| ca24838a3f | |||
| ff7219b557 | |||
| 711acfe120 | |||
| f1acda85c9 | |||
| 228f62d218 | |||
| 6186a88568 | |||
| 074a8c287f | |||
| d4736710a5 | |||
| 0bc86631a5 | |||
| 9751b6af05 | |||
| 6d641aaac1 | |||
| 489a248e9a | |||
| 9c00872a57 | |||
| d77bc6bdaa | |||
| 911d703f57 | |||
| 09bf5b85aa | |||
| 41a694d0dc | |||
| 6232ee36f9 | |||
| 5b1cc2cab5 | |||
| f706adfb81 | |||
| b0358a5a7b | |||
| eb09cc8af8 | |||
| 7c61c7d200 | |||
| 92d28901ac | |||
| 32769184b0 | |||
| de7f1d9d46 | |||
| cbd0c8a410 | |||
| f4f40c4b16 | |||
| f9d70fd51d | |||
| bb43405299 | |||
| 00cb3cd0db | |||
| 823f988536 | |||
| d95977a784 | |||
| 4f9991fb13 | |||
| d8b2cc77cc | |||
| 077f9b701d | |||
| 36ade628cf | |||
| 0f59f3e35a | |||
| 31e7bad056 | |||
| eee693dfcb | |||
| 4a8e03862b | |||
| 04f7fc0bdb | |||
| fbe9bb1ee2 | |||
| ca59b3220b | |||
| 39c8c8db37 |
@@ -3,7 +3,7 @@ name: Publish-blenderbim-multiplatform
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/ifcblenderexport/**'
|
||||
- 'src/blenderbim/**'
|
||||
- '.github/workflows/ci-blenderbim-matrix.yml'
|
||||
|
||||
env:
|
||||
@@ -52,15 +52,15 @@ jobs:
|
||||
run: echo "::set-output name=date::$(date +'%y%m%d')"
|
||||
- name: Compile
|
||||
run: |
|
||||
cp -r src/ifcblenderexport src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
- name: Upload Zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcblenderexport_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
tag: "blenderbim-${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
body: "Package release blenderbim-${{steps.date.outputs.date}}"
|
||||
body: "Daily developer testing build blenderbim-${{steps.date.outputs.date}}"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Dependency and build folders created by the build scripts
|
||||
/_build-vs2017-x64/
|
||||
/_deps-vs2017-x64-installed/
|
||||
/_deps/
|
||||
/deps*/
|
||||
/build*/
|
||||
/install*/
|
||||
@@ -23,3 +26,5 @@ __pycache__
|
||||
*.mo
|
||||
# Vim
|
||||
*.swp
|
||||
|
||||
|
||||
|
||||
@@ -1,56 +1,60 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
cache: ccache
|
||||
os: linux
|
||||
dist: bionic
|
||||
sudo: required
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- libboost-date-time-dev
|
||||
- libboost-filesystem-dev
|
||||
- libboost-iostreams-dev
|
||||
- libboost-program-options-dev
|
||||
- libboost-regex-dev
|
||||
- libboost-system-dev
|
||||
- libboost-thread-dev
|
||||
- libocct-data-exchange-dev
|
||||
- libocct-foundation-dev
|
||||
- libocct-modeling-algorithms-dev
|
||||
- libocct-modeling-data-dev
|
||||
- libxml2-dev
|
||||
- nlohmann-json3-dev
|
||||
- opencollada-dev
|
||||
- python3-all-dev
|
||||
- swig
|
||||
|
||||
before_install:
|
||||
- "sudo add-apt-repository -y ppa:freecad-community/ppa"
|
||||
- sudo apt-get update -qq
|
||||
|
||||
|
||||
install:
|
||||
- |
|
||||
sudo apt-get install -y \
|
||||
libocct-data-exchange-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
|
||||
libboost-system-dev libboost-program-options-dev libboost-regex-dev libboost-thread-dev libboost-date-time-dev libboost-iostreams-dev libboost-filesystem-dev \
|
||||
build-essential cmake python2.7 libpython2.7-dev swig zlib1g liblzma5 opencollada-dev wget apt-transport-https
|
||||
- sudo mkdir -p /usr/include/json/nlohmann/
|
||||
- sudo wget https://github.com/nlohmann/json/releases/download/v3.6.1/json.hpp -O /usr/include/json/nlohmann/json.hpp
|
||||
# - sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
||||
# - sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
||||
# - sudo apt-get update -qq && sudo apt-get install -y dart
|
||||
# - export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
||||
# - git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
||||
before_script:
|
||||
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -z; fi
|
||||
|
||||
script:
|
||||
- pwd
|
||||
- cd cmake
|
||||
- mkdir build
|
||||
- cd build
|
||||
- mkdir build && cd build
|
||||
- |
|
||||
cmake \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so \
|
||||
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
|
||||
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
|
||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.60;/usr/lib/x86_64-linux-gnu/libicudata.so.60" \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include/json \
|
||||
..
|
||||
- sudo make -j2 install
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
../cmake
|
||||
|
||||
# TODO: Drop sudo
|
||||
- sudo make -j $(nproc) install
|
||||
|
||||
- cd ../../test
|
||||
- /usr/bin/python2.7 tests.py
|
||||
- cd ../test
|
||||
- /usr/bin/python tests.py
|
||||
|
||||
- cd input
|
||||
|
||||
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
|
||||
# - gltf_validator acad2010_walls.glb
|
||||
|
||||
- /usr/bin/python2.7 -c "from __future__ import print_function; from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
|
||||
- /usr/bin/python -c "from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
|
||||
|
||||
- |
|
||||
(for i in *.ifc; do \
|
||||
@@ -64,3 +68,6 @@ script:
|
||||
- echo Succeeded
|
||||
- grep 0$ statuses
|
||||
- grep 0$ statuses | wc -l
|
||||
|
||||
after_script:
|
||||
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -s; fi
|
||||
|
||||
@@ -19,8 +19,8 @@ Prerequisites
|
||||
Dependencies
|
||||
-------------
|
||||
* [Boost](http://www.boost.org/)
|
||||
* [Open Cascade](http://opencascade.org) - *optional*, but required for building IfcGeom
|
||||
([official](http://www.opencascade.org/getocc/download/loadocc/), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
|
||||
* [Open Cascade](https://dev.opencascade.org/) - *optional*, but required for building IfcGeom
|
||||
([official](https://dev.opencascade.org/release), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
|
||||
For converting IFC representation items into BRep solids and tesselated meshes
|
||||
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
|
||||
For IfcConvert to be able to write tessellated Collada (.dae) files
|
||||
@@ -101,7 +101,7 @@ Note: where `make -j` is written, add a number roughly equal to the amount of CP
|
||||
$ make -j
|
||||
$ sudo make install
|
||||
|
||||
**2c)** or obtain and compile OCCT from http://www.opencascade.org/getocc/download/loadocc/
|
||||
**2c)** or obtain and compile OCCT from https://dev.opencascade.org/release
|
||||
|
||||
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
|
||||
|
||||
|
||||
@@ -466,6 +466,10 @@ ElSE()
|
||||
else()
|
||||
add_definitions(-Wno-maybe-uninitialized)
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0))
|
||||
# OpenCascade spews a lot of deprecated-copy warnings
|
||||
add_definitions(-Wno-deprecated-copy)
|
||||
endif()
|
||||
# -fPIC is not relevant on Windows and creates pointless warnings
|
||||
if (UNIX)
|
||||
add_definitions(-fPIC)
|
||||
|
||||
@@ -50,9 +50,12 @@ class BcfXml:
|
||||
zip_file = zipfile.ZipFile(filepath)
|
||||
self.filepath = tempfile.mkdtemp()
|
||||
zip_file.extractall(self.filepath)
|
||||
data = self._read_xml("project.bcfp", "project.xsd")
|
||||
self.project.project_id = data["Project"]["@ProjectId"]
|
||||
self.project.name = data["Project"]["Name"]
|
||||
if os.path.isfile(os.path.join(self.filepath, "project.bcfp")):
|
||||
data = self._read_xml("project.bcfp", "project.xsd")
|
||||
self.project.extension_schema = data["ExtensionSchema"]
|
||||
if "Project" in data:
|
||||
self.project.project_id = data["Project"]["@ProjectId"]
|
||||
self.project.name = data["Project"].get("Name")
|
||||
return self.project
|
||||
|
||||
def edit_project(self):
|
||||
|
||||
@@ -2,6 +2,7 @@ class Project:
|
||||
def __init__(self):
|
||||
self.project_id = ""
|
||||
self.name = ""
|
||||
self.extension_schema = ""
|
||||
|
||||
|
||||
class BimSnippet:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
xmlschema
|
||||
@@ -52,16 +52,16 @@ endif
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
ifeq ($(PYVERSION), py37)
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-517b819-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-ff7219b-$(PLATFORM)64.zip
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-517b819-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-ff7219b-$(PLATFORM)64.zip
|
||||
endif
|
||||
cd dist/working && unzip ifcblender*
|
||||
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
# See bug #812
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcblenderexport/occ_utils.py
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/blenderbim/occ_utils.py
|
||||
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
|
||||
rm -rf dist/working
|
||||
|
||||
@@ -142,11 +142,16 @@ endif
|
||||
cd dist/working && unzip v0.6.0.zip
|
||||
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
|
||||
# Provides bcf functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCClash functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/* dist/blenderbim/libs/site/packages/
|
||||
# Provides BIMTester functionality
|
||||
cd dist/working && python -m venv env
|
||||
cd dist/working && . env/bin/activate && pip install pybabel
|
||||
cd dist/working && . env/bin/activate && pip install babel
|
||||
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.6.0/src/ifcbimtester/bimtester/locale/
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCOBie functionality
|
||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
||||
@@ -172,6 +177,20 @@ endif
|
||||
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 jsgantt-improved supports for web-based construction sequencing gantt charts
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCDiff
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/Moult/deepdiff/archive/master.zip
|
||||
@@ -249,6 +268,11 @@ endif
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
|
||||
cd dist/working && tar -xzvf behave*
|
||||
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
|
||||
# See bug #1294
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/patch/model_core.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/patch/runner_util.patch
|
||||
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/model_core.py < model_core.patch
|
||||
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by behave
|
||||
|
Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 399 KiB |
@@ -24,6 +24,8 @@ if bpy is not None:
|
||||
"aggregate": None,
|
||||
"geometry": None,
|
||||
"cobie": None,
|
||||
"resource": None,
|
||||
"cost": None,
|
||||
"sequence": None,
|
||||
"group": None,
|
||||
"structural": None,
|
||||
@@ -124,7 +126,6 @@ if bpy is not None:
|
||||
prop.BIMProperties,
|
||||
prop.DocProperties,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
prop.GlobalId,
|
||||
prop.RepresentationItem,
|
||||
@@ -173,7 +174,6 @@ if bpy is not None:
|
||||
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
|
||||
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
||||
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
|
||||
bpy.app.handlers.save_pre.append(handler.storeIdMap)
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
@@ -200,7 +200,6 @@ if bpy is not None:
|
||||
bpy.utils.unregister_class(cls)
|
||||
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
|
||||
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
|
||||
bpy.app.handlers.save_pre.remove(handler.storeIdMap)
|
||||
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
@@ -6,68 +6,71 @@ import numpy
|
||||
import pickle
|
||||
import multiprocessing
|
||||
|
||||
try:
|
||||
from OCC.Core import (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.Core.TopoDS import topods
|
||||
except ImportError:
|
||||
from OCC import (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.TopoDS import topods
|
||||
|
||||
def load_occ():
|
||||
# Don't import until we really need to, as a temporary step before we can purge OCC
|
||||
try:
|
||||
from OCC.Core import (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.Core.TopoDS import topods
|
||||
except ImportError:
|
||||
from OCC import (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.TopoDS import topods
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
@@ -79,6 +82,7 @@ this_file = os.path.join(cwd, "cut_ifc.py")
|
||||
|
||||
|
||||
def get_booleaned_edges(shape):
|
||||
load_occ()
|
||||
edges = []
|
||||
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
|
||||
while exp.More():
|
||||
@@ -88,6 +92,7 @@ def get_booleaned_edges(shape):
|
||||
|
||||
|
||||
def connect_edges_into_wires(unconnected_edges):
|
||||
load_occ()
|
||||
edges = TopTools.TopTools_HSequenceOfShape()
|
||||
edges_handle = TopTools.Handle_TopTools_HSequenceOfShape(edges)
|
||||
wires = TopTools.TopTools_HSequenceOfShape()
|
||||
@@ -101,6 +106,7 @@ def connect_edges_into_wires(unconnected_edges):
|
||||
|
||||
|
||||
def do_cut(process_data):
|
||||
load_occ()
|
||||
global_id, shape, section, trsf_data = process_data
|
||||
|
||||
axis = gp.gp_Ax2(
|
||||
@@ -320,6 +326,7 @@ class IfcCutter:
|
||||
return False
|
||||
|
||||
def create_section_box(self):
|
||||
load_occ()
|
||||
top_left_corner = gp.gp_Pnt(
|
||||
self.section_box["top_left_corner"][0],
|
||||
self.section_box["top_left_corner"][1],
|
||||
@@ -350,6 +357,7 @@ class IfcCutter:
|
||||
self.transformation.SetDisplacement(source, destination)
|
||||
|
||||
def get_bbox(self, shape):
|
||||
load_occ()
|
||||
bbox = Bnd.Bnd_Box()
|
||||
BRepBndLib.brepbndlib_Add(shape, bbox)
|
||||
return bbox
|
||||
@@ -361,6 +369,7 @@ class IfcCutter:
|
||||
return zpos, zmax
|
||||
|
||||
def get_booleaned_edges(self, shape):
|
||||
load_occ()
|
||||
edges = []
|
||||
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
|
||||
while exp.More():
|
||||
@@ -377,6 +386,7 @@ class IfcCutter:
|
||||
|
||||
def get_annotation(self):
|
||||
import mathutils
|
||||
load_occ()
|
||||
|
||||
self.annotation_objs = []
|
||||
settings_2d = ifcopenshell.geom.settings()
|
||||
@@ -0,0 +1,11 @@
|
||||
<link href="jsgantt.css" rel="stylesheet" type="text/css"/>
|
||||
<script src="jsgantt.js" type="text/javascript"></script>
|
||||
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
|
||||
<script type="text/javascript">
|
||||
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
|
||||
var json_data = `
|
||||
{{{json_data}}}
|
||||
`;
|
||||
JSGantt.parseJSONString(json_data, g);
|
||||
g.Draw();
|
||||
</script>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
@@ -25,7 +25,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
|
||||
id="rect1036"
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -25,7 +25,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
y="3.1753578e-05"
|
||||
x="-4.3300333e-05"
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -25,7 +25,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
y="3.1753578e-05"
|
||||
x="-4.3300333e-05"
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -7,6 +7,8 @@ import zipfile
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.api
|
||||
from ifcopenshell.api.spatial.data import Data as SpatialData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import addon_utils
|
||||
|
||||
@@ -19,7 +21,7 @@ class IfcExporter:
|
||||
self.file = IfcStore.get_file()
|
||||
self.set_header()
|
||||
if bpy.context.scene.BIMProjectProperties.is_authoring:
|
||||
self.sync_object_placements()
|
||||
self.sync_object_placements_and_deletions()
|
||||
self.sync_edited_objects()
|
||||
extension = self.ifc_export_settings.output_file.split(".")[-1]
|
||||
if extension == "ifczip":
|
||||
@@ -45,9 +47,6 @@ class IfcExporter:
|
||||
jsonData = ifcjson.IFC2JSON5a(self.file, self.ifc_export_settings.json_compact).spf2Json()
|
||||
with open(self.ifc_export_settings.output_file, "w") as outfile:
|
||||
json.dump(jsonData, outfile, indent=None if self.ifc_export_settings.json_compact else 4)
|
||||
if bpy.context.scene.BIMProjectProperties.is_authoring:
|
||||
if bpy.data.filepath:
|
||||
bpy.ops.wm.save_mainfile()
|
||||
|
||||
def set_header(self):
|
||||
# TODO: add all metadata, pending bug #747
|
||||
@@ -74,13 +73,25 @@ class IfcExporter:
|
||||
# else:
|
||||
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
|
||||
|
||||
def sync_object_placements(self):
|
||||
def sync_object_placements_and_deletions(self):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
for obj in bpy.data.objects:
|
||||
to_delete = []
|
||||
|
||||
for ifc_definition_id, obj in IfcStore.id_map.items():
|
||||
try:
|
||||
self.sync_object_placement(obj)
|
||||
except:
|
||||
pass
|
||||
self.sync_object_container(ifc_definition_id, obj)
|
||||
except ReferenceError:
|
||||
pass # The object is likely deleted
|
||||
if self.should_delete(obj):
|
||||
to_delete.append(ifc_definition_id)
|
||||
|
||||
SpatialData.purge()
|
||||
|
||||
for ifc_definition_id in to_delete:
|
||||
product = self.file.by_id(ifc_definition_id)
|
||||
IfcStore.unlink_element(product)
|
||||
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
|
||||
|
||||
def sync_edited_objects(self):
|
||||
for obj_name in IfcStore.edited_objs.copy():
|
||||
@@ -94,9 +105,10 @@ class IfcExporter:
|
||||
|
||||
def sync_object_placement(self, obj):
|
||||
blender_matrix = np.matrix(obj.matrix_world)
|
||||
ifc_matrix = ifcopenshell.util.placement.get_local_placement(
|
||||
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).ObjectPlacement
|
||||
)
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if not hasattr(element, "ObjectPlacement"):
|
||||
return
|
||||
ifc_matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
ifc_matrix[0][3] *= self.unit_scale
|
||||
ifc_matrix[1][3] *= self.unit_scale
|
||||
ifc_matrix[2][3] *= self.unit_scale
|
||||
@@ -114,6 +126,43 @@ class IfcExporter:
|
||||
if not np.allclose(ifc_matrix, blender_matrix, atol=0.0001):
|
||||
bpy.ops.bim.edit_object_placement(obj=obj.name)
|
||||
|
||||
def sync_object_container(self, guid, obj):
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element_collection = bpy.data.collections.get(obj.name)
|
||||
|
||||
if self.file.schema == "IFC2X3":
|
||||
if element.is_a("IfcProject"):
|
||||
return
|
||||
elif element.is_a("IfcContext"):
|
||||
return
|
||||
|
||||
if (element.is_a("IfcElement") and element_collection) or element.is_a("IfcSpatialStructureElement"):
|
||||
try:
|
||||
parent_collection = [c for c in bpy.data.collections if c.children.get(element_collection.name)][0]
|
||||
except:
|
||||
return # Out of the spatial tree
|
||||
else:
|
||||
parent_collection = obj.users_collection[0]
|
||||
|
||||
parent_obj = bpy.data.objects.get(parent_collection.name)
|
||||
if not parent_obj or not parent_obj.BIMObjectProperties.ifc_definition_id:
|
||||
return
|
||||
parent = self.file.by_id(parent_obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
if parent.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpatialStructureElement"):
|
||||
if parent != ifcopenshell.util.element.get_container(element):
|
||||
bpy.ops.bim.assign_container(relating_structure=parent.id(), related_element=obj.name)
|
||||
elif parent != ifcopenshell.util.element.get_aggregate(element):
|
||||
bpy.ops.bim.assign_object(relating_object=parent_obj.name, related_object=obj.name)
|
||||
|
||||
def should_delete(self, obj):
|
||||
try:
|
||||
# This will throw an exception if the Blender object no longer exists
|
||||
foo = obj.name
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
def get_application_name(self):
|
||||
return "BlenderBIM"
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import bpy
|
||||
import json
|
||||
import addon_utils
|
||||
import blenderbim.bim.decoration as decoration
|
||||
import ifcopenshell.api.owner.settings
|
||||
from bpy.app.handlers import persistent
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.attribute.data import Data as AttributeData
|
||||
from ifcopenshell.api.attribute.data import Data as AttributeData
|
||||
from ifcopenshell.api.type.data import Data as TypeData
|
||||
|
||||
|
||||
def mode_callback(obj, data):
|
||||
if (
|
||||
obj.mode != "OBJECT"
|
||||
or not obj.data
|
||||
or not isinstance(obj.data, bpy.types.Mesh)
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||
):
|
||||
return
|
||||
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
|
||||
IfcStore.edited_objs.add(obj.name)
|
||||
for obj in bpy.context.selected_objects:
|
||||
if (
|
||||
obj.mode != "EDIT"
|
||||
or not obj.data
|
||||
or not isinstance(obj.data, bpy.types.Mesh)
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||
):
|
||||
return
|
||||
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
|
||||
IfcStore.edited_objs.add(obj.name)
|
||||
|
||||
|
||||
def name_callback(obj, data):
|
||||
@@ -27,8 +31,13 @@ def name_callback(obj, data):
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if not element.is_a("IfcRoot"):
|
||||
return
|
||||
if element.is_a("IfcSpatialStructureElement") or (hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy):
|
||||
collection = obj.users_collection[0]
|
||||
collection.name = obj.name
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
TypeData.purge()
|
||||
element.Name = "/".join(obj.name.split("/")[1:])
|
||||
AttributeData.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
|
||||
def subscribe_to(object, data_path, callback):
|
||||
@@ -47,59 +56,122 @@ def subscribe_to(object, data_path, callback):
|
||||
)
|
||||
|
||||
|
||||
@persistent
|
||||
def loadIfcStore(scene):
|
||||
IfcStore.file = None
|
||||
IfcStore.schema = None
|
||||
props = bpy.context.scene.BIMProperties
|
||||
IfcStore.id_map = (
|
||||
{int(k): bpy.data.objects.get(v) for k, v in json.loads(props.id_map).items()} if props.id_map else {}
|
||||
)
|
||||
IfcStore.guid_map = (
|
||||
{k: bpy.data.objects.get(v) for k, v in json.loads(props.guid_map).items()} if props.id_map else {}
|
||||
)
|
||||
|
||||
# Purge data cache
|
||||
def purge_module_data():
|
||||
from blenderbim.bim import modules
|
||||
|
||||
for module in modules.values():
|
||||
if not module:
|
||||
continue
|
||||
for name, value in modules.items():
|
||||
try:
|
||||
getattr(getattr(module, "data"), "Data").purge()
|
||||
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
getattr(value, "prop").purge()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
@persistent
|
||||
def ensureIfcExported(scene):
|
||||
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
|
||||
# The invocation pops up a file select window.
|
||||
# This is non-blocking, therefore the Blend file is saved before we export.
|
||||
bpy.ops.export_ifc.bim("INVOKE_DEFAULT", should_force_resave=True)
|
||||
def loadIfcStore(scene):
|
||||
IfcStore.purge()
|
||||
ifc_file = IfcStore.get_file()
|
||||
IfcStore.get_schema()
|
||||
[
|
||||
IfcStore.link_element(ifc_file.by_id(o.BIMObjectProperties.ifc_definition_id), o)
|
||||
for o in bpy.data.objects
|
||||
if o.BIMObjectProperties.ifc_definition_id
|
||||
]
|
||||
purge_module_data()
|
||||
|
||||
|
||||
@persistent
|
||||
def storeIdMap(scene):
|
||||
try:
|
||||
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
|
||||
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
|
||||
except:
|
||||
# Regenerate maps. Is there a better solution for this? It seems fragile.
|
||||
file = IfcStore.get_file()
|
||||
IfcStore.id_map = {
|
||||
o.ifc_definition_id: o.name for o in bpy.data.objects if o.BIMObjectProperties.ifc_definition_id
|
||||
}
|
||||
IfcStore.guid_map = {
|
||||
file.by_id(i).GlobalId: n for i, n in IfcStore.id_map.items() if file.by_id(i).is_a("IfcRoot")
|
||||
}
|
||||
# Then attempt to store it again
|
||||
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
|
||||
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
|
||||
def ensureIfcExported(scene):
|
||||
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
|
||||
bpy.ops.export_ifc.bim("INVOKE_DEFAULT")
|
||||
|
||||
|
||||
def get_application(ifc):
|
||||
version = get_application_version()
|
||||
for element in ifc.by_type("IfcApplication"):
|
||||
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
|
||||
return element
|
||||
return ifc.create_entity(
|
||||
"IfcApplication",
|
||||
**{
|
||||
"ApplicationDeveloper": create_application_organisation(ifc),
|
||||
"Version": get_application_version(),
|
||||
"ApplicationFullName": "BlenderBIM Add-on",
|
||||
"ApplicationIdentifier": "BlenderBIM",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_application_version():
|
||||
return ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
addon.bl_info.get("version", (-1, -1, -1))
|
||||
for addon in addon_utils.modules()
|
||||
if addon.bl_info["name"] == "BlenderBIM"
|
||||
][0]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def create_application_organisation(ifc):
|
||||
return ifc.create_entity(
|
||||
"IfcOrganization",
|
||||
**{
|
||||
"Name": "IfcOpenShell",
|
||||
"Description": "IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.",
|
||||
"Roles": [ifc.create_entity("IfcActorRole", **{"Role": "USERDEFINED", "UserDefinedRole": "CONTRIBUTOR"})],
|
||||
"Addresses": [
|
||||
ifc.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "WEBPAGE",
|
||||
"Description": "The main webpage of the software collection.",
|
||||
"WWWHomePageURL": "https://ifcopenshell.org",
|
||||
},
|
||||
),
|
||||
ifc.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "WEBPAGE",
|
||||
"Description": "The BlenderBIM Add-on webpage of the software collection.",
|
||||
"WWWHomePageURL": "https://blenderbim.org",
|
||||
},
|
||||
),
|
||||
ifc.create_entity(
|
||||
"IfcTelecomAddress",
|
||||
**{
|
||||
"Purpose": "USERDEFINED",
|
||||
"UserDefinedPurpose": "REPOSITORY",
|
||||
"Description": "The source code repository of the software collection.",
|
||||
"WWWHomePageURL": "https://github.com/IfcOpenShell/IfcOpenShell.git",
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@persistent
|
||||
def setDefaultProperties(scene):
|
||||
ifcopenshell.api.owner.settings.get_person = (
|
||||
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_person))
|
||||
if bpy.context.scene.BIMOwnerProperties.user_person
|
||||
else None
|
||||
)
|
||||
ifcopenshell.api.owner.settings.get_organisation = (
|
||||
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_organisation))
|
||||
if bpy.context.scene.BIMOwnerProperties.user_organisation
|
||||
else None
|
||||
)
|
||||
ifcopenshell.api.owner.settings.get_application = get_application
|
||||
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||
drawing_style.name = "Technical"
|
||||
@@ -12,6 +12,21 @@ class IfcStore:
|
||||
edited_objs = set()
|
||||
pset_template_path = ""
|
||||
pset_template_file = None
|
||||
library_path = ""
|
||||
library_file = None
|
||||
|
||||
@staticmethod
|
||||
def purge():
|
||||
IfcStore.path = ""
|
||||
IfcStore.file = None
|
||||
IfcStore.schema = None
|
||||
IfcStore.id_map = {}
|
||||
IfcStore.guid_map = {}
|
||||
IfcStore.edited_objs = set()
|
||||
IfcStore.pset_template_path = ""
|
||||
IfcStore.pset_template_file = None
|
||||
IfcStore.library_path = ""
|
||||
IfcStore.library_file = None
|
||||
|
||||
@staticmethod
|
||||
def get_file():
|
||||
@@ -21,7 +21,6 @@ import numpy as np
|
||||
from pathlib import Path
|
||||
from itertools import cycle
|
||||
from datetime import datetime
|
||||
from blenderbim.bim.module.context.data import Data as ContextData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from . import schema
|
||||
|
||||
@@ -415,12 +414,16 @@ class IfcImporter:
|
||||
self.exclude_elements |= self.native_elements
|
||||
|
||||
def is_native(self, element):
|
||||
if not element.Representation or not element.Representation.Representations or element.HasOpenings:
|
||||
if (
|
||||
not element.Representation
|
||||
or not element.Representation.Representations
|
||||
or getattr(element, "HasOpenings", None)
|
||||
):
|
||||
return
|
||||
representations = self.get_transformed_body_representations(element.Representation.Representations)
|
||||
|
||||
# Single swept disk solids (e.g. rebar) are better natively represented as beveled curves
|
||||
if [r for r in representations if self.is_native_swept_disk_solid(r)]:
|
||||
if self.is_native_swept_disk_solid(representations):
|
||||
self.native_data[element.GlobalId] = {
|
||||
"representations": representations,
|
||||
"representation": self.get_body_representation(element.Representation.Representations),
|
||||
@@ -429,19 +432,26 @@ class IfcImporter:
|
||||
return True
|
||||
# FacetedBreps (without voids) are meshes. See #841.
|
||||
# Commented out as seems currently too slow.
|
||||
# if [r for r in representations if self.is_native_faceted_brep(r)]:
|
||||
# self.native_data[element.GlobalId] = {
|
||||
# "representations": representations,
|
||||
# "representation": self.get_body_representation(element.Representation.Representations),
|
||||
# "type": "IfcFacetedBrep",
|
||||
# }
|
||||
# return True
|
||||
# if self.is_native_faceted_brep(representations):
|
||||
# self.native_data[element.GlobalId] = {
|
||||
# "representations": representations,
|
||||
# "representation": self.get_body_representation(element.Representation.Representations),
|
||||
# "type": "IfcFacetedBrep",
|
||||
# }
|
||||
# return True
|
||||
|
||||
def is_native_swept_disk_solid(self, representation):
|
||||
return len(representation["raw"].Items) == 1 and representation["raw"].Items[0].is_a("IfcSweptDiskSolid")
|
||||
def is_native_swept_disk_solid(self, representations):
|
||||
for representation in representations:
|
||||
if len(representation["raw"].Items) > 1 or not representation["raw"].Items[0].is_a("IfcSweptDiskSolid"):
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_native_faceted_brep(self, representation):
|
||||
return bool([i for i in representation["raw"].Items if i.is_a() == "IfcFacetedBrep"])
|
||||
def is_native_faceted_brep(self, representations):
|
||||
for representation in representations:
|
||||
for i in representation["raw"].Items:
|
||||
if i.is_a() != "IfcFacetedBrep":
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_products_from_shape_representation(self, element):
|
||||
products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation]
|
||||
@@ -634,6 +644,7 @@ class IfcImporter:
|
||||
def create_native_products(self):
|
||||
total = 0
|
||||
checkpoint = time.time()
|
||||
bm = bmesh.new()
|
||||
for element in self.native_elements:
|
||||
total += 1
|
||||
if total % 250 == 0:
|
||||
@@ -641,6 +652,8 @@ class IfcImporter:
|
||||
checkpoint = time.time()
|
||||
native_data = self.native_data[element.GlobalId]
|
||||
representation = native_data["representation"]
|
||||
if not representation:
|
||||
continue
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
mesh_name = f"{context_id}/{representation.id()}"
|
||||
mesh = self.meshes.get(mesh_name)
|
||||
@@ -650,16 +663,17 @@ class IfcImporter:
|
||||
elif native_data["type"] == "IfcFacetedBrep":
|
||||
mesh = self.create_native_faceted_brep(element, mesh_name)
|
||||
mesh.BIMMeshProperties.ifc_definition_id = representation.id()
|
||||
mesh.name = mesh_name
|
||||
self.meshes[mesh_name] = mesh
|
||||
self.create_product(element, mesh=mesh)
|
||||
if native_data["type"] == "IfcFacetedBrep":
|
||||
# The current implementation doesn't reuse vertices, so we weld it after assigning materials.
|
||||
# This welding isn't true to the representation, but is easy and seems inexpensive.
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
bm.clear()
|
||||
bm.free()
|
||||
|
||||
print("Done creating geometry")
|
||||
|
||||
@@ -715,21 +729,27 @@ class IfcImporter:
|
||||
|
||||
def create_structural_point_connections(self):
|
||||
for product in self.file.by_type("IfcStructuralPointConnection"):
|
||||
# TODO: make this based off ifcopenshell. See #1409
|
||||
placement_matrix = ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement)
|
||||
vertex = None
|
||||
for subelement in self.file.traverse(product):
|
||||
context = None
|
||||
representation = None
|
||||
for subelement in self.file.traverse(product.Representation):
|
||||
if subelement.is_a("IfcVertex") and subelement.VertexGeometry.is_a("IfcCartesianPoint"):
|
||||
vertex = list(subelement.VertexGeometry.Coordinates)
|
||||
break
|
||||
if not vertex:
|
||||
elif subelement.is_a("IfcGeometricRepresentationContext"):
|
||||
context = subelement
|
||||
elif subelement.is_a("IfcTopologyRepresentation"):
|
||||
representation = subelement
|
||||
if not vertex or not context or not representation:
|
||||
continue # TODO implement non cartesian point vertexes
|
||||
placement_matrix[0, 3] += vertex[0] * self.unit_scale
|
||||
placement_matrix[1, 3] += vertex[1] * self.unit_scale
|
||||
placement_matrix[2, 3] += vertex[2] * self.unit_scale
|
||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), None)
|
||||
|
||||
mesh_name = f"{context.id()}/{representation.id()}"
|
||||
mesh = bpy.data.meshes.new(mesh_name)
|
||||
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
|
||||
|
||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
||||
obj.matrix_world = mathutils.Matrix(placement_matrix.tolist())
|
||||
obj.empty_display_type = "SPHERE"
|
||||
obj.empty_display_size = 0.1
|
||||
self.link_element(product, obj)
|
||||
|
||||
def create_curve_products(self, products):
|
||||
@@ -760,9 +780,6 @@ class IfcImporter:
|
||||
if element is None:
|
||||
return
|
||||
|
||||
if not self.ifc_import_settings.should_import_spaces and element.is_a("IfcSpace"):
|
||||
return
|
||||
|
||||
self.ifc_import_settings.logger.info("Creating object %s", element)
|
||||
|
||||
if mesh:
|
||||
@@ -843,7 +860,7 @@ class IfcImporter:
|
||||
points = outer_bound[0].copy()
|
||||
[points.extend(p) for p in inner_bounds]
|
||||
tessellated_polygons = mathutils.geometry.tessellate_polygon(outer_bound + inner_bounds)
|
||||
tessellated_faces = [[{"Coordinates": points[pi]} for pi in t] for t in tessellated_polygons]
|
||||
tessellated_faces = [({"Coordinates": points[pi]} for pi in t) for t in tessellated_polygons]
|
||||
else:
|
||||
tessellated_faces = [face["Bounds"][0]["Bound"]["Polygon"]]
|
||||
|
||||
@@ -855,7 +872,7 @@ class IfcImporter:
|
||||
for point in tessellated_face:
|
||||
co.extend(
|
||||
representation["matrix"]
|
||||
@ mathutils.Vector([c * self.unit_scale for c in point["Coordinates"]])
|
||||
@ mathutils.Vector((c * self.unit_scale for c in point["Coordinates"]))
|
||||
)
|
||||
total_verts += 1
|
||||
loop_count += 1
|
||||
@@ -983,6 +1000,7 @@ class IfcImporter:
|
||||
bpy.ops.mesh.tris_convert_to_quads(context_override)
|
||||
bpy.ops.mesh.normals_make_consistent(context_override)
|
||||
bpy.ops.object.editmode_toggle(context_override)
|
||||
IfcStore.edited_objs.clear()
|
||||
|
||||
def add_opening_relation(self, element, obj):
|
||||
if not element.is_a("IfcOpeningElement"):
|
||||
@@ -1064,8 +1082,6 @@ class IfcImporter:
|
||||
|
||||
def add_related_objects(self, parent, related_objects):
|
||||
for element in related_objects:
|
||||
if element.is_a("IfcSpace"):
|
||||
continue
|
||||
global_id = element.GlobalId
|
||||
collection = bpy.data.collections.new(self.get_name(element))
|
||||
self.spatial_structure_elements[global_id] = {"blender": collection}
|
||||
@@ -1109,8 +1125,6 @@ class IfcImporter:
|
||||
container = element.ContainedInStructure[0].RelatingStructure
|
||||
elif hasattr(element, "Decomposes") and element.Decomposes:
|
||||
container = element.Decomposes[0].RelatingObject
|
||||
if container.is_a("IfcSpace"):
|
||||
return self.get_aggregate_container(container)
|
||||
return container
|
||||
|
||||
def create_openings_collection(self):
|
||||
@@ -1159,9 +1173,7 @@ class IfcImporter:
|
||||
and element.ContainedInStructure[0].RelatingStructure
|
||||
):
|
||||
container = element.ContainedInStructure[0].RelatingStructure
|
||||
if container.is_a("IfcSpace"):
|
||||
return self.place_object_in_spatial_tree(container, obj)
|
||||
elif element.is_a("IfcGrid"):
|
||||
if element.is_a("IfcGrid"):
|
||||
grid_collection = bpy.data.collections.get(obj.name)
|
||||
if grid_collection: # Just in case we ran into invalid grids from Revit
|
||||
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
|
||||
@@ -1173,22 +1185,15 @@ class IfcImporter:
|
||||
if element.Decomposes[0].RelatingObject.is_a("IfcProject"):
|
||||
collection = self.project["blender"]
|
||||
elif element.Decomposes[0].RelatingObject.is_a("IfcSpatialStructureElement"):
|
||||
if element.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpace"):
|
||||
if element.is_a("IfcSpatialStructureElement"):
|
||||
global_id = element.GlobalId
|
||||
else:
|
||||
global_id = element.Decomposes[0].RelatingObject.GlobalId
|
||||
if global_id in self.spatial_structure_elements:
|
||||
if (
|
||||
element.is_a("IfcSpatialStructureElement")
|
||||
and not element.is_a("IfcSpace")
|
||||
and "blender_obj" in self.spatial_structure_elements[global_id]
|
||||
):
|
||||
bpy.data.objects.remove(self.spatial_structure_elements[global_id]["blender_obj"])
|
||||
collection = self.spatial_structure_elements[global_id]["blender"]
|
||||
# This may occur if we are nesting an IfcSpace (which is special
|
||||
# since it does not have a collection within an IfcSpace
|
||||
if not collection:
|
||||
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
|
||||
else:
|
||||
collection = self.aggregates[element.Decomposes[0].RelatingObject.GlobalId]["blender"]
|
||||
if collection:
|
||||
@@ -1412,7 +1417,6 @@ class IfcImportSettings:
|
||||
self.logger = None
|
||||
self.input_file = None
|
||||
self.diff_file = None
|
||||
self.should_import_spaces = False
|
||||
self.should_auto_set_workarounds = True
|
||||
self.should_use_cpu_multiprocessing = True
|
||||
self.should_merge_by_class = False
|
||||
@@ -1,7 +1,7 @@
|
||||
import bpy
|
||||
import blenderbim.bim.module.aggregate.assign_object as assign_object
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.aggregate.data import Data
|
||||
from ifcopenshell.api.aggregate.data import Data
|
||||
|
||||
|
||||
class AssignObject(bpy.types.Operator):
|
||||
@@ -12,36 +12,41 @@ class AssignObject(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
|
||||
props = related_object.BIMObjectProperties
|
||||
relating_object = bpy.data.objects.get(self.relating_object) if self.relating_object else props.relating_object
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
)
|
||||
relating_object = bpy.data.objects.get(self.relating_object)
|
||||
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
|
||||
return {"FINISHED"}
|
||||
product = self.file.by_id(props.ifc_definition_id)
|
||||
assign_object.Usecase(
|
||||
self.file,
|
||||
{
|
||||
"product": product,
|
||||
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
|
||||
},
|
||||
).execute()
|
||||
bpy.ops.bim.edit_object_placement(obj=related_object.name)
|
||||
Data.load(props.ifc_definition_id)
|
||||
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
|
||||
for related_object in related_objects:
|
||||
oprops = related_object.BIMObjectProperties
|
||||
product = self.file.by_id(oprops.ifc_definition_id)
|
||||
ifcopenshell.api.run(
|
||||
"aggregate.assign_object",
|
||||
self.file,
|
||||
**{
|
||||
"product": product,
|
||||
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
|
||||
},
|
||||
)
|
||||
bpy.ops.bim.edit_object_placement(obj=related_object.name)
|
||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
|
||||
|
||||
spatial_collection = bpy.data.collections.get(related_object.name)
|
||||
relating_collection = bpy.data.collections.get(relating_object.name)
|
||||
if spatial_collection:
|
||||
self.remove_collection(bpy.context.scene.collection, spatial_collection)
|
||||
for collection in bpy.data.collections:
|
||||
if collection == relating_collection:
|
||||
collection.children.link(spatial_collection)
|
||||
continue
|
||||
self.remove_collection(collection, spatial_collection)
|
||||
else:
|
||||
for collection in related_object.users_collection:
|
||||
collection.objects.unlink(related_object)
|
||||
relating_collection.objects.link(related_object)
|
||||
spatial_collection = bpy.data.collections.get(related_object.name)
|
||||
relating_collection = bpy.data.collections.get(relating_object.name)
|
||||
if spatial_collection:
|
||||
self.remove_collection(bpy.context.scene.collection, spatial_collection)
|
||||
for collection in bpy.data.collections:
|
||||
if collection == relating_collection:
|
||||
if not collection.children.get(spatial_collection.name):
|
||||
collection.children.link(spatial_collection)
|
||||
continue
|
||||
self.remove_collection(collection, spatial_collection)
|
||||
else:
|
||||
for collection in related_object.users_collection:
|
||||
collection.objects.unlink(related_object)
|
||||
relating_collection.objects.link(related_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
def remove_collection(self, parent, child):
|
||||
@@ -1,5 +1,5 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.aggregate.data import Data
|
||||
from ifcopenshell.api.aggregate.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class BIM_PT_aggregate(Panel):
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
if props.ifc_definition_id not in Data.products:
|
||||
Data.load(props.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), props.ifc_definition_id)
|
||||
if not Data.products[props.ifc_definition_id]:
|
||||
return False
|
||||
return True
|
||||
@@ -29,12 +29,13 @@ class BIM_PT_aggregate(Panel):
|
||||
if not props.ifc_definition_id:
|
||||
return
|
||||
if props.ifc_definition_id not in Data.products:
|
||||
Data.load(props.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), props.ifc_definition_id)
|
||||
|
||||
if props.is_editing_aggregate:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "relating_object", text="")
|
||||
row.operator("bim.assign_object", icon="CHECKMARK", text="")
|
||||
if props.relating_object:
|
||||
row.operator("bim.assign_object", icon="CHECKMARK", text="").relating_object = props.relating_object.name
|
||||
row.operator("bim.disable_editing_aggregate", icon="X", text="")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -1,9 +1,9 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.attribute.data import Data
|
||||
from ifcopenshell.api.attribute.data import Data
|
||||
|
||||
|
||||
class EnableEditingAttributes(bpy.types.Operator):
|
||||
@@ -33,7 +33,7 @@ class EnableEditingAttributes(bpy.types.Operator):
|
||||
elif attribute["type"] == "integer":
|
||||
new.int_value = attribute["value"] or 0
|
||||
elif attribute["type"] == "float":
|
||||
new.float_value = attribute["value"] or 0.
|
||||
new.float_value = attribute["value"] or 0.0
|
||||
elif attribute["type"] == "enum":
|
||||
new.enum_items = json.dumps(attribute["enum_items"])
|
||||
if attribute["value"]:
|
||||
@@ -95,17 +95,16 @@ class EditAttributes(bpy.types.Operator):
|
||||
elif attribute["type"] == "enum":
|
||||
attributes[attribute["name"]] = blender_attribute.enum_value
|
||||
product = self.file.by_id(oprops.ifc_definition_id)
|
||||
edit_attributes.Usecase(self.file, {
|
||||
"product": product,
|
||||
"attributes": attributes
|
||||
}).execute()
|
||||
ifcopenshell.api.run(
|
||||
"attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes}
|
||||
)
|
||||
if "Name" in attributes:
|
||||
new_name = "{}/{}".format(product.is_a(), product.Name or "Unnamed")
|
||||
collection = bpy.data.collections.get(obj.name)
|
||||
if collection:
|
||||
collection.name = new_name
|
||||
obj.name = new_name
|
||||
Data.load(oprops.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import bpy
|
||||
import blenderbim.bim.schema # refactor
|
||||
from blenderbim.bim.module.material.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -1,5 +1,6 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.attribute.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.attribute.data import Data
|
||||
|
||||
|
||||
def draw_ui(context, layout, obj_type):
|
||||
@@ -7,7 +8,7 @@ def draw_ui(context, layout, obj_type):
|
||||
oprops = obj.BIMObjectProperties
|
||||
props = obj.BIMAttributeProperties
|
||||
if oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(oprops.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||
|
||||
if props.is_editing_attributes:
|
||||
row = layout.row(align=True)
|
||||
@@ -17,6 +17,11 @@ from bpy.props import (
|
||||
bcfviewpoints_enum = None
|
||||
|
||||
|
||||
def purge():
|
||||
global bcfviewpoints_enum
|
||||
bcfviewpoints_enum = None
|
||||
|
||||
|
||||
def updateBcfReferenceLink(self, context):
|
||||
if bpy.context.scene.BCFProperties.is_loaded:
|
||||
bpy.ops.bim.edit_bcf_reference_links()
|
||||
@@ -18,6 +18,13 @@ scenarios_enum = []
|
||||
classes_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global scenarios_enum
|
||||
global classes_enum
|
||||
scenarios_enum = []
|
||||
classes_enum = []
|
||||
|
||||
|
||||
def getScenarios(self, context):
|
||||
global scenarios_enum
|
||||
if len(scenarios_enum) < 1:
|
||||
@@ -1,6 +1,5 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.owner.data import Data
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -1,13 +1,8 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.classification.add_classification as add_classification
|
||||
import blenderbim.bim.module.classification.remove_classification as remove_classification
|
||||
import blenderbim.bim.module.classification.edit_classification as edit_classification
|
||||
import blenderbim.bim.module.classification.add_reference as add_reference
|
||||
import blenderbim.bim.module.classification.remove_reference as remove_reference
|
||||
import blenderbim.bim.module.classification.edit_reference as edit_reference
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
from ifcopenshell.api.classification.data import Data
|
||||
from blenderbim.bim.module.classification.prop import getClassifications, getReferences
|
||||
|
||||
|
||||
@@ -34,10 +29,12 @@ class AddClassification(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
add_classification.Usecase(
|
||||
IfcStore.get_file(), {"classification": Data.library_file.by_id(int(props.available_classifications))}
|
||||
).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"classification.add_classification",
|
||||
IfcStore.get_file(),
|
||||
**{"classification": Data.library_file.by_id(int(props.available_classifications))},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -80,8 +77,12 @@ class RemoveClassification(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
remove_classification.Usecase(self.file, {"classification": self.file.by_id(self.classification)}).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"classification.remove_classification",
|
||||
self.file,
|
||||
**{"classification": self.file.by_id(self.classification)},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -100,10 +101,12 @@ class EditClassification(bpy.types.Operator):
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_classification.Usecase(
|
||||
self.file, {"classification": self.file.by_id(props.active_classification_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"classification.edit_classification",
|
||||
self.file,
|
||||
**{"classification": self.file.by_id(props.active_classification_id), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_classification()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -152,15 +155,16 @@ class RemoveClassificationReference(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
remove_reference.Usecase(
|
||||
ifcopenshell.api.run(
|
||||
"classification.remove_reference",
|
||||
self.file,
|
||||
{
|
||||
**{
|
||||
"reference": self.file.by_id(self.reference),
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
},
|
||||
).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load()
|
||||
)
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -179,10 +183,12 @@ class EditClassificationReference(bpy.types.Operator):
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_reference.Usecase(
|
||||
self.file, {"reference": self.file.by_id(props.active_reference_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"classification.edit_reference",
|
||||
self.file,
|
||||
**{"reference": self.file.by_id(props.active_reference_id), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_classification_reference()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -206,16 +212,17 @@ class AddClassificationReference(bpy.types.Operator):
|
||||
classification = self.file.by_id(classification_id)
|
||||
break
|
||||
|
||||
add_reference.Usecase(
|
||||
ifcopenshell.api.run(
|
||||
"classification.add_reference",
|
||||
self.file,
|
||||
{
|
||||
**{
|
||||
"reference": Data.library_file.by_id(self.reference),
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"classification": classification
|
||||
"classification": classification,
|
||||
},
|
||||
).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load()
|
||||
)
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
from ifcopenshell.api.classification.data import Data
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -16,6 +16,11 @@ from bpy.props import (
|
||||
classification_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global classification_enum
|
||||
classification_enum = []
|
||||
|
||||
|
||||
def getClassifications(self, context):
|
||||
global classification_enum
|
||||
if len(classification_enum) < 1:
|
||||
@@ -1,6 +1,6 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.classification.data import Data
|
||||
from ifcopenshell.api.classification.data import Data
|
||||
|
||||
|
||||
class BIM_PT_classifications(Panel):
|
||||
@@ -17,7 +17,7 @@ class BIM_PT_classifications(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
self.props = context.scene.BIMClassificationProperties
|
||||
|
||||
@@ -79,9 +79,9 @@ class BIM_PT_classification_references(Panel):
|
||||
self.props = obj.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
Data.load(IfcStore.get_file())
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.constraint.add_objective as add_objective
|
||||
import blenderbim.bim.module.constraint.edit_objective as edit_objective
|
||||
import blenderbim.bim.module.constraint.remove_constraint as remove_constraint
|
||||
import blenderbim.bim.module.constraint.assign_constraint as assign_constraint
|
||||
import blenderbim.bim.module.constraint.unassign_constraint as unassign_constraint
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.constraint.data import Data
|
||||
from ifcopenshell.api.constraint.data import Data
|
||||
|
||||
|
||||
class LoadObjectives(bpy.types.Operator):
|
||||
@@ -50,19 +47,18 @@ class EnableEditingConstraint(bpy.types.Operator):
|
||||
data = Data.objectives[self.constraint]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(props.is_editing).all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
new = props.constraint_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if "<string>" in data_type:
|
||||
new.data_type = data_type
|
||||
if data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
new.data_type = "string"
|
||||
elif "<enumeration" in data_type:
|
||||
new.enum_items = json.dumps(attribute.type_of_attribute().declared_type().enumeration_items())
|
||||
new.data_type = "enum"
|
||||
elif data_type == "enum":
|
||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
props.active_constraint_id = self.constraint
|
||||
@@ -83,8 +79,8 @@ class AddObjective(bpy.types.Operator):
|
||||
bl_label = "Add Objective"
|
||||
|
||||
def execute(self, context):
|
||||
result = add_objective.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_objectives()
|
||||
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
||||
return {"FINISHED"}
|
||||
@@ -105,10 +101,12 @@ class EditObjective(bpy.types.Operator):
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
self.file = IfcStore.get_file()
|
||||
edit_objective.Usecase(
|
||||
self.file, {"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
||||
).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"constraint.edit_objective",
|
||||
self.file,
|
||||
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_objectives()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -121,8 +119,10 @@ class RemoveConstraint(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
remove_constraint.Usecase(self.file, {"constraint": self.file.by_id(self.constraint)}).execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
if props.is_editing == "IfcObjective":
|
||||
bpy.ops.bim.load_objectives()
|
||||
return {"FINISHED"}
|
||||
@@ -163,11 +163,15 @@ class AssignConstraint(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
assign_constraint.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
ifcopenshell.api.run(
|
||||
"constraint.assign_constraint",
|
||||
self.file,
|
||||
**{
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint),
|
||||
}
|
||||
)
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -180,9 +184,13 @@ class UnassignConstraint(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
unassign_constraint.Usecase(self.file, {
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint)
|
||||
}).execute()
|
||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
||||
ifcopenshell.api.run(
|
||||
"constraint.unassign_constraint",
|
||||
self.file,
|
||||
**{
|
||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
"constraint": self.file.by_id(self.constraint),
|
||||
}
|
||||
)
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
@@ -1,6 +1,6 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.constraint.data import Data
|
||||
from ifcopenshell.api.constraint.data import Data
|
||||
|
||||
|
||||
class BIM_PT_constraints(Panel):
|
||||
@@ -17,7 +17,7 @@ class BIM_PT_constraints(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
self.props = context.scene.BIMConstraintProperties
|
||||
|
||||
@@ -73,9 +73,9 @@ class BIM_PT_object_constraints(Panel):
|
||||
self.props = obj.BIMObjectConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
Data.load(IfcStore.get_file())
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(self.oprops.ifc_definition_id)
|
||||
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import bpy
|
||||
import blenderbim.bim.module.context.add_context as add_context
|
||||
import blenderbim.bim.module.context.remove_context as remove_context
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.context.data import Data
|
||||
from ifcopenshell.api.context.data import Data
|
||||
|
||||
|
||||
class AddSubcontext(bpy.types.Operator):
|
||||
@@ -14,15 +13,16 @@ class AddSubcontext(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
add_context.Usecase(
|
||||
ifcopenshell.api.run(
|
||||
"context.add_context",
|
||||
self.file,
|
||||
{
|
||||
**{
|
||||
"context": self.context or bpy.context.scene.BIMProperties.available_contexts,
|
||||
"subcontext": self.subcontext or bpy.context.scene.BIMProperties.available_subcontexts,
|
||||
"target_view": self.target_view or bpy.context.scene.BIMProperties.available_target_views,
|
||||
},
|
||||
).execute()
|
||||
Data.load()
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ class RemoveSubcontext(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
usecase = remove_context.Usecase(self.file, {"context": self.file.by_id(self.ifc_definition_id)})
|
||||
usecase.execute()
|
||||
Data.load()
|
||||
ifcopenshell.api.run(
|
||||
"context.remove_context", self.file, **{"context": self.file.by_id(self.ifc_definition_id)}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
@@ -1,5 +1,5 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.context.data import Data
|
||||
from ifcopenshell.api.context.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class BIM_PT_context(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
props = context.scene.BIMProperties
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCostSchedule,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.EditCostSchedule,
|
||||
operator.EditCostItem,
|
||||
operator.EditCostItemQuantity,
|
||||
operator.EditCostItemValue,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EnableEditingCostItem,
|
||||
operator.EnableEditingCostItemQuantities,
|
||||
operator.EnableEditingCostItemQuantity,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostItemValue,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.AddCostItem,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignControl,
|
||||
operator.UnassignControl,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostItemValue,
|
||||
operator.RemoveCostItemValue,
|
||||
prop.CostItem,
|
||||
prop.BIMCostProperties,
|
||||
ui.BIM_PT_cost_schedules,
|
||||
ui.BIM_UL_cost_items,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMCostProperties = bpy.props.PointerProperty(type=prop.BIMCostProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMCostProperties
|
||||
@@ -0,0 +1,608 @@
|
||||
import os
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
|
||||
|
||||
class AddCostSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.add_cost_schedule"
|
||||
bl_label = "Add Cost Schedule"
|
||||
|
||||
def execute(self, context):
|
||||
ifcopenshell.api.run("cost.add_cost_schedule", IfcStore.get_file())
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditCostSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_cost_schedule"
|
||||
bl_label = "Edit Cost Schedule"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
attributes = {}
|
||||
for attribute in props.cost_schedule_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
if attribute.data_type == "string":
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
elif attribute.data_type == "enum":
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_schedule",
|
||||
self.file,
|
||||
**{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_cost_schedule()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveCostSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_cost_schedule"
|
||||
bl_label = "Remove Cost Schedule"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
ifcopenshell.api.run(
|
||||
"cost.remove_cost_schedule",
|
||||
IfcStore.get_file(),
|
||||
cost_schedule=IfcStore.get_file().by_id(self.cost_schedule),
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_schedule"
|
||||
bl_label = "Enable Editing Cost Schedule"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
self.props.active_cost_schedule_id = self.cost_schedule
|
||||
while len(self.props.cost_schedule_attributes) > 0:
|
||||
self.props.cost_schedule_attributes.remove(0)
|
||||
self.enable_editing_cost_schedule()
|
||||
self.props.is_editing = "COST_SCHEDULE"
|
||||
return {"FINISHED"}
|
||||
|
||||
def enable_editing_cost_schedule(self):
|
||||
data = Data.cost_schedules[self.cost_schedule]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcCostSchedule").all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
new = self.props.cost_schedule_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type
|
||||
if attribute.name() in ["SubmittedOn", "UpdateDate"]:
|
||||
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
|
||||
elif data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
elif data_type == "enum":
|
||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
|
||||
|
||||
class EnableEditingCostItems(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_items"
|
||||
bl_label = "Enable Editing Cost Items"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound:
|
||||
# lol
|
||||
# TODO: make pitch higher as costs rise
|
||||
try:
|
||||
import aud
|
||||
|
||||
device = aud.Device()
|
||||
# chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/
|
||||
sound = aud.Sound(os.path.join(context.scene.BIMProperties.data_dir, "chaching.mp3"))
|
||||
handle = device.play(sound)
|
||||
sound_buffered = aud.Sound.buffer(sound)
|
||||
handle_buffered = device.play(sound_buffered)
|
||||
handle.stop()
|
||||
handle_buffered.stop()
|
||||
except:
|
||||
pass # ah well
|
||||
self.props = context.scene.BIMCostProperties
|
||||
self.props.active_cost_schedule_id = self.cost_schedule
|
||||
while len(self.props.cost_items) > 0:
|
||||
self.props.cost_items.remove(0)
|
||||
|
||||
self.contracted_cost_items = json.loads(self.props.contracted_cost_items)
|
||||
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
|
||||
self.create_new_cost_item_li(related_object_id, 0)
|
||||
self.props.is_editing = "COST_ITEMS"
|
||||
return {"FINISHED"}
|
||||
|
||||
def create_new_cost_item_li(self, related_object_id, level_index):
|
||||
cost_item = Data.cost_items[related_object_id]
|
||||
new = self.props.cost_items.add()
|
||||
new.ifc_definition_id = related_object_id
|
||||
new.name = cost_item["Name"] or "Unnamed"
|
||||
new.is_expanded = related_object_id not in self.contracted_cost_items
|
||||
new.level_index = level_index
|
||||
if cost_item["IsNestedBy"]:
|
||||
new.has_children = True
|
||||
if new.is_expanded:
|
||||
for related_object_id in cost_item["IsNestedBy"]:
|
||||
self.create_new_cost_item_li(related_object_id, level_index + 1)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingCostSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_cost_schedule"
|
||||
bl_label = "Disable Editing Cost Schedule"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMCostProperties.active_cost_schedule_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddSummaryCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.add_summary_cost_item"
|
||||
bl_label = "Add Cost Item"
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_schedule": self.file.by_id(self.cost_schedule)})
|
||||
Data.load(self.file)
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=self.cost_schedule)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.add_cost_item"
|
||||
bl_label = "Add Cost Item"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_item": self.file.by_id(self.cost_item)})
|
||||
Data.load(self.file)
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExpandCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.expand_cost_item"
|
||||
bl_label = "Expand Cost Item"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
self.file = IfcStore.get_file()
|
||||
contracted_cost_items = json.loads(props.contracted_cost_items)
|
||||
contracted_cost_items.remove(self.cost_item)
|
||||
props.contracted_cost_items = json.dumps(contracted_cost_items)
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ContractCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.contract_cost_item"
|
||||
bl_label = "Contract Cost Item"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
self.file = IfcStore.get_file()
|
||||
contracted_cost_items = json.loads(props.contracted_cost_items)
|
||||
contracted_cost_items.append(self.cost_item)
|
||||
props.contracted_cost_items = json.dumps(contracted_cost_items)
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_cost_item"
|
||||
bl_label = "Remove Cost item"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.remove_cost_item",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
)
|
||||
contracted_cost_items = json.loads(props.contracted_cost_items)
|
||||
if props.active_cost_item_index in contracted_cost_items:
|
||||
contracted_cost_items.remove(props.active_cost_item_index)
|
||||
props.contracted_cost_items = json.dumps(contracted_cost_items)
|
||||
Data.load(self.file)
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_item"
|
||||
bl_label = "Enable Editing Cost Item"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
while len(props.cost_item_attributes) > 0:
|
||||
props.cost_item_attributes.remove(0)
|
||||
|
||||
data = Data.cost_items[self.cost_item]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcCostItem").all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity" or isinstance(data_type, tuple):
|
||||
continue
|
||||
new = props.cost_item_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type
|
||||
if data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
elif data_type == "enum":
|
||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
props.active_cost_item_id = self.cost_item
|
||||
props.cost_item_editing_type = "ATTRIBUTES"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_cost_item"
|
||||
bl_label = "Disable Editing Cost Item"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMCostProperties.active_cost_item_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditCostItem(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_cost_item"
|
||||
bl_label = "Edit Cost Item"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
attributes = {}
|
||||
for attribute in props.cost_item_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
if attribute.data_type == "string":
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
elif attribute.data_type == "boolean":
|
||||
attributes[attribute.name] = attribute.bool_value
|
||||
elif attribute.data_type == "integer":
|
||||
attributes[attribute.name] = attribute.int_value
|
||||
elif attribute.data_type == "enum":
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
**{"cost_item": self.file.by_id(props.active_cost_item_id), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_cost_item()
|
||||
bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignControl(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_control"
|
||||
bl_label = "Assign Control"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
)
|
||||
for related_object in related_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||
relating_control=self.file.by_id(self.cost_item),
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignControl(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_control"
|
||||
bl_label = "Unassign Control"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
)
|
||||
for related_object in related_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"control.unassign_control",
|
||||
self.file,
|
||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||
relating_control=self.file.by_id(self.cost_item),
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostItemQuantities(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_item_quantities"
|
||||
bl_label = "Enable Editing Cost Item Quantities"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
props.active_cost_item_id = self.cost_item
|
||||
props.cost_item_editing_type = "QUANTITIES"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostItemValues(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_item_values"
|
||||
bl_label = "Enable Editing Cost Item Values"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
props.active_cost_item_id = self.cost_item
|
||||
props.cost_item_editing_type = "VALUES"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.add_cost_item_quantity"
|
||||
bl_label = "Add Cost Item Quantity"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.props = context.scene.BIMCostProperties
|
||||
if self.props.quantity_types == "QTO":
|
||||
self.add_quantities_from_qto_filter()
|
||||
else:
|
||||
self.add_manual_quantity()
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
def add_quantities_from_qto_filter(self):
|
||||
ifcopenshell.api.run(
|
||||
"cost.assign_cost_item_product_quantities",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
qto_name=self.props.qto_name,
|
||||
prop_name=self.props.prop_name
|
||||
)
|
||||
|
||||
def add_manual_quantity(self):
|
||||
ifcopenshell.api.run(
|
||||
"cost.add_cost_item_quantity",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
ifc_class=self.ifc_class,
|
||||
)
|
||||
|
||||
|
||||
class RemoveCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_cost_item_quantity"
|
||||
bl_label = "Add Cost Item Quantity"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
physical_quantity: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.remove_cost_item_quantity",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
physical_quantity=self.file.by_id(self.physical_quantity),
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_item_quantity"
|
||||
bl_label = "Enable Editing Cost Item Quantity"
|
||||
physical_quantity: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
while len(self.props.quantity_attributes) > 0:
|
||||
self.props.quantity_attributes.remove(0)
|
||||
self.props.active_cost_item_quantity_id = self.physical_quantity
|
||||
data = Data.physical_quantities[self.physical_quantity]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
new = self.props.quantity_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type
|
||||
if data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
elif data_type == "float":
|
||||
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
||||
elif data_type == "integer":
|
||||
new.int_value = 0 if new.is_null else data[attribute.name()]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_cost_item_quantity"
|
||||
bl_label = "Disable Editing Cost Item Quantity"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
props.active_cost_item_quantity_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_cost_item_quantity"
|
||||
bl_label = "Edit Cost Item Quantity"
|
||||
physical_quantity: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
attributes = {}
|
||||
for attribute in props.quantity_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
if attribute.data_type == "string":
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
if attribute.data_type == "float":
|
||||
attributes[attribute.name] = attribute.float_value
|
||||
if attribute.data_type == "integer":
|
||||
attributes[attribute.name] = attribute.int_value
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item_quantity",
|
||||
self.file,
|
||||
**{"physical_quantity": self.file.by_id(self.physical_quantity), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_cost_item_quantity()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddCostItemValue(bpy.types.Operator):
|
||||
bl_idname = "bim.add_cost_item_value"
|
||||
bl_label = "Add Cost Item Value"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
cost_type: bpy.props.StringProperty()
|
||||
cost_category: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
if self.cost_type == "FIXED":
|
||||
category = None
|
||||
elif self.cost_type == "SUM":
|
||||
category = "*"
|
||||
elif self.cost_type == "CATEGORY":
|
||||
category = self.cost_category
|
||||
value = ifcopenshell.api.run("cost.add_cost_item_value", self.file, cost_item=self.file.by_id(self.cost_item))
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item_value", self.file, cost_value=value, attributes={"Category": category}
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveCostItemValue(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_cost_item_value"
|
||||
bl_label = "Add Cost Item Value"
|
||||
cost_value: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.remove_cost_item_value",
|
||||
self.file,
|
||||
cost_value=self.file.by_id(self.cost_value),
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingCostItemValue(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_cost_item_value"
|
||||
bl_label = "Enable Editing Cost Item Value"
|
||||
cost_value: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
while len(self.props.cost_value_attributes) > 0:
|
||||
self.props.cost_value_attributes.remove(0)
|
||||
self.props.active_cost_item_value_id = self.cost_value
|
||||
data = Data.cost_values[self.cost_value]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity" or isinstance(data_type, tuple):
|
||||
continue
|
||||
new = self.props.cost_value_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type
|
||||
if attribute.name() == "AppliedValue":
|
||||
# TODO: for now, only support simple values
|
||||
new.data_type = "float"
|
||||
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
||||
if data_type == "string":
|
||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||
elif data_type == "float":
|
||||
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
||||
elif data_type == "integer":
|
||||
new.int_value = 0 if new.is_null else data[attribute.name()]
|
||||
elif data_type == "enum":
|
||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||
if data[attribute.name()]:
|
||||
new.enum_value = data[attribute.name()]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingCostItemValue(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_cost_item_value"
|
||||
bl_label = "Disable Editing Cost Item Value"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
props.active_cost_item_value_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditCostItemValue(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_cost_item_value"
|
||||
bl_label = "Edit Cost Item Value"
|
||||
cost_value: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
attributes = {}
|
||||
for attribute in props.cost_value_attributes:
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
else:
|
||||
if attribute.data_type == "string":
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
if attribute.data_type == "float":
|
||||
attributes[attribute.name] = attribute.float_value
|
||||
if attribute.data_type == "integer":
|
||||
attributes[attribute.name] = attribute.int_value
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item_value",
|
||||
self.file,
|
||||
**{"cost_value": self.file.by_id(self.cost_value), "attributes": attributes},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.disable_editing_cost_item_value()
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,90 @@
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
quantitytypes_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global quantitytypes_enum
|
||||
quantitytypes_enum = []
|
||||
|
||||
|
||||
def getQuantityTypes(self, context):
|
||||
global quantitytypes_enum
|
||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||
quantitytypes_enum.clear()
|
||||
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
|
||||
quantitytypes_enum.extend(
|
||||
[
|
||||
(t.name(), t.name(), "")
|
||||
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
|
||||
]
|
||||
)
|
||||
return quantitytypes_enum
|
||||
|
||||
|
||||
def updateCostItemName(self, context):
|
||||
if self.name == "Unnamed":
|
||||
return
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMCostProperties
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
if props.active_cost_item_id == self.ifc_definition_id:
|
||||
attribute = props.cost_item_attributes.get("Name")
|
||||
attribute.string_value = self.name
|
||||
|
||||
|
||||
class CostItem(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=updateCostItemName)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
has_children: BoolProperty(name="Has Children")
|
||||
is_expanded: BoolProperty(name="Is Expanded")
|
||||
level_index: IntProperty(name="Level Index")
|
||||
|
||||
|
||||
class BIMCostProperties(PropertyGroup):
|
||||
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
|
||||
is_editing: StringProperty(name="Is Editing")
|
||||
active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id")
|
||||
cost_items: CollectionProperty(name="Work Calendar", type=CostItem)
|
||||
active_cost_item_id: IntProperty(name="Active Cost Id")
|
||||
cost_item_editing_type: StringProperty(name="Cost Item Editing Type")
|
||||
active_cost_item_index: IntProperty(name="Active Cost Item Index")
|
||||
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
|
||||
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
|
||||
qto_name: StringProperty(name="Qto Name")
|
||||
prop_name: StringProperty(name="Prop Name")
|
||||
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||
cost_types: EnumProperty(
|
||||
items=[
|
||||
("FIXED", "Fixed", "The cost value is a fixed number"),
|
||||
("SUM", "Sum", "The cost value is automatically derived from the sum of all nested cost items"),
|
||||
("CATEGORY", "Category", "The cost value represents a single category"),
|
||||
],
|
||||
name="Cost Types",
|
||||
)
|
||||
cost_category: StringProperty(name="Cost Category")
|
||||
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
|
||||
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
|
||||
@@ -0,0 +1,249 @@
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
bl_label = "IFC Cost Schedules"
|
||||
bl_idname = "BIM_PT_cost_schedules"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
|
||||
if not Data.is_loaded:
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_cost_schedule", icon="ADD")
|
||||
|
||||
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
|
||||
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
|
||||
|
||||
def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
|
||||
|
||||
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id:
|
||||
if self.props.is_editing == "COST_SCHEDULE":
|
||||
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
||||
elif self.props.is_editing == "COST_ITEMS":
|
||||
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id
|
||||
row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL")
|
||||
elif self.props.active_cost_schedule_id:
|
||||
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
|
||||
else:
|
||||
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule_id
|
||||
row.operator(
|
||||
"bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL"
|
||||
).cost_schedule = cost_schedule_id
|
||||
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
|
||||
|
||||
if self.props.active_cost_schedule_id == cost_schedule_id:
|
||||
if self.props.is_editing == "COST_SCHEDULE":
|
||||
self.draw_editable_cost_schedule_ui()
|
||||
elif self.props.is_editing == "COST_ITEMS":
|
||||
self.draw_editable_cost_item_ui(cost_schedule_id)
|
||||
|
||||
def draw_editable_cost_schedule_ui(self):
|
||||
for attribute in self.props.cost_schedule_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_editable_cost_item_ui(self, cost_schedule_id):
|
||||
self.layout.template_list(
|
||||
"BIM_UL_cost_items",
|
||||
"",
|
||||
self.props,
|
||||
"cost_items",
|
||||
self.props,
|
||||
"active_cost_item_index",
|
||||
)
|
||||
if self.props.active_cost_item_id:
|
||||
if self.props.cost_item_editing_type == "ATTRIBUTES":
|
||||
self.draw_editable_cost_item_attributes_ui()
|
||||
elif self.props.cost_item_editing_type == "QUANTITIES":
|
||||
self.draw_editable_cost_item_quantities_ui()
|
||||
elif self.props.cost_item_editing_type == "VALUES":
|
||||
self.draw_editable_cost_item_values_ui()
|
||||
|
||||
def draw_editable_cost_item_attributes_ui(self):
|
||||
for attribute in self.props.cost_item_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_editable_cost_item_quantities_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "quantity_types", text="")
|
||||
if self.props.quantity_types == "QTO":
|
||||
row.prop(self.props, "qto_name", text="")
|
||||
row.prop(self.props, "prop_name", text="")
|
||||
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.ifc_class = self.props.quantity_types
|
||||
|
||||
for quantity_id in Data.cost_items[self.props.active_cost_item_id]["CostQuantities"]:
|
||||
quantity = Data.physical_quantities[quantity_id]
|
||||
value = quantity[[k for k in quantity.keys() if "Value" in k][0]]
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=quantity["Name"])
|
||||
row.label(text=str(value))
|
||||
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
|
||||
op = row.operator("bim.edit_cost_item_quantity", text="", icon="CHECKMARK")
|
||||
op.physical_quantity = quantity_id
|
||||
row.operator("bim.disable_editing_cost_item_quantity", text="", icon="CANCEL")
|
||||
elif self.props.active_cost_item_quantity_id:
|
||||
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.physical_quantity = quantity_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_cost_item_quantity", text="", icon="GREASEPENCIL")
|
||||
op.physical_quantity = quantity_id
|
||||
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.physical_quantity = quantity_id
|
||||
|
||||
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
|
||||
box = self.layout.box()
|
||||
self.draw_editable_cost_item_quantity_ui(box)
|
||||
|
||||
def draw_editable_cost_item_quantity_ui(self, layout):
|
||||
for attribute in self.props.quantity_attributes:
|
||||
row = layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "float":
|
||||
row.prop(attribute, "float_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_editable_cost_item_values_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "cost_types", text="")
|
||||
if self.props.cost_types == "CATEGORY":
|
||||
row.prop(self.props, "cost_category", text="")
|
||||
op = row.operator("bim.add_cost_item_value", text="", icon="ADD")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.cost_type = self.props.cost_types
|
||||
if self.props.cost_types == "CATEGORY":
|
||||
op.cost_category = self.props.cost_category
|
||||
|
||||
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
|
||||
cost_value = Data.cost_values[cost_value_id]
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=str(cost_value["Category"]))
|
||||
row.label(text=str(cost_value["AppliedValue"]))
|
||||
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
|
||||
op = row.operator("bim.edit_cost_item_value", text="", icon="CHECKMARK")
|
||||
op.cost_value = cost_value_id
|
||||
row.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
|
||||
elif self.props.active_cost_item_value_id:
|
||||
op = row.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||
op.cost_value = cost_value_id
|
||||
else:
|
||||
op = row.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
|
||||
op.cost_value = cost_value_id
|
||||
op = row.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||
op.cost_value = cost_value_id
|
||||
|
||||
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
|
||||
box = self.layout.box()
|
||||
self.draw_editable_cost_item_value_ui(box)
|
||||
|
||||
def draw_editable_cost_item_value_ui(self, layout):
|
||||
for attribute in self.props.cost_value_attributes:
|
||||
row = layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "float":
|
||||
row.prop(attribute, "float_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
|
||||
class BIM_UL_cost_items(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
props = context.scene.BIMCostProperties
|
||||
cost_item = Data.cost_items[item.ifc_definition_id]
|
||||
row = layout.row(align=True)
|
||||
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_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
|
||||
).cost_item = item.ifc_definition_id
|
||||
else:
|
||||
row.operator(
|
||||
"bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).cost_item = item.ifc_definition_id
|
||||
else:
|
||||
row.label(text="", icon="DOT")
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
|
||||
row.label(text="M3")
|
||||
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
||||
op.cost_item = item.ifc_definition_id
|
||||
row.label(text=str(cost_item["TotalCostQuantity"]))
|
||||
|
||||
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
|
||||
op.cost_item = item.ifc_definition_id
|
||||
row.label(text=str(cost_item["TotalAppliedValue"]))
|
||||
row.label(text=str(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE")
|
||||
|
||||
if context.active_object:
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
row = layout.row(align=True)
|
||||
if oprops.ifc_definition_id in cost_item["Controls"]:
|
||||
op = row.operator("bim.unassign_control", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
op.cost_item = item.ifc_definition_id
|
||||
else:
|
||||
op = row.operator("bim.assign_control", text="", icon="KEYFRAME", emboss=False)
|
||||
op.cost_item = item.ifc_definition_id
|
||||
|
||||
if props.active_cost_item_id == item.ifc_definition_id:
|
||||
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
|
||||
elif props.active_cost_item_id:
|
||||
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
|
||||
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
|
||||
else:
|
||||
row.operator(
|
||||
"bim.enable_editing_cost_item", text="", icon="GREASEPENCIL"
|
||||
).cost_item = item.ifc_definition_id
|
||||
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
|
||||
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
|
||||
@@ -7,6 +7,7 @@ import json
|
||||
import webbrowser
|
||||
import tempfile
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.handler import purge_module_data
|
||||
|
||||
|
||||
class AddCsvAttribute(bpy.types.Operator):
|
||||
@@ -78,9 +79,19 @@ class ImportIfcCsv(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
import ifccsv
|
||||
|
||||
props = context.scene.CsvProperties
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
ifc_csv.output = self.filepath
|
||||
ifc_csv.Import(context.scene.CsvProperties.csv_ifc_file)
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
ifc_csv.delimiter = props.csv_custom_delimiter
|
||||
else:
|
||||
ifc_csv.delimiter = props.csv_delimiter
|
||||
ifc_csv.Import(ifc_file)
|
||||
purge_module_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ class BIM_PT_debug(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, "name", text="")
|
||||
row.prop(attribute, "string_value", text="")
|
||||
if attribute.name == "GlobalId":
|
||||
op = row.operator("bim.select_global_id", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.global_id = attribute.string_value
|
||||
if attribute.int_value:
|
||||
row.operator(
|
||||
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
|
||||