mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-07 08:21:42 +00:00
Compare commits
467 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2531a58083 | |||
| 70ae0fd850 | |||
| 174749caf7 | |||
| dd8ec95fad | |||
| 0a791a3b85 | |||
| 1c91b966b5 | |||
| db6cda2816 | |||
| d6dab75653 | |||
| 0dc084d785 | |||
| 3bafea62f4 | |||
| 69b7658196 | |||
| 9e93f22e53 | |||
| 94bcb8d266 | |||
| bbd4e00972 | |||
| 16e8c504f3 | |||
| 95c457f53d | |||
| 38f5b77f92 | |||
| 4f1a36753c | |||
| 565a0edb73 | |||
| 54a65abd76 | |||
| 5b16699eb4 | |||
| b4482780cf | |||
| 589b9d4a79 | |||
| 04365053a8 | |||
| c79e490d6d | |||
| b674b8ca58 | |||
| f56bae141b | |||
| 8579c61ab6 | |||
| 5440445cbc | |||
| e28b4d40ab | |||
| a6940d2cba | |||
| 9f14769cba | |||
| 585eeb283a | |||
| 3a6c3a8312 | |||
| 86fa24aba3 | |||
| 474d8381ab | |||
| feea42f988 | |||
| 88d3152f04 | |||
| 515bfd1beb | |||
| 472e2f8e59 | |||
| 83a0d7a63c | |||
| 4e78de6ce9 | |||
| 670bf9d4a6 | |||
| ad1183dab8 | |||
| a1568641b0 | |||
| 76a6ad6596 | |||
| 355cca3679 | |||
| 7e1eadf54c | |||
| a2dead2dd7 | |||
| 220fb13eec | |||
| 7c1c139074 | |||
| 3a3421897c | |||
| b34d10bd35 | |||
| 8e86b3d1d8 | |||
| acf135a6ed | |||
| 7c1459b83e | |||
| dd561d3703 | |||
| 24c4858570 | |||
| 9690bfb1ed | |||
| fd48f70ef1 | |||
| c33c19c483 | |||
| c9989c5f8f | |||
| d996811306 | |||
| 10b5910654 | |||
| ee5b3d1775 | |||
| b45a995bb3 | |||
| fc45e587c0 | |||
| c18e4ea6ce | |||
| ff662fdfcf | |||
| f075b0ca52 | |||
| a3844fe098 | |||
| d00e60369b | |||
| 22b7027746 | |||
| 410fff6ee9 | |||
| 9d11f8a25b | |||
| 95f71a2e2f | |||
| accb1890dc | |||
| 2a808318bf | |||
| 24d1a2ee64 | |||
| 1d59edcbe8 | |||
| 8a9ca9ac60 | |||
| 7a89d8f5ed | |||
| a6951d1a6d | |||
| 1cdd4c27fc | |||
| 7acea25fdf | |||
| 2510e4e6c4 | |||
| d0f4c25c9f | |||
| 0557ff810a | |||
| fdaf059c60 | |||
| 79f96d22a5 | |||
| 31e7cd7a8c | |||
| 6b274535c6 | |||
| 410767921d | |||
| 78dd4856eb | |||
| 166e75db3f | |||
| 01c7be2b48 | |||
| d05e30b4c5 | |||
| 4f44bde9e9 | |||
| bd50276e7d | |||
| 58b132136d | |||
| bd8988a474 | |||
| c0216efffe | |||
| 25e964788c | |||
| 52d9cbff28 | |||
| 1eb9537d58 | |||
| 24c753a09d | |||
| b5a130263a | |||
| 13a1252c77 | |||
| f338f66b4b | |||
| a782812911 | |||
| 10f96d873a | |||
| 09a2494967 | |||
| 84d58cc572 | |||
| 74e27a27b5 | |||
| f5b71543af | |||
| ef39acfb05 | |||
| 8abb8d1102 | |||
| 4db3c60ccf | |||
| baec208303 | |||
| dda4dd3ab5 | |||
| 2ddbf57597 | |||
| f1290ef061 | |||
| cf3d050423 | |||
| a4532cc722 | |||
| 813a8e12ed | |||
| 10059a722b | |||
| 4211e7a936 | |||
| 33785620d5 | |||
| 5a0df870f5 | |||
| f9aba58342 | |||
| 788fb62f25 | |||
| cc6d254698 | |||
| 3f2703ca21 | |||
| e72f2758c6 | |||
| 2feae06ebd | |||
| fccf56d5fd | |||
| 6e9309aefc | |||
| fbe60c85a1 | |||
| 8f92877fb4 | |||
| 5d1b2e4651 | |||
| 10c3893b79 | |||
| e2198c3ba7 | |||
| 752a64bf3e | |||
| 71ef1bf056 | |||
| d4efcd40f9 | |||
| 9485190604 | |||
| d1523eaeac | |||
| 1af8e7d0ef | |||
| 19b89a3b1f | |||
| b73da046ee | |||
| 675e1982ee | |||
| 0357d15ce4 | |||
| 22cd1e8b9e | |||
| 0f8f6419e9 | |||
| bd7ee0a1c6 | |||
| 4463548740 | |||
| 5655df2081 | |||
| deb0b3482b | |||
| ae2a299616 | |||
| 375deaad1e | |||
| f692e943c9 | |||
| 8538a916a3 | |||
| a8b2997e58 | |||
| 5c151c6b57 | |||
| 8c12da13e6 | |||
| 20f8181ac3 | |||
| 656a519126 | |||
| 71869b65f3 | |||
| fe8d42c27c | |||
| 688c841a8e | |||
| 989219e1e2 | |||
| f2c3e62ba1 | |||
| 73f546aab1 | |||
| c4b3e57677 | |||
| 4e9f2ea4b2 | |||
| 2efeb6edb3 | |||
| c68dcba087 | |||
| a92d51aeb1 | |||
| 0544bbc886 | |||
| 9a871f1459 | |||
| 0d401c9272 | |||
| 2129350ab7 | |||
| 43bc979114 | |||
| a0458eeb91 | |||
| c7232a780a | |||
| e96b4c11a2 | |||
| fecca6a8b2 | |||
| 1fc56585dd | |||
| d1be0e8c1c | |||
| dcc568f5a3 | |||
| 2e4a2712e9 | |||
| d4b055aeb2 | |||
| 0e2cc463c8 | |||
| c3930a6573 | |||
| 3c094b7bd2 | |||
| f8dffa1a9b | |||
| 03b6c08c50 | |||
| 42b96d6290 | |||
| c240f0582a | |||
| 76b7748be1 | |||
| a2334f4ce0 | |||
| db50c87bf4 | |||
| dc8766cf0a | |||
| 158f3d2962 | |||
| 008fa70345 | |||
| e59cd80868 | |||
| f0d7965d49 | |||
| b616cafa49 | |||
| 70bb0e5481 | |||
| 07060fc769 | |||
| b5d613989e | |||
| f604aa8b6b | |||
| dc8912c128 | |||
| 07a0b4fe2d | |||
| eb67ffcbec | |||
| 4ec8345716 | |||
| 56d3b2ca36 | |||
| dd06b1a6c4 | |||
| 3469a9caad | |||
| 599ca56496 | |||
| 14dbe121f6 | |||
| dee936b2c3 | |||
| a98cddf676 | |||
| 48d058a32a | |||
| 98afb72ba3 | |||
| 2d5be6cf89 | |||
| 1299a8f512 | |||
| eccbdfa76e | |||
| 24e16d97f3 | |||
| 13d61f0135 | |||
| c56a4a6c9d | |||
| 832dce462e | |||
| 6f45373edd | |||
| d5613e0e26 | |||
| d0ec4fe345 | |||
| 665bf00f8b | |||
| 2d2ca64a72 | |||
| 60cb41a428 | |||
| a69f3e8da9 | |||
| 2f81cab5f1 | |||
| 78e81542d9 | |||
| 7bbc81b7c3 | |||
| 6e6b256c22 | |||
| d23eba9343 | |||
| e899104d56 | |||
| de00429df6 | |||
| 3aba82f608 | |||
| cb85b690e7 | |||
| 527742ca93 | |||
| b85dcdb437 | |||
| f740ca3cf1 | |||
| b9269b5a11 | |||
| 16e98e1400 | |||
| 4976074b18 | |||
| e670a7ca71 | |||
| d892ad956c | |||
| ac42ed7bab | |||
| bf677b839d | |||
| 464815b045 | |||
| ae91c2b9f0 | |||
| 39cd980008 | |||
| f364643d7e | |||
| ef9bbd8de6 | |||
| 8172096a5b | |||
| 46d3d3afe1 | |||
| 39cd4187ea | |||
| d6a3bc3556 | |||
| ce9e4f2854 | |||
| 7700e8016b | |||
| 6e1ebb02ac | |||
| d2e7e3daaf | |||
| 5d6215d889 | |||
| 17eb45fb4e | |||
| 0a06d58c42 | |||
| a65c6b6d3f | |||
| c5262274eb | |||
| 65f94bcc0c | |||
| 804bbc47c3 | |||
| a967665cbd | |||
| cbd017aceb | |||
| 8d9d142bc2 | |||
| f6c30f2ddf | |||
| d2a27999ba | |||
| 2738b280a1 | |||
| c8946a19f2 | |||
| 6b1932c8d9 | |||
| a133b51dcc | |||
| 6155edef0d | |||
| 439595f04c | |||
| c1fa9fe28e | |||
| 54a0a5b454 | |||
| bace73f854 | |||
| 8da68f599b | |||
| 278524ee3e | |||
| a9912f351f | |||
| 1e5c51e5b2 | |||
| 44100f3c46 | |||
| 713e9c8e14 | |||
| e6238a1f75 | |||
| cfd440dae9 | |||
| a0e5d350bd | |||
| c37240cf96 | |||
| 115844279f | |||
| cbb7c5620d | |||
| 6d5d15793b | |||
| 8ac6f59394 | |||
| 5d8addf1f8 | |||
| 9b9301cbb0 | |||
| 4d9b461776 | |||
| 36f7cdacbd | |||
| feb0e46257 | |||
| 0b901bff5f | |||
| b45b0f2c81 | |||
| 7a36c05c30 | |||
| ac58e6b8b2 | |||
| 68d3771a83 | |||
| 80349d70d8 | |||
| e295d596fe | |||
| 013b138e5c | |||
| 95d2d0af83 | |||
| 03ee826e67 | |||
| 9f31815d9e | |||
| 1e9bfec655 | |||
| fd8cb7e8ed | |||
| 4ef237e0f7 | |||
| 3c48cfbc19 | |||
| 177d43c30e | |||
| a6c3ae8446 | |||
| d0373da0ad | |||
| 7bb3de8371 | |||
| 45e0045820 | |||
| 347b2b783d | |||
| 2fc7cc7de2 | |||
| 4d39804045 | |||
| 746051815d | |||
| 4385bf0bcc | |||
| 1e092c140f | |||
| c4ae9578e9 | |||
| a834e5b4cd | |||
| e329b18e33 | |||
| 5731c4036a | |||
| a594a6cfe2 | |||
| 8b6235f42f | |||
| d443c5e2a6 | |||
| 68d3539a86 | |||
| 301cdf6504 | |||
| 882e0a4ae9 | |||
| f921b65953 | |||
| 1e26587bd3 | |||
| bce6485b91 | |||
| a0cb980a8f | |||
| fc0e9e900f | |||
| d700262e7f | |||
| d92679223e | |||
| 7ac117cd8d | |||
| b87a6bc15c | |||
| fbefe8c555 | |||
| 030119fd0e | |||
| fcf2c37432 | |||
| fab3ee70dd | |||
| 4738528e69 | |||
| 88c8bcf119 | |||
| 3c3e4dd047 | |||
| b44b1cb8b0 | |||
| 6ad2bef05c | |||
| 8e0c92d5eb | |||
| 6bafbd92f3 | |||
| 03663eec3f | |||
| a3d4642ea9 | |||
| 0e42130e9d | |||
| 691fe549d7 | |||
| f5979be4d5 | |||
| 9fe40c3137 | |||
| 82496e8126 | |||
| 9c22e6ecc8 | |||
| 17a433a97b | |||
| b90c1ee7c9 | |||
| bd30f73b3a | |||
| 0aad3d99b5 | |||
| f7d55a4a4a | |||
| df2655e7ae | |||
| 9ec51e040e | |||
| 467602aec3 | |||
| 88610e23b2 | |||
| 89fb59307c | |||
| 65a1fbea78 | |||
| 78bc8fe977 | |||
| 6ab08a2f49 | |||
| de7d315eb3 | |||
| 24662d75e4 | |||
| 76e6697bbc | |||
| 3c2fcffd0d | |||
| 0188757197 | |||
| 444554a12d | |||
| ba42f01db2 | |||
| 5d91766f07 | |||
| c4cbc577d8 | |||
| bfd9cc0cf0 | |||
| ac0b5abfc5 | |||
| ad61cc3c92 | |||
| 6fa586a1b6 | |||
| 4c09773211 | |||
| 2300411ce4 | |||
| 9c61a9eed7 | |||
| 743a197542 | |||
| 99f329c13d | |||
| 623d487df7 | |||
| 348ef4023f | |||
| b253b74e88 | |||
| a3b6a6ea39 | |||
| 8246d2c593 | |||
| 3a6cf0e5ae | |||
| 1db26aab15 | |||
| a23e6dcd66 | |||
| 9331292dd1 | |||
| cad7db0162 | |||
| 9f356542bc | |||
| 1f7d80e6f1 | |||
| f3f1e8721c | |||
| 114fca8be5 | |||
| 52167a47b9 | |||
| d4ff2c8dda | |||
| 75f8e75a8e | |||
| d0f612afc2 | |||
| b6b681c244 | |||
| 5911a602b9 | |||
| 453ef437d3 | |||
| 88494d3379 | |||
| 1b41440aba | |||
| 105434fe68 | |||
| a0a505d64b | |||
| 3a4bdd623d | |||
| 99338187de | |||
| 9d7ef6ff47 | |||
| ffd944f4f9 | |||
| ecef745c3b | |||
| 44507785ac | |||
| 79c0246e6a | |||
| 3c659a342f | |||
| d192217e78 | |||
| 6474552649 | |||
| 4b454dd646 | |||
| 85e1786ce5 | |||
| 45b81ab814 | |||
| 8241b557fe | |||
| 1312a8e704 | |||
| 2a5e42e359 | |||
| dae7d86dad | |||
| 9906149b0d | |||
| 2e2c116180 | |||
| 13c6b9d673 | |||
| 2fbac57faf | |||
| f803447651 | |||
| bec834c651 | |||
| 3df75e9af3 | |||
| 6630db21ee | |||
| f54f45a051 | |||
| acd4274136 | |||
| d400a48a9d | |||
| 4331f4f785 | |||
| 95e36979d5 | |||
| 07827940cc | |||
| c2efcda7bb | |||
| aa0fbd434e | |||
| 8cff046a46 | |||
| ff4154c2bd | |||
| d7e497727c |
@@ -1,24 +1,8 @@
|
||||
name: ci-bcf-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 * *"
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-bcf-pypi.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: ifcopenshell
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -54,4 +38,4 @@ jobs:
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/bcf/dist
|
||||
packages_dir: src/bcf/dist
|
||||
|
||||
@@ -28,65 +28,18 @@ jobs:
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
name: "choco_release"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py310]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win,
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
python-version: '3.10.9' # 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
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
- run: echo ${{ env.DATE }}
|
||||
|
||||
- name: Check in published releases if we should do a choco release
|
||||
id: do_choco
|
||||
- name: Check in release tags if we should do a choco release and perform the release if needed
|
||||
id: do_choco_release
|
||||
run: |
|
||||
echo "::set-output name=choco_release::$(python3 /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/check_repo_infos.py --do_choco_release?)"
|
||||
|
||||
- name: Fill chocolatey scripts on win with latest blender python
|
||||
if: ${{steps.do_choco.outputs.choco_release}} == 'do_choco_release'
|
||||
run: |
|
||||
yesterday_non_iso="$(date --date='yesterday' +'%y%m%d' | tr -d '\n')" &&
|
||||
target_os=${{ matrix.config.short_name }} &&
|
||||
latest_blender_python_version_maj_min=$(python3 /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/check_repo_infos.py --latest_blender_python_version_maj_min?) &&
|
||||
echo "latest_blender_python_version_maj_min?: $latest_blender_python_version_maj_min" &&
|
||||
pyver=$(python3 /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/check_repo_infos.py --pyver?) &&
|
||||
export latest_blender_version_maj_min=$(python3 /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/check_repo_infos.py --latest_blender_release_maj_min?) &&
|
||||
export latest_blender_version_maj_min_pat=$(python3 /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/check_repo_infos.py --latest_blender_release_maj_min_pat?) &&
|
||||
echo latest_blender_version_maj_min_pat?: $latest_blender_version_maj_min_pat &&
|
||||
export blenderbim_build_version="${{ env.major }}.${{ env.minor }}.$yesterday_non_iso" &&
|
||||
export url_blenderbim_py310_win_zip="https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-$yesterday_non_iso/blenderbim-$yesterday_non_iso-$pyver-$target_os.zip" &&
|
||||
wget $url_blenderbim_py310_win_zip --no-verbose &&
|
||||
export sha256sum_blenderbim_py310_win_zip=$( sha256sum blenderbim-$yesterday_non_iso-$pyver-$target_os.zip --tag | cut -d ' ' -f 4 | tr -d '\n') &&
|
||||
echo sha256sum_blenderbim_py310_win_zip: $sha256sum_blenderbim_py310_win_zip &&
|
||||
python3 choco/blenderbim/fill_dynamic_parameters.py &&
|
||||
echo __build choco with mono &&
|
||||
wget "https://github.com/chocolatey/choco/archive/refs/tags/$choco_version.tar.gz" --quiet &&
|
||||
tar -xzf "$choco_version.tar.gz" &&
|
||||
cd choco-$choco_version &&
|
||||
chmod +x build.sh zip.sh &&
|
||||
./build.sh &&
|
||||
cp -r build_output/chocolatey /opt/chocolatey &&
|
||||
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/ &&
|
||||
echo __choco pack &&
|
||||
mono /opt/chocolatey/choco.exe --version &&
|
||||
mono /opt/chocolatey/choco.exe pack --allow-unofficial &&
|
||||
echo __choco set apiKey &&
|
||||
mono /opt/chocolatey/choco.exe setapikey --key="$CHOCO_TOKEN" --source="https://push.chocolatey.org/" --allow-unofficial # &&
|
||||
echo __choco push &&
|
||||
mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose
|
||||
|
||||
- name: Inform user about not packaging
|
||||
if: ${{steps.do_choco.outputs.choco_release}} != 'do_choco_release'
|
||||
run: |
|
||||
echo "no releases found today ${{ env.DATE }}, therefore choco packaging is skipped."
|
||||
|
||||
python3 choco_release.py
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifc4d-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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/ifc4d &&
|
||||
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/ifc4d/dist
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifc5d-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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/ifc5d &&
|
||||
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/ifc5d/dist
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifccityjson-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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/ifccityjson &&
|
||||
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/ifccityjson/dist
|
||||
@@ -1,21 +1,8 @@
|
||||
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
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifcdiff-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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/ifcdiff &&
|
||||
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/ifcdiff/dist
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifcfm-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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/ifcfm &&
|
||||
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/ifcfm/dist
|
||||
@@ -0,0 +1,101 @@
|
||||
name: ci-ifcopenshell-conda-daily-cleaner
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
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: "00 23 * * *" # 11min before utc midnight every day
|
||||
|
||||
env:
|
||||
ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
|
||||
NUM_SUPPORTED_VERSIONS: 5
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
environment-name: test-env
|
||||
create-args: >-
|
||||
python=3.11
|
||||
anaconda-client=1.12.3
|
||||
|
||||
- name: Run conda cleaner
|
||||
run: |
|
||||
python - << EOF
|
||||
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from binstar_client.utils import get_server_api
|
||||
from binstar_client.errors import BinstarError
|
||||
|
||||
# Configuration
|
||||
api_token = os.environ.get('ANACONDA_TOKEN')
|
||||
pkg_name = 'ifcopenshell'
|
||||
channel_name = 'ifcopenshell'
|
||||
|
||||
# Authenticate with Anaconda
|
||||
aserver_api = get_server_api(token=api_token)
|
||||
|
||||
|
||||
# Get the list of packages in the channel
|
||||
def get_package(filter_package_name: str = None):
|
||||
try:
|
||||
user_packages = aserver_api.user_packages(channel_name)
|
||||
if filter_package_name:
|
||||
user_packages = [pkg for pkg in user_packages if pkg['name'] == filter_package_name]
|
||||
if len(user_packages) == 0:
|
||||
print(f"No packages found for {filter_package_name}.")
|
||||
if len(user_packages) > 1:
|
||||
raise ValueError(f"Found {len(user_packages)} package for {filter_package_name}. Will only support 1 package.")
|
||||
|
||||
return user_packages[0]
|
||||
except BinstarError as err:
|
||||
raise ValueError(f"Failed to fetch packages: {err}")
|
||||
|
||||
|
||||
# Delete a package version
|
||||
def delete_package(package_name, version):
|
||||
try:
|
||||
aserver_api.remove_release(channel_name, package_name, version)
|
||||
print(f"Deleted {package_name} version {version}")
|
||||
except BinstarError as err:
|
||||
print(f"Failed to delete {package_name} version {version}: {err}")
|
||||
|
||||
|
||||
# Main logic
|
||||
def main():
|
||||
package = get_package(pkg_name)
|
||||
if not package:
|
||||
print("No packages found.")
|
||||
return
|
||||
|
||||
number_of_supported_versions = ${{ env.NUM_SUPPORTED_VERSIONS }}
|
||||
|
||||
package_name = package['name']
|
||||
versions = package["versions"]
|
||||
if len(versions) <= number_of_supported_versions:
|
||||
print(f"Number of versions {len(versions)} is less than or equal to {number_of_supported_versions}.")
|
||||
return
|
||||
|
||||
# sort the versions in descending order
|
||||
versions = sorted(versions, reverse=True)
|
||||
releases = versions[number_of_supported_versions:]
|
||||
|
||||
for release in releases:
|
||||
delete_package(package_name, release)
|
||||
|
||||
main()
|
||||
|
||||
EOF
|
||||
@@ -2,21 +2,44 @@ name: ci-ifcopenshell-conda-daily-v0.8.0
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- v0.8.0
|
||||
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: "49 23 * * *" # 11min before utc midnight every day
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
date: ${{ steps.date.outputs.date }}
|
||||
verdate: ${{ steps.verdate.outputs.verdate }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Version + date str
|
||||
id: verdate
|
||||
run: echo "verdate='${{ steps.version.outputs.version }}.${{ steps.date.outputs.date }}'" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
test:
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}-${{ matrix.variant }}
|
||||
needs: activate
|
||||
runs-on: ${{ matrix.platform.distver }}
|
||||
defaults:
|
||||
@@ -25,8 +48,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: [novtk, all]
|
||||
pyver: [
|
||||
{ name: py311, distver: '3.11'},
|
||||
# { name: py311, distver: '3.11'},
|
||||
{ name: py312, distver: '3.12'}
|
||||
]
|
||||
platform: [
|
||||
@@ -35,24 +59,28 @@ jobs:
|
||||
{ name: macOS, distver: macos-latest, pkg_dir: 'osx-64' }
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up swap space
|
||||
if: runner.os == 'Linux'
|
||||
uses: pierotofy/set-swap-space@v1.0
|
||||
with:
|
||||
swap-size-gb: 10
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Download and extract MacOSX SDK
|
||||
if: ${{ matrix.platform.name == 'macOS' }}
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
run: |
|
||||
curl -o MacOSX10.13.sdk.tar.xz -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz && \
|
||||
tar xf MacOSX10.13.sdk.tar.xz && \
|
||||
sudo mv -v MacOSX10.13.sdk /opt/ && \
|
||||
ls /opt/
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
curl -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz | tar -xvJf - -C /Users/runner/work/
|
||||
|
||||
- uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
environment-file: conda/environment.build.yml
|
||||
environment-name: test-env
|
||||
create-args: >-
|
||||
python=${{ matrix.pyver.distver }}
|
||||
python=3.11
|
||||
anaconda-client
|
||||
boa
|
||||
|
||||
- name: create conda package dist dir
|
||||
run: |
|
||||
@@ -60,8 +88,10 @@ jobs:
|
||||
|
||||
- name: build & test ifcopenshell
|
||||
run: |
|
||||
boa build . --python ${{ matrix.pyver.distver }} --no-remove-work-dir --output-folder '${{ github.workspace }}/dist'
|
||||
conda mambabuild . --python ${{ matrix.pyver.distver }} --no-remove-work-dir --output-folder '${{ github.workspace }}/dist' --variants='{variant: ${{ matrix.variant }}}'
|
||||
working-directory: ./conda
|
||||
env:
|
||||
VERSION_OVERRIDE: ${{ needs.activate.outputs.verdate }}
|
||||
|
||||
- name: upload to anaconda
|
||||
if: ${{ matrix.platform.name == 'win' }}
|
||||
@@ -71,4 +101,4 @@ jobs:
|
||||
- name: upload to anaconda
|
||||
if: ${{ matrix.platform.name != 'win' }}
|
||||
run: |
|
||||
anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell ${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2
|
||||
anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell ${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2
|
||||
|
||||
@@ -6,7 +6,6 @@ on:
|
||||
tags:
|
||||
- v0.**
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
activate:
|
||||
|
||||
@@ -27,19 +27,19 @@ jobs:
|
||||
pyver: [py39, py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
name: "Windows 64bit",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
name: "Linux 64bit",
|
||||
short_name: linux64
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
name: "MacOS Intel 64bit",
|
||||
short_name: macos64
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
name: "MacOS Silicon 64bit",
|
||||
short_name: macosm164
|
||||
}
|
||||
steps:
|
||||
|
||||
@@ -22,19 +22,23 @@ jobs:
|
||||
pyver: [py39, py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
name: "Windows 64bit",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
name: "Windows 32bit",
|
||||
short_name: win32,
|
||||
}
|
||||
- {
|
||||
name: "Linux 64bit",
|
||||
short_name: linux64
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
name: "MacOS Intel 64bit",
|
||||
short_name: macos64
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
name: "MacOS Silicon 64bit",
|
||||
short_name: macosm164
|
||||
}
|
||||
steps:
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
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
|
||||
|
||||
@@ -46,6 +46,6 @@ jobs:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcsverchok/dist/ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}.zip
|
||||
asset_name: ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}.zip
|
||||
release_name: "ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
release_name: "ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
name: ci-ifctester-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
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Restart failed daily conda builds
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ ci-ifcopenshell-conda-daily ]
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
REPO_OWNER: IfcOpenShell/IfcOpenShell
|
||||
MY_WORKFLOW: ci-ifcopenshell-conda-daily
|
||||
|
||||
jobs:
|
||||
restart-failed-runs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Use python 3.11
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.11
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
- name: Check for failed runs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
run: |
|
||||
python check_repo_ci_jobs.py
|
||||
working-directory: .github/workflows
|
||||
@@ -26,49 +26,45 @@ For more information, see:
|
||||
* [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)
|
||||
|
||||
Development is sponsored through your generous donations!
|
||||
|
||||
| Service | Status |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Anaconda Daily Build | [](https://anaconda.org/ifcopenshell/ifcopenshell) |
|
||||
| Anaconda v0.7.0 Stable | [](https://anaconda.org/conda-forge/ifcopenshell) |
|
||||
| PyPi Daily Build | [](https://pypi.org/project/ifcopenshell/) |
|
||||
| ArchLinux AUR Package Stable | [](https://aur.archlinux.org/packages/ifcopenshell) |
|
||||
| ArchLinux AUR Package git | [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| BlenderBIM Add-on Chocolatey (under moderation) | [](https://community.chocolatey.org/packages/blenderbim-nightly/) |
|
||||
| Sponsor development on OpenCollective | [](https://opencollective.com/opensourcebim/) |
|
||||
| Docker hub | [](https://hub.docker.com/r/aecgeeks/ifcopenshell) |
|
||||
[](https://opencollective.com/opensourcebim/)
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
Those marked with an asterisk are part of IfcOpenShell.
|
||||
| Name | Description | License | Service |
|
||||
| ------------------------- | --------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| bcf | Library to read and write BCF-XML and query OpenCDE BCF-API modules | LGPL-3.0-or-later | [](https://pypi.org/project/bcf-client/) |
|
||||
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later | [](https://blenderbim.org/download.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=blenderbim&expanded=true) [](https://community.chocolatey.org/packages/blenderbim-nightly/) |
|
||||
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later | [](https://pypi.org/project/bsdd/) |
|
||||
| ifc2ca | Utility to convert IFC structural analysis models to Code_Aster | LGPL-3.0-or-later |
|
||||
| ifc4d | Convert to and from IFC and project management software | LGPL-3.0-or-later | [](https://pypi.org/project/ifc4d/) |
|
||||
| ifc5d | Report and optimise cost information from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifc5d/) |
|
||||
| ifcbimtester | Wrapper for Gherkin based unit testing for IFC models | LGPL-3.0-or-later |
|
||||
| ifcblender | Historic Blender IFC import add-on | LGPL-3.0-or-later\* |
|
||||
| ifccityjson | Convert CityJSON to IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccityjson/) |
|
||||
| ifcclash | Clash detection library and CLI app | LGPL-3.0-or-later | [](https://pypi.org/project/ifcclash/) |
|
||||
| ifcconvert | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html)
|
||||
| ifccsv | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccsv/) |
|
||||
| ifcdiff | Compare changes between IFC models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcdiff/) |
|
||||
| ifcfm | Extract IFC data for FM handover requirements | LGPL-3.0-or-later | [](https://pypi.org/project/ifcfm/) |
|
||||
| ifcmax | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcmax.html)
|
||||
| ifcopenshell-python | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| ifcpatch | Utility to run pre-packaged scripts to manipulate IFCs | LGPL-3.0-or-later | [](https://pypi.org/project/ifcpatch/) |
|
||||
| ifcsverchok | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcsverchok&expanded=true)
|
||||
| ifctester | Library, CLI and webapp for IDS model auditing | LGPL-3.0-or-later | [](https://pypi.org/project/ifctester/) |
|
||||
|
||||
The IfcOpenShell C++ codebase is split into multiple interal libraries:
|
||||
|
||||
| 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 |
|
||||
| ifc2ca | Utility to convert IFC structural analysis models to Code_Aster | LGPL-3.0-or-later |
|
||||
| ifc4d | Convert to and from IFC and project management software | LGPL-3.0-or-later |
|
||||
| ifc5d | Report and optimise cost information from IFC | LGPL-3.0-or-later |
|
||||
| ifcbimtester | Wrapper for Gherkin based unit testing for IFC models | LGPL-3.0-or-later |
|
||||
| ifcblender | Historic Blender IFC import add-on | LGPL-3.0-or-later\* |
|
||||
| ifccityjson | Convert CityJSON to IFC | LGPL-3.0-or-later |
|
||||
| ifcclash | Clash detection library and CLI app | LGPL-3.0-or-later |
|
||||
| ifcconvert | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* |
|
||||
| ifccsv | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later |
|
||||
| ifcdiff | Compare changes between IFC models | LGPL-3.0-or-later |
|
||||
| ifcfm | Extract IFC data for FM handover requirements | LGPL-3.0-or-later |
|
||||
| ifcgeom | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| ifcgeom\_schema\_agnostic | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| ifcgeomserver | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| ifcjni | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| ifcmax | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* |
|
||||
| ifcopenshell-python | Python library for IFC manipulation | LGPL-3.0-or-later\* |
|
||||
| ifcparse | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| ifcpatch | Utility to run pre-packaged scripts to manipulate IFCs | LGPL-3.0-or-later |
|
||||
| ifcsverchok | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later |
|
||||
| ifctester | Library, CLI and webapp for IDS model auditing | LGPL-3.0-or-later |
|
||||
| ifcwrap | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| qtviewer | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
| serializers | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<projectUrl>https://github.com/IfcOpenShell/IfcOpenShell</projectUrl>
|
||||
<iconUrl>https://rawcdn.githack.com/IfcOpenShell/IfcOpenShell/c6ee1d1679d1298de0d7447ff108c22709c68e54/choco/blenderbim/blenderbim.png</iconUrl>
|
||||
<!-- <copyright>Year Software Vendor</copyright> -->
|
||||
<licenseUrl>https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/COPYING</licenseUrl>
|
||||
<licenseUrl>https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.8.0/COPYING</licenseUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<projectSourceUrl>https://github.com/IfcOpenShell/IfcOpenShell</projectSourceUrl>
|
||||
<docsUrl>https://docs.blenderbim.org/</docsUrl>
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import datetime
|
||||
import re
|
||||
import sys
|
||||
from urllib import request
|
||||
|
||||
|
||||
def request_repo_info(url: str):
|
||||
req = request.Request(url)
|
||||
resp = request.urlopen(req)
|
||||
if not resp.status == 200:
|
||||
print(f"[ERROR] could not contact server: {url}")
|
||||
quit(1)
|
||||
return resp
|
||||
|
||||
|
||||
def get_choco_package_info() -> str:
|
||||
resp = request_repo_info(URL_CHOCO_PACKAGE)
|
||||
html_txt = str(resp.read())
|
||||
return html_txt
|
||||
|
||||
|
||||
def get_latest_blender_version() -> list:
|
||||
html_txt = get_choco_package_info()
|
||||
return re.findall(RE_BLENDER_VERSION_MIN_MAJ_PAT, html_txt)
|
||||
|
||||
|
||||
URL_IFCOS_RELEASES = "https://github.com/IfcOpenShell/IfcOpenShell/releases"
|
||||
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+)\)"
|
||||
|
||||
|
||||
if sys.argv[1] == "--do_choco_release?":
|
||||
now = datetime.datetime.now()
|
||||
blenderbim_date = (now - datetime.timedelta(days=1)).strftime("%y%m%d")
|
||||
resp = request_repo_info(URL_IFCOS_RELEASES)
|
||||
text = str(resp.read())
|
||||
if blenderbim_date in text:
|
||||
print("do_choco_release", end="")
|
||||
|
||||
|
||||
elif sys.argv[1] == "--latest_blender_release_maj_min_pat?":
|
||||
latest_blender_version = get_latest_blender_version()
|
||||
if latest_blender_version:
|
||||
print(latest_blender_version[0], end="")
|
||||
else:
|
||||
print("[ERROR] could not determine blender_version_min_maj_pat")
|
||||
quit(1)
|
||||
|
||||
|
||||
elif sys.argv[1] == "--latest_blender_release_maj_min?":
|
||||
html_txt = get_choco_package_info()
|
||||
blender_version_min_maj = re.findall(RE_BLENDER_VERSION_MIN_MAJ, html_txt)
|
||||
if blender_version_min_maj:
|
||||
print(blender_version_min_maj[0], end="")
|
||||
else:
|
||||
print("[ERROR] could not determine blender_version_min_maj")
|
||||
quit(1)
|
||||
|
||||
|
||||
elif sys.argv[1] == "--latest_blender_python_version_maj_min?":
|
||||
latest_blender_version = get_latest_blender_version()
|
||||
if latest_blender_version:
|
||||
latest_blender_version_tag = f"v{latest_blender_version[0]}"
|
||||
resp = request_repo_info(URL_BLENDER_CMAKE.format(latest_blender_version_tag))
|
||||
html_txt = str(resp.read())
|
||||
found = re.findall(RE_BLENDER_PYTHON_VERSION_MAJ_MIN, html_txt)
|
||||
if found:
|
||||
print(found[0], end="")
|
||||
else:
|
||||
print("[ERROR] could not determine blender_python_version_maj_min")
|
||||
quit(1)
|
||||
|
||||
|
||||
elif sys.argv[1] == "--pyver?":
|
||||
latest_blender_version = get_latest_blender_version()
|
||||
if latest_blender_version:
|
||||
latest_blender_version_tag = f"v{latest_blender_version[0]}"
|
||||
resp = request_repo_info(URL_BLENDER_CMAKE.format(latest_blender_version_tag))
|
||||
html_txt = str(resp.read())
|
||||
found = re.findall(RE_BLENDER_PYTHON_VERSION_MAJ_MIN, html_txt)
|
||||
if found:
|
||||
print(f"py{found[0].replace('.', '')}", end="")
|
||||
else:
|
||||
print("[ERROR] could not determine pyver")
|
||||
quit(1)
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# commands to run it on a generic ubuntu 2404 oci container:
|
||||
"""
|
||||
apt update && apt install git wget curl ptpython mono-devel micro
|
||||
mkdir -p /home/runner/work/IfcOpenShell && cd /home/runner/work/IfcOpenShell
|
||||
git clone https://github.com/IfcOpenShell/IfcOpenShell
|
||||
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/
|
||||
micro choco_release.py # paste this script, comment out push command
|
||||
export CHOCO_TOKEN="secret_choco_release_token"
|
||||
python3 choco_release.py
|
||||
"""
|
||||
import datetime
|
||||
import hashlib
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
from urllib import request
|
||||
|
||||
|
||||
def get_repo_tag_names():
|
||||
git_return = os.popen("git tag -l").read()
|
||||
tag_names = [tag_name for tag_name in git_return.split("\n") if tag_name]
|
||||
print(f"{len(tag_names)} tag_names found in repo")
|
||||
return tag_names
|
||||
|
||||
|
||||
def request_repo_info(url: str):
|
||||
req = request.Request(url)
|
||||
resp = request.urlopen(req)
|
||||
if not resp.status == 200:
|
||||
print(f"[ERROR] could not contact server: {url}")
|
||||
quit(1)
|
||||
return resp
|
||||
|
||||
|
||||
def get_choco_package_info() -> str:
|
||||
resp = request_repo_info(URL_CHOCO_PACKAGE)
|
||||
html_txt = str(resp.read())
|
||||
return html_txt
|
||||
|
||||
|
||||
def get_latest_choco_blender_version() -> list:
|
||||
html_txt = get_choco_package_info()
|
||||
return re.findall(RE_BLENDER_VERSION_MIN_MAJ_PAT, html_txt)
|
||||
|
||||
|
||||
def get_file_sha256_hash(file_path):
|
||||
BLOCKSIZE = 65536
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
with open(file_path, "rb") as input_file:
|
||||
buffer = input_file.read(BLOCKSIZE)
|
||||
while len(buffer) > 0:
|
||||
hasher.update(buffer)
|
||||
buffer = input_file.read(BLOCKSIZE)
|
||||
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
def quit_with_error_message(message: str):
|
||||
print(f"ERROR: {message}")
|
||||
quit(0)
|
||||
|
||||
|
||||
start = datetime.datetime.now()
|
||||
|
||||
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
|
||||
URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
|
||||
URL_IFCOS_RELEASES = "https://github.com/IfcOpenShell/IfcOpenShell/releases/download/"
|
||||
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"\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
|
||||
|
||||
BLENDERBIM_DIR = pathlib.Path("/home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/")
|
||||
|
||||
print("_____ check choco release needed?")
|
||||
|
||||
os.chdir(BLENDERBIM_DIR)
|
||||
|
||||
blenderbim_date_yesterday = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%y%m%d")
|
||||
should_release = False
|
||||
target_release_tag = ""
|
||||
target_os = "win"
|
||||
|
||||
git_status = os.popen("git status").read()
|
||||
print(git_status)
|
||||
|
||||
for tag_name in get_repo_tag_names():
|
||||
print(tag_name)
|
||||
if blenderbim_date_yesterday in tag_name:
|
||||
should_release = True
|
||||
target_release_tag = tag_name
|
||||
print(f"found {tag_name=} - {should_release=}")
|
||||
break
|
||||
|
||||
if not should_release:
|
||||
print(f"INFO: no blenderbim release tags found for {blenderbim_date_yesterday} -> no choco release today.")
|
||||
quit(0)
|
||||
print(f"{should_release=}")
|
||||
|
||||
if not os.environ.get("CHOCO_TOKEN"):
|
||||
quit_with_error_message("could retrieve CHOCO_TOKEN env var")
|
||||
choco_token = os.environ["CHOCO_TOKEN"]
|
||||
|
||||
print("\n_____ get blender info")
|
||||
# blender_version_min_maj_pat - from chocolatey.org
|
||||
latest_blender_release_maj_min_pat = get_latest_choco_blender_version()
|
||||
if not latest_blender_release_maj_min_pat:
|
||||
quit_with_error_message("could not determine blender_version_min_maj_pat")
|
||||
latest_blender_release_maj_min_pat = latest_blender_release_maj_min_pat[0]
|
||||
print(f"{latest_blender_release_maj_min_pat=}")
|
||||
|
||||
# blender_version_min_maj - from chocolatey.org
|
||||
blender_version_min_maj = latest_blender_release_maj_min_pat.rsplit(".", 1)[0]
|
||||
print(f"{blender_version_min_maj=}")
|
||||
|
||||
# blender_python_version_maj_min - from blender repo
|
||||
python_version = ""
|
||||
latest_blender_version_tag = f"v{latest_blender_release_maj_min_pat}"
|
||||
resp = request_repo_info(URL_BLENDER_CMAKE.format(latest_blender_version_tag))
|
||||
html_txt = str(resp.read())
|
||||
found = re.findall(RE_BLENDER_PYTHON_VERSION_MAJ_MIN, html_txt)
|
||||
if not found:
|
||||
quit_with_error_message("could not determine blender_python_version_maj_min")
|
||||
|
||||
blender_python_version_maj_min = found[0]
|
||||
print(f"{blender_python_version_maj_min=}")
|
||||
python_version = f"py{found[0].replace('.', '')}"
|
||||
print(f"{python_version=}")
|
||||
|
||||
blenderbim_build_version = target_release_tag.replace("blenderbim-", "")
|
||||
|
||||
# url_blenderbim_py3x_win_zip
|
||||
release_zip_file_name = f"{target_release_tag}-{python_version}-{target_os}.zip"
|
||||
|
||||
# download release
|
||||
url_blenderbim_py3x_win_zip = f"{URL_IFCOS_RELEASES}{target_release_tag}/{release_zip_file_name}"
|
||||
os.popen(f"wget {url_blenderbim_py3x_win_zip} --no-verbose").read()
|
||||
|
||||
# sha256sum_blenderbim_py310_win_zip
|
||||
sha256sum_blenderbim_py3x_win_zip = get_file_sha256_hash(release_zip_file_name)
|
||||
|
||||
print("\n_____ fill dynamic chocolatey package parameters")
|
||||
HERE_DIR = pathlib.Path(__file__).parent.absolute()
|
||||
# print(f"[INFO] HERE_DIR: {HERE_DIR}")
|
||||
|
||||
topics = {
|
||||
"spec": {
|
||||
"path": HERE_DIR / "blenderbim.nuspec",
|
||||
"key_values": {
|
||||
"latest_blender_version_maj_min_pat": latest_blender_release_maj_min_pat,
|
||||
"blenderbim_build_version" : blenderbim_build_version,
|
||||
},
|
||||
},
|
||||
"install": {
|
||||
"path": HERE_DIR / "tools" / "chocolateyinstall.ps1",
|
||||
"key_values": {
|
||||
"url_blenderbim_py3x_win_zip" : url_blenderbim_py3x_win_zip,
|
||||
"sha256sum_blenderbim_py3x_win_zip": sha256sum_blenderbim_py3x_win_zip,
|
||||
"latest_blender_version_maj_min" : blender_version_min_maj,
|
||||
},
|
||||
},
|
||||
"uninstall": {
|
||||
"path": HERE_DIR / "tools" / "chocolateyuninstall.ps1",
|
||||
"key_values": {
|
||||
"latest_blender_version_maj_min": blender_version_min_maj,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for topic, info in topics.items():
|
||||
print(f" {topic}:")
|
||||
with open(info["path"], encoding="utf-8") as txt:
|
||||
content = txt.read()
|
||||
|
||||
for key, value in info["key_values"].items():
|
||||
# print(f"[INFO] replace: {env_var_name}")
|
||||
if not key in content:
|
||||
print(f" {key=} not found in {info['path']}")
|
||||
content = content.replace(key, value)
|
||||
|
||||
with open(info["path"], "w", encoding="utf-8") as txt:
|
||||
txt.write(content)
|
||||
print(f" written: {info['path']}")
|
||||
|
||||
print("[INFO] inserting dynamic chocolatey package parameters successful")
|
||||
|
||||
|
||||
print("\n_____ build choco.exe with mono")
|
||||
|
||||
choco_version = "1.1.0"
|
||||
os.popen(f"wget https://github.com/chocolatey/choco/archive/refs/tags/{choco_version}.tar.gz --quiet").read()
|
||||
os.popen(f"tar -xzf {choco_version}.tar.gz").read()
|
||||
print("choco tar unpack successful")
|
||||
os.chdir("choco-1.1.0")
|
||||
os.popen("./build.sh").read()
|
||||
|
||||
os.popen("cp -r build_output/chocolatey /opt/chocolatey").read()
|
||||
os.chdir(BLENDERBIM_DIR)
|
||||
|
||||
if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
||||
print("choco build successful")
|
||||
|
||||
print("\n_____ build choco pack")
|
||||
|
||||
os.popen("mono /opt/chocolatey/choco.exe pack --allow-unofficial").read()
|
||||
os.popen('mono /opt/chocolatey/choco.exe setapikey --key="{choco_token}" --source="https://push.chocolatey.org/" --allow-unofficial').read()
|
||||
|
||||
print("\n_____ build choco push")
|
||||
os.popen('mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose').read()
|
||||
|
||||
print(f"choco push of version: {target_release_tag} successful!")
|
||||
print(f"it took: {datetime.datetime.now() - start}")
|
||||
@@ -1,49 +0,0 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
print("[INFO] inserting dynamic chocolatey package parameters")
|
||||
HERE_DIR = Path(__file__).parent.absolute()
|
||||
# print(f"[INFO] HERE_DIR: {HERE_DIR}")
|
||||
|
||||
topics = {
|
||||
"spec": {
|
||||
"path": HERE_DIR / "blenderbim.nuspec",
|
||||
"env_vars": {
|
||||
"latest_blender_version_maj_min_pat",
|
||||
"blenderbim_build_version",
|
||||
},
|
||||
},
|
||||
"install": {
|
||||
"path": HERE_DIR / "tools" / "chocolateyinstall.ps1",
|
||||
"env_vars": {
|
||||
"url_blenderbim_py310_win_zip",
|
||||
"sha256sum_blenderbim_py310_win_zip",
|
||||
"latest_blender_version_maj_min",
|
||||
},
|
||||
},
|
||||
"uninstall": {
|
||||
"path": HERE_DIR / "tools" / "chocolateyuninstall.ps1",
|
||||
"env_vars": {
|
||||
"latest_blender_version_maj_min",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for topic, info in topics.items():
|
||||
print(f"[INFO] {topic}:")
|
||||
for env_var_name in info["env_vars"]:
|
||||
print(f"[INFO] {env_var_name} exists?: {os.environ[env_var_name]}")
|
||||
|
||||
with open(info["path"], encoding="utf-8") as txt:
|
||||
content = txt.read()
|
||||
|
||||
for env_var_name in info["env_vars"]:
|
||||
# print(f"[INFO] replace: {env_var_name}")
|
||||
content = content.replace(env_var_name, os.environ[env_var_name])
|
||||
|
||||
with open(info["path"], "w", encoding="utf-8") as txt:
|
||||
txt.write(content)
|
||||
print(f"[INFO] written: {info['path']}")
|
||||
|
||||
print("[INFO] inserting dynamic chocolatey package parameters successful")
|
||||
@@ -1,7 +1,7 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$url64 = 'url_blenderbim_py310_win_zip'
|
||||
$checksum64 = 'sha256sum_blenderbim_py310_win_zip'
|
||||
$url64 = 'url_blenderbim_py3x_win_zip'
|
||||
$checksum64 = 'sha256sum_blenderbim_py3x_win_zip'
|
||||
$checksumType64 = 'sha256'
|
||||
|
||||
$appDataUserDir = [System.Environment]::GetEnvironmentVariable('appdata')
|
||||
|
||||
+46
-30
@@ -27,8 +27,6 @@ if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
endif()
|
||||
|
||||
project(IfcOpenShell VERSION 0.8.0)
|
||||
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
cmake_policy(SET CMP0078 OLD)
|
||||
@@ -82,6 +80,17 @@ option(WITH_RELATIONSHIP_VALIDATION "Build IfcConvert with option to validate ge
|
||||
|
||||
option(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
|
||||
option(ADD_COMMIT_SHA "Add commit sha and branch in version number, warning results in many rebuilds, requires git" OFF)
|
||||
option(VERSION_OVERRIDE "Override the version defined in IfcParse.h with the file VERSION in the repository root" OFF)
|
||||
|
||||
if (VERSION_OVERRIDE)
|
||||
file(READ "../VERSION" "RELEASE_VERSION_")
|
||||
string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION)
|
||||
message(STATUS "Detected version '${RELEASE_VERSION}'")
|
||||
else()
|
||||
set(RELEASE_VERSION "0.8.0")
|
||||
endif()
|
||||
|
||||
project(IfcOpenShell VERSION ${RELEASE_VERSION})
|
||||
|
||||
if(MINIMAL_BUILD)
|
||||
message(STATUS "Setting options for minimal build")
|
||||
@@ -590,25 +599,27 @@ if(HDF5_SUPPORT)
|
||||
else()
|
||||
message(STATUS "Packaging hdf5 and zlib for conda distribution")
|
||||
|
||||
if(WIN32)
|
||||
# Windows
|
||||
set(zlib_post zlib)
|
||||
set(lib_ext lib)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
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
|
||||
set(HDF5_LIBRARIES
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
|
||||
)
|
||||
else()
|
||||
# linux and windows
|
||||
# Find HDF5 package
|
||||
find_package(HDF5 REQUIRED COMPONENTS C CXX)
|
||||
# Find ZLIB package
|
||||
find_package(ZLIB REQUIRED)
|
||||
# Include directories
|
||||
include_directories(${HDF5_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})
|
||||
# Link libraries
|
||||
set(HDF5_LIBRARIES ${HDF5_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
message(STATUS "HDF5 libraries: ${HDF5_LIBRARIES}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -741,6 +752,7 @@ endif(MSVC)
|
||||
include_directories(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR} ${HDF5_INCLUDE_DIR}
|
||||
${EIGEN_DIR} ${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${USD_INCLUDE_DIR}
|
||||
${TBB_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(NOT SCHEMA_VERSIONS)
|
||||
@@ -1095,22 +1107,26 @@ if(ADD_COMMIT_SHA)
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND)
|
||||
message("git found: ${GIT_EXECUTABLE} with version ${GIT_VERSION_STRING}")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} branch -a --contains HEAD
|
||||
OUTPUT_VARIABLE git_branches
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REPLACE "\n" ";" git_branch_list "${git_branches}")
|
||||
if (VERSION_OVERRIDE)
|
||||
set (git_branch ${RELEASE_VERSION})
|
||||
else()
|
||||
message("git found: ${GIT_EXECUTABLE} with version ${GIT_VERSION_STRING}")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} branch -a --contains HEAD
|
||||
OUTPUT_VARIABLE git_branches
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REPLACE "\n" ";" git_branch_list "${git_branches}")
|
||||
|
||||
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
|
||||
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
|
||||
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
|
||||
if(NOT git_branch_candidate_2 MATCHES "^HEAD$")
|
||||
string(REPLACE "/" ";" git_branch_candidate_2_list "${git_branch_candidate_2}")
|
||||
list(GET git_branch_candidate_2_list -1 git_branch)
|
||||
endif()
|
||||
endforeach()
|
||||
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
|
||||
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
|
||||
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
|
||||
if(NOT git_branch_candidate_2 MATCHES "^HEAD$")
|
||||
string(REPLACE "/" ";" git_branch_candidate_2_list "${git_branch_candidate_2}")
|
||||
list(GET git_branch_candidate_2_list -1 git_branch)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
@@ -1177,7 +1193,7 @@ if(BUILD_IFCGEOM)
|
||||
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
|
||||
)
|
||||
|
||||
install(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom
|
||||
install(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} ${kernel_libraries} IfcGeom
|
||||
ARCHIVE DESTINATION ${LIBDIR}
|
||||
LIBRARY DESTINATION ${LIBDIR}
|
||||
RUNTIME DESTINATION ${BINDIR}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.env
|
||||
+8
-6
@@ -1,10 +1,13 @@
|
||||
mkdir build && cd build
|
||||
|
||||
REM Remove dot from PY_VER for use in library name
|
||||
REM From https://github.com/tpaviot/pythonocc-core/blob/master/ci/conda/bld.bat
|
||||
set MY_PY_VER=%PY_VER:.=%
|
||||
rem set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
cmake -G "Ninja" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3_add2" ^
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
@@ -39,11 +42,10 @@ cmake -G "Ninja" ^
|
||||
-D Boost_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D Boost_USE_STATIC_LIBS:BOOL=OFF ^
|
||||
-D CITYJSON_SUPPORT:BOOL=OFF ^
|
||||
%SRC_DIR%/cmake
|
||||
|
||||
../cmake
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
:: Build and install
|
||||
cmake --build . -- install
|
||||
ninja install -j 1
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
+3
-5
@@ -6,16 +6,14 @@
|
||||
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
export FSUFFIX=dylib
|
||||
export CFLAGS="$CFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export CXXFLAGS="$CXXFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export LDFLAGS="$LDFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
||||
export FSUFFIX=so
|
||||
fi
|
||||
|
||||
|
||||
cmake -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" \
|
||||
cmake ${CMAKE_ARGS} -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3_add2" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
${CMAKE_PLATFORM_FLAGS[@]} \
|
||||
@@ -48,4 +46,4 @@ cmake -G Ninja \
|
||||
|
||||
ninja
|
||||
|
||||
ninja install -j 1
|
||||
ninja install -j 1
|
||||
@@ -1,11 +1,58 @@
|
||||
CONDA_BUILD_SYSROOT: # used for github actions conda daily build
|
||||
- /Users/runner/work/MacOSX10.13.sdk # [osx]
|
||||
variant:
|
||||
- novtk
|
||||
- all
|
||||
|
||||
occt:
|
||||
- 7.7.1
|
||||
- 7.8.1
|
||||
|
||||
c_compiler:
|
||||
- vs2022 # [win]
|
||||
|
||||
- vs2022 # [win]
|
||||
- gcc # [linux]
|
||||
- clang # [osx]
|
||||
c_stdlib:
|
||||
- vs # [win]
|
||||
- sysroot # [linux]
|
||||
- macosx_deployment_target # [osx]
|
||||
cxx_compiler:
|
||||
- vs2022 # [win]
|
||||
- vs2022 # [win]
|
||||
- gcc # [linux]
|
||||
- clangxx # [osx]
|
||||
c_compiler_version:
|
||||
- '12' # [linux]
|
||||
- '16' # [osx]
|
||||
cxx_compiler_version:
|
||||
- '12' # [linux]
|
||||
- '16' # [osx]
|
||||
c_stdlib_version:
|
||||
- '2.12' # [linux]
|
||||
- '10.13' # [osx]
|
||||
hdf5:
|
||||
- 1.14.3
|
||||
libboost_devel:
|
||||
- '1.84'
|
||||
libxml2:
|
||||
- '2'
|
||||
mpfr:
|
||||
- '4'
|
||||
gmp:
|
||||
- '6' # [not win]
|
||||
pin_run_as_build:
|
||||
python:
|
||||
min_pin: x.x
|
||||
max_pin: x.x
|
||||
|
||||
zlib:
|
||||
- '1'
|
||||
target_platform:
|
||||
- win-64 # [win]
|
||||
- linux-64 # [linux]
|
||||
- osx-64 # [osx]
|
||||
macos_machine:
|
||||
- x86_64-apple-darwin13.4.0
|
||||
MACOSX_DEPLOYMENT_TARGET:
|
||||
- '10.13'
|
||||
MACOSX_SDK_VERSION:
|
||||
- '10.13'
|
||||
|
||||
CONDA_BUILD_SYSROOT:
|
||||
- "/Users/runner/work/MacOSX10.13.sdk" # [osx]
|
||||
@@ -1,58 +0,0 @@
|
||||
@echo off
|
||||
|
||||
:: This is a batch file to set the environment variables for the project
|
||||
:: It is not necessary for conda compilation, but it provides you with type hints when working with the c++ libraries
|
||||
:: OpenCascade, CGAL, Eigen, etc distributed using conda-forge in your IDE.
|
||||
::
|
||||
:: mamba env update -f environment.build.yml --prune
|
||||
:: mamba activate ifcopenshell-build
|
||||
::
|
||||
:: Note!
|
||||
:: You have to add a .env file next to this env.bat file where you set PREFIX=<path to your conda env>
|
||||
set CONDA_BUILD=1
|
||||
set MY_PY_VER=311
|
||||
|
||||
:: set this file's parent directory as a variable
|
||||
set THIS_DIR=%~dp0
|
||||
|
||||
:: read the .env file located in THIS_DIR and set the environment variables.
|
||||
:: the .env file should contain a line like this:
|
||||
:: PREFIX=C:\Users\your_user_name\mambaforge3\envs\ifcopenshell-build
|
||||
for /f "tokens=*" %%i in (%THIS_DIR%.env) do set %%i
|
||||
|
||||
set LIBRARY_PREFIX=%PREFIX%/Library
|
||||
set CMAKE_PREFIX_PATH=%PREFIX%;%LIBRARY_PREFIX%/include;%LIBRARY_PREFIX%/lib;%LIBRARY_PREFIX%/bin
|
||||
|
||||
set OCC_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set OCC_INCLUDE_DIR=%LIBRARY_PREFIX%/include/opencascade
|
||||
|
||||
set CGAL_DIR=%LIBRARY_PREFIX%/lib/cmake/CGAL
|
||||
set CGAL_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set EIGEN_DIR=%LIBRARY_PREFIX%/include/eigen3
|
||||
set LIBXML2_INCLUDE_DIR=%LIBRARY_PREFIX%/include/libxml2
|
||||
set LIBXML2_LIBRARIES=%LIBRARY_PREFIX%/lib/libxml2.lib
|
||||
set Boost_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set Boost_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set Boost_USE_STATIC_LIBS=OFF
|
||||
|
||||
set GMP_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set GMP_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set MPFR_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
|
||||
set HDF5_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set HDF5_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set JSON_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
|
||||
set HDF5_SUPPORT=ON
|
||||
set BUILD_IFCPYTHON=ON
|
||||
set BUILD_IFCGEOM=ON
|
||||
set COLLADA_SUPPORT=OFF
|
||||
set BUILD_EXAMPLES=OFF
|
||||
set BUILD_GEOMSERVER=OFF
|
||||
set GLTF_SUPPORT=ON
|
||||
set BUILD_CONVERT=ON
|
||||
set BUILD_IFCMAX=OFF
|
||||
set IFCXML_SUPPORT=ON
|
||||
|
||||
set PYTHON_EXECUTABLE=%PREFIX%/python.exe
|
||||
set PYTHON_LIBRARY=%PREFIX%/libs/python%MY_PY_VER%.lib
|
||||
@@ -1,17 +0,0 @@
|
||||
name: ifcopenshell-build
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- boa
|
||||
- boost-cpp
|
||||
- occt
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- mpfr
|
||||
- nlohmann_json
|
||||
- swig
|
||||
- zlib
|
||||
# These needs to be installed per platform
|
||||
#- sel(unix): gmp
|
||||
#- sel(win): mpir
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
{% set version = environ.get('VERSION_OVERRIDE', '0.8.0') %}
|
||||
{% set build = 0 %}
|
||||
|
||||
# Higher number -> Always prioritize "novtk" variant over "all" variant
|
||||
{% set build = build + 200 %} # [variant == "novtk"]
|
||||
{% set build = build + 100 %} # [variant == "all"]
|
||||
|
||||
package:
|
||||
name: ifcopenshell
|
||||
version: {{ version }}
|
||||
|
||||
source:
|
||||
path: ../
|
||||
|
||||
|
||||
build:
|
||||
number: {{ build }}
|
||||
skip: true # [py<39]
|
||||
binary_relocation: false # [osx]
|
||||
string: py{{ CONDA_PY }}_{{ variant }}_h{{ PKG_HASH }}_{{ build }}
|
||||
run_exports:
|
||||
- {{ pin_subpackage('ifcopenshell', max_pin='x.x.x') }} *{{ variant }}*
|
||||
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- cmake
|
||||
- ninja
|
||||
- swig >=4.1.1
|
||||
- {{ stdlib("c") }}
|
||||
- {{ compiler('c') }}
|
||||
- {{ compiler('cxx') }}
|
||||
host:
|
||||
- python
|
||||
- libboost-devel
|
||||
- occt *=*{{ variant }}*
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- eigen
|
||||
- mpfr
|
||||
- nlohmann_json
|
||||
- gmp # [unix]
|
||||
- mpir # [win]
|
||||
- zlib
|
||||
run:
|
||||
- python
|
||||
- {{ pin_compatible('occt', max_pin='x.x.x') }} *{{ variant }}*
|
||||
- {{ pin_compatible('cgal-cpp', max_pin='x.x') }}
|
||||
|
||||
|
||||
test:
|
||||
imports:
|
||||
- ifcopenshell
|
||||
|
||||
|
||||
about:
|
||||
home: https://ifcopenshell.org
|
||||
license: LGPL-3.0-or-later
|
||||
license_file: COPYING
|
||||
summary: 'IfcOpenShell is a library to support the IFC file format'
|
||||
description: |
|
||||
IfcOpenShell
|
||||
============
|
||||
|
||||
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, IFC4x3, 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.
|
||||
|
||||
For more information, see:
|
||||
|
||||
* [IfcOpenShell Website](http://ifcopenshell.org)
|
||||
* [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](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)
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Anaconda Daily Build</td>
|
||||
<td><a href="https://anaconda.org/ifcopenshell/ifcopenshell"><img src="https://img.shields.io/conda/vn/ifcopenshell/ifcopenshell" alt="Anaconda-Server Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Anaconda v0.8.0 Stable</td>
|
||||
<td><a href="https://anaconda.org/conda-forge/ifcopenshell"><img src="https://img.shields.io/conda/vn/conda-forge/ifcopenshell" alt="Anaconda-Server Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PyPi Daily Build</td>
|
||||
<td><a href="https://pypi.org/project/ifcopenshell/"><img src="https://img.shields.io/pypi/v/ifcopenshell" alt="PyPi Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ArchLinux AUR Package Stable</td>
|
||||
<td><a href="https://aur.archlinux.org/packages/ifcopenshell"><img src="https://img.shields.io/aur/version/ifcopenshell" alt="AUR Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ArchLinux AUR Package git</td>
|
||||
<td><a href="https://aur.archlinux.org/packages/ifcopenshell-git"><img src="https://img.shields.io/aur/version/ifcopenshell-git" alt="AUR Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BlenderBIM Add-on Chocolatey (under moderation)</td>
|
||||
<td><a href="https://community.chocolatey.org/packages/blenderbim-nightly/"><img src="https://img.shields.io/chocolatey/v/blenderbim-nightly" alt="Chocolatey Badge"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sponsor development on OpenCollective</td>
|
||||
<td><a href="https://opencollective.com/opensourcebim/"><img src="https://opencollective.com/opensourcebim/tiers/badge.svg" alt="Financial Contributors"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Docker hub</td>
|
||||
<td><a href="https://hub.docker.com/r/aecgeeks/ifcopenshell"><img src="https://img.shields.io/docker/pulls/aecgeeks/ifcopenshell" alt="Docker Pulls"></a></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
Contents
|
||||
--------
|
||||
|
||||
Those marked with an asterisk are part of IfcOpenShell.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>License</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>bcf</td>
|
||||
<td>Library to read and write BCF-XML and query OpenCDE BCF-API modules</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>blenderbim</td>
|
||||
<td>Add-on to Blender providing a graphical native IFC authoring platform</td>
|
||||
<td>GPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>bsdd</td>
|
||||
<td>Library to query the bSDD API</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifc2ca</td>
|
||||
<td>Utility to convert IFC structural analysis models to Code_Aster</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifc4d</td>
|
||||
<td>Convert to and from IFC and project management software</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifc5d</td>
|
||||
<td>Report and optimise cost information from IFC</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcbimtester</td>
|
||||
<td>Wrapper for Gherkin based unit testing for IFC models</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcblender</td>
|
||||
<td>Historic Blender IFC import add-on</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifccityjson</td>
|
||||
<td>Convert CityJSON to IFC</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcclash</td>
|
||||
<td>Clash detection library and CLI app</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifccobie</td>
|
||||
<td>Extract IFC data for COBie handover requirements</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcconvert</td>
|
||||
<td>CLI app to convert IFC to many other formats</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifccsv</td>
|
||||
<td>Library and CLI app to export and import schedules from IFC</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcdiff</td>
|
||||
<td>Compare changes between IFC models</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcfm</td>
|
||||
<td>Extract IFC data for FM handover requirements</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcgeom</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcgeom_schema_agnostic</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcgeomserver</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcjni</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcmax</td>
|
||||
<td>Historic extension for IFC support in 3DS Max</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcopenshell-python</td>
|
||||
<td>Python library for IFC manipulation</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcparse</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcpatch</td>
|
||||
<td>Utility to run pre-packaged scripts to manipulate IFCs</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcsverchok</td>
|
||||
<td>Blender Add-on for visual node programming with IFC</td>
|
||||
<td>GPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifctester</td>
|
||||
<td>Library, CLI and webapp for IDS model auditing</td>
|
||||
<td>LGPL-3.0-or-later</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ifcwrap</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>qtviewer</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>serializers</td>
|
||||
<td>Internal library for IfcOpenShell</td>
|
||||
<td>LGPL-3.0-or-later*</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
[LGPL]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/COPYING.LESSER "LGPL-3.0-or-later"
|
||||
[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"
|
||||
[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"
|
||||
[win/readme.md]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/win/readme.md "win/readme.md"
|
||||
[nix/build-all.py]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/nix/build-all.py "nix/build-all.py"
|
||||
doc_url: https://ifcopenshell.org/
|
||||
dev_url: https://github.com/IfcOpenShell/IfcOpenShell
|
||||
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- adrianinsaval
|
||||
- looooo
|
||||
- Krande
|
||||
@@ -1,64 +0,0 @@
|
||||
context:
|
||||
name: ifcopenshell
|
||||
version: 0.8.0alpha1
|
||||
|
||||
package:
|
||||
name: '{{ name|lower }}'
|
||||
version: '{{ version }}'
|
||||
|
||||
source:
|
||||
path: ..
|
||||
|
||||
build:
|
||||
number: 1
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- "{{ compiler('c') }}"
|
||||
- "{{ compiler('cxx') }}"
|
||||
- ninja >=1.10.2
|
||||
- cmake
|
||||
- swig 4.1.1
|
||||
|
||||
host:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- eigen
|
||||
- mpfr
|
||||
- sel(unix): gmp
|
||||
- sel(win): mpir
|
||||
- nlohmann_json
|
||||
- zlib
|
||||
|
||||
run:
|
||||
- python
|
||||
- {{ 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') }}
|
||||
- libxml2
|
||||
- hdf5
|
||||
- eigen
|
||||
- mpfr
|
||||
- sel(unix): gmp
|
||||
- sel(win): mpir
|
||||
- nlohmann_json
|
||||
- zlib
|
||||
|
||||
test:
|
||||
imports:
|
||||
- ifcopenshell
|
||||
|
||||
about:
|
||||
home: https://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://blenderbim.org/docs-python/
|
||||
dev_url: https://github.com/IfcOpenShell/IfcOpenShell
|
||||
+5
-2
@@ -53,6 +53,7 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import glob
|
||||
import subprocess as sp
|
||||
import shutil
|
||||
import tarfile
|
||||
@@ -576,6 +577,7 @@ if "freetype" in targets:
|
||||
download_url = "https://github.com/freetype/freetype",
|
||||
download_name = "freetype2",
|
||||
download_tool=download_tool_git,
|
||||
revision="VER-2-11-1"
|
||||
)
|
||||
|
||||
if USE_OCCT and "occ" in targets:
|
||||
@@ -830,7 +832,8 @@ cmake_args = [
|
||||
"-DJSON_INCLUDE_DIR=" f"{DEPS_DIR}/install/json",
|
||||
"-DEIGEN_DIR=" f"{DEPS_DIR}/install/eigen-3.3.9",
|
||||
"-DBoost_NO_BOOST_CMAKE=" "On",
|
||||
"-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off")
|
||||
"-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off"),
|
||||
"-DVERSION_OVERRIDE=" +("On" if ADD_COMMIT_SHA else "Off")
|
||||
]
|
||||
|
||||
if "wasm" in flags:
|
||||
@@ -969,7 +972,7 @@ if "IfcOpenShell-Python" in targets:
|
||||
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)
|
||||
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", glob.glob(os.path.join(module_dir, "_ifcopenshell_wrapper*.so"))[0]], cwd=module_dir)
|
||||
|
||||
return module_dir
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package:
|
||||
name: ifcopenshell
|
||||
version: 0.7.0
|
||||
version: 0.8.0
|
||||
|
||||
source:
|
||||
path: IfcOpenShell
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name='IfcOpenShell',
|
||||
version='0.7.0',
|
||||
version='0.8.0',
|
||||
description='IfcOpenShell is an open source (LGPL) software library for working with the Industry Foundation Classes (IFC) file format.',
|
||||
author='Thomas Krijnen',
|
||||
author_email='thomas@aecgeeks.com',
|
||||
|
||||
+5
-1
@@ -1,2 +1,6 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
line-length = 120
|
||||
|
||||
[tool.pyright]
|
||||
reportInvalidTypeForm = false
|
||||
disableBytesTypePromotions = true
|
||||
|
||||
@@ -10,7 +10,7 @@ type: software
|
||||
authors:
|
||||
- name: "IfcOpenShell contributors"
|
||||
repository-code: >-
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/bcf
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/bcf
|
||||
abstract: >-
|
||||
Library to read and write BCF-XML and query OpenCDE
|
||||
BCF-API modules
|
||||
|
||||
+26
-4
@@ -1,4 +1,22 @@
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
# BCF - BCF Python library
|
||||
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
|
||||
#
|
||||
# This file is part of BCF.
|
||||
#
|
||||
# BCF is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BCF is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
VERSION:=$(shell cat ../../VERSION)
|
||||
SED:=sed -i
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED:=sed -i '' -e
|
||||
@@ -21,10 +39,14 @@ models:
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -rf dist
|
||||
$(SED) "s/999999/$(VERSION)/" pyproject.toml
|
||||
cp pyproject.toml pyproject.toml.bak
|
||||
cp src/bcf/__init__.py __init__.py.bak
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' pyproject.toml
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' src/bcf/__init__.py
|
||||
python -m build
|
||||
$(SED) "s/$(VERSION)/999999/" pyproject.toml
|
||||
mv pyproject.toml.bak pyproject.toml
|
||||
mv __init__.py.bak src/bcf/__init__.py
|
||||
|
||||
# .PHONY
|
||||
# api:
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
|
||||
@@ -17,7 +17,7 @@ dependencies = [
|
||||
"numpy",
|
||||
"ifcopenshell",
|
||||
]
|
||||
version = "0.0.999999"
|
||||
version = "0.0.0"
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: OS Independent",
|
||||
@@ -138,4 +138,4 @@ max-attributes = 10
|
||||
|
||||
[tool.pylint.format]
|
||||
expected-line-ending-format = "LF"
|
||||
max-line-length = 120
|
||||
max-line-length = 120
|
||||
|
||||
@@ -15,3 +15,5 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__version__ = version = "0.0.0"
|
||||
|
||||
@@ -10,7 +10,7 @@ type: software
|
||||
authors:
|
||||
- name: "IfcOpenShell contributors"
|
||||
repository-code: >-
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/blenderbim
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/blenderbim
|
||||
url: 'https://blenderbim.org/'
|
||||
repository-artifact: 'https://blenderbim.org/download.html'
|
||||
abstract: >-
|
||||
|
||||
+25
-12
@@ -82,12 +82,13 @@ ifeq ($(PLATFORM), win)
|
||||
PYPI_PLATFORM:=--platform win_amd64
|
||||
endif
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=c18e4ea
|
||||
.PHONY: bump
|
||||
bump:
|
||||
cd . && $(SED) "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/ && $(SED) "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/docs/ifcconvert/ && $(SED) "s/$(OLD)/$(NEW)/" installation.rst
|
||||
cd ../ifcopenshell-python/docs/ifcopenshell-python/ && $(SED) "s/$(OLD)/$(NEW)/" installation.rst
|
||||
cd . && $(SED) -b "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/ && $(SED) -b "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/docs/ifcconvert/ && $(SED) -b "s/$(OLD)/$(NEW)/" installation.rst
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
@@ -100,7 +101,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.8.0-90ae709-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.9-${OLD}-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
@@ -113,13 +114,11 @@ endif
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/refs/heads/v0.8.0.zip
|
||||
cd dist/working && unzip v0.8.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.8.0/src/ifcopenshell-python/ifcopenshell/util dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/*.py dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
|
||||
# IfcOpenBot sometimes lags behind, so we only use its binaries
|
||||
mv dist/blenderbim/libs/site/packages/ifcopenshell/*ifcopenshell_wrapper* dist/working/
|
||||
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
mv dist/working/*ifcopenshell_wrapper* dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
# Provides bcf functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCClash functionality
|
||||
@@ -172,6 +171,8 @@ endif
|
||||
mkdir dist/working
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && mkdir site-packages
|
||||
# IfcOpenShell dependency - support for new typing features
|
||||
cd dist/working && . env/bin/activate && $(PIP) install typing_extensions --target=./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
|
||||
@@ -213,6 +214,18 @@ endif
|
||||
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
|
||||
# Required by web module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install python-socketio[asyncio_client] --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install aiohttp --target=./site-packages
|
||||
# Required by light module
|
||||
ifeq ($(PLATFORM), linux)
|
||||
# See https://github.com/LBNL-ETA/pyradiance/issues/34
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pyradiance --platform manylinux1_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
else ifneq ($(PLATFORM), macos)
|
||||
# No support for MacOS Intel from upstream. Too bad :(
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
endif
|
||||
|
||||
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ import sys
|
||||
IN_BLENDER = sys.modules.get("bpy", None)
|
||||
if IN_BLENDER:
|
||||
import bpy
|
||||
import addon_utils
|
||||
import platform
|
||||
import traceback
|
||||
import webbrowser
|
||||
import addon_utils
|
||||
from collections import deque
|
||||
|
||||
# NOTE: bl_info is superseded by blender_manifest.toml
|
||||
# if addon is installed as an extension (Blender 4.2+)
|
||||
bl_info = {
|
||||
"name": "BlenderBIM",
|
||||
"description": "Transforms Blender into a native Building Information Model authoring platform using IFC.",
|
||||
@@ -92,7 +94,6 @@ if IN_BLENDER:
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
|
||||
|
||||
try:
|
||||
import blenderbim.bim
|
||||
import ifcopenshell.api
|
||||
|
||||
def log_api(usecase_path, ifc_file, settings):
|
||||
@@ -107,9 +108,21 @@ if IN_BLENDER:
|
||||
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
|
||||
|
||||
def register():
|
||||
global BLENDER_PACKAGE_NAME, __name__, __package__
|
||||
BLENDER_PACKAGE_NAME = __name__
|
||||
if bpy.app.version >= (4, 2, 0):
|
||||
sys.modules["blenderbim"] = sys.modules[__name__]
|
||||
__name__ = "blenderbim"
|
||||
# Only renaming __name__ is actually required to make imports work.
|
||||
# We renaming __package__ just for consistency.
|
||||
__package__ = "blenderbim"
|
||||
import blenderbim.bim
|
||||
|
||||
blenderbim.bim.register()
|
||||
|
||||
def unregister():
|
||||
import blenderbim.bim
|
||||
|
||||
blenderbim.bim.unregister()
|
||||
|
||||
except:
|
||||
|
||||
@@ -84,6 +84,8 @@ modules = {
|
||||
"debug": None,
|
||||
"ifcgit": None,
|
||||
"covering": None,
|
||||
"web": None,
|
||||
"light": None,
|
||||
# Uncomment this line to enable loading of the demo module. Happy hacking!
|
||||
# The name "demo" must correlate to a folder name in `bim/module/`.
|
||||
# "demo": None,
|
||||
@@ -130,7 +132,6 @@ classes = [
|
||||
prop.GlobalId,
|
||||
prop.BIMObjectProperties,
|
||||
prop.BIMCollectionProperties,
|
||||
prop.BIMMaterialProperties,
|
||||
prop.BIMMeshProperties,
|
||||
prop.BIMFacet,
|
||||
prop.BIMFilterGroup,
|
||||
@@ -141,6 +142,7 @@ classes = [
|
||||
# Tabs panel
|
||||
ui.BIM_PT_tabs,
|
||||
# Project overview
|
||||
ui.BIM_PT_tab_new_project_wizard,
|
||||
ui.BIM_PT_tab_project_info,
|
||||
ui.BIM_PT_tab_spatial_decomposition,
|
||||
ui.BIM_PT_tab_project_setup,
|
||||
@@ -223,8 +225,6 @@ def register():
|
||||
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)
|
||||
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
||||
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||
@@ -283,8 +283,6 @@ def unregister():
|
||||
del bpy.types.Scene.BIMProperties
|
||||
del bpy.types.Collection.BIMCollectionProperties
|
||||
del bpy.types.Object.BIMObjectProperties
|
||||
del bpy.types.Material.BIMObjectProperties
|
||||
del bpy.types.Material.BIMMaterialProperties
|
||||
del bpy.types.Mesh.BIMMeshProperties
|
||||
del bpy.types.Curve.BIMMeshProperties
|
||||
del bpy.types.Camera.BIMMeshProperties
|
||||
|
||||
@@ -67,11 +67,11 @@ DATA;
|
||||
#60=IFCMATERIALPROFILESET($,$,(#62),$);
|
||||
#61=IFCRELASSOCIATESMATERIAL('09FXUZCnrFEffxBrtg16gx',$,$,$,(#59),#60);
|
||||
#62=IFCMATERIALPROFILE($,$,#20,#58,$,$);
|
||||
#63=IFCSLABTYPE('1Akbal7tP5DAPW0ti$Qp30',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#63=IFCSLABTYPE('1Akbal7tP5DAPW0ti$Qp30',$,'FLR200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#64=IFCMATERIALLAYERSET((#66),$,$);
|
||||
#65=IFCRELASSOCIATESMATERIAL('0jzkXlkBL4HhmQ_FTWW6Cj',$,$,$,(#63),#64);
|
||||
#66=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#67=IFCSLABTYPE('0$gpYNi3T08BrxaR67CxgI',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#67=IFCSLABTYPE('0$gpYNi3T08BrxaR67CxgI',$,'FLR300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#68=IFCMATERIALLAYERSET((#70),$,$);
|
||||
#69=IFCRELASSOCIATESMATERIAL('3a6kjEM29DleYTlSkA8_DK',$,$,$,(#67),#68);
|
||||
#70=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
|
||||
@@ -67,11 +67,11 @@ DATA;
|
||||
#60=IFCMATERIALPROFILESET($,$,(#62),$);
|
||||
#61=IFCRELASSOCIATESMATERIAL('09FXUZCnrFEffxBrtg16gx',$,$,$,(#59),#60);
|
||||
#62=IFCMATERIALPROFILE($,$,#20,#58,$,$);
|
||||
#63=IFCSLABTYPE('1Akbal7tP5DAPW0ti$Qp30',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#63=IFCSLABTYPE('1Akbal7tP5DAPW0ti$Qp30',$,'FLR200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#64=IFCMATERIALLAYERSET((#66),$,$);
|
||||
#65=IFCRELASSOCIATESMATERIAL('0jzkXlkBL4HhmQ_FTWW6Cj',$,$,$,(#63),#64);
|
||||
#66=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#67=IFCSLABTYPE('0$gpYNi3T08BrxaR67CxgI',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#67=IFCSLABTYPE('0$gpYNi3T08BrxaR67CxgI',$,'FLR300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#68=IFCMATERIALLAYERSET((#70),$,$);
|
||||
#69=IFCRELASSOCIATESMATERIAL('3a6kjEM29DleYTlSkA8_DK',$,$,$,(#67),#68);
|
||||
#70=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import argparse
|
||||
from aiohttp import web
|
||||
import socketio
|
||||
import pystache
|
||||
import json
|
||||
|
||||
sio_port = 8080 # default port
|
||||
|
||||
sio = socketio.AsyncServer(
|
||||
cors_allowed_origins="*",
|
||||
async_mode="aiohttp",
|
||||
)
|
||||
|
||||
# sio.instrument(
|
||||
# auth={
|
||||
# "username": "admin",
|
||||
# "password": "admin",
|
||||
# }
|
||||
# )
|
||||
|
||||
app = web.Application()
|
||||
sio.attach(app)
|
||||
|
||||
blender_messages = {}
|
||||
|
||||
|
||||
# Web namespace
|
||||
class WebNamespace(socketio.AsyncNamespace):
|
||||
def __init__(self, namespace):
|
||||
super().__init__(namespace)
|
||||
|
||||
async def on_connect(self, sid, environ):
|
||||
print(f"Web client connected: {sid}")
|
||||
|
||||
async def on_disconnect(self, sid):
|
||||
print(f"Web client disconnected: {sid}")
|
||||
|
||||
async def on_web_operator(self, sid, data):
|
||||
await sio.emit(
|
||||
"web_operator",
|
||||
data,
|
||||
namespace="/blender",
|
||||
room=data["blenderId"],
|
||||
)
|
||||
|
||||
|
||||
# Blender namespace
|
||||
class BlenderNamespace(socketio.AsyncNamespace):
|
||||
def __init__(self, namespace):
|
||||
super().__init__(namespace)
|
||||
|
||||
async def on_connect(self, sid, environ):
|
||||
print(f"Blender client connected: {sid}")
|
||||
# Notify web client about new connection
|
||||
blender_messages[sid] = {}
|
||||
await sio.emit("blender_connect", sid, namespace="/web")
|
||||
|
||||
async def on_disconnect(self, sid):
|
||||
print(f"Blender client disconnected: {sid}")
|
||||
# Remove client message and notify web client about disconnection
|
||||
if sid in blender_messages:
|
||||
del blender_messages[sid]
|
||||
await sio.emit("blender_disconnect", sid, namespace="/web")
|
||||
|
||||
async def on_data(self, sid, data):
|
||||
print(f"Data from Blender client {sid}")
|
||||
# blender_messages[sid]["default_data"] = data
|
||||
# await sio.emit("default_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
async def on_csv_data(self, sid, data):
|
||||
print(f"CSV data from Blender client {sid}")
|
||||
# Store the message and forward it to the web client
|
||||
blender_messages[sid]["csv_data"] = data
|
||||
await sio.emit("csv_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
async def on_gantt_data(self, sid, data):
|
||||
print(f"Gant Chart data from Blender client {sid}")
|
||||
blender_messages[sid]["gantt_data"] = data
|
||||
await sio.emit("gantt_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
|
||||
# Attach namespaces
|
||||
sio.register_namespace(WebNamespace("/web"))
|
||||
sio.register_namespace(BlenderNamespace("/blender"))
|
||||
|
||||
|
||||
# Define a route to render the index.html template
|
||||
async def index(request):
|
||||
with open("templates/index.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def gantt(request):
|
||||
with open("templates/gantt.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
app.router.add_get("/", index)
|
||||
app.router.add_get("/gantt", gantt)
|
||||
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
||||
app.router.add_static("/static/", path="./static", name="static")
|
||||
|
||||
|
||||
def main():
|
||||
global sio_port
|
||||
|
||||
parser = argparse.ArgumentParser(description="SocketIO server")
|
||||
parser.add_argument("--host", type=str, default="127.0.0.1", help="Host to run the server on")
|
||||
parser.add_argument("--port", type=int, default=8080, help="Port to run the server on")
|
||||
|
||||
args = parser.parse_args()
|
||||
sio_port = args.port
|
||||
web.run_app(app, host=args.host, port=sio_port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# web.run_app(app, host="127.0.0.1", port=sio_port)
|
||||
@@ -0,0 +1,280 @@
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #252525;
|
||||
color: #e0e0e0;
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-top: 20px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: #121212;
|
||||
padding: 1em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: 40px;
|
||||
height: 40px;
|
||||
/* Adjust height as needed */
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 1px;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: #3fb449;
|
||||
}
|
||||
|
||||
nav .bottom-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #25682a;
|
||||
}
|
||||
|
||||
.table-description {
|
||||
margin-bottom: 1rem;
|
||||
width: 50%;
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-color: #464444;
|
||||
border: 1px solid #464444;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 0 0.7rem rgba(95, 95, 95, 0.4);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-description tr {
|
||||
display: table-row;
|
||||
border-bottom: 1px solid #464444;
|
||||
}
|
||||
|
||||
.gantt-info {
|
||||
margin: 0.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin: 0.5rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.no-print {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------ Overwriting JSGantt CSS rules ------------ */
|
||||
|
||||
div.gantt {
|
||||
background-color: #252525;
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
.gantt table {
|
||||
border-color: #464444;
|
||||
;
|
||||
}
|
||||
|
||||
.gantt td {
|
||||
border-color: #464444;
|
||||
;
|
||||
}
|
||||
|
||||
/* Headings and cell defaults */
|
||||
.gmajorheading,
|
||||
.gminorheading,
|
||||
.gminorheadingwkend,
|
||||
.gtaskcell,
|
||||
.gtaskcellcurrent,
|
||||
.gtaskcellwkend,
|
||||
.gname,
|
||||
.ggroupitem,
|
||||
.gtaskheading {
|
||||
background-color: #252525;
|
||||
border-color: #464444;
|
||||
;
|
||||
}
|
||||
|
||||
.gtaskheading,
|
||||
.gname,
|
||||
.gtaskname,
|
||||
.gres,
|
||||
.gdur,
|
||||
.gcomp,
|
||||
.gstartdate,
|
||||
.gplanstartdate,
|
||||
.gplanenddate,
|
||||
.gcost,
|
||||
.gchartlbl,
|
||||
.gcontainercol,
|
||||
.genddate {
|
||||
color: #c7c7c7;
|
||||
border-color: #464444;
|
||||
;
|
||||
}
|
||||
|
||||
.gtaskname>div {
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
.gtaskbarcontainer.gplan,
|
||||
.gchartlbl.gcontainercol {
|
||||
background: #252525;
|
||||
border-color: #464444;
|
||||
}
|
||||
|
||||
.gtaskname div,
|
||||
.gtaskheading div,
|
||||
.gtaskname span {
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
|
||||
.gtasklist,
|
||||
.gadditional {
|
||||
border: #464444 1px solid
|
||||
}
|
||||
|
||||
.gchartgrid {
|
||||
background-color: #252525;
|
||||
}
|
||||
|
||||
.glistgrid,
|
||||
.glistlbl {
|
||||
background-color: #252525;
|
||||
border-color: #464444;
|
||||
;
|
||||
}
|
||||
|
||||
/* .gTaskInfo {
|
||||
background-color: #2e2e2e;
|
||||
color: #c7c7c7;
|
||||
border-color: #c7c7c7;;
|
||||
} */
|
||||
|
||||
/* Dark mode scrollbar */
|
||||
.frame::-webkit-scrollbar-thumb {
|
||||
background-color: #252525;
|
||||
}
|
||||
|
||||
.frame::-webkit-scrollbar-track {
|
||||
background-color: #252525;
|
||||
}
|
||||
|
||||
/* Highlight row */
|
||||
.gitemhighlight td {
|
||||
background-color: #009136;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Differentiate Group, Milestone and Ordinary task items (applied to row) */
|
||||
.ggroupitem {
|
||||
background-color: #252525;
|
||||
font-weight: bold;
|
||||
border-color: #464444;
|
||||
}
|
||||
|
||||
.gmileitem,
|
||||
.glineitem {
|
||||
background-color: #252525;
|
||||
}
|
||||
|
||||
/* Task bar caption text styles */
|
||||
.gmilecaption,
|
||||
.ggroupcaption,
|
||||
.gcaption {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Task complete %age bar */
|
||||
.gtaskcomplete {
|
||||
background-color: #777777;
|
||||
}
|
||||
|
||||
/* Milestones */
|
||||
.gmdtop {
|
||||
border-bottom: 5px solid #ffffff;
|
||||
}
|
||||
|
||||
.gmdbottom {
|
||||
border-top: 5px solid #ffffff;
|
||||
}
|
||||
|
||||
.gfoldercollapse {
|
||||
color: #c7c7c7;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
|
||||
font-size: 12px;
|
||||
font-family: Courier, "Courier New", monospace;
|
||||
}
|
||||
|
||||
/* Highlight for collapsible row */
|
||||
.gname.ggroupitem {
|
||||
background-color: rgb(75, 75, 75);
|
||||
}
|
||||
|
||||
/* Form label and selected highlighting */
|
||||
.gformlabel {
|
||||
background-color: #2e2e2e;
|
||||
color: #c7c7c7;
|
||||
border: #4a4a4a 1px solid;
|
||||
}
|
||||
|
||||
span.gformlabel:hover {
|
||||
background-color: #3a3a3a;
|
||||
border-color: #565656;
|
||||
}
|
||||
|
||||
span.gselected {
|
||||
background-color: #565656;
|
||||
border-color: #777777;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.gantt-inputtable {
|
||||
background-color: #3b3b3b;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #000;
|
||||
color: #c7c7c7;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #252525;
|
||||
color: #e0e0e0;
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-top: 20px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.csv-table {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: #121212;
|
||||
padding: 1em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: 40px;
|
||||
height: 40px;
|
||||
/* Adjust height as needed */
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 1px;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: #3fb449;
|
||||
}
|
||||
|
||||
nav .bottom-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #25682a;
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
// keeps track of blenders connected in form of
|
||||
// shown:bool, workSchedule: {}, ganttTasks: {},
|
||||
const connectedClients = {};
|
||||
let socket;
|
||||
|
||||
// print options
|
||||
const pageSizes = [
|
||||
{ value: "210,297", text: "A4 Portrait" },
|
||||
{ value: "297,210", text: "A4 Landscape" },
|
||||
{ value: "297,420", text: "A3 Portrait" },
|
||||
{ value: "420,297", text: "A3 Landscape" },
|
||||
{ value: "420,594", text: "A2 Portrait" },
|
||||
{ value: "594,420", text: "A2 Landscape" },
|
||||
{ value: "594,841", text: "A1 Portrait" },
|
||||
{ value: "841,594", text: "A1 Landscape" },
|
||||
{ value: "841,1189", text: "A0 Portrait" },
|
||||
{ value: "1189,841", text: "A0 Landscape" },
|
||||
];
|
||||
|
||||
// Document ready function
|
||||
$(document).ready(function () {
|
||||
connectSocket();
|
||||
});
|
||||
|
||||
// Function to connect to Socket.IO server
|
||||
function connectSocket() {
|
||||
const url = "ws://localhost:" + SOCKET_PORT + "/web";
|
||||
socket = io(url);
|
||||
console.log("socket: ", socket);
|
||||
|
||||
// Register socket event handlers
|
||||
socket.on("blender_connect", handleBlenderConnect);
|
||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||
socket.on("gantt_data", handleGanttData);
|
||||
}
|
||||
|
||||
// Function to handle 'blender_connect' event
|
||||
function handleBlenderConnect(blenderId) {
|
||||
console.log("blender_connect: ", blenderId);
|
||||
if (!connectedClients.hasOwnProperty(blenderId)) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: false,
|
||||
workSchedule: {},
|
||||
ganttTasks: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle 'blender_disconnect' event
|
||||
function handleBlenderDisconnect(blenderId) {
|
||||
console.log("blender_disconnect: ", blenderId);
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
delete connectedClients[blenderId];
|
||||
removeGanttElement(blenderId);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle 'gantt_data' event
|
||||
function handleGanttData(data) {
|
||||
const blenderId = data["blenderId"];
|
||||
|
||||
console.log(data);
|
||||
|
||||
const filename = data["data"]["IFC_File"];
|
||||
const ganttTasks = data["data"]["gantt_data"]["tasks"];
|
||||
const ganttWorkSched = data["data"]["gantt_data"]["work_schedule"];
|
||||
// const ganttWorkSched = {};
|
||||
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
if (!connectedClients[blenderId].shown) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ganttTasks: ganttTasks,
|
||||
workSchedule: ganttWorkSched,
|
||||
};
|
||||
addGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
|
||||
} else {
|
||||
updateGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
|
||||
connectedClients[blenderId].workSchedule = ganttWorkSched;
|
||||
connectedClients[blenderId].ganttTasks = ganttTasks;
|
||||
}
|
||||
} else {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ganttTasks: ganttTasks,
|
||||
workSchedule: ganttWorkSched,
|
||||
};
|
||||
addGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a new gantt with data and filename
|
||||
function addGanttElement(blenderId, tasks, workSched, filename) {
|
||||
const ganttContainer = $("<div></div>")
|
||||
.addClass("gantt-container")
|
||||
.attr("id", "container-" + blenderId);
|
||||
|
||||
const ganttTitle = $("<h3></h3>")
|
||||
.attr("id", "title-" + blenderId)
|
||||
.text(filename)
|
||||
.addClass("no-print")
|
||||
.css("margin-bottom", "10px");
|
||||
|
||||
const workSchedDiv = $("<div></div>").attr("id", "workSched" + blenderId);
|
||||
|
||||
const scheduleTable = $("<table></table>")
|
||||
.addClass("no-print table-description")
|
||||
.attr("id", "workSchedTable-" + blenderId)
|
||||
.hide();
|
||||
|
||||
$.each(workSched, (key, value) => {
|
||||
value = value ? value : "null";
|
||||
$("<tr></tr>")
|
||||
.append($("<td></td>").text(key))
|
||||
.append($("<td></td>").text(value))
|
||||
.appendTo(scheduleTable);
|
||||
});
|
||||
|
||||
const toggleButton = $("<button></button>")
|
||||
.text("Show Schedule Info")
|
||||
.addClass("btn no-print")
|
||||
.on("click", function () {
|
||||
scheduleTable.toggle();
|
||||
const buttonText = scheduleTable.is(":visible")
|
||||
? "Hide Schedule Info"
|
||||
: "Show Schedule Info";
|
||||
toggleButton.text(buttonText);
|
||||
});
|
||||
|
||||
var ganttInfoDiv = $("<div></div>")
|
||||
.addClass("gantt-info")
|
||||
.attr("id", "gantt-info-" + blenderId);
|
||||
|
||||
const scheduleName = $("<span></span>").text("Schedule: " + workSched.Name);
|
||||
|
||||
const createdOn = $("<span></span>")
|
||||
.text("Created: " + new Date(workSched.CreationDate).toLocaleDateString())
|
||||
.css("float", "right");
|
||||
|
||||
const ganttDiv = $("<div></div>")
|
||||
.addClass("gantt-chart")
|
||||
.attr("id", "gantt-" + blenderId);
|
||||
|
||||
ganttInfoDiv.append(scheduleName);
|
||||
ganttInfoDiv.append(createdOn);
|
||||
|
||||
workSchedDiv.append(toggleButton);
|
||||
workSchedDiv.append(scheduleTable);
|
||||
|
||||
ganttContainer.append(ganttTitle);
|
||||
ganttContainer.append(workSchedDiv);
|
||||
ganttContainer.append(ganttInfoDiv);
|
||||
ganttContainer.append(ganttDiv);
|
||||
|
||||
$("#container").append(ganttContainer);
|
||||
|
||||
let g = new JSGantt.GanttChart($("#gantt-" + blenderId)[0], "week");
|
||||
g.setOptions({
|
||||
vCaptionType: "Caption", // Set to Show Caption : None,Caption,Resource,Duration,Complete,
|
||||
vQuarterColWidth: 36,
|
||||
vDateTaskDisplayFormat: "day dd month yyyy", // Shown in tool tip box
|
||||
vDayMajorDateDisplayFormat: "mon yyyy - Week ww", // Set format to dates in the "Major" header of the "Day" view
|
||||
vWeekMinorDateDisplayFormat: "dd mon", // Set format to display dates in the "Minor" header of the "Week" view
|
||||
vLang: "en",
|
||||
vShowTaskInfoLink: 1, // Show link in tool tip (0/1)
|
||||
vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily
|
||||
vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data.
|
||||
vFormatArr: ["Day", "Week", "Month", "Quarter"], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
|
||||
vShowRes: true, // Disable the resource column.
|
||||
vShowComp: false, // Disable the completion column.
|
||||
vShowDur: false, // Disable the duration column, because jsgantt doesn't calculate durations the way we want.
|
||||
vAdditionalHeaders: {
|
||||
ifcduration: { title: "Duration" },
|
||||
resourceUsage: { title: "Resource Usage" },
|
||||
},
|
||||
vUseToolTip: true, // Disable tooltips.
|
||||
vTooltipTemplate: generateTooltip,
|
||||
vTotalHeight: 900,
|
||||
|
||||
vEventsChange: {
|
||||
taskname: editValue, // if you need to use the this scope, do: editValue.bind(this)
|
||||
res: editValue,
|
||||
dur: editValue,
|
||||
comp: editValue,
|
||||
start: editValue,
|
||||
end: editValue,
|
||||
planstart: editValue,
|
||||
planend: editValue,
|
||||
cost: editValue,
|
||||
additional_category: editValue,
|
||||
},
|
||||
});
|
||||
JSGantt.addJSONTask(g, tasks);
|
||||
g.setEditable(true);
|
||||
g.Draw();
|
||||
|
||||
connectedClients[blenderId]["gantt"] = g;
|
||||
|
||||
let printButton = $("<button>", {
|
||||
id: "print-btn-" + blenderId,
|
||||
html: "Print",
|
||||
class: "btn no-print",
|
||||
});
|
||||
|
||||
let printOptions = $("<select>", {
|
||||
id: "print-options-" + blenderId,
|
||||
class: "no-print",
|
||||
});
|
||||
|
||||
$.each(pageSizes, function (index, size) {
|
||||
printOptions.append(
|
||||
$("<option>", {
|
||||
value: size.value,
|
||||
text: size.text,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
printButton.on("click", function () {
|
||||
// make it only the corresponding gantt chart is printed
|
||||
$(".gantt-chart").removeClass("no-print");
|
||||
$(".gantt-chart")
|
||||
.not("#gantt-" + blenderId)
|
||||
.addClass("your-css-class");
|
||||
|
||||
var values = $("#print-options-" + blenderId)
|
||||
.val()
|
||||
.split(",");
|
||||
|
||||
g.setEditable(false);
|
||||
g.setTotalHeight("");
|
||||
g.Draw();
|
||||
|
||||
addEventListener("afterprint", (event) => {
|
||||
g.setEditable(true);
|
||||
},);
|
||||
|
||||
|
||||
let css =
|
||||
"@media print {\n" +
|
||||
" @page {\n" +
|
||||
" size: " +
|
||||
values[0] +
|
||||
"mm " +
|
||||
values[1] +
|
||||
"mm;\n" +
|
||||
" }\n" +
|
||||
" /* Make all text black */\n" +
|
||||
" body, p, span, h1, h2, h3, h4, h5, h6, div, a, li, td, th, * {\n" +
|
||||
" color: black !important;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
g.printChart(values[0], values[1], css);
|
||||
g.setTotalHeight(900);
|
||||
g.Draw();
|
||||
});
|
||||
|
||||
ganttContainer.append(printOptions);
|
||||
ganttContainer.append(printButton);
|
||||
}
|
||||
|
||||
// Function to update gantt and filename
|
||||
function updateGanttElement(blenderId, tasks, workSched, filename) {
|
||||
// update work schedule table
|
||||
const table = $("#workSchedTable-" + blenderId);
|
||||
table.empty();
|
||||
$.each(workSched, (key, value) => {
|
||||
value = value ? value : "null";
|
||||
$("<tr></tr>")
|
||||
.append($("<td></td>").text(key))
|
||||
.append($("<td></td>").text(value))
|
||||
.appendTo(table);
|
||||
});
|
||||
|
||||
// update gantt chart with new data
|
||||
let g = connectedClients[blenderId]["gantt"];
|
||||
g.ClearTasks();
|
||||
g.Draw();
|
||||
JSGantt.addJSONTask(g, tasks);
|
||||
g.Draw();
|
||||
|
||||
$("#title-" + blenderId).text(filename);
|
||||
}
|
||||
|
||||
// Function to remove gantt element
|
||||
function removeGanttElement(blenderId) {
|
||||
$("#container-" + blenderId).remove();
|
||||
}
|
||||
|
||||
// Utility function to create a tooltip for the gantt chars
|
||||
function generateTooltip(task) {
|
||||
var dataObject = task.getDataObject();
|
||||
var numberResources = dataObject.resourceUsage
|
||||
? dataObject.resourceUsage
|
||||
: "NULL";
|
||||
return `
|
||||
<dl>
|
||||
<dt>Name:</dt><dd>{{pName}}</dd>
|
||||
<dt>Start:</dt><dd>{{pStart}}</dd>
|
||||
<dt>End:</dt><dd>{{pEnd}}</dd>
|
||||
<dt>Duration:</dt><dd>${dataObject.ifcduration}</dd>
|
||||
<dt>Number of Resources:</dt><dd>${numberResources}</dd>
|
||||
<dt>Resources:</dt><dd>{{pRes}}</dd>
|
||||
</dl>
|
||||
`;
|
||||
}
|
||||
|
||||
// Event handlers for editing gantt table data
|
||||
function editValue(list, task, event, cell, column) {
|
||||
console.log("editValue function called with the following parameters:");
|
||||
console.log("list:", list);
|
||||
console.log("task:", task);
|
||||
console.log("event:", event);
|
||||
console.log("cell:", cell);
|
||||
console.log("column:", column);
|
||||
|
||||
const ganttId = task.getGantt()["vDiv"].id;
|
||||
const index = ganttId.indexOf("-") + 1;
|
||||
const blenderId = ganttId.substring(index);
|
||||
const workSchedId = connectedClients[blenderId].workSchedule.id;
|
||||
|
||||
// update data object reprsenting the task
|
||||
const dataObj = task.getDataObject();
|
||||
dataObj[column] = event.target.value;
|
||||
task.setDataObject(dataObj);
|
||||
|
||||
const msg = {
|
||||
sourcePage: "gantt",
|
||||
blenderId: blenderId,
|
||||
operator: {
|
||||
type: "editTask",
|
||||
workScheduleId: workSchedId,
|
||||
taskId: task.getOriginalID(),
|
||||
column: column,
|
||||
value: event.target.value,
|
||||
},
|
||||
};
|
||||
socket.emit("web_operator", msg);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// keeps track of blenders connected in form of
|
||||
// {shown:bool, filename:string, headers:array}
|
||||
const connectedClients = {};
|
||||
let socket;
|
||||
|
||||
// Document ready function
|
||||
$(document).ready(function () {
|
||||
connectSocket();
|
||||
});
|
||||
|
||||
// Function to connect to Socket.IO server
|
||||
function connectSocket() {
|
||||
const url = "ws://localhost:" + SOCKET_PORT + "/web";
|
||||
socket = io(url);
|
||||
console.log("socket: ", socket);
|
||||
|
||||
// Register socket event handlers
|
||||
socket.on("blender_connect", handleBlenderConnect);
|
||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||
socket.on("csv_data", handleCsvData);
|
||||
}
|
||||
|
||||
// Function to handle 'blender_connect' event
|
||||
function handleBlenderConnect(blenderId) {
|
||||
console.log("blender_connect: ", blenderId);
|
||||
if (!connectedClients.hasOwnProperty(blenderId)) {
|
||||
connectedClients[blenderId] = { shown: false, ifc_file: "" };
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle 'blender_disconnect' event
|
||||
function handleBlenderDisconnect(blenderId) {
|
||||
console.log("blender_disconnect: ", blenderId);
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
delete connectedClients[blenderId];
|
||||
removeTableElement(blenderId);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle 'csv_data' event
|
||||
function handleCsvData(data) {
|
||||
const blenderId = data["blenderId"];
|
||||
const csvData = data["data"]["csv_data"];
|
||||
const filename = data["data"]["IFC_File"];
|
||||
console.log(data);
|
||||
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
if (!connectedClients[blenderId].shown) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
headers: [],
|
||||
};
|
||||
addTableElement(blenderId, csvData, filename);
|
||||
} else {
|
||||
updateTableElement(blenderId, csvData, filename);
|
||||
}
|
||||
} else {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
headers: [],
|
||||
};
|
||||
addTableElement(blenderId, csvData, filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a new table with data and filename
|
||||
function addTableElement(blenderId, csvData, filename) {
|
||||
// store headers of the csv data
|
||||
const firstLine = csvData.indexOf("\n");
|
||||
const csvHeaders = csvData.substring(0, firstLine).split(",");
|
||||
connectedClients[blenderId].headers = csvHeaders;
|
||||
|
||||
console.log(connectedClients[blenderId]);
|
||||
const tableContainer = $("<div></div>")
|
||||
.addClass("table-container")
|
||||
.attr("id", "container-" + blenderId);
|
||||
|
||||
const tableTitle = $("<h3></h3>")
|
||||
.attr("id", "title-" + blenderId)
|
||||
.text(filename)
|
||||
.css("margin-bottom", "10px");
|
||||
|
||||
const tableDiv = $("<div></div>")
|
||||
.addClass("csv-table")
|
||||
.attr("id", "table-" + blenderId);
|
||||
|
||||
tableContainer.append(tableTitle);
|
||||
tableContainer.append(tableDiv);
|
||||
$("#container").append(tableContainer);
|
||||
|
||||
function createHeaderMenu(definitions) {
|
||||
var menu = [];
|
||||
|
||||
for (let column of definitions) {
|
||||
// Create text element for checkbox
|
||||
if (!column.hasOwnProperty("key")) {
|
||||
column.visible = true;
|
||||
}
|
||||
let checkbox = document.createElement("span");
|
||||
checkbox.textContent = column.visible ? "\u2611" : "\u2610"; // ☑ or ☐
|
||||
|
||||
// Build label
|
||||
let label = document.createElement("span");
|
||||
let title = document.createElement("span");
|
||||
|
||||
title.textContent = " " + column.title;
|
||||
|
||||
label.appendChild(checkbox);
|
||||
label.appendChild(title);
|
||||
|
||||
// Create menu item
|
||||
menu.push({
|
||||
label: label,
|
||||
action: function (e) {
|
||||
// Prevent menu closing
|
||||
e.stopPropagation();
|
||||
|
||||
// Toggle current column visibility
|
||||
column.visible = !column.visible;
|
||||
console.log(column.title, column.visible);
|
||||
// Change menu item checkbox
|
||||
if (column.visible) {
|
||||
table.showColumn(column.title);
|
||||
checkbox.textContent = "\u2611"; // ☑
|
||||
} else {
|
||||
table.hideColumn(column.title);
|
||||
checkbox.textContent = "\u2610"; // ☐
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
var table = new Tabulator("#table-" + blenderId, {
|
||||
height: "400px",
|
||||
resizableColumnGuide: true,
|
||||
index: "GlobalId",
|
||||
data: csvData,
|
||||
importFormat: "csv",
|
||||
autoColumns: true,
|
||||
selectableRows: 1,
|
||||
layout: "fitColumns",
|
||||
autoColumnsDefinitions: function (definitions) {
|
||||
menu = createHeaderMenu(definitions, table);
|
||||
definitions.forEach((column) => {
|
||||
column.visible = true;
|
||||
column.headerMenu = menu;
|
||||
});
|
||||
return definitions;
|
||||
},
|
||||
});
|
||||
|
||||
table.on("rowSelected", function (row) {
|
||||
var index = row.getIndex(); // the guid of the object
|
||||
|
||||
if (!index) {
|
||||
console.log("No Global ID found");
|
||||
return;
|
||||
}
|
||||
|
||||
// table id is usually "table-BlenderId" so blender id is always
|
||||
// after the first 6 characters
|
||||
var tableId = row.getTable().element.id.substr(6);
|
||||
|
||||
const msg = {
|
||||
sourcePage: "csv",
|
||||
blenderId: tableId,
|
||||
operator: {
|
||||
type: "selection",
|
||||
globalId: index,
|
||||
},
|
||||
};
|
||||
socket.emit("web_operator", msg);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to update table and filename
|
||||
function updateTableElement(blenderId, csvData, filename) {
|
||||
// check if the headers are the same
|
||||
// if yes, just replace the data
|
||||
// else, replace the whole data and column definations
|
||||
const firstLine = csvData.indexOf("\n");
|
||||
const newHeaders = csvData.substring(0, firstLine).split(",");
|
||||
const sameHeaders = compareHeaders(
|
||||
connectedClients[blenderId].headers,
|
||||
newHeaders
|
||||
);
|
||||
|
||||
const table = Tabulator.findTable("#table-" + blenderId)[0];
|
||||
if (table) {
|
||||
if (sameHeaders) {
|
||||
table.replaceData(csvData, { importFormat: "csv" });
|
||||
} else {
|
||||
table.setData(csvData);
|
||||
connectedClients[blenderId].headers = newHeaders;
|
||||
}
|
||||
$("#title-" + blenderId).text(filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to remove table element
|
||||
function removeTableElement(blenderId) {
|
||||
$("#container-" + blenderId).remove();
|
||||
}
|
||||
|
||||
// Utility function to compare two csv header
|
||||
function compareHeaders(headers1, headers2) {
|
||||
if (headers1.length !== headers2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < headers1.length; i++) {
|
||||
if (headers1[i] !== headers2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Web Client</title>
|
||||
<link rel="stylesheet" type="text/css" href="/jsgantt/jsgantt.css" />
|
||||
<link rel="stylesheet" href="/static/css/gantt.css" />
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.socket.io/4.0.0/socket.io.min.js"
|
||||
></script>
|
||||
<script type="text/javascript" src="./jsgantt/jsgantt.js"></script>
|
||||
<script>
|
||||
var SOCKET_PORT = {{port}};
|
||||
</script>
|
||||
<script defer src="./static/js/gantt.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="no-print">
|
||||
<img src="https://blenderbim.org/assets/images/blender/blender-logo.png" alt="Logo" class="logo" />
|
||||
<ul>
|
||||
<li><a href="/">IFC Data</a></li>
|
||||
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
||||
</ul>
|
||||
<div class="bottom-bar"></div>
|
||||
</nav>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Web Client</title>
|
||||
<link rel="stylesheet" href="/static/css/index.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css"
|
||||
/>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.socket.io/4.0.0/socket.io.min.js"
|
||||
></script>
|
||||
<script>
|
||||
var SOCKET_PORT = {{port}};
|
||||
</script>
|
||||
<script defer src="./static/js/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<img src="https://blenderbim.org/assets/images/blender/blender-logo.png" alt="Logo" class="logo" />
|
||||
<ul>
|
||||
<li><a href="/" class="active">IFC Data</a></li>
|
||||
<li><a href="/gantt">Gantt Chart</a></li>
|
||||
</ul>
|
||||
<div class="bottom-bar"></div>
|
||||
</nav>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -89,7 +89,7 @@ class IfcExporter:
|
||||
self.get_application_name(), tool.Blender.get_blenderbim_version()
|
||||
)
|
||||
|
||||
def sync_all_objects(self, skip_unlinking=False) -> list[ifcopenshell.entity_instance]:
|
||||
def sync_all_objects(self) -> list[ifcopenshell.entity_instance]:
|
||||
results: list[ifcopenshell.entity_instance] = []
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
for ifc_definition_id in list(IfcStore.id_map.keys()):
|
||||
@@ -118,8 +118,8 @@ class IfcExporter:
|
||||
continue
|
||||
try:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
if tool.Ifc.has_changed_shading(obj):
|
||||
blenderbim.core.style.update_style_colours(tool.Ifc, tool.Style, obj=obj)
|
||||
# TODO: do we add materials to edited_objs?
|
||||
continue
|
||||
else:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -137,9 +137,9 @@ class IfcExporter:
|
||||
return
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
def has_changed_materials(self, obj):
|
||||
def has_changed_materials(self, obj: bpy.types.Object) -> bool:
|
||||
checksum = obj.data.BIMMeshProperties.material_checksum
|
||||
return checksum != str([s.id() for s in tool.Geometry.get_styles(obj) if s])
|
||||
return checksum != tool.Geometry.get_material_checksum(obj)
|
||||
|
||||
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
@@ -20,6 +20,8 @@ import os
|
||||
import bpy
|
||||
import json
|
||||
import addon_utils
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.api.owner.settings
|
||||
import blenderbim.bim
|
||||
import blenderbim.tool as tool
|
||||
@@ -51,10 +53,8 @@ def name_callback(obj, data):
|
||||
return
|
||||
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
if obj.BIMObjectProperties.ifc_definition_id:
|
||||
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
|
||||
if ifc_definition_id := obj.BIMStyleProperties.ifc_definition_id:
|
||||
IfcStore.get_file().by_id(ifc_definition_id).Name = obj.name
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
@@ -86,11 +86,6 @@ def name_callback(obj, data):
|
||||
refresh_ui_data()
|
||||
|
||||
|
||||
def color_callback(obj, data):
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
|
||||
def active_object_callback():
|
||||
refresh_ui_data()
|
||||
update_bim_tool_props()
|
||||
@@ -193,6 +188,8 @@ def refresh_ui_data():
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = bpy.context.scene.DocProperties.should_draw_decorations
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
tool.Web.send_webui_data()
|
||||
|
||||
|
||||
@persistent
|
||||
@@ -296,12 +293,13 @@ def load_post(scene):
|
||||
ifcopenshell.api.owner.settings.get_application = get_application
|
||||
AuthoringData.type_thumbnails = {}
|
||||
|
||||
if not bpy.context.preferences.addons["blenderbim"].preferences.should_setup_toolbar:
|
||||
preferences = tool.Blender.get_addon_preferences()
|
||||
if not preferences.should_setup_toolbar:
|
||||
tool.Blender.unregister_toolbar()
|
||||
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
|
||||
if preferences.should_setup_workspace:
|
||||
if "BIM" in bpy.data.workspaces:
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.activate_workspace:
|
||||
if preferences.activate_workspace:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
else:
|
||||
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import bpy
|
||||
import json
|
||||
import math
|
||||
import zipfile
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.element
|
||||
@@ -322,50 +321,3 @@ def draw_filter(layout, filter_groups, data, module):
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
op.module = module
|
||||
|
||||
|
||||
# TODO this should move into ifcopenshell.util
|
||||
|
||||
|
||||
class IfcHeaderExtractor:
|
||||
def __init__(self, filepath: str):
|
||||
self.filepath = filepath
|
||||
|
||||
def extract(self):
|
||||
extension = self.filepath.split(".")[-1]
|
||||
if extension.lower() == "ifc":
|
||||
with open(self.filepath) as ifc_file:
|
||||
return self.extract_ifc_spf(ifc_file)
|
||||
elif extension.lower() == "ifczip":
|
||||
return self.extract_ifc_zip()
|
||||
elif extension.lower() == "ifcsqlite":
|
||||
return {} # TODO
|
||||
|
||||
def extract_ifc_spf(self, ifc_file):
|
||||
# https://www.steptools.com/stds/step/IS_final_p21e3.html#clause-8
|
||||
data = {}
|
||||
max_lines_to_parse = 50
|
||||
for _ in range(max_lines_to_parse):
|
||||
line = next(ifc_file)
|
||||
if isinstance(line, bytes):
|
||||
line = line.decode("utf-8")
|
||||
if line.startswith("FILE_DESCRIPTION"):
|
||||
for i, part in enumerate(line.split("'")):
|
||||
if i == 1:
|
||||
data["description"] = part
|
||||
elif i == 3:
|
||||
data["implementation_level"] = part
|
||||
elif line.startswith("FILE_NAME"):
|
||||
for i, part in enumerate(line.split("'")):
|
||||
if i == 1:
|
||||
data["name"] = part
|
||||
elif i == 3:
|
||||
data["time_stamp"] = part
|
||||
elif line.startswith("FILE_SCHEMA"):
|
||||
data["schema_name"] = line.split("'")[1]
|
||||
break
|
||||
return data
|
||||
|
||||
def extract_ifc_zip(self):
|
||||
archive = zipfile.ZipFile(self.filepath, "r")
|
||||
return self.extract_ifc_spf(archive.open(archive.filelist[0]))
|
||||
|
||||
@@ -32,12 +32,29 @@ 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
|
||||
from typing import Set, Union, Optional, TypedDict, Callable
|
||||
|
||||
|
||||
IFC_CONNECTED_TYPE = Union[bpy.types.Material, bpy.types.Object]
|
||||
|
||||
|
||||
class OperationData(TypedDict):
|
||||
id: int
|
||||
guid: Union[str, None]
|
||||
obj: str
|
||||
|
||||
|
||||
class Operation(TypedDict):
|
||||
rollback: Callable
|
||||
commit: Callable
|
||||
data: OperationData
|
||||
|
||||
|
||||
class TransactionStep(TypedDict):
|
||||
key: str
|
||||
operations: list[Operation]
|
||||
|
||||
|
||||
class IfcStore:
|
||||
path: str = ""
|
||||
file: Optional[ifcopenshell.file] = None
|
||||
@@ -55,9 +72,9 @@ class IfcStore:
|
||||
library_file: Optional[ifcopenshell.file] = None
|
||||
current_transaction = ""
|
||||
last_transaction = ""
|
||||
history = []
|
||||
future = []
|
||||
schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3"]
|
||||
history: list[TransactionStep] = []
|
||||
future: list[TransactionStep] = []
|
||||
schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3_ADD2"]
|
||||
session_files: dict[str, ifcopenshell.file] = {}
|
||||
|
||||
@staticmethod
|
||||
@@ -77,7 +94,7 @@ class IfcStore:
|
||||
IfcStore.last_transaction = ""
|
||||
IfcStore.history = []
|
||||
IfcStore.future = []
|
||||
IfcStore.schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3"]
|
||||
IfcStore.schema_identifiers = ["IFC4", "IFC2X3", "IFC4X3_ADD2"]
|
||||
IfcStore.session_files = {}
|
||||
|
||||
@staticmethod
|
||||
@@ -150,7 +167,7 @@ class IfcStore:
|
||||
IfcStore.file = ifcopenshell.open(path)
|
||||
|
||||
@staticmethod
|
||||
def get_schema():
|
||||
def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
|
||||
if IfcStore.file is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
|
||||
bpy.context.scene.BIMProjectProperties.export_schema
|
||||
@@ -162,14 +179,13 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_element(id_or_guid: Union[int, str]) -> IFC_CONNECTED_TYPE:
|
||||
if isinstance(id_or_guid, int):
|
||||
map_object = IfcStore.id_map
|
||||
obj = IfcStore.id_map.get(id_or_guid)
|
||||
else:
|
||||
map_object = IfcStore.guid_map
|
||||
try:
|
||||
obj = map_object[id_or_guid]
|
||||
obj.name # In case the object has been deleted, this triggers an exception
|
||||
except:
|
||||
return
|
||||
obj = IfcStore.guid_map.get(id_or_guid)
|
||||
if obj is None:
|
||||
return None
|
||||
if not tool.Blender.is_valid_data_block(obj):
|
||||
return None
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
@@ -189,21 +205,26 @@ class IfcStore:
|
||||
def relink_object(obj: IFC_CONNECTED_TYPE) -> None:
|
||||
if not obj:
|
||||
return
|
||||
if obj.BIMObjectProperties.ifc_definition_id:
|
||||
try:
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
except:
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id
|
||||
if not ifc_definition_id:
|
||||
return
|
||||
data = {"id": element.id(), "obj": obj.name}
|
||||
try:
|
||||
element = IfcStore.get_file().by_id(ifc_definition_id)
|
||||
except RuntimeError:
|
||||
return
|
||||
data = OperationData(id=element.id(), obj=obj.name)
|
||||
if hasattr(element, "GlobalId"):
|
||||
data["guid"] = element.GlobalId
|
||||
IfcStore.commit_link_element(data)
|
||||
if hasattr(obj, "BIMMaterialProperties") and obj.BIMMaterialProperties.ifc_style_id:
|
||||
return
|
||||
else: # bpy.types.Material
|
||||
ifc_definition_id = obj.BIMStyleProperties.ifc_definition_id
|
||||
try:
|
||||
element = IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id)
|
||||
except:
|
||||
element = IfcStore.get_file().by_id(ifc_definition_id)
|
||||
except RuntimeError:
|
||||
return
|
||||
data = {"id": element.id(), "obj": obj.name}
|
||||
data = OperationData(id=element.id(), obj=obj.name)
|
||||
IfcStore.commit_link_element(data)
|
||||
|
||||
@staticmethod
|
||||
@@ -218,44 +239,40 @@ class IfcStore:
|
||||
existing_obj = IfcStore.id_map.get(element.id(), None)
|
||||
if existing_obj == obj:
|
||||
return
|
||||
elif existing_obj:
|
||||
try:
|
||||
existing_obj.name
|
||||
IfcStore.unlink_element(element=element, obj=existing_obj)
|
||||
except:
|
||||
pass
|
||||
# TODO: When does this occur?
|
||||
elif existing_obj and tool.Blender.is_valid_data_block(existing_obj):
|
||||
IfcStore.unlink_element(obj=existing_obj)
|
||||
|
||||
IfcStore.id_map[element.id()] = obj
|
||||
if hasattr(element, "GlobalId"):
|
||||
IfcStore.guid_map[element.GlobalId] = obj
|
||||
|
||||
if element.is_a("IfcSurfaceStyle"):
|
||||
obj.BIMMaterialProperties.ifc_style_id = element.id()
|
||||
obj.BIMStyleProperties.ifc_definition_id = element.id()
|
||||
else:
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
|
||||
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
|
||||
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
|
||||
if IfcStore.history:
|
||||
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
|
||||
data = OperationData(id=element.id(), guid=getattr(element, "GlobalId", None), obj=obj.name)
|
||||
IfcStore.history[-1]["operations"].append(
|
||||
{"rollback": IfcStore.rollback_link_element, "commit": IfcStore.commit_link_element, "data": data}
|
||||
Operation(rollback=IfcStore.rollback_link_element, commit=IfcStore.commit_link_element, data=data)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def rollback_link_element(data):
|
||||
def rollback_link_element(data: OperationData) -> None:
|
||||
del IfcStore.id_map[data["id"]]
|
||||
if data["guid"]:
|
||||
del IfcStore.guid_map[data["guid"]]
|
||||
|
||||
@staticmethod
|
||||
def commit_link_element(data):
|
||||
def commit_link_element(data: OperationData) -> None:
|
||||
obj = bpy.data.objects.get(data["obj"])
|
||||
if not obj:
|
||||
obj = bpy.data.materials.get(data["obj"])
|
||||
@@ -263,9 +280,7 @@ class IfcStore:
|
||||
if "guid" in data:
|
||||
IfcStore.guid_map[data["guid"]] = obj
|
||||
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
@@ -273,7 +288,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: OperationData) -> None:
|
||||
if "id" not in data or "obj" not in data:
|
||||
return
|
||||
obj = bpy.data.objects.get(data["obj"])
|
||||
@@ -282,7 +297,7 @@ class IfcStore:
|
||||
IfcStore.guid_map[data["guid"]] = obj
|
||||
|
||||
@staticmethod
|
||||
def commit_unlink_element(data) -> None:
|
||||
def commit_unlink_element(data: OperationData) -> None:
|
||||
del IfcStore.id_map[data["id"]]
|
||||
if data["guid"]:
|
||||
del IfcStore.guid_map[data["guid"]]
|
||||
@@ -291,50 +306,62 @@ class IfcStore:
|
||||
def unlink_element(
|
||||
element: Optional[ifcopenshell.entity_instance] = None, obj: Optional[IFC_CONNECTED_TYPE] = None
|
||||
) -> None:
|
||||
if element is None:
|
||||
try:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
except:
|
||||
pass
|
||||
"""Unlink IFC `element` or Blender `obj`.
|
||||
|
||||
If element is provided then it will be unlinked from the related Blender object.
|
||||
Blender object's IFC information is also will be purged.
|
||||
|
||||
If Blender object is provided then all IFC information will be purged from this object.
|
||||
Method won't be searching for related IFC elements as Blender object might come from
|
||||
different Blender session and there might be an ids/guids clash.
|
||||
|
||||
Only one argument must be provided and other should be omitted as they have different meaning.
|
||||
:raises TypeError: If both arguments or no arguments provided.
|
||||
|
||||
"""
|
||||
if not bool(element) ^ bool(obj):
|
||||
raise TypeError("Only one argument must be provided - element or obj.")
|
||||
|
||||
if obj:
|
||||
IfcStore.purge_blender_ifc_data(obj)
|
||||
return
|
||||
|
||||
assert element # Type checker.
|
||||
if obj is None:
|
||||
try:
|
||||
potential_obj = IfcStore.id_map[element.id()]
|
||||
potential_obj.name
|
||||
obj = potential_obj
|
||||
if tool.Blender.is_valid_data_block(potential_obj):
|
||||
obj = potential_obj
|
||||
IfcStore.purge_blender_ifc_data(obj)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if element:
|
||||
del IfcStore.id_map[element.id()]
|
||||
else:
|
||||
del IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id]
|
||||
del IfcStore.id_map[element.id()]
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if element and hasattr(element, "GlobalId"):
|
||||
del IfcStore.guid_map[element.GlobalId]
|
||||
except:
|
||||
pass
|
||||
|
||||
if element and element.is_a("IfcSurfaceStyle"):
|
||||
obj.BIMMaterialProperties.ifc_style_id = 0
|
||||
elif obj:
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
if global_id := getattr(element, "GlobalId", None):
|
||||
try:
|
||||
del IfcStore.guid_map[global_id]
|
||||
except:
|
||||
pass
|
||||
|
||||
if IfcStore.history:
|
||||
data = {}
|
||||
if element:
|
||||
data["id"] = element.id()
|
||||
data["guid"] = getattr(element, "GlobalId", None)
|
||||
data = OperationData(id=element.id(), guid=global_id)
|
||||
if obj:
|
||||
data["obj"] = obj.name
|
||||
IfcStore.history[-1]["operations"].append(
|
||||
{"rollback": IfcStore.rollback_unlink_element, "commit": IfcStore.commit_unlink_element, "data": data}
|
||||
Operation(rollback=IfcStore.rollback_unlink_element, commit=IfcStore.commit_unlink_element, data=data)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def purge_blender_ifc_data(obj: IFC_CONNECTED_TYPE) -> None:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
obj.BIMStyleProperties.ifc_definition_id = 0
|
||||
else: # bpy.types.Object
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
|
||||
@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})
|
||||
@@ -393,15 +420,15 @@ class IfcStore:
|
||||
rollback = rollback or getattr(operator, "rollback", lambda data: True)
|
||||
commit = commit or getattr(operator, "commit", lambda data: True)
|
||||
if IfcStore.history and IfcStore.history[-1]["key"] == key:
|
||||
IfcStore.history[-1]["operations"].append({"rollback": rollback, "commit": commit, "data": data})
|
||||
IfcStore.history[-1]["operations"].append(OperationData(rollback=rollback, commit=commit, data=data))
|
||||
else:
|
||||
IfcStore.history.append(
|
||||
{"key": key, "operations": [{"rollback": rollback, "commit": commit, "data": data}]}
|
||||
TransactionStep(key=key, operations=[OperationData(rollback=rollback, commit=commit, data=data)])
|
||||
)
|
||||
IfcStore.future = []
|
||||
|
||||
@staticmethod
|
||||
def undo(until_key=None) -> None:
|
||||
def undo(until_key: str = None) -> None:
|
||||
BrickStore.undo()
|
||||
if not IfcStore.history:
|
||||
return
|
||||
@@ -416,7 +443,7 @@ class IfcStore:
|
||||
IfcStore.future.append(event)
|
||||
|
||||
@staticmethod
|
||||
def redo(until_key=None) -> None:
|
||||
def redo(until_key: str = None) -> None:
|
||||
BrickStore.redo()
|
||||
|
||||
if not IfcStore.future:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,14 +26,7 @@ import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
|
||||
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"""Create aggregation relationship between two ifc elements"""
|
||||
|
||||
bl_idname = "bim.aggregate_assign_object"
|
||||
@@ -75,7 +68,7 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
|
||||
self.report({"ERROR"}, f"Cannot aggregate to {relating_obj.name} with a body representation")
|
||||
|
||||
|
||||
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"""Remove aggregation relationship between two ifc elements"""
|
||||
|
||||
bl_idname = "bim.aggregate_unassign_object"
|
||||
@@ -99,14 +92,14 @@ class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
# Removes Pset related to Linked Aggregates
|
||||
if not element.is_a('IfcElementAssembly'):
|
||||
pset = ifcopenshell.util.element.get_pset(element, 'BBIM_Linked_Aggregate')
|
||||
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):
|
||||
|
||||
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"""Enable editing aggregation relationship"""
|
||||
|
||||
bl_idname = "bim.enable_editing_aggregate"
|
||||
@@ -117,7 +110,7 @@ class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
|
||||
core.enable_editing_aggregate(tool.Aggregate, obj=context.active_object)
|
||||
|
||||
|
||||
class BIM_OT_disable_editing_aggregate(bpy.types.Operator, Operator):
|
||||
class BIM_OT_disable_editing_aggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"""Disable editing aggregation relationship"""
|
||||
|
||||
bl_idname = "bim.disable_editing_aggregate"
|
||||
@@ -232,7 +225,7 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
|
||||
# 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)
|
||||
@@ -245,7 +238,7 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
|
||||
pass
|
||||
if not element:
|
||||
obj.select_set(False)
|
||||
|
||||
|
||||
if self.select_parts:
|
||||
all_objs = []
|
||||
for part in all_parts:
|
||||
@@ -263,12 +256,12 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
|
||||
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
|
||||
bpy.context.view_layer.objects.active = aggregate_obj
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
|
||||
class BIM_OT_add_part_to_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_part_to_object"
|
||||
bl_label = "Add Part to Object"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -293,7 +286,7 @@ class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
|
||||
|
||||
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
|
||||
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.break_link_to_other_aggregates"
|
||||
bl_label = "Break link to other aggregates"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -324,7 +317,7 @@ class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_select_linked_aggregates(bpy.types.Operator, Operator):
|
||||
class BIM_OT_select_linked_aggregates(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_linked_aggregates"
|
||||
bl_label = "Select linked aggregates"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -338,7 +331,7 @@ class BIM_OT_select_linked_aggregates(bpy.types.Operator, Operator):
|
||||
return "Select all aggregates"
|
||||
|
||||
def _execute(self, context):
|
||||
|
||||
|
||||
for obj in context.selected_objects:
|
||||
obj.select_set(False)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
@@ -28,13 +28,6 @@ import blenderbim.core.attribute as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingAttributes(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_attributes"
|
||||
bl_label = "Enable Editing Attributes"
|
||||
@@ -89,7 +82,7 @@ class DisableEditingAttributes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditAttributes(bpy.types.Operator, Operator):
|
||||
class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_attributes"
|
||||
bl_label = "Edit Attributes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -118,7 +111,7 @@ class EditAttributes(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GenerateGlobalId(bpy.types.Operator, Operator):
|
||||
class GenerateGlobalId(bpy.types.Operator, tool.Ifc.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"
|
||||
@@ -161,7 +154,7 @@ class GenerateGlobalId(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CopyAttributeToSelection(bpy.types.Operator, Operator):
|
||||
class CopyAttributeToSelection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.copy_attribute_to_selection"
|
||||
bl_label = "Copy Attribute To Selection"
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
@@ -949,9 +949,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
matrix,
|
||||
float(props.blender_eastings) * unit_scale,
|
||||
float(props.blender_northings) * unit_scale,
|
||||
float(props.blender_orthogonal_height) * unit_scale,
|
||||
float(props.blender_offset_x) * unit_scale,
|
||||
float(props.blender_offset_y) * unit_scale,
|
||||
float(props.blender_offset_z) * unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
|
||||
@@ -49,7 +49,7 @@ class BoundaryDecorator:
|
||||
batch.draw(shader)
|
||||
|
||||
def __call__(self, context):
|
||||
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||
self.addon_prefs = tool.Blender.get_addon_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
|
||||
|
||||
@@ -551,9 +551,9 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
||||
for boundary in element.BoundedBy or []:
|
||||
boundary_obj = tool.Ifc.get_object(boundary)
|
||||
if boundary_obj:
|
||||
to_delete.add(boundary_obj)
|
||||
for boundary_obj in to_delete:
|
||||
tool.Ifc.unlink(obj=boundary_obj)
|
||||
to_delete.add((boundary, boundary_obj))
|
||||
for boundary, boundary_obj in to_delete:
|
||||
tool.Ifc.unlink(element=boundary)
|
||||
bpy.data.objects.remove(boundary_obj)
|
||||
context.scene.BIMBoundaryProperties.boundaries.clear()
|
||||
return {"FINISHED"}
|
||||
@@ -602,6 +602,11 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
parent_boundaries = []
|
||||
|
||||
objs = context.selected_objects
|
||||
|
||||
if not any((element:=tool.Ifc.get_entity(obj)) and element.is_a("IfcSpace") for obj in objs):
|
||||
self.report({"INFO"}, "No IfcSpace elements selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
if len(objs) == 2:
|
||||
# The user may select two objects, a space and its related building element
|
||||
for obj in objs:
|
||||
|
||||
@@ -26,14 +26,7 @@ from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.tool.brick import BrickStore
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadBrickProject(bpy.types.Operator, Operator):
|
||||
class LoadBrickProject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_brick_project"
|
||||
bl_label = "Load Brickschema Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -46,14 +39,14 @@ class LoadBrickProject(bpy.types.Operator, Operator):
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.load_brick_project(tool.Brick, filepath=self.filepath, brick_root=root)
|
||||
else:
|
||||
self.report({'ERROR'}, f'Failed to load {self.filepath}')
|
||||
self.report({"ERROR"}, f"Failed to load {self.filepath}")
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class ViewBrickClass(bpy.types.Operator, Operator):
|
||||
class ViewBrickClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.view_brick_class"
|
||||
bl_label = "View Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -65,7 +58,7 @@ class ViewBrickClass(bpy.types.Operator, Operator):
|
||||
core.view_brick_class(tool.Brick, brick_class=self.brick_class, split_screen=self.split_screen)
|
||||
|
||||
|
||||
class ViewBrickItem(bpy.types.Operator, Operator):
|
||||
class ViewBrickItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.view_brick_item"
|
||||
bl_label = "View Brick Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -77,10 +70,10 @@ class ViewBrickItem(bpy.types.Operator, Operator):
|
||||
try:
|
||||
core.view_brick_item(tool.Brick, item=self.item, split_screen=self.split_screen)
|
||||
except:
|
||||
self.report({'ERROR'}, f'Could not find {self.item}')
|
||||
self.report({"ERROR"}, f"Could not find {self.item}")
|
||||
|
||||
|
||||
class RewindBrickClass(bpy.types.Operator, Operator):
|
||||
class RewindBrickClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.rewind_brick_class"
|
||||
bl_label = "Rewind Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -91,7 +84,7 @@ class RewindBrickClass(bpy.types.Operator, Operator):
|
||||
core.rewind_brick_class(tool.Brick, split_screen=self.split_screen)
|
||||
|
||||
|
||||
class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
class CloseBrickProject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.close_brick_project"
|
||||
bl_label = "Close Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -101,7 +94,7 @@ class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
core.close_brick_project(tool.Brick)
|
||||
|
||||
|
||||
class ConvertBrickProject(bpy.types.Operator, Operator):
|
||||
class ConvertBrickProject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_brick_project"
|
||||
bl_label = "Convert Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -111,7 +104,7 @@ class ConvertBrickProject(bpy.types.Operator, Operator):
|
||||
core.convert_brick_project(tool.Ifc, tool.Brick)
|
||||
|
||||
|
||||
class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
class AssignBrickReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_brick_reference"
|
||||
bl_label = "Assign Brick Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -119,13 +112,13 @@ class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
if not context.active_object:
|
||||
self.report({'ERROR'}, f'No Ifc selected')
|
||||
self.report({"ERROR"}, f"No Ifc selected")
|
||||
return
|
||||
props = context.scene.BIMBrickProperties
|
||||
try:
|
||||
props.bricks[props.active_brick_index]
|
||||
except:
|
||||
self.report({'ERROR'}, f'No Brick selected')
|
||||
self.report({"ERROR"}, f"No Brick selected")
|
||||
return
|
||||
core.assign_brick_reference(
|
||||
tool.Ifc,
|
||||
@@ -136,7 +129,7 @@ class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddBrick(bpy.types.Operator, Operator):
|
||||
class AddBrick(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_brick"
|
||||
bl_label = "Add Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -155,7 +148,7 @@ class AddBrick(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
class AddBrickRelation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_brick_relation"
|
||||
bl_label = "Add Brick Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -170,15 +163,10 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
object = props.split_screen_bricks[props.split_screen_active_brick_index].uri
|
||||
else:
|
||||
object = props.namespace + props.new_brick_relation_object
|
||||
core.add_brick_relation(
|
||||
tool.Brick,
|
||||
brick_uri=brick.uri,
|
||||
predicate=props.new_brick_relation_type,
|
||||
object=object
|
||||
)
|
||||
core.add_brick_relation(tool.Brick, brick_uri=brick.uri, predicate=props.new_brick_relation_type, object=object)
|
||||
|
||||
|
||||
class ConvertIfcToBrick(bpy.types.Operator, Operator):
|
||||
class ConvertIfcToBrick(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_ifc_to_brick"
|
||||
bl_label = "Convert IFC To Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -225,7 +213,7 @@ class NewBrickFile(bpy.types.Operator):
|
||||
BrickStore.graph = data["graph"]
|
||||
|
||||
|
||||
class RefreshBrickViewer(bpy.types.Operator, Operator):
|
||||
class RefreshBrickViewer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.refresh_brick_viewer"
|
||||
bl_label = "Refresh Brick Viewer"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -235,7 +223,7 @@ class RefreshBrickViewer(bpy.types.Operator, Operator):
|
||||
core.refresh_brick_viewer(tool.Brick)
|
||||
|
||||
|
||||
class RemoveBrick(bpy.types.Operator, Operator):
|
||||
class RemoveBrick(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_brick"
|
||||
bl_label = "Remove Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -279,7 +267,7 @@ class SerializeBrick(bpy.types.Operator):
|
||||
return "Save the Brick project"
|
||||
|
||||
|
||||
class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
class AddBrickNamespace(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_brick_namespace"
|
||||
bl_label = "Add Brick Namespace"
|
||||
bl_description = "Bind a new namespace to the Brick project"
|
||||
@@ -291,7 +279,7 @@ class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
core.add_brick_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class RemoveBrickRelation(bpy.types.Operator, Operator):
|
||||
class RemoveBrickRelation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_brick_relation"
|
||||
bl_label = "Remove Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -302,4 +290,4 @@ class RemoveBrickRelation(bpy.types.Operator, Operator):
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
brick = props.bricks[props.active_brick_index]
|
||||
core.remove_brick_relation(tool.Brick, brick_uri=brick.uri, predicate=self.predicate, object=self.object)
|
||||
core.remove_brick_relation(tool.Brick, brick_uri=brick.uri, predicate=self.predicate, object=self.object)
|
||||
|
||||
@@ -55,7 +55,7 @@ class ClashDecorator:
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_text(self, context):
|
||||
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||
self.addon_prefs = tool.Blender.get_addon_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
|
||||
@@ -75,7 +75,7 @@ class ClashDecorator:
|
||||
blf.draw(font_id, text) # Set your text here
|
||||
|
||||
def draw_geometry(self, context):
|
||||
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||
self.addon_prefs = tool.Blender.get_addon_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
|
||||
|
||||
@@ -24,14 +24,7 @@ import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddContext(bpy.types.Operator, Operator):
|
||||
class AddContext(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_context"
|
||||
bl_label = "Add Subcontext"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -50,7 +43,7 @@ class AddContext(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class RemoveContext(bpy.types.Operator, Operator):
|
||||
class RemoveContext(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_context"
|
||||
bl_label = "Remove Context"
|
||||
bl_description = (
|
||||
@@ -64,7 +57,7 @@ class RemoveContext(bpy.types.Operator, Operator):
|
||||
core.remove_context(tool.Ifc, context=tool.Ifc.get().by_id(self.context))
|
||||
|
||||
|
||||
class EnableEditingContext(bpy.types.Operator, Operator):
|
||||
class EnableEditingContext(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_context"
|
||||
bl_label = "Enable Editing Context"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -74,7 +67,7 @@ class EnableEditingContext(bpy.types.Operator, Operator):
|
||||
core.enable_editing_context(tool.Context, context=tool.Ifc.get().by_id(self.context))
|
||||
|
||||
|
||||
class DisableEditingContext(bpy.types.Operator, Operator):
|
||||
class DisableEditingContext(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_context"
|
||||
bl_label = "Disable Editing Context"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -83,7 +76,7 @@ class DisableEditingContext(bpy.types.Operator, Operator):
|
||||
core.disable_editing_context(tool.Context)
|
||||
|
||||
|
||||
class EditContext(bpy.types.Operator, Operator):
|
||||
class EditContext(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_context"
|
||||
bl_label = "Edit Context"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -28,14 +28,6 @@ from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.bim.handler
|
||||
|
||||
|
||||
# declaring it here to avoid circular import problems
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CoveringTool(WorkSpaceTool):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_context_mode = "OBJECT"
|
||||
@@ -153,7 +145,7 @@ class CoveringToolUI:
|
||||
op.ifc_class = cls.props.ifc_class
|
||||
|
||||
|
||||
class Hotkey(bpy.types.Operator, Operator):
|
||||
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.covering_hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -185,6 +185,8 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = context.scene.CsvProperties
|
||||
if props.format == "web":
|
||||
return self.execute(context)
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, f".{props.format}")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
@@ -222,6 +224,10 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
if attribute.formatting != "{{value}}" and "{{value}}" in attribute.formatting:
|
||||
formatting.append({"name": attribute.name, "format": attribute.formatting})
|
||||
|
||||
file_format = props.format
|
||||
if props.format == "web":
|
||||
file_format = "pd"
|
||||
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
ifc_csv.export(
|
||||
ifc_file,
|
||||
@@ -229,7 +235,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
attributes,
|
||||
headers=headers,
|
||||
output=self.filepath,
|
||||
format=props.format,
|
||||
format=file_format,
|
||||
should_preserve_existing=props.should_preserve_existing,
|
||||
delimiter=sep,
|
||||
include_global_id=props.include_global_id,
|
||||
@@ -247,6 +253,10 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
if props.format != "csv" and props.should_generate_svg:
|
||||
schedule_creator = scheduler.Scheduler()
|
||||
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
|
||||
if props.format == "web":
|
||||
tool.Web.send_webui_data(
|
||||
extra_data=ifc_csv.dataframe.to_csv(index=False), extra_data_key="csv_data", event="csv_data"
|
||||
)
|
||||
self.report({"INFO"}, f"Data is exported to {props.format.upper()}.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class CsvProperties(PropertyGroup):
|
||||
("csv", "csv", ""),
|
||||
("xlsx", "xlsx", ""),
|
||||
("ods", "ods", ""),
|
||||
("web", "web", ""),
|
||||
],
|
||||
name="Output format",
|
||||
default="csv",
|
||||
|
||||
@@ -87,6 +87,11 @@ class BIM_PT_ifccsv(Panel):
|
||||
|
||||
blenderbim.bim.helper.draw_filter(self.layout, props.filter_groups, SearchData, "csv")
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.search", text="Select", icon="VIEWZOOM")
|
||||
op.property_group = "CsvProperties"
|
||||
layout.separator()
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_csv_attribute", icon="ADD")
|
||||
row.prop(props, "should_show_sort", icon="SORTSIZE", text="")
|
||||
@@ -123,3 +128,6 @@ class BIM_PT_ifccsv(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ classes = (
|
||||
operator.SelectExpressFile,
|
||||
operator.SelectHighPolygonMeshes,
|
||||
operator.SelectHighestPolygonMeshes,
|
||||
operator.ToggleDetailedIOSLogs,
|
||||
operator.ValidateIfcFile,
|
||||
prop.BIMDebugProperties,
|
||||
ui.BIM_PT_debug,
|
||||
|
||||
@@ -34,6 +34,7 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.debug as core
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.bim.import_ifc as import_ifc
|
||||
from pathlib import Path
|
||||
from blenderbim import get_debug_info, format_debug_info
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -90,12 +91,11 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type in {"MESH", "EMPTY"}:
|
||||
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
if obj.data:
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
material.BIMObjectProperties.ifc_definition_id = 0
|
||||
material.BIMMaterialProperties.ifc_style_id = 0
|
||||
tool.Ifc.unlink(obj=material)
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
IfcStore.purge()
|
||||
@@ -140,20 +140,30 @@ class ValidateIfcFile(bpy.types.Operator):
|
||||
|
||||
|
||||
class ProfileImportIFC(bpy.types.Operator):
|
||||
profile_filename = "blender.prof"
|
||||
bl_idname = "bim.profile_import_ifc"
|
||||
bl_label = "Profile Import IFC"
|
||||
bl_description = f"Reload currently loaded project and save cprofile stats for reloading to '{profile_filename}'"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.scene.BIMProperties.ifc_file
|
||||
if not tool.Ifc.get():
|
||||
cls.poll_message_set("No IFC file loaded.")
|
||||
return False
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
cls.poll_message_set("Current IFC file is not saved.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
import cProfile
|
||||
import pstats
|
||||
|
||||
cProfile.run("import bpy; bpy.ops.bim.load_project_elements()", "blender.prof")
|
||||
p = pstats.Stats("blender.prof")
|
||||
profile_file = Path(profile_filename)
|
||||
cProfile.run("import bpy; bpy.ops.bim.load_project_elements()", str(profile_file))
|
||||
p = pstats.Stats(str(profile_file))
|
||||
p.sort_stats("cumulative").print_stats(50)
|
||||
self.report({"INFO"}, f'Profile stats are saved to "{profile_file.absolute()}".')
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -172,6 +182,7 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
|
||||
total = len(elements)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("keep-bounding-boxes", True)
|
||||
settings_2d = ifcopenshell.geom.settings()
|
||||
settings_2d.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
failures = []
|
||||
@@ -225,6 +236,7 @@ class CreateShapeFromStepId(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
element = self.file.by_id(self.step_id or int(context.scene.BIMDebugProperties.step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("keep-bounding-boxes", True)
|
||||
if self.should_include_curves:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
@@ -660,7 +672,9 @@ class DebugActiveDrawing(bpy.types.Operator):
|
||||
def drawing_fails_to_load(chunk_to_include: set) -> bool:
|
||||
current_elements = all_elements - chunk_to_include
|
||||
excluded_guids = ", ".join([e.GlobalId for e in current_elements if hasattr(e, "GlobalId")])
|
||||
tool.Ifc.run("pset.edit_pset", pset=pset, properties={"Exclude": f"{original_exclude}, {excluded_guids}"})
|
||||
new_exclude = "" if not original_exclude else f"{original_exclude}, "
|
||||
new_exclude += excluded_guids
|
||||
tool.Ifc.run("pset.edit_pset", pset=pset, properties={"Exclude": new_exclude})
|
||||
|
||||
try:
|
||||
bpy.ops.bim.create_drawing(sync=False)
|
||||
@@ -723,3 +737,36 @@ class DebugActiveDrawing(bpy.types.Operator):
|
||||
|
||||
self.report({"INFO"}, "See system console for the results")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleDetailedIOSLogs(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_detailed_ios_logs"
|
||||
bl_label = "Toggle Detailed IfcOpenShell Logs"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Turn on detailed IfcOpenShell logs in the system console.\n"
|
||||
+ "Could be useful debugging issues "
|
||||
+ "loading IFC representations / serializing IFC geometry."
|
||||
+ "\n\nALT+CLICK to disable the logs"
|
||||
)
|
||||
# NOTE: No idea if it's possible to check from Python
|
||||
# whether detailed logs are currently enabled or not.
|
||||
# Therefore we just distinguish between click
|
||||
# and alt-click to allow enabling/disabling the logs.
|
||||
turn_on_logs: bpy.props.BoolProperty(name="Turn On Logs", default=True, options={"SKIP_SAVE"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
if event.type == "LEFTMOUSE" and event.alt:
|
||||
self.turn_on_logs = False
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
import ifcopenshell.ifcopenshell_wrapper as wrapper
|
||||
|
||||
if self.turn_on_logs:
|
||||
wrapper.turn_on_detailed_logging()
|
||||
self.report({"INFO"}, "Detailed IfcOpenShell logs turned on.")
|
||||
else:
|
||||
wrapper.turn_off_detailed_logging()
|
||||
self.report({"INFO"}, "Detailed IfcOpenShell logs turned off.")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -75,6 +75,9 @@ class BIM_PT_debug(Panel):
|
||||
row = layout.row()
|
||||
row.operator("bim.debug_active_drawing")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.toggle_detailed_ios_logs")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.operator("bim.create_shape_from_step_id").should_include_curves = False
|
||||
row.operator("bim.create_shape_from_step_id", text="", icon="IPO_ELASTIC").should_include_curves = True
|
||||
|
||||
@@ -188,7 +188,6 @@ class ExecuteIfcDiff(bpy.types.Operator):
|
||||
tool.Ifc.set(ifc_diff.old)
|
||||
ifc_importer.file = ifc_diff.old
|
||||
ifc_importer.process_context_filter()
|
||||
ifc_importer.create_materials()
|
||||
ifc_importer.create_styles()
|
||||
|
||||
elements = {ifc_diff.old.by_guid(guid) for guid in ifc_diff.deleted_elements}
|
||||
@@ -204,7 +203,6 @@ class ExecuteIfcDiff(bpy.types.Operator):
|
||||
tool.Ifc.set(ifc_diff.new)
|
||||
ifc_importer.file = ifc_diff.new
|
||||
ifc_importer.process_context_filter()
|
||||
ifc_importer.create_materials()
|
||||
ifc_importer.create_styles()
|
||||
|
||||
elements = {ifc_diff.new.by_guid(guid) for guid in ifc_diff.added_elements}
|
||||
|
||||
@@ -71,6 +71,8 @@ classes = (
|
||||
operator.OpenReference,
|
||||
operator.OpenSchedule,
|
||||
operator.OpenSheet,
|
||||
operator.OrderTextLiteralDown,
|
||||
operator.OrderTextLiteralUp,
|
||||
operator.ReloadDrawingStyles,
|
||||
operator.RemoveDrawing,
|
||||
operator.RemoveDrawingFromSheet,
|
||||
|
||||
@@ -335,7 +335,7 @@ class BaseDecorator:
|
||||
"""`verts` should be in winspace with `(0,0,0)` in the screen left bottom corner, not in the center"""
|
||||
region = context.region
|
||||
if not color:
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
color = tool.Blender.get_addon_preferences().decorations_colour
|
||||
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
@@ -404,7 +404,7 @@ class BaseDecorator:
|
||||
|
||||
font_id = self.font_id
|
||||
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
color = tool.Blender.get_addon_preferences().decorations_colour
|
||||
|
||||
ang = -Vector((1, 0)).angle_signed(text_dir)
|
||||
cos = math.cos(ang)
|
||||
@@ -812,7 +812,7 @@ class AngleDecorator(BaseDecorator):
|
||||
arcs_color = None
|
||||
edges_color = UNSPECIAL_ELEMENT_COLOR
|
||||
if context.active_object == obj and obj.data.is_editmode:
|
||||
arcs_color = context.preferences.addons["blenderbim"].preferences.decorator_color_special
|
||||
arcs_color = tool.Blender.get_addon_preferences().decorator_color_special
|
||||
edges_color = None
|
||||
|
||||
self.draw_lines(
|
||||
@@ -1093,7 +1093,7 @@ class HiddenDecorator(BaseDecorator):
|
||||
else:
|
||||
verts, idxs = self.get_mesh_geom(obj)
|
||||
winspace_verts = worldspace_to_winspace(verts, context)
|
||||
color = [i for i in context.preferences.addons["blenderbim"].preferences.decorations_colour]
|
||||
color = [i for i in tool.Blender.get_addon_preferences().decorations_colour]
|
||||
color[3] = 0.5
|
||||
self.draw_lines(context, obj, winspace_verts, idxs, color)
|
||||
|
||||
@@ -1409,7 +1409,7 @@ class GridDecorator(BaseDecorator):
|
||||
# stem with gaps for circles
|
||||
add_verts_sequence([v0 + gap, v1 - gap], start_i, **out_kwargs)
|
||||
|
||||
color = [i for i in context.preferences.addons["blenderbim"].preferences.decorations_colour]
|
||||
color = [i for i in tool.Blender.get_addon_preferences().decorations_colour]
|
||||
color[3] = 0.5
|
||||
self.draw_lines(context, obj, output_verts, output_edges, color)
|
||||
self.draw_labels(context, obj, verts)
|
||||
@@ -1591,7 +1591,7 @@ class CutDecorator:
|
||||
cls.installed = None
|
||||
|
||||
def __call__(self, context):
|
||||
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
selected_elements_color = self.addon_prefs.decorator_color_selected
|
||||
|
||||
all_vertices = []
|
||||
|
||||
@@ -121,7 +121,13 @@ class BoundingBox:
|
||||
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
|
||||
# MeasureIt-ARCH is GPL-v3
|
||||
def format_distance(
|
||||
value, isArea=False, hide_units=True, precision=None, decimal_places=None, suppress_zero_inches=False
|
||||
value,
|
||||
isArea=False,
|
||||
hide_units=True,
|
||||
precision=None,
|
||||
decimal_places=None,
|
||||
suppress_zero_inches=False,
|
||||
in_unit_length = False
|
||||
):
|
||||
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
|
||||
|
||||
@@ -130,17 +136,22 @@ def format_distance(
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
|
||||
toInches = 39.3700787401574887
|
||||
inPerFoot = 11.9999
|
||||
|
||||
if isArea:
|
||||
toInches = 1550
|
||||
inPerFoot = 143.999
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
# Imperial Formatting
|
||||
if unit_system == "IMPERIAL":
|
||||
if in_unit_length:
|
||||
if unit_length == 'INCHES':
|
||||
toInches = 1
|
||||
if unit_length == 'FEET':
|
||||
toInches = 11.9999
|
||||
else:
|
||||
toInches = 39.3700787401574887
|
||||
inPerFoot = 11.9999
|
||||
|
||||
if isArea:
|
||||
toInches = 1550
|
||||
inPerFoot = 143.999
|
||||
if not precision:
|
||||
precision = 256
|
||||
elif precision == "1":
|
||||
@@ -210,6 +221,12 @@ def format_distance(
|
||||
|
||||
# METRIC FORMATTING
|
||||
elif unit_system == "METRIC":
|
||||
if in_unit_length:
|
||||
if unit_length == 'CENTIMETERS':
|
||||
value = value/100
|
||||
if unit_length == 'MILLIMETERS':
|
||||
value = value/1000
|
||||
|
||||
if precision:
|
||||
value = precision * round(float(value) / precision)
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ import blenderbim.bim.module.drawing.helper as helper
|
||||
import blenderbim.bim.export_ifc
|
||||
from blenderbim.bim.module.drawing.decoration import CutDecorator
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
||||
from typing import NamedTuple, List, Union, Optional
|
||||
from typing import NamedTuple, List, Union, Optional, Literal
|
||||
from lxml import etree
|
||||
from mathutils import Vector, Color, Matrix
|
||||
from timeit import default_timer as timer
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty, Literal, RASTER_STYLE_PROPERTIES_EXCLUDE
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty, RASTER_STYLE_PROPERTIES_EXCLUDE
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from pathlib import Path
|
||||
from bpy_extras.image_utils import load_image
|
||||
@@ -73,19 +73,12 @@ class profile:
|
||||
print(self.task, timer() - self.start)
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LineworkContexts(NamedTuple):
|
||||
body: List[List[int]]
|
||||
annotation: List[List[int]]
|
||||
|
||||
|
||||
class AddAnnotationType(bpy.types.Operator, Operator):
|
||||
class AddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_annotation_type"
|
||||
bl_label = "Add Annotation Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -116,7 +109,7 @@ class AddAnnotationType(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name)
|
||||
|
||||
|
||||
class EnableAddAnnotationType(bpy.types.Operator, Operator):
|
||||
class EnableAddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_add_annotation_type"
|
||||
bl_label = "Enable Add Annotation Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -125,7 +118,7 @@ class EnableAddAnnotationType(bpy.types.Operator, Operator):
|
||||
bpy.context.scene.BIMAnnotationProperties.is_adding_type = True
|
||||
|
||||
|
||||
class DisableAddAnnotationType(bpy.types.Operator, Operator):
|
||||
class DisableAddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_add_annotation_type"
|
||||
bl_label = "Disable Add Annotation Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -134,7 +127,7 @@ class DisableAddAnnotationType(bpy.types.Operator, Operator):
|
||||
bpy.context.scene.BIMAnnotationProperties.is_adding_type = False
|
||||
|
||||
|
||||
class AddDrawing(bpy.types.Operator, Operator):
|
||||
class AddDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_drawing"
|
||||
bl_label = "Add Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -158,7 +151,7 @@ class AddDrawing(bpy.types.Operator, Operator):
|
||||
pass
|
||||
|
||||
|
||||
class DuplicateDrawing(bpy.types.Operator, Operator):
|
||||
class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.duplicate_drawing"
|
||||
bl_label = "Duplicate Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -278,9 +271,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
with profile("Combine SVG layers"):
|
||||
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
|
||||
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, svg_path
|
||||
)
|
||||
tool.Drawing.open_with_user_command(tool.Blender.get_addon_preferences().svg_command, svg_path)
|
||||
|
||||
if self.print_all:
|
||||
bpy.ops.bim.activate_drawing(drawing=original_drawing_id, camera_view_point=False)
|
||||
@@ -465,25 +456,25 @@ class CreateDrawing(bpy.types.Operator):
|
||||
ifc: ifcopenshell.file,
|
||||
tree: ifcopenshell.geom.tree,
|
||||
contexts: LineworkContexts,
|
||||
context_type: str,
|
||||
context_type: Literal["body", "annotation"],
|
||||
drawing_elements: set[ifcopenshell.entity_instance],
|
||||
target_view: str,
|
||||
) -> None:
|
||||
drawing_elements = drawing_elements.copy()
|
||||
contexts = getattr(contexts, context_type)
|
||||
contexts: list[list[int]] = getattr(contexts, context_type)
|
||||
for context in contexts:
|
||||
with profile(f"Processing {context_type} context"):
|
||||
if not context or not drawing_elements:
|
||||
continue
|
||||
geom_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True
|
||||
)
|
||||
geom_settings = ifcopenshell.geom.settings()
|
||||
geom_settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
geom_settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.NATIVE)
|
||||
if ifc.by_id(context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
|
||||
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
|
||||
# A 2mm Z offset to combat Z-fighting in plan or RCPs
|
||||
offset[2] = 0.002 if target_view == "PLAN_VIEW" else -0.002
|
||||
geom_settings.offset = offset
|
||||
geom_settings.set_context_ids(context)
|
||||
geom_settings.set("context-ids", context)
|
||||
it = ifcopenshell.geom.iterator(
|
||||
geom_settings, ifc, multiprocessing.cpu_count(), include=drawing_elements
|
||||
)
|
||||
@@ -507,7 +498,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# All very hackish whilst prototyping
|
||||
exporter = blenderbim.bim.export_ifc.IfcExporter(None)
|
||||
exporter.file = tool.Ifc.get()
|
||||
invalidated_elements = exporter.sync_all_objects(skip_unlinking=True)
|
||||
invalidated_elements = exporter.sync_all_objects()
|
||||
invalidated_elements += exporter.sync_edited_objects()
|
||||
invalidated_guids = [e.GlobalId for e in invalidated_elements if hasattr(e, "GlobalId")]
|
||||
cache = IfcStore.get_cache()
|
||||
@@ -561,7 +552,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
if tool.Ifc.get() == ifc and self.camera_element not in drawing_elements:
|
||||
with profile("Camera element"):
|
||||
# The camera must always be included, regardless of any include/exclude filters.
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True)
|
||||
geom_settings = ifcopenshell.geom.settings()
|
||||
geom_settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.NATIVE)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||
for elem in it:
|
||||
self.serialiser.write(elem)
|
||||
@@ -874,11 +866,14 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return svg_path
|
||||
|
||||
def setup_serialiser(self, target_view):
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True
|
||||
)
|
||||
self.svg_settings = ifcopenshell.geom.settings()
|
||||
self.svg_settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
self.svg_settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.NATIVE)
|
||||
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
||||
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
|
||||
self.serialiser_settings = ifcopenshell.geom.serializer_settings()
|
||||
self.serialiser = ifcopenshell.geom.serializers.svg(
|
||||
self.svg_buffer, self.svg_settings, self.serialiser_settings
|
||||
)
|
||||
self.serialiser.setWithoutStoreys(True)
|
||||
self.serialiser.setPolygonal(True)
|
||||
self.serialiser.setUseHlrPoly(True)
|
||||
@@ -982,7 +977,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
|
||||
if not obj: # This is a linked model object. For now, do nothing.
|
||||
if not obj: # This is a linked model object. For now, do nothing.
|
||||
continue
|
||||
|
||||
if not self.is_manifold(obj):
|
||||
@@ -1181,7 +1176,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return drawing_path
|
||||
|
||||
|
||||
class AddAnnotation(bpy.types.Operator, Operator):
|
||||
class AddAnnotation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_annotation"
|
||||
bl_label = "Add Annotation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1208,7 +1203,7 @@ class AddAnnotation(bpy.types.Operator, Operator):
|
||||
self.report({"WARNING"}, r)
|
||||
|
||||
|
||||
class AddSheet(bpy.types.Operator, Operator):
|
||||
class AddSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_sheet"
|
||||
bl_label = "Add Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1217,7 +1212,7 @@ class AddSheet(bpy.types.Operator, Operator):
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=context.scene.DocProperties.titleblock)
|
||||
|
||||
|
||||
class OpenSheet(bpy.types.Operator, Operator):
|
||||
class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.open_sheet"
|
||||
bl_label = "Open Sheet Layout"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1230,7 +1225,7 @@ class OpenSheet(bpy.types.Operator, Operator):
|
||||
core.open_sheet(tool.Drawing, sheet=sheet)
|
||||
|
||||
|
||||
class AddDrawingToSheet(bpy.types.Operator, Operator):
|
||||
class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_drawing_to_sheet"
|
||||
bl_label = "Add Drawing To Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1282,7 +1277,7 @@ class AddDrawingToSheet(bpy.types.Operator, Operator):
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
|
||||
class RemoveDrawingFromSheet(bpy.types.Operator, Operator):
|
||||
class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_drawing_from_sheet"
|
||||
bl_label = "Remove Drawing From Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1301,7 +1296,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, Operator):
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
|
||||
class CreateSheets(bpy.types.Operator, Operator):
|
||||
class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.create_sheets"
|
||||
bl_label = "Create Sheets"
|
||||
bl_description = "Build a sheet from the sheet layout"
|
||||
@@ -1370,8 +1365,8 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
),
|
||||
)
|
||||
|
||||
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
|
||||
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
svg2dxf_command = tool.Blender.get_addon_preferences().svg2dxf_command
|
||||
|
||||
if svg2pdf_command:
|
||||
# With great power comes great responsibility. Example:
|
||||
@@ -1389,9 +1384,9 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
subprocess.run([replacements.get(c, c) for c in command])
|
||||
|
||||
if svg2pdf_command:
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
|
||||
tool.Drawing.open_with_user_command(tool.Blender.get_addon_preferences().pdf_command, pdf)
|
||||
else:
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
|
||||
tool.Drawing.open_with_user_command(tool.Blender.get_addon_preferences().svg_command, svg)
|
||||
|
||||
|
||||
class SelectAllDrawings(bpy.types.Operator):
|
||||
@@ -1458,9 +1453,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
for drawing_uri in drawing_uris:
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri
|
||||
)
|
||||
tool.Drawing.open_with_user_command(tool.Blender.get_addon_preferences().svg_command, drawing_uri)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1553,6 +1546,13 @@ class ActivateDrawing(bpy.types.Operator):
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
CutDecorator.install(context)
|
||||
tool.Drawing.show_decorations()
|
||||
|
||||
# Save drawing bounds to the .ifc file
|
||||
camera = context.scene.camera
|
||||
camera_props = camera.data.BIMCameraProperties
|
||||
if camera_props.update_representation(camera):
|
||||
bpy.ops.bim.update_representation(obj=camera.name, ifc_representation_class="")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1586,7 +1586,7 @@ class ResizeText(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_drawing"
|
||||
bl_label = "Remove Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1689,7 +1689,7 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddDrawingStyle(bpy.types.Operator, Operator):
|
||||
class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_drawing_style"
|
||||
bl_label = "Add Drawing Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1703,7 +1703,7 @@ class AddDrawingStyle(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveDrawingStyle(bpy.types.Operator, Operator):
|
||||
class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_drawing_style"
|
||||
bl_label = "Remove Drawing Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1716,7 +1716,7 @@ class RemoveDrawingStyle(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SaveDrawingStyle(bpy.types.Operator, Operator):
|
||||
class SaveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.save_drawing_style"
|
||||
bl_label = "Save Drawing Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1781,7 +1781,7 @@ class SaveDrawingStyle(bpy.types.Operator, Operator):
|
||||
return space
|
||||
|
||||
|
||||
class SaveDrawingStylesData(bpy.types.Operator, Operator):
|
||||
class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.save_drawing_styles_data"
|
||||
bl_label = "Save Drawing Styles Data"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1834,7 +1834,7 @@ class SaveDrawingStylesData(bpy.types.Operator, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ActivateDrawingStyle(bpy.types.Operator, Operator):
|
||||
class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.activate_drawing_style"
|
||||
bl_label = "Activate Drawing Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1923,7 +1923,7 @@ class ActivateDrawingStyle(bpy.types.Operator, Operator):
|
||||
return space
|
||||
|
||||
|
||||
class RemoveSheet(bpy.types.Operator, Operator):
|
||||
class RemoveSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_sheet"
|
||||
bl_label = "Remove Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1933,7 +1933,7 @@ class RemoveSheet(bpy.types.Operator, Operator):
|
||||
core.remove_sheet(tool.Ifc, tool.Drawing, sheet=tool.Ifc.get().by_id(self.sheet))
|
||||
|
||||
|
||||
class AddSchedule(bpy.types.Operator, Operator):
|
||||
class AddSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_schedule"
|
||||
bl_label = "Add Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1963,7 +1963,7 @@ class AddSchedule(bpy.types.Operator, Operator):
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class RemoveSchedule(bpy.types.Operator, Operator):
|
||||
class RemoveSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_schedule"
|
||||
bl_label = "Remove Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1973,7 +1973,7 @@ class RemoveSchedule(bpy.types.Operator, Operator):
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", document=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class OpenSchedule(bpy.types.Operator, Operator):
|
||||
class OpenSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.open_schedule"
|
||||
bl_label = "Open Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1983,7 +1983,7 @@ class OpenSchedule(bpy.types.Operator, Operator):
|
||||
core.open_schedule(tool.Drawing, schedule=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class BuildSchedule(bpy.types.Operator, Operator):
|
||||
class BuildSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.build_schedule"
|
||||
bl_label = "Build Schedule"
|
||||
schedule: bpy.props.IntProperty()
|
||||
@@ -1992,7 +1992,7 @@ class BuildSchedule(bpy.types.Operator, Operator):
|
||||
core.build_schedule(tool.Drawing, schedule=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class AddScheduleToSheet(bpy.types.Operator, Operator):
|
||||
class AddScheduleToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_schedule_to_sheet"
|
||||
bl_label = "Add Schedule To Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2048,7 +2048,7 @@ class AddScheduleToSheet(bpy.types.Operator, Operator):
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
|
||||
class AddReferenceToSheet(bpy.types.Operator, Operator):
|
||||
class AddReferenceToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_reference_to_sheet"
|
||||
bl_label = "Add Reference To Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2105,7 +2105,7 @@ class AddReferenceToSheet(bpy.types.Operator, Operator):
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
|
||||
class AddReference(bpy.types.Operator, Operator):
|
||||
class AddReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_reference"
|
||||
bl_label = "Add Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2135,7 +2135,7 @@ class AddReference(bpy.types.Operator, Operator):
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class RemoveReference(bpy.types.Operator, Operator):
|
||||
class RemoveReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_reference"
|
||||
bl_label = "Remove Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2145,7 +2145,7 @@ class RemoveReference(bpy.types.Operator, Operator):
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "REFERENCE", document=tool.Ifc.get().by_id(self.reference))
|
||||
|
||||
|
||||
class OpenReference(bpy.types.Operator, Operator):
|
||||
class OpenReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.open_reference"
|
||||
bl_label = "Open Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2258,7 +2258,7 @@ class EditTextPopup(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditText(bpy.types.Operator, Operator):
|
||||
class EditText(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_text"
|
||||
bl_label = "Edit Text"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2268,7 +2268,7 @@ class EditText(bpy.types.Operator, Operator):
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
|
||||
class EnableEditingText(bpy.types.Operator, Operator):
|
||||
class EnableEditingText(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_text"
|
||||
bl_label = "Enable Editing Text"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2277,7 +2277,7 @@ class EnableEditingText(bpy.types.Operator, Operator):
|
||||
core.enable_editing_text(tool.Drawing, obj=context.active_object)
|
||||
|
||||
|
||||
class DisableEditingText(bpy.types.Operator, Operator):
|
||||
class DisableEditingText(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_text"
|
||||
bl_label = "Disable Editing Text"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2340,6 +2340,32 @@ class RemoveTextLiteral(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OrderTextLiteralUp(bpy.types.Operator):
|
||||
bl_idname = "bim.order_text_literal_up"
|
||||
bl_label = "Move Text Literal Up"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
literal_prop_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
obj.BIMTextProperties.literals.move(self.literal_prop_id, self.literal_prop_id - 1)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OrderTextLiteralDown(bpy.types.Operator):
|
||||
bl_idname = "bim.order_text_literal_down"
|
||||
bl_label = "Move Text Literal Down"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
literal_prop_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
obj.BIMTextProperties.literals.move(self.literal_prop_id, self.literal_prop_id + 1)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignSelectedObjectAsProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_selected_as_product"
|
||||
bl_label = "Assign Selected Object As Product"
|
||||
@@ -2369,7 +2395,7 @@ class AssignSelectedObjectAsProduct(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditAssignedProduct(bpy.types.Operator, Operator):
|
||||
class EditAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_assigned_product"
|
||||
bl_label = "Edit Text Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2382,7 +2408,7 @@ class EditAssignedProduct(bpy.types.Operator, Operator):
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
|
||||
class EnableEditingAssignedProduct(bpy.types.Operator, Operator):
|
||||
class EnableEditingAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_assigned_product"
|
||||
bl_label = "Enable Editing Assigned Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2391,7 +2417,7 @@ class EnableEditingAssignedProduct(bpy.types.Operator, Operator):
|
||||
core.enable_editing_assigned_product(tool.Drawing, obj=context.active_object)
|
||||
|
||||
|
||||
class DisableEditingAssignedProduct(bpy.types.Operator, Operator):
|
||||
class DisableEditingAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_assigned_product"
|
||||
bl_label = "Disable Editing Assigned Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2400,7 +2426,7 @@ class DisableEditingAssignedProduct(bpy.types.Operator, Operator):
|
||||
core.disable_editing_assigned_product(tool.Drawing, obj=context.active_object)
|
||||
|
||||
|
||||
class LoadSheets(bpy.types.Operator, Operator):
|
||||
class LoadSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_sheets"
|
||||
bl_label = "Load Sheets"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2427,7 +2453,7 @@ class LoadSheets(bpy.types.Operator, Operator):
|
||||
self.report({"ERROR"}, "Some sheets svg files are missing:\n" + "\n".join(sheets_not_found))
|
||||
|
||||
|
||||
class EditSheet(bpy.types.Operator, Operator):
|
||||
class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_sheet"
|
||||
bl_label = "Edit Sheet"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2483,7 +2509,7 @@ class EditSheet(bpy.types.Operator, Operator):
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
|
||||
class DisableEditingSheets(bpy.types.Operator, Operator):
|
||||
class DisableEditingSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_sheets"
|
||||
bl_label = "Disable Editing Sheets"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2492,7 +2518,7 @@ class DisableEditingSheets(bpy.types.Operator, Operator):
|
||||
core.disable_editing_sheets(tool.Drawing)
|
||||
|
||||
|
||||
class LoadSchedules(bpy.types.Operator, Operator):
|
||||
class LoadSchedules(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_schedules"
|
||||
bl_label = "Load Schedules"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2501,7 +2527,7 @@ class LoadSchedules(bpy.types.Operator, Operator):
|
||||
core.load_schedules(tool.Drawing)
|
||||
|
||||
|
||||
class DisableEditingSchedules(bpy.types.Operator, Operator):
|
||||
class DisableEditingSchedules(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_schedules"
|
||||
bl_label = "Disable Editing Schedules"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2510,7 +2536,7 @@ class DisableEditingSchedules(bpy.types.Operator, Operator):
|
||||
core.disable_editing_schedules(tool.Drawing)
|
||||
|
||||
|
||||
class LoadReferences(bpy.types.Operator, Operator):
|
||||
class LoadReferences(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_references"
|
||||
bl_label = "Load References"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2519,7 +2545,7 @@ class LoadReferences(bpy.types.Operator, Operator):
|
||||
core.load_references(tool.Drawing)
|
||||
|
||||
|
||||
class DisableEditingReferences(bpy.types.Operator, Operator):
|
||||
class DisableEditingReferences(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_references"
|
||||
bl_label = "Disable Editing References"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2528,7 +2554,7 @@ class DisableEditingReferences(bpy.types.Operator, Operator):
|
||||
core.disable_editing_references(tool.Drawing)
|
||||
|
||||
|
||||
class LoadDrawings(bpy.types.Operator, Operator):
|
||||
class LoadDrawings(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_drawings"
|
||||
bl_label = "Load Drawings"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2537,7 +2563,7 @@ class LoadDrawings(bpy.types.Operator, Operator):
|
||||
core.load_drawings(tool.Drawing)
|
||||
|
||||
|
||||
class DisableEditingDrawings(bpy.types.Operator, Operator):
|
||||
class DisableEditingDrawings(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_drawings"
|
||||
bl_label = "Disable Editing Drawings"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2602,7 +2628,7 @@ class ContractSheet(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectAssignedProduct(bpy.types.Operator, Operator):
|
||||
class SelectAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_assigned_product"
|
||||
bl_label = "Select Assigned Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2611,7 +2637,7 @@ class SelectAssignedProduct(bpy.types.Operator, Operator):
|
||||
core.select_assigned_product(tool.Drawing, context)
|
||||
|
||||
|
||||
class EnableEditingElementFilter(bpy.types.Operator, Operator):
|
||||
class EnableEditingElementFilter(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_element_filter"
|
||||
bl_label = "Enable Editing Element Filter"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2631,7 +2657,7 @@ class EnableEditingElementFilter(bpy.types.Operator, Operator):
|
||||
pass
|
||||
|
||||
|
||||
class EditElementFilter(bpy.types.Operator, Operator):
|
||||
class EditElementFilter(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_element_filter"
|
||||
bl_label = "Edit Element Filter"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2652,7 +2678,7 @@ class EditElementFilter(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.activate_drawing(drawing=element.id(), camera_view_point=False)
|
||||
|
||||
|
||||
class AddReferenceImage(bpy.types.Operator, Operator):
|
||||
class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_reference_image"
|
||||
bl_label = "Add Reference Image"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2738,7 +2764,7 @@ class AddReferenceImage(bpy.types.Operator, Operator):
|
||||
obj.material_slots[0].material = material
|
||||
bpy.ops.bim.add_style()
|
||||
|
||||
style = ifc_file.by_id(material.BIMMaterialProperties.ifc_style_id)
|
||||
style = ifc_file.by_id(material.BIMStyleProperties.ifc_definition_id)
|
||||
tool.Style.assign_style_to_object(style, obj)
|
||||
|
||||
# TODO: IfcSurfaceStyleRendering is unnecessary here, added it only because
|
||||
|
||||
@@ -371,6 +371,7 @@ class DocProperties(PropertyGroup):
|
||||
shadingstyle_default: StringProperty(default="Blender Default", name="Default Shading Style")
|
||||
drawing_font: StringProperty(default="OpenGost Type B TT.ttf", name="Drawing Font")
|
||||
magic_font_scale: bpy.props.FloatProperty(default=0.004118616, name="Font Scale Factor")
|
||||
imperial_precision: StringProperty(default="1/32", name="Imperial Precision")
|
||||
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
@@ -506,31 +507,9 @@ class BIMAssignedProductProperties(PropertyGroup):
|
||||
relating_product: PointerProperty(name="Relating Product", type=bpy.types.Object)
|
||||
|
||||
|
||||
# ObjectType: annotation_name, description, icon, data_type
|
||||
# fmt: off
|
||||
ANNOTATION_TYPES_DATA = {
|
||||
"DIMENSION": ("Dimension", "Add dimensions annotation.\nMeasurement values can be hidden through ShowDescriptionOnly property\nof BBIM_Dimension property set", "FIXED_SIZE", "curve"),
|
||||
"ANGLE": ("Angle", "", "DRIVER_ROTATIONAL_DIFFERENCE", "curve"),
|
||||
"RADIUS": ("Radius", "", "FORWARD", "curve"),
|
||||
"DIAMETER": ("Diameter", "Add diameter annotation.\nMeasurement values can be hidden through ShowDescriptionOnly property\nof BBIM_Dimension property set", "ARROW_LEFTRIGHT", "curve"),
|
||||
"TEXT": ("Text", "", "SMALL_CAPS", "empty"),
|
||||
"TEXT_LEADER": ("Leader", "", "TRACKING_BACKWARDS", "curve"),
|
||||
"STAIR_ARROW": ("Stair Arrow", "Add stair arrow annotation.\nIf you have IfcStairFlight object selected, it will be used as a reference for the annotation", "SCREEN_BACK", "curve"),
|
||||
"PLAN_LEVEL": ("Level (Plan)", "", "SORTBYEXT", "curve"),
|
||||
"SECTION_LEVEL": ("Level (Section)", "", "TRIA_DOWN", "curve"),
|
||||
"BREAKLINE": ("Breakline", "", "FCURVE", "mesh"),
|
||||
"SYMBOL": ("Symbol", "", "KEYFRAME", "empty"),
|
||||
"MULTI_SYMBOL": ("Multi-Symbol", "", "OUTLINER_DATA_POINTCLOUD", "mesh"),
|
||||
"LINEWORK": ("Line", "", "SNAP_MIDPOINT", "mesh"),
|
||||
"BATTING": ("Batting", "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set", "FORCE_FORCE", "mesh"),
|
||||
"REVISION_CLOUD":("Revision Cloud", "Add revision cloud", "VOLUME_DATA", "mesh"),
|
||||
"FILL_AREA": ("Fill Area", "", "NODE_TEXTURE", "mesh"),
|
||||
"FALL": ("Fall", "", "SORT_ASC", "curve"),
|
||||
"IMAGE": ("Image", "Add reference image attached to the drawing", "TEXTURE", "mesh"),
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
annotation_classes = [(x, *ANNOTATION_TYPES_DATA[x][:3], i) for i, x in enumerate(ANNOTATION_TYPES_DATA)]
|
||||
annotation_classes = [
|
||||
(x, *tool.Drawing.ANNOTATION_TYPES_DATA[x][:3], i) for i, x in enumerate(tool.Drawing.ANNOTATION_TYPES_DATA)
|
||||
]
|
||||
|
||||
|
||||
def get_relating_type_id(self, context):
|
||||
|
||||
@@ -28,7 +28,6 @@ from blenderbim.bim.module.drawing.data import (
|
||||
ElementFiltersData,
|
||||
DecoratorData,
|
||||
)
|
||||
from blenderbim.bim.module.drawing.prop import ANNOTATION_TYPES_DATA
|
||||
|
||||
|
||||
class BIM_PT_camera(Panel):
|
||||
@@ -525,6 +524,10 @@ class BIM_PT_text(Panel):
|
||||
|
||||
row = box.row(align=True)
|
||||
row.label(text=f"Literal[{i}]:")
|
||||
if i > 0:
|
||||
row.operator("bim.order_text_literal_up", icon="TRIA_UP", text="").literal_prop_id = i
|
||||
if i < len(props.literals) - 1:
|
||||
row.operator("bim.order_text_literal_down", icon="TRIA_DOWN", text="").literal_prop_id = i
|
||||
row.operator("bim.remove_text_literal", icon="X", text="").literal_prop_id = i
|
||||
|
||||
# skip BoxAlignment since we're going to format it ourselves
|
||||
|
||||
@@ -23,21 +23,11 @@ import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import WorkSpaceTool
|
||||
|
||||
from blenderbim.bim.module.drawing.prop import ANNOTATION_TYPES_DATA
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData, AnnotationData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.bim.handler
|
||||
|
||||
|
||||
# TODO: Fix circular import.
|
||||
# Declaring it here to avoid circular import problems
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LaunchAnnotationTypeManager(bpy.types.Operator):
|
||||
bl_idname = "bim.launch_annotation_type_manager"
|
||||
bl_label = "Launch Annotation Type Manager"
|
||||
@@ -196,7 +186,9 @@ def create_annotation():
|
||||
create_annotation_occurrence(bpy.context)
|
||||
else:
|
||||
object_type = props.object_type
|
||||
bpy.ops.bim.add_annotation(object_type=object_type, data_type=ANNOTATION_TYPES_DATA[object_type][-1])
|
||||
bpy.ops.bim.add_annotation(
|
||||
object_type=object_type, data_type=tool.Drawing.ANNOTATION_TYPES_DATA[object_type][-1]
|
||||
)
|
||||
if props.object_type == "IMAGE":
|
||||
bpy.ops.bim.add_reference_image(
|
||||
"INVOKE_DEFAULT", use_existing_object_by_name=bpy.context.active_object.name
|
||||
@@ -266,7 +258,7 @@ class AnnotationToolUI:
|
||||
)
|
||||
|
||||
|
||||
class Hotkey(bpy.types.Operator, Operator):
|
||||
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.annotation_hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -24,32 +24,21 @@ import logging
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.operator import MultipleFileSelector
|
||||
|
||||
|
||||
class SelectFMIfcFile(bpy.types.Operator):
|
||||
class SelectFMIfcFile(MultipleFileSelector):
|
||||
bl_idname = "bim.select_fm_ifc_file"
|
||||
bl_label = "Select FM IFC File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".ifc"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
files: bpy.props.CollectionProperty(name="File Path", type=bpy.types.OperatorFileListElement)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMFMProperties
|
||||
props.ifc_files.clear()
|
||||
dirname = os.path.dirname(self.filepath)
|
||||
for f in self.files:
|
||||
new = props.ifc_files.add()
|
||||
new.name = os.path.join(dirname, f.name)
|
||||
props.ifc_file = self.filepath
|
||||
|
||||
self.update_props(props, "ifc_file", props.ifc_files)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class ExecuteIfcFM(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_ifcfm"
|
||||
bl_label = "Execute IfcFM"
|
||||
|
||||
@@ -24,6 +24,7 @@ from mathutils import Vector
|
||||
|
||||
|
||||
def refresh():
|
||||
ViewportData.is_loaded = False
|
||||
PlacementData.is_loaded = False
|
||||
DerivedCoordinatesData.is_loaded = False
|
||||
RepresentationsData.is_loaded = False
|
||||
@@ -31,6 +32,28 @@ def refresh():
|
||||
ConnectionsData.is_loaded = False
|
||||
|
||||
|
||||
class ViewportData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {"mode": cls.mode()}
|
||||
|
||||
@classmethod
|
||||
def mode(cls):
|
||||
obj = bpy.context.active_object
|
||||
obj_mode = [("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0)]
|
||||
mesh_modes = [
|
||||
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
|
||||
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
|
||||
]
|
||||
if not obj or not tool.Blender.is_editable(obj):
|
||||
return obj_mode
|
||||
return mesh_modes
|
||||
|
||||
|
||||
class RepresentationsData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
@@ -39,7 +62,10 @@ class RepresentationsData:
|
||||
def load(cls):
|
||||
cls.data = {"representations": cls.representations()}
|
||||
cls.data["contexts"] = cls.contexts()
|
||||
|
||||
# Only after cls.representations().
|
||||
cls.data["shape_aspects"] = cls.shape_aspects()
|
||||
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -51,12 +77,7 @@ class RepresentationsData:
|
||||
if bpy.context.active_object.data and hasattr(bpy.context.active_object.data, "BIMMeshProperties"):
|
||||
active_representation_id = bpy.context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
|
||||
representations = []
|
||||
if element.is_a("IfcProduct") and element.Representation:
|
||||
representations = element.Representation.Representations
|
||||
elif element.is_a("IfcTypeProduct"):
|
||||
representations = [rm.MappedRepresentation for rm in element.RepresentationMaps or []]
|
||||
for representation in representations:
|
||||
for representation in tool.Geometry.get_representations_iter(element):
|
||||
representation_type = representation.RepresentationType
|
||||
if representation_type == "MappedRepresentation":
|
||||
representation_type = representation.Items[0].MappingSource.MappedRepresentation.RepresentationType
|
||||
@@ -95,7 +116,11 @@ class RepresentationsData:
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def shape_aspects(cls):
|
||||
def shape_aspects(cls) -> list[tuple[str, str, str]]:
|
||||
"""Get list of current's representation shape aspects for prop's enum_items."""
|
||||
# Ignore objects without representations, e.g. IfcRelSpaceBoundary.
|
||||
if not cls.data["representations"]:
|
||||
return []
|
||||
obj = bpy.context.active_object
|
||||
if not obj.data:
|
||||
return []
|
||||
@@ -127,12 +152,12 @@ class RepresentationItemsData:
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def total_items(cls):
|
||||
active_representation_id = None
|
||||
def total_items(cls) -> int:
|
||||
result = 0
|
||||
if bpy.context.active_object.data and hasattr(bpy.context.active_object.data, "BIMMeshProperties"):
|
||||
active_representation_id = bpy.context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
element = tool.Ifc.get().by_id(active_representation_id)
|
||||
obj = bpy.context.active_object
|
||||
assert obj
|
||||
element = tool.Geometry.get_active_representation(obj)
|
||||
if element:
|
||||
if not element.is_a("IfcShapeRepresentation"):
|
||||
return 0
|
||||
queue = list(element.Items)
|
||||
@@ -334,11 +359,13 @@ class PlacementData:
|
||||
obj = bpy.context.active_object
|
||||
if obj and props.has_blender_offset:
|
||||
xyz = cls.original_xyz(obj)
|
||||
cls.data.update({
|
||||
"original_x": str(xyz[0]),
|
||||
"original_y": str(xyz[1]),
|
||||
"original_z": str(xyz[2]),
|
||||
})
|
||||
cls.data.update(
|
||||
{
|
||||
"original_x": str(xyz[0]),
|
||||
"original_y": str(xyz[1]),
|
||||
"original_z": str(xyz[2]),
|
||||
}
|
||||
)
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -356,9 +383,9 @@ class PlacementData:
|
||||
obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][3],
|
||||
obj.matrix_world[2][3],
|
||||
float(props.blender_eastings) * unit_scale,
|
||||
float(props.blender_northings) * unit_scale,
|
||||
float(props.blender_orthogonal_height) / unit_scale,
|
||||
float(props.blender_offset_x) * unit_scale,
|
||||
float(props.blender_offset_y) * unit_scale,
|
||||
float(props.blender_offset_z) * unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
1.0,
|
||||
|
||||
@@ -43,16 +43,13 @@ from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditObjectPlacement(bpy.types.Operator, Operator):
|
||||
class EditObjectPlacement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_object_placement"
|
||||
bl_label = "Edit Object Placement"
|
||||
bl_description = (
|
||||
"Write selected objects placements to IFC.\n"
|
||||
"A star in the operator name indicates that active object placement in IFC is not yet synced with Blender"
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
@@ -62,7 +59,7 @@ class EditObjectPlacement(bpy.types.Operator, Operator):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
|
||||
class OverrideMeshSeparate(bpy.types.Operator, Operator):
|
||||
class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_mesh_separate"
|
||||
bl_label = "IFC Mesh Separate"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -98,7 +95,7 @@ class OverrideMeshSeparate(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.update_representation(obj=new_obj.name)
|
||||
|
||||
|
||||
class OverrideOriginSet(bpy.types.Operator, Operator):
|
||||
class OverrideOriginSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_origin_set"
|
||||
bl_label = "IFC Origin Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -123,7 +120,7 @@ class OverrideOriginSet(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
|
||||
class AddRepresentation(bpy.types.Operator, Operator):
|
||||
class AddRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_representation"
|
||||
bl_label = "Add Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -138,8 +135,23 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
"for Profile - 2D bounding box by local XZ axes.\n"
|
||||
"For other contexts - bounding box is 3d.",
|
||||
),
|
||||
("OBJECT", "From Object", "Copies geometry from another object"),
|
||||
(
|
||||
"OBJECT",
|
||||
"From Object",
|
||||
(
|
||||
"Copies geometry from another object.\n"
|
||||
"Final version of the geometry will be used (e.g. with all modifiers, shape keys applied)"
|
||||
),
|
||||
),
|
||||
("PROJECT", "Full Representation", "Reuses the current representation"),
|
||||
(
|
||||
"CUBE",
|
||||
"Cube",
|
||||
(
|
||||
"Add cube representation.\n"
|
||||
"Useful for adding a representation to an object without any representations"
|
||||
),
|
||||
),
|
||||
],
|
||||
name="Representation Conversion Method",
|
||||
)
|
||||
@@ -155,7 +167,17 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
|
||||
original_data = obj.data
|
||||
|
||||
if self.representation_conversion_method == "OUTLINE":
|
||||
conversion_method = self.representation_conversion_method
|
||||
if not original_data and conversion_method not in ("OBJECT", "CUBE"):
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
(f"No mesh data found for the active object. Mesh data required for '{conversion_method}' method."),
|
||||
)
|
||||
return {"FINISH"}
|
||||
|
||||
# NOTE: `data` is temporary data generated to add a representation.
|
||||
data: bpy.types.Mesh
|
||||
if conversion_method == "OUTLINE":
|
||||
if ifc_context.ContextType == "Plan":
|
||||
data = tool.Geometry.generate_outline_mesh(obj, axis="+Z")
|
||||
elif ifc_context.ContextIdentifier == "Profile":
|
||||
@@ -163,7 +185,7 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
else:
|
||||
data = tool.Geometry.generate_outline_mesh(obj, axis="+Z")
|
||||
tool.Geometry.change_object_data(obj, data, is_global=True)
|
||||
elif self.representation_conversion_method == "BOX":
|
||||
elif conversion_method == "BOX":
|
||||
if ifc_context.ContextType == "Plan":
|
||||
data = tool.Geometry.generate_2d_box_mesh(obj, axis="Z")
|
||||
elif ifc_context.ContextIdentifier == "Profile":
|
||||
@@ -171,13 +193,25 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
else:
|
||||
data = tool.Geometry.generate_3d_box_mesh(obj)
|
||||
tool.Geometry.change_object_data(obj, data, is_global=True)
|
||||
elif (
|
||||
self.representation_conversion_method == "OBJECT"
|
||||
and props.representation_from_object
|
||||
and props.representation_from_object.data
|
||||
):
|
||||
data = tool.Geometry.duplicate_object_data(props.representation_from_object)
|
||||
tool.Geometry.change_object_data(obj, data, is_global=True)
|
||||
elif conversion_method in ("OBJECT", "CUBE"):
|
||||
if conversion_method == "OBJECT":
|
||||
if not (source_obj := props.representation_from_object):
|
||||
self.report({"ERROR"}, "No object is selected to copy a representation from.")
|
||||
return {"FINISHED"}
|
||||
|
||||
depsgraph = context.evaluated_depsgraph_get()
|
||||
eval_obj = source_obj.evaluated_get(depsgraph)
|
||||
data = bpy.data.meshes.new_from_object(eval_obj)
|
||||
else: # CUBE
|
||||
data = bpy.data.meshes.new("Cube")
|
||||
bm = tool.Blender.get_bmesh_for_mesh(data)
|
||||
bmesh.ops.create_cube(bm, size=1)
|
||||
tool.Blender.apply_bmesh(data, bm)
|
||||
|
||||
if original_data:
|
||||
tool.Geometry.change_object_data(obj, data, is_global=True)
|
||||
else:
|
||||
obj = tool.Geometry.recreate_object_with_data(obj, data, is_global=True)
|
||||
|
||||
try:
|
||||
core.add_representation(
|
||||
@@ -190,6 +224,10 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
ifc_representation_class=None,
|
||||
profile_set_usage=None,
|
||||
)
|
||||
# Object might be recreated, need to set it as active again.
|
||||
if context.active_object != obj:
|
||||
tool.Blender.set_active_object(obj)
|
||||
bpy.data.meshes.remove(data)
|
||||
except core.IncompatibleRepresentationError:
|
||||
if obj.data != original_data:
|
||||
tool.Geometry.change_object_data(obj, original_data, is_global=True)
|
||||
@@ -208,7 +246,7 @@ class AddRepresentation(bpy.types.Operator, Operator):
|
||||
row.prop(context.scene.BIMGeometryProperties, "representation_from_object", text="")
|
||||
|
||||
|
||||
class SelectConnection(bpy.types.Operator, Operator):
|
||||
class SelectConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_connection"
|
||||
bl_label = "Select Connection"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -218,7 +256,7 @@ class SelectConnection(bpy.types.Operator, Operator):
|
||||
core.select_connection(tool.Geometry, connection=tool.Ifc.get().by_id(self.connection))
|
||||
|
||||
|
||||
class RemoveConnection(bpy.types.Operator, Operator):
|
||||
class RemoveConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_connection"
|
||||
bl_label = "Remove Connection"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -228,7 +266,7 @@ class RemoveConnection(bpy.types.Operator, Operator):
|
||||
core.remove_connection(tool.Geometry, connection=tool.Ifc.get().by_id(self.connection))
|
||||
|
||||
|
||||
class SwitchRepresentation(bpy.types.Operator, Operator):
|
||||
class SwitchRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.switch_representation"
|
||||
bl_label = "Switch Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -277,7 +315,7 @@ class SwitchRepresentation(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class RemoveRepresentation(bpy.types.Operator, Operator):
|
||||
class RemoveRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_representation"
|
||||
bl_label = "Remove Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -292,7 +330,7 @@ class RemoveRepresentation(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class PurgeUnusedRepresentations(bpy.types.Operator, Operator):
|
||||
class PurgeUnusedRepresentations(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.purge_unused_representations"
|
||||
bl_label = "Purge Unused Representations"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -302,9 +340,13 @@ class PurgeUnusedRepresentations(bpy.types.Operator, Operator):
|
||||
self.report({"INFO"}, f"{purged_representations} representations were purged.")
|
||||
|
||||
|
||||
class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.update_representation"
|
||||
bl_label = "Update Representation"
|
||||
bl_description = (
|
||||
"Write selected objects representations to IFC.\n"
|
||||
"A star in the operator name indicates that active object representation in IFC is not yet synced with Blender"
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
ifc_representation_class: bpy.props.StringProperty()
|
||||
@@ -314,8 +356,9 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
# Ensure mode is object to prevent invalid mesh data causing CTD
|
||||
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
||||
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||
self.file = IfcStore.get_file()
|
||||
obj_name: str = self.obj
|
||||
objs = [bpy.data.objects[obj_name]] if obj_name else context.selected_objects
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
for obj in objs:
|
||||
# TODO: write unit tests to see how this bulk operation handles
|
||||
@@ -324,10 +367,10 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
if not obj.data:
|
||||
continue
|
||||
self.update_obj_mesh_representation(context, obj)
|
||||
IfcStore.edited_objs.discard(obj)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
def update_obj_mesh_representation(self, context, obj):
|
||||
def update_obj_mesh_representation(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
material = ifcopenshell.util.element.get_material(product, should_skip_usage=True)
|
||||
|
||||
@@ -357,7 +400,9 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
element_type = ifcopenshell.util.element.get_type(product)
|
||||
if element_type: # Some invalid IFCs use material sets without a type.
|
||||
ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), products=[element_type])
|
||||
tool.Material.ensure_material_unassigned([element_type])
|
||||
ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), products=[product])
|
||||
tool.Material.ensure_material_unassigned([product])
|
||||
else:
|
||||
# These objects are parametrically based on an axis and should not be modified as a mesh
|
||||
return
|
||||
@@ -476,7 +521,7 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetRepresentationIfcParameters(bpy.types.Operator, Operator):
|
||||
class GetRepresentationIfcParameters(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.get_representation_ifc_parameters"
|
||||
bl_label = "Get Representation IFC Parameters"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -487,7 +532,7 @@ class GetRepresentationIfcParameters(bpy.types.Operator, Operator):
|
||||
self.report({"INFO"}, f"{len(parameters)} parameters found.")
|
||||
|
||||
|
||||
class CopyRepresentation(bpy.types.Operator, Operator):
|
||||
class CopyRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.copy_representation"
|
||||
bl_label = "Copy Representation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1126,7 +1171,6 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
location_diff = new_obj.location - base_obj_location
|
||||
new_obj.location = context.scene.cursor.location + location_diff
|
||||
|
||||
|
||||
if len(context.selected_objects) != 1:
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1406,7 +1450,7 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OverrideJoin(bpy.types.Operator, Operator):
|
||||
class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_object_join"
|
||||
bl_label = "IFC Join"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1531,7 +1575,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
selected_objs = context.selected_objects or ([context.active_object] if context.active_object else [])
|
||||
selected_objs = tool.Blender.get_selected_objects()
|
||||
active_obj = context.active_object
|
||||
|
||||
if context.active_object:
|
||||
@@ -1542,23 +1586,13 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
return bpy.ops.bim.enable_editing_boundary_geometry()
|
||||
|
||||
for obj in selected_objs:
|
||||
if not obj:
|
||||
continue
|
||||
obj_supports_edit_mode, message = tool.Blender.object_supports_edit_mode(obj)
|
||||
if not obj_supports_edit_mode:
|
||||
self.report({"INFO"}, message)
|
||||
if not tool.Blender.is_editable(obj):
|
||||
self.report({"INFO"}, f"Object cannot be edited: {obj.name}")
|
||||
obj.select_set(False)
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
usage_type = tool.Model.get_usage_type(element)
|
||||
if usage_type is not None and usage_type not in ("PROFILE", "LAYER3"):
|
||||
# Parametric objects shall not be edited as meshes as they
|
||||
# can be modified to be incompatible with the parametric
|
||||
# constraints.
|
||||
obj.select_set(False)
|
||||
continue
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
@@ -1566,6 +1600,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
obj.select_set(False)
|
||||
continue
|
||||
|
||||
usage_type = tool.Model.get_usage_type(element)
|
||||
is_profile = True
|
||||
representation_class = tool.Geometry.get_ifc_representation_class(element, representation)
|
||||
if usage_type == "PROFILE":
|
||||
@@ -1616,6 +1651,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=False,
|
||||
)
|
||||
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
|
||||
tool.Geometry.dissolve_triangulated_edges(obj)
|
||||
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
|
||||
else:
|
||||
@@ -1663,6 +1699,8 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
if not context.active_object:
|
||||
return {"FINISHED"}
|
||||
for obj in self.edited_objs:
|
||||
if self.should_save:
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="")
|
||||
@@ -1694,6 +1732,8 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
|
||||
|
||||
def _invoke(self, context, event):
|
||||
if not context.active_object:
|
||||
return {"FINISHED"}
|
||||
self.is_valid = True
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
|
||||
@@ -1775,7 +1815,7 @@ class FlipObject(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingRepresentationItems(bpy.types.Operator, Operator):
|
||||
class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_representation_items"
|
||||
bl_label = "Enable Editing Representation Items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1826,7 +1866,7 @@ class EnableEditingRepresentationItems(bpy.types.Operator, Operator):
|
||||
props.items.move(props.items[:].index(item), i)
|
||||
|
||||
|
||||
class DisableEditingRepresentationItems(bpy.types.Operator, Operator):
|
||||
class DisableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_representation_items"
|
||||
bl_label = "Disable Editing Representation Items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1836,7 +1876,7 @@ class DisableEditingRepresentationItems(bpy.types.Operator, Operator):
|
||||
obj.BIMGeometryProperties.is_editing = False
|
||||
|
||||
|
||||
class RemoveRepresentationItem(bpy.types.Operator, Operator):
|
||||
class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_representation_item"
|
||||
bl_label = "Remove Representation Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1894,7 +1934,7 @@ def poll_editing_representaiton_item_style(cls, context):
|
||||
return True
|
||||
|
||||
|
||||
class EnableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
class EnableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_representation_item_style"
|
||||
bl_label = "Enable Editing Representation Item Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1916,7 +1956,7 @@ class EnableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
props.representation_item_style = str(style.id())
|
||||
|
||||
|
||||
class EditRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
class EditRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_representation_item_style"
|
||||
bl_label = "Edit Representation Item Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1938,7 +1978,7 @@ class EditRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.enable_editing_representation_items()
|
||||
|
||||
|
||||
class DisableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
class DisableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_representation_item_style"
|
||||
bl_label = "Disable Editing Representation Item Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1948,7 +1988,7 @@ class DisableEditingRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
props.is_editing_item_style = False
|
||||
|
||||
|
||||
class UnassignRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
class UnassignRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_representation_item_style"
|
||||
bl_label = "Unassign Representation Item Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1976,7 +2016,7 @@ class UnassignRepresentationItemStyle(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.enable_editing_representation_items()
|
||||
|
||||
|
||||
class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_representation_item_shape_aspect"
|
||||
bl_label = "Enable Editing Representation Item Shape Aspect"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -1991,7 +2031,7 @@ class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
props.representation_item_shape_aspect = str(shape_aspect_id)
|
||||
|
||||
|
||||
class EditRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_representation_item_shape_aspect"
|
||||
bl_label = "Edit Representation Item Shape Aspect"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2047,7 +2087,7 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
bpy.ops.bim.enable_editing_representation_items()
|
||||
|
||||
|
||||
class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_representation_item_shape_aspect"
|
||||
bl_label = "Disable Editing Representation Item Shape Aspect"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -2057,7 +2097,7 @@ class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator):
|
||||
props.is_editing_item_shape_aspect = False
|
||||
|
||||
|
||||
class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, Operator):
|
||||
class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_representation_item_from_shape_aspect"
|
||||
bl_label = "Remove Representation Item From Shape Aspect"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.geometry.data import RepresentationsData
|
||||
from blenderbim.bim.module.geometry.data import RepresentationsData, ViewportData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -48,6 +48,12 @@ def update_mode(self, context):
|
||||
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
|
||||
|
||||
|
||||
def get_mode(self, context):
|
||||
if not ViewportData.is_loaded:
|
||||
ViewportData.load()
|
||||
return ViewportData.data["mode"]
|
||||
|
||||
|
||||
def get_styles(self, context):
|
||||
# postponed import to avoid circular import
|
||||
from blenderbim.bim.module.material.data import MaterialsData
|
||||
@@ -129,13 +135,13 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
# Navisworks workaround
|
||||
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
|
||||
is_changing_mode: BoolProperty(name="Is Changing Mode", default=False)
|
||||
mode: EnumProperty(
|
||||
default="OBJECT",
|
||||
items=[
|
||||
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
|
||||
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
|
||||
],
|
||||
name="IFC Interaction Mode",
|
||||
update=update_mode,
|
||||
mode: EnumProperty(items=get_mode, name="IFC Interaction Mode", update=update_mode)
|
||||
|
||||
def is_object_valid_for_representation_copy(self, obj: bpy.types.Object) -> bool:
|
||||
return bool(obj != bpy.context.active_object and obj.data)
|
||||
|
||||
representation_from_object: PointerProperty(
|
||||
name="Object to copy a representation from.\nIt doesn't have to be an IFC object.",
|
||||
type=bpy.types.Object,
|
||||
poll=is_object_valid_for_representation_copy,
|
||||
)
|
||||
representation_from_object: PointerProperty(type=bpy.types.Object)
|
||||
|
||||
@@ -33,6 +33,8 @@ from blenderbim.bim.module.layer.data import LayersData
|
||||
|
||||
|
||||
def mode_menu(self, context):
|
||||
if not tool.Ifc.get():
|
||||
return
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
row.operator("bim.override_mode_set_object", icon="CANCEL", text="Discard Changes").should_save = False
|
||||
@@ -350,7 +352,10 @@ class BIM_PT_mesh(Panel):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.update_representation", text="Manually Save Representation")
|
||||
text = "Manually Save Representation"
|
||||
if tool.Ifc.is_edited(context.active_object):
|
||||
text += "*"
|
||||
row.operator("bim.update_representation", text=text)
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.copy_representation", text="Copy Mesh From Active To Selected")
|
||||
@@ -419,10 +424,11 @@ class BIM_PT_placement(Panel):
|
||||
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
||||
row.label(text=context.active_object.BIMObjectProperties.blender_offset_type)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=PlacementData.data["original_x"], icon="EMPTY_AXIS")
|
||||
row.label(text=PlacementData.data["original_y"])
|
||||
row.label(text=PlacementData.data["original_z"])
|
||||
if context.active_object.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=PlacementData.data["original_x"], icon="EMPTY_AXIS")
|
||||
row.label(text=PlacementData.data["original_y"])
|
||||
row.label(text=PlacementData.data["original_z"])
|
||||
|
||||
|
||||
class BIM_PT_derived_coordinates(Panel):
|
||||
@@ -444,7 +450,10 @@ class BIM_PT_derived_coordinates(Panel):
|
||||
DerivedCoordinatesData.load()
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.edit_object_placement", icon="EXPORT")
|
||||
text = "Edit Object Placement"
|
||||
if tool.Ifc.is_moved(context.active_object):
|
||||
text += "*"
|
||||
row.operator("bim.edit_object_placement", text=text, icon="EXPORT")
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="XYZ Dimensions")
|
||||
|
||||
@@ -20,23 +20,32 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.EnableEditingGeoreferencing,
|
||||
operator.DisableEditingGeoreferencing,
|
||||
operator.EditGeoreferencing,
|
||||
operator.SetIfcGridNorth,
|
||||
operator.SetBlenderGridNorth,
|
||||
operator.SetIfcTrueNorth,
|
||||
operator.SetBlenderTrueNorth,
|
||||
operator.RemoveGeoreferencing,
|
||||
operator.AddGeoreferencing,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.ConvertGlobalToLocal,
|
||||
operator.GetCursorLocation,
|
||||
operator.SetCursorLocation,
|
||||
operator.ConvertAngleToCoordinates,
|
||||
operator.ConvertGlobalToLocal,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.DisableEditingGeoreferencing,
|
||||
operator.DisableEditingTrueNorth,
|
||||
operator.DisableEditingWCS,
|
||||
operator.EditGeoreferencing,
|
||||
operator.EditTrueNorth,
|
||||
operator.EditWCS,
|
||||
operator.EnableEditingGeoreferencing,
|
||||
operator.EnableEditingTrueNorth,
|
||||
operator.EnableEditingWCS,
|
||||
operator.GetCursorLocation,
|
||||
operator.ImportPlot,
|
||||
operator.RemoveGeoreferencing,
|
||||
operator.RemoveTrueNorth,
|
||||
operator.SetBlenderGridNorth,
|
||||
operator.SetBlenderTrueNorth,
|
||||
operator.SetIfcGridNorth,
|
||||
operator.SetIfcTrueNorth,
|
||||
prop.BIMGeoreferenceProperties,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_gis_blender,
|
||||
ui.BIM_PT_gis_wcs,
|
||||
ui.BIM_PT_gis_true_north,
|
||||
ui.BIM_PT_gis_calculator,
|
||||
)
|
||||
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.geolocation
|
||||
import numpy as np
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.geolocation
|
||||
from mathutils import Matrix
|
||||
from ifcopenshell.util.doc import get_entity_doc
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -32,58 +35,59 @@ class GeoreferenceData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data["blender_derived_angle"] = cls.blender_derived_angle()
|
||||
cls.data["map_conversion"] = cls.map_conversion()
|
||||
cls.data["coordinate_operation_class"] = cls.coordinate_operation_class()
|
||||
cls.data["coordinate_operation"] = cls.coordinate_operation()
|
||||
cls.data["map_derived_angle"] = cls.map_derived_angle()
|
||||
cls.data["projected_crs"] = cls.projected_crs()
|
||||
cls.data["true_north"] = cls.true_north()
|
||||
cls.data["true_derived_angle"] = cls.true_derived_angle()
|
||||
cls.data["local_unit_symbol"] = cls.local_unit_symbol()
|
||||
cls.data["map_unit_symbol"] = cls.map_unit_symbol()
|
||||
cls.data["world_coordinate_system"] = cls.world_coordinate_system()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def blender_derived_angle(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
return str(
|
||||
round(
|
||||
ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
|
||||
),
|
||||
3,
|
||||
)
|
||||
)
|
||||
def coordinate_operation_class(cls):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return []
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcCoordinateOperation")
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
names = [d.name() for d in declarations]
|
||||
version = tool.Ifc.get_schema()
|
||||
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def map_conversion(cls):
|
||||
def coordinate_operation(cls):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
map_conversion = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion")
|
||||
if not map_conversion:
|
||||
coordinate_operation = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion")
|
||||
if not coordinate_operation:
|
||||
return {}
|
||||
del map_conversion["id"]
|
||||
return map_conversion
|
||||
del coordinate_operation["id"]
|
||||
coordinate_operation["type"] = "ePSet_MapConversion"
|
||||
return coordinate_operation
|
||||
|
||||
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if context.HasCoordinateOperation:
|
||||
map_conversion = context.HasCoordinateOperation[0].get_info()
|
||||
del map_conversion["id"]
|
||||
del map_conversion["type"]
|
||||
del map_conversion["SourceCRS"]
|
||||
del map_conversion["TargetCRS"]
|
||||
return map_conversion
|
||||
coordinate_operation = context.HasCoordinateOperation[0].get_info()
|
||||
del coordinate_operation["id"]
|
||||
del coordinate_operation["SourceCRS"]
|
||||
del coordinate_operation["TargetCRS"]
|
||||
return coordinate_operation
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def map_derived_angle(cls):
|
||||
if (
|
||||
cls.data["map_conversion"]
|
||||
and cls.data["map_conversion"].get("XAxisAbscissa", None) is not None
|
||||
and cls.data["map_conversion"].get("XAxisOrdinate", None) is not None
|
||||
cls.data["coordinate_operation"]
|
||||
and cls.data["coordinate_operation"].get("XAxisAbscissa", None) is not None
|
||||
and cls.data["coordinate_operation"].get("XAxisOrdinate", None) is not None
|
||||
):
|
||||
return str(
|
||||
round(
|
||||
ifcopenshell.util.geolocation.xaxis2angle(
|
||||
cls.data["map_conversion"]["XAxisAbscissa"], cls.data["map_conversion"]["XAxisOrdinate"]
|
||||
cls.data["coordinate_operation"]["XAxisAbscissa"],
|
||||
cls.data["coordinate_operation"]["XAxisOrdinate"],
|
||||
),
|
||||
3,
|
||||
)
|
||||
@@ -130,4 +134,41 @@ class GeoreferenceData:
|
||||
@classmethod
|
||||
def true_derived_angle(cls):
|
||||
if cls.data["true_north"]:
|
||||
return str(round(ifcopenshell.util.geolocation.yaxis2angle(*cls.data["true_north"][0:2]), 3))
|
||||
return str(round(ifcopenshell.util.geolocation.yaxis2angle(*cls.data["true_north"][:2]), 3))
|
||||
|
||||
@classmethod
|
||||
def local_unit_symbol(cls):
|
||||
if not (unit := ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "LENGTHUNIT")):
|
||||
return "n/a"
|
||||
return ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
|
||||
@classmethod
|
||||
def map_unit_symbol(cls):
|
||||
if tool.Ifc.get().schema == "IFC2X3":
|
||||
return cls.local_unit_symbol()
|
||||
if crs := tool.Ifc.get().by_type("IfcProjectedCRS"):
|
||||
crs = crs[0]
|
||||
if not (unit := crs.MapUnit):
|
||||
return cls.local_unit_symbol()
|
||||
return ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
return "n/a"
|
||||
|
||||
@classmethod
|
||||
def world_coordinate_system(cls):
|
||||
wcs = ifcopenshell.util.geolocation.get_wcs(tool.Ifc.get())
|
||||
result = {}
|
||||
if wcs is None:
|
||||
result["has_transformation"] = False
|
||||
return result
|
||||
if np.allclose(wcs, np.eye(4)):
|
||||
result["has_transformation"] = False
|
||||
else:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
result["has_transformation"] = True
|
||||
result["rotation"] = str(round(ifcopenshell.util.geolocation.yaxis2angle(*wcs[:, 1][:2]), 3))
|
||||
result["x"], result["y"], result["z"] = wcs[:, 3][:3]
|
||||
wcs[0][3] *= unit_scale
|
||||
wcs[1][3] *= unit_scale
|
||||
wcs[2][3] *= unit_scale
|
||||
result["matrix"] = Matrix(wcs)
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
# 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 ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from math import radians
|
||||
from bpy.types import SpaceView3D
|
||||
from mathutils import Vector, Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
from blenderbim.bim.module.georeference.data import GeoreferenceData
|
||||
|
||||
|
||||
class GeoreferenceDecorator:
|
||||
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.calculate_angles(context)
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
e, n, h = [round(float(o), 7) for o in props.model_origin.split(",")]
|
||||
|
||||
wcs_matrix = None
|
||||
if GeoreferenceData.data["world_coordinate_system"]["has_transformation"]:
|
||||
wcs_matrix = GeoreferenceData.data["world_coordinate_system"]["matrix"]
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
|
||||
self.font_id = 0
|
||||
blf.size(self.font_id, 12)
|
||||
color = self.addon_prefs.decorations_colour
|
||||
blf.color(self.font_id, *color)
|
||||
|
||||
text = "X: 0, Y: 0, Z: 0"
|
||||
self.draw_text_at_position(context, text, Vector((0, -0.1, 0)))
|
||||
|
||||
angle = Matrix.Rotation(radians(self.pn_angle), 4, "Z")
|
||||
p = angle @ Vector((0, 1.55, 0))
|
||||
self.draw_text_at_position(context, "Project North", p)
|
||||
|
||||
if not tool.Cad.is_x(self.pn_angle, 0):
|
||||
arc_start = Vector((0, 1.05, 0))
|
||||
angle_half = Matrix.Rotation(radians(self.pn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
self.draw_text_at_position(context, f"{self.pn_angle}deg", arc_mid)
|
||||
|
||||
if GeoreferenceData.data["coordinate_operation"]:
|
||||
angle = Matrix.Rotation(radians(self.gn_angle), 4, "Z")
|
||||
grid_north_p = angle @ Vector((0, 1.05, 0))
|
||||
self.draw_text_at_position(context, "Grid North", grid_north_p)
|
||||
|
||||
arc_start = Vector((0, 0.55, 0))
|
||||
angle_half = Matrix.Rotation(radians(self.gn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
self.draw_text_at_position(context, f"{self.gn_angle}deg", arc_mid)
|
||||
|
||||
text = f"\nE: {e}, N: {n}, H: {h}"
|
||||
self.draw_text_at_position(context, text, Vector((0, -0.1, 0)))
|
||||
|
||||
if GeoreferenceData.data["true_north"]:
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
grid_north_p = angle @ Vector((0, 0.8, 0))
|
||||
self.draw_text_at_position(context, "True North", grid_north_p)
|
||||
|
||||
arc_start = Vector((0, 0.3, 0))
|
||||
angle_half = Matrix.Rotation(radians(self.tn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
self.draw_text_at_position(context, f"{self.tn_angle}deg", arc_mid)
|
||||
|
||||
if wcs_matrix:
|
||||
x, y, z = wcs_matrix.translation
|
||||
if operation := GeoreferenceData.data["coordinate_operation"]:
|
||||
e = operation.get("Eastings")
|
||||
n = operation.get("Northings")
|
||||
h = operation.get("OrthogonalHeight")
|
||||
text = f"WCS\n X: {x}, Y: {y}, Z: {z}\nE: {e}, N: {n}, H: {h}"
|
||||
else:
|
||||
text = f"WCS\n X: {x}, Y: {y}, Z: {z}"
|
||||
|
||||
if wcs_matrix.translation.length < 1000:
|
||||
position = wcs_matrix.translation.copy()
|
||||
else:
|
||||
position = wcs_matrix.translation.normalized() * 3
|
||||
text += "\n(Warning: Actual XYZ Not Shown)"
|
||||
position -= Vector((0, 0.1, 0))
|
||||
|
||||
self.draw_text_at_position(context, text, position)
|
||||
|
||||
def draw_text_at_position(self, context, text, position):
|
||||
coords_2d = location_3d_to_region_2d(context.region, context.region_data, position)
|
||||
if not coords_2d:
|
||||
return
|
||||
for i, line in enumerate(text.split("\n")):
|
||||
w, h = blf.dimensions(self.font_id, line)
|
||||
co = coords_2d.copy()
|
||||
co -= Vector((w * 0.5, 15 * i))
|
||||
blf.position(self.font_id, co[0], co[1], 0)
|
||||
blf.draw(self.font_id, line)
|
||||
|
||||
def draw_geometry(self, context):
|
||||
self.calculate_angles(context)
|
||||
|
||||
wcs_matrix = None
|
||||
if GeoreferenceData.data["world_coordinate_system"]["has_transformation"]:
|
||||
wcs_matrix = GeoreferenceData.data["world_coordinate_system"]["matrix"]
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
decorator_color = self.addon_prefs.decorations_colour
|
||||
decorator_color_special = self.addon_prefs.decorator_color_special
|
||||
decorator_color_selected = self.addon_prefs.decorator_color_selected
|
||||
decorator_color_error = self.addon_prefs.decorator_color_error
|
||||
|
||||
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))
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
# A crosshair to emphasize the coordinate system, in case they have grids turned off
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
verts = [Vector((0, -1.25, 0)), Vector((0, 1.25, 0)), Vector((-1.25, 0, 0)), Vector((1.25, 0, 0))]
|
||||
edges = [[0, 1], [2, 3]]
|
||||
self.draw_batch("LINES", verts, decorator_color, edges)
|
||||
self.line_shader.uniform_float("lineWidth", 5.0)
|
||||
|
||||
# A point at the origin to emphasize its significance as a Blender origin
|
||||
points = [Vector((0, 0, 0))]
|
||||
gpu.state.point_size_set(10)
|
||||
self.draw_batch("POINTS", points, decorator_color_selected)
|
||||
|
||||
# A project north arrow always exists
|
||||
angle = Matrix.Rotation(radians(self.pn_angle), 4, "Z")
|
||||
points = [angle @ Vector((0, 1.5, 0))]
|
||||
gpu.state.point_size_set(4)
|
||||
self.draw_batch("POINTS", points, decorator_color_selected)
|
||||
|
||||
verts = [Vector((0, 0, 0)), angle @ Vector((0, 1.5, 0))]
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", verts, decorator_color_selected, edges)
|
||||
|
||||
verts = [Vector((0, 1.5, 0)), Vector((-0.15, 1.35, 0)), Vector((0.15, 1.35, 0))]
|
||||
edges = [[0, 1], [0, 2]]
|
||||
verts = [angle @ v for v in verts]
|
||||
self.draw_batch("LINES", verts, decorator_color_selected, edges)
|
||||
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# If we have a Blender offset, project north may not be up
|
||||
if not tool.Cad.is_x(self.pn_angle, 0):
|
||||
arc_start = Vector((0, 1, 0))
|
||||
arc_end = angle @ arc_start
|
||||
angle_half = Matrix.Rotation(radians(self.pn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
arc_segments = tool.Cad.create_arc_segments(
|
||||
pts=[arc_start, arc_mid, arc_end], num_verts=12, make_edges=True
|
||||
)
|
||||
verts, edges = arc_segments
|
||||
self.draw_batch("LINES", verts, decorator_color_selected, edges)
|
||||
|
||||
if GeoreferenceData.data["coordinate_operation"]:
|
||||
points = [Vector((0, 0, 0)), Vector((0, 1, 0))]
|
||||
angle = Matrix.Rotation(radians(self.gn_angle), 4, "Z")
|
||||
points = [angle @ v for v in points]
|
||||
self.draw_batch("POINTS", points, decorator_color_special)
|
||||
|
||||
verts = [Vector((0, 0, 0)), Vector((0, 1, 0))]
|
||||
edges = [[0, 1]]
|
||||
angle = Matrix.Rotation(radians(self.gn_angle), 4, "Z")
|
||||
verts = [angle @ v for v in verts]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
verts = [Vector((0, 1, 0)), Vector((-0.15, 0.85, 0)), Vector((0.15, 0.85, 0))]
|
||||
edges = [[0, 1], [0, 2]]
|
||||
verts = [angle @ v for v in verts]
|
||||
self.line_shader.uniform_float("lineWidth", 5.0)
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
arc_start = Vector((0, 0.5, 0))
|
||||
arc_end = angle @ arc_start
|
||||
angle_half = Matrix.Rotation(radians(self.gn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
arc_segments = tool.Cad.create_arc_segments(
|
||||
pts=[arc_start, arc_mid, arc_end], num_verts=12, make_edges=True
|
||||
)
|
||||
verts, edges = arc_segments
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
if GeoreferenceData.data["true_north"]:
|
||||
points = [Vector((0, 0, 0)), Vector((0, 0.75, 0))]
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
points = [angle @ v for v in points]
|
||||
self.draw_batch("POINTS", points, decorator_color_special)
|
||||
|
||||
verts = [Vector((0, 0, 0)), Vector((0, 0.75, 0))]
|
||||
edges = [[0, 1]]
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
verts = [angle @ v for v in verts]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
verts = [Vector((0, 0.75, 0)), Vector((-0.15, 0.6, 0)), Vector((0.15, 0.6, 0))]
|
||||
edges = [[0, 1], [0, 2]]
|
||||
verts = [angle @ v for v in verts]
|
||||
self.line_shader.uniform_float("lineWidth", 5.0)
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
arc_start = Vector((0, 0.25, 0))
|
||||
arc_end = angle @ arc_start
|
||||
angle_half = Matrix.Rotation(radians(self.tn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
arc_segments = tool.Cad.create_arc_segments(
|
||||
pts=[arc_start, arc_mid, arc_end], num_verts=12, make_edges=True
|
||||
)
|
||||
verts, edges = arc_segments
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
if wcs_matrix:
|
||||
if wcs_matrix.translation.length < 1000:
|
||||
verts = [Vector((0, 0, 0)), wcs_matrix.translation]
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.draw_batch("POINTS", verts[1:], decorator_color_special)
|
||||
else:
|
||||
edges = [[0, 1]]
|
||||
verts = [Vector((0, 0, 0)), wcs_matrix.translation.normalized() * 3]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3, wcs_matrix.translation.normalized() * 3.1]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.12, wcs_matrix.translation.normalized() * 3.2]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.22, wcs_matrix.translation.normalized() * 3.3]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.32, wcs_matrix.translation.normalized() * 3.4]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
|
||||
def calculate_angles(self, context):
|
||||
self.pn_angle = 0.0
|
||||
self.gn_angle = float(GeoreferenceData.data["map_derived_angle"] or 0)
|
||||
self.tn_angle = float(GeoreferenceData.data["true_derived_angle"] or 0)
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
blender_angle = ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
|
||||
)
|
||||
self.pn_angle += blender_angle
|
||||
self.gn_angle += blender_angle
|
||||
self.tn_angle += blender_angle
|
||||
self.pn_angle = tool.Cad.normalise_angle(self.pn_angle)
|
||||
self.gn_angle = tool.Cad.normalise_angle(self.gn_angle)
|
||||
self.tn_angle = tool.Cad.normalise_angle(self.tn_angle)
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.georeference as core
|
||||
from blenderbim.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
|
||||
|
||||
class AddGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -29,7 +30,7 @@ class AddGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a new georeference"
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_georeferencing(tool.Ifc)
|
||||
core.add_georeferencing(tool.Georeference)
|
||||
|
||||
|
||||
class EnableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -106,21 +107,6 @@ class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.get_cursor_location(tool.Georeference)
|
||||
|
||||
|
||||
class SetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_cursor_location"
|
||||
bl_label = "Set Cursor Location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Move cursor location to the specified coordinates"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return tool.Ifc.get() and props.coordinate_output.count(",") == 2
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_cursor_location(tool.Georeference)
|
||||
|
||||
|
||||
class SetIfcTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_ifc_true_north"
|
||||
bl_label = "Set IFC True North"
|
||||
@@ -151,7 +137,7 @@ class ConvertLocalToGlobal(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def poll(cls, context):
|
||||
file = tool.Ifc.get()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and props.coordinate_input.count(",") == 2
|
||||
return file and props.local_coordinates.count(",") == 2
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_local_to_global(tool.Georeference)
|
||||
@@ -167,7 +153,7 @@ class ConvertGlobalToLocal(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def poll(cls, context):
|
||||
file = tool.Ifc.get()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and file.by_type("IfcUnitAssignment") and props.coordinate_input.count(",") == 2
|
||||
return file and file.by_type("IfcUnitAssignment") and props.local_coordinates.count(",") == 2
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_global_to_local(tool.Georeference)
|
||||
@@ -204,3 +190,73 @@ class ImportPlot(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class EnableEditingWCS(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_wcs"
|
||||
bl_label = "Enable Editing WCS"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Enable editing WCS"
|
||||
|
||||
def _execute(self, context):
|
||||
core.enable_editing_wcs(tool.Georeference)
|
||||
|
||||
|
||||
class EditWCS(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_wcs"
|
||||
bl_label = "Edit WCS"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Edit the WCS"
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_wcs(tool.Ifc, tool.Georeference)
|
||||
|
||||
|
||||
class DisableEditingWCS(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_wcs"
|
||||
bl_label = "Disable Editing WCS"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Close editing panel"
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_wcs(tool.Georeference)
|
||||
|
||||
|
||||
class EnableEditingTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_true_north"
|
||||
bl_label = "Enable Editing True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Enable editing True North"
|
||||
|
||||
def _execute(self, context):
|
||||
core.enable_editing_true_north(tool.Georeference)
|
||||
|
||||
|
||||
class EditTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_true_north"
|
||||
bl_label = "Edit True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Edit the True North"
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_true_north(tool.Ifc, tool.Georeference)
|
||||
|
||||
|
||||
class DisableEditingTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_true_north"
|
||||
bl_label = "Disable Editing True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Close editing panel"
|
||||
|
||||
def _execute(self, context):
|
||||
core.disable_editing_true_north(tool.Georeference)
|
||||
|
||||
|
||||
class RemoveTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_true_north"
|
||||
bl_label = "Remove True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Remove true north"
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_true_north(tool.Ifc)
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
import ifcopenshell.util.geolocation
|
||||
from blenderbim.bim.prop import Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -29,26 +30,132 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from blenderbim.bim.module.georeference.data import GeoreferenceData
|
||||
from blenderbim.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
|
||||
|
||||
def get_coordinate_operation_class(self, context):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
return GeoreferenceData.data["coordinate_operation_class"]
|
||||
|
||||
|
||||
def update_true_north_angle(self, context):
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
try:
|
||||
x, y = ifcopenshell.util.geolocation.angle2yaxis(float(self.true_north_angle))
|
||||
self.true_north_abscissa = str(x)
|
||||
self.true_north_ordinate = str(y)
|
||||
except:
|
||||
pass
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_true_north_vector(self, context):
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
try:
|
||||
x = float(self.true_north_abscissa)
|
||||
y = float(self.true_north_ordinate)
|
||||
self.true_north_angle = str(round(ifcopenshell.util.geolocation.yaxis2angle(x, y), 7))
|
||||
except:
|
||||
pass
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_angle(self, context):
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
try:
|
||||
x, y = ifcopenshell.util.geolocation.angle2xaxis(float(self.grid_north_angle))
|
||||
self.x_axis_abscissa = str(x)
|
||||
self.x_axis_ordinate = str(y)
|
||||
self.x_axis_is_null = False
|
||||
except:
|
||||
pass
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_vector(self, context):
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
try:
|
||||
x = float(self.x_axis_abscissa)
|
||||
y = float(self.x_axis_ordinate)
|
||||
self.grid_north_angle = str(round(ifcopenshell.util.geolocation.xaxis2angle(x, y), 7))
|
||||
self.x_axis_is_null = False
|
||||
except:
|
||||
pass
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_should_visualise(self, context):
|
||||
if self.should_visualise:
|
||||
GeoreferenceDecorator.install(bpy.context)
|
||||
else:
|
||||
GeoreferenceDecorator.uninstall()
|
||||
|
||||
|
||||
class BIMGeoreferenceProperties(PropertyGroup):
|
||||
coordinate_operation_class: bpy.props.EnumProperty(
|
||||
items=get_coordinate_operation_class, name="Coordinate Operation Class"
|
||||
)
|
||||
is_changing_angle: BoolProperty(name="Is Changing Angle", default=False)
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
map_conversion: CollectionProperty(name="Map Conversion", type=Attribute)
|
||||
is_editing_wcs: BoolProperty(name="Is Editing WCS")
|
||||
is_editing_true_north: BoolProperty(name="Is Editing True North")
|
||||
coordinate_operation: CollectionProperty(name="Coordinate Operation", type=Attribute)
|
||||
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
||||
coordinate_input: StringProperty(name="Coordinate Input", description='Formatted "x,y,z" (without quotes)')
|
||||
coordinate_output: StringProperty(name="Coordinate Output", description='Formatted "x,y,z" (without quotes)')
|
||||
angle_degree_input_x: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to Easting")
|
||||
angle_degree_input_y: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to +Y")
|
||||
x_axis_abscissa_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
|
||||
x_axis_ordinate_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
|
||||
y_axis_abscissa_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
|
||||
y_axis_ordinate_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
|
||||
local_coordinates: StringProperty(
|
||||
name="Local Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0"
|
||||
)
|
||||
map_coordinates: StringProperty(
|
||||
name="Map Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0"
|
||||
)
|
||||
should_visualise: BoolProperty(
|
||||
name="Should Visualise",
|
||||
description="Displays a visualisation of all georeferencing data at the origin",
|
||||
default=False,
|
||||
update=update_should_visualise,
|
||||
)
|
||||
grid_north_angle: StringProperty(name="Grid North Angle", update=update_grid_north_angle)
|
||||
x_axis_abscissa: StringProperty(name="X Axis Abscissa", update=update_grid_north_vector)
|
||||
x_axis_ordinate: StringProperty(name="X Axis Ordinate", update=update_grid_north_vector)
|
||||
x_axis_is_null: BoolProperty(name="X Axis Is Null")
|
||||
|
||||
# These are only for reference to capture data about a host model from a linked model
|
||||
# If you relink a model from a new host origin, we can autodetect it in theory with this
|
||||
host_model_origin: StringProperty(name="Host Model Origin")
|
||||
host_model_origin_si: StringProperty(name="Host Model Origin SI")
|
||||
host_model_project_north: StringProperty(name="Host Model Angle to Grid North")
|
||||
|
||||
# This is the ENH in project units and SI units of the Blender session's 0,0,0.
|
||||
# These are only for reference, using tool.Georeference.set_model_origin on
|
||||
# project load, project create, and when linking for the first time from an
|
||||
# empty Blender session.
|
||||
model_origin: StringProperty(name="Model Origin")
|
||||
model_origin_si: StringProperty(name="Model Origin SI")
|
||||
model_project_north: StringProperty(name="Model Angle to Grid North")
|
||||
|
||||
# True if there is a temporary Blender session coordinate system
|
||||
has_blender_offset: BoolProperty(name="Has Blender Offset")
|
||||
blender_eastings: StringProperty(name="Blender Eastings", default="0")
|
||||
blender_northings: StringProperty(name="Blender Northings", default="0")
|
||||
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0")
|
||||
|
||||
# These are the helmert transformation parameters of the Blender session
|
||||
blender_offset_x: StringProperty(name="Blender Offset X", default="0")
|
||||
blender_offset_y: StringProperty(name="Blender Offset Y", default="0")
|
||||
blender_offset_z: StringProperty(name="Blender Offset Z", default="0")
|
||||
blender_x_axis_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1")
|
||||
blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0")
|
||||
has_true_north: BoolProperty(name="Has True North", default=True)
|
||||
true_north_abscissa: StringProperty(name="True North Abscissa")
|
||||
true_north_ordinate: StringProperty(name="True North Ordinate")
|
||||
|
||||
true_north_angle: StringProperty(name="True North Angle", update=update_true_north_angle)
|
||||
true_north_abscissa: StringProperty(name="True North Abscissa", update=update_true_north_vector)
|
||||
true_north_ordinate: StringProperty(name="True North Ordinate", update=update_true_north_vector)
|
||||
wcs_x: StringProperty(name="WCS X", default="0")
|
||||
wcs_y: StringProperty(name="WCS Y", default="0")
|
||||
wcs_z: StringProperty(name="WCS Z", default="0")
|
||||
wcs_rotation: StringProperty(name="WCS Rotation", default="0")
|
||||
|
||||
@@ -40,8 +40,9 @@ class BIM_PT_gis(Panel):
|
||||
GeoreferenceData.load()
|
||||
|
||||
if props.is_editing:
|
||||
return self.draw_editable_ui(context)
|
||||
self.draw_ui(context)
|
||||
self.draw_editable_ui(context)
|
||||
else:
|
||||
self.draw_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
@@ -53,67 +54,53 @@ class BIM_PT_gis(Panel):
|
||||
draw_attributes(props.projected_crs, self.layout)
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Map Conversion", icon="GRID")
|
||||
row.label(text="Coordinate Operation", icon="GRID")
|
||||
|
||||
for attribute in props.map_conversion:
|
||||
if attribute.name == "Scale" and hasattr(context.scene, "sun_pos_properties"):
|
||||
for attribute in props.coordinate_operation:
|
||||
if attribute.name == "XAxisAbscissa":
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_grid_north", text="Set Blender North")
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="True North", icon="LIGHT_SUN")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "has_true_north")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "true_north_abscissa")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "true_north_ordinate")
|
||||
if hasattr(context.scene, "sun_pos_properties"):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_true_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_true_north", text="Set Blender North")
|
||||
row.prop(props, "grid_north_angle", text="Angle")
|
||||
row.prop(
|
||||
props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text=""
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "x_axis_abscissa", text="XAxis Abscissa")
|
||||
row.prop(
|
||||
props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text=""
|
||||
)
|
||||
elif attribute.name == "XAxisOrdinate":
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "x_axis_ordinate", text="XAxis Ordinate")
|
||||
row.prop(
|
||||
props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text=""
|
||||
)
|
||||
if hasattr(context.scene, "sun_pos_properties"):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_grid_north", text="Set Blender North")
|
||||
else:
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
def draw_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
row = self.layout.row()
|
||||
row.label(text="IFC2X3 Fallback In Use", icon="ERROR")
|
||||
row.label(text="IFC2X3 Fallback In Use", icon="INFO")
|
||||
|
||||
if not GeoreferenceData.data["projected_crs"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Not Georeferenced")
|
||||
row.label(text="Not Georeferenced", icon="ERROR")
|
||||
row.prop(props, "should_visualise", icon="HIDE_OFF", text="")
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "coordinate_operation_class", text="")
|
||||
row.operator("bim.add_georeferencing", icon="ADD", text="")
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row()
|
||||
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Eastings")
|
||||
row.label(text=props.blender_eastings)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Northings")
|
||||
row.label(text=props.blender_northings)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="OrthogonalHeight")
|
||||
row.label(text=props.blender_orthogonal_height)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="XAxisAbscissa")
|
||||
row.label(text=props.blender_x_axis_abscissa)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="XAxisOrdinate")
|
||||
row.label(text=props.blender_x_axis_ordinate)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Derived Grid North")
|
||||
row.label(text=GeoreferenceData.data["blender_derived_angle"])
|
||||
|
||||
if GeoreferenceData.data["projected_crs"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
row.prop(props, "should_visualise", icon="HIDE_OFF", text="")
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.remove_georeferencing", icon="X", text="")
|
||||
@@ -125,30 +112,173 @@ class BIM_PT_gis(Panel):
|
||||
row.label(text=key)
|
||||
row.label(text=str(value))
|
||||
|
||||
if GeoreferenceData.data["map_conversion"]:
|
||||
if GeoreferenceData.data["coordinate_operation"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Map Conversion", icon="GRID")
|
||||
row.label(text="Coordinate Operation", icon="GRID")
|
||||
|
||||
for key, value in GeoreferenceData.data["map_conversion"].items():
|
||||
for key, value in GeoreferenceData.data["coordinate_operation"].items():
|
||||
if value is None:
|
||||
continue
|
||||
if key == "type":
|
||||
key = "Type"
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=key)
|
||||
if key in ("Eastings", "Northings", "OrthogonalHeight"):
|
||||
row.label(text=f"{key} ({GeoreferenceData.data['map_unit_symbol']})")
|
||||
else:
|
||||
row.label(text=key)
|
||||
row.label(text=str(value))
|
||||
if key == "XAxisOrdinate":
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Derived Angle")
|
||||
row.label(text="*Angle to Grid North")
|
||||
row.label(text=GeoreferenceData.data["map_derived_angle"])
|
||||
|
||||
|
||||
class BIM_PT_gis_true_north(Panel):
|
||||
bl_idname = "BIM_PT_gis_true_north"
|
||||
bl_label = "True North"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_gis"
|
||||
|
||||
def draw(self, context):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
self.props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
if self.props.is_editing_true_north:
|
||||
self.draw_editable_ui(context)
|
||||
else:
|
||||
self.draw_ui()
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "true_north_angle", text="Angle")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "true_north_abscissa", text="Abscissa")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "true_north_ordinate", text="Ordinate")
|
||||
if hasattr(context.scene, "sun_pos_properties"):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_true_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_true_north", text="Set Blender North")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_true_north", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_true_north", icon="CANCEL", text="")
|
||||
|
||||
def draw_ui(self):
|
||||
if GeoreferenceData.data["true_north"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="True North", icon="LIGHT_SUN")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Vector")
|
||||
row.label(text=str(GeoreferenceData.data["true_north"][0:2])[1:-1])
|
||||
row.label(text="Abscissa")
|
||||
row.label(text=str(GeoreferenceData.data["true_north"][0]))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Derived Angle")
|
||||
row.label(text="Ordinate")
|
||||
row.label(text=str(GeoreferenceData.data["true_north"][1]))
|
||||
row.operator("bim.enable_editing_true_north", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.remove_true_north", icon="X", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="*Angle to True North")
|
||||
row.label(text=GeoreferenceData.data["true_derived_angle"])
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No True North Found", icon="LIGHT_SUN")
|
||||
row.operator("bim.enable_editing_true_north", icon="GREASEPENCIL", text="")
|
||||
|
||||
|
||||
class BIM_PT_gis_blender(Panel):
|
||||
bl_idname = "BIM_PT_gis_blender"
|
||||
bl_label = "Blender Coordinates"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_gis"
|
||||
|
||||
def draw(self, context):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row()
|
||||
row.label(text="Temporary Offset Is Active", icon="TRACKING_REFINE_FORWARDS")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Eastings ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=props.model_origin.split(",")[0])
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Northings ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=props.model_origin.split(",")[1])
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"OrthogonalHeight ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=props.model_origin.split(",")[2])
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Angle to Grid North")
|
||||
row.label(text=props.model_project_north)
|
||||
|
||||
|
||||
class BIM_PT_gis_wcs(Panel):
|
||||
bl_idname = "BIM_PT_gis_wcs"
|
||||
bl_label = "World Coordinate System"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_gis"
|
||||
|
||||
def draw(self, context):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
if props.is_editing_wcs:
|
||||
self.draw_editable_ui(context)
|
||||
else:
|
||||
self.draw_ui()
|
||||
|
||||
def draw_ui(self):
|
||||
if GeoreferenceData.data["world_coordinate_system"]["has_transformation"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Unrecommended Transformation Found", icon="ERROR")
|
||||
row.operator("bim.enable_editing_wcs", icon="GREASEPENCIL", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"X ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=str(GeoreferenceData.data["world_coordinate_system"]["x"]))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Y ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=str(GeoreferenceData.data["world_coordinate_system"]["y"]))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Z ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.label(text=str(GeoreferenceData.data["world_coordinate_system"]["z"]))
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Rotation")
|
||||
row.label(text=str(GeoreferenceData.data["world_coordinate_system"]["rotation"]))
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No WCS Transformation", icon="CHECKMARK")
|
||||
row.operator("bim.enable_editing_wcs", icon="GREASEPENCIL", text="")
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="World Coordinate System", icon="EMPTY_ARROWS")
|
||||
row.operator("bim.edit_wcs", icon="CHECKMARK", text="")
|
||||
row.operator("bim.disable_editing_wcs", icon="CANCEL", text="")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "wcs_x")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "wcs_y")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "wcs_z")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "wcs_rotation")
|
||||
|
||||
|
||||
class BIM_PT_gis_calculator(Panel):
|
||||
@@ -161,33 +291,17 @@ class BIM_PT_gis_calculator(Panel):
|
||||
bl_parent_id = "BIM_PT_gis"
|
||||
|
||||
def draw(self, context):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "coordinate_input", text="Input")
|
||||
row.prop(props, "local_coordinates", text=f"Local ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.operator("bim.get_cursor_location", text="", icon="TRACKER")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "coordinate_output", text="Output")
|
||||
row.operator("bim.set_cursor_location", text="", icon="TRACKER")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "map_coordinates", text=f"Map ({GeoreferenceData.data['map_unit_symbol']})")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.convert_local_to_global", text="Local to Global")
|
||||
row.operator("bim.convert_global_to_local", text="Global to Local")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Orientation Calculator", icon="TRACKING_REFINE_FORWARDS")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "angle_degree_input_x", text="Angle (°) rel to Easting")
|
||||
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_x"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "x_axis_abscissa_output", text="XAxis Abscissa")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "x_axis_ordinate_output", text="XAxis Ordinate")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "angle_degree_input_y", text="Angle (°) rel to to +Y")
|
||||
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_y"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "y_axis_abscissa_output", text="North Abscissa")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "y_axis_ordinate_output", text="North Ordinate")
|
||||
|
||||
@@ -24,14 +24,7 @@ import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddLibrary(bpy.types.Operator, Operator):
|
||||
class AddLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_library"
|
||||
bl_label = "Add Library"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -40,7 +33,7 @@ class AddLibrary(bpy.types.Operator, Operator):
|
||||
core.add_library(tool.Ifc)
|
||||
|
||||
|
||||
class RemoveLibrary(bpy.types.Operator, Operator):
|
||||
class RemoveLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_library"
|
||||
bl_label = "Remove Library"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -50,7 +43,7 @@ class RemoveLibrary(bpy.types.Operator, Operator):
|
||||
core.remove_library(tool.Ifc, library=tool.Ifc.get().by_id(self.library))
|
||||
|
||||
|
||||
class EnableEditingLibraryReferences(bpy.types.Operator, Operator):
|
||||
class EnableEditingLibraryReferences(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_library_references"
|
||||
bl_label = "Enable Editing Library References"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -60,7 +53,7 @@ class EnableEditingLibraryReferences(bpy.types.Operator, Operator):
|
||||
core.enable_editing_library_references(tool.Library, library=tool.Ifc.get().by_id(self.library))
|
||||
|
||||
|
||||
class DisableEditingLibraryReferences(bpy.types.Operator, Operator):
|
||||
class DisableEditingLibraryReferences(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_library_references"
|
||||
bl_label = "Disable Editing Library References"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -69,7 +62,7 @@ class DisableEditingLibraryReferences(bpy.types.Operator, Operator):
|
||||
core.disable_editing_library_references(tool.Library)
|
||||
|
||||
|
||||
class EnableEditingLibrary(bpy.types.Operator, Operator):
|
||||
class EnableEditingLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_library"
|
||||
bl_label = "Enable Editing Library"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -78,7 +71,7 @@ class EnableEditingLibrary(bpy.types.Operator, Operator):
|
||||
core.enable_editing_library(tool.Library)
|
||||
|
||||
|
||||
class DisableEditingLibrary(bpy.types.Operator, Operator):
|
||||
class DisableEditingLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_library"
|
||||
bl_label = "Disable Editing Library"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -87,7 +80,7 @@ class DisableEditingLibrary(bpy.types.Operator, Operator):
|
||||
core.disable_editing_library(tool.Library)
|
||||
|
||||
|
||||
class EditLibrary(bpy.types.Operator, Operator):
|
||||
class EditLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_library"
|
||||
bl_label = "Edit Library"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -96,7 +89,7 @@ class EditLibrary(bpy.types.Operator, Operator):
|
||||
core.edit_library(tool.Ifc, tool.Library)
|
||||
|
||||
|
||||
class AddLibraryReference(bpy.types.Operator, Operator):
|
||||
class AddLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_library_reference"
|
||||
bl_label = "Add Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -105,7 +98,7 @@ class AddLibraryReference(bpy.types.Operator, Operator):
|
||||
core.add_library_reference(tool.Ifc, tool.Library)
|
||||
|
||||
|
||||
class RemoveLibraryReference(bpy.types.Operator, Operator):
|
||||
class RemoveLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_library_reference"
|
||||
bl_label = "Remove Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -115,7 +108,7 @@ class RemoveLibraryReference(bpy.types.Operator, Operator):
|
||||
core.remove_library_reference(tool.Ifc, tool.Library, reference=tool.Ifc.get().by_id(self.reference))
|
||||
|
||||
|
||||
class EnableEditingLibraryReference(bpy.types.Operator, Operator):
|
||||
class EnableEditingLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_library_reference"
|
||||
bl_label = "Enable Editing Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -125,7 +118,7 @@ class EnableEditingLibraryReference(bpy.types.Operator, Operator):
|
||||
core.enable_editing_library_reference(tool.Library, reference=tool.Ifc.get().by_id(self.reference))
|
||||
|
||||
|
||||
class DisableEditingLibraryReference(bpy.types.Operator, Operator):
|
||||
class DisableEditingLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_library_reference"
|
||||
bl_label = "Disable Editing Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -134,7 +127,7 @@ class DisableEditingLibraryReference(bpy.types.Operator, Operator):
|
||||
core.disable_editing_library_reference(tool.Library)
|
||||
|
||||
|
||||
class EditLibraryReference(bpy.types.Operator, Operator):
|
||||
class EditLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_library_reference"
|
||||
bl_label = "Edit Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -143,7 +136,7 @@ class EditLibraryReference(bpy.types.Operator, Operator):
|
||||
core.edit_library_reference(tool.Ifc, tool.Library)
|
||||
|
||||
|
||||
class AssignLibraryReference(bpy.types.Operator, Operator):
|
||||
class AssignLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_library_reference"
|
||||
bl_label = "Assign Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -155,7 +148,7 @@ class AssignLibraryReference(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class UnassignLibraryReference(bpy.types.Operator, Operator):
|
||||
class UnassignLibraryReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_library_reference"
|
||||
bl_label = "Unassign Library Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# 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/>.
|
||||
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
|
||||
classes = (
|
||||
operator.ExportOBJ,
|
||||
operator.RadianceRender,
|
||||
prop.RadianceExporterProperties,
|
||||
ui.BIM_PT_radiance_exporter,
|
||||
)
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.radiance_exporter_properties = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.radiance_exporter_properties
|
||||
@@ -0,0 +1,237 @@
|
||||
# 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/>.
|
||||
|
||||
import os
|
||||
|
||||
try:
|
||||
import pyradiance as pr
|
||||
except ImportError:
|
||||
print("PyRadiance is not available. Rendering functionality will be disabled.")
|
||||
pr = None
|
||||
|
||||
import bpy
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional, Sequence
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import multiprocessing
|
||||
import shutil
|
||||
|
||||
|
||||
|
||||
class ExportOBJ(bpy.types.Operator):
|
||||
"""Exports the IFC File to OBJ"""
|
||||
bl_idname = "export_scene.radiance"
|
||||
bl_label = "Export"
|
||||
bl_description = "Export the IFC to OBJ"
|
||||
|
||||
def execute(self, context):
|
||||
# Get the resolution from the user input
|
||||
resolution_x, resolution_y = self.getResolution(context)
|
||||
quality = context.scene.radiance_exporter_properties.radiance_quality.upper()
|
||||
detail = context.scene.radiance_exporter_properties.radiance_detail.upper()
|
||||
variability = context.scene.radiance_exporter_properties.radiance_variability.upper()
|
||||
|
||||
# Calculate the aspect ratio
|
||||
aspect_ratio = resolution_x / resolution_y
|
||||
|
||||
# Get the blend file path and create the "Radiance Rendering" directory
|
||||
self.report({'INFO'}, "Exporting Radiance files...")
|
||||
blend_file_path = bpy.data.filepath
|
||||
if not blend_file_path:
|
||||
self.report({'ERROR'}, "Please save the Blender file before exporting.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
blend_file_dir = os.path.dirname(blend_file_path)
|
||||
radiance_dir = os.path.join(blend_file_dir, "RadianceRendering")
|
||||
|
||||
self.report({'INFO'}, "Radiance directory: {}".format(radiance_dir))
|
||||
|
||||
if not os.path.exists(radiance_dir):
|
||||
os.makedirs(radiance_dir)
|
||||
|
||||
# IFC file export and processing
|
||||
settings = ifcopenshell.geom.settings()
|
||||
# Settings for obj
|
||||
|
||||
serializer_settings = ifcopenshell.geom.serializer_settings()
|
||||
|
||||
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.SURFACES_AND_SOLIDS)
|
||||
settings.set("apply-default-materials", True)
|
||||
serializer_settings.set("use-element-guids", True)
|
||||
settings.set("use-world-coords", True)
|
||||
|
||||
ifc_file_name = context.scene.radiance_exporter_properties.ifc_file_name
|
||||
ifc_file_path = os.path.join(blend_file_dir, f"{ifc_file_name}.ifc")
|
||||
|
||||
if not os.path.exists(ifc_file_path):
|
||||
self.report({'ERROR'}, f"IFC file not found: {ifc_file_path}")
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
ifc_file = ifcopenshell.open(ifc_file_path)
|
||||
for material in ifc_file.by_type("IfcMaterial"):
|
||||
self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}")
|
||||
|
||||
obj_file_path = os.path.join(radiance_dir, "model.obj")
|
||||
mtl_file_path = os.path.join(radiance_dir, "model.mtl")
|
||||
|
||||
|
||||
# serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, ifcopenshell.geom.serializer_settings())
|
||||
serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, serializer_settings)
|
||||
serialiser.setFile(ifc_file)
|
||||
serialiser.setUnitNameAndMagnitude("METER", 1.0)
|
||||
serialiser.writeHeader()
|
||||
|
||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
serialiser.write(iterator.get())
|
||||
if not iterator.next():
|
||||
break
|
||||
serialiser.finalize()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def getResolution(self, context):
|
||||
scene = context.scene
|
||||
props = scene.radiance_exporter_properties
|
||||
resolution_x = props.radiance_resolution_x
|
||||
resolution_y = props.radiance_resolution_y
|
||||
return resolution_x, resolution_y
|
||||
|
||||
|
||||
class RadianceRender(bpy.types.Operator):
|
||||
"""Radiance Rendering"""
|
||||
bl_idname = "render_scene.radiance"
|
||||
bl_label = "Render"
|
||||
bl_description = "Renders the scene using Radiance"
|
||||
|
||||
def execute(self, context):
|
||||
if pr is None:
|
||||
self.report({'ERROR'}, "PyRadiance is not available. Cannot perform rendering.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
# Get the resolution from the user input
|
||||
resolution_x, resolution_y = self.getResolution(context)
|
||||
quality = context.scene.radiance_exporter_properties.radiance_quality.upper()
|
||||
detail = context.scene.radiance_exporter_properties.radiance_detail.upper()
|
||||
variability = context.scene.radiance_exporter_properties.radiance_variability.upper()
|
||||
|
||||
# Get the blend file path and create the "Radiance Rendering" directory
|
||||
blend_file_path = bpy.data.filepath
|
||||
if not blend_file_path:
|
||||
self.report({'ERROR'}, "Please save the Blender file before rendering.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
blend_file_dir = os.path.dirname(blend_file_path)
|
||||
radiance_dir = os.path.join(blend_file_dir, "RadianceRendering")
|
||||
|
||||
|
||||
# Material processing
|
||||
style = []
|
||||
obj_file_path = os.path.join(radiance_dir, "model.obj")
|
||||
with open(obj_file_path, "r") as obj_file:
|
||||
for line in obj_file:
|
||||
if line.startswith("usemtl"):
|
||||
l = line.strip().split(" ")
|
||||
style.append(l[1])
|
||||
|
||||
# json_file_path = os.path.join(blend_file_dir, "material_mapping.json")
|
||||
|
||||
json_file_path = context.scene.radiance_exporter_properties.json_file_path
|
||||
self.report({'INFO'}, f"Selected JSON file: {json_file_path}")
|
||||
if json_file_path:
|
||||
# self.report({'INFO'}, f"Selected JSON file: {json_file_name}")
|
||||
json_dest_path = os.path.join(blend_file_dir, json_file_path.split("\\")[-1])
|
||||
shutil.copy(json_file_path, json_dest_path)
|
||||
self.report({'INFO'}, f"JSON file saved to: {json_dest_path}")
|
||||
else:
|
||||
self.report({'WARNING'}, "No JSON file selected")
|
||||
|
||||
|
||||
with open(json_file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
|
||||
# Create materials.rad file
|
||||
materials_file = os.path.join(radiance_dir, "materials.rad")
|
||||
with open(materials_file, "w") as file:
|
||||
file.write("void plastic white\n0\n0\n5 1 1 1 0 0\n")
|
||||
file.write("void plastic blue_plastic\n0\n0\n5 0.1 0.2 0.8 0.05 0.1\n")
|
||||
file.write("void plastic red_plastic\n0\n0\n5 0.8 0.1 0.2 0.05 0.1\n")
|
||||
file.write("void metal silver_metal\n0\n0\n5 0.8 0.8 0.8 0.9 0.1\n")
|
||||
file.write("void glass clear_glass\n0\n0\n3 0.96 0.96 0.96\n")
|
||||
file.write("void light white_light\n0\n0\n3 1.0 1.0 1.0\n")
|
||||
file.write("void trans olive_trans\n0\n0\n7 0.6 0.7 0.4 0.05 0.05 0.7 0.2\n")
|
||||
|
||||
for i in set(style):
|
||||
file.write("inherit alias " + i + " " + data.get(i, "white") + "\n")
|
||||
|
||||
self.report({'INFO'}, "Exported Materials Rad file to: {}".format(materials_file))
|
||||
|
||||
# Run obj2mesh
|
||||
rtm_file_path = os.path.join(radiance_dir, "model.rtm")
|
||||
mesh_file_path = save_obj2mesh_output(obj_file_path, rtm_file_path, matfiles=[materials_file])
|
||||
# subprocess.run(["obj2mesh", "-a", materials_file, obj_file_path, rtm_file_path])
|
||||
self.report({'INFO'}, "obj2mesh output: {}".format(mesh_file_path))
|
||||
scene_file = os.path.join(radiance_dir, "scene.rad")
|
||||
with open(scene_file, "w") as file:
|
||||
file.write("void mesh model\n1 " + rtm_file_path + "\n0\n0\n")
|
||||
|
||||
self.report({'INFO'}, "Exported Scene file to: {}".format(scene_file))
|
||||
|
||||
# Py Radiance Rendering code
|
||||
scene = pr.Scene("ascene")
|
||||
|
||||
material_path = os.path.join(radiance_dir, "materials.rad")
|
||||
scene_path = os.path.join(radiance_dir, "scene.rad")
|
||||
|
||||
scene.add_material(material_path)
|
||||
scene.add_surface(scene_path)
|
||||
|
||||
aview = pr.View(position=(1, 1.5, 1), direction=(1, 0, 0))
|
||||
scene.add_view(aview)
|
||||
|
||||
octpath = os.path.join(blend_file_dir, "ascene.oct")
|
||||
print("Reached here")
|
||||
image = pr.render(scene, ambbounce=1, resolution=(resolution_x, resolution_y),
|
||||
quality=quality, detail=detail, variability=variability)
|
||||
|
||||
raw_hdr_path = os.path.join(radiance_dir, "raw.hdr")
|
||||
with open(raw_hdr_path, "wb") as wtr:
|
||||
wtr.write(image)
|
||||
|
||||
self.report({'INFO'}, "Radiance rendering completed. Output: {}".format(raw_hdr_path))
|
||||
return {'FINISHED'}
|
||||
|
||||
def getResolution(self, context):
|
||||
scene = context.scene
|
||||
props = scene.radiance_exporter_properties
|
||||
resolution_x = props.radiance_resolution_x
|
||||
resolution_y = props.radiance_resolution_y
|
||||
return resolution_x, resolution_y
|
||||
|
||||
def save_obj2mesh_output(inp: Union[bytes, str, Path], output_file: str, **kwargs):
|
||||
output_bytes = pr.obj2mesh(inp, **kwargs)
|
||||
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(output_bytes)
|
||||
return output_file
|
||||
@@ -0,0 +1,87 @@
|
||||
# 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/>.
|
||||
|
||||
import bpy
|
||||
from bpy.props import IntProperty, StringProperty, EnumProperty
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
class RadianceExporterProperties(PropertyGroup):
|
||||
|
||||
def update_json_file_path(self, context):
|
||||
if self.json_file_path:
|
||||
self.json_file_path = bpy.path.abspath(self.json_file_path)
|
||||
|
||||
|
||||
|
||||
|
||||
radiance_resolution_x: IntProperty(
|
||||
name="X",
|
||||
description="Horizontal resolution of the output image",
|
||||
default=1920,
|
||||
min=1
|
||||
)
|
||||
radiance_resolution_y: IntProperty(
|
||||
name="Y",
|
||||
description="Vertical resolution of the output image",
|
||||
default=1080,
|
||||
min=1
|
||||
)
|
||||
ifc_file_name: StringProperty(
|
||||
name="IFC File Name",
|
||||
description="Name of the IFC file to use (without .ifc extension)",
|
||||
default=""
|
||||
)
|
||||
|
||||
json_file_path: StringProperty(
|
||||
name="JSON File",
|
||||
description="Path to the JSON file",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
update=lambda self, context: self.update_json_file_path(context)
|
||||
)
|
||||
|
||||
radiance_quality: EnumProperty(
|
||||
name="Quality",
|
||||
description="Radiance rendering quality",
|
||||
items=[
|
||||
('LOW', "Low", "Low quality"),
|
||||
('MEDIUM', "Medium", "Medium quality"),
|
||||
('HIGH', "High", "High quality")
|
||||
],
|
||||
default='MEDIUM'
|
||||
)
|
||||
radiance_detail: EnumProperty(
|
||||
name="Detail",
|
||||
description="Radiance rendering detail",
|
||||
items=[
|
||||
('LOW', "Low", "Low detail"),
|
||||
('MEDIUM', "Medium", "Medium detail"),
|
||||
('HIGH', "High", "High detail")
|
||||
],
|
||||
default='MEDIUM'
|
||||
)
|
||||
radiance_variability: EnumProperty(
|
||||
name="Variability",
|
||||
description="Radiance rendering variability",
|
||||
items=[
|
||||
('LOW', "Low", "Low variability"),
|
||||
('MEDIUM', "Medium", "Medium variability"),
|
||||
('HIGH', "High", "High variability")
|
||||
],
|
||||
default='MEDIUM'
|
||||
)
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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/>.
|
||||
|
||||
import bpy
|
||||
|
||||
class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
"""Creates a Panel in the render properties window"""
|
||||
bl_label = "Radiance Exporter"
|
||||
bl_idname = "BIM_PT_radiance_exporter"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_services"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
|
||||
props = scene.radiance_exporter_properties
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "ifc_file_name")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "json_file_path")
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Resolution")
|
||||
row.prop(props, "radiance_resolution_x", text="X")
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="")
|
||||
row.prop(props, "radiance_resolution_y", text="Y")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_quality")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_detail")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_variability")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("export_scene.radiance", text="Export to OBJ")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("render_scene.radiance", text="Radiance Render")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user