Compare commits

..

1 Commits

Author SHA1 Message Date
Andrej730 1afb421407 .reg file for #3778
Forgot to commit the .reg file...
2023-10-04 15:08:25 +05:00
1464 changed files with 42807 additions and 222445 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
- run: echo ok go
build:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
@@ -80,7 +80,7 @@ jobs:
path: build/assets/Ifc*
deliver:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
needs: build
name: Docker Build, Tag, Push
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310, py311, py312]
pyver: [py39, py310]
config:
- {
name: "Windows Build",
@@ -62,7 +62,7 @@ jobs:
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
python-version: '3.10'
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
-48
View File
@@ -1,48 +0,0 @@
name: ci-bsdd-pypi
on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "0 0 18 * *"
workflow_dispatch:
env:
major: 0
minor: 0
name: ifcopenshell
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
needs: activate
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
run: |
pip install build
cd src/bsdd &&
make dist
- name: Publish a Python distribution to PyPI
uses: ortega2247/pypi-upload-action@master
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: src/bsdd/dist
-48
View File
@@ -1,48 +0,0 @@
name: ci-ifcclash-pypi
on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "0 0 18 * *"
workflow_dispatch:
env:
major: 0
minor: 0
name: ifcopenshell
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
needs: activate
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
run: |
pip install build
cd src/ifcclash &&
make dist
- name: Publish a Python distribution to PyPI
uses: ortega2247/pypi-upload-action@master
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: src/ifcclash/dist
-48
View File
@@ -1,48 +0,0 @@
name: ci-ifccsv-pypi
on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "0 0 18 * *"
workflow_dispatch:
env:
major: 0
minor: 0
name: ifcopenshell
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
needs: activate
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
run: |
pip install build
cd src/ifccsv &&
make dist
- name: Publish a Python distribution to PyPI
uses: ortega2247/pypi-upload-action@master
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: src/ifccsv/dist
@@ -1,7 +1,10 @@
name: ci-ifcopenshell-conda-daily
on:
workflow_dispatch:
push:
paths:
- .github/workflows/ci-ifcopenshell-conda-daily.yml
- conda/**
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
@@ -12,8 +15,17 @@ on:
- cron: "49 23 * * *" # 11min before utc midnight every day
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
test:
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}-${{ matrix.variant }}
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}
needs: activate
runs-on: ${{ matrix.platform.distver }}
defaults:
run:
@@ -22,23 +34,18 @@ jobs:
fail-fast: false
matrix:
pyver: [
{ name: py311, distver: '3.11'},
{ name: py312, distver: '3.12'}
{ name: py310, distver: '3.10'},
{ name: py311, distver: '3.11'}
]
platform: [
{ name: Windows, distver: windows-2022, upload: 'true' },
{ name: Linux, distver: ubuntu-22.04, upload: 'true' },
{ name: macOS, distver: macos-12, upload: 'true' }
]
variant: [
'novtk',
# 'all'
]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Download MacOSX SDK
if: ${{ matrix.platform.name == 'macOS' }}
run: |
@@ -46,22 +53,21 @@ jobs:
tar xf MacOSX10.13.sdk.tar.xz && \
sudo mv -v MacOSX10.13.sdk /opt/ && \
ls /opt/
- uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: mamba-org/provision-with-micromamba@main # https://github.com/mamba-org/provision-with-micromamba
with:
environment-name: build-env
cache-environment: true
condarc: |
channels:
- conda-forge
channel_priority: strict
create-args: >-
python=3.11
anaconda-client
boa
channels: conda-forge
channel-priority: strict
environment-file: conda/environment.yml
extra-specs: |
python=${{ matrix.pyver.distver }}
- name: build, test and upload ifcopenshell
if: ${{ matrix.platform.upload == 'true' }}
run: |
conda mambabuild . --python ${{ matrix.pyver.distver }} -c conda-forge --variants "{variant: ${{ matrix.variant }}}" --token ${{ secrets.ANACONDA_TOKEN }} --user ifcopenshell
conda-build . --python ${{ matrix.pyver.distver }} -c conda-forge --token ${{ secrets.ANACONDA_TOKEN }} --user ifcopenshell
working-directory: ./conda
- name: build & test ifcopenshell
if: ${{ matrix.platform.upload == 'false' }}
run: |
conda-build . --python ${{ matrix.pyver.distver }} -c conda-forge
working-directory: ./conda
+2 -3
View File
@@ -1,7 +1,6 @@
name: ci-ifcopenshell-pypi
on:
workflow_dispatch:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
@@ -36,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310, py311, py312]
pyver: [py36, py37, py38, py39, py310, py311]
config:
- {
name: "Windows Build",
@@ -58,7 +57,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
python-version: '3.10' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- run: echo ${{ env.DATE }}
- name: Get current date
-48
View File
@@ -1,48 +0,0 @@
name: ci-ifcpatch-pypi
on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "0 0 18 * *"
workflow_dispatch:
env:
major: 0
minor: 0
name: ifcopenshell
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
needs: activate
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
run: |
pip install build
cd src/ifcpatch &&
make dist
- name: Publish a Python distribution to PyPI
uses: ortega2247/pypi-upload-action@master
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: src/ifcpatch/dist
@@ -1,53 +0,0 @@
name: Publish-ifcsverchok
on:
push:
paths:
- '.github/workflows/ci-ifcsverchok-build.yml'
- 'src/ifcsverchok/*'
branches:
- v0.7.0
env:
major: 0
minor: 0
name: ifcsverchok
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
needs: activate
name: ifcsverchok
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%y%m%d')"
- name: Compile
run: |
cd src/ifcsverchok
make dist
- name: Upload Zip file to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: src/ifcsverchok/dist/ifcsverchok-${{steps.date.outputs.date}}.zip
asset_name: ifcsverchok-${{steps.date.outputs.date}}.zip
tag: "ifcsverchok-${{steps.date.outputs.date}}"
overwrite: true
body: "ifcsverchok build for ${{steps.date.outputs.date}}"
+10 -1
View File
@@ -9,6 +9,9 @@ on:
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# * * * * *
- cron: "0 0 18 * *"
push:
paths:
- '.github/workflows/ci-ifctester-pypi.yml'
workflow_dispatch:
env:
@@ -27,6 +30,7 @@ jobs:
build:
needs: activate
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -34,7 +38,12 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
python-version: '3.10' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
- run: echo ${{ env.DATE }}
- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%y%m%d')"
- name: Compile
run: |
pip install build
+99
View File
@@ -0,0 +1,99 @@
name: ci_py_only
on:
push:
paths:
- 'src/**'
- 'test/**'
- 'conda/**'
- 'cmake/**'
- '.github/workflows/ci-py-only.yml'
pull_request:
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell' &&
!contains(github.event.head_commit.message, 'skip ci')
steps:
- run: echo ok go
build:
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install C++ dependencies
run: |
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-system-dev \
libboost-thread-dev \
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libhdf5-dev libcgal-dev
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${GITHUB_WORKFLOW}
- name: Build ifcopenshell
run: |
sudo /usr/sbin/update-ccache-symlinks
mkdir build && cd build
PATH="/usr/lib/ccache:$PATH" cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-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 \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DBUILD_CONVERT=Off \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DCGAL_INCLUDE_DIR=/usr/include \
-DGMP_INCLUDE_DIR=/usr/include \
-DMPFR_INCLUDE_DIR=/usr/include \
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
../cmake
sudo make -j $(nproc)
sudo make install
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
sudo /usr/bin/python -m pip install mathutils
- name: Test
run: |
cd test
sudo /usr/bin/python tests.py
cd ../src/ifcopenshell-python
mv ifcopenshell ifcopenshell-local # Force testing on installed module
make test
+23 -25
View File
@@ -31,25 +31,12 @@ jobs:
- run: echo ok go
build:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil shapely
pip install src/bcf --no-deps
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
- name: Install C++ dependencies
run: |
sudo apt update
@@ -62,6 +49,7 @@ jobs:
libboost-regex-dev \
libboost-system-dev \
libboost-thread-dev \
python3-all-dev python3-pip \
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
@@ -74,9 +62,6 @@ jobs:
- name: Build ifcopenshell
run: |
echo $Python3_ROOT_DIR
echo ${{ env.pythonLocation }}
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
@@ -86,11 +71,11 @@ jobs:
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=${{ env.pythonLocation }}/bin/python \
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
-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 \
-DCOLLADA_SUPPORT=Off \
"-DSCHEMA_VERSIONS=2x3;4;4x3;4x3_add1;4x3_add2" \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DCGAL_INCLUDE_DIR=/usr/include \
@@ -117,11 +102,24 @@ jobs:
grep 0$ statuses
grep 0$ statuses | wc -l
- name: Install Python dependencies
run: |
sudo /usr/bin/python -m pip install -U pip
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
sudo /usr/bin/python -m pip install src/bcf --no-deps
sudo /usr/bin/python -m pip install pytest
sudo /usr/bin/python -m pip install isodate
sudo /usr/bin/python -m pip install lark
sudo /usr/bin/python -m pip install networkx
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
sudo /usr/bin/python -m pip install tabulate
sudo /usr/bin/python -m pip install python-dateutil
sudo /usr/bin/python -m pip install mathutils
- name: Test ifcopenshell-python
- name: Test
run: |
cd test
python tests.py
sudo /usr/bin/python tests.py
cd ../src/ifcopenshell-python
mv ifcopenshell ifcopenshell-local # Force testing on installed module
make test-safe
make test
+12 -12
View File
@@ -7,26 +7,34 @@
# output directories
/cmake/out/
/docs/out/
/src/examples/out/
/src/ifcmax/out/
/src/ifcwrap/out/
/src/qtviewer/out/
/win/BuildDepsCache*.txt
/win/BuildDepsCache*.txt
# IfcExpressParser residue
/src/ifcexpressparser/express_parser.py
# General Python residue
__pycache__
*.py.bak
# Visual Studio Code files
.vscode
.vs
# PyCharm files
.idea
#Virtual Env Files
Pipfile
Pipfile.lock
# Docs
/docs/cpp-api/output
/docs/output
/docs/rst_files
/docs/doxygen
/src/ifcblenderexport/docs/_build
# gettext binary translation files
*.mo
@@ -76,14 +84,8 @@ src/ifcopenshell-python/test/build
# blenderbim libs
src/blenderbim/blenderbim/libs
# blenderbim i18n
src/blenderbim/blenderbim/translations.py
# blenderbim test temp files
src/blenderbim/test/files/temp
src/blenderbim/test/files/basic.ifc.cache.blend
src/blenderbim/test/files/basic.ifc.cache.sqlite
src/blenderbim/drawings
src/blenderbim/layouts
@@ -94,8 +96,6 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# apple
.DS_Store
# clangd config
.clangd
# clangd cache
.cache
+1 -4
View File
@@ -10,7 +10,4 @@
url = https://github.com/IfcOpenShell/svgfill
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/IfcOpenShell/ids-test-files
[submodule "docs/cpp-api/assets/doxygen-awesome-css"]
path = docs/cpp-api/assets/doxygen-awesome-css
url = https://github.com/jothepro/doxygen-awesome-css.git
url = https://github.com/IfcOpenShell/ids-test-files
+14 -15
View File
@@ -7,33 +7,33 @@ IfcOpenShell
</p>
IfcOpenShell is an open source ([LGPL]) software library for working with Industry Foundation Classes ([IFC]). Complete
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and [IFC4x3 Add2]. Extensive geometric support
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and IFC4x3. Extensive geometric support
is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1]. Extending with support for arbitrary IFC schemas
is possible at compile-time when using C++ and at run-time when using Python.
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application
to convert IFC models to other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform),
and many other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application to convert IFC models to
other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform), and many
other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
For more information, see:
* [IfcOpenShell Website](http://ifcopenshell.org)
* [IfcOpenShell Documentation](https://docs.ifcopenshell.org)
* [IfcOpenShell C++ Installation](https://docs.ifcopenshell.org/ifcopenshell/installation.html)
* [IfcOpenShell Python Installation](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html)
* [IfcOpenShell Python Hello World Tutorial](https://docs.ifcopenshell.org/ifcopenshell-python/hello_world.html)
* [IfcOpenShell Documentation](http://blenderbim.org/docs-python)
* [IfcOpenShell C++ Installation](https://blenderbim.org/docs-python/ifcopenshell/installation.html)
* [IfcOpenShell Python Installation](https://blenderbim.org/docs-python/ifcopenshell-python/installation.html)
* [IfcOpenShell Python Hello World Tutorial](https://blenderbim.org/docs-python/ifcopenshell-python/hello_world.html)
* [BlenderBIM Add-on Website](https://blenderbim.org)
* [BlenderBIM Add-on Documentation](https://docs.blenderbim.org/index.html)
* [Add-on Installation](https://docs.blenderbim.org/users/installation.html)
* [Exploring an IFC model](https://docs.blenderbim.org/users/exploring_an_ifc_model.html)
* [BlenderBIM Add-on Documentation](http://blenderbim.org/docs)
* [Add-on Installation](https://blenderbim.org/docs/users/installation.html)
* [Exploring an IFC model](https://blenderbim.org/docs/users/exploring_an_ifc_model.html)
| Service | Status |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Anaconda Daily Build | [![Anaconda-Server Badge](https://img.shields.io/conda/vn/ifcopenshell/ifcopenshell)](https://anaconda.org/ifcopenshell/ifcopenshell) |
| Anaconda v0.7.0 Stable | [![Anaconda-Server Badge](https://img.shields.io/conda/vn/conda-forge/ifcopenshell)](https://anaconda.org/conda-forge/ifcopenshell) |
| PyPi Daily Build | [![PyPi Badge](https://img.shields.io/pypi/v/ifcopenshell)](https://pypi.org/project/ifcopenshell/) |
| ArchLinux AUR Package Stable | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell)](https://aur.archlinux.org/packages/ifcopenshell) |
| ArchLinux AUR Package git | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell-git)](https://aur.archlinux.org/packages/ifcopenshell-git) |
| ArchLinux AUR Package Stable | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell)](https://aur.archlinux.org/packages/ifcopenshell) |
| ArchLinux AUR Package git | [![AUR Badge](https://img.shields.io/aur/version/ifcopenshell-git)](https://aur.archlinux.org/packages/ifcopenshell-git) |
| BlenderBIM Add-on Chocolatey (under moderation) | [![Chocolatey Badge](https://img.shields.io/chocolatey/v/blenderbim-nightly)](https://community.chocolatey.org/packages/blenderbim-nightly/) |
| Sponsor development on OpenCollective | [![Financial Contributors](https://opencollective.com/opensourcebim/tiers/badge.svg)](https://opencollective.com/opensourcebim/) |
| Docker hub | [![Docker Pulls](https://img.shields.io/docker/pulls/aecgeeks/ifcopenshell)](https://hub.docker.com/r/aecgeeks/ifcopenshell) |
@@ -44,7 +44,7 @@ Contents
Those marked with an asterisk are part of IfcOpenShell.
| Name | Description | License |
| ------------------------- | --------------------------------------------------------------------- | ------------------- |
| ------------------------- | ----------------------------------------------------------------------| ------------------- |
| bcf | Library to read and write BCF-XML and query OpenCDE BCF-API modules | LGPL-3.0-or-later |
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later |
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later |
@@ -77,7 +77,6 @@ Those marked with an asterisk are part of IfcOpenShell.
[IFC]: https://technical.buildingsmart.org/standards/ifc/ "IFC"
[IFC2x3 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ "IFC2x3 TC1"
[IFC4 Add2 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/ "IFC4 Add2 TC1"
[IFC4x3 Add2]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/ "IFC4x3 Add2"
[Visual Studio]: https://www.visualstudio.com/ "Visual Studio"
[Visual C++ Build Tools]: http://landinghub.visualstudio.com/visual-cpp-build-tools "Visual C++ Build Tools"
[MSYS2]: https://msys2.github.io/ "MSYS2"
+1 -1
View File
@@ -12,7 +12,7 @@ RUN apt-get -y update && apt-get -y install unzip curl
# Install AWS Lambda runtime interface client
RUN pip install --target ${FUNCTION_DIR} awslambdaric
# Set the IfcOpenShell build version (check available builds at: https://docs.ifcopenshell.org/ifcopenshell-python/installation.html)
# Set the IfcOpenShell build version (check available builds at: https://blenderbim.org/docs-python/ifcopenshell-python/installation.html)
ARG IFC_OPENSHELL_BUILD="39-v0.7.0-476ab50"
# Download and extract IfcOpenShell
+1 -1
View File
@@ -15,7 +15,7 @@
<licenseUrl>https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/COPYING</licenseUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<projectSourceUrl>https://github.com/IfcOpenShell/IfcOpenShell</projectSourceUrl>
<docsUrl>https://docs.blenderbim.org/</docsUrl>
<docsUrl>https://blenderbim.org/docs/</docsUrl>
<!--<mailingListUrl></mailingListUrl>-->
<bugTrackerUrl>https://github.com/IfcOpenShell/IfcOpenShell/issues</bugTrackerUrl>
<tags>blender bim blenderbim ifc python opensource foss</tags>
+1 -1
View File
@@ -29,7 +29,7 @@ URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+<span>Blender (\d+\.\d+)\..+</span>"
RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+<span>Blender (\d+\.\d+\.\d+)</span>"
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"(?:set|SET)\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"SET\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
if sys.argv[1] == "--do_choco_release?":
+38 -81
View File
@@ -17,7 +17,7 @@
# #
################################################################################
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.1.3)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
@@ -27,18 +27,13 @@ cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0074 NEW)
cmake_policy(SET CMP0078 OLD)
cmake_policy(SET CMP0086 NEW)
if (POLICY CMP0144)
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
endif()
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
# use extra version to make pre-release using eg semver
if(NOT DEFINED EXTRA_VERSION)
set(EXTRA_VERSION "-alpha.3")
endif()
set(EXTRA_VERSION "-alpha.3")
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
option(WASM_BUILD "Build a WebAssembly binary." OFF)
@@ -191,7 +186,7 @@ if(GLTF_SUPPORT AND BUILD_CONVERT)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
endif()
# Add USD support to serializers
# Add USD support to serializers
if(USD_SUPPORT)
UNIFY_ENVVARS_AND_CACHE(USD_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(USD_LIBRARY_DIR)
@@ -214,13 +209,13 @@ if(USD_SUPPORT)
endif()
set(USD_LIBRARIES
usd_usd
usd_usd
usd_usdGeom
usd_usdShade
usd_usdLux
usd_vt
usd_sdf
usd_tf
usd_usdShade
usd_usdLux
usd_vt
usd_sdf
usd_tf
usd_gf
)
@@ -378,34 +373,13 @@ if(BUILD_IFCGEOM)
message(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
endif()
if(OCC_INCLUDE_DIR)
file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAJOR
REGEX "#define OCC_VERSION_MAJOR.*"
)
string(REGEX MATCH "[0-9]+" OCC_MAJOR ${OCC_MAJOR})
file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MINOR
REGEX "#define OCC_VERSION_MINOR.*"
)
string(REGEX MATCH "[0-9]+" OCC_MINOR ${OCC_MINOR})
file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAINT
REGEX "#define OCC_VERSION_MAINTENANCE.*"
)
string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT})
set(OCC_VERSION_STRING "${OCC_MAJOR}.${OCC_MINOR}.${OCC_MAINT}")
endif(OCC_INCLUDE_DIR)
set(OPENCASCADE_LIBRARY_NAMES
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
TKFillet TKXSBase TKOffset TKHLR
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
# @todo investigate the exact conditions when this is necessary
TKBin
)
if(OCC_VERSION_STRING VERSION_LESS 7.8.0)
list(APPEND OPENCASCADE_LIBRARY_NAMES TKIGES TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP)
else(OCC_VERSION_STRING VERSION_LESS 7.8.0)
list(APPEND OPENCASCADE_LIBRARY_NAMES TKDESTEP TKDEIGES)
endif(OCC_VERSION_STRING VERSION_LESS 7.8.0)
if("${OCC_LIBRARY_DIR}" STREQUAL "")
find_library(OCC_LIBRARY TKernel
@@ -461,19 +435,15 @@ if(BUILD_IFCGEOM)
if(OCCT_STATIC)
find_package(Threads)
if(WASM_BUILD)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
else()
# OPENCASCADE_LIBRARIES repeated N times below in order to fix cyclic dependencies
# OPENCASCADE_LIBRARIES repeated N times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
# tfk: --start-group ... --end-group didn't work on the apple linker when last tested
if(APPLE)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
else()
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
endif()
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
if (NOT APPLE AND NOT WIN32)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
endif()
@@ -611,31 +581,23 @@ if(HDF5_SUPPORT)
if(WIN32)
# Windows
set(HDF5_LIBRARIES)
find_package(HDF5 COMPONENTS CXX)
if (HDF5_FOUND)
set(HDF5_LIBRARIES ${HDF5_CXX_LIBRARIES})
endif()
list(APPEND HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/zlib.lib"
)
set(zlib_post zlib)
set(lib_ext lib)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# macOS
set(zlib_post libz)
set(lib_ext dylib)
else()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# macOS
set(zlib_post libz)
set(lib_ext dylib)
else()
# linux
set(zlib_post libz)
set(lib_ext so)
endif()
set(HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
)
# linux
set(zlib_post libz)
set(lib_ext so)
endif()
set(HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
)
endif()
endif()
@@ -682,10 +644,6 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
endif()
else()
if(MSVC)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od")
endif()
endif()
if(MSVC)
@@ -781,12 +739,10 @@ if(NOT SCHEMA_VERSIONS)
# super arbitrarily try to keep size down at least a little bit
set(SCHEMA_VERSIONS "2x3" "4")
else()
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4" "4x3" "4x3_tc1" "4x3_add1" "4x3_add2")
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4" "4x3" "4x3_tc1" "4x3_add1")
endif()
endif()
message(STATUS "IFC SCHEMA_VERSIONS that will be used for the build: ${SCHEMA_VERSIONS}.")
foreach(schema ${SCHEMA_VERSIONS})
add_definitions(-DHAS_SCHEMA_${schema})
endforeach()
@@ -869,7 +825,7 @@ if (BUILD_IFCGEOM)
if (WASM_BUILD)
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
else()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
endif()
endif()
@@ -932,7 +888,7 @@ if(BUILD_IFCGEOM)
add_library(IfcGeom_ifc${schema} STATIC ${IFCGEOM_FILES})
set_target_properties(IfcGeom_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
if (NOT WASM_BUILD)
target_link_libraries(IfcGeom_ifc${schema} IfcParse ${OPENCASCADE_LIBRARIES})
target_link_libraries(IfcGeom_ifc${schema} IfcParse ${OPENCASCADE_LIBRARIES})
endif()
endforeach()
@@ -968,7 +924,7 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON)
foreach(schema ${SCHEMA_VERSIONS})
add_library(Serializers_ifc${schema} STATIC ${SERIALIZERS_S_FILES})
set_target_properties(Serializers_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
if (WASM_BUILD)
target_link_libraries(Serializers_ifc${schema} ${HDF5_LIBRARIES})
else()
@@ -979,6 +935,8 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON)
add_library(Serializers ${SERIALIZERS_FILES})
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS" VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
message(STATUS "WITH_PROJ ${WITH_PROJ}")
if (WITH_PROJ)
target_compile_definitions(Serializers PRIVATE "WITH_PROJ")
if (PROJ_STATIC)
@@ -1149,10 +1107,9 @@ endif()
# Packaging
list(APPEND CPACK_SOURCE_IGNORE_FILES
"/\\\\.git"
"/build/"
.git
.gitignore
)
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/..;/")
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
@@ -1175,7 +1132,7 @@ endforeach(COMPONENT)
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3-dev, python3-pytest ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3.8, python3-pytest ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
+8
View File
@@ -0,0 +1,8 @@
compile:
conda activate conda-build && conda-build -c conda-forge . --keep-old-work --python 3.10.5
debug:
conda activate conda-build && conda-debug -c conda-forge . --python 3.10.4
env:
conda env update --f environment.yml --prune
+1 -5
View File
@@ -1,6 +1,2 @@
CONDA_BUILD_SYSROOT:
- /opt/MacOSX10.13.sdk # [osx]
variant:
- novtk
- all
- /opt/MacOSX10.13.sdk # [osx]
+13
View File
@@ -0,0 +1,13 @@
name: conda-build
channels:
- conda-forge
dependencies:
- conda-build
- conda-verify
- anaconda-client
- ninja
- doxygen
- ripgrep
- pip
- pip:
- lief==0.13
+7 -12
View File
@@ -1,10 +1,6 @@
{% set name = "ifcopenshell" %}
{% set version = "0.7.0" %}
{% set build = 1 %}
# Higher number -> Always prioritize "novtk" variant over "all" variant
{% set build = build + 200 %} # [variant == "novtk"]
{% set build = build + 100 %} # [variant == "all"]
package:
name: {{ name }}
@@ -14,9 +10,8 @@ source:
path: ..
build:
string: py{{ CONDA_PY }}_{{ variant }}_h{{ PKG_HASH }}_{{ build }}
binary_relocation: false [osx]
number: {{ build }}
number: 1
requirements:
build:
@@ -24,12 +19,12 @@ requirements:
- {{ compiler('cxx') }}
- ninja >=1.10.2
- cmake
- swig 4.1.1
- swig >=4.0.2
host:
- python
- boost-cpp
- occt 7.7.2 *{{ variant }}*
- occt ==7.7.0
- libxml2
- cgal-cpp
- hdf5
@@ -41,11 +36,11 @@ requirements:
run:
- python
- occt 7.7.2 *{{ variant }}*
- {{ pin_compatible('occt', max_pin='x.x.x') }}
- {{ pin_compatible('cgal-cpp', max_pin='x.x.x') }}
- {{ pin_compatible('boost-cpp', max_pin='x.x.x') }}
- {{ pin_compatible('hdf5', max_pin='x.x.x') }}
- libxml2
- hdf5
- mpfr
- gmp # [unix]
- mpir # [win]
@@ -57,12 +52,12 @@ test:
- ifcopenshell
about:
home: https://ifcopenshell.org
home: http://ifcopenshell.org
license: LGPL-3.0-or-later
license_file: COPYING
summary: 'IfcOpenShell is a library to support the IFC file format'
description: |
IfcOpenShell is an open source (LGPL) software library for
working with the Industry Foundation Classes (IFC) file format.
doc_url: https://ifcopenshell.org/
doc_url: http://ifcopenshell.org/
dev_url: https://github.com/IfcOpenShell/IfcOpenShell
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
+11
View File
@@ -0,0 +1,11 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
-33
View File
@@ -1,33 +0,0 @@
# IfcOpenShell C++ API documentation
This folder contains the setup to build the IfcOpenShell C++ API documentation from the source code.
## Generating the documentation
> Prerequisites:
>
> Make sure to have [Doxygen](https://www.doxygen.nl) and [Graphviz](https://graphviz.org) installed into your `$PATH` variable.
>
> The documentation also use the [doxygen-awesome](https://jothepro.github.io/doxygen-awesome-css) theme as a git submodule.
Build with the command (from within the `/docs/cpp-api` folder):
```shell
$ doxygen
```
To include the current git commit hash into the build documentation, use the following command:
```shell
$ PROJECT_NUMBER=$(git rev-parse --short HEAD) doxygen
```
This will extract the current commit hash in short version and sets the propper ENV variable used by doxygen.
The generation of the documentation might take a while depending on your systems hardware, as it is configured to generate the Class graphs using .
The resulting documentation is located unter `/cpp-api/output/html` and can be directly accessed with your browser:
```shell
$ open ./output/html/index.html
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

+240
View File
@@ -0,0 +1,240 @@
APPLY_DEFAULT_MATERIALS
-----------------------
Given the command invocation:
Duplex_A_20110907_optimized.ifc d.dae -yv --include attribute GlobalId 3bXiCStxP6Fgxdej$yc50U
You will find log messages along the lines of
[Warning] {3bXiCStxP6Fgxdej$yc50U} No material and surface styles for:
#333=IfcCovering('3bXiCStxP6Fgxdej$yc50U',#1,'Compound Ceiling:Gypsum Board:187483',$,'Compound Ceiling:Gypsum Board',#17840,#17052,'187483',.CEILING.)
This means that there is no IfcStyledItem associated to the representation items and that the element does not have an IfcMaterial association with IfcMaterialRepresentation from which we can derive a style (colour) for the element.
The interactive session below shows how with this setting enabled you will get a default generated material from the IFC element entity type and material indices of 0 pointing to that. With this setting disabled the material index would be -1 to indicate a missing style. Note that there is one material index for every triangle in the list of `shp.geometry.faces`.
>>> import ifcopenshell, ifcopenshell.geom
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> s = ifcopenshell.geom.settings()
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>>
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[]
>>>
>>> s.set(s.APPLY_DEFAULT_MATERIALS, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[('IfcCovering', (0.7, 0.7, 0.7))]
This is enabled by default for the IfcConvert serializers as they will not gracefully handle -1 material indices and allows users to quickly assign colours based on entity types in their modelling applications.
APPLY_LAYERSETS
---------------
This setting is available in IfcConvert as `--enable-layerset-slicing`.
For IfcWall and IfcSlab elements, takes the associated IfcMaterialLayerSet and builds a set of surfaces to segment the building element geometry.
Note that enabling this settings is computationally intensive as it involves 3D Boolean operations.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d2.dae --enable-layerset-slicing -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d2.dae](images/settings-2.png)
BUILDING_LOCAL_PLACEMENT
------------------------
This setting is available in IfcConvert using `--building-local-placement`.
In the typical IfcSite > IfcBuilding > IfcBuildingStorey > ... hierarchy of elements, don't incorporate the ObjectPlacement of the IfcBuilding and above in the placement of elements in the output. This is useful when there is a large offset in this placement that reduces precision in further processing.
CONVERT_BACK_UNITS
------------------
This setting is available in IfcConvert using `--convert-back-units`.
Internally IfcOpenShell uses meters as the global length unit to do calculations. This setting restores the coordinate positions after conversion by multiplying the factor of the IfcUnit with UnitType=LENGTHUNIT into the output geometry coordinate values.
DISABLE_OPENING_SUBTRACTIONS
----------------------------
This setting is available in IfcConvert using `--disable-opening-subtraction`.
As in most viewer applications, IfcOpeningElement geometry is subtracted from their host elements. This setting disables this behavior.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d3.dae --disable-opening-subtraction -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d3.dae](images/settings-3.png)
Note that disabling this settings will reduce processing time and improve robustness as it involves 3D Boolean operations.
DISABLE_TRIANGULATION
---------------------
To be used in conjunction with `USE_BREP_DATA`. Do not apply the triangulation and - when `USE_BREP_DATA` is set - return a OpenCASCADE serialized TopoDS_Shape from `create_shape()` and `iterator`.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.DISABLE_TRIANGULATION, True)
>>> s.set(s.USE_BREP_DATA, True)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> print(shp.geometry.brep_data)
CASCADE Topology V1, (c) Matra-Datavision
Locations 0
Curve2ds 0
Curves 12
1 4.6750000000000034 -8.0749999999999904 2.657 -2.0455514041918775e-15 -1 0
1 4.6750000000000034 -8.0749999999999904 2.657 1 -3.435893306383461e-15 0
1 6.2260000000000044 -8.0749999999999957 2.657 -2.0455514041918724e-15 -1 0
1 6.226 -10.246000000000031 2.657 -1 6.8717866127669219e-15 0
...
EDGE_ARROWS
-----------
When `INCLUDE_CURVES` is true and geometric elements include curves (such as the wall axis), add arrow heads to the edges to indicate direction of the curve.
Duplex_A_20110907_optimized.ifc d4.dae --model --plan --edge-arrows -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d4.dae](images/settings-4.png)
EXCLUDE_SOLIDS_AND_SURFACES
---------------------------
Exclude faces, shells and solids from geometrical output. Implied when using `--plan` without `--model` in IfcConvert.
FASTER_BOOLEANS
---------------
NOTE: Only applicable when using OCCT 6.9 and earlier.
This setting is available in IfcConvert using `--merge-boolean-operands`.
Fuse the collection of all boolean operands into a single union before applying the boolean subtraction, as opposed to doing individual subtractions. This likely improves performance. From OCCT 7.0 onwards the boolean operations with multiple arguments is used.
GENERATE_UVS
------------
This setting is available in IfcConvert using `--generate-uvs`.
Applies a box projection on the generated geometry for the element to obtain UV coordinates. This is purely generated, it does not involve texture coordinates stored in the IFC model.
Duplex_A_20110907_optimized.ifc d5.dae --generate-uvs -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d5.dae with a UV grid applied in Blender](images/settings-5.png)
INCLUDE_CURVES
--------------
This setting is available in IfcConvert using `--plan`.
Include edge and wire geometries in the geometric output.
LAYERSET_FIRST
--------------
This setting is available in IfcConvert using `--layerset-first`.
When not using APPLY_LAYERSETS, take the first material layer from the set to use as the material for the overall element.
NO_NORMALS
----------
This setting is available in IfcConvert using `--no-normals`.
Do not emit normals on geometric output
SEARCH_FLOOR
------------
Note: Only applicable to Collada .DAE output when used from IfcConvert.
This setting is available in IfcConvert using `--use-element-hierarchy`.
Include the spatial hierarchy in the elements.
SEW_SHELLS
----------
This setting is available in IfcConvert using `--orient-shells`.
Re-orient or sew connected face sets to have a consistent outwards orientation.
SITE_LOCAL_PLACEMENT
--------------------
This setting is available in IfcConvert using `--site-local-placement`.
See `BUILDING_LOCAL_PLACEMENT`, but exclude also the ObjectPlacement of the IfcSite.
USE_BREP_DATA
-------------
See `DISABLE_TRIANGULATION`.
USE_PYTHON_OPENCASCADE
----------------------
Note: Only available when an import of `OCC.Core.BRepTools` or `OCC.BRepTools` succeeds.
This implies `USE_WORLD_COORDS` `USE_BREP_DATA` and `DISABLE_TRIANGULATION`. The serialized TopoDS_Shape of `USE_BREP_DATA` is deserialized by Python OpenCASCADE.
USE_WORLD_COORDS
----------------
Apply the ObjectPlacement of the building elements to the geometric output. This is implied when using the Wavefront .OBJ output in IfcConvert. Note that this also eliminates the possibility for geometric elements to point to the same interpreted geometry result.
VALIDATE_QUANTITIES
-------------------
This setting is available in IfcConvert using `--validate`.
Running IfcConvert with `--validate` will set a non-zero exit code when ever a log message with severity equal or greater than ERROR has been emitted.
Currently for internal use only. For every building element geometry converted, looks for an associated quantity set where the OwnerHistory's organization name is IfcOpenShell. And looks for the quantities "Total Surface Area", "Volume", "Shape Validation Properties.Surface Genus" and validates these according to the interpreted geometry definition. Emit Logger::Error when calculated values are outside of the tolerance range for the value stored in the model.
WELD_VERTICES
-------------
Note: In Python, this setting is *on* by default.
Note: This setting only affects triangulated output.
This setting is available in IfcConvert using `--weld-vertices`.
Discards normals and joins vertices solely based on position. This is useful when output is to be modified in a modeling application.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.WELD_VERTICES, False)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.657, 4.674999999999999, -10.24600000000002, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226, -10.24600000000003, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226000000000004, -8.074999999999996, 2.6)
>>> shp.geometry.normals
(3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0)
>>>
>>> s.set(s.WELD_VERTICES, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6)
>>> shp.geometry.normals
()
+5 -12
View File
@@ -76,7 +76,7 @@ PROJECT_NAME = "IfcOpenShell"
USE_CURRENT_PYTHON_VERSION = os.getenv("USE_CURRENT_PYTHON_VERSION")
ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
PYTHON_VERSIONS = ["3.9.11", "3.10.3", "3.11.8", "3.12.1"]
PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.0"]
JSON_VERSION = "v3.6.1"
OCE_VERSION = "0.18.3"
OCCT_VERSION = "7.5.3"
@@ -254,11 +254,6 @@ if not os.path.exists(LOG_FILE):
open(LOG_FILE, "w").close()
logger.info(f"using command log file '{LOG_FILE}'")
# Causing havoc in python 3.11 build
try:
del os.environ['__PYVENV_LAUNCHER__']
except: pass
def run(cmds, cwd=None, can_fail=False):
"""
@@ -289,9 +284,8 @@ if platform.system() == "Darwin":
# Apparently not supported
PYTHON_VERSIONS = [pv for pv in PYTHON_VERSIONS if tuple(map(int, pv.split("."))) >= (3, 7)]
if run(["sw_vers", "-productVersion"]) < "10.16":
# This is now solved with the '__PYVENV_LAUNCHER__' hack
# PYTHON_VERSIONS = [pv for pv in PYTHON_VERSIONS if tuple(map(int, pv.split("."))) < (3, 11)]
pass
# Apparently not supported
PYTHON_VERSIONS = [pv for pv in PYTHON_VERSIONS if tuple(map(int, pv.split("."))) < (3, 11)]
BOOST_VERSION_UNDERSCORE = BOOST_VERSION.replace(".", "_")
@@ -898,9 +892,8 @@ if "IfcOpenShell-Python" in targets:
module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
if platform.system() != "Darwin":
if BUILD_CFG == "Release":
# TODO: This symbol name depends on the Python version?
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
# TODO: This symbol name depends on the Python version?
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
return module_dir
+1 -1
View File
@@ -13,7 +13,7 @@ readme = "README.md"
requires-python = ">=3.8"
keywords = ["IFC", "BCF", "BIM"]
dependencies = [
"xsdata>=24.4",
"xsdata",
"numpy",
"ifcopenshell",
]
+5 -7
View File
@@ -10,14 +10,14 @@ from xsdata.formats.dataclass.serializers.config import SerializerConfig
def build_xml_parser(context: Optional[XmlContext] = None) -> XmlParser:
"""Return a parser for an XML file."""
parser = XmlParser(context=context or XmlContext())
parser.register_namespace(ns_map=parser.ns_map, prefix="xs", uri="http://www.w3.org/2001/XMLSchema")
parser.register_namespace("xs", "http://www.w3.org/2001/XMLSchema")
return parser
def build_serializer(context: Optional[XmlContext] = None) -> XmlSerializer:
"""Return a serializer for an XML file."""
return XmlSerializer(
config=SerializerConfig(indent=" "),
config=SerializerConfig(pretty_print=True),
context=context or XmlContext(),
)
@@ -36,9 +36,8 @@ class AbstractXmlParserSerializer(Protocol):
xml: The XML file as bytes.
clazz: The class to parse to.
"""
...
def serialize(self, obj: object, ns_map: Optional[dict[str, str]] = None) -> str:
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
"""
Serialize an object to XML.
@@ -49,7 +48,6 @@ class AbstractXmlParserSerializer(Protocol):
Returns:
The XML as string.
"""
...
class XmlParserSerializer:
@@ -70,7 +68,7 @@ class XmlParserSerializer:
"""
return self.parser.from_bytes(xml, clazz)
def serialize(self, obj: object, ns_map: Optional[dict[Optional[str], str]] = None) -> str:
def serialize(self, obj: T, ns_map: Optional[dict[str, str]] = None) -> str:
"""
Serialize an object to XML.
@@ -81,5 +79,5 @@ class XmlParserSerializer:
Returns:
The XML as string.
"""
ns_map = ns_map or self.parser.ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
ns_map = ns_map or {"xs": "http://www.w3.org/2001/XMLSchema"}
return self.serializer.render(obj, ns_map)
+438 -55
View File
@@ -50,31 +50,105 @@ PYLIBDIR:=python3.10
PYNUMBER:=310
PYPI_VERSION:=3.10
endif
ifeq ($(PYVERSION), py311)
PYLIBDIR:=python3.11
PYNUMBER:=311
PYPI_VERSION:=3.11
endif
ifeq ($(PYVERSION), py312)
PYLIBDIR:=python3.12
PYNUMBER:=312
PYPI_VERSION:=3.12
endif
ifeq ($(PLATFORM), linux)
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py39h40a70d0_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py39hdfdd6bb_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py39h7c9e3ff_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/2d/f2/8ec281d357e8bb7d08dc8d727f0e4c8ef3dae7d3fa75c69c8e452bb82d50/shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/01/61/3ff85fb4bb596ce3d223c8fcf93c8df5c12bc8899dfb4fb3cb1c5b20dd5f/Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py310h995690b_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py310hf02b7e0_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py310hc4a4660_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a8/a5/403728b5614b28083f6424dfdefec5fcf58068495fb03bb08532671c642f/shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/f6/51/320986ebd6d46a0e95c2240468ced73153b691ce07617078bcdf30c609ec/Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/linux-64/octomap-1.9.8-h924138e_0.tar.bz2
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/linux-64/boost-cpp-1.78.0-h6582d0a_3.conda
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2
endif
ifeq ($(PLATFORM), macos)
PYPI_PLATFORM:=--platform macosx_10_10_x86_64
PYPI_PLATFORM:=--platform macosx_10_9_x86_64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py39hd85b194_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py39hc4d6e28_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py39h953a6b8_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/36/a4/7e542a209f862f967d7cb8e939eff155f4294a27d17e16441fb8bdd51a2c/shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/88/7a/ddfe28b485b623361457d4783007c1f9ba83a87f93e7fec32f64793efb6c/Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py310h1db6f5f_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py310h43da829_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py310h3e792ce_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/1f/2a/dc3353c2431cf53e8d04bb8fba27e584410ca3435c9c85f76d71bf0c0e80/shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/d8/60/b13c00d403f34110e96c1b5c0afa73ce461efe3fe960c3a7e3e7fe190d82/Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-64/octomap-1.9.8-hb8565cd_0.tar.bz2
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-64/boost-cpp-1.78.0-hf5ba120_3.conda
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-64/zlib-1.2.11-h7795811_1010.tar.bz2
endif
ifeq ($(PLATFORM), macosm1)
PYPI_PLATFORM:=--platform macosx_11_0_arm64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py39hc34188a_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py39h13cfc01_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py39h99de9ae_4.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h9eb174b_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/ea/aa/45fbd031edf3149cb767d8b9f9db45d5faf0324d743c6b8fb0298cc022d0/shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py310h46fc4cd_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py310ha2643af_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py310h629746b_4.tar.bz2
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
SHAPELY_URL:=https://files.pythonhosted.org/packages/ec/41/d59208743e737184e1b403e95a937aebb022b8459e99efbcd5208fc8be46/shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/0c/5f/117b653cad585f3aedfe0de996c292e67d4b020ed77f652e5a6c8c24f908/Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-arm64/assimp-5.0.1-h0f81e16_7.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-arm64/octomap-1.9.8-hffc8910_0.tar.bz2
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-arm64/boost-cpp-1.78.0-h9ed8d21_3.conda
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2
endif
ifeq ($(PLATFORM), win)
PYPI_PLATFORM:=--platform win_amd64
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py39h39b25cb_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py39hb6c915b_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py39hea4d8d1_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/a7/ae/eab645c4075093584b7a65ab71cb8ff4563a015bd935c9b55dba14b2c1eb/shapely-2.0.1-cp39-cp39-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/19/3f/b4d4bcf05dbcbe07f2e9613a8f4180c297395e73a91d8ad22c32c6624f8c/Pillow-9.2.0-cp39-cp39-win_amd64.whl
endif
ifeq ($(PYVERSION), py310)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py310heb59267_0.conda
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py310hd146abe_0.conda
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py310h220cb41_4.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
SHAPELY_URL:=https://files.pythonhosted.org/packages/81/8a/7ac076a86b2632f1872284c5e60ed5f2fc26094875a85b35d9fa17b52504/shapely-2.0.1-cp310-cp310-win_amd64.whl
PILLOW_URL:=https://files.pythonhosted.org/packages/02/55/67a3c17b9e7d972ed8c246f104da99ca4f3ea42fba566697e479011b84b6/Pillow-9.2.0-cp310-cp310-win_amd64.whl
endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.2.5/download/win-64/assimp-5.2.5-h4dcb625_0.tar.bz2
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/win-64/qhull-2020.2-h70d2c02_2.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/win-64/octomap-1.9.8-h91493d7_0.tar.bz2
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/win-64/boost-cpp-1.78.0-h9f4b32c_3.conda
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/win-64/zlib-1.2.11-h62dcd97_1010.tar.bz2
endif
.PHONY: bump
@@ -95,7 +169,7 @@ endif
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-c7830e9-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-9cc1f5f-$(PLATFORM)64.zip
cd dist/working && unzip ifcopenshell-python*
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
@@ -109,10 +183,8 @@ endif
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.7.0.zip
cd dist/working && unzip v0.7.0.zip
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/util/
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/api/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/util dist/blenderbim/libs/site/packages/ifcopenshell/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/*.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
# Provides bcf functionality
@@ -150,6 +222,49 @@ endif
cp -r dist/working/site-packages/git dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Mustache templating in construction documentation
# TODO: remove this dependency, it seems overkill and we seem to get by with str replaces
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/3f/e7/8750ba6c6101d6aa5ceeb20c013adf2c6f3554a12c71d75654b468404bfa/pystache-0.6.0.tar.gz
cd dist/working && tar -xzvf pystache*
cd dist/working/pystache-0.6.0/ && $(PYTHON) setup.py build && cp -r build/lib/pystache ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides SVG export in construction documentation
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/79/e8/7eb2ba188eda14a4b47e33b51f3b4978985f4116655c699bcd18c79279b5/svgwrite-1.3.1.zip
cd dist/working && unzip svgwrite*
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides fuzzy date parsing for construction sequencing
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
cd dist/working && tar -xzvf python-dateutil*
cp -r dist/working/python-dateutil-2.8.1/dateutil dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides duration parsing for construction sequencing
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz
cd dist/working && tar -xzvf isodate*
cp -r dist/working/isodate-0.6.0/src/isodate dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides networkx graph analysis for project dependency calculations
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b0/21/adfbf6168631e28577e4af9eb9f26d75fe72b2bb1d33762a5f2c425e6c2a/networkx-2.5.1.tar.gz
cd dist/working && tar -xzvf networkx*
cp -r dist/working/networkx-2.5.1/networkx dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by networkx
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz
cd dist/working && tar -xzvf decorator*
cp -r dist/working/decorator-5.0.9/src/decorator.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides audio playback for costing
mkdir -p dist/working
git clone https://github.com/Andrej730/aud.git --branch master-reduced-size --depth 1 dist/working/aud
@@ -164,13 +279,112 @@ endif
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
rm -rf dist/working
# Required by IFCDiff
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/0f/ca/caead2949fbb824c7142e3774fa841aa853bb4d4331b440da8c8514dfc6f/deepdiff-5.8.1.tar.gz
cd dist/working && tar -xzvf deepdiff*
cp -r dist/working/deepdiff-5.8.1/deepdiff dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by deepdiff
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/00/55/ce2cbc6d64034b30cad81a29ba61bdba456f190f5e83c09831304bf68d6b/jsonpickle-1.2.tar.gz
cd dist/working && tar -xzvf jsonpickle*
cp -r dist/working/jsonpickle-1.2/jsonpickle dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by deepdiff
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/a3/b7/d4d69641cbe707a45c23b190f2d717466ba5accc4c70b5f7a8a450387895/ordered-set-3.1.1.tar.gz
cd dist/working && tar -xzvf ordered-set*
cp -r dist/working/ordered-set-3.1.1/ordered_set.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by lark
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/00/32/8076fa13e832bb4dcff379f18f228e5a53412be0631808b9ca2610c0f566/pyparsing-2.4.5.tar.gz
cd dist/working && tar -xzvf pyparsing*
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/5b/ef/f97c3e1a7efa00e989a793fe15297214fc95ad7d9e3810586bd08ce9f0f3/xmlschema-2.0.2.tar.gz
cd dist/working && tar -xzvf xmlschema*
cp -r dist/working/xmlschema-2.0.2/xmlschema dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/ad/c7/17c9d16320d8e2cfdb27d2fd298b5e8f7a3f211025b0c1bc7a39a85bd690/xsdata-22.11.tar.gz
cd dist/working && tar -xzvf xsdata*
cp -r dist/working/xsdata-22.11/xsdata dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/11/bc/5afb61dd5d863e5cf77cd952445c50c17e65953405986f19e97e4389692a/elementpath-3.0.2.tar.gz
cd dist/working && tar -xzvf elementpath*
cp -r dist/working/elementpath-3.0.2/elementpath dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/21/9f/b251f7f8a76dec1d6651be194dfba8fb8d7781d10ab3987190de8391d08e/six-1.14.0.tar.gz
cd dist/working && tar -xzvf six*
cp -r dist/working/six-1.14.0/six.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCSV and ifcopenshell.util.selector
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/18/4d/8d522136c37d9e1ea74062b41b8d5e1318ebf45063ae46ce72ed60af223b/lark-parser-0.8.5.tar.gz
cd dist/working && tar -xzvf lark-parser*
cp -r dist/working/lark-parser-0.8.5/lark dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFC4D
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/b0/bb/9c4dddd3ca173cb56241cfb2eddfae24690dc676d357ac4cab17d0a36d9d/PyP6Xer-1.13.0.tar.gz
cd dist/working && tar -xzvf PyP6Xer*
cp -r dist/working/PyP6Xer-1.13.0/xerparser dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by QTOCalculator
mkdir dist/working
cd dist/working && wget $(SHAPELY_URL)
cd dist/working && cp *.whl shapely.zip && unzip shapely.zip
cp -r dist/working/shapely dist/blenderbim/libs/site/packages/
ifeq ($(PLATFORM), win)
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
endif
ifeq ($(PLATFORM), linux)
cp -r dist/working/shapely.libs dist/blenderbim/libs/site/packages/
endif
rm -rf dist/working
# Required by the BIM tool thumbnail generator
mkdir dist/working
cd dist/working && wget $(PILLOW_URL)
cd dist/working && cp *.whl pillow.zip && unzip pillow.zip
cp -r dist/working/PIL dist/blenderbim/libs/site/packages/
ifeq ($(PLATFORM), linux)
cp -r dist/working/Pillow.libs dist/blenderbim/libs/site/packages/
endif
rm -rf dist/working
# Required by xerparser and IFC4D
# TODO: remove this dependency. It's only used to show a progress bar.
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/9f/7b/76c4e5ef1a1b528fcaada4133f972e77d33c252831676cf414119ca6093d/tqdm-4.50.0.tar.gz
cd dist/working && tar -xzvf tqdm*
cp -r dist/working/tqdm-4.50.0/tqdm dist/blenderbim/libs/site/packages/
rm -rf dist/working
mkdir dist/working
cd dist/working && $(PYTHON) -m venv env
cd dist/working && mkdir site-packages
# Required by IfcCSV
cd dist/working && . env/bin/activate && $(PIP) install pandas $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
# Pandas ships with very heavy tests
cd dist/working/site-packages/pandas && rm -rf tests
cd dist/working && . env/bin/activate && $(PIP) install openpyxl --target=./site-packages
cd dist/working && . env/bin/activate && $(PIP) install odfpy --target=./site-packages
# Required by IFCCityJSON
@@ -178,39 +392,161 @@ endif
# Provides express rule validation for ifcopenshell.validate
cd dist/working && . env/bin/activate && $(PIP) install pytest --target=./site-packages
# Provides Brickschema functionality
cd dist/working && . env/bin/activate && $(PIP) install "brickschema[persistence]==0.7.6a2" --target=./site-packages
# Required for SVG to DXF conversion
cd dist/working && . env/bin/activate && $(PIP) install ezdxf --target=./site-packages
# Required by bcf
cd dist/working && . env/bin/activate && $(PIP) install xsdata --target=./site-packages
cd dist/working && . env/bin/activate && $(PIP) install xmlschema --target=./site-packages
cd dist/working && . env/bin/activate && $(PIP) install elementpath --target=./site-packages
cd dist/working && . env/bin/activate && $(PIP) install six --target=./site-packages
# Required by drawing module
cd dist/working && . env/bin/activate && $(PIP) install lxml $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
# Required by qto and drawing module
cd dist/working && . env/bin/activate && $(PIP) install shapely $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
# Required by the BIM type manager thumbnail generator
cd dist/working && . env/bin/activate && $(PIP) install pillow $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
# Provides mustache templating in construction docs and web UI data
cd dist/working && . env/bin/activate && $(PIP) install pystache --target=./site-packages
# Provides SVG export in construction documentation
cd dist/working && . env/bin/activate && $(PIP) install svgwrite --target=./site-packages
# Provides fuzzy date parsing for construction sequencing
cd dist/working && . env/bin/activate && $(PIP) install python-dateutil --target=./site-packages
# Provides duration parsing for construction sequencing
cd dist/working && . env/bin/activate && $(PIP) install isodate --target=./site-packages
# Provides networkx graph analysis for project dependency calculations
cd dist/working && . env/bin/activate && $(PIP) install networkx --target=./site-packages
# Required by IFCDiff
cd dist/working && . env/bin/activate && $(PIP) install deepdiff --target=./site-packages
# Required by IFCCSV and ifcopenshell.util.selector
cd dist/working && . env/bin/activate && $(PIP) install lark --target=./site-packages
# Required by IFC4D
cd dist/working && . env/bin/activate && $(PIP) install PyP6Xer --target=./site-packages
cd dist/working && . env/bin/activate && $(PIP) install brickschema[persistence] --target=./site-packages
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCClash
mkdir dist/working
cd dist/working && wget $(HPPFCL_URL)
cd dist/working && unzip hpp-fcl*
cd dist/working && tar -I zstd -xvf pkg*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/$(PYLIBDIR)/site-packages/hppfcl dist/blenderbim/libs/site/packages/
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/$(PYLIBDIR)/site-packages/hppfcl dist/blenderbim/libs/site/packages/
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/$(PYLIBDIR)/site-packages/hppfcl dist/blenderbim/libs/site/packages/
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Lib/site-packages/hppfcl dist/blenderbim/libs/site/packages/
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(ASSIMP_URL)
cd dist/working && tar -xf assimp*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(QHULL_URL)
cd dist/working && tar -xf qhull*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(EIGENPY_URL)
cd dist/working && unzip eigenpy*
cd dist/working && tar -I zstd -xvf pkg*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(OCTOMAP_URL)
cd dist/working && tar -xf octomap*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(BOOST_URL)
cd dist/working && tar -xf boost*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(BOOSTCPP_URL)
cd dist/working && unzip boost-cpp*
cd dist/working && tar -I zstd -xvf pkg*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/libboost_serialization.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/libboost_serialization.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/libboost_serialization.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/boost_serialization.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by hpp-fcl
mkdir dist/working
cd dist/working && wget $(ZLIB_URL)
cd dist/working && tar -xf zlib*
ifeq ($(PLATFORM), linux)
cp -r dist/working/lib/*.so* dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macos)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), macosm1)
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
endif
ifeq ($(PLATFORM), win)
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
endif
rm -rf dist/working
# Required by BIMTester
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
@@ -226,6 +562,24 @@ endif
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/reporter/junit.py < junit.patch
rm -rf dist/working
# Required by ids - uh, not any more, but I'm trying it out in the drawing module
ifeq ($(PLATFORM), macosm1)
# No wheels available for MacOS M1, but turns out this Anaconda build works
mkdir dist/working
cd dist/working && wget $(LXML_URL)
cd dist/working && tar -xf lxml*
cp -r dist/working/lib/$(PYLIBDIR)/site-packages/lxml dist/blenderbim/libs/site/packages/
rm -rf dist/working
else
# Wheels are preferred for other OSes. A first attempt at using Anaconda
# builds for everything shows it breaks on windows. See #2422.
mkdir dist/working
cd dist/working && wget $(LXML_URL)
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
cp -r dist/working/lxml dist/blenderbim/libs/site/packages/
rm -rf dist/working
endif
# Required by behave
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
@@ -240,6 +594,34 @@ endif
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by augin
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
cd dist/working && tar -xzvf boto3*
cd dist/working/boto3-1.17.17/ && cp -r boto3 ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/7f/2e/e02fdfd0f0377c4e44e61de27b05d5cfe93575770661ef9ded80ed90fa88/botocore-1.20.17.tar.gz
cd dist/working && tar -xzvf botocore*
cd dist/working/botocore-1.20.17/ && cp -r botocore ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/3c/56/3f325b1eef9791759784aa5046a8f6a1aff8f7c898a2e34506771d3b99d8/jmespath-0.10.0.tar.gz
cd dist/working && tar -xzvf jmespath*
cd dist/working/jmespath-0.10.0/ && cp -r jmespath ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/08/e1/3ee2096ebaeeb8c186d20ed16c8faf4a503913e5c9a0e14cd6b8ffc405a3/s3transfer-0.3.4.tar.gz
cd dist/working && tar -xzvf s3transfer*
cd dist/working/s3transfer-0.3.4/ && cp -r s3transfer ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides IFCJSON functionality
mkdir dist/working
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
@@ -247,6 +629,13 @@ endif
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides OpenLCA functionality
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/85/33/f91b96e9e8608ff65a003b692e8a9cdd60f2178f60617e5b1d21334c009c/olca-ipc-0.0.10.tar.gz
cd dist/working && tar -xzvf olca-ipc*
cd dist/working/olca-ipc-0.0.10/ && cp -r olca ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Brickschema functionality
mkdir dist/working
# For now lets bundle the latest nightly schema
@@ -257,7 +646,7 @@ endif
# Required by brickschema
# This is a bit of a dodgy one, it should be provided by setuptools which Blender doesn't ship.
mkdir dist/working
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/raw/v0.7.0/src/blenderbim/scripts/pkg_resources.tar.gz
cd dist/working && wget https://blenderbim.org/builds/pkg_resources.tar.gz
cd dist/working && tar -xzvf pkg_resources*
cd dist/working/ && cp -r pkg_resources ../blenderbim/libs/site/packages/
rm -rf dist/working
@@ -272,17 +661,11 @@ endif
# Required for Desktop icon and file association
cp -r blenderbim/libs/desktop dist/blenderbim/libs/
# Generate translations module for BBIM build
git clone https://github.com/IfcOpenShell/blenderbim-translations.git dist/working
$(PYTHON) scripts/bbim_translations.py -i "dist/working" -o "dist/blenderbim"
rm -rf dist/working
# Remove dependencies also bundled with Blender
rm -rf dist/blenderbim/libs/site/packages/numpy
rm -rf dist/blenderbim/libs/site/packages/numpy.libs
cd dist/blenderbim && $(SED) "s/999999/$(VERSION)/" __init__.py
cd dist/blenderbim && $(SED) "s/999999/$(VERSION)/" blender_manifest.toml
cd dist/blenderbim/bim && $(SED) "s/8888888/$(LAST_COMMIT_HASH)/" __init__.py
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
+8 -169
View File
@@ -18,21 +18,7 @@
import os
import sys
# Ensure we don't try to import bpy or blenderbim.bim
# to support running core tests.
# We assume if bpy was never loaded in current python session
# then we're not in Blender. It's still possible to use
# bpy in core and core tests for annotations using TYPE_CHECKING.
IN_BLENDER = sys.modules.get("bpy", None)
if IN_BLENDER:
import bpy
import platform
import traceback
import subprocess
import webbrowser
import addon_utils
from collections import deque
import site
bl_info = {
"name": "BlenderBIM",
@@ -41,168 +27,21 @@ bl_info = {
"blender": (3, 1, 0),
"version": (0, 0, 999999),
"location": "File Menu, Scene Properties Tab. See documentation for more.",
"doc_url": "https://docs.blenderbim.org/",
"doc_url": "https://blenderbim.org/docs",
"tracker_url": "https://github.com/IfcOpenShell/IfcOpenShell/issues",
"category": "System",
}
last_error = None
last_actions: deque = deque(maxlen=10)
def get_debug_info():
version = ".".join(
[
str(x)
for x in [
addon.bl_info.get("version", (-1, -1, -1))
for addon in addon_utils.modules()
if addon.bl_info["name"] == "BlenderBIM"
][0]
]
)
return {
"os": platform.system(),
"os_version": platform.version(),
"python_version": platform.python_version(),
"architecture": platform.architecture(),
"machine": platform.machine(),
"processor": platform.processor(),
"blender_version": bpy.app.version_string,
"blenderbim_version": version,
"last_actions": last_actions,
"last_error": last_error,
}
def format_debug_info(info: dict):
last_actions = ""
for action in info["last_actions"]:
last_actions += f"\n# {action['type']}: {action['name']}"
if settings := action.get("settings"):
last_actions += f"\n>>> {settings}"
info["last_actions"] = last_actions
text = "\n".join(f"{k}: {v}" for k, v in info.items())
return text.strip()
if IN_BLENDER:
if sys.modules.get("bpy", None):
# Process *.pth in /libs/site/packages to setup globally importable modules
# This is 3 levels deep as required by the static RPATH of ../../ from dependencies taken from Anaconda
# site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
try:
import blenderbim.bim
import ifcopenshell.api
import blenderbim.bim
def log_api(usecase_path, ifc_file, settings):
last_actions.append(
{
"type": "ifcopenshell.api",
"name": usecase_path,
"settings": ifcopenshell.api.serialise_settings(settings),
}
)
def register():
blenderbim.bim.register()
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
def register():
blenderbim.bim.register()
def unregister():
blenderbim.bim.unregister()
except:
last_error = traceback.format_exc()
print(last_error)
print(format_debug_info(get_debug_info()))
print("\nFATAL ERROR: Unable to load the BlenderBIM Add-on")
class BIM_PT_fatal_error(bpy.types.Panel):
bl_label = "BlenderBIM Fatal Error"
bl_idname = "SCENE_PT_error_message"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
info = get_debug_info()
layout = self.layout
layout.alert = True
layout.label(text="BlenderBIM could not load.", icon="ERROR")
if info["os"] == "Windows":
layout.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
else:
layout.label(text="View the console for full logs.", icon="CONSOLE")
box = layout.box()
py = ".".join(info["python_version"].split(".")[0:2])
b3d = ".".join(info["blender_version"].split(".")[0:2])
box.label(text=f"Blender {b3d} {info['os']} {info['machine']}", icon="BLENDER")
box.label(text=f"Python {py} BBIM {info['blenderbim_version']}", icon="SCRIPTPLUGINS")
layout.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
op = layout.operator("bim.open_uri", text="How Can I Fix This?")
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html#installation-issues"
layout.label(text="Try Reinstalling:", icon="IMPORT")
op = layout.operator("bim.open_uri", text="Re-download Add-on")
bbim_date = info["blenderbim_version"].split(".")[-1]
py_tag = py.replace(".", "")
if "Linux" in info["os"]:
os = "linux"
elif "Darwin" in info["os"]:
if "arm64" in info["machine"]:
os = "macosm1"
else:
os = "macos"
else:
os = "win"
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_date}/blenderbim-{bbim_date}-py{py_tag}-{os}.zip"
class OpenUri(bpy.types.Operator):
bl_idname = "bim.open_uri"
bl_label = "Open URI"
uri: bpy.props.StringProperty()
def execute(self, context):
webbrowser.open(self.uri)
return {"FINISHED"}
class CopyDebugInformation(bpy.types.Operator):
bl_idname = "bim.copy_debug_information"
bl_label = "Copy Debug Information"
bl_description = "Copies debugging information to your clipboard for use in bugreports"
def execute(self, context):
info = format_debug_info(get_debug_info())
context.window_manager.clipboard = info
return {"FINISHED"}
class HiddenPanel:
@classmethod
def false_poll(cls, context):
return False
def register():
# Only show our error panel and nothing else in the scene tab
for item_name in dir(bpy.types):
item = getattr(bpy.types, item_name)
if not hasattr(item, "bl_rna") or not isinstance(item.bl_rna, bpy.types.Panel):
continue
if getattr(item, "bl_context", None) != "scene":
continue
# Reregister scene panel with a new poll to hide it
item.poll = HiddenPanel.false_poll
bpy.utils.unregister_class(item)
bpy.utils.register_class(item)
bpy.utils.register_class(BIM_PT_fatal_error)
bpy.utils.register_class(CopyDebugInformation)
bpy.utils.register_class(OpenUri)
def unregister():
bpy.utils.unregister_class(OpenUri)
bpy.utils.unregister_class(CopyDebugInformation)
bpy.utils.unregister_class(BIM_PT_fatal_error)
def unregister():
blenderbim.bim.unregister()
+31 -55
View File
@@ -17,18 +17,13 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
from pathlib import Path
import bpy
import bpy.utils.previews
import blenderbim
import importlib
from pathlib import Path
from . import handler, ui, prop, operator, helper
from typing import Callable, Union
try:
from blenderbim.translations import translations_dict
except ImportError:
translations_dict = {}
cwd = os.path.dirname(os.path.realpath(__file__))
modules = {
@@ -49,7 +44,6 @@ modules = {
"spatial": None,
"void": None,
"aggregate": None,
"nest": None,
"geometry": None,
"fm": None,
"resource": None,
@@ -73,6 +67,7 @@ modules = {
"document": None,
"pset_template": None,
"clash": None,
"lca": None,
"csv": None,
"tester": None,
"bimtester": None,
@@ -97,33 +92,28 @@ for name in modules.keys():
classes = [
operator.AddIfcFile,
operator.BIM_OT_add_section_plane,
operator.BIM_OT_delete_object,
operator.BIM_OT_open_webbrowser,
operator.BIM_OT_remove_section_plane,
operator.BIM_OT_select_object,
operator.BIM_OT_show_description,
operator.ClippingPlaneCutWithCappings,
operator.CloseError,
operator.EditBlenderCollection,
operator.FileAssociate,
operator.FileUnassociate,
operator.OpenUpstream,
operator.OpenUri,
operator.SwitchTab,
operator.SetTab,
operator.ReloadIfcFile,
operator.RemoveIfcFile,
operator.RevertClippingPlaneCut,
operator.SelectDataDir,
operator.SelectIfcFile,
operator.ReloadSelectedIfcFile,
operator.SelectSchemaDir,
operator.FileAssociate,
operator.FileUnassociate,
operator.SelectURIAttribute,
operator.SetTab,
operator.SwitchTab,
operator.EditBlenderCollection,
operator.BIM_OT_open_webbrowser,
operator.BIM_OT_show_description,
prop.StrProperty,
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
prop.ObjProperty,
prop.Attribute,
prop.BIMAreaProperties,
prop.BIMTabProperties,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
@@ -132,46 +122,33 @@ classes = [
prop.BIMCollectionProperties,
prop.BIMMaterialProperties,
prop.BIMMeshProperties,
prop.BIMFacet,
prop.BIMFilterGroup,
ui.BIM_UL_clipping_plane,
ui.BIM_UL_generic,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
# Project overview
ui.BIM_PT_project_info,
ui.BIM_PT_project_setup,
ui.BIM_PT_geometry,
ui.BIM_PT_tab_grouping_and_filtering,
# Tabs panel
ui.BIM_PT_tabs,
# Project overview
ui.BIM_PT_tab_project_info,
ui.BIM_PT_tab_spatial_decomposition,
ui.BIM_PT_tab_project_setup,
ui.BIM_PT_tab_geometry,
ui.BIM_PT_tab_stakeholders,
ui.BIM_PT_tab_grouping_and_filtering,
# Object information
ui.BIM_PT_tab_object_metadata,
ui.BIM_PT_tab_misc,
# Geometry and materials
ui.BIM_PT_tab_placement,
ui.BIM_PT_tab_representations,
ui.BIM_PT_tab_geometric_relationships,
ui.BIM_PT_tab_parametric_geometry,
ui.BIM_PT_tab_object_materials,
ui.BIM_PT_tab_materials,
ui.BIM_PT_tab_styles,
ui.BIM_PT_tab_profiles,
# Drawings and documents
ui.BIM_PT_tab_sheets,
ui.BIM_PT_tab_drawings,
ui.BIM_PT_tab_schedules,
ui.BIM_PT_tab_references,
# Services and systems
ui.BIM_PT_tab_services,
ui.BIM_PT_tab_zones,
ui.BIM_PT_tab_services_object,
# Structural analysis
ui.BIM_PT_tab_structural,
# Construction scheduling
ui.BIM_PT_tab_status,
ui.BIM_PT_tab_qto,
ui.BIM_PT_tab_resources,
ui.BIM_PT_tab_cost,
ui.BIM_PT_tab_sequence,
@@ -185,7 +162,6 @@ classes = [
ui.BIM_PT_tab_sandbox,
# TODO: move this somewhere else and clean it up
ui.BIM_PT_section_plane,
ui.BIM_PT_section_with_cappings,
]
for mod in modules.values():
@@ -195,7 +171,6 @@ addon_keymaps = []
icons = None
is_registering = False
last_commit_hash = "8888888"
original_scene_panels_polls: dict[bpy.types.Panel, Union[Callable, None]] = dict()
def on_register(scene):
@@ -220,7 +195,6 @@ def register():
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties)
bpy.types.Screen.BIMTabProperties = bpy.props.PointerProperty(type=prop.BIMTabProperties)
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
@@ -238,8 +212,8 @@ def register():
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type="EMPTY")
kmi = km.keymap_items.new("bim.switch_tab", "TAB", "PRESS", ctrl=True)
km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY')
kmi = km.keymap_items.new('bim.switch_tab', 'TAB', 'PRESS', ctrl=True)
addon_keymaps.append((km, kmi))
global icons
@@ -257,16 +231,12 @@ def register():
global last_commit_hash
try:
import git
path = Path(__file__).resolve().parent
repo = git.Repo(str(path), search_parent_directories=True)
last_commit_hash = repo.head.object.hexsha
except:
pass
bpy.app.translations.register("blenderbim", translations_dict)
def unregister():
global icons
@@ -303,10 +273,16 @@ def unregister():
km.keymap_items.remove(kmi)
addon_keymaps.clear()
import blenderbim.tool as tool
# use tuple() as method will be removing keys from dict
for panel in tuple(original_scene_panels_polls.keys()):
tool.Blender.remove_scene_panel_override(panel)
bpy.app.translations.unregister("blenderbim")
for panel in [
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_custom_props",
]:
try:
bpy.utils.unregister_class(getattr(handler, f"Override_{panel}"))
except:
pass
@@ -10,7 +10,7 @@
* (at your option) any later version.
*
* BlenderBIM Add-on is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
@@ -10,7 +10,7 @@
* (at your option) any later version.
*
* BlenderBIM Add-on is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
@@ -1,45 +0,0 @@
/*
* BlenderBIM Add-on - OpenBIM Blender Add-on
* Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
*
* This file is part of BlenderBIM Add-on.
*
* BlenderBIM Add-on is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BlenderBIM Add-on is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* You may copy this `schedule.css` template alongside your input schedule
* document with the same filename. For example if you have a schedule called
* `door_types.ods`, you can create a `door_types.css` in the same folder to
* style that schedule.
*/
/**
* If you specify a font size in CSS, such as text { font-size: 5; }, all fonts
* will be overriden to match that size.
*
* If your CSS, ODS, XLSX does not specify a font size, the variables below
* will specify the default font size.
*
* If your ODS, XLSX does specify a font size, they will scale linearly based
* on the variables below.
*/
:root {
--font-size-pt: 12;
--font-size-px: 4.13;
--font-width: 0.45;
}
text, tspan { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT', 'DejaVu Sans Condensed', 'Liberation Sans', 'Arial Narrow', 'Arial'; }
@@ -1,17 +1,17 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('/dev/null','2024-03-03T16:03:58+11:00',(),(),'IfcOpenShell v0.7.0-eaa2aa05a','IfcOpenShell v0.7.0-eaa2aa05a','Nobody');
FILE_NAME('/dev/null','2023-09-07T13:40:06+10:00',(),(),'IfcOpenShell v0.7.0-fbd8ea1ed','IfcOpenShell v0.7.0-fbd8ea1ed','Nobody');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('3NJUxHyTzEv96oezyDYEqf',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
#2=IFCPROJECTLIBRARY('0D39k7hbT5ce$jWaCd2niG',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
#3=IFCRELDECLARES('2RWVohyP57PBHT9zCs4H2O',$,$,$,#1,(#2));
#1=IFCPROJECT('1arVsMni92iPUaaryGTUz2',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
#2=IFCPROJECTLIBRARY('1FfDG5b21DuBDOO4eUE9rF',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
#3=IFCRELDECLARES('3tfDIC3Tf9wP8EUOoFeycK',$,$,$,#1,(#2));
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#6=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#7=IFCUNITASSIGNMENT((#6,#4,#5));
#7=IFCUNITASSIGNMENT((#5,#6,#4));
#8=IFCCARTESIANPOINT((0.,0.,0.));
#9=IFCDIRECTION((0.,0.,1.));
#10=IFCDIRECTION((1.,0.,0.));
@@ -23,84 +23,84 @@ DATA;
#16=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#15,$);
#17=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#12,$,.MODEL_VIEW.,$);
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
#19=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#16,$,.MODEL_VIEW.,$);
#19=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
#20=IFCMATERIAL('Unknown',$,$);
#21=IFCWALLTYPE('2F1k78lI53fwS0rg3rqrRb',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
#21=IFCWALLTYPE('2o8NqQQbHA5BeErrgwfr7d',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
#22=IFCMATERIALLAYERSET((#24),$,$);
#23=IFCRELASSOCIATESMATERIAL('0khP_Smdj12f2JrOTRbRKj',$,$,$,(#21),#22);
#23=IFCRELASSOCIATESMATERIAL('1Q$5DZKr98n82Eitz2dWey',$,$,$,(#21),#22);
#24=IFCMATERIALLAYER(#20,0.05,$,$,$,$,$);
#25=IFCRELDECLARES('31JriXdev6BP2sC9lcxdC1',$,$,$,#2,(#26,#48,#72,#59,#92,#30,#96,#63,#21,#54,#87,#210,#153,#34,#67,#38,#82,#77,#42));
#26=IFCWALLTYPE('3R5onkTtX1IxdCMPoThRaw',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
#25=IFCRELDECLARES('1KXKUEn8z0g8lvyEsCTWD3',$,$,$,#2,(#38,#82,#77,#42,#26,#48,#72,#59,#92,#30,#96,#63,#21,#54,#87,#153,#34,#67,#210));
#26=IFCWALLTYPE('3KlIOv_P9A79tkt8D_jq_m',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
#27=IFCMATERIALLAYERSET((#29),$,$);
#28=IFCRELASSOCIATESMATERIAL('0$mXRPCT16nP6Q70XFNvZv',$,$,$,(#26),#27);
#28=IFCRELASSOCIATESMATERIAL('32B9Wx8Z1FxhIN7m77MiHG',$,$,$,(#26),#27);
#29=IFCMATERIALLAYER(#20,0.1,$,$,$,$,$);
#30=IFCWALLTYPE('1juanculbDJPqvqT2FCm8m',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
#30=IFCWALLTYPE('2DqrSeel1AGw6h3b$ujafZ',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
#31=IFCMATERIALLAYERSET((#33),$,$);
#32=IFCRELASSOCIATESMATERIAL('0FsB2A$frFn9AZW2IG38ny',$,$,$,(#30),#31);
#32=IFCRELASSOCIATESMATERIAL('2FeYUpt3D4tAvwgGmwXWZn',$,$,$,(#30),#31);
#33=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
#34=IFCWALLTYPE('05UYduR9r1vRxZZgP64Rdw',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
#34=IFCWALLTYPE('2jNdEOFIP1eQb3WZePlFGY',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
#35=IFCMATERIALLAYERSET((#37),$,$);
#36=IFCRELASSOCIATESMATERIAL('2FYhYrvmv4SBoZHVWDTTte',$,$,$,(#34),#35);
#36=IFCRELASSOCIATESMATERIAL('2bFeM6xevAwucuwMlauX77',$,$,$,(#34),#35);
#37=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
#38=IFCCOVERINGTYPE('0iLxgfHB9F2hRH2vMcw7Yv',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
#38=IFCCOVERINGTYPE('3310gCgH59w9tu$DfmiCEN',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
#39=IFCMATERIALLAYERSET((#41),$,$);
#40=IFCRELASSOCIATESMATERIAL('0yvFX0zgb9uvJ3ARnf4mfe',$,$,$,(#38),#39);
#40=IFCRELASSOCIATESMATERIAL('3pTNmWd_jBoOWyHc8Yf4qI',$,$,$,(#38),#39);
#41=IFCMATERIALLAYER(#20,0.01,$,$,$,$,$);
#42=IFCCOVERINGTYPE('15Riw2WTrAUPwvfLyKXJ2g',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
#42=IFCCOVERINGTYPE('2sksWFuPzCgeNk1ofXS6PZ',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
#43=IFCMATERIALLAYERSET((#45),$,$);
#44=IFCRELASSOCIATESMATERIAL('1iAOG6NK9FBxitFtzN9qP5',$,$,$,(#42),#43);
#44=IFCRELASSOCIATESMATERIAL('36rl5NddT1ux6ejkQAkT2z',$,$,$,(#42),#43);
#45=IFCMATERIALLAYER(#20,0.02,$,$,$,$,$);
#46=IFCPROPERTYSET('18ICCZ0fjDG9UXMyzrsFBA',$,'EPset_Parametric',$,(#47));
#46=IFCPROPERTYSET('2VZJ9xfdn1NeOY6uEmnnYW',$,'EPset_Parametric',$,(#47));
#47=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$);
#48=IFCCOVERINGTYPE('0gEOtYULD0F9KD9zPoA6cJ',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
#48=IFCCOVERINGTYPE('1uU6rxt95AC91lZ0jTRG6G',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
#49=IFCMATERIALLAYERSET((#51),$,$);
#50=IFCRELASSOCIATESMATERIAL('0xd6dsjf13JA98q9fnAMpQ',$,$,$,(#48),#49);
#50=IFCRELASSOCIATESMATERIAL('2lFv4xQYr5bwtiSsMTVCgz',$,$,$,(#48),#49);
#51=IFCMATERIALLAYER(#20,0.03,$,$,$,$,$);
#52=IFCPROPERTYSET('3qCbs1tuDCvuOXam5RrA$a',$,'EPset_Parametric',$,(#53));
#52=IFCPROPERTYSET('0HnrRhcd16Yfbgtci4sqCG',$,'EPset_Parametric',$,(#53));
#53=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$);
#54=IFCRAMPTYPE('2yW1ePlyb8cOR6HdEntpRh',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
#54=IFCRAMPTYPE('3nonXCVn58jhVV08N8c97B',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
#55=IFCMATERIALLAYERSET((#57),$,$);
#56=IFCRELASSOCIATESMATERIAL('1x9ZyVim19AQtJhfEE06XR',$,$,$,(#54),#55);
#56=IFCRELASSOCIATESMATERIAL('2NdIrmbUP6bh4x$p_RY31C',$,$,$,(#54),#55);
#57=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
#58=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3);
#59=IFCPILETYPE('25MTkMtaH5QeCf$gFbvSSw',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
#59=IFCPILETYPE('2uuf5kq5TDDfO9jGN7XIHh',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
#60=IFCMATERIALPROFILESET($,$,(#62),$);
#61=IFCRELASSOCIATESMATERIAL('09FXUZCnrFEffxBrtg16gx',$,$,$,(#59),#60);
#61=IFCRELASSOCIATESMATERIAL('3hi821Ffj8YR9yFjp226LU',$,$,$,(#59),#60);
#62=IFCMATERIALPROFILE($,$,#20,#58,$,$);
#63=IFCSLABTYPE('1Akbal7tP5DAPW0ti$Qp30',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
#63=IFCSLABTYPE('2janmz3nH4P9IHaOVvGY_B',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
#64=IFCMATERIALLAYERSET((#66),$,$);
#65=IFCRELASSOCIATESMATERIAL('0jzkXlkBL4HhmQ_FTWW6Cj',$,$,$,(#63),#64);
#65=IFCRELASSOCIATESMATERIAL('0og5Zg4ib67g6XWgOgqBWy',$,$,$,(#63),#64);
#66=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
#67=IFCSLABTYPE('0$gpYNi3T08BrxaR67CxgI',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
#67=IFCSLABTYPE('26UKpEaTb9fh4qrqi3Ymzj',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
#68=IFCMATERIALLAYERSET((#70),$,$);
#69=IFCRELASSOCIATESMATERIAL('3a6kjEM29DleYTlSkA8_DK',$,$,$,(#67),#68);
#69=IFCRELASSOCIATESMATERIAL('1BeBXPqen9TQEE42SxzhjF',$,$,$,(#67),#68);
#70=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
#71=IFCRECTANGLEPROFILEDEF(.AREA.,'500x600',$,0.5,0.6);
#72=IFCCOLUMNTYPE('278sjptdDDzgOZseERj390',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
#72=IFCCOLUMNTYPE('1KnWzBBoLCNfJ3_J79LT7d',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
#73=IFCMATERIALPROFILESET($,$,(#75),$);
#74=IFCRELASSOCIATESMATERIAL('1ppYXw39v6kBwi3rrIm4ZE',$,$,$,(#72),#73);
#74=IFCRELASSOCIATESMATERIAL('1W8OWQjEL05gH5hEpve6Lh',$,$,$,(#72),#73);
#75=IFCMATERIALPROFILE($,$,#20,#71,$,$);
#76=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,'500.0x5.0 CHS',$,0.25,0.005);
#77=IFCCOLUMNTYPE('3aSZOvGmr7jggSNwsq5PJE',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
#77=IFCCOLUMNTYPE('33jPFERfL9bfTeWfTL5kZ7',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
#78=IFCMATERIALPROFILESET($,$,(#80),$);
#79=IFCRELASSOCIATESMATERIAL('1AtNJPaD14DgINbAjXvbU4',$,$,$,(#77),#78);
#79=IFCRELASSOCIATESMATERIAL('3czOrQw2v9BfS0PeQCrzWq',$,$,$,(#77),#78);
#80=IFCMATERIALPROFILE($,$,#20,#76,$,$);
#81=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,'150x75x2.0 RHS',$,0.075,0.15,0.002,0.005,0.005);
#82=IFCCOLUMNTYPE('28iv3Kru12yQ7R7RRwNjvD',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
#82=IFCCOLUMNTYPE('21YcNdKNj2$95U55yNx1Nw',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
#83=IFCMATERIALPROFILESET($,$,(#85),$);
#84=IFCRELASSOCIATESMATERIAL('2M_oL$n3j6rBRnOW5RSk87',$,$,$,(#82),#83);
#84=IFCRELASSOCIATESMATERIAL('3n6Cru3xr6jvDptU9u7QEF',$,$,$,(#82),#83);
#85=IFCMATERIALPROFILE($,$,#20,#81,$,$);
#86=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$);
#87=IFCBEAMTYPE('2E$V5l4b54dxuToJ1A6IHp',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
#87=IFCBEAMTYPE('2CsGpD$6nDCxWv9jXTtvq9',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
#88=IFCMATERIALPROFILESET($,$,(#90),$);
#89=IFCRELASSOCIATESMATERIAL('3PUNnY7cj8LhyXbHKVqZnf',$,$,$,(#87),#88);
#89=IFCRELASSOCIATESMATERIAL('2LJExc74j9rgBSBx51HDPK',$,$,$,(#87),#88);
#90=IFCMATERIALPROFILE($,$,#20,#86,$,$);
#91=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005);
#92=IFCBEAMTYPE('3qutoZTvP0lvLWNXyjPhPm',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
#92=IFCBEAMTYPE('32pHN2b0P0awGw$U8PpneO',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
#93=IFCMATERIALPROFILESET($,$,(#95),$);
#94=IFCRELASSOCIATESMATERIAL('3dGyQf42H2ahch2bSYPPHP',$,$,$,(#92),#93);
#94=IFCRELASSOCIATESMATERIAL('2wg8R1Drb2xhQ2Y_pRSh3c',$,$,$,(#92),#93);
#95=IFCMATERIALPROFILE($,$,#20,#91,$,$);
#96=IFCWINDOWTYPE('0bK3c4PWL3eOMNwkPN$rlg',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#96=IFCWINDOWTYPE('1Gg9HfZEr69u8SdWfDs2J3',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#97=IFCINDEXEDPOLYGONALFACE((13,17,18,14));
#98=IFCINDEXEDPOLYGONALFACE((5,6,3,4));
#99=IFCINDEXEDPOLYGONALFACE((7,8,2,1));
@@ -157,7 +157,7 @@ DATA;
#150=IFCDIRECTION((0.,0.,1.));
#151=IFCAXIS2PLACEMENT3D(#148,#150,#149);
#152=IFCREPRESENTATIONMAP(#151,#147);
#153=IFCDOORTYPE('2j0vIBgkH0ERnBCP0cpcCz',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#153=IFCDOORTYPE('0NBUmPKyT9WecsIeYJrEqg',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
#154=IFCINDEXEDPOLYGONALFACE((17,16,15,14));
#155=IFCINDEXEDPOLYGONALFACE((2,3,29,28));
#156=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31));
@@ -214,7 +214,7 @@ DATA;
#207=IFCDIRECTION((0.,0.,1.));
#208=IFCAXIS2PLACEMENT3D(#205,#207,#206);
#209=IFCREPRESENTATIONMAP(#208,#204);
#210=IFCFURNITURETYPE('02XxQ_3oT0SPrmFPATrt7o',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
#210=IFCFURNITURETYPE('3Kyc6IyarAUw3_8fkNtXIg',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
#211=IFCINDEXEDPOLYGONALFACE((187,278,44));
#212=IFCINDEXEDPOLYGONALFACE((21,52,60));
#213=IFCINDEXEDPOLYGONALFACE((91,100,31));
@@ -957,35 +957,35 @@ DATA;
#950=IFCDIRECTION((0.,0.,1.));
#951=IFCAXIS2PLACEMENT3D(#948,#950,#949);
#952=IFCREPRESENTATIONMAP(#951,#947);
#953=IFCTYPEPRODUCT('0UrP0fLdD5OwzwD41aRKao',$,'SETOUT-POINT',$,'IfcAnnotation/SYMBOL',(#954),$,$);
#954=IFCPROPERTYSET('27lmSbeAXC08EWkEq8XdUG',$,'EPset_Annotation',$,(#955));
#953=IFCTYPEPRODUCT('0TBMBnD_b66QLUWKD9HLsc',$,'SETOUT-POINT',$,'IfcAnnotation/SYMBOL',(#954),$,$);
#954=IFCPROPERTYSET('2dQzX_K4r1Xet6dz9zBlgs',$,'EPset_Annotation',$,(#955));
#955=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('setout-point'),$);
#956=IFCTYPEPRODUCT('3IQrQOSFP0WfkuLk0ak7_0',$,'CONTROL-POINT',$,'IfcAnnotation/SYMBOL',(#957),$,$);
#957=IFCPROPERTYSET('23aN9DsdjFcfWq8KGSIVoN',$,'EPset_Annotation',$,(#958));
#956=IFCTYPEPRODUCT('0vo_7PU3H9ygTnrIipqPRI',$,'CONTROL-POINT',$,'IfcAnnotation/SYMBOL',(#957),$,$);
#957=IFCPROPERTYSET('3APQOw$FP1ivxp08UxHsl6',$,'EPset_Annotation',$,(#958));
#958=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('control-point'),$);
#959=IFCTYPEPRODUCT('3wvbaiaIz8agQgDRzt01vz',$,'TRAVERSE-POINT',$,'IfcAnnotation/SYMBOL',(#960),$,$);
#960=IFCPROPERTYSET('1j7aFM1Rb9BRuKgUEn1U10',$,'EPset_Annotation',$,(#961));
#959=IFCTYPEPRODUCT('32V27G3$T1OO3TbXg6948F',$,'TRAVERSE-POINT',$,'IfcAnnotation/SYMBOL',(#960),$,$);
#960=IFCPROPERTYSET('0cTSYXMc9B$PTXVDrhBYYW',$,'EPset_Annotation',$,(#961));
#961=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('traverse-point'),$);
#962=IFCTYPEPRODUCT('0p5ZTfTnX9ZOywroa7Ffql',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#963),$,$);
#963=IFCPROPERTYSET('27smSHyiv39vhBRSwfDVCD',$,'EPset_Annotation',$,(#964));
#962=IFCTYPEPRODUCT('2PXIC7Bg914gJhRh2XeGnN',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#963),$,$);
#963=IFCPROPERTYSET('0RiYsOxp529gXnCkw44wF8',$,'EPset_Annotation',$,(#964));
#964=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('dashed'),$);
#965=IFCTYPEPRODUCT('2EvrG9Vuf6t9etgMeWFuJ2',$,'FINE',$,'IfcAnnotation/LINEWORK',(#966),$,$);
#966=IFCPROPERTYSET('0gVTuDcZ5ByRdR3sZnEZUR',$,'EPset_Annotation',$,(#967));
#965=IFCTYPEPRODUCT('1Ou1kA3Vb4HhuNBvM0uGe0',$,'FINE',$,'IfcAnnotation/LINEWORK',(#966),$,$);
#966=IFCPROPERTYSET('2gzly9D0L1qPK2MExeXgRO',$,'EPset_Annotation',$,(#967));
#967=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('fine'),$);
#968=IFCTYPEPRODUCT('0IeM1ywXn1qhR_6NhB6N4s',$,'THIN',$,'IfcAnnotation/LINEWORK',(#969),$,$);
#969=IFCPROPERTYSET('0t6c$uCeT9092GYkbm8hDS',$,'EPset_Annotation',$,(#970));
#968=IFCTYPEPRODUCT('3lx$KQPRbEZwbQ7Xdfm5gw',$,'THIN',$,'IfcAnnotation/LINEWORK',(#969),$,$);
#969=IFCPROPERTYSET('0vsVpqs6zArvA2bfBtvQew',$,'EPset_Annotation',$,(#970));
#970=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thin'),$);
#971=IFCTYPEPRODUCT('1jZVbCwrTCGhZdKbH4uqTP',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#972),$,$);
#972=IFCPROPERTYSET('2uTJ11lF98GeN8pPIf340N',$,'EPset_Annotation',$,(#973));
#971=IFCTYPEPRODUCT('00j$y97p903w2HOb35lAQ2',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#972),$,$);
#972=IFCPROPERTYSET('240rEBDOn8PAvfnm_43s55',$,'EPset_Annotation',$,(#973));
#973=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('medium'),$);
#974=IFCTYPEPRODUCT('38zW9E1uH2ae$zat9KrieS',$,'THICK',$,'IfcAnnotation/LINEWORK',(#975),$,$);
#975=IFCPROPERTYSET('2XQxgO16v9vxjvuIJpaPpG',$,'EPset_Annotation',$,(#976));
#974=IFCTYPEPRODUCT('2tVdFGorj6dfrL4uA1kyW8',$,'THICK',$,'IfcAnnotation/LINEWORK',(#975),$,$);
#975=IFCPROPERTYSET('2IEGHncr1D$R8fKA9zCEJP',$,'EPset_Annotation',$,(#976));
#976=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thick'),$);
#977=IFCTYPEPRODUCT('3G2s7ZLzfDJh5J$2iIzHw9',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#978),$,$);
#978=IFCPROPERTYSET('0TcB8Gal96vebTrLWa5CEw',$,'EPset_Annotation',$,(#979));
#977=IFCTYPEPRODUCT('1T5C$$ONTBB8A9a7vv0Gn6',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#978),$,$);
#978=IFCPROPERTYSET('1gRomAS1L2WguA51ZI0E40',$,'EPset_Annotation',$,(#979));
#979=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('strong'),$);
#980=IFCTYPEPRODUCT('3pq2B$0k52ZOv2bFuRYkxO',$,'SETOUT-TAG',$,'IfcAnnotation/TEXT',(#981),(#1000),$);
#981=IFCPROPERTYSET('39$oNFI052cBhtpZCVVLfj',$,'EPset_Annotation',$,(#982));
#980=IFCTYPEPRODUCT('2LmKYrAEr2K8EwJyCAsyc4',$,'SETOUT-TAG',$,'IfcAnnotation/TEXT',(#981),(#1000),$);
#981=IFCPROPERTYSET('0nF9du8qDAF9aldqjCUsbX',$,'EPset_Annotation',$,(#982));
#982=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('setout-tag'),$);
#983=IFCCARTESIANPOINT((0.,0.,0.));
#984=IFCDIRECTION((0.,0.,1.));
@@ -1005,8 +1005,8 @@ DATA;
#998=IFCDIRECTION((0.,0.,1.));
#999=IFCAXIS2PLACEMENT3D(#996,#998,#997);
#1000=IFCREPRESENTATIONMAP(#999,#995);
#1001=IFCTYPEPRODUCT('11M3ahhrr9NBdB29YEazzw',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#1002),(#1021),$);
#1002=IFCPROPERTYSET('3xUNoLKPT9kB4LMdoctm5k',$,'EPset_Annotation',$,(#1003));
#1001=IFCTYPEPRODUCT('1cdhVmqEPF6e13_TFOdlQw',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#1002),(#1021),$);
#1002=IFCPROPERTYSET('1MUPGQolnDEgp0NxcMN56E',$,'EPset_Annotation',$,(#1003));
#1003=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('door-tag'),$);
#1004=IFCCARTESIANPOINT((0.,0.,0.));
#1005=IFCDIRECTION((0.,0.,1.));
@@ -1026,8 +1026,8 @@ DATA;
#1019=IFCDIRECTION((0.,0.,1.));
#1020=IFCAXIS2PLACEMENT3D(#1017,#1019,#1018);
#1021=IFCREPRESENTATIONMAP(#1020,#1016);
#1022=IFCTYPEPRODUCT('1eE8Y$BVDFDgG8Fj6d9wiV',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#1023),(#1036),$);
#1023=IFCPROPERTYSET('2MqzSGkXDBcPSolaAQvYO4',$,'EPset_Annotation',$,(#1024));
#1022=IFCTYPEPRODUCT('0OuDi3gRH2CxW9mrtE0vXw',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#1023),(#1036),$);
#1023=IFCPROPERTYSET('2X7dAo_1P5ruRnlKA4kIl6',$,'EPset_Annotation',$,(#1024));
#1024=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('window-tag'),$);
#1025=IFCCARTESIANPOINT((0.,0.,0.));
#1026=IFCDIRECTION((0.,0.,1.));
@@ -1041,8 +1041,8 @@ DATA;
#1034=IFCDIRECTION((0.,0.,1.));
#1035=IFCAXIS2PLACEMENT3D(#1032,#1034,#1033);
#1036=IFCREPRESENTATIONMAP(#1035,#1031);
#1037=IFCTYPEPRODUCT('0vvfHSiaPBxewA$32C4LdW',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1038),(#1063),$);
#1038=IFCPROPERTYSET('2zKuFdTPj3DhNdQ0U2kd5u',$,'EPset_Annotation',$,(#1039));
#1037=IFCTYPEPRODUCT('3WEV_9wQn6AQTESgjW36PH',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1038),(#1063),$);
#1038=IFCPROPERTYSET('02FAX8dIzAlunWD5ak$6sU',$,'EPset_Annotation',$,(#1039));
#1039=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('space-tag'),$);
#1040=IFCCARTESIANPOINT((0.,0.,0.));
#1041=IFCDIRECTION((0.,0.,1.));
@@ -1068,8 +1068,8 @@ DATA;
#1061=IFCDIRECTION((0.,0.,1.));
#1062=IFCAXIS2PLACEMENT3D(#1059,#1061,#1060);
#1063=IFCREPRESENTATIONMAP(#1062,#1058);
#1064=IFCTYPEPRODUCT('36lTcd9yT8UBNDejQUNWrq',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1065),(#1078),$);
#1065=IFCPROPERTYSET('1x86VNVk1Drwe5XOj$GZX3',$,'EPset_Annotation',$,(#1066));
#1064=IFCTYPEPRODUCT('1evqvQLLL1zxdsIWqEn0lK',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1065),(#1078),$);
#1065=IFCPROPERTYSET('2KPJlGyer0UBmphfR7952k',$,'EPset_Annotation',$,(#1066));
#1066=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('rectangle-tag'),$);
#1067=IFCCARTESIANPOINT((0.,0.,0.));
#1068=IFCDIRECTION((0.,0.,1.));
@@ -1083,8 +1083,8 @@ DATA;
#1076=IFCDIRECTION((0.,0.,1.));
#1077=IFCAXIS2PLACEMENT3D(#1074,#1076,#1075);
#1078=IFCREPRESENTATIONMAP(#1077,#1073);
#1079=IFCTYPEPRODUCT('1rxhEX6I16oPqglXaGZ7Sw',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1080),(#1093),$);
#1080=IFCPROPERTYSET('3BeqEbDzX0xORamqqSx_kZ',$,'EPset_Annotation',$,(#1081));
#1079=IFCTYPEPRODUCT('3Nem6d4xX87O3deyWDi3AW',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1080),(#1093),$);
#1080=IFCPROPERTYSET('15bspgnA9CEuFox6xFjoTL',$,'EPset_Annotation',$,(#1081));
#1081=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
#1082=IFCCARTESIANPOINT((0.,0.,0.));
#1083=IFCDIRECTION((0.,0.,1.));
@@ -1098,8 +1098,8 @@ DATA;
#1091=IFCDIRECTION((0.,0.,1.));
#1092=IFCAXIS2PLACEMENT3D(#1089,#1091,#1090);
#1093=IFCREPRESENTATIONMAP(#1092,#1088);
#1094=IFCTYPEPRODUCT('0sws1hxNb2Og1etXqyoEh1',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1095),(#1108),$);
#1095=IFCPROPERTYSET('11L$PtdnX8LfYigzRZ$g0a',$,'EPset_Annotation',$,(#1096));
#1094=IFCTYPEPRODUCT('0klFX9AjnEnPBkdIURv8XD',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1095),(#1108),$);
#1095=IFCPROPERTYSET('1d53tifbv2rwcoFFJosiHf',$,'EPset_Annotation',$,(#1096));
#1096=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
#1097=IFCCARTESIANPOINT((0.,0.,0.));
#1098=IFCDIRECTION((0.,0.,1.));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22,#23));
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -27,6 +27,5 @@ DATA;
#20=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#21=IFCSIMPLEPROPERTYTEMPLATE('0nYMT3OSj5gArVniCWZRtv',$,'ShadingStyles','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#22=IFCSIMPLEPROPERTYTEMPLATE('3VWG22eZXBdQwdKlzMeVQH',$,'CurrentShadingStyle','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#23=IFCSIMPLEPROPERTYTEMPLATE('28mJ$GDxb7kBbKFH_rACMQ',$,'AngleDecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -5,7 +5,6 @@ FILE_NAME('EQto_BodyGeometryValidation.ifc','2020-01-01T00:00:00',(),(),'EQto_Bo
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
/* This quantity set is Qto_BodyGeometryValidation backport from IFC4X3 to support IFC4. */
#1=IFCPROPERTYSETTEMPLATE('2KS9su6r517uLXTGKwwDdn',$,'EQto_BodyGeometryValidation','Quantities supplied for validating the correct interpretation of the body shape representation at import. In case of multiple representation items, the quantities are summed for each of the items (irrespective of any overlap). Choosing a suitable tolerance value for comparing the supplied numbers to the numbers calculated from the reconstructed geometry is at the discretion of the importing application.',.QTO_OCCURRENCEDRIVEN.,'IfcProduct',(#2,#3,#4,#5,#6,#7));
#2=IFCSIMPLEPROPERTYTEMPLATE('3iCdOOLRjCwQ_HRTB41Xh8',$,'GrossSurfaceArea','Total gross area of the object, normally generated as perimeter * length + 2 * cross section area. It is the sum of OuterSurfaceArea + (2 x CrossSectionArea) and shall only be given, if the OuterSurfaceArea and CrossSectionArea cannot be established separately.\X2\000A000A\X0\Total gross surface area of the element before applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('0juemEqF5889vg7HwR87Fv',$,'NetSurfaceArea','Net surface area of the object, normally generated as perimeter * length + 2 * cross section area taking into account possible processing features (cut-out''s, etc.) or openings and recesses.\X2\000A000A\X0\Total net surface area of the element after applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
@@ -5,20 +5,20 @@ FILE_NAME('Psets_BBIM_Annotation.ifc','2020-01-01T00:00:00',(),(),'Psets_BBIM_An
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4));
#1=IFCPROPERTYSETTEMPLATE('3VuPUwdCD2Qx3XDDRs0R1N',$,'EPset_Annotation','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#2,#3,#4));
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separarated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/BATTING,IfcTypeProduct',(#6,#7));
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'BBIM_Batting','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#6,#7));
#6=IFCSIMPLEPROPERTYTEMPLATE('0t2LEesGT1QRQtrIZUAR8L',$,'Thickness','Batting thickness',.P_SINGLEVALUE.,'IfcPositiveLengthMeasure',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/SECTION,IfcTypeProduct',(#9,#10,#11,#12,#13));
#8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#9,#10,#11,#12,#13));
#9=IFCSIMPLEPROPERTYTEMPLATE('2a_9s8spHDc9dZHtgg71XL',$,'ShowStartArrow','Display start arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3i6SH_GbT7zhKea$wVE56A',$,'StartArrowSymbol','Custom symbol for the start of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1DtsPn5a9FG8$zXHDDMavY',$,'ShowEndArrow','Display end arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('2$6U0mLI9AiPRWeBabdY3u',$,'EndArrowSymbol','Custom symbol for the end of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('1naFqntIL7igCY7hCaE7kq',$,'HasConnectedSectionLine','Connect or disconnect section markers with line (by default = True).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#14=IFCPROPERTYSETTEMPLATE('3V8oZ8YRD3_O7uR5vcUleS',$,'BBIM_Documentation','',.PSET_TYPEDRIVENOVERRIDE.,'IfcProject',(#15,#16,#17,#18,#19,#20,#21,#22,#23));
#14=IFCPROPERTYSETTEMPLATE('3V8oZ8YRD3_O7uR5vcUleS',$,'BBIM_Documentation','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#15,#16,#17,#18,#19,#20,#21,#22,#23));
#15=IFCSIMPLEPROPERTYTEMPLATE('0ulAhgk3v9qfGlDILauR6J',$,'SheetsDir','Default sheets directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#16=IFCSIMPLEPROPERTYTEMPLATE('2yvlVKiQXASucfH40deCvu',$,'LayoutsDir','Default layouts directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#17=IFCSIMPLEPROPERTYTEMPLATE('2kXZqXicL3jRwsOnLM_0ho',$,'TitleblocksDir','Default titleblocks directory',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -28,7 +28,7 @@ DATA;
#21=IFCSIMPLEPROPERTYTEMPLATE('1UDakJ5_f7kBhggNSW4$h5',$,'SymbolsPath','Default symbols SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#22=IFCSIMPLEPROPERTYTEMPLATE('0d53LEtgLDQxnv__NfgH7i',$,'PatternsPath','Default patterns SVG',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#23=IFCSIMPLEPROPERTYTEMPLATE('26qFNMv7nCHgU6Jd7Anga5',$,'ShadingStylesPath','Default shading styles',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#24=IFCPROPERTYSETTEMPLATE('0I9merLinF5Ap$aZwaclgm',$,'BBIM_Dimension','',.PSET_TYPEDRIVENOVERRIDE.,'IfcAnnotation/DIMENSION,IfcTypeProduct',(#25,#26,#27,#28));
#24=IFCPROPERTYSETTEMPLATE('0I9merLinF5Ap$aZwaclgm',$,'BBIM_Dimension','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation,IfcTypeProduct',(#25,#26,#27,#28));
#25=IFCSIMPLEPROPERTYTEMPLATE('1rL2AbQsXD8RbpoWH5pYOV',$,'ShowDescriptionOnly','Hide the measurement values and show only annotation description',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#26=IFCSIMPLEPROPERTYTEMPLATE('0SVyOfB0rC2xNfdRYf3XvY',$,'SuppressZeroInches','Suppress 0 inch values in dimension annotation text (for example: 12'' - 0" -> 12'')',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#27=IFCSIMPLEPROPERTYTEMPLATE('2bUmj458PBqPAtUoI3MXsb',$,'TextPrefix','Text to add before annotation measurement value',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
File diff suppressed because one or more lines are too long
Binary file not shown.
+14 -21
View File
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import os
import bpy
import json
@@ -28,7 +27,6 @@ import addon_utils
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.placement
import ifcopenshell.util.unit
import blenderbim.tool as tool
import blenderbim.core.geometry
import blenderbim.core.aggregate
@@ -36,8 +34,6 @@ import blenderbim.core.spatial
import blenderbim.core.style
from blenderbim.bim.ifc import IfcStore
from mathutils import Vector
from typing import Union
from logging import Logger
class IfcExporter:
@@ -89,8 +85,8 @@ class IfcExporter:
self.get_application_name(), tool.Blender.get_blenderbim_version()
)
def sync_all_objects(self, skip_unlinking=False) -> list[ifcopenshell.entity_instance]:
results: list[ifcopenshell.entity_instance] = []
def sync_all_objects(self):
results = []
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
for ifc_definition_id in list(IfcStore.id_map.keys()):
obj = IfcStore.id_map[ifc_definition_id]
@@ -99,21 +95,19 @@ class IfcExporter:
continue
if obj.library:
continue
tool.Collector.sync(obj, skip_unlinking)
tool.Collector.sync(obj)
result = self.sync_object_placement(obj)
if result:
results.append(result)
result = self.sync_object_material(obj)
# TODO: sync_object_material always returns None
# so it's never really appended
if result:
results.append(result)
except ReferenceError:
pass # The object is likely deleted
return results
def sync_edited_objects(self) -> list[ifcopenshell.entity_instance]:
results: list[ifcopenshell.entity_instance] = []
def sync_edited_objects(self):
results = []
for obj in IfcStore.edited_objs.copy():
if not obj:
continue
@@ -131,7 +125,7 @@ class IfcExporter:
IfcStore.edited_objs.clear()
return results
def sync_object_material(self, obj: bpy.types.Object) -> None:
def sync_object_material(self, obj):
if not obj.data or not isinstance(obj.data, bpy.types.Mesh):
return
if not self.has_changed_materials(obj):
@@ -142,10 +136,11 @@ class IfcExporter:
checksum = obj.data.BIMMeshProperties.material_checksum
return checksum != str([s.id() for s in tool.Geometry.get_styles(obj) if s])
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
def sync_object_placement(self, obj):
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if not tool.Ifc.is_moved(obj):
return
blender_matrix = np.array(obj.matrix_world)
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
bpy.ops.bim.update_representation(obj=obj.name)
return element
@@ -156,7 +151,7 @@ class IfcExporter:
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
return element
def sync_grid_axis_object_placement(self, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None:
def sync_grid_axis_object_placement(self, obj, element):
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
grid_obj = tool.Ifc.get_object(grid)
if grid_obj:
@@ -165,10 +160,10 @@ class IfcExporter:
bpy.ops.bim.update_representation(obj=obj.name)
tool.Geometry.record_object_position(obj)
def get_application_name(self) -> str:
def get_application_name(self):
return "BlenderBIM"
def get_application_version(self) -> str:
def get_application_version(self):
version = ".".join(
[
str(x)
@@ -186,13 +181,11 @@ class IfcExporter:
class IfcExportSettings:
def __init__(self):
self.logger: Logger = None
self.output_file: str = None
self.json_version: str = None
self.json_compact: bool = None
self.logger = None
self.output_file = None
@staticmethod
def factory(context: bpy.types.Context, output_file: str, logger: Logger) -> IfcExportSettings:
def factory(context, output_file, logger):
settings = IfcExportSettings()
settings.output_file = output_file
settings.logger = logger
+99 -36
View File
@@ -21,7 +21,6 @@ import bpy
import json
import addon_utils
import ifcopenshell.api.owner.settings
import blenderbim.bim
import blenderbim.tool as tool
import blenderbim.core.owner as core_owner
from bpy.app.handlers import persistent
@@ -31,7 +30,6 @@ from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.workspace import LIST_OF_TOOLS, TOOLS_TO_CLASSES_MAP
from mathutils import Vector
from math import cos, degrees
from typing import Union
cwd = os.path.dirname(os.path.realpath(__file__))
@@ -61,10 +59,6 @@ def name_callback(obj, data):
if not obj.BIMObjectProperties.ifc_definition_id:
return
if obj.BIMObjectProperties.is_renaming:
obj.BIMObjectProperties.is_renaming = False
return
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
if "/" in obj.name:
object_name = obj.name
@@ -192,8 +186,6 @@ def refresh_ui_data():
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
tool.Ifc.get().clear_cache()
bpy.context.scene.DocProperties.should_draw_decorations = bpy.context.scene.DocProperties.should_draw_decorations
@persistent
def loadIfcStore(scene):
@@ -223,7 +215,7 @@ def redo_post(scene):
tool.Ifc.rebuild_element_maps()
def get_application(ifc: ifcopenshell.file) -> ifcopenshell.entity_instance:
def get_application(ifc):
# TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts.
version = get_application_version()
for element in ifc.by_type("IfcApplication"):
@@ -238,20 +230,7 @@ def get_application(ifc: ifcopenshell.file) -> ifcopenshell.entity_instance:
)
def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
# TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts.
if pao := next(iter(ifc.by_type("IfcPersonAndOrganization")), None):
return pao
elif ifc.schema == "IFC2X3":
if (person := next(iter(ifc.by_type("IfcPerson")), None)) is None:
person = tool.Ifc.run("owner.add_person")
if (organization := next(iter(ifc.by_type("IfcOrganization")), None)) is None:
organization = tool.Ifc.run("owner.add_organisation")
pao = tool.Ifc.run("owner.add_person_and_organisation", person=person, organisation=organization)
return pao
def get_application_version() -> str:
def get_application_version():
return ".".join(
[
str(x)
@@ -270,6 +249,76 @@ def viewport_shading_changed_callback(area):
bpy.context.scene.BIMStylesProperties.active_style_type = "External"
if getattr(bpy.types, "SCENE_PT_scene"):
class Override_SCENE_PT_scene(bpy.types.SCENE_PT_scene):
bl_idname = "SCENE_PT_scene_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_unit"):
class Override_SCENE_PT_unit(bpy.types.SCENE_PT_unit):
bl_idname = "SCENE_PT_unit_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_physics"):
class Override_SCENE_PT_physics(bpy.types.SCENE_PT_physics):
bl_idname = "SCENE_PT_physics_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_rigid_body_world"):
class Override_SCENE_PT_rigid_body_world(bpy.types.SCENE_PT_rigid_body_world):
bl_idname = "SCENE_PT_rigid_body_world_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_audio"):
class Override_SCENE_PT_audio(bpy.types.SCENE_PT_audio):
bl_idname = "SCENE_PT_audio_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_keying_sets"):
class Override_SCENE_PT_keying_sets(bpy.types.SCENE_PT_keying_sets):
bl_idname = "SCENE_PT_keying_sets_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
if getattr(bpy.types, "SCENE_PT_custom_props"):
class Override_SCENE_PT_custom_props(bpy.types.SCENE_PT_custom_props):
bl_idname = "SCENE_PT_custom_props_override"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "BLENDER")
@persistent
def load_post(scene):
global global_subscription_owner
@@ -292,25 +341,39 @@ def load_post(scene):
key=key, owner=global_subscription_owner, args=(area,), notify=viewport_shading_changed_callback
)
ifcopenshell.api.owner.settings.get_user = get_user
ifcopenshell.api.owner.settings.get_user = lambda ifc: core_owner.get_user(tool.Owner)
ifcopenshell.api.owner.settings.get_application = get_application
AuthoringData.type_thumbnails = {}
if not bpy.context.preferences.addons["blenderbim"].preferences.should_setup_toolbar:
tool.Blender.unregister_toolbar()
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
if "BIM" in bpy.data.workspaces:
if bpy.context.preferences.addons["blenderbim"].preferences.activate_workspace:
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
else:
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
# To improve usability for new users, we hijack the scene properties
# tab. We override default scene properties panels with our own poll
# to hide them unless the user has chosen to view Blender properties.
for panel in tool.Blender.get_scene_panels_list():
if panel in blenderbim.bim.original_scene_panels_polls:
# To improve usability for new users, we hijack the scene properties
# tab. We override default scene properties panels with our own poll
# to hide them unless the user has chosen to view Blender properties.
for panel in [
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_custom_props",
]:
if getattr(bpy.types, panel, None):
try:
bpy.utils.register_class(globals()[f"Override_{panel}"])
bpy.utils.unregister_class(getattr(bpy.types, panel))
except:
pass
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
for screen in bpy.data.screens:
if len(screen.BIMAreaProperties) == 20:
continue
tool.Blender.override_scene_panel(panel)
tool.Blender.setup_tabs()
screen.BIMAreaProperties.clear()
for i in range(20): # 20 is an arbitrary value of split areas
screen.BIMAreaProperties.add()
+58 -34
View File
@@ -29,7 +29,6 @@ from mathutils import geometry
from mathutils import Vector
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from typing import Optional, Callable, Any, Union
def draw_attributes(props, layout, copy_operator=None, popup_active_attribute=None):
@@ -76,10 +75,6 @@ def draw_attribute(attribute, layout, copy_operator=None):
op.data_path = attribute.path_from_id("string_value")
if attribute.is_optional:
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if attribute.name == "GlobalId":
layout.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
if copy_operator:
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.name = attribute.name
@@ -91,20 +86,9 @@ def import_attributes(ifc_class, props, data, callback=None):
# A more elegant attribute importer signature, intended to supersede import_attributes
def import_attributes2(
element: Union[str, ifcopenshell.entity_instance],
props: bpy.types.PropertyGroup,
callback: Optional[Callable] = None,
) -> None:
if isinstance(element, str):
attributes = tool.Ifc.schema().declaration_by_name(element).as_entity().all_attributes()
info = {a.name(): None for a in attributes}
info["type"] = element
else:
attributes = element.wrapped_data.declaration().as_entity().all_attributes()
info = element.get_info()
for attribute in attributes:
import_attribute(attribute, props, info, callback=callback)
def import_attributes2(element, props, callback=None):
for attribute in element.wrapped_data.declaration().as_entity().all_attributes():
import_attribute(attribute, props, element.get_info(), callback=callback)
def import_attribute(attribute, props, data, callback=None):
@@ -125,7 +109,7 @@ def import_attribute(attribute, props, data, callback=None):
elif is_handled_by_callback is False:
props.remove(len(props) - 1)
elif data_type == "string":
new.string_value = "" if new.is_null else str(data[attribute.name()]).replace("\n", "\\n")
new.string_value = "" if new.is_null else str(data[attribute.name()])
if attribute.type_of_attribute().declared_type().name() == "IfcURIReference":
new.is_uri = True
elif data_type == "boolean":
@@ -187,7 +171,7 @@ def add_attribute_description(attribute_blender, attribute_ifc=None):
attribute_blender.description = description
def export_attributes(props, callback: Optional[Callable] = None) -> dict[str, Any]:
def export_attributes(props, callback=None):
attributes = {}
for prop in props:
is_handled_by_callback = callback(attributes, prop) if callback else False
@@ -225,6 +209,46 @@ def get_enum_items(data, prop_name, context=None):
return items
def get_obj_ifc_definition_id(context, obj, obj_type):
if obj_type == "Object":
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
elif obj_type == "Material":
return bpy.data.materials.get(obj).BIMObjectProperties.ifc_definition_id
elif obj_type == "MaterialSet":
return ifcopenshell.util.element.get_material(
tool.Ifc.get_entity(bpy.data.objects.get(obj)), should_skip_usage=True
).id()
elif obj_type == "MaterialSetItem":
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
elif obj_type == "Task":
return context.scene.BIMTaskTreeProperties.tasks[
context.scene.BIMWorkScheduleProperties.active_task_index
].ifc_definition_id
elif obj_type == "Cost":
return context.scene.BIMCostProperties.cost_items[
context.scene.BIMCostProperties.active_cost_item_index
].ifc_definition_id
elif obj_type == "Resource":
return context.scene.BIMResourceTreeProperties.resources[
context.scene.BIMResourceProperties.active_resource_index
].ifc_definition_id
elif obj_type == "Profile":
return context.scene.BIMProfileProperties.profiles[
context.scene.BIMProfileProperties.active_profile_index
].ifc_definition_id
elif obj_type == "WorkSchedule":
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
# hack to close popup
# https://blender.stackexchange.com/a/202576/130742
def close_operator_panel(event):
x, y = event.mouse_x, event.mouse_y
bpy.context.window.cursor_warp(10, 10)
move_back = lambda: bpy.context.window.cursor_warp(x, y)
bpy.app.timers.register(move_back, first_interval=0.01)
def convert_property_group_from_si(property_group, skip_props=()):
"""Method converts property group values from si to current ifc project units
@@ -236,7 +260,12 @@ def convert_property_group_from_si(property_group, skip_props=()):
for prop_name in property_group.bl_rna.properties.keys():
if prop_name in skip_props:
continue
prop_value = tool.Blender.get_blender_prop_default_value(property_group, prop_name)
prop_bl_rna = property_group.bl_rna.properties[prop_name]
if prop_bl_rna.array_length > 0:
prop_value = prop_bl_rna.default_array
else:
prop_value = prop_bl_rna.default
if type(prop_value) is float:
prop_value = prop_value * conversion_k
elif type(prop_value) is bpy.types.bpy_prop_array:
@@ -244,7 +273,7 @@ def convert_property_group_from_si(property_group, skip_props=()):
setattr(property_group, prop_name, prop_value)
def draw_filter(layout, filter_groups, data, module):
def draw_filter(layout, props, data, module):
if not data.is_loaded:
data.load()
@@ -262,7 +291,7 @@ def draw_filter(layout, filter_groups, data, module):
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD").module = module
row.operator("bim.edit_filter_query", text="", icon="FILTER").module = module
for i, filter_group in enumerate(filter_groups):
for i, filter_group in enumerate(props.filter_groups):
box = layout.box()
row = box.row(align=True)
@@ -271,9 +300,6 @@ def draw_filter(layout, filter_groups, data, module):
op.type = sprops.facet
op.index = i
op.module = module
op = row.operator("bim.remove_filter_group", text="", icon="X")
op.index = i
op.module = module
for j, ifc_filter in enumerate(filter_group.filters):
if ifc_filter.type == "entity":
@@ -293,7 +319,6 @@ def draw_filter(layout, filter_groups, data, module):
row = box.row(align=True)
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
row.prop(ifc_filter, "name", text="")
row.prop(ifc_filter, "comparison", text="")
row.prop(ifc_filter, "value", text="")
elif ifc_filter.type == "classification":
row = box.row(align=True)
@@ -301,12 +326,6 @@ def draw_filter(layout, filter_groups, data, module):
elif ifc_filter.type == "location":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="PACKAGE")
elif ifc_filter.type == "group":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="OUTLINER_COLLECTION")
elif ifc_filter.type == "parent":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="FILE_PARENT")
elif ifc_filter.type == "query":
row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
@@ -323,6 +342,11 @@ def draw_filter(layout, filter_groups, data, module):
op.index = j
op.module = module
row = box.row()
op = row.operator("bim.remove_filter_group", icon="X")
op.index = i
op.module = module
# TODO this should move into ifcopenshell.util
+37 -64
View File
@@ -19,46 +19,37 @@
import os
import bpy
import uuid
import shutil
import hashlib
import zipfile
import tempfile
import traceback
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper
import blenderbim
import blenderbim.bim.handler
import blenderbim.tool as tool
from pathlib import Path
from blenderbim.tool.brick import BrickStore
from typing import Set, Union, Optional
IFC_CONNECTED_TYPE = Union[bpy.types.Material, bpy.types.Object]
class IfcStore:
path: str = ""
file: Optional[ifcopenshell.file] = None
schema: Optional[ifcopenshell.ifcopenshell_wrapper.schema_definition] = None
cache: Optional[ifcopenshell.ifcopenshell_wrapper.HdfSerializer] = None
cache_path: Optional[str] = None
id_map: dict[int, IFC_CONNECTED_TYPE] = {}
guid_map: dict[str, IFC_CONNECTED_TYPE] = {}
edited_objs: Set[bpy.types.Object] = set()
pset_template_path: str = ""
pset_template_file: Optional[ifcopenshell.file] = None
classification_path: str = ""
classification_file: Optional[ifcopenshell.file] = None
library_path: str = ""
library_file: Optional[ifcopenshell.file] = None
path = ""
file = None
schema = None
cache = None
cache_path = None
id_map = {}
guid_map = {}
edited_objs = set()
pset_template_path = ""
pset_template_file = None
classification_path = ""
classification_file = None
library_path = ""
library_file = None
current_transaction = ""
last_transaction = ""
history = []
future = []
schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3"]
session_files: dict[str, ifcopenshell.file] = {}
session_files = {}
@staticmethod
def purge():
@@ -89,8 +80,8 @@ class IfcStore:
if IfcStore.path:
try:
IfcStore.load_file(IfcStore.path)
except Exception as e:
print(f"Failed to load file {IfcStore.path}. Error details: {e}")
except:
pass
return IfcStore.file
@staticmethod
@@ -122,17 +113,11 @@ class IfcStore:
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
IfcStore.cache = None
try:
shutil.move(IfcStore.cache_path, new_cache_path)
except PermissionError:
try:
shutil.copy2(IfcStore.cache_path, new_cache_path)
except PermissionError:
pass # Well we tried. No cache for you!
os.replace(IfcStore.cache_path, new_cache_path)
IfcStore.get_cache()
@staticmethod
def load_file(path) -> None:
def load_file(path):
if not os.path.isfile(path):
return
extension = path.split(".")[-1]
@@ -161,7 +146,7 @@ class IfcStore:
return IfcStore.schema
@staticmethod
def get_element(id_or_guid: Union[int, str]) -> IFC_CONNECTED_TYPE:
def get_element(id_or_guid):
if isinstance(id_or_guid, int):
map_object = IfcStore.id_map
else:
@@ -174,20 +159,16 @@ class IfcStore:
return obj
@staticmethod
def relink_all_objects() -> None:
def relink_all_objects():
if not IfcStore.get_file():
return
for obj in bpy.data.objects:
if obj.library:
continue
IfcStore.relink_object(obj)
for obj in bpy.data.materials:
if obj.library:
continue
IfcStore.relink_object(obj)
@staticmethod
def relink_object(obj: IFC_CONNECTED_TYPE) -> None:
def relink_object(obj):
if not obj:
return
if obj.BIMObjectProperties.ifc_definition_id:
@@ -208,7 +189,7 @@ class IfcStore:
IfcStore.commit_link_element(data)
@staticmethod
def link_element(element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
def link_element(element, obj):
# Please use tool.Ifc.link() instead of this method. We want to
# refactor this class and deprecate usage of IfcStore in favour of
# tools.
@@ -222,7 +203,7 @@ class IfcStore:
elif existing_obj:
try:
existing_obj.name
IfcStore.unlink_element(element=element, obj=existing_obj)
IfcStore.unlink_element(obj=existing_obj)
except:
pass
IfcStore.id_map[element.id()] = obj
@@ -274,7 +255,7 @@ class IfcStore:
# TODO We're handling id_map and guid_map, but what about edited_objs? This might cause big problems.
@staticmethod
def rollback_unlink_element(data) -> None:
def rollback_unlink_element(data):
if "id" not in data or "obj" not in data:
return
obj = bpy.data.objects.get(data["obj"])
@@ -283,15 +264,13 @@ class IfcStore:
IfcStore.guid_map[data["guid"]] = obj
@staticmethod
def commit_unlink_element(data) -> None:
def commit_unlink_element(data):
del IfcStore.id_map[data["id"]]
if data["guid"]:
del IfcStore.guid_map[data["guid"]]
@staticmethod
def unlink_element(
element: Optional[ifcopenshell.entity_instance] = None, obj: Optional[IFC_CONNECTED_TYPE] = None
) -> None:
def unlink_element(element=None, obj=None):
if element is None:
try:
element = tool.Ifc.get_entity(obj)
@@ -337,9 +316,7 @@ class IfcStore:
)
@staticmethod
def execute_ifc_operator(operator: bpy.types.Operator, context: bpy.types.Context, is_invoke=False):
blenderbim.last_actions.append({"type": "operator", "name": operator.bl_idname})
bpy.context.scene.BIMProperties.is_dirty = True
def execute_ifc_operator(operator, context, is_invoke=False):
is_top_level_operator = not bool(IfcStore.current_transaction)
if is_top_level_operator:
@@ -353,14 +330,10 @@ class IfcStore:
else:
operator.transaction_key = IfcStore.current_transaction
try:
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context)
except:
blenderbim.last_error = traceback.format_exc()
raise
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context)
if is_top_level_operator:
if tool.Ifc.get():
@@ -376,17 +349,17 @@ class IfcStore:
return result
@staticmethod
def begin_transaction(operator: bpy.types.Operator) -> None:
def begin_transaction(operator):
IfcStore.current_transaction = str(uuid.uuid4())
operator.transaction_key = IfcStore.current_transaction
@staticmethod
def end_transaction(operator: bpy.types.Operator) -> None:
def end_transaction(operator):
IfcStore.current_transaction = ""
operator.transaction_key = ""
@staticmethod
def add_transaction_operation(operator: bpy.types.Operator, rollback=None, commit=None) -> None:
def add_transaction_operation(operator, rollback=None, commit=None):
key = getattr(operator, "transaction_key", None)
data = getattr(operator, "transaction_data", None)
bpy.context.scene.BIMProperties.last_transaction = key
@@ -402,7 +375,7 @@ class IfcStore:
IfcStore.future = []
@staticmethod
def undo(until_key=None) -> None:
def undo(until_key=None):
BrickStore.undo()
if not IfcStore.history:
return
@@ -417,7 +390,7 @@ class IfcStore:
IfcStore.future.append(event)
@staticmethod
def redo(until_key=None) -> None:
def redo(until_key=None):
BrickStore.redo()
if not IfcStore.future:
File diff suppressed because it is too large Load Diff
@@ -20,19 +20,16 @@ import bpy
from . import ui, prop, operator
classes = (
operator.BIM_OT_add_aggregate,
operator.BIM_OT_add_part_to_object,
operator.BIM_OT_aggregate_assign_object,
operator.BIM_OT_disable_editing_aggregate,
operator.BIM_OT_assign_object,
operator.BIM_OT_unassign_object,
operator.BIM_OT_enable_editing_aggregate,
operator.BIM_OT_select_aggregate,
operator.BIM_OT_disable_editing_aggregate,
operator.BIM_OT_add_aggregate,
operator.BIM_OT_select_parts,
operator.BIM_OT_aggregate_unassign_object,
operator.BIM_OT_break_link_to_other_aggregates,
operator.BIM_OT_select_linked_aggregates,
operator.BIM_OT_select_aggregate,
operator.BIM_OT_add_part_to_object,
prop.BIMObjectAggregateProperties,
ui.BIM_PT_aggregate,
ui.BIM_PT_linked_aggregate,
)
@@ -35,8 +35,8 @@ class AggregateData:
"has_relating_object": cls.has_relating_object(),
"relating_object_label": cls.get_relating_object_label(),
"has_related_objects": cls.has_related_objects(),
"total_parts": cls.total_parts(),
"total_linked_aggregate": cls.total_linked_aggregate(),
"related_objects_amount": cls.get_related_objects_amount(),
"ifc_class": cls.ifc_class(),
}
cls.is_loaded = True
@@ -59,7 +59,7 @@ class AggregateData:
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def total_parts(cls):
def get_related_objects_amount(cls):
parts = cls.get_related_objects()
return len(parts) if parts else 0
@@ -68,24 +68,7 @@ class AggregateData:
return bool(cls.get_related_objects())
@classmethod
def total_linked_aggregate(cls):
def ifc_class(cls) -> str:
element = tool.Ifc.get_entity(bpy.context.active_object)
aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate:
return []
if not element:
return []
product_linked_agg_group = [
r
for r in getattr(aggregate, "HasAssignments", []) or []
if r.is_a("IfcRelAssignsToGroup")
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
]
if not product_linked_agg_group:
return []
total = len(product_linked_agg_group[0].RelatedObjects)
return total
if element:
return element.is_a()
@@ -33,53 +33,45 @@ class Operator:
return {"FINISHED"}
class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
class BIM_OT_assign_object(bpy.types.Operator, Operator):
"""Create aggregation relationship between two ifc elements"""
bl_idname = "bim.aggregate_assign_object"
bl_label = "Assign Object To Aggregation"
bl_idname = "bim.assign_object"
bl_label = "Assign Object"
bl_options = {"REGISTER", "UNDO"}
relating_object: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def _execute(self, context):
relating_obj = None
if self.relating_object:
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
elif self.related_object:
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get().by_id(self.related_object))
if aggregate:
relating_obj = tool.Ifc.get_object(aggregate)
elif context.active_object:
relating_obj = context.active_object
if not relating_obj:
return
for obj in tool.Blender.get_selected_objects():
for obj in bpy.context.selected_objects:
if obj == relating_obj:
continue
element = tool.Ifc.get_entity(obj)
if not element:
continue
try:
core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=relating_obj,
related_obj=obj,
)
except core.IncompatibleAggregateError:
self.report({"ERROR"}, f"Cannot aggregate {obj.name} to {relating_obj.name}")
except core.AggregateRepresentationError:
self.report({"ERROR"}, f"Cannot aggregate to {relating_obj.name} with a body representation")
result = core.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=relating_obj,
related_obj=obj,
)
if not result:
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
"""Remove aggregation relationship between two ifc elements"""
bl_idname = "bim.aggregate_unassign_object"
bl_label = "Unassign Object From Aggregation"
bl_idname = "bim.unassign_object"
bl_label = "Unassign Object"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
@@ -98,14 +90,7 @@ class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
related_obj=tool.Ifc.get_object(element),
)
# Removes Pset related to Linked Aggregates
if not element.is_a('IfcElementAssembly'):
pset = ifcopenshell.util.element.get_pset(element, 'BBIM_Linked_Aggregate')
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
"""Enable editing aggregation relationship"""
@@ -136,7 +121,6 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
ifc_class: bpy.props.StringProperty(name="IFC Class", default="IfcElementAssembly")
aggregate_name: bpy.props.StringProperty(name="Name", default="Default_Name")
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
@@ -144,15 +128,13 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
def draw(self, context):
row = self.layout
row.prop(self, "ifc_class")
row = self.layout
row.prop(self, "aggregate_name")
def _execute(self, context):
try:
ifc_class = tool.Ifc.schema().declaration_by_name(self.ifc_class).name()
except:
return
aggregate = self.create_aggregate(context, ifc_class, self.aggregate_name)
aggregate = self.create_aggregate(context, ifc_class)
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
@@ -180,9 +162,8 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
)
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
def create_aggregate(self, context, ifc_class, aggregate_name):
aggregate = bpy.data.objects.new(aggregate_name, None)
aggregate.location = context.scene.cursor.location
def create_aggregate(self, context, ifc_class):
aggregate = bpy.data.objects.new("Assembly", None)
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class=ifc_class)
return aggregate
@@ -197,17 +178,12 @@ class BIM_OT_select_parts(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
# obj = bpy.data.objects.get(self.obj) or context.active_object
for obj in context.selected_objects:
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(obj))
if parts:
parts_objs = set(tool.Ifc.get_object(part) for part in parts)
selectable_parts_objs = set(context.selectable_objects).intersection(parts_objs)
for selectable_part_obj in selectable_parts_objs:
selectable_part_obj.select_set(True)
else:
obj.select_set(False)
obj = bpy.data.objects.get(self.obj) or context.active_object
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(obj))
parts_objs = set(tool.Ifc.get_object(part) for part in parts)
selectable_parts_objs = set(context.selectable_objects).intersection(parts_objs)
for selectable_part_obj in selectable_parts_objs:
selectable_part_obj.select_set(True)
return {"FINISHED"}
@@ -218,54 +194,15 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
bl_label = "Select Aggregate"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
select_parts: bpy.props.BoolProperty(default=False)
@classmethod
def description(cls, context, properties):
if properties.select_parts:
return "Select Aggregate and Parts"
else:
return "Select Aggregate"
def execute(self, context):
self.file = IfcStore.get_file()
# obj = bpy.data.objects.get(self.obj) or context.active_object
# aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(obj))
# aggregate_obj = tool.Ifc.get_object(aggregate)
all_parts = []
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if element:
aggregate = ifcopenshell.util.element.get_aggregate(element)
if aggregate:
all_parts.append(aggregate)
obj.select_set(False)
else:
pass
if not element:
obj.select_set(False)
if self.select_parts:
all_objs = []
for part in all_parts:
if part.IsDecomposedBy:
for subpart in part.IsDecomposedBy[0].RelatedObjects:
all_parts.append(subpart)
all_objs.append(part)
for element in all_objs:
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
else:
for aggregate_element in all_parts:
aggregate_obj = tool.Ifc.get_object(aggregate_element)
aggregate_obj.select_set(True)
bpy.context.view_layer.objects.active = aggregate_obj
obj = bpy.data.objects.get(self.obj) or context.active_object
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(obj))
aggregate_obj = tool.Ifc.get_object(aggregate)
if aggregate_obj in context.selectable_objects:
aggregate_obj.select_set(True)
bpy.context.view_layer.objects.active = aggregate_obj
return {"FINISHED"}
@@ -275,105 +212,20 @@ class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"}
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
part_name: bpy.props.StringProperty(name="Name")
element: bpy.props.IntProperty(options={"HIDDEN"})
obj: bpy.props.StringProperty(options={"HIDDEN"})
def invoke(self, context, event):
self.part_name = "My " + self.part_class.lstrip("Ifc")
return context.window_manager.invoke_props_dialog(self)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) or context.active_object
core.add_part_to_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
tool.Blender,
obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.element)) if self.element else context.active_object,
obj=obj,
part_class=self.part_class,
part_name=self.part_name,
)
tool.Spatial.load_container_manager()
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
bl_idname = "bim.break_link_to_other_aggregates"
bl_label = "Break link to other aggregates"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
element = tool.Ifc.get_entity(bpy.context.active_object)
aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate:
return []
if not element:
return []
parts = ifcopenshell.util.element.get_parts(aggregate)
for part in parts:
pset = ifcopenshell.util.element.get_pset(part, "BBIM_Linked_Aggregate")
pset = tool.Ifc.get().by_id(pset["id"])
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
linked_aggregate_group = [
r.RelatingGroup
for r in getattr(aggregate, "HasAssignments", []) or []
if r.is_a("IfcRelAssignsToGroup")
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
]
tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], products=[aggregate])
return {"FINISHED"}
class BIM_OT_select_linked_aggregates(bpy.types.Operator, Operator):
bl_idname = "bim.select_linked_aggregates"
bl_label = "Select linked aggregates"
bl_options = {"REGISTER", "UNDO"}
select_parts: bpy.props.BoolProperty(default=False)
@classmethod
def description(cls, context, properties):
if properties.select_parts:
return "Select all aggregates, subaggregates and all their parts"
else:
return "Select all aggregates"
def _execute(self, context):
for obj in context.selected_objects:
obj.select_set(False)
element = tool.Ifc.get_entity(obj)
aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate:
continue
if not element:
continue
linked_aggregate_group = [
r.RelatingGroup
for r in getattr(aggregate, "HasAssignments", []) or []
if r.is_a("IfcRelAssignsToGroup")
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
]
group_rel = linked_aggregate_group[0].IsGroupedBy or []
for group_link in group_rel:
parts = list(group_link.RelatedObjects)
if self.select_parts:
parts_objs = []
for part in parts:
if part.IsDecomposedBy:
for subpart in part.IsDecomposedBy[0].RelatedObjects:
parts.append(subpart)
parts_objs.append(part)
for element in parts_objs:
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
else:
for element in parts:
obj = tool.Ifc.get_object(element)
obj.select_set(True)
return {"FINISHED"}
@@ -34,27 +34,15 @@ from bpy.props import (
def update_relating_object(self, context):
def message(self, context):
self.layout.label(text="Please select a valid IFC Element")
self.layout.label(text="Please select a valid Ifc Element")
if self.relating_object:
self.related_object = None
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
self.relating_object = None
def update_related_object(self, context):
def message(self, context):
self.layout.label(text="Please select a valid IFC Element")
if self.related_object:
if self.relating_object is None:
return
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
self.relating_object = None
if not self.related_object.BIMObjectProperties.ifc_definition_id:
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
self.related_object = None
class BIMObjectAggregateProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_object: PointerProperty(name="Relating Whole", type=bpy.types.Object, update=update_relating_object)
related_object: PointerProperty(name="Related Part", type=bpy.types.Object, update=update_related_object)
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object, update=update_relating_object)
@@ -18,9 +18,7 @@
from bpy.types import Panel
from blenderbim.bim.module.aggregate.data import AggregateData
from blenderbim.bim.module.group.data import GroupsData, ObjectGroupsData
from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
class BIM_PT_aggregate(Panel):
@@ -53,104 +51,42 @@ class BIM_PT_aggregate(Panel):
props = context.active_object.BIMObjectAggregateProperties
if props.is_editing:
row = layout.row()
row.prop(props, "relating_object", text="Whole")
row = layout.row()
row.prop(props, "related_object", text="Or Part")
row = layout.row(align=True)
col = row.column(align=True)
if not props.relating_object and not props.related_object:
col.enabled = False
op = col.operator("bim.aggregate_assign_object", icon="CHECKMARK")
row.prop(props, "relating_object", text="")
if props.relating_object:
op = row.operator("bim.assign_object", icon="CHECKMARK", text="")
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
elif props.related_object:
op.related_object = props.related_object.BIMObjectProperties.ifc_definition_id
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
return
else:
row = layout.row(align=True)
if AggregateData.data["has_relating_object"]:
row.label(text=AggregateData.data["relating_object_label"], icon="TRIA_UP")
op = row.operator("bim.select_aggregate", icon="OBJECT_DATA", text="")
op.obj = context.active_object.name
op.select_parts = False
op = row.operator("bim.select_aggregate", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
op.select_parts = True
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
op = row.operator("bim.aggregate_unassign_object", icon="X", text="")
op = row.operator("bim.unassign_object", icon="X", text="")
else:
row.label(text="No Whole Relationship Found", icon="TRIA_UP")
row.label(text="No Aggregate", icon="TRIA_UP")
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
row = layout.row(align=True)
total_parts = AggregateData.data["total_parts"]
if total_parts == 0:
row.label(text="No Parts", icon="TRIA_DOWN")
elif total_parts == 1:
row.label(text="1 Part", icon="TRIA_DOWN")
else:
row.label(text=f"{total_parts} Parts", icon="TRIA_DOWN")
parts = AggregateData.data["related_objects_amount"]
row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN")
if AggregateData.data["has_related_objects"]:
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
class BIM_PT_linked_aggregate(Panel):
bl_label = "Linked Aggregates"
bl_idname = "BIM_PT_linked_aggregate"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_order = 2
bl_parent_id = "BIM_PT_aggregate"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
return False
return True
def draw(self, context):
layout = self.layout
if not AggregateData.is_loaded:
AggregateData.load()
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMObjectAggregateProperties
row = layout.row(align=True)
if element.Decomposes:
Number_Linked_Aggregates = AggregateData.data['total_linked_aggregate']
if not Number_Linked_Aggregates:
row.label(text="Not a Linked Aggregate")
else:
row.label(text=f"{Number_Linked_Aggregates} Linked Aggregates")
op = row.operator("bim.duplicate_linked_aggregate_to_3d_cursor", text="", icon="DUPLICATE")
if type(Number_Linked_Aggregates) is int:
if Number_Linked_Aggregates > 0:
op = row.operator("bim.select_linked_aggregates", text="", icon="OUTLINER_DATA_POINTCLOUD")
op.select_parts = False
op = row.operator("bim.select_linked_aggregates", text="", icon="OUTLINER_OB_POINTCLOUD")
op.select_parts = True
row.operator("bim.refresh_linked_aggregate", text="", icon="FILE_REFRESH")
op = row.operator("bim.break_link_to_other_aggregates", text="", icon="X")
else:
row.label(text="Not an Aggregate")
ifc_class = AggregateData.data["ifc_class"]
part_class = ""
if ifc_class == "IfcBuilding":
part_class = "IfcBuildingStorey"
elif ifc_class == "IfcSite":
part_class = "IfcBuilding"
elif ifc_class == "IfcProject":
part_class = "IfcSite"
if part_class != "":
op = layout.operator("bim.add_part_to_object", text="Add " + part_class.lstrip("Ifc"))
op.part_class = part_class
op.obj = context.active_object.name
@@ -27,12 +27,15 @@ classes = (
operator.CopyAttributeToSelection,
prop.BIMAttributeProperties,
ui.BIM_PT_object_attributes,
ui.BIM_PT_material_attributes,
)
def register():
bpy.types.Object.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
bpy.types.Material.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
def unregister():
del bpy.types.Object.BIMAttributeProperties
del bpy.types.Material.BIMAttributeProperties
@@ -23,6 +23,7 @@ import blenderbim.tool as tool
def refresh():
AttributesData.is_loaded = False
MaterialAttributesData.is_loaded = False
class AttributesData:
@@ -50,3 +51,28 @@ class AttributesData:
key = "STEP ID"
results.append({"name": key, "value": str(value)})
return results
class MaterialAttributesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"ifc_definition_id": cls.ifc_definition_id(), "attributes": cls.attributes()}
cls.is_loaded = True
@classmethod
def ifc_definition_id(cls):
return bpy.context.active_object.active_material.BIMObjectProperties.ifc_definition_id
@classmethod
def attributes(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
data = element.get_info()
for key, value in data.items():
if value is None or isinstance(value, ifcopenshell.entity_instance) or key in ["id", "type"]:
continue
results.append({"name": key, "value": str(value)})
return results
@@ -20,7 +20,6 @@ import bpy
import json
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.guid
import blenderbim.bim.helper
import blenderbim.bim.handler
import blenderbim.tool as tool
@@ -40,22 +39,18 @@ class EnableEditingAttributes(bpy.types.Operator):
bl_label = "Enable Editing Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj)
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
props.attributes.clear()
element = tool.Ifc.get_entity(obj)
has_inherited_predefined_type = False
if not element.is_a("IfcTypeObject") and (element_type := ifcopenshell.util.element.get_type(element)):
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
has_inherited_predefined_type = ifcopenshell.util.element.get_predefined_type(element_type) not in (
"NOTDEFINED",
None,
)
def callback(name, prop, data):
if name in ("RefLatitude", "RefLongitude"):
new = props.attributes.add()
@@ -67,11 +62,10 @@ class EnableEditingAttributes(bpy.types.Operator):
new.string_value = "" if new.is_null else json.dumps(data[name])
blenderbim.bim.helper.add_attribute_description(new)
new.description += " The degrees, minutes and seconds should follow this format : [12,34,56]"
if name in ("PredefinedType", "ObjectType") and has_inherited_predefined_type:
props.attributes.remove(len(props.attributes) - 1)
return True
blenderbim.bim.helper.import_attributes2(element, props.attributes, callback=callback)
blenderbim.bim.helper.import_attributes2(
tool.Ifc.get().by_id(oprops.ifc_definition_id), props.attributes, callback=callback
)
props.is_editing_attributes = True
return {"FINISHED"}
@@ -81,9 +75,13 @@ class DisableEditingAttributes(bpy.types.Operator):
bl_label = "Disable Editing Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj)
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
props = obj.BIMAttributeProperties
props.is_editing_attributes = False
return {"FINISHED"}
@@ -94,10 +92,14 @@ class EditAttributes(bpy.types.Operator, Operator):
bl_label = "Edit Attributes"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def _execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj)
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
props = obj.BIMAttributeProperties
product = tool.Ifc.get_entity(obj)
@@ -114,50 +116,24 @@ class EditAttributes(bpy.types.Operator, Operator):
attributes = blenderbim.bim.helper.export_attributes(props.attributes, callback=callback)
ifcopenshell.api.run("attribute.edit_attributes", self.file, product=product, attributes=attributes)
bpy.ops.bim.disable_editing_attributes(obj=obj.name)
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
return {"FINISHED"}
class GenerateGlobalId(bpy.types.Operator, Operator):
class GenerateGlobalId(bpy.types.Operator):
bl_idname = "bim.generate_global_id"
bl_label = "Regenerate GlobalId"
bl_description = "Regenerate GlobalId\n\nSHIFT+CLICK to regenerate GlobalIds for all selected objects"
bl_options = {"REGISTER", "UNDO"}
use_selected: bpy.props.BoolProperty(name="Use All Selected Objects", default=False, options={"SKIP_SAVE"})
def invoke(self, context, event):
# using all selected objects on shift+click
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
if event.type == "LEFTMOUSE" and event.shift:
self.use_selected = True
return self.execute(context)
def _execute(self, context):
if self.use_selected:
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element or not element.is_a("IfcRoot"):
continue
element.GlobalId = ifcopenshell.guid.new()
obj = context.active_object
if not obj or not obj.BIMAttributeProperties.is_editing_attributes:
return {"FINISHED"}
props = obj.BIMAttributeProperties
element = tool.Ifc.get_entity(obj)
if not element.is_a("IfcRoot"):
return {"FINISHED"}
if self.use_selected and obj in context.selected_objects:
# guid value was already regenerated, just update the ui prop
guid_value = element.GlobalId
def execute(self, context):
index = context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
if index >= 0:
global_id = context.active_object.BIMAttributeProperties.attributes[index]
else:
guid_value = ifcopenshell.guid.new()
props.attributes["GlobalId"].string_value = guid_value
global_id = context.active_object.BIMAttributeProperties.attributes.add()
global_id.name = "GlobalId"
global_id.data_type = "string"
global_id.string_value = ifcopenshell.guid.new()
return {"FINISHED"}
@@ -19,34 +19,34 @@
import blenderbim.bim.helper
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.attribute.data import AttributesData
from blenderbim.bim.module.attribute.data import AttributesData, MaterialAttributesData
def draw_ui(context, layout, attributes):
obj = context.active_object
def draw_ui(context, layout, obj_type, attributes):
obj = context.active_object if obj_type == "Object" else context.active_object.active_material
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
if props.is_editing_attributes:
row = layout.row(align=True)
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
op.obj_type = obj_type
op.obj = obj.name
op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
op.obj_type = obj_type
op.obj = obj.name
blenderbim.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection")
else:
row = layout.row()
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
op.obj_type = obj_type
op.obj = obj.name
for attribute in attributes:
row = layout.row(align=True)
row.label(text=attribute["name"])
# row.label(text=attribute["value"])
op = row.operator("bim.select_similar", text=attribute["value"], icon="NONE", emboss=False)
op.key = attribute['name']
row.label(text=attribute["value"])
# TODO: reimplement, see #1222
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
@@ -72,4 +72,32 @@ class BIM_PT_object_attributes(Panel):
def draw(self, context):
if not AttributesData.is_loaded:
AttributesData.load()
draw_ui(context, self.layout, AttributesData.data["attributes"])
draw_ui(context, self.layout, "Object", AttributesData.data["attributes"])
class BIM_PT_material_attributes(Panel):
bl_label = "Material Attributes"
bl_idname = "BIM_PT_material_attributes"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
@classmethod
def poll(cls, context):
if not IfcStore.get_file():
return False
try:
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
except:
return False
def draw(self, context):
if not MaterialAttributesData.is_loaded:
MaterialAttributesData.load()
elif (
context.active_object.active_material.BIMObjectProperties.ifc_definition_id
!= MaterialAttributesData.data["ifc_definition_id"]
):
MaterialAttributesData.load()
draw_ui(context, self.layout, "Material", MaterialAttributesData.data["attributes"])
@@ -16,8 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import bcf
import bcf.v2.bcfxml
@@ -25,31 +23,12 @@ import bcf.v2.bcfxml
class BcfStore:
bcfxml = None
@classmethod
def get_bcfxml(cls):
if not cls.bcfxml:
bcf_filepath = bpy.context.scene.BCFProperties.bcf_file
if not os.path.isabs(bcf_filepath):
bcf_filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), bcf_filepath))
if bcf_filepath:
try:
cls.bcfxml = bcf.bcfxml.load(bcf_filepath)
except:
# there will be a plenty of "Permission denied" errors
# as many poll() methods will try to access the bcfxml simultaneously
# the first time it's loaded
pass
return cls.bcfxml
@staticmethod
def get_bcfxml():
if not BcfStore.bcfxml:
BcfStore.bcfxml = bcf.v2.bcfxml.BcfXml()
return BcfStore.bcfxml
@classmethod
def set(cls, bcfxml, filepath):
def set(cls, bcfxml):
cls.bcfxml = bcfxml
bpy.context.scene.BCFProperties.bcf_file = filepath
@classmethod
def set_by_filepath(cls, filepath):
cls.set(None, filepath)
@classmethod
def unload_bcfxml(cls):
cls.set(None, "")
@@ -42,9 +42,11 @@ class NewBcfProject(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BCFProperties.is_loaded = False
bcfxml = bcf.v2.bcfxml.BcfXml.create_new("New Project")
bcfstore.BcfStore.set(bcfxml, "")
bcfstore.BcfStore.set(bcfxml)
bpy.ops.bim.load_bcf_project()
context.scene.BCFProperties.is_loaded = True
return {"FINISHED"}
@@ -57,10 +59,11 @@ class LoadBcfProject(bpy.types.Operator):
def execute(self, context):
if self.filepath:
bcfstore.BcfStore.set_by_filepath(self.filepath)
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
bcfxml = bcfstore.BcfStore.get_bcfxml()
context.scene.BCFProperties.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
context.scene.BCFProperties.is_loaded = True
return {"FINISHED"}
def invoke(self, context, event):
@@ -74,7 +77,8 @@ class UnloadBcfProject(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfstore.BcfStore.unload_bcfxml()
bcfstore.BcfStore.set(None)
context.scene.BCFProperties.is_loaded = False
return {"FINISHED"}
@@ -263,7 +267,6 @@ class SaveBcfProject(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.save(self.filepath)
bcfstore.BcfStore.set(bcfxml, self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
@@ -938,7 +941,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
# Operators with context overrides are used because they are
# significantly faster than looping through all objects
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions.component or []}
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions or []}
if viewpoint.visualization_info.components.visibility.default_visibility:
old = context.area.type
@@ -958,13 +961,10 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if objs:
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.hide_view_clear()
context_override = {}
context_override["object"] = context_override["active_object"] = objs[0]
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
with context.temp_override(**context_override):
bpy.ops.object.hide_view_set(unselected=True)
bpy.data.objects["Viewpoint"].hide_set(False)
bpy.ops.object.hide_view_set(context_override, unselected=True)
context.area.type = old
if viewpoint.visualization_info.components.view_setup_hints:
@@ -978,16 +978,14 @@ class ActivateBcfViewpoint(bpy.types.Operator):
self.hide_spaces(context)
self.set_openings_visibility(False, context)
# set selection at the end not to conflict with .hide_spaces
self.set_selection(viewpoint)
self.set_colours(viewpoint)
def hide_spaces(self, context):
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.select_all(action="DESELECT")
bpy.ops.object.select_pattern(pattern="IfcSpace/*")
bpy.ops.object.hide_view_set()
bpy.ops.object.hide_view_set({})
context.area.type = old
def set_openings_visibility(self, is_visible, context):
@@ -1002,7 +1000,6 @@ class ActivateBcfViewpoint(bpy.types.Operator):
obj = IfcStore.get_element(global_id)
if obj:
obj.select_set(True)
obj.hide_set(False)
def set_colours(self, viewpoint):
global_id_colours = {}
@@ -41,37 +41,37 @@ def purge():
def updateBcfReferenceLink(self, context):
if bcfstore.BcfStore.get_bcfxml():
if context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_reference_links()
def updateBcfLabel(self, context):
if bcfstore.BcfStore.get_bcfxml():
if context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_labels()
def updateBcfProjectName(self, context):
if bcfstore.BcfStore.get_bcfxml():
if self.is_loaded:
bpy.ops.bim.edit_bcf_project_name()
def updateBcfAuthor(self, context):
if bcfstore.BcfStore.get_bcfxml():
if self.is_loaded:
bpy.ops.bim.edit_bcf_author()
def updateBcfTopicName(self, context):
if bcfstore.BcfStore.get_bcfxml():
if context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_topic_name()
def updateBcfTopicIsEditable(self, context):
if bcfstore.BcfStore.get_bcfxml() and not self.is_editable:
if context.scene.BCFProperties.is_loaded and not self.is_editable:
bpy.ops.bim.edit_bcf_topic()
def updateBcfCommentIsEditable(self, context):
if bcfstore.BcfStore.get_bcfxml() and not self.is_editable:
if context.scene.BCFProperties.is_loaded and not self.is_editable:
bpy.ops.bim.edit_bcf_comment(comment_guid=self.name)
@@ -151,7 +151,7 @@ class BcfTopic(PropertyGroup):
class BCFProperties(PropertyGroup):
bcf_file: StringProperty(name="BCF File")
is_loaded: BoolProperty(name="Is Loaded", default=False)
comment_text_width: IntProperty(name="Comment Text Width", default=40)
name: StringProperty(default="", name="Project Name", update=updateBcfProjectName)
author: StringProperty(default="john@doe.com", name="Author Email", update=updateBcfAuthor)
@@ -39,7 +39,8 @@ class BIM_PT_bcf(Panel):
scene = context.scene
props = scene.BCFProperties
if not bcfstore.BcfStore.get_bcfxml():
if not props.is_loaded:
row = layout.row(align=True)
row.operator("bim.new_bcf_project", text="New Project")
row.operator("bim.load_bcf_project", text="Load Project")
@@ -22,7 +22,6 @@ from . import ui, operator, prop
classes = (
operator.AddBoundary,
operator.ColourByRelatedBuildingElement,
operator.DecorateBoundaries,
operator.DisableEditingBoundary,
operator.DisableEditingBoundaryGeometry,
operator.EditBoundaryAttributes,
@@ -62,14 +62,14 @@ class BoundaryDecorator:
gpu.state.point_size_set(6)
gpu.state.blend_set("ALPHA")
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
self.line_shader.bind() # required to be able to change uniforms of the shader
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
self.line_shader.uniform_float("lineWidth", 2.0)
# general shader
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
selected_vertices = []
selected_edges = []
@@ -20,12 +20,9 @@ import bpy
import bmesh
import logging
import shapely
import shapely.ops
import mathutils
import numpy as np
import multiprocessing
import ifcopenshell.api
import ifcopenshell.geom
import ifcopenshell.util.unit
import ifcopenshell.util.shape
import ifcopenshell.util.element
@@ -33,13 +30,12 @@ import ifcopenshell.util.placement
import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.bim.import_ifc as import_ifc
from math import pi, inf, degrees, acos, radians
from math import pi, inf
from mathutils import Vector, Matrix
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.decorator import ProfileDecorator
from blenderbim.bim.module.boundary.decorator import BoundaryDecorator
import blenderbim.core
import blenderbim.core.geometry
def get_boundaries_collection(blender_space):
@@ -67,8 +63,7 @@ def disable_editing_boundary_geometry(context):
class Loader:
def __init__(self, operator=None):
self.operator = operator
def __init__(self):
self.ifc_file = None
self.logger = None
self.ifc_importer = None
@@ -90,33 +85,14 @@ class Loader:
tool.Loader.link_mesh(shape, mesh)
except RuntimeError:
# Fallback solution for invalid geometry provided by Revit. (InnerBoundaries cuting OuterBoundary)
self.operator.report(
{"WARNING"},
(
f"IfcRelSpaceBoundary {boundary.id()} mesh creation failed. Geometry might be invalid. Using fallback solution"
),
)
try:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
except RuntimeError:
self.operator.report(
{"WARNING"},
f"Skipping IfcRelSpaceBoundary {boundary.id()}. Fallback solution failed. Geometry might be invalid",
)
return None
print(f"Failed to create mesh from IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid")
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
mesh = bpy.data.meshes.new(str(surface.id()))
bm = bmesh.new()
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
bm.faces.new(verts)
for inner_boundary in surface.InnerBoundaries:
try:
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
except RuntimeError:
self.operator.report(
{"WARNING"},
f"Skipping an inner boundary for IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid",
)
return None
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
for i in range(len(verts) - 1):
bm.edges.new(verts[i : i + 2])
@@ -127,7 +103,7 @@ class Loader:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
matrix = mathutils.Matrix(
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
)
)
matrix.translation *= unit_scale
mesh.transform(matrix)
return mesh
@@ -150,17 +126,7 @@ class Loader:
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
self.ifc_importer.file = self.ifc_file
def load_boundary(
self, boundary: ifcopenshell.entity_instance, blender_space: bpy.types.Object = None
) -> bpy.types.Object:
if not blender_space:
if not boundary.RelatingSpace:
self.operator.report(
{"WARNING"},
f"Skipping IfcRelSpaceBoundary {boundary.id()} which have no relating space. Invalid boundary.",
)
return
blender_space = tool.Ifc.get_object(boundary.RelatingSpace)
def load_boundary(self, boundary, blender_space):
obj = tool.Ifc.get_object(boundary)
if obj:
return obj
@@ -179,9 +145,9 @@ class LoadProjectSpaceBoundaries(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader(self)
loader = Loader()
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
loader.load_boundary(rel)
loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace))
return {"FINISHED"}
@@ -192,7 +158,7 @@ class LoadBoundary(bpy.types.Operator):
boundary_id: bpy.props.IntProperty()
def execute(self, context):
loader = Loader(self)
loader = Loader()
for obj in context.visible_objects:
obj.select_set(False)
obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object)
@@ -207,7 +173,7 @@ class LoadSpaceBoundaries(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
loader = Loader(self)
loader = Loader()
element = tool.Ifc.get_entity(context.active_object)
for rel in element.BoundedBy or []:
loader.load_boundary(rel, context.active_object)
@@ -455,7 +421,7 @@ class EnableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context))
if not bpy.app.background:
tool.Blender.set_viewport_tool("bim.cad_tool")
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
return {"FINISHED"}
@@ -493,7 +459,7 @@ class EditBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_surface)
old_mesh = obj.data
loader = Loader(self)
loader = Loader()
obj.data = loader.create_mesh(element)
tool.Geometry.delete_data(old_mesh)
@@ -514,11 +480,11 @@ class DisableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.show_boundaries"
bl_label = "Show Boundaries"
bl_description = "Load selected objects boundaries if they are not loaded"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
loader = Loader(self)
props = bpy.context.scene.BIMBoundaryProperties
loader = Loader()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element or not getattr(element, "BoundedBy", None):
@@ -529,7 +495,7 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
for rel in element.BoundedBy or []:
boundary_obj = loader.load_boundary(rel, obj)
tool.Boundary.decorate_boundary(boundary_obj)
BoundaryDecorator.install(context)
BoundaryDecorator.install(bpy.context)
return {"FINISHED"}
@@ -562,36 +528,6 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"}
class DecorateBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.decorate_boundaries"
bl_label = "Toggle Boundaries Decorations"
bl_description = "Add special decorations for all boundaries in the scene or hide them if they are already added"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMBoundaryProperties
# filter not decorated boundaries and add decorations for them
decorated_boundaries = set([i.obj for i in props.boundaries])
active_boundaries = set()
for element in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
obj = tool.Ifc.get_object(element)
if obj is not None:
active_boundaries.add(obj)
boundaries_to_decorate = active_boundaries - decorated_boundaries
if boundaries_to_decorate:
for obj in boundaries_to_decorate:
tool.Boundary.decorate_boundary(obj)
BoundaryDecorator.install(context)
else:
for obj in decorated_boundaries:
tool.Boundary.undecorate_boundary(obj)
props.boundaries.clear()
BoundaryDecorator.uninstall()
tool.Blender.update_viewport()
return {"FINISHED"}
class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_boundary"
bl_label = "Add Boundary"
@@ -602,7 +538,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
related_building_element = None
relating_space_obj = None
related_building_element_obj = None
parent_boundaries = []
objs = context.selected_objects
if len(objs) == 2:
@@ -617,19 +552,8 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
else:
related_building_element = element
related_building_element_obj = obj
parent_boundary = self.create_element_boundary(
context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
)
if parent_boundary:
parent_boundaries.append(parent_boundary)
elif len(objs) == 1:
# New prototype, old code not yet removed. Still testing.
space = tool.Ifc.get_entity(objs[0])
if space.is_a("IfcSpace"):
self.auto_generate_boundaries(space, objs[0])
elif len(objs) == 1:
# Optionally the user may select just the space, and the building element shall be auto-detected
# TODO : refactor to be able to generate all boundaries for selected space automatically or with an option
def msg(self, context):
self.layout.label(text="NO ACTIVE STOREY")
@@ -639,6 +563,8 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
relating_space = element
relating_space_obj = objs[0]
target = bpy.context.scene.cursor.location
collection = context.view_layer.active_layer_collection.collection
collection_obj = bpy.data.objects.get(collection.name)
if not collection_obj:
@@ -650,258 +576,19 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
return
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
if not (
subelement.is_a("IfcWall") or subelement.is_a("IfcSlab") or subelement.is_a("IfcVirtualElement")
):
if not (subelement.is_a("IfcWall") or subelement.is_a("IfcSlab")):
continue
obj = tool.Ifc.get_object(subelement)
if obj:
related_building_element = subelement
related_building_element_obj = obj
parent_boundary = self.create_element_boundary(
context,
relating_space,
relating_space_obj,
related_building_element,
related_building_element_obj,
)
if parent_boundary:
parent_boundaries.append(parent_boundary)
raycast = obj.closest_point_on_mesh(obj.matrix_world.inverted() @ target, distance=0.1)
if raycast[0]:
related_building_element = subelement
related_building_element_obj = obj
break
bpy.ops.bim.show_boundaries()
for parent_boundary in parent_boundaries:
obj = tool.Ifc.get_object(parent_boundary)
obj.select_set(True)
def auto_generate_boundaries(self, space, space_obj):
# Identify all potential building elements
# TODO: don't select everything, use AABB culling in Blender
building_elements = (
tool.Ifc.get().by_type("IfcWall")
+ tool.Ifc.get().by_type("IfcSlab")
+ tool.Ifc.get().by_type("IfcVirtualElement")
)
# Don't generate boundaries of building elements that we've already got bounaries for.
for boundary in space.BoundedBy:
if boundary.RelatedBuildingElement in building_elements:
building_elements.remove(boundary.RelatedBuildingElement)
# Create tree of gross shapes of all potential related building elements
include = building_elements + [space]
tree = ifcopenshell.geom.tree()
shapes = {}
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=include)
if iterator.initialize():
while True:
tree.add_element(iterator.get_native())
shape = iterator.get()
shapes[shape.id] = shape
if not iterator.next():
break
# Spatially query all potential boundary elements via a 100mm extension of the space
building_elements = [e for e in tree.select(space, extend=0.1) if e != space]
if not building_elements:
return
# Create a dissolved bmesh for the space
space_bm = bmesh.new()
space_bm.from_mesh(space_obj.data)
bmesh.ops.dissolve_limit(space_bm, angle_limit=pi * 2 / 360, verts=space_bm.verts, edges=space_bm.edges)
# Create dissolved bmeshes for all boundary elements
building_element_bms = {}
for building_element in building_elements:
bm = bmesh.new()
shape = shapes[building_element.id()]
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
for vert in verts:
bm.verts.new(Vector(vert))
bm.verts.ensure_lookup_table()
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
for face in faces:
bm.faces.new([bm.verts[i] for i in face])
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
bm.normal_update() # Needed so that dissolve_limit will work.
bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts[:], edges=bm.edges[:])
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
building_element_bms[building_element.id()] = bm
# Compare space faces and building element faces to see if they relate to one another
for space_face in space_bm.faces:
space_face_normal = space_obj.matrix_world.to_3x3() @ space_face.normal
space_face_vert = space_obj.matrix_world @ space_face.verts[0].co
for building_element in building_elements:
for face in building_element_bms[building_element.id()].faces:
building_obj = tool.Ifc.get_object(building_element)
face_normal = building_obj.matrix_world.to_3x3() @ face.normal
angle = degrees(acos(max(min(space_face_normal.dot(face_normal), 1), -1)))
if tool.Cad.is_x(angle, 180, tolerance=2):
pass # Faces need to be parallel and have opposite normals to be related.
elif building_element.is_a("IfcVirtualElement") and tool.Cad.is_x(angle, 0, tolerance=2):
pass # Virtual elements only need to be parallel to be related, since they are planes.
else:
continue
# Both faces should be close to one another. Say within 50mm.
space_vert = building_obj.matrix_world.inverted() @ space_face_vert
dist = mathutils.geometry.distance_point_to_plane(space_vert, face.verts[0].co, face.normal)
if abs(dist) > 0.05:
continue
# Project the building element face onto the space face
space_face_verts = [v.co.copy() for v in space_face.verts]
space_face_matrix = self.get_face_matrix(*[v.copy() for v in space_face_verts[0:3]])
space_face_matrix_i = space_face_matrix.inverted()
space_face_polygon = shapely.Polygon(
[tuple((space_face_matrix_i @ v).xy) for v in space_face_verts]
)
space_matrix_world_i = space_obj.matrix_world.inverted()
face_verts = [space_matrix_world_i @ building_obj.matrix_world @ v.co.copy() for v in face.verts]
face_polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in face_verts])
gross_boundary_polygon = space_face_polygon.intersection(face_polygon)
if type(gross_boundary_polygon) == shapely.GeometryCollection:
for geom in gross_boundary_polygon.geoms:
if type(geom) == shapely.Polygon:
gross_boundary_polygon = geom
break
if (
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
or gross_boundary_polygon.is_empty
):
continue
# The gross boundary polygon may not be a true gross boundary since it
# may have openings already removed, such as in IFC4 Reference View. So
# we cheat by using the exterior boundary to mean "gross".
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
parent_boundary = tool.Ifc.run(
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
)
if building_element.is_a("IfcVirtualElement"):
parent_boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
else:
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
parent_boundary.InternalOrExternalBoundary = "NOTDEFINED"
if building_element.is_a("IfcWall"):
is_external = ifcopenshell.util.element.get_pset(
building_element, "Pset_WallCommon", "IsExternal"
)
if is_external is True:
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
elif is_external is False:
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
elif building_element.is_a("IfcSlab"):
predefined_type = ifcopenshell.util.element.get_predefined_type(building_element)
if predefined_type == "BASESLAB":
parent_boundary.InternalOrExternalBoundary = "EXTERNAL_EARTH"
else:
is_external = ifcopenshell.util.element.get_pset(
building_element, "Pset_SlabCommon", "IsExternal"
)
if is_external is True:
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
elif is_external is False:
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
parent_boundary.RelatingSpace = space
parent_boundary.RelatedBuildingElement = building_element
parent_boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
exterior_boundary_polygon, space_face_matrix
)
self.set_boundary_name(parent_boundary)
for rel in getattr(building_element, "HasOpenings", []):
opening = rel.RelatedOpeningElement
filling = opening.HasFillings[0].RelatedBuildingElement if opening.HasFillings else None
# Create shape of opening as a dissolved BMesh
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
shape = ifcopenshell.geom.create_shape(settings, opening)
m = shape.transformation.matrix.data
mat = Matrix(
(
[m[0], m[3], m[6], m[9]],
[m[1], m[4], m[7], m[10]],
[m[2], m[5], m[8], m[11]],
[0, 0, 0, 1],
)
)
opening_bm = bmesh.new()
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
for vert in verts:
opening_bm.verts.new(Vector(vert))
opening_bm.verts.ensure_lookup_table()
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
for face in faces:
opening_bm.faces.new([opening_bm.verts[i] for i in face])
opening_bm.verts.ensure_lookup_table()
opening_bm.faces.ensure_lookup_table()
opening_bm.normal_update() # Needed so that dissolve_limit will work.
bmesh.ops.dissolve_limit(
opening_bm, angle_limit=radians(1), verts=opening_bm.verts[:], edges=opening_bm.edges[:]
)
opening_bm.verts.ensure_lookup_table()
opening_bm.faces.ensure_lookup_table()
# Get relevant faces of BMesh that can turn into boundaries
opening_polygons = []
for opening_face in opening_bm.faces:
opening_face_normal = mat.to_3x3() @ opening_face.normal
angle = degrees(acos(max(min(opening_face_normal.dot(face_normal), 1), -1)))
if not tool.Cad.is_x(angle, 180, tolerance=2):
continue # Any non-parallel faces are not relevant
opening_face_verts = [space_matrix_world_i @ mat @ v.co.copy() for v in opening_face.verts]
polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in opening_face_verts])
opening_polygons.append(polygon)
# Merge them all into a single opening polygon for our boundary
opening_polygon = shapely.ops.unary_union(opening_polygons)
# Only openings that are projected onto our exterior boundary are relevant.
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
continue
boundary = tool.Ifc.run(
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
)
boundary.RelatingSpace = space
boundary.RelatedBuildingElement = filling or opening
boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
opening_polygon, space_face_matrix
)
if filling:
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
else:
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
if boundary.is_a() != "IfcRelSpaceBoundary":
boundary.ParentBoundary = parent_boundary
self.set_boundary_name(boundary)
def create_element_boundary(
self, context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
):
if not relating_space or not related_building_element:
return
# Find which face on space should be bounded to related building element
# TODO: Handle round wall where multiple faces need to be bound to the same related building element
bm = bmesh.new()
bm.from_mesh(relating_space_obj.data)
bmesh.ops.dissolve_limit(bm, angle_limit=pi * 2 / 360, verts=bm.verts, edges=bm.edges)
@@ -922,6 +609,8 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
if not target_face:
return
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
# Is this right? Or should I use loop?
target_face_verts = [v.co.copy() for v in target_face.verts]
target_face_matrix = self.get_face_matrix(*[v.copy() for v in target_face_verts[0:3]])
@@ -941,18 +630,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
gross_boundary_polygon = geom
break
if (
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
or gross_boundary_polygon.is_empty
):
return
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
# Set to EXTERNAL by default and turn later to internal if there is a corresponding boundary relating to an
# internal space
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
# The gross boundary polygon may not be a true gross boundary since it
# may have openings already removed, such as in IFC4 Reference View. So
# we cheat by using the exterior boundary to mean "gross". Later, we
@@ -960,25 +637,19 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
# space.
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
net_boundary_polygon = shapely.Polygon(gross_boundary_polygon)
inner_boundaries = []
for rel in getattr(related_building_element, "HasOpenings", []):
opening = rel.RelatedOpeningElement
if not opening.HasFillings:
continue
# TODO: HasFilling is a zero to many relationship. How to handle many ?
filling = opening.HasFillings[0].RelatedBuildingElement
filling = None
if opening.HasFillings:
filling = opening.HasFillings[0].RelatedBuildingElement
opening_polygon = self.get_flattened_polygon(opening, relating_space_obj, target_face_matrix_i)
# An inner boundary is supposed to overlap its parent boundary according to IFC4 documentation
# so we extend our exterior boundary with all opening which has a filling
# Also see Implementation aggreement SB 1.1 for IFC 2x3 TC1 Space Boundary Addon View
# https://standards.buildingsmart.org/MVD/RELEASE/IFC2x3/TC1/SB1_1/IFC%20Space%20Boundary%20Implementation%20Agreement%20Addendum%202010-03-22.pdf
unionised_object = exterior_boundary_polygon.union(opening_polygon)
if isinstance(unionised_object, shapely.Polygon):
exterior_boundary_polygon = unionised_object
net_boundary_polygon = net_boundary_polygon.difference(opening_polygon)
# Only openings that are projected onto our exterior boundary are relevant.
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
continue
@@ -986,25 +657,24 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
connection_geometry = self.create_connection_geometry_from_polygon(opening_polygon, target_face_matrix)
boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
boundary.RelatingSpace = relating_space
boundary.RelatedBuildingElement = filling
boundary.RelatedBuildingElement = filling or related_building_element
boundary.ConnectionGeometry = connection_geometry
if related_building_element.is_a("IfcVirtualElement"):
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
else:
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
self.set_boundary_name(boundary)
boundary.PhysicalOrVirtualBoundary = "PHYSICAL" if filling else "VIRTUAL"
boundary.InternalOrExternalBoundary = "INTERNAL"
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
boundary.ParentBoundary = parent_boundary
connection_geometry = self.create_connection_geometry_from_polygon(
exterior_boundary_polygon, target_face_matrix
)
connection_geometry = self.create_connection_geometry_from_polygon(net_boundary_polygon, target_face_matrix)
parent_boundary.RelatingSpace = relating_space
parent_boundary.RelatedBuildingElement = related_building_element
parent_boundary.ConnectionGeometry = connection_geometry
self.set_boundary_name(parent_boundary)
return parent_boundary
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
bpy.ops.bim.show_boundaries()
obj = tool.Ifc.get_object(parent_boundary)
obj.select_set(True)
def get_face_matrix(self, p1, p2, p3):
edge1 = p2 - p1
@@ -1059,13 +729,11 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
tool.Model.unit_scale = self.unit_scale
surface = tool.Ifc.get().createIfcCurveBoundedPlane()
surface.BasisSurface = tool.Ifc.get().createIfcPlane(
tool.Ifc.get().createIfcAxis2Placement3D(
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
)
)
surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D(
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
))
if tool.Ifc.get().schema != "IFC2X3":
points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords]
@@ -1078,24 +746,9 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False))
else:
pass # TODO
pass # TODO
surface.OuterBoundary = outer_boundary
surface.InnerBoundaries = inner_boundaries
return surface
def set_boundary_name(self, boundary):
"""
By convention 1stLevel and 2ndLevel boundary have specific name and description
See https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRelSpaceBoundary.htm
Warning: IfcRelSpaceBoundary2ndLevel is a subclass of IfcRelSpaceBoundary1stLevel test order matter
"""
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
boundary.Name = "2ndLevel"
if boundary.CorrespondingBoundary:
boundary.Description = "2a"
else:
boundary.Description = "2b"
elif boundary.is_a("IfcRelSpaceBoundary1stLevel"):
boundary.Name = "1stLevel"
@@ -31,7 +31,7 @@ class BIM_PT_SceneBoundaries(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_geometry"
bl_parent_id = "BIM_PT_geometry"
@classmethod
def poll(cls, context):
@@ -22,8 +22,8 @@ from . import ui, prop, operator
classes = (
operator.GetBSDDClassificationProperties,
operator.LoadBSDDDomains,
operator.SearchBSDDClass,
operator.SetActiveBSDDDictionary,
operator.SearchBSDDClassifications,
operator.SetActiveBSDDDomain,
prop.BSDDDomain,
prop.BSDDClassification,
prop.BSDDPset,
@@ -16,42 +16,73 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import bsdd
import json
import ifcopenshell
import blenderbim.tool as tool
from blenderbim.core import bsdd as core
class LoadBSDDDomains(bpy.types.Operator):
bl_idname = "bim.load_bsdd_domains"
bl_label = "Load bSDD Dictionaries"
bl_label = "Load bSDD Domains"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
core.load_bsdd(bsdd.Client(), tool.Bsdd)
props = context.scene.BIMBSDDProperties
props.domains.clear()
client = bsdd.Client()
for domain in sorted(client.Domain(), key=lambda x: x["name"]):
new = props.domains.add()
new.name = domain["name"]
new.namespace_uri = domain["namespaceUri"]
new.default_language_code = domain["defaultLanguageCode"]
new.organization_name_owner = domain["organizationNameOwner"]
new.status = domain["status"]
new.version = domain["version"]
return {"FINISHED"}
class SetActiveBSDDDictionary(bpy.types.Operator):
class SetActiveBSDDDomain(bpy.types.Operator):
bl_idname = "bim.set_active_bsdd_domain"
bl_label = "Load bSDD Dictionary"
bl_label = "Load bSDD Domains"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
uri: bpy.props.StringProperty()
def execute(self, context):
core.set_active_bsdd_dictionary(self.name, self.uri, tool.Bsdd)
props = context.scene.BIMBSDDProperties
props.active_domain = self.name
props.active_uri = self.uri
return {"FINISHED"}
class SearchBSDDClass(bpy.types.Operator):
class SearchBSDDClassifications(bpy.types.Operator):
bl_idname = "bim.search_bsdd_classifications"
bl_label = "Search bSDD Class"
bl_label = "Search bSDD Classifications"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
keyword = context.scene.BIMBSDDProperties.keyword
core.search_class(keyword, bsdd.Client(), tool.Bsdd)
props = context.scene.BIMBSDDProperties
props.classifications.clear()
client = bsdd.Client()
related_ifc_entities = []
if len(props.keyword) < 3:
return {"FINISHED"}
if props.should_filter_ifc_class and context.active_object:
element = tool.Ifc.get_entity(context.active_object)
if element:
related_ifc_entities = [element.is_a()]
results = client.ClassificationSearchOpen(props.keyword, DomainNamespaceUris=[props.active_uri], RelatedIfcEntities=related_ifc_entities)
for result in sorted(results["classifications"], key=lambda x: x["referenceCode"]):
new = props.classifications.add()
new.name = result["name"]
new.reference_code = result["referenceCode"]
new.description = result.get("description", "")
new.namespace_uri = result["namespaceUri"]
new.domain_name = result["domainName"]
new.domain_namespace_uri = result["domainNamespaceUri"]
return {"FINISHED"}
@@ -61,5 +92,41 @@ class GetBSDDClassificationProperties(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
core.get_class_properties(bsdd.Client(), tool.Bsdd)
bprops = context.scene.BIMBSDDProperties
bprops.classification_psets.clear()
bsdd_classification = bprops.classifications[bprops.active_classification_index]
client = bsdd.Client()
data = client.Classification(bsdd_classification.namespace_uri)
properties = data.get("classificationProperties", None)
if not properties:
return {"FINISHED"}
psets = {}
for prop in properties:
if prop.get("propertyDomainName") != "IFC":
continue
pset = prop.get("propertySet", None)
if not pset:
continue
psets.setdefault(pset, {})
predefined_value = prop.get("predefinedValue")
if predefined_value:
possible_values = [predefined_value]
else:
possible_values = prop.get("possibleValues", []) or []
possible_values = [v["value"] for v in possible_values]
psets[pset][prop["name"]] = possible_values
for pset_name, pset in psets.items():
new = bprops.classification_psets.add()
new.name = pset_name
for name, values in pset.items():
new2 = new.properties.add()
new2.name = name
new2.enum_items = json.dumps(values)
new2.data_type = "enum"
return {"FINISHED"}
@@ -33,7 +33,7 @@ from bpy.props import (
class BSDDDomain(PropertyGroup):
name: StringProperty(name="Name")
uri: StringProperty(name="URI")
namespace_uri: StringProperty(name="URI")
default_language_code: StringProperty(name="Language")
organization_name_owner: StringProperty(name="Organization")
status: StringProperty(name="Status")
@@ -44,7 +44,7 @@ class BSDDClassification(PropertyGroup):
name: StringProperty(name="Name")
reference_code: StringProperty(name="Reference Code")
description: StringProperty(name="Description")
uri: StringProperty(name="Namespace URI")
namespace_uri: StringProperty(name="Namespace URI")
domain_name: StringProperty(name="Domain Name")
domain_namespace_uri: StringProperty(name="Domain Namespace URI")
@@ -63,5 +63,4 @@ class BIMBSDDProperties(PropertyGroup):
active_classification_index: IntProperty(name="Active Classification Index")
keyword: StringProperty(name="Keyword")
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
load_preview_domains: BoolProperty(name="Load Preview Domains", default=False)
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
@@ -28,15 +28,10 @@ class BIM_PT_bsdd(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_project_setup"
bl_parent_id = "BIM_PT_project_setup"
def draw(self, context):
props = context.scene.BIMBSDDProperties
row = self.layout.row(align=True)
row.prop(props, "load_preview_domains")
if len(props.domains):
row.operator("bim.load_bsdd_domains", text="", icon="FILE_REFRESH")
if props.active_domain:
row = self.layout.row()
row.label(text="Active: " + props.active_domain, icon="URL")
@@ -62,13 +57,10 @@ class BIM_UL_bsdd_domains(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
if item.status != "Active":
row.label(text=f"{item.name} ({item.organization_name_owner}) - {item.status}", icon="ERROR")
else:
row.label(text=f"{item.name} ({item.organization_name_owner})")
row.label(text=f"{item.name} ({item.organization_name_owner})")
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
op.name = item.name
op.uri = item.uri
op.uri = item.namespace_uri
class BIM_UL_bsdd_classifications(UIList):
@@ -37,7 +37,7 @@ classes = (
def register():
if not bpy.app.background:
bpy.utils.register_tool(workspace.CadTool, after={"builtin.transform"}, separator=True, group=False)
bpy.utils.register_tool(workspace.CadTool, after={"builtin.transform"}, separator=True, group=True)
bpy.types.Scene.BIMCadProperties = bpy.props.PointerProperty(type=prop.BIMCadProperties)
@@ -24,8 +24,6 @@ import bpy_extras
import blenderbim.tool as tool
from mathutils import Vector, Matrix
from math import pi, radians, sin, cos, sqrt
import ifcopenshell.util.unit
messages = {
"SHARED_VERTEX": "Shared Vertex, no intersection possible",
@@ -142,9 +140,6 @@ class CadFillet(bpy.types.Operator):
layout.prop(self, prop)
def execute(self, context):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
self.radius = self.radius * si_conversion
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
@@ -366,10 +361,6 @@ class CadOffset(bpy.types.Operator):
# dialog that Mesh Tools has. Sverchok is 2D based, but has a weird side
# effect of converting an unclosed loop into a closed loop which is also
# not what users expect.
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
self.distance = self.distance * si_conversion
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
@@ -507,8 +498,6 @@ class CadOffset(bpy.types.Operator):
local_direction = (rotation_i @ direction).normalized()
normals.append(local_direction)
if len(normals) == 2:
# https://stackoverflow.com/a/54042831/9627415
new_normal = (normals[0].lerp(normals[1], 0.5)).normalized()
@@ -542,7 +531,7 @@ class CadOffset(bpy.types.Operator):
class AddIfcCircle(bpy.types.Operator):
bl_idname = "bim.add_ifccircle"
bl_label = "Add IfcCircle"
radius: bpy.props.FloatProperty(name="Radius", default=0.5)
radius: bpy.props.FloatProperty(name="Radius", default=0.1)
@classmethod
def poll(cls, context):
@@ -550,9 +539,6 @@ class AddIfcCircle(bpy.types.Operator):
return bool(obj) and obj.type == "MESH"
def execute(self, context):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
self.radius = self.radius * si_conversion
if self.has_selected_existing_circle(context):
self.change_radius(context)
else:
@@ -636,9 +622,6 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
layout.prop(self, prop)
def execute(self, context):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
self.radius = self.radius * si_conversion
if self.has_selected_existing_arc(context):
self.change_radius(context)
else:
@@ -803,8 +786,8 @@ class AlignViewToProfile(bpy.types.Operator):
class AddRectangle(bpy.types.Operator):
bl_idname = "bim.add_rectangle"
bl_label = "Add Rectangle"
x: bpy.props.FloatProperty(name="X", default=1)
y: bpy.props.FloatProperty(name="Y", default=1)
x: bpy.props.FloatProperty(name="X", default=0.1)
y: bpy.props.FloatProperty(name="Y", default=0.1)
@classmethod
def poll(cls, context):
@@ -812,10 +795,6 @@ class AddRectangle(bpy.types.Operator):
return bool(obj) and obj.type == "MESH"
def execute(self, context):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
self.x = self.x * si_conversion
self.y = self.y * si_conversion
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
cursor = obj.matrix_world.inverted() @ context.scene.cursor.location
@@ -24,10 +24,10 @@ from math import pi
class BIMCadProperties(PropertyGroup):
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
radius: bpy.props.FloatProperty(name="Radius", default=0.1, subtype="DISTANCE")
distance: bpy.props.FloatProperty(name="Distance", default=0.1, subtype="DISTANCE")
x: bpy.props.FloatProperty(name="X", default=0.2, subtype="DISTANCE")
y: bpy.props.FloatProperty(name="Y", default=0.1, subtype="DISTANCE")
radius: bpy.props.FloatProperty(name="Radius", default=0.1)
distance: bpy.props.FloatProperty(name="Distance", default=0.1)
x: bpy.props.FloatProperty(name="X", default=0.2)
y: bpy.props.FloatProperty(name="Y", default=0.1)
gable_roof_edge_angle: bpy.props.FloatProperty(
name="Gable Roof Edge Angle", default=pi / 2, soft_min=0, soft_max=pi / 2, subtype="ANGLE"
)
@@ -20,7 +20,6 @@ import os
import bpy
import blenderbim.tool as tool
import blenderbim.bim.module.type.prop as type_prop
import ifcopenshell.util.unit
from bpy.types import WorkSpaceTool
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData
@@ -240,9 +239,8 @@ class CadHotkey(bpy.types.Operator):
row.prop(props, "resolution")
def hotkey_S_C(self):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
if self.is_profile():
bpy.ops.bim.add_ifccircle(radius=self.props.radius / si_conversion)
bpy.ops.bim.add_ifccircle(radius=self.props.radius)
else:
bpy.ops.bim.cad_arc_from_2_points()
@@ -250,15 +248,13 @@ class CadHotkey(bpy.types.Operator):
bpy.ops.bim.cad_trim_extend()
def hotkey_S_F(self):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
if self.is_profile():
bpy.ops.bim.add_ifcarcindex_fillet(radius=self.props.radius / si_conversion)
bpy.ops.bim.add_ifcarcindex_fillet(radius=self.props.radius)
else:
bpy.ops.bim.cad_fillet(resolution=self.props.resolution, radius=self.props.radius / si_conversion)
bpy.ops.bim.cad_fillet(resolution=self.props.resolution, radius=self.props.radius)
def hotkey_S_O(self):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bpy.ops.bim.cad_offset(distance=self.props.distance / si_conversion)
bpy.ops.bim.cad_offset(distance=self.props.distance)
def hotkey_S_Q(self):
element = tool.Ifc.get_entity(bpy.context.active_object)
@@ -274,8 +270,7 @@ class CadHotkey(bpy.types.Operator):
def hotkey_S_R(self):
if self.is_profile():
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bpy.ops.bim.add_rectangle(x=self.props.x / si_conversion, y=self.props.y / si_conversion)
bpy.ops.bim.add_rectangle(x=self.props.x, y=self.props.y)
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["pset_data"]
@@ -20,29 +20,26 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddClashSet,
operator.AddClashSource,
operator.ExecuteIfcClash,
operator.ExportClashSets,
operator.ImportClashSets,
operator.LoadSmartGroupsForActiveClashSet,
operator.AddClashSet,
operator.RemoveClashSet,
operator.AddClashSource,
operator.RemoveClashSource,
operator.SelectClash,
operator.SelectClashResults,
operator.SelectClashSource,
operator.SelectSmartGroup,
operator.ExecuteIfcClash,
operator.SelectIfcClashResults,
operator.SelectClashResults,
operator.SelectSmartGroupedClashesPath,
operator.SmartClashGroup,
prop.Clash,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
prop.ClashSource,
prop.ClashSet,
prop.SmartClashGroup,
prop.BIMClashProperties,
ui.BIM_PT_ifcclash,
ui.BIM_PT_clash_manager,
ui.BIM_PT_smart_clash_manager,
ui.BIM_UL_clashes,
ui.BIM_UL_clash_sets,
ui.BIM_UL_smart_groups,
)
@@ -1,52 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import json
import ifcopenshell.util.element
import blenderbim.tool as tool
def refresh():
ClashData.is_loaded = False
class ClashData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {}
cls.data["saved_searches"] = cls.saved_searches()
@classmethod
def saved_searches(cls):
if not tool.Ifc.get():
return []
groups = tool.Ifc.get().by_type("IfcGroup")
results = []
for group in groups:
try:
data = json.loads(group.Description)
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search" and data.get("query", None):
results.append(group)
except:
pass
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
@@ -1,102 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blf
import gpu
import bmesh
import blenderbim.tool as tool
from bpy.types import SpaceView3D
from mathutils import Vector
from gpu_extras.batch import batch_for_shader
from bpy_extras.view3d_utils import location_3d_to_region_2d
class ClashDecorator:
is_installed = False
handlers = []
@classmethod
def install(cls, context):
if cls.is_installed:
cls.uninstall()
handler = cls()
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_text, (context,), "WINDOW", "POST_PIXEL"))
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_geometry, (context,), "WINDOW", "POST_VIEW"))
cls.is_installed = True
@classmethod
def uninstall(cls):
for handler in cls.handlers:
try:
SpaceView3D.draw_handler_remove(handler, "WINDOW")
except ValueError:
pass
cls.is_installed = False
def draw_batch(self, shader_type, content_pos, color, indices=None):
shader = self.line_shader if shader_type == "LINES" else self.shader
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
shader.uniform_float("color", color)
batch.draw(shader)
def draw_text(self, context):
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
selected_elements_color = self.addon_prefs.decorator_color_selected
unselected_elements_color = self.addon_prefs.decorator_color_unselected
special_elements_color = self.addon_prefs.decorator_color_special
text = context.scene.BIMClashProperties.active_clash_text
p = context.scene.BIMClashProperties.p1.lerp(context.scene.BIMClashProperties.p2, 0.5)
font_id = 0
blf.size(font_id, 12)
coords_2d = location_3d_to_region_2d(context.region, context.region_data, p)
color = self.addon_prefs.decorations_colour
blf.color(font_id, *color)
if coords_2d:
w, h = blf.dimensions(font_id, text)
coords_2d -= Vector((w * .5, 0))
blf.position(font_id, coords_2d[0], coords_2d[1], 0)
blf.draw(font_id, text) # Set your text here
def draw_geometry(self, context):
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
selected_elements_color = self.addon_prefs.decorator_color_selected
unselected_elements_color = self.addon_prefs.decorator_color_unselected
special_elements_color = self.addon_prefs.decorator_color_special
gpu.state.point_size_set(6)
gpu.state.blend_set("ALPHA")
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
self.line_shader.bind() # required to be able to change uniforms of the shader
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
self.line_shader.uniform_float("lineWidth", 2.0)
# general shader
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
selected_vertices = [context.scene.BIMClashProperties.p1, context.scene.BIMClashProperties.p2]
selected_edges = []
if selected_vertices[0] != selected_vertices[1]:
selected_edges = [[0, 1]]
self.draw_batch("POINTS", selected_vertices, special_elements_color)
if selected_edges:
self.draw_batch("LINES", selected_vertices, special_elements_color, selected_edges)
@@ -23,11 +23,9 @@ import bmesh
import logging
import numpy as np
import ifcopenshell
import blenderbim.tool as tool
from math import radians
from mathutils import Matrix, Vector
from math import radians
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.clash.decorator import ClashDecorator
class ExportClashSets(bpy.types.Operator):
@@ -46,7 +44,18 @@ class ExportClashSets(bpy.types.Operator):
def execute(self, context):
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
clash_sets = tool.Clash.export_clash_sets()
clash_sets = []
for clash_set in context.scene.BIMClashProperties.clash_sets:
self.a = []
self.b = []
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
clash_source = {"file": data.name}
if data.selector:
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b})
with open(self.filepath, "w") as destination:
destination.write(json.dumps(clash_sets, indent=4))
return {"FINISHED"}
@@ -68,34 +77,25 @@ class ImportClashSets(bpy.types.Operator):
return {"RUNNING_MODAL"}
def execute(self, context):
tool.Clash.load_clash_sets(self.filepath)
context.scene.BIMClashProperties.clash_sets.clear()
for clash_set in tool.Clash.get_clash_sets():
with open(self.filepath) as f:
clash_sets = json.load(f)
for clash_set in clash_sets:
new = context.scene.BIMClashProperties.clash_sets.add()
new.name = clash_set["name"]
new.mode = clash_set["mode"]
if new.mode == "intersection":
new.tolerance = clash_set["tolerance"]
new.check_all = clash_set["check_all"]
elif new.mode == "collision":
new.allow_touching = clash_set["allow_touching"]
elif new.mode == "clearance":
new.clearance = clash_set["clearance"]
new.check_all = clash_set["check_all"]
new.tolerance = clash_set["tolerance"]
for clash_source in clash_set["a"]:
new_source = new.a.add()
new_source.name = clash_source["file"]
if "selector" in clash_source:
tool.Search.import_filter_query(clash_source["selector"], new_source.filter_groups)
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
if "b" in clash_set and clash_set["b"]:
if clash_set["b"]:
for clash_source in clash_set["b"]:
new_source = new.b.add()
new_source.name = clash_source["file"]
if "selector" in clash_source:
tool.Search.import_filter_query(clash_source["selector"], new_source.filter_groups)
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
tool.Clash.import_active_clashes()
return {"FINISHED"}
@@ -108,6 +108,7 @@ class AddClashSet(bpy.types.Operator):
def execute(self, context):
new = context.scene.BIMClashProperties.clash_sets.add()
new.name = "New Clash Set"
new.tolerance = 0.01
return {"FINISHED"}
@@ -209,11 +210,8 @@ class ExecuteIfcClash(bpy.types.Operator):
def invoke(self, context, event):
_, extension = os.path.splitext(self.filepath)
if extension != ".bcf":
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
# TODO Temporarily until BCF support comes back
# if extension != ".json":
# self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
if extension != ".json":
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
@@ -221,12 +219,7 @@ class ExecuteIfcClash(bpy.types.Operator):
def execute(self, context):
from ifcclash import ifcclash
self.props = context.scene.BIMClashProperties
_, extension = os.path.splitext(self.filepath)
if extension != ".bcf":
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
# TODO Temporarily until BCF support comes back
if extension != ".json":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
@@ -236,7 +229,7 @@ class ExecuteIfcClash(bpy.types.Operator):
settings.logger.setLevel(logging.DEBUG)
clasher = ifcclash.Clasher(settings)
if self.props.should_create_clash_snapshots:
if context.scene.BIMClashProperties.should_create_clash_snapshots:
def get_viewpoint_snapshot(viewpoint):
camera = bpy.data.objects.get("IFC Clash Camera")
@@ -252,14 +245,12 @@ class ExecuteIfcClash(bpy.types.Operator):
y = Vector([y.x, y.y, y.z])
x = y.cross(z)
mat = Matrix(
[
[x[0], y[0], z[0], p.x],
[x[1], y[1], z[1], p.y],
[x[2], y[2], z[2], p.z],
[0, 0, 0, 0],
]
)
mat = Matrix([
[x[0], y[0], z[0], p.x],
[x[1], y[1], z[1], p.y],
[x[2], y[2], z[2], p.z],
[0, 0, 0, 0],
])
camera.matrix_world = mat
context.scene.camera = camera
@@ -277,13 +268,23 @@ class ExecuteIfcClash(bpy.types.Operator):
clasher.get_viewpoint_snapshot = get_viewpoint_snapshot
clasher.clash_sets = tool.Clash.export_clash_sets()
clasher.clash_sets = []
for clash_set in context.scene.BIMClashProperties.clash_sets:
self.a = []
self.b = []
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
clash_source = {"file": data.name}
if data.selector:
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
clash_set_data = {"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a}
if self.b:
clash_set_data["b"] = self.b
clasher.clash_sets.append(clash_set_data)
clasher.clash()
clasher.export()
if extension == ".json":
tool.Clash.load_clash_sets(self.filepath)
tool.Clash.import_active_clashes()
return {"FINISHED"}
@@ -302,7 +303,6 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"RUNNING_MODAL"}
def execute(self, context):
# TODO refactor into new clash results system
self.file = IfcStore.get_file()
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
with open(self.filepath) as f:
@@ -349,40 +349,6 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"FINISHED"}
class SelectClash(bpy.types.Operator):
bl_idname = "bim.select_clash"
bl_label = "Select Clash"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select the clashing IFC geometry stored in a file"
index: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMClashProperties
clash_set = tool.Clash.get_clash_set(self.props.active_clash_set.name)
active_clash = self.props.active_clash
clash = tool.Clash.get_clash(clash_set, active_clash.a_global_id, active_clash.b_global_id)
if not clash:
return {"FINISHED"}
products = []
for global_id in (clash["a_global_id"], clash["b_global_id"]):
try:
products.append(tool.Ifc.get().by_guid(global_id))
except:
pass
tool.Spatial.select_products(products, unhide=True)
ClashDecorator.install(bpy.context)
target = Vector(clash["p1"])
tool.Clash.look_at(target, target + Vector((5, 5, 5)))
self.props.p1 = clash["p1"]
self.props.p2 = clash["p2"]
self.props.active_clash_text = clash["type"].title() + " " + str(round(clash["distance"] * 1000)) + "mm"
return {"FINISHED"}
class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes"
@@ -467,9 +433,9 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
new_group.number = f"{smart_group}"
for pair in global_id_pairs:
for guid in pair:
for id in pair:
new_global_id = new_group.global_ids.add()
new_global_id.guid = guid
new_global_id.name = id
return {"FINISHED"}
@@ -484,16 +450,20 @@ class SelectSmartGroup(bpy.types.Operator):
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
def execute(self, context):
self.file = IfcStore.get_file()
# Select smart group in view
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
products = []
for global_id in selected_smart_group.global_ids:
try:
products.append(tool.Ifc.get().by_guid(global_id.guid))
except:
continue
tool.Spatial.select_products(products, unhide=True)
context_override = tool.Blender.get_viewport_context()
with bpy.context.temp_override(**context_override):
bpy.ops.view3d.view_selected()
return {"FINISHED"}
# print(selected_smart_group.number)
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
for id in selected_smart_group.global_ids:
# print("Id: ", id)
# print("Global id: ", element.GlobalId)
if element.GlobalId in id.name:
# print("object match: ", global_id)
obj.select_set(True)
return {"FINISHED"}
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute, BIMFilterGroup
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -33,48 +33,21 @@ from bpy.props import (
class ClashSource(PropertyGroup):
name: StringProperty(name="File")
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
selector: StringProperty(name="Selector")
mode: EnumProperty(
items=[
("a", "All Elements", "All elements will be used for clashing"),
("i", "Include", "Only the selected elements are included for clashing"),
("e", "Exclude", "All elements except the selected elements are included for clashing"),
("i", "Include", "Only the selected objects are included for clashing"),
("e", "Exclude", "All objects except the selected objects are included for clashing"),
],
name="Mode",
)
class Clash(PropertyGroup):
a_global_id: StringProperty(name="A")
b_global_id: StringProperty(name="B")
a_name: StringProperty(name="A Name")
b_name: StringProperty(name="B Name")
status: BoolProperty(name="Status", default=False)
class ClashSet(PropertyGroup):
name: StringProperty(name="Name")
mode: EnumProperty(
items=[
(
"intersection",
"Intersection",
"Detect objects that protrude or pierce another object",
"PIVOT_MEDIAN",
1,
),
("collision", "Collision", "Detect touching objects with any surface collision", "PIVOT_INDIVIDUAL", 2),
("clearance", "Clearance", "Detect objects within a proximity threshold", "PIVOT_ACTIVE", 3),
],
name="Mode",
)
tolerance: FloatProperty(name="Tolerance", default=0.002)
clearance: FloatProperty(name="Clearance", default=0.01)
allow_touching: BoolProperty(name="Allow Touching", default=False)
check_all: BoolProperty(name="Check All", default=False)
tolerance: FloatProperty(name="Tolerance")
a: CollectionProperty(name="Group A", type=ClashSource)
b: CollectionProperty(name="Group B", type=ClashSource)
clashes: CollectionProperty(name="Clashes", type=Clash)
class SmartClashGroup(PropertyGroup):
@@ -90,27 +63,20 @@ class BIMClashProperties(PropertyGroup):
clash_results_path: StringProperty(name="Clash Results Path")
smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path")
active_clash_set_index: IntProperty(name="Active Clash Set Index")
active_clash_index: IntProperty(name="Active Clash Index")
smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup)
active_smart_group_index: IntProperty(name="Active Smart Group Index")
smart_clash_grouping_max_distance: IntProperty(
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
)
p1: FloatVectorProperty(name="P1", default=(0.0, 0.0, 0.0), subtype="XYZ")
p2: FloatVectorProperty(name="P2", default=(0.0, 0.0, 0.0), subtype="XYZ")
active_clash_text: StringProperty(name="Active Clash Text")
@property
def active_clash_set(self):
if self.active_clash_set_index < len(self.clash_sets):
return self.clash_sets[self.active_clash_set_index]
if not self.clash_sets:
return None
return self.clash_sets[self.active_clash_set_index]
@property
def active_smart_group(self):
if self.active_smart_group_index < len(self.smart_clash_groups):
return self.smart_clash_groups[self.active_smart_group_index]
@property
def active_clash(self):
if self.active_clash_index < len(self.active_clash_set.clashes):
return self.active_clash_set.clashes[self.active_clash_index]
if not self.smart_clash_groups:
return None
return self.smart_clash_groups[self.active_smart_group_index]
+45 -100
View File
@@ -17,9 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.bim.helper
from bpy.types import Panel
from blenderbim.bim.module.clash.data import ClashData
class BIM_PT_ifcclash(Panel):
@@ -32,9 +30,6 @@ class BIM_PT_ifcclash(Panel):
bl_parent_id = "BIM_PT_tab_clash_detection"
def draw(self, context):
if not ClashData.is_loaded:
ClashData.load()
layout = self.layout
scene = context.scene
@@ -50,83 +45,57 @@ class BIM_PT_ifcclash(Panel):
layout.template_list("BIM_UL_clash_sets", "", props, "clash_sets", props, "active_clash_set_index")
if not props.active_clash_set:
return
if props.active_clash_set_index < len(props.clash_sets):
clash_set = props.active_clash_set
clash_set = props.active_clash_set
row = layout.row(align=True)
row.prop(clash_set, "name")
row.operator("bim.remove_clash_set", icon="X", text="").index = props.active_clash_set_index
row = layout.row(align=True)
row.prop(clash_set, "name")
row.operator("bim.remove_clash_set", icon="X", text="").index = props.active_clash_set_index
row = layout.row()
row.prop(clash_set, "mode")
if clash_set.mode == "intersection":
row = layout.row()
row = layout.row(align=True)
row.prop(clash_set, "tolerance")
row = layout.row()
row.prop(clash_set, "check_all")
elif clash_set.mode == "collision":
row = layout.row()
row.prop(clash_set, "allow_touching")
elif clash_set.mode == "clearance":
row = layout.row()
row.prop(clash_set, "clearance")
row = layout.row()
row.prop(clash_set, "check_all")
row = layout.row(align=True)
row.label(text="Group A:", icon="OUTLINER_OB_POINTCLOUD")
row.operator("bim.add_clash_source", icon="ADD", text="").group = "a"
layout.label(text="Group A:")
row = layout.row()
row.operator("bim.add_clash_source").group = "a"
for index, source in enumerate(clash_set.a):
for index, source in enumerate(clash_set.a):
row = layout.row(align=True)
row.prop(source, "name", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "a"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "a"
row = layout.row(align=True)
row.prop(source, "mode", text="")
row.prop(source, "selector", text="")
layout.label(text="Group B:")
row = layout.row()
row.operator("bim.add_clash_source").group = "b"
for index, source in enumerate(clash_set.b):
row = layout.row(align=True)
row.prop(source, "name", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "b"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "b"
row = layout.row(align=True)
row.prop(source, "mode", text="")
row.prop(source, "selector", text="")
row = layout.row()
row.prop(props, "should_create_clash_snapshots")
row = layout.row(align=True)
row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "a"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "a"
if source.mode != "a":
blenderbim.bim.helper.draw_filter(
layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_a_{index}"
)
row = layout.row(align=True)
row.label(text="Group B:", icon="OUTLINER_OB_POINTCLOUD")
row.operator("bim.add_clash_source", icon="ADD", text="").group = "b"
for index, source in enumerate(clash_set.b):
row = layout.row(align=True)
row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "b"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "b"
if source.mode != "a":
blenderbim.bim.helper.draw_filter(
layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_b_{index}"
)
row = layout.row()
row.prop(props, "should_create_clash_snapshots")
row = layout.row()
row.operator("bim.execute_ifc_clash")
row = layout.row()
row.label(text=f"{len(clash_set.clashes)} Clashes Found", icon="PIVOT_CURSOR")
layout.template_list("BIM_UL_clashes", "", props.active_clash_set, "clashes", props, "active_clash_index")
row = layout.row()
row.operator("bim.select_clash")
row.operator("bim.execute_ifc_clash")
row.operator("bim.select_ifc_clash_results")
class BIM_PT_clash_manager(Panel):
@@ -138,19 +107,6 @@ class BIM_PT_clash_manager(Panel):
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_clash_detection"
def draw(self, context):
pass
class BIM_PT_smart_clash_manager(Panel):
bl_idname = "BIM_PT_smart_clash_manager"
bl_label = "Smart Clash Manager"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_clash_manager"
def draw(self, context):
layout = self.layout
props = context.scene.BIMClashProperties
@@ -200,14 +156,3 @@ class BIM_UL_smart_groups(bpy.types.UIList):
layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0)
else:
layout.label(text="", translate=False)
class BIM_UL_clashes(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=str(item.a_name), translate=False, icon="NONE", icon_value=0)
row.label(text=str(item.b_name), translate=False, icon="NONE", icon_value=0)
row.prop(item, "status", text="")
else:
layout.label(text="", translate=False)
@@ -24,18 +24,12 @@ classes = (
operator.AddClassificationFromBSDD,
operator.AddClassificationReference,
operator.AddClassificationReferenceFromBSDD,
operator.AddManualClassification,
operator.AddManualClassificationReference,
operator.ChangeClassificationLevel,
operator.DisableAddingManualClassification,
operator.DisableAddingManualClassificationReference,
operator.DisableEditingClassification,
operator.DisableEditingClassificationReference,
operator.DisableEditingClassificationReferences,
operator.EditClassification,
operator.EditClassificationReference,
operator.EnableAddingManualClassification,
operator.EnableAddingManualClassificationReference,
operator.EnableEditingClassification,
operator.EnableEditingClassificationReference,
operator.LoadClassificationLibrary,
@@ -54,11 +48,11 @@ classes = (
def register():
bpy.types.Scene.BIMClassificationProperties = bpy.props.PointerProperty(type=prop.BIMClassificationProperties)
bpy.types.Scene.BIMClassificationReferenceProperties = bpy.props.PointerProperty(
bpy.types.Object.BIMClassificationReferenceProperties = bpy.props.PointerProperty(
type=prop.BIMClassificationReferenceProperties
)
def unregister():
del bpy.types.Scene.BIMClassificationProperties
del bpy.types.Scene.BIMClassificationReferenceProperties
del bpy.types.Object.BIMClassificationReferenceProperties
@@ -73,10 +73,6 @@ class ReferencesData:
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
return name
@classmethod
def classifications(cls):
return [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type("IfcClassification")]
class ClassificationReferencesData(ReferencesData):
data = {}
@@ -87,8 +83,6 @@ class ClassificationReferencesData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "Object"
@classmethod
def references(cls):
@@ -111,22 +105,16 @@ class MaterialClassificationsData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "Material"
@classmethod
def references(cls):
results = []
props = bpy.context.scene.BIMMaterialProperties
if props.materials and props.active_material_index < len(props.materials):
material = props.materials[props.active_material_index]
if material.ifc_definition_id:
element = tool.Ifc.get().by_id(material.ifc_definition_id)
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
return results
@@ -139,8 +127,6 @@ class CostClassificationsData(ReferencesData):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "Cost"
@classmethod
def references(cls):
@@ -20,8 +20,6 @@ import bpy
import json
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.classification
import ifcopenshell.util.element
import blenderbim.tool as tool
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore
@@ -56,61 +54,6 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
)
class AddManualClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_manual_classification"
bl_label = "Add Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMClassificationProperties
attributes = blenderbim.bim.helper.export_attributes(props.classification_attributes)
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification="Unnamed"
)
ifcopenshell.api.run(
"classification.edit_classification", tool.Ifc.get(), classification=classification, attributes=attributes
)
props.is_adding = False
class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_manual_classification_reference"
bl_label = "Add Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def _execute(self, context):
if self.obj_type == "Object":
if context.selected_objects:
objects = [o.name for o in context.selected_objects]
else:
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationReferenceProperties
attributes = blenderbim.bim.helper.export_attributes(props.reference_attributes)
products = [
tool.Ifc.get().by_id(ifc_definition_id)
for obj in objects
if (ifc_definition_id := tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context))
]
if products:
classification = tool.Ifc.get().by_id(int(props.classifications))
reference = ifcopenshell.api.run(
"classification.add_reference",
tool.Ifc.get(),
products=products,
classification=classification,
identification="X",
name="Unnamed",
)
ifcopenshell.api.run(
"classification.edit_reference", tool.Ifc.get(), reference=reference, attributes=attributes
)
props.is_adding = False
class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_classification_from_bsdd"
bl_label = "Add Classification From bSDD"
@@ -120,65 +63,16 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
props = context.scene.BIMBSDDProperties
domain = [d for d in props.domains if d.name == props.active_domain][0]
for element in tool.Ifc.get().by_type("IfcClassification"):
if element.Name == props.active_domain or element.Location == domain.uri:
if element.Name == props.active_domain or element.Location == domain.namespace_uri:
return
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
)
classification.Source = domain.organization_name_owner
classification.Location = domain.uri
classification.Location = domain.namespace_uri
classification.Edition = domain.version
class EnableAddingManualClassification(bpy.types.Operator):
bl_idname = "bim.enable_adding_manual_classification"
bl_label = "Enable Adding Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props.is_adding = True
props.active_classification_id = 0
props.classification_attributes.clear()
blenderbim.bim.helper.import_attributes2("IfcClassification", props.classification_attributes)
return {"FINISHED"}
class DisableAddingManualClassification(bpy.types.Operator):
bl_idname = "bim.disable_adding_manual_classification"
bl_label = "Disable Adding Manual Classification"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props.is_adding = False
return {"FINISHED"}
class EnableAddingManualClassificationReference(bpy.types.Operator):
bl_idname = "bim.enable_adding_manual_classification_reference"
bl_label = "Enable Adding Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props.is_adding = True
props.reference_attributes.clear()
blenderbim.bim.helper.import_attributes2("IfcClassificationReference", props.reference_attributes)
return {"FINISHED"}
class DisableAddingManualClassificationReference(bpy.types.Operator):
bl_idname = "bim.disable_adding_manual_classification_reference"
bl_label = "Disable Adding Manual Classification Reference"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props.is_adding = False
return {"FINISHED"}
class EnableEditingClassification(bpy.types.Operator):
bl_idname = "bim.enable_editing_classification"
bl_label = "Enable Editing Classification"
@@ -218,10 +112,6 @@ class DisableEditingClassification(bpy.types.Operator):
class RemoveClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_classification"
bl_label = "Remove Classification"
bl_description = (
"The classification and all of its relationships, children references, "
"and relationships between objects and child references will be completely removed from a project"
)
bl_options = {"REGISTER", "UNDO"}
classification: bpy.props.IntProperty()
@@ -266,7 +156,8 @@ class EnableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
props = obj.BIMClassificationReferenceProperties
props.reference_attributes.clear()
blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.reference), props.reference_attributes)
props.active_reference_id = self.reference
@@ -280,7 +171,8 @@ class DisableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
context.scene.BIMClassificationReferenceProperties.active_reference_id = 0
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
obj.BIMClassificationReferenceProperties.active_reference_id = 0
return {"FINISHED"}
@@ -302,28 +194,21 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
else:
objects = [self.obj]
active_reference = tool.Ifc.get().by_id(self.reference)
identification = active_reference[1]
identification = tool.Ifc.get().by_id(self.reference)[0]
elements_by_references: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]] = {}
for obj in objects:
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
element = tool.Ifc.get().by_id(ifc_definition_id)
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
for reference in references:
if (identification and reference[1] == identification) or (
not identification and reference == active_reference
):
elements_by_references.setdefault(reference, []).append(element)
if elements_by_references:
for reference, products in elements_by_references.items():
ifcopenshell.api.run(
"classification.remove_reference",
tool.Ifc.get(),
reference=reference,
products=products,
)
if reference[0] == identification:
ifcopenshell.api.run(
"classification.remove_reference",
tool.Ifc.get(),
reference=reference,
product=element,
)
break
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
@@ -333,7 +218,8 @@ class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
obj: bpy.props.StringProperty()
def _execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
props = obj.BIMClassificationReferenceProperties
attributes = {}
for attribute in props.reference_attributes:
if attribute.is_null:
@@ -374,17 +260,15 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
classification = element
break
products = [
tool.Ifc.get().by_id(ifc_definition_id)
for obj in objects
if (ifc_definition_id := tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context))
]
if products:
for obj in objects:
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
if not ifc_definition_id:
continue
ifcopenshell.api.run(
"classification.add_reference",
tool.Ifc.get(),
reference=IfcStore.classification_file.by_id(self.reference),
products=products,
product=tool.Ifc.get().by_id(ifc_definition_id),
classification=classification,
)
@@ -425,47 +309,32 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
classification.Location = bsdd_classification.domain_namespace_uri
for obj in objects:
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
if not ifc_definition_id:
continue
element = tool.Ifc.get().by_id(ifc_definition_id)
reference = ifcopenshell.api.run(
"classification.add_reference",
tool.Ifc.get(),
products=[element],
product=element,
classification=classification,
identification=bsdd_classification.reference_code,
name=bsdd_classification.name,
)
reference.Location = bsdd_classification.uri
reference.Location = bsdd_classification.namespace_uri
for classification_pset in bprops.classification_psets:
is_pset = not classification_pset.name.startswith("Qto_")
if is_pset:
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, psets_only=True)
else:
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, qtos_only=True)
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name)
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
elif is_pset:
else:
pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=element, name=classification_pset.name
)
else:
pset = ifcopenshell.api.run(
"pset.add_qto", tool.Ifc.get(), product=element, name=classification_pset.name
)
properties = {}
for prop in classification_pset.properties:
properties[prop.name] = prop.get_value()
if is_pset:
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
else:
ifcopenshell.api.run("pset.edit_qto", tool.Ifc.get(), qto=pset, properties=properties)
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
class ChangeClassificationLevel(bpy.types.Operator):
@@ -17,10 +17,9 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.classification.data import ClassificationsData, ClassificationReferencesData
from blenderbim.bim.module.classification.data import ClassificationsData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -40,12 +39,6 @@ def get_available_classifications(self, context):
return ClassificationsData.data["available_classifications"]
def get_classifications(self, context):
if not ClassificationReferencesData.is_loaded:
ClassificationReferencesData.load()
return ClassificationReferencesData.data["classifications"]
class ClassificationReference(PropertyGroup):
name: StringProperty(name="Name")
identification: StringProperty(name="Identification")
@@ -55,7 +48,6 @@ class ClassificationReference(PropertyGroup):
class BIMClassificationProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classification_source: EnumProperty(
items=[
("FILE", "IFC File", ""),
@@ -74,7 +66,5 @@ class BIMClassificationProperties(PropertyGroup):
class BIMClassificationReferenceProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classifications: EnumProperty(items=get_classifications, name="Classifications")
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
active_reference_id: IntProperty(name="Active Reference Id")
@@ -36,7 +36,7 @@ class BIM_PT_classifications(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_project_setup"
bl_parent_id = "BIM_PT_project_setup"
@classmethod
def poll(cls, context):
@@ -66,14 +66,8 @@ class BIM_PT_classifications(Panel):
self.draw_ui(classification)
def draw_add_manual_ui(self, context):
if self.props.is_adding:
blenderbim.bim.helper.draw_attributes(self.props.classification_attributes, self.layout)
row = self.layout.row(align=True)
row.operator("bim.add_manual_classification", text="Save", icon="CHECKMARK")
row.operator("bim.disable_adding_manual_classification", text="", icon="CANCEL")
else:
row = self.layout.row()
row.operator("bim.enable_adding_manual_classification", text="Add Classification", icon="ADD")
row = self.layout.row()
row.label(text="TODO", icon="ERROR")
def draw_add_bsdd_ui(self, context):
self.bprops = context.scene.BIMBSDDProperties
@@ -118,9 +112,10 @@ class BIM_PT_classifications(Panel):
class ReferenceUI:
def draw_ui(self, context):
obj = context.active_object
self.oprops = obj.BIMObjectProperties
self.sprops = context.scene.BIMClassificationProperties
self.bprops = context.scene.BIMBSDDProperties
self.props = context.scene.BIMClassificationReferenceProperties
self.props = obj.BIMClassificationReferenceProperties
self.file = IfcStore.get_file()
self.draw_add_ui(context)
@@ -149,16 +144,7 @@ class ReferenceUI:
def draw_add_manual_ui(self, context):
row = self.layout.row()
row.prop(self.props, "classifications", text="")
if self.props.is_adding:
blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout)
row = self.layout.row(align=True)
op = row.operator("bim.add_manual_classification_reference", text="Save", icon="CHECKMARK")
op.obj_type = self.data.data["object_type"]
row.operator("bim.disable_adding_manual_classification_reference", text="", icon="CANCEL")
else:
row = self.layout.row()
row.operator("bim.enable_adding_manual_classification_reference", text="Add Reference", icon="ADD")
row.label(text="TODO", icon="ERROR")
def draw_add_bsdd_ui(self, context):
if not self.bprops.active_domain:
@@ -253,8 +239,6 @@ class ReferenceUI:
name = reference["Identification"] or "No Identification"
row.label(text=name, icon="ASSET_MANAGER")
row.label(text=reference["Name"] or "")
if reference["Location"]:
row.operator("bim.open_uri", icon="URL", text="").uri = reference["Location"]
if not self.props.active_reference_id:
op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL")
op.reference = reference["id"]
@@ -294,31 +278,28 @@ class BIM_PT_material_classifications(Panel, ReferenceUI):
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_materials"
bl_context = "material"
@classmethod
def poll(cls, context):
if not tool.Ifc.get():
return False
props = context.scene.BIMMaterialProperties
if props.materials and props.active_material_index < len(props.materials):
material = props.materials[props.active_material_index]
if material.ifc_definition_id:
return True
return False
try:
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
except:
return False
def draw(self, context):
if not MaterialClassificationsData.is_loaded:
MaterialClassificationsData.load()
self.data = MaterialClassificationsData
self.obj = ""
self.obj = context.active_object.active_material.name
self.obj_type = "Material"
self.draw_ui(context)
class BIM_PT_cost_classifications(Panel, ReferenceUI):
bl_label = "Cost Item Classifications"
bl_label = "Cost Classifications"
bl_idname = "BIM_PT_cost_classifications"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
@@ -114,7 +114,7 @@ class EditObjective(bpy.types.Operator):
ifcopenshell.api.run(
"constraint.edit_objective",
self.file,
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes},
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
)
bpy.ops.bim.load_objectives()
return {"FINISHED"}
@@ -179,17 +179,19 @@ class AssignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = tool.Ifc.get()
self.file = IfcStore.get_file()
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
if products:
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"constraint.assign_constraint",
self.file,
**{
"products": products,
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
},
}
)
return {"FINISHED"}
@@ -205,16 +207,18 @@ class UnassignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = tool.Ifc.get()
self.file = IfcStore.get_file()
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)]
if products:
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"constraint.unassign_constraint",
self.file,
**{
"products": products,
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
},
}
)
return {"FINISHED"}
@@ -29,7 +29,7 @@ class BIM_PT_constraints(Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_project_setup"
bl_parent_id = "BIM_PT_project_setup"
@classmethod
def poll(cls, context):
@@ -53,10 +53,6 @@ class AddContext(bpy.types.Operator, Operator):
class RemoveContext(bpy.types.Operator, Operator):
bl_idname = "bim.remove_context"
bl_label = "Remove Context"
bl_description = (
"Remove representation context. Any representation geometry that is assigned to the context is also removed. "
"If a context is removed, then any subcontexts are also removed"
)
bl_options = {"REGISTER", "UNDO"}
context: bpy.props.IntProperty()
@@ -29,7 +29,7 @@ class BIM_PT_context(bpy.types.Panel):
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_geometry"
bl_parent_id = "BIM_PT_geometry"
@classmethod
def poll(cls, context):
@@ -82,7 +82,6 @@ classes = (
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
prop.ScheduleColumn,
prop.BIMCostProperties,
ui.BIM_PT_cost_schedules,
ui.BIM_PT_cost_item_quantities,
@@ -19,13 +19,10 @@
import bpy
import ifcopenshell
import ifcopenshell.util.cost
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.unit
import blenderbim.tool as tool
import blenderbim.bim.schema
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
from typing import Any
def refresh():
@@ -37,7 +34,6 @@ def refresh():
class CostSchedulesData:
data = {}
is_loaded = False
_cost_values: dict[int, dict[str, Any]]
@classmethod
def load(cls):
@@ -140,11 +136,13 @@ class CostSchedulesData:
data["UnitBasisUnitSymbol"] = "U"
if cost_value.Category == "*":
is_sum = True
cost_quantity = 1 if data["TotalCostQuantity"] is None else data["TotalCostQuantity"]
if has_unit_basis:
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity / data["UnitBasisValueComponent"]
data["TotalCost"] = data["TotalAppliedValue"] / data["UnitBasisValueComponent"]
else:
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity
if data["TotalCostQuantity"] is not None:
data["TotalCost"] = data["TotalAppliedValue"] * data["TotalCostQuantity"]
else:
data["TotalCost"] = data["TotalAppliedValue"]
if is_sum:
data["TotalAppliedValue"] = None
@@ -159,14 +157,11 @@ class CostSchedulesData:
data["UnitSymbol"] = "-"
if cost_item.CostQuantities:
quantity = cost_item.CostQuantities[0]
data["QuantityType"] = quantity.is_a()
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
if unit:
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
if quantity.is_a("IfcPhysicalSimpleQuantity"):
measure_class = quantity.wrapped_data.declaration().as_entity().attribute_by_index(3).type_of_attribute().declared_type().name()
if "Count" in measure_class:
data["UnitSymbol"] = "U"
else:
data["UnitSymbol"] = "U"
# same_unit_nested_cost_item = set()
# data["DerivedTotalCostQuantity"] = None
@@ -198,16 +193,10 @@ class CostSchedulesData:
# data["DerivedUnitSymbol"] = "?"
# print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name)
# TODO: dead code?
@classmethod
def _get_object_quantities(
cls, cost_item: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
) -> list[int]:
def _get_object_quantities(cls, cost_item, element):
if not element.is_a("IfcObject"):
return []
cost_quantities = cost_item.CostQuantities
if not cost_quantities:
return []
results = []
for relationship in element.IsDefinedBy:
if not relationship.is_a("IfcRelDefinesByProperties"):
@@ -216,7 +205,7 @@ class CostSchedulesData:
if not qto.is_a("IfcElementQuantity"):
continue
for prop in qto.Quantities:
if prop in cost_quantities:
if prop in cost_item.CostQuantities or []:
results.append(prop.id())
return results
@@ -267,7 +256,7 @@ class CostSchedulesData:
for cost_value in cost_item.CostValues or []:
label = "{0:.2f}".format(ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value))
label += " = {}".format(ifcopenshell.util.cost.serialise_cost_value(cost_value))
results.append({"id": cost_value.id(), "label": label, "name": cost_value.Name})
results.append({"id": cost_value.id(), "label": label})
return results
@classmethod
@@ -37,10 +37,12 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
object_type: bpy.props.StringProperty()
def _execute(self, context):
predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types
if predefined_type == "USERDEFINED":
predefined_type = self.object_type
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
core.add_cost_schedule(
tool.Ifc,
name=self.name,
predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types,
object_type=self.object_type,
)
def draw(self, context):
layout = self.layout
@@ -73,7 +75,7 @@ class RemoveCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
cost_schedule: bpy.props.IntProperty()
def _execute(self, context):
core.remove_cost_schedule(tool.Ifc, tool.Cost, cost_schedule=tool.Ifc.get().by_id(self.cost_schedule))
core.remove_cost_schedule(tool.Ifc, cost_schedule=tool.Ifc.get().by_id(self.cost_schedule))
class EnableEditingCostSchedule(bpy.types.Operator):
@@ -223,40 +225,36 @@ class EditCostItem(bpy.types.Operator, tool.Ifc.Operator):
class AssignCostItemType(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_cost_item_type"
bl_label = "Assign Cost Item To Product Types"
bl_description = "Assign cost item to currently selected or active product types"
bl_label = "Assign Cost Item Type Product"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
prop_name: bpy.props.StringProperty()
def _execute(self, context):
product_types = core.assign_cost_item_type(
core.assign_cost_item_type(
tool.Ifc,
tool.Cost,
tool.Spatial,
cost_item=tool.Ifc.get().by_id(self.cost_item),
prop_name=self.prop_name, # TODO: REVIEW PROP_NAME USABILITY
)
self.report({"INFO"}, f"Cost item was assigned to {len(product_types)} product types.")
class UnassignCostItemType(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_cost_item_type"
bl_label = "Unassign Cost Item Type"
bl_description = "Unassign cost item from currently selected or active product types"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def _execute(self, context):
product_types = core.unassign_cost_item_type(
core.unassign_cost_item_type(
tool.Ifc,
tool.Cost,
tool.Spatial,
cost_item=tool.Ifc.get().by_id(self.cost_item),
product_types=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else None,
product_types=[tool.Ifc.get().by_id(self.related_object)] if self.related_object else [],
)
self.report({"INFO"}, f"Cost item was unassigned from {len(product_types)} product types.")
return {"FINISHED"}
@@ -291,11 +289,9 @@ class UnassignCostItemQuantity(bpy.types.Operator, tool.Ifc.Operator):
tool.Ifc,
tool.Cost,
cost_item=tool.Ifc.get().by_id(self.cost_item),
products=(
[tool.Ifc.get().by_id(self.related_object)]
if self.related_object
else tool.Spatial.get_selected_products()
),
products=[tool.Ifc.get().by_id(self.related_object)]
if self.related_object
else tool.Spatial.get_selected_products(),
)
@@ -515,7 +511,7 @@ class SelectCostScheduleProducts(bpy.types.Operator):
class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
bl_idname = "bim.import_cost_schedule_csv"
bl_idname = "import_cost_schedule_csv.bim"
bl_label = "Import Cost Schedule CSV"
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".csv"
@@ -533,13 +529,6 @@ class AddCostColumn(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
if not context.scene.BIMCostProperties.cost_column:
cls.poll_message_set("Cost column name is empty")
return False
return True
def execute(self, context):
core.add_cost_column(tool.Cost, self.name)
return {"FINISHED"}
@@ -551,7 +540,7 @@ class RemoveCostColumn(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
def execute(self, context):
def _execute(self, context):
core.remove_cost_column(tool.Cost, self.name)
return {"FINISHED"}
@@ -669,17 +658,13 @@ class ExportCostSchedules(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
bl_description = "Export a cost schedule to a CSV, XSLX OR ODS file"
cost_schedule: bpy.props.IntProperty()
format: bpy.props.EnumProperty(name="Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
directory: bpy.props.StringProperty(subtype="FILE_PATH")
filter_folder: bpy.props.BoolProperty(
name="Filter Folders",
default=True,
)
format: bpy.props.EnumProperty("Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
cost_schedule = tool.Ifc.get().by_id(self.cost_schedule) if self.cost_schedule else None
r = core.export_cost_schedules(
tool.Cost, filepath=self.directory, format=self.format, cost_schedule=cost_schedule
tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule
)
if isinstance(r, str):
self.report({"ERROR"}, r)
@@ -693,7 +678,6 @@ class ExportCostSchedules(bpy.types.Operator):
def draw(self, context):
self.layout.label(text="Choose a format")
self.layout.prop(self, "format")
self.layout.label(text="Select a directory.")
class ClearCostItemAssignments(bpy.types.Operator, tool.Ifc.Operator):
@@ -175,10 +175,6 @@ def update_active_cost_item_resources(self, context):
bpy.ops.bim.load_cost_item_resource_quantities()
class ScheduleColumn(PropertyGroup):
schedule_id: IntProperty()
class BIMCostProperties(PropertyGroup):
cost_schedule_predefined_types: EnumProperty(
items=get_schedule_predefined_types, name="Predefined Type", default=None
@@ -214,14 +210,9 @@ class BIMCostProperties(PropertyGroup):
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
cost_value_formula: StringProperty(name="Cost Value Formula")
cost_column: StringProperty(name="Cost Column")
should_show_column_ui: BoolProperty(
name="Should Show Column UI",
description="Display UI for adding cost schedule columns, column names represent a category for cost item values",
default=False,
)
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
should_show_currency_ui: BoolProperty(name="Should Show Currency UI", default=False)
columns: CollectionProperty(name="Active Schedule Columns", type=StrProperty)
columns_storage: CollectionProperty(name="Columns", type=ScheduleColumn)
columns: CollectionProperty(name="Columns", type=StrProperty)
active_column_index: IntProperty(name="Active Column Index")
cost_item_products: CollectionProperty(name="Cost Item Products", type=CostItemQuantity)
active_cost_item_product_index: IntProperty(name="Active Cost Item Product Index")

Some files were not shown because too many files have changed in this diff Show More