Compare commits
446 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e072870fd3 | |||
| af4475fe46 | |||
| eb3104367a | |||
| 90f9ebd25e | |||
| f2554bc968 | |||
| 53a78f38e5 | |||
| b476cb825c | |||
| ec21574be5 | |||
| b552665dd9 | |||
| 48b630696f | |||
| 8c94ef6e54 | |||
| bdcadfd100 | |||
| 90e83d77a7 | |||
| 2e80363bac | |||
| b6f80eabb4 | |||
| c93e52089b | |||
| 76ba3947f5 | |||
| 50cb09aac2 | |||
| dac1aaa7f3 | |||
| eed603bbcd | |||
| 1d62f6f736 | |||
| 799cf47316 | |||
| 03935a91da | |||
| 37baa9235a | |||
| b4b2b60935 | |||
| a11c7535d8 | |||
| e8d6dd333c | |||
| 9633d92c17 | |||
| afd5a5b23e | |||
| 15481c0a59 | |||
| 32b37b8823 | |||
| f3ab214752 | |||
| 4a59b30f1b | |||
| 87d6960356 | |||
| aff893e690 | |||
| 486eb259ea | |||
| 8164b496f8 | |||
| fda23f350a | |||
| 711c597065 | |||
| 80a396dc12 | |||
| 2b70f12d44 | |||
| c3bcc2d90c | |||
| 88e5a51f01 | |||
| 8b8af680bc | |||
| 8a57a52949 | |||
| 720eda51c3 | |||
| 01a701007e | |||
| ec5368224f | |||
| 56e9488d3e | |||
| 23ec2fc48e | |||
| fbcb19fdbb | |||
| d9f3c9d012 | |||
| 91e236c42c | |||
| 03ab88b5bb | |||
| 42ef06aae1 | |||
| 691dfb45f0 | |||
| 760816b7f0 | |||
| fc04c6e41e | |||
| 99e67d528f | |||
| c113061742 | |||
| 174ff00a6d | |||
| 84a2d4266d | |||
| 64cba98c3d | |||
| 1aed2a6b3c | |||
| 4e1a956830 | |||
| 747a39b440 | |||
| 1b49515d9f | |||
| dabb85c466 | |||
| b74811b3f4 | |||
| 79eaf895f7 | |||
| 994c85a05a | |||
| 0faacccf6d | |||
| 89d27bdf5f | |||
| 76853db32c | |||
| 002c50e29d | |||
| d2efb2ab78 | |||
| 8bb78959ef | |||
| 48f0dc6a00 | |||
| 27aee31adf | |||
| 24aaefb927 | |||
| 7daec25454 | |||
| 8309b13db0 | |||
| 5e95985d19 | |||
| fefe325ece | |||
| b717abf639 | |||
| 32ae12b45a | |||
| 48b74ebb76 | |||
| 197d0b3258 | |||
| 2a1bc30f19 | |||
| 6dcbd98769 | |||
| 1be025ed86 | |||
| 6fa87a16ab | |||
| 8c19b695ea | |||
| 5831c56380 | |||
| 3dde1b80b9 | |||
| 08dd8f87ff | |||
| 1c3cf44122 | |||
| 868454c011 | |||
| edcfcd8850 | |||
| 33bbf9fc53 | |||
| 286e90e299 | |||
| 5aa7758795 | |||
| 6c47baf0de | |||
| 51018177cd | |||
| be9719f5ca | |||
| 9193ff7252 | |||
| 53c3019b73 | |||
| 6bbf3d3917 | |||
| 47a15011f6 | |||
| f26f6dfe5e | |||
| 1745a4fd39 | |||
| fcaac3787b | |||
| 2d3adce59e | |||
| 7e6607adcf | |||
| 46c62d7cdb | |||
| c521892ca2 | |||
| d4a0dd1527 | |||
| 527cdd3304 | |||
| 31e836cfb4 | |||
| 86f123b6b0 | |||
| fea8e3a5c9 | |||
| 3137904055 | |||
| f89dee7668 | |||
| c042d1afe5 | |||
| 42fc72bd4f | |||
| f72c128ba6 | |||
| 3ceeaa739f | |||
| 7a2e58944e | |||
| b9855b289f | |||
| c7a8fd9309 | |||
| daa6b31c5d | |||
| cd411e1f14 | |||
| 7a168069f7 | |||
| 97d2e08286 | |||
| f440a3ca17 | |||
| 81bdb551ab | |||
| 5fdad744d3 | |||
| ab6f408db4 | |||
| cdf8b9c038 | |||
| 918fb41a7e | |||
| 1d8e39e55f | |||
| b0b65e550c | |||
| a1527a7e31 | |||
| 2ca3f86fc9 | |||
| d80bcd1a94 | |||
| 60a06138d2 | |||
| e2001e82dd | |||
| 92f4cb0ffa | |||
| 9f00e8aef0 | |||
| 638d200a27 | |||
| 59717d6e92 | |||
| 009795f7dc | |||
| a0792a4b93 | |||
| 8534b6e154 | |||
| 7910a68028 | |||
| d44ee9874b | |||
| 8cd86581ff | |||
| 0a5b6a41b0 | |||
| 7bac468151 | |||
| b375579dcd | |||
| d3d1ca0169 | |||
| 2b0572af10 | |||
| ed6365b292 | |||
| e3b1e0773f | |||
| 3d38068b90 | |||
| a4a2497f33 | |||
| 1ef2fb89af | |||
| 902d777574 | |||
| 3bd0caade1 | |||
| 301cb4bbdc | |||
| f0338ffdda | |||
| 2100528cbe | |||
| ae4f681f73 | |||
| 53d7624fc4 | |||
| 951b2b76a6 | |||
| e9b4b6560e | |||
| cabffdc549 | |||
| 7ea60742d6 | |||
| 358c834427 | |||
| 5d6655cbc7 | |||
| 4d122d77dc | |||
| d4e863ed3b | |||
| b6ccf505e7 | |||
| ce9842983b | |||
| eb19b26a6e | |||
| 06602d0e39 | |||
| 00279b63de | |||
| 6418353119 | |||
| 3afbbe51d2 | |||
| ddb767674b | |||
| 5575ea8296 | |||
| e8a7a032aa | |||
| f23e419685 | |||
| b87e8136af | |||
| 473d0ae888 | |||
| 94f5137684 | |||
| 4e75945435 | |||
| e31019eb67 | |||
| c505630a20 | |||
| 860fa81f73 | |||
| 847b5d226e | |||
| ad77d09916 | |||
| 9b12cae8b4 | |||
| 84a8c5dfeb | |||
| 12a7b8a79e | |||
| ef04402ed8 | |||
| 45fed747c2 | |||
| 85229fc865 | |||
| 916f854905 | |||
| 1915541d20 | |||
| e37b84a95a | |||
| bcb2a37ce0 | |||
| 018d6847a0 | |||
| 35918265d0 | |||
| 4e48c35425 | |||
| d985fe9240 | |||
| 7c00021aaa | |||
| d2dc8b519f | |||
| 25fe025354 | |||
| 1aae710286 | |||
| 38e8130793 | |||
| 7e94388074 | |||
| ef835e43b8 | |||
| e5d0eb13bf | |||
| 4fcf23319f | |||
| 99154afdb1 | |||
| e83eb68fe1 | |||
| e4a4a33920 | |||
| e8848c9701 | |||
| cfd8044cb3 | |||
| 2d7866570e | |||
| eb0ed7fe6b | |||
| 67733c4449 | |||
| b3940cdc45 | |||
| 94e75aebb0 | |||
| ff203b2a13 | |||
| 10616df494 | |||
| 1dc6d4935e | |||
| 04088af2b6 | |||
| c477173096 | |||
| 2f4093f958 | |||
| 95fc51da76 | |||
| 50e629c02c | |||
| 4a178fca58 | |||
| aff5b238d9 | |||
| 315b6060b0 | |||
| b137197586 | |||
| d1d9db3ed8 | |||
| 736b50c6fa | |||
| 64b96633c2 | |||
| 8eae997053 | |||
| fee56fc120 | |||
| d1f87d5467 | |||
| 7539404c27 | |||
| 71ddc6384a | |||
| 4b402f8ea5 | |||
| c39fa80e09 | |||
| c89ad8c7da | |||
| 6dfe46d9bd | |||
| 1de51ecf77 | |||
| 8a55f18c95 | |||
| 7a1976cfc1 | |||
| 78c6401453 | |||
| e12838d93e | |||
| 7aa5b71a6a | |||
| 2ef3af5e1b | |||
| d399f7c6b7 | |||
| beb7f75368 | |||
| dc6fdc2a98 | |||
| 45be17c25a | |||
| 4f11b19b3d | |||
| f646cf4372 | |||
| 538859f60a | |||
| 75da994faf | |||
| 99827df89a | |||
| 054a900779 | |||
| e800f8b93d | |||
| 7163697362 | |||
| 29600711ae | |||
| 1d0ee61ba7 | |||
| fb909ace1a | |||
| 6ae5dbacf4 | |||
| 52bffa7f9c | |||
| e9a502ccec | |||
| f3d4eed19e | |||
| d260298ab2 | |||
| c7d0cff300 | |||
| 631deeb094 | |||
| 953a49f9ae | |||
| 2e94a7c3f1 | |||
| 68f10e1502 | |||
| 625a57c6de | |||
| 4348b9bff9 | |||
| 28ee1a7781 | |||
| d40119d617 | |||
| 774533911c | |||
| 7597b1b29c | |||
| bd8a2736d4 | |||
| aafb4b6147 | |||
| 2066ebcc11 | |||
| 795ebc0ac1 | |||
| 7612cf92c0 | |||
| db1f4d126a | |||
| b15820dd10 | |||
| f8f7884278 | |||
| 3318de7f6d | |||
| 726bf40d58 | |||
| 3860884db3 | |||
| 5784e3103d | |||
| 2a1d1d6e10 | |||
| 485643863f | |||
| c03777cfe6 | |||
| be442ff1be | |||
| 95e7910f69 | |||
| d5592ed2eb | |||
| a29806ef9e | |||
| 3a9d79dcd3 | |||
| b9c2e312e3 | |||
| f1db51df1a | |||
| 312a58ddce | |||
| 1e71708aba | |||
| ad6e00571e | |||
| c8d05951b0 | |||
| 0f8c3b3c83 | |||
| 25071dfec6 | |||
| cc28f5a92b | |||
| a57a7041ac | |||
| 17b90c3241 | |||
| 90089f91c6 | |||
| 37dd2a5607 | |||
| bc70130123 | |||
| 8d53141d5a | |||
| 0ffc62fbf3 | |||
| 4350e3df82 | |||
| 2ad601ed34 | |||
| f9e8d6d478 | |||
| 65cc5bf4c1 | |||
| 73e5985777 | |||
| de14e92720 | |||
| 09ad47d11b | |||
| debe63c1af | |||
| 6f2eb6c54a | |||
| 56ca431965 | |||
| 74e8b80fb4 | |||
| 7ecfe372ad | |||
| b017ab7596 | |||
| bd3905fe76 | |||
| 0c8048c14d | |||
| 77f72ecdfa | |||
| 723701f1b8 | |||
| bf96c63f7d | |||
| cef8251458 | |||
| 547981f32c | |||
| a3cc0245ca | |||
| 5d934a2baa | |||
| 4409a35441 | |||
| f637ef8016 | |||
| 5e2cefee12 | |||
| 67b01dcd0f | |||
| bc0efd7389 | |||
| 4b4e6fa7e2 | |||
| 17a0face6e | |||
| a3b2860789 | |||
| f8f22257b4 | |||
| 8284ed0cdb | |||
| d995e010cf | |||
| fcfc070825 | |||
| 55552770a6 | |||
| 350df4eee5 | |||
| 04a273c720 | |||
| 61641acf56 | |||
| 10035662dd | |||
| 056df7758b | |||
| 0522adac63 | |||
| 1f781e5cab | |||
| a30fd7cab4 | |||
| 94ca2423e9 | |||
| 2938c1fd0b | |||
| ab8710bd1e | |||
| a791abe23e | |||
| e70122f354 | |||
| 96fdced471 | |||
| 1c95f6cc3f | |||
| 0f03e412c3 | |||
| 6ca3e2a6db | |||
| 079446a4b6 | |||
| e706e568bd | |||
| 526a2a6a80 | |||
| 3e7a2c1b83 | |||
| 9bb04d3ffc | |||
| 7c4d45ab32 | |||
| dafec70097 | |||
| eca1d47d06 | |||
| 9671be1734 | |||
| 644f32d11c | |||
| 1626dd85f4 | |||
| 3c0103b4da | |||
| f9c4b6a98f | |||
| b01e93161e | |||
| ffbd3e50c5 | |||
| 98c4a1d110 | |||
| e2c4bcb217 | |||
| 7e60b6888e | |||
| bbf8ebcb52 | |||
| c1936b871e | |||
| 31d67b5e62 | |||
| 683e168ca1 | |||
| cbc0b4837f | |||
| 02ff6c00ef | |||
| 78f664e892 | |||
| d745265048 | |||
| 917991feab | |||
| 4877ab4426 | |||
| 48c5100d3b | |||
| 72caf7525e | |||
| 242d91bc17 | |||
| afb22ce7d5 | |||
| e45a670ff5 | |||
| 45042aa5e0 | |||
| 07aec0730f | |||
| 7e6da3edb3 | |||
| 4f0eafa92d | |||
| de75eded09 | |||
| f2b4bea4de | |||
| 4f28b2506d | |||
| 281da8ed77 | |||
| bdf4258895 | |||
| 385d9b9df7 | |||
| 0e40df4fa3 | |||
| b0c132f73a | |||
| 4fd802bf3d | |||
| e461040e17 | |||
| 43b574011e | |||
| 9bae914b51 | |||
| 945070bdff | |||
| d2b4afe1be | |||
| 3760f8fafe | |||
| b9e5cefddf | |||
| 9f5f757ccf | |||
| 3a147d7f62 | |||
| 9f1d14e5a7 | |||
| bce71f3797 | |||
| 253dca730d | |||
| e0f33a56ec | |||
| 2287931e9c | |||
| 1f7a498606 |
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v2 # https://github.com/actions/checkout
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
python-version: '3.10' # Version range or exact version of a Python version to use, using SemVer's version range syntax
|
||||
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current date
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
name: ci-blenderbim-daily
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-blenderbim-daily.yml'
|
||||
- 'src/blenderbim/**'
|
||||
- 'src/ifcopenshell-python/ifcopenshell/**'
|
||||
- 'src/bcf/bcf/**'
|
||||
- 'src/ifcclash/ifcclash/**'
|
||||
- 'src/ifccobie/**'
|
||||
- 'src/ifcdiff/**'
|
||||
- 'src/ifccsv/**'
|
||||
- 'src/ifcpatch/ifcpatch/**'
|
||||
- 'src/ifc4d/ifc4d/**'
|
||||
- 'src/ifc5d/ifc5d/**'
|
||||
- 'src/ifccityjson/**'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux,
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos,
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm1,
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- 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: Compile
|
||||
run: |
|
||||
cd src/blenderbim && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
- name: Find zip file name
|
||||
id: find_zip
|
||||
run: |
|
||||
filepath=$(ls src/blenderbim/dist/blenderbim_*.zip)
|
||||
echo "filepath=$filepath" >> $GITHUB_OUTPUT
|
||||
echo "filename=$(basename $filepath)" >> $GITHUB_OUTPUT
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.find_zip.outputs.filepath }}
|
||||
asset_name: ${{ steps.find_zip.outputs.filename }}
|
||||
release_name: "blenderbim-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "blenderbim-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish-blenderbim-chocolatey package
|
||||
name: ci-bonsai-choco
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -9,11 +9,12 @@ on:
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# * * * * *
|
||||
- cron: "30 0 * * *" # 30min past utc midnight
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: blenderbim
|
||||
name: bonsai
|
||||
choco_version: 1.1.0
|
||||
CHOCO_TOKEN: ${{ secrets.CHOCO_TOKEN }}
|
||||
|
||||
@@ -41,5 +42,6 @@ jobs:
|
||||
- name: Check in release tags if we should do a choco release and perform the release if needed
|
||||
id: do_choco_release
|
||||
run: |
|
||||
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/ &&
|
||||
pip install pygithub
|
||||
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/bonsai/ &&
|
||||
python3 choco_release.py
|
||||
@@ -0,0 +1,155 @@
|
||||
name: ci-bonsai-daily
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-bonsai-daily.yml'
|
||||
- 'src/bonsai/**'
|
||||
- 'src/ifcopenshell-python/ifcopenshell/**'
|
||||
- 'src/bcf/bcf/**'
|
||||
- 'src/ifcclash/ifcclash/**'
|
||||
- 'src/ifccobie/**'
|
||||
- 'src/ifcdiff/**'
|
||||
- 'src/ifccsv/**'
|
||||
- 'src/ifcpatch/ifcpatch/**'
|
||||
- 'src/ifc4d/ifc4d/**'
|
||||
- 'src/ifc5d/ifc5d/**'
|
||||
- 'src/ifccityjson/**'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux,
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos,
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm1,
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- 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: Compile
|
||||
run: |
|
||||
cd src/bonsai && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
- name: Find zip file name
|
||||
id: find_zip
|
||||
run: |
|
||||
filepath=$(ls src/bonsai/dist/bonsai_*.zip)
|
||||
echo "filepath=$filepath" >> $GITHUB_OUTPUT
|
||||
echo "filename=$(basename $filepath)" >> $GITHUB_OUTPUT
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.find_zip.outputs.filepath }}
|
||||
asset_name: ${{ steps.find_zip.outputs.filename }}
|
||||
release_name: "bonsai-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "bonsai-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
body: "See README in https://github.com/IfcOpenShell/bonsai_unstable_repo/ on how to setup autoupdates for daily Bonsai builds."
|
||||
|
||||
update-extensions-repo-and-run-tests:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout bonsai_unstable_repo repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: IfcOpenShell/bonsai_unstable_repo
|
||||
token: ${{ secrets.IOS_TO_BLENDER_REPO }}
|
||||
path: bonsai_unstable_repo
|
||||
|
||||
- name: Update index.json on extensions repo
|
||||
run: |
|
||||
set -x -e
|
||||
|
||||
# Download Blender.
|
||||
wget -q -O blender.tar.xz https://ftp.nluug.nl/pub/graphics/blender/release/Blender4.2/blender-4.2.0-linux-x64.tar.xz
|
||||
tar -xf blender.tar.xz
|
||||
|
||||
# Setup Blender.
|
||||
BLENDER_PATH=$(find blender-*/ -maxdepth 0 -exec readlink -f {} \;)
|
||||
export PATH="$PATH:$BLENDER_PATH"
|
||||
blender --version
|
||||
|
||||
cd bonsai_unstable_repo
|
||||
pip install -r requirements.txt
|
||||
python setup_extensions_repo.py --last-tag
|
||||
git config --global user.name 'IfcOpenBot'
|
||||
git config --global user.email 'IfcOpenBot@users.noreply.github.com'
|
||||
git add index.json
|
||||
git add readme.md
|
||||
git commit -m "Update index.json"
|
||||
git push
|
||||
- name: Run bonsai tests
|
||||
run: |
|
||||
set -x -e
|
||||
BLENDER_PATH=$(find blender-*/ -maxdepth 0 -exec readlink -f {} \;)
|
||||
export PATH="$PATH:$BLENDER_PATH"
|
||||
bonsai_zip="$(pwd)/$(ls bonsai_unstable_repo/bonsai_py311*-linux-x64.zip)"
|
||||
blender --version
|
||||
blender --command extension install-file -r user_default -e $bonsai_zip
|
||||
blender --command extension list
|
||||
|
||||
wget -q -O sverchok.zip https://github.com/nortikin/sverchok/archive/refs/heads/master.zip
|
||||
# ifcsverchok expecting sverchok to be named "sverchok" and not "sverchok-master".
|
||||
unzip -q sverchok.zip
|
||||
mv sverchok-master sverchok
|
||||
zip -q -r sverchok.zip sverchok
|
||||
rm -r sverchok
|
||||
blender --command extension install-file -r user_default sverchok.zip
|
||||
|
||||
git clone https://github.com/IfcOpenShell/IfcOpenShell.git IfcOpenShell
|
||||
cd IfcOpenShell/src/ifcsverchok
|
||||
make dist
|
||||
sverchok_zip="$(pwd)/dist/$(ls dist)"
|
||||
blender --command extension install-file -r user_default $sverchok_zip
|
||||
|
||||
# Install Sun Position extension.
|
||||
blender --online-mode --command extension sync
|
||||
blender --online-mode --background --python-expr "import bpy; \
|
||||
bpy.ops.extensions.package_install(repo_index=0, pkg_id='sun_position'); \
|
||||
bpy.ops.preferences.addon_enable(module='bl_ext.blender_org.sun_position'); bpy.ops.wm.save_userpref()"
|
||||
|
||||
cd ../bonsai
|
||||
pip install pytest-blender
|
||||
blender --background --python scripts/setup_pytest.py
|
||||
blender --python-expr "import bonsai; print(bonsai.bbim_semver); import ifcopenshell; print(ifcopenshell.version)" --background
|
||||
make test
|
||||
@@ -1,6 +1,6 @@
|
||||
name: ci-blenderbim
|
||||
name: ci-bonsai
|
||||
|
||||
# Differences from ci-blenderbim-daily.yml:
|
||||
# Differences from ci-bonsai-daily.yml:
|
||||
# - make has IS_STABLE=TRUE
|
||||
# - action is never triggered and executed only manually
|
||||
# - doesn't add a current date to the release and tag
|
||||
@@ -53,12 +53,12 @@ jobs:
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cd src/blenderbim &&
|
||||
cd src/bonsai &&
|
||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE
|
||||
- name: Find zip file name
|
||||
id: find_zip
|
||||
run: |
|
||||
filepath=$(ls src/blenderbim/dist/blenderbim_*.zip)
|
||||
filepath=$(ls src/bonsai/dist/bonsai_*.zip)
|
||||
echo "filepath=$filepath" >> $GITHUB_OUTPUT
|
||||
echo "filename=$(basename $filepath)" >> $GITHUB_OUTPUT
|
||||
- name: Upload zip file to release
|
||||
@@ -67,6 +67,6 @@ jobs:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.find_zip.outputs.filepath }}
|
||||
asset_name: ${{ steps.find_zip.outputs.filename }}
|
||||
release_name: "blenderbim-${{steps.version.outputs.version}}"
|
||||
tag: "blenderbim-${{steps.version.outputs.version}}"
|
||||
release_name: "bonsai-${{steps.version.outputs.version}}"
|
||||
tag: "bonsai-${{steps.version.outputs.version}}"
|
||||
overwrite: true
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Run IfcConvert on Sample files
|
||||
run: |
|
||||
(find test/input src/blenderbim/test/files -name '*.ifc' | while read i; do \
|
||||
(find test/input src/bonsai/test/files -name '*.ifc' | while read i; do \
|
||||
echo $i | tee -a log; \
|
||||
timeout 1m "$(which IfcConvert)" -yv "$i" "$i.obj" --validate >> log 2>&1; \
|
||||
echo $i $? >> statuses; \
|
||||
@@ -130,5 +130,7 @@ jobs:
|
||||
cd ../bcf && make test
|
||||
pip install requests
|
||||
cd ../bsdd && make test
|
||||
pip install deepdiff
|
||||
cd ../ifcdiff && make test
|
||||
cd ../ifcpatch && make test
|
||||
cd ../ifctester && make test
|
||||
|
||||
@@ -8,12 +8,12 @@ on:
|
||||
jobs:
|
||||
activate:
|
||||
if: github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: echo ok go
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -52,8 +52,8 @@ jobs:
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.10 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.10.so \
|
||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \
|
||||
-DGLTF_SUPPORT=On \
|
||||
|
||||
@@ -73,19 +73,16 @@ src/ifcopenshell-python/test/build
|
||||
# mypy cache
|
||||
.mypy_cache
|
||||
|
||||
# blenderbim libs
|
||||
src/blenderbim/blenderbim/libs
|
||||
# bonsai i18n
|
||||
src/bonsai/bonsai/translations.py
|
||||
|
||||
# blenderbim i18n
|
||||
src/blenderbim/blenderbim/translations.py
|
||||
# bonsai test temp files
|
||||
src/bonsai/test/files/temp
|
||||
src/bonsai/test/files/basic.ifc.cache.blend
|
||||
src/bonsai/test/files/basic.ifc.cache.sqlite
|
||||
|
||||
# blenderbim test temp files
|
||||
src/blenderbim/test/files/temp
|
||||
src/blenderbim/test/files/basic.ifc.cache.blend
|
||||
src/blenderbim/test/files/basic.ifc.cache.sqlite
|
||||
|
||||
src/blenderbim/drawings
|
||||
src/blenderbim/layouts
|
||||
src/bonsai/drawings
|
||||
src/bonsai/layouts
|
||||
|
||||
# ifcopenshell swig and compiled files
|
||||
src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper.so
|
||||
@@ -100,4 +97,4 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
|
||||
.cache
|
||||
|
||||
# Brickschema
|
||||
src/blenderbim/blenderbim/bim/schema/Brick.ttl
|
||||
src/bonsai/bonsai/bim/schema/Brick.ttl
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- mode: Dockerfile -*-
|
||||
|
||||
FROM ubuntu:focal
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG CHANNEL
|
||||
ENV CHANNEL=${CHANNEL:-latest}
|
||||
@@ -23,7 +23,7 @@ RUN echo "deb http://archive.ubuntu.com/ubuntu focal-proposed main restricted" |
|
||||
echo "deb http://archive.ubuntu.com/ubuntu focal-proposed multiverse" | tee -a /etc/apt/sources.list; \
|
||||
apt-get -qq update; \
|
||||
apt-get -y install tzdata dos2unix rsync; \
|
||||
apt-get -y install python3 libxml2 libpython3.8 \
|
||||
apt-get -y install python3 libxml2 libpython3.10 \
|
||||
libboost-all-dev \
|
||||
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
|
||||
libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
|
||||
|
||||
@@ -12,7 +12,7 @@ is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1]. Extending
|
||||
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),
|
||||
to convert IFC models to other formats), Bonsai (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:
|
||||
@@ -22,10 +22,10 @@ For more information, see:
|
||||
* [IfcOpenShell C++ Installation](https://docs.ifcopenshell.org/ifcopenshell/installation.html)
|
||||
* [IfcOpenShell Python Installation](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html)
|
||||
* [IfcOpenShell Python Hello World Tutorial](https://docs.ifcopenshell.org/ifcopenshell-python/hello_world.html)
|
||||
* [BlenderBIM Add-on Website](https://blenderbim.org)
|
||||
* [BlenderBIM Add-on Documentation](https://docs.blenderbim.org/index.html)
|
||||
* [Add-on Installation](https://docs.blenderbim.org/users/installation.html)
|
||||
* [Exploring an IFC model](https://docs.blenderbim.org/users/exploring_an_ifc_model.html)
|
||||
* [Bonsai Website](https://bonsaibim.org)
|
||||
* [Bonsai Documentation](https://docs.bonsaibim.org/index.html)
|
||||
* [Add-on Installation](https://docs.bonsaibim.org/users/installation.html)
|
||||
* [Exploring an IFC model](https://docs.bonsaibim.org/users/exploring_an_ifc_model.html)
|
||||
|
||||
Development is sponsored through your generous donations!
|
||||
|
||||
@@ -37,7 +37,7 @@ Contents
|
||||
| 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/) |
|
||||
| bonsai | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later | [](https://bonsaibim.org/download.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=bonsai&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/) |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<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>
|
||||
<docsUrl>https://docs.bonsaibim.org/</docsUrl>
|
||||
<!--<mailingListUrl></mailingListUrl>-->
|
||||
<bugTrackerUrl>https://github.com/IfcOpenShell/IfcOpenShell/issues</bugTrackerUrl>
|
||||
<tags>blender bim blenderbim ifc python opensource foss</tags>
|
||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -14,9 +14,11 @@ import os
|
||||
import pathlib
|
||||
import re
|
||||
from urllib import request
|
||||
from github import Github
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
def get_repo_tag_names():
|
||||
def get_repo_tag_names() -> list[str]:
|
||||
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")
|
||||
@@ -43,7 +45,7 @@ def get_latest_choco_blender_version() -> list:
|
||||
return re.findall(RE_BLENDER_VERSION_MIN_MAJ_PAT, html_txt)
|
||||
|
||||
|
||||
def get_file_sha256_hash(file_path):
|
||||
def get_file_sha256_hash(file_path: str) -> str:
|
||||
BLOCKSIZE = 65536
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
@@ -56,16 +58,29 @@ def get_file_sha256_hash(file_path):
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
def quit_with_error_message(message: str):
|
||||
def quit_with_error_message(message: str) -> NoReturn:
|
||||
print(f"ERROR: {message}")
|
||||
quit(0)
|
||||
|
||||
|
||||
def get_release_zip(tag: str) -> tuple[str, str]:
|
||||
g = Github()
|
||||
repo = g.get_repo("IfcOpenShell/IfcOpenShell")
|
||||
release = repo.get_release(tag)
|
||||
for asset in release.get_assets():
|
||||
asset_name = asset.name
|
||||
if python_version not in asset_name:
|
||||
continue
|
||||
if TARGET_OS not in asset_name:
|
||||
continue
|
||||
return (asset_name, asset.browser_download_url)
|
||||
raise Exception(f"Couldn't find the release matching '{python_version}' and '{TARGET_OS}' in tag '{tag}'.")
|
||||
|
||||
|
||||
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+)\)"
|
||||
@@ -79,7 +94,7 @@ 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"
|
||||
TARGET_OS = "windows-x64"
|
||||
|
||||
git_status = os.popen("git status").read()
|
||||
print(git_status)
|
||||
@@ -130,10 +145,7 @@ 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}"
|
||||
release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag)
|
||||
os.popen(f"wget {url_blenderbim_py3x_win_zip} --no-verbose").read()
|
||||
|
||||
# sha256sum_blenderbim_py310_win_zip
|
||||
@@ -221,7 +221,7 @@ endif()
|
||||
|
||||
if(GLTF_SUPPORT OR CITYJSON_SUPPORT)
|
||||
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
|
||||
find_path(json_header_path "json.hpp" ${JSON_INCLUDE_DIR} PATH_SUFFIXES "nlohmann")
|
||||
find_path(json_header_path "nlohmann/json.hpp" HINTS ${JSON_INCLUDE_DIR})
|
||||
set(JSON_INCLUDE_DIR ${json_header_path})
|
||||
|
||||
if(json_header_path)
|
||||
|
||||
@@ -76,14 +76,14 @@ about:
|
||||
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)
|
||||
* [IfcOpenShell Documentation](http://bonsaibim.org/docs-python)
|
||||
* [IfcOpenShell C++ Installation](https://bonsaibim.org/docs-python/ifcopenshell/installation.html)
|
||||
* [IfcOpenShell Python Installation](https://bonsaibim.org/docs-python/ifcopenshell-python/installation.html)
|
||||
* [IfcOpenShell Python Hello World Tutorial](https://bonsaibim.org/docs-python/ifcopenshell-python/hello_world.html)
|
||||
* [Bonsai Website](https://bonsaibim.org)
|
||||
* [Bonsai Documentation](http://bonsaibim.org/docs)
|
||||
* [Add-on Installation](https://bonsaibim.org/docs/users/installation.html)
|
||||
* [Exploring an IFC model](https://bonsaibim.org/docs/users/exploring_an_ifc_model.html)
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -81,7 +81,7 @@ ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
|
||||
PYTHON_VERSIONS = ["3.9.11", "3.10.3", "3.11.8", "3.12.1"]
|
||||
JSON_VERSION = "v3.6.1"
|
||||
OCE_VERSION = "0.18.3"
|
||||
OCCT_VERSION = "7.7.1"
|
||||
OCCT_VERSION = "7.5.3"
|
||||
BOOST_VERSION = "1.80.0"
|
||||
PCRE_VERSION = "8.41"
|
||||
LIBXML2_VERSION = "2.9.11"
|
||||
|
||||
@@ -30,8 +30,8 @@ license:
|
||||
# TODO: make this based on xsd file presence
|
||||
.PHONY: models
|
||||
models:
|
||||
cd src && xsdata generate -p bcf.v2.model --unnest-classes --kw-only --slots -ds Google bcf/v2/xsd
|
||||
cd src && xsdata generate -p bcf.v3.model --unnest-classes --kw-only --slots -ds Google bcf/v3/xsd
|
||||
xsdata generate -p bcf.v2.model --unnest-classes --kw-only --slots -ds Google bcf/v2/xsd
|
||||
xsdata generate -p bcf.v3.model --unnest-classes --kw-only --slots -ds Google bcf/v3/xsd
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
A simple Python implementation of the BCF standard. Manipulation of BCF-XML is
|
||||
available via `bcfxml.py` and manipulation of BCF-API is available via
|
||||
`bcfapi.py`.
|
||||
|
||||
Python files in 'model' folder are automatically generated from .xsd files (located in 'xsd' folder).
|
||||
To regenerate them you can use `make models`.
|
||||
The only exception is 'v2/model/extensions.py', see it's note for the details.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import bcf.v2.model.extensions
|
||||
import bcf.v3.model.extensions
|
||||
from typing import NamedTuple, Union
|
||||
from dataclasses import fields
|
||||
|
||||
|
||||
class AttributeData(NamedTuple):
|
||||
attr_type: type
|
||||
subattr_name: str
|
||||
subattr_xsd_name: str
|
||||
|
||||
|
||||
Extensions = Union[bcf.v2.model.extensions.Extensions, bcf.v3.model.extensions.Extensions]
|
||||
|
||||
|
||||
def get_extensions_attributes(extensions: Extensions) -> dict[str, AttributeData]:
|
||||
"""Return mapping of xsd attribute name to a tuple that consists of:
|
||||
- Extensions attribute name
|
||||
- Extensions attribute type type
|
||||
- subattribute name"""
|
||||
possible_attributes = {}
|
||||
for field in fields(type(extensions)):
|
||||
field_type = field.type.__args__[0] # type: ignore [reportAttributeAccessIssue]
|
||||
subfield = next(iter(fields(field_type)))
|
||||
xsd_name = subfield.metadata["name"]
|
||||
possible_attributes[field.name] = AttributeData(field_type, subfield.name, xsd_name)
|
||||
|
||||
return possible_attributes
|
||||
@@ -0,0 +1,10 @@
|
||||
import bcf.v2.model
|
||||
import bcf.v3.model
|
||||
from typing import Union
|
||||
|
||||
BimSnippet = Union[bcf.v2.model.BimSnippet, bcf.v3.model.BimSnippet]
|
||||
BitMap = Union[bcf.v2.model.VisualizationInfoBitmap, bcf.v3.model.Bitmap]
|
||||
DocumentReference = Union[bcf.v2.model.TopicDocumentReference, bcf.v3.model.DocumentReference]
|
||||
HeaderFile = Union[bcf.v2.model.HeaderFile, bcf.v3.model.File]
|
||||
Topic = Union[bcf.v2.model.Topic, bcf.v3.model.Topic]
|
||||
ViewPoint = Union[bcf.v2.model.ViewPoint, bcf.v3.model.ViewPoint]
|
||||
@@ -0,0 +1,101 @@
|
||||
import tempfile
|
||||
import bcf.v2.bcfxml
|
||||
import bcf.v2.model
|
||||
import bcf.v2.topic
|
||||
import bcf.v3.bcfxml
|
||||
import bcf.v3.model
|
||||
import bcf.v3.topic
|
||||
import bcf.agnostic.model as mdl
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional
|
||||
from typing_extensions import assert_never
|
||||
|
||||
TopicHandler = Union[bcf.v2.topic.TopicHandler, bcf.v3.topic.TopicHandler]
|
||||
|
||||
|
||||
def extract_file(
|
||||
topic: TopicHandler,
|
||||
entity: Union[mdl.HeaderFile, mdl.BimSnippet, mdl.DocumentReference, mdl.BitMap],
|
||||
bcfxml: Optional[Union[bcf.v2.bcfxml.BcfXml, bcf.v3.bcfxml.BcfXml]] = None,
|
||||
outfile: Optional[Path] = None,
|
||||
) -> Union[Path, str, None]:
|
||||
"""Extracts an element with a file into a temporary directory
|
||||
|
||||
These include header files, bim snippets, document references, and
|
||||
viewpoint bitmaps. External reference are not downloaded. Instead, the
|
||||
URI reference is returned.
|
||||
|
||||
:param entity: The entity with a file reference to extract
|
||||
:param outfile: If provided, save the header file to that location.
|
||||
Otherwise, a temporary directory is created and the filename is
|
||||
derived from the header's original filename.
|
||||
:param bcfxml: The BCF XML file to use for resolving document references files.
|
||||
Required only for BCF v3 document references (in BCF v3 internal documents
|
||||
are stored at BCF root, not in the topic).
|
||||
:return: The filepath of the extracted file. It may be a URL if the
|
||||
header file is external.
|
||||
"""
|
||||
if isinstance(entity, mdl.DocumentReference):
|
||||
if isinstance(entity, bcf.v2.model.TopicDocumentReference):
|
||||
reference = entity.referenced_document
|
||||
else:
|
||||
reference = entity.document_guid
|
||||
else:
|
||||
reference = entity.reference
|
||||
|
||||
if not reference:
|
||||
return None
|
||||
|
||||
# For v3 document references external documents are detected by empty document_guid.
|
||||
# External bitmaps are not supported by bcf.
|
||||
if not isinstance(entity, (bcf.v3.model.DocumentReference, mdl.BitMap)) and entity.is_external:
|
||||
return reference
|
||||
|
||||
if isinstance(entity, bcf.v3.model.DocumentReference):
|
||||
# Extract document reference filename and contents.
|
||||
if not bcfxml:
|
||||
raise TypeError("bcfxml is required for BCF v3 document references.")
|
||||
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
|
||||
error_msg = f"BCF XML is missing document with guid '{reference}'."
|
||||
if not bcfxml.documents:
|
||||
raise Exception(error_msg)
|
||||
definition_docs = bcfxml.documents.definition.documents
|
||||
if not definition_docs:
|
||||
raise Exception(error_msg)
|
||||
docs = next((doc for doc in definition_docs.document if doc.guid == reference), None)
|
||||
if not docs:
|
||||
raise Exception(error_msg)
|
||||
filename = docs.filename
|
||||
bytes_data = bcfxml.documents.documents[filename]
|
||||
else:
|
||||
if isinstance(entity, mdl.BimSnippet):
|
||||
bytes_data = topic.bim_snippet
|
||||
assert isinstance(bytes_data, bytes)
|
||||
elif isinstance(entity, mdl.HeaderFile):
|
||||
bytes_data = topic.reference_files[reference]
|
||||
elif isinstance(entity, mdl.BitMap):
|
||||
bytes_data = next(
|
||||
byte_data
|
||||
for vp in topic.viewpoints.values()
|
||||
for data_reference, byte_data in vp.bitmaps.items()
|
||||
if data_reference == reference
|
||||
)
|
||||
elif isinstance(entity, bcf.v2.model.TopicDocumentReference):
|
||||
assert isinstance(topic, bcf.v2.topic.TopicHandler)
|
||||
bytes_data = topic.document_references[reference]
|
||||
else:
|
||||
assert_never(entity)
|
||||
|
||||
# We don't really need it if 'outfile' is None, just keeping type checker happy.
|
||||
if isinstance(entity, mdl.HeaderFile) and entity.filename:
|
||||
filename = entity.filename
|
||||
else:
|
||||
filename = Path(reference).name
|
||||
|
||||
if not outfile:
|
||||
outfile = Path(tempfile.mkdtemp()) / filename
|
||||
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(bytes_data)
|
||||
|
||||
return outfile
|
||||
@@ -0,0 +1,5 @@
|
||||
import bcf.v2.visinfo
|
||||
import bcf.v3.visinfo
|
||||
from typing import Union
|
||||
|
||||
VisualizationInfoHandler = Union[bcf.v2.visinfo.VisualizationInfoHandler, bcf.v3.visinfo.VisualizationInfoHandler]
|
||||
@@ -30,9 +30,10 @@ from bcf.v3.model import Version as Version3
|
||||
from bcf.xml_parser import AbstractXmlParserSerializer, XmlParserSerializer
|
||||
|
||||
|
||||
def load(
|
||||
filepath: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None
|
||||
) -> Optional[Union[BcfXml2, BcfXml3]]:
|
||||
BcfXml = Union[BcfXml2, BcfXml3]
|
||||
|
||||
|
||||
def load(filepath: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None) -> Optional[BcfXml]:
|
||||
"""
|
||||
Load a BCF file.
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Any, NoReturn, Optional, TypeVar
|
||||
|
||||
import bcf.agnostic.extensions
|
||||
import bcf.v2.model as mdl
|
||||
import bcf.v2.model.extensions as mdl_extensions
|
||||
from bcf.inmemory_zipfile import InMemoryZipFile, ZipFileInterface
|
||||
from bcf.v2.topic import TopicHandler
|
||||
from bcf.xml_parser import AbstractXmlParserSerializer, XmlParserSerializer
|
||||
@@ -24,7 +26,8 @@ class BcfXml:
|
||||
self._xml_handler = xml_handler or XmlParserSerializer()
|
||||
self._version: Optional[mdl.Version] = None
|
||||
self._project_info: Optional[mdl.ProjectExtension] = None
|
||||
self._topics: dict[str, TopicHandler] = {}
|
||||
self._extensions: Optional[mdl_extensions.Extensions] = None
|
||||
self._topics: Optional[dict[str, TopicHandler]] = None
|
||||
self._extension_schema: Optional[bytes] = None
|
||||
self._zip_file = self._load_zip_file()
|
||||
|
||||
@@ -85,24 +88,63 @@ class BcfXml:
|
||||
def extension_schema(self, value: bytes) -> None:
|
||||
self._extension_schema = value
|
||||
|
||||
@property
|
||||
def extensions(self) -> Optional[mdl_extensions.Extensions]:
|
||||
"""BCF extensions."""
|
||||
|
||||
if not self._extensions and self.extension_schema:
|
||||
import io
|
||||
from xml.etree import ElementTree as etree
|
||||
|
||||
extensions = mdl_extensions.Extensions()
|
||||
|
||||
xs = "{http://www.w3.org/2001/XMLSchema}"
|
||||
root = etree.parse(io.BytesIO((self.extension_schema)))
|
||||
|
||||
attrs = bcf.agnostic.extensions.get_extensions_attributes(extensions)
|
||||
xsd_to_attrs = {v.subattr_xsd_name: k for k, v in attrs.items()}
|
||||
for node in root.findall(f".//{xs}restriction"):
|
||||
attr_type = node.attrib.get("base")
|
||||
if not attr_type:
|
||||
continue
|
||||
attr_name = xsd_to_attrs.get(attr_type)
|
||||
if attr_name is None:
|
||||
continue
|
||||
values = []
|
||||
for enum in node.findall(f".//{xs}enumeration"):
|
||||
values.append(enum.attrib["value"])
|
||||
if not values:
|
||||
continue
|
||||
attr_data = attrs[attr_name]
|
||||
attr = attr_data.attr_type()
|
||||
setattr(attr, attr_data.subattr_name, values)
|
||||
setattr(extensions, attr_name, attr)
|
||||
|
||||
self._extensions = extensions
|
||||
return self._extensions
|
||||
|
||||
@extensions.setter
|
||||
def extensions(self, value: Optional[mdl_extensions.Extensions]) -> None:
|
||||
self._extensions = value
|
||||
|
||||
@property
|
||||
def topics(self) -> dict[str, TopicHandler]:
|
||||
"""BCF topics."""
|
||||
if not self._topics and self._zip_file:
|
||||
self._topics = self._load_topics(self._zip_file, self._xml_handler)
|
||||
if self._topics is None:
|
||||
self._topics = self._load_topics()
|
||||
return self._topics
|
||||
|
||||
def _load_topics(
|
||||
self, zip_file: zipfile.ZipFile, xml_handler: AbstractXmlParserSerializer
|
||||
) -> dict[str, TopicHandler]:
|
||||
def _load_topics(self) -> dict[str, TopicHandler]:
|
||||
topics = {}
|
||||
for topic_dir in zipfile.Path(zip_file).iterdir():
|
||||
if self._zip_file is None:
|
||||
return topics
|
||||
for topic_dir in zipfile.Path(self._zip_file).iterdir():
|
||||
if not topic_dir.is_dir():
|
||||
continue
|
||||
markup_path = topic_dir.joinpath("markup.bcf")
|
||||
if not markup_path.exists():
|
||||
continue
|
||||
topics[topic_dir.name] = TopicHandler(topic_dir, xml_handler)
|
||||
topics[topic_dir.name] = TopicHandler(topic_dir, self._xml_handler)
|
||||
return topics
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
# NOTE: This file is not generated from
|
||||
# https://github.com/buildingSMART/BCF-XML/blob/release_2_1/Extension%20Schemas/extensions.xsd
|
||||
# because in bcf 2.1 there is no extensions.xml - I guess, the schema assumes that each .bcf
|
||||
# will use their own schema patched by it's own extensions.xsd.
|
||||
#
|
||||
# To make things simpler we just mimic extensions structures from bcf 3, so they'll have common API,
|
||||
# and parse extensions.xsd inside .bcf as .xml and fill our structures.
|
||||
#
|
||||
# Preferably if we could generate some kind of extensions.xml from extensions.xsd
|
||||
# and leave all the handling to xsdata.
|
||||
#
|
||||
# We also don't add it to __init__.py not to mess with generator.
|
||||
#
|
||||
# Currently extensions support for v2 is only read-only.
|
||||
|
||||
|
||||
from dataclasses import dataclass, field, fields
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsPriorities:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
priority: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "Priority",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsSnippetTypes:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
snippet_type: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "SnippetType",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsStages:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
stage: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "Stage",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsTopicLabels:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
topic_label: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "TopicLabel",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsTopicStatuses:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
topic_status: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "TopicStatus",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsTopicTypes:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
topic_type: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "TopicType",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class ExtensionsUsers:
|
||||
class Meta:
|
||||
global_type = False
|
||||
|
||||
user: List[str] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "UserIdType",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class Extensions:
|
||||
topic_types: Optional[ExtensionsTopicTypes] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "TopicTypes",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
topic_statuses: Optional[ExtensionsTopicStatuses] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "TopicStatuses",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
priorities: Optional[ExtensionsPriorities] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Priorities",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
topic_labels: Optional[ExtensionsTopicLabels] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "TopicLabels",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
users: Optional[ExtensionsUsers] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Users",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
snippet_types: Optional[ExtensionsSnippetTypes] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "SnippetTypes",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
stages: Optional[ExtensionsStages] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Stages",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
},
|
||||
)
|
||||
@@ -5,7 +5,7 @@ import tempfile
|
||||
import uuid
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Any, NoReturn, Optional
|
||||
from typing import Any, NoReturn, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
from ifcopenshell import entity_instance
|
||||
@@ -27,9 +27,9 @@ class TopicHandler:
|
||||
xml_handler: Optional[AbstractXmlParserSerializer] = None,
|
||||
) -> None:
|
||||
self._markup: Optional[mdl.Markup] = None
|
||||
self._viewpoints: dict[str, VisualizationInfoHandler] = {}
|
||||
self._reference_files: dict[str, bytes] = {}
|
||||
self._document_references: dict[str, bytes] = {}
|
||||
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
|
||||
self._reference_files: Optional[dict[str, bytes]] = None
|
||||
self._document_references: Optional[dict[str, bytes]] = None
|
||||
self._bim_snippet: Optional[bytes] = None
|
||||
self._xml_handler = xml_handler or XmlParserSerializer()
|
||||
self._topic_dir = topic_dir
|
||||
@@ -63,11 +63,21 @@ class TopicHandler:
|
||||
"""Return the header of the topic."""
|
||||
return self.markup.header if self.markup else None
|
||||
|
||||
@header.setter
|
||||
def header(self, header: mdl.Header) -> None:
|
||||
"""Set the header of the topic."""
|
||||
self.markup.header = header
|
||||
|
||||
@property
|
||||
def comments(self) -> list[mdl.Comment]:
|
||||
"""Return the comments of the topic."""
|
||||
return self.markup.comment if self.markup else []
|
||||
|
||||
@comments.setter
|
||||
def comments(self, comments: list[mdl.Comment]) -> None:
|
||||
assert self.markup
|
||||
self.markup.comment = comments
|
||||
|
||||
@property
|
||||
def bim_snippet(self) -> Optional[bytes]:
|
||||
if not self._bim_snippet and self._topic_dir:
|
||||
@@ -80,14 +90,24 @@ class TopicHandler:
|
||||
|
||||
@property
|
||||
def viewpoints(self) -> dict[str, VisualizationInfoHandler]:
|
||||
if not self._viewpoints and self._topic_dir:
|
||||
if self._viewpoints is None:
|
||||
self._viewpoints = self._load_viewpoints()
|
||||
return self._viewpoints
|
||||
|
||||
def _load_viewpoints(self) -> dict[str, VisualizationInfoHandler]:
|
||||
if self._topic_dir and self.markup and (viewpoints := self.markup.viewpoints):
|
||||
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
|
||||
return {}
|
||||
|
||||
@property
|
||||
def reference_files(self) -> dict[str, bytes]:
|
||||
if self._reference_files or not self.header:
|
||||
if self._reference_files is not None:
|
||||
return self._reference_files
|
||||
|
||||
self._reference_files = {}
|
||||
if not self.header:
|
||||
return self._reference_files
|
||||
|
||||
for ref in self.header.file:
|
||||
if ref.is_external:
|
||||
continue
|
||||
@@ -99,8 +119,13 @@ class TopicHandler:
|
||||
|
||||
@property
|
||||
def document_references(self) -> dict[str, bytes]:
|
||||
if self._document_references or not self.topic:
|
||||
if self._document_references is not None:
|
||||
return self._document_references
|
||||
|
||||
self._document_references = {}
|
||||
if not self.topic:
|
||||
return self._document_references
|
||||
|
||||
for doc in self.topic.document_reference:
|
||||
if doc.is_external or not doc.referenced_document:
|
||||
continue
|
||||
@@ -118,11 +143,6 @@ class TopicHandler:
|
||||
return bim_snippet_path.read_bytes()
|
||||
return None
|
||||
|
||||
def _load_viewpoints(self) -> dict[str, VisualizationInfoHandler]:
|
||||
if self.markup and (viewpoints := self.markup.viewpoints):
|
||||
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def create_new(
|
||||
cls,
|
||||
@@ -220,55 +240,7 @@ class TopicHandler:
|
||||
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
|
||||
destination_zip.writestr(real_path.at, self.document_references[doc.referenced_document])
|
||||
|
||||
def extract_file(self, entity, outfile: Optional[Path] = None) -> Path:
|
||||
"""Extracts an element with a file into a temporary directory
|
||||
|
||||
These include header files, bim snippets, document references, and
|
||||
viewpoint bitmaps. External reference are not downloaded. Instead, the
|
||||
URI reference is returned.
|
||||
|
||||
:param entity: The entity with a file reference to extract
|
||||
:type entity: bcf.v2.model.HeaderFile,bcf.v2.model.BimSnippet,bcf.v2.model.TopicDocumentReference
|
||||
:param outfile: If provided, save the header file to that location.
|
||||
Otherwise, a temporary directory is created and the filename is
|
||||
derived from the header's original filename.
|
||||
:type outfile: pathlib.Path,optional
|
||||
:return: The filepath of the extracted file. It may be a URL if the
|
||||
header file is external.
|
||||
:rtype: Path
|
||||
"""
|
||||
if hasattr(entity, "reference"):
|
||||
reference = entity.reference
|
||||
else:
|
||||
reference = entity.referenced_document
|
||||
|
||||
if not reference:
|
||||
return
|
||||
|
||||
if getattr(entity, "is_external", False):
|
||||
return entity.reference
|
||||
|
||||
resolved_reference = self._topic_dir
|
||||
|
||||
for part in Path(reference).parts:
|
||||
if part == "..":
|
||||
resolved_reference = resolved_reference.parent
|
||||
else:
|
||||
resolved_reference = resolved_reference.joinpath(part)
|
||||
|
||||
if not outfile:
|
||||
if getattr(entity, "filename", None):
|
||||
filename = entity.filename
|
||||
else:
|
||||
filename = resolved_reference.name
|
||||
outfile = Path(tempfile.mkdtemp()) / filename
|
||||
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(resolved_reference.read_bytes())
|
||||
|
||||
return outfile
|
||||
|
||||
def add_viewpoint(self, element: entity_instance) -> None:
|
||||
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
|
||||
"""Add a viewpoint pointed at the placement of an IFC element to the topic.
|
||||
|
||||
Args:
|
||||
@@ -278,7 +250,9 @@ class TopicHandler:
|
||||
self.add_visinfo_handler(new_viewpoint)
|
||||
return new_viewpoint
|
||||
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
|
||||
def add_viewpoint_from_point_and_guids(
|
||||
self, position: NDArray[np.float64], *guids: str
|
||||
) -> VisualizationInfoHandler:
|
||||
"""Add a viewpoint pointing at an XYZ point in space
|
||||
|
||||
Args:
|
||||
@@ -291,9 +265,17 @@ class TopicHandler:
|
||||
self.add_visinfo_handler(vi_handler)
|
||||
return vi_handler
|
||||
|
||||
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
|
||||
def add_visinfo_handler(
|
||||
self, new_viewpoint: VisualizationInfoHandler, snapshot_filename: Optional[str] = None
|
||||
) -> mdl.ViewPoint:
|
||||
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
|
||||
self.markup.viewpoints.append(mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid))
|
||||
viewpoint = mdl.ViewPoint(
|
||||
viewpoint=new_viewpoint.guid + ".bcfv",
|
||||
snapshot=snapshot_filename,
|
||||
guid=new_viewpoint.guid,
|
||||
)
|
||||
self.markup.viewpoints.append(viewpoint)
|
||||
return viewpoint
|
||||
|
||||
def __eq__(self, other: object) -> bool | NoReturn:
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import uuid
|
||||
import zipfile
|
||||
from functools import lru_cache
|
||||
from typing import Any, Iterable, Optional
|
||||
from typing import Any, Iterable, Optional, Literal, Union
|
||||
|
||||
import numpy as np
|
||||
from ifcopenshell import entity_instance
|
||||
@@ -134,6 +133,15 @@ class VisualizationInfoHandler:
|
||||
self._save_bitmaps(bcf_zip, topic_dir)
|
||||
|
||||
def _save_snapshot(self, bcf_zip: ZipFileInterface, topic_dir: str, filename: Optional[str]) -> None:
|
||||
if bool(self.snapshot) ^ bool(filename):
|
||||
data = ["data (VisualizationInfoHandler.snapshot)", "filename (ViewPoint.snapshot)"]
|
||||
provided_data, missing_data = data if self.snapshot else data[::-1]
|
||||
print(
|
||||
f"WARNING. Snapshot with viewpoint guid '{self.guid}' won't be saved to bcf. "
|
||||
f"Only snapshot {provided_data} is provided but snapshot {missing_data} is missing."
|
||||
)
|
||||
return
|
||||
|
||||
if self.snapshot and filename:
|
||||
bcf_zip.writestr(f"{topic_dir}/{filename}", self.snapshot)
|
||||
|
||||
@@ -196,13 +204,110 @@ class VisualizationInfoHandler:
|
||||
visualization_info=build_viewpoint_from_position_and_guids(position, *guids), xml_handler=xml_handler
|
||||
)
|
||||
|
||||
def get_selected_guids(self) -> Union[list[str], None]:
|
||||
"""
|
||||
Return viewpoint selected elements IFC guids.
|
||||
|
||||
Returns:
|
||||
If viewpoint has no selection settings, return `None`.
|
||||
Otherwise return a list of selected elements IFC guids.
|
||||
"""
|
||||
visualization_info = self.visualization_info
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
return None
|
||||
|
||||
selection = components.selection
|
||||
if not selection:
|
||||
return None
|
||||
return [guid for c in selection.component if (guid := c.ifc_guid)]
|
||||
|
||||
def set_selected_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
visualization_info = self.visualization_info
|
||||
|
||||
guids = [e.GlobalId for e in elements]
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=True)
|
||||
components = mdl.Components(visibility=visibility)
|
||||
visualization_info.components = components
|
||||
|
||||
selection = components.selection
|
||||
components_list = [mdl.Component(ifc_guid=guid) for guid in guids]
|
||||
if not selection:
|
||||
selection = mdl.ComponentSelection()
|
||||
components.selection = selection
|
||||
selection.component = components_list
|
||||
|
||||
def set_visible_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
self.set_visibility(elements, elements_visibility="VISIBLE")
|
||||
|
||||
def set_hidden_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
self.set_visibility(elements, elements_visibility="HIDDEN")
|
||||
|
||||
def set_visibility(
|
||||
self, elements: list[ifcopenshell.entity_instance], elements_visibility: Literal["VISIBLE", "HIDDEN"]
|
||||
) -> None:
|
||||
visualization_info = self.visualization_info
|
||||
default_visibility = elements_visibility == "HIDDEN"
|
||||
|
||||
guids = [e.GlobalId for e in elements]
|
||||
components_list = [mdl.Component(ifc_guid=guid) for guid in guids]
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=default_visibility)
|
||||
components = mdl.Components(visibility=visibility)
|
||||
visualization_info.components = components
|
||||
|
||||
visibility = components.visibility
|
||||
if not visibility:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=default_visibility)
|
||||
components.visibility = visibility
|
||||
elif visibility.default_visibility != default_visibility:
|
||||
visibility.default_visibility = default_visibility
|
||||
|
||||
exceptions = visibility.exceptions
|
||||
if not exceptions:
|
||||
exceptions = mdl.ComponentVisibilityExceptions()
|
||||
visibility.exceptions = exceptions
|
||||
exceptions.component = components_list
|
||||
|
||||
def get_elements_visibility(self) -> Union[tuple[bool, list[str]], None]:
|
||||
"""
|
||||
Return viewpoint elements visibility settings.
|
||||
|
||||
Returns:
|
||||
If viewpoint has no visibility settings, return `None`.
|
||||
Otherwise return a tuple containing the default visibility
|
||||
and a list of IFC element GUIDs listed as exceptions.
|
||||
|
||||
If default visibility is `True`, all elements are visible except the exceptions.
|
||||
|
||||
If default visibility is `False`, all elements are hidden except the exceptions.
|
||||
"""
|
||||
|
||||
visualization_info = self.visualization_info
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
return None
|
||||
|
||||
visibility = components.visibility
|
||||
if not visibility:
|
||||
return None
|
||||
default_visibility = visibility.default_visibility or False
|
||||
|
||||
exceptions = visibility.exceptions
|
||||
if not exceptions:
|
||||
return default_visibility, []
|
||||
guids = [guid for c in exceptions.component if (guid := c.ifc_guid)]
|
||||
return default_visibility, guids
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
This function is cached to speedudp the creation of multiple BCF topics regarding the same element.
|
||||
This function is cached to speed up the creation of multiple BCF topics regarding the same element.
|
||||
|
||||
Args:
|
||||
element: The IFC element to point at.
|
||||
@@ -228,7 +333,7 @@ def build_viewpoint_from_position_and_guids(position: NDArray[np.float64], *guid
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
This function is cached to speedudp the creation of multiple BCF topics regarding the same element.
|
||||
This function is cached to speed up the creation of multiple BCF topics regarding the same element.
|
||||
|
||||
Args:
|
||||
position: target point coordinates.
|
||||
@@ -249,7 +354,7 @@ def build_components(*guids: str) -> mdl.Components:
|
||||
Return the BCF components from an IFC element GUID.
|
||||
|
||||
Args:
|
||||
*guids: One or more IFC element GUID.
|
||||
*guids: One or more selected IFC element GUID.
|
||||
|
||||
Returns:
|
||||
The BCF components definition.
|
||||
|
||||
@@ -26,7 +26,7 @@ class BcfXml:
|
||||
self._version: Optional[mdl.Version] = None
|
||||
self._project_info: Optional[mdl.ProjectInfo] = None
|
||||
self._extensions: Optional[mdl.Extensions] = None
|
||||
self._topics: dict[str, TopicHandler] = {}
|
||||
self._topics: Optional[dict[str, TopicHandler]] = None
|
||||
self._documents: Optional[DocumentsHandler] = None
|
||||
self._zip_file = self._load_zip_file()
|
||||
|
||||
@@ -91,18 +91,22 @@ class BcfXml:
|
||||
@property
|
||||
def topics(self) -> dict[str, TopicHandler]:
|
||||
"""BCF topics."""
|
||||
if not self._topics and self._zip_file:
|
||||
self._load_topics()
|
||||
if self._topics is None:
|
||||
self._topics = self._load_topics()
|
||||
return self._topics
|
||||
|
||||
def _load_topics(self) -> None:
|
||||
def _load_topics(self) -> dict[str, TopicHandler]:
|
||||
topics = {}
|
||||
if self._zip_file is None:
|
||||
return topics
|
||||
for topic_dir in zipfile.Path(self._zip_file).iterdir():
|
||||
if not topic_dir.is_dir():
|
||||
continue
|
||||
markup_path = topic_dir.joinpath("markup.bcf")
|
||||
if not markup_path.exists():
|
||||
continue
|
||||
self._topics[topic_dir.name] = TopicHandler(topic_dir, self._xml_handler)
|
||||
topics[topic_dir.name] = TopicHandler(topic_dir, self._xml_handler)
|
||||
return topics
|
||||
|
||||
@property
|
||||
def documents(self) -> Optional[DocumentsHandler]:
|
||||
|
||||
@@ -26,7 +26,8 @@ class TopicHandler:
|
||||
xml_handler: Optional[AbstractXmlParserSerializer] = None,
|
||||
) -> None:
|
||||
self._markup: Optional[mdl.Markup] = None
|
||||
self._viewpoints: dict[str, VisualizationInfoHandler] = {}
|
||||
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
|
||||
self._reference_files: Optional[dict[str, bytes]] = None
|
||||
self._bim_snippet: Optional[bytes] = None
|
||||
self._xml_handler = xml_handler or XmlParserSerializer()
|
||||
self._topic_dir = topic_dir
|
||||
@@ -49,22 +50,36 @@ class TopicHandler:
|
||||
return self.markup.topic
|
||||
|
||||
@property
|
||||
def guid(self) -> Optional[str]:
|
||||
def guid(self) -> str:
|
||||
"""Return the GUID of the topic."""
|
||||
if self._markup:
|
||||
return self.topic.guid
|
||||
return self._topic_dir.name if self._topic_dir else None
|
||||
return self._topic_dir.name if self._topic_dir else ""
|
||||
|
||||
@property
|
||||
def header(self) -> Optional[mdl.Header]:
|
||||
"""Return the header of the topic."""
|
||||
return self.markup.header
|
||||
|
||||
@header.setter
|
||||
def header(self, header: mdl.Header) -> None:
|
||||
"""Set the header of the topic."""
|
||||
self.markup.header = header
|
||||
|
||||
@property
|
||||
def comments(self) -> list[mdl.Comment]:
|
||||
"""Return the comments of the topic."""
|
||||
return self.topic.comments.comment if self.topic.comments else []
|
||||
|
||||
@comments.setter
|
||||
def comments(self, comments: list[mdl.Comment]) -> None:
|
||||
topic_comments = self.topic.comments
|
||||
if topic_comments is None:
|
||||
if not comments:
|
||||
return
|
||||
self.topic.comments = (topic_comments := mdl.TopicComments())
|
||||
topic_comments.comment = comments
|
||||
|
||||
@property
|
||||
def bim_snippet(self) -> Optional[bytes]:
|
||||
if not self._bim_snippet and self._topic_dir:
|
||||
@@ -77,15 +92,15 @@ class TopicHandler:
|
||||
|
||||
@property
|
||||
def viewpoints(self) -> dict[str, "VisualizationInfoHandler"]:
|
||||
if (
|
||||
not self._viewpoints
|
||||
and self._topic_dir
|
||||
and self.topic.viewpoints
|
||||
and (viewpoints := self.topic.viewpoints.view_point)
|
||||
):
|
||||
self._viewpoints = VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
|
||||
if self._viewpoints is None:
|
||||
self._viewpoints = self._load_viewpoints()
|
||||
return self._viewpoints
|
||||
|
||||
def _load_viewpoints(self) -> dict[str, "VisualizationInfoHandler"]:
|
||||
if self._topic_dir and self.topic.viewpoints and (viewpoints := self.topic.viewpoints.view_point):
|
||||
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
|
||||
return {}
|
||||
|
||||
def _load_bim_snippet(self) -> Optional[bytes]:
|
||||
bim_snippet_obj = self.topic.bim_snippet
|
||||
if bim_snippet_obj and not bim_snippet_obj.is_external and self._topic_dir:
|
||||
@@ -94,6 +109,27 @@ class TopicHandler:
|
||||
return bim_snippet_path.read_bytes()
|
||||
return None
|
||||
|
||||
@property
|
||||
def reference_files(self) -> dict[str, bytes]:
|
||||
if self._reference_files is not None:
|
||||
return self._reference_files
|
||||
|
||||
self._reference_files = {}
|
||||
if not self.header:
|
||||
return self._reference_files
|
||||
|
||||
if not self.header.files:
|
||||
return self._reference_files
|
||||
|
||||
for ref in self.header.files.file:
|
||||
if ref.is_external:
|
||||
continue
|
||||
real_path = self._topic_dir
|
||||
for path_part in ref.reference.split("/"):
|
||||
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
|
||||
self._reference_files[ref.reference] = real_path.read_bytes()
|
||||
return self._reference_files
|
||||
|
||||
@classmethod
|
||||
def create_new(
|
||||
cls,
|
||||
@@ -145,6 +181,7 @@ class TopicHandler:
|
||||
self._save_xml(destination_zip, self._markup, "markup.bcf")
|
||||
self._save_viewpoints(destination_zip, topic_dir)
|
||||
self._save_bim_snippet(destination_zip)
|
||||
self._save_reference_files(destination_zip)
|
||||
|
||||
def _save_viewpoints(self, destination_zip: ZipFileInterface, topic_dir: str) -> None:
|
||||
if not self.topic.viewpoints or not (viewpoints := self.topic.viewpoints.view_point):
|
||||
@@ -165,7 +202,20 @@ class TopicHandler:
|
||||
if self.bim_snippet:
|
||||
destination_zip.writestr(f"{self.topic.guid}/{ref_filename}", self.bim_snippet)
|
||||
|
||||
def add_viewpoint(self, element: entity_instance) -> None:
|
||||
def _save_reference_files(self, destination_zip: ZipFileInterface) -> None:
|
||||
if not self.header:
|
||||
return
|
||||
if not self.header.files:
|
||||
return
|
||||
for ref in self.header.files.file:
|
||||
if ref.is_external or not ref.reference:
|
||||
continue
|
||||
real_path = self._topic_dir
|
||||
for path_part in ref.reference.split("/"):
|
||||
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
|
||||
destination_zip.writestr(real_path.at, self.reference_files[ref.reference])
|
||||
|
||||
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
|
||||
"""
|
||||
Add a viewpoint tergeting an IFC element to the topic.
|
||||
|
||||
@@ -174,8 +224,11 @@ class TopicHandler:
|
||||
"""
|
||||
new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler)
|
||||
self.add_visinfo_handler(new_viewpoint)
|
||||
return new_viewpoint
|
||||
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
|
||||
def add_viewpoint_from_point_and_guids(
|
||||
self, position: NDArray[np.float64], *guids: str
|
||||
) -> VisualizationInfoHandler:
|
||||
"""
|
||||
Add a viewpoint tergeting an IFC element to the topic.
|
||||
|
||||
@@ -186,14 +239,21 @@ class TopicHandler:
|
||||
position, *guids, xml_handler=self._xml_handler
|
||||
)
|
||||
self.add_visinfo_handler(vi_handler)
|
||||
return vi_handler
|
||||
|
||||
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
|
||||
def add_visinfo_handler(
|
||||
self, new_viewpoint: VisualizationInfoHandler, snapshot_filename: Optional[str] = None
|
||||
) -> mdl.ViewPoint:
|
||||
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
|
||||
if self.topic.viewpoints is None:
|
||||
self.topic.viewpoints = mdl.TopicViewpoints()
|
||||
self.topic.viewpoints.view_point.append(
|
||||
mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid)
|
||||
viewpoint = mdl.ViewPoint(
|
||||
viewpoint=new_viewpoint.guid + ".bcfv",
|
||||
snapshot=snapshot_filename,
|
||||
guid=new_viewpoint.guid,
|
||||
)
|
||||
self.topic.viewpoints.view_point.append(viewpoint)
|
||||
return viewpoint
|
||||
|
||||
def __eq__(self, other: object) -> bool | NoReturn:
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import uuid
|
||||
import zipfile
|
||||
from functools import lru_cache
|
||||
from typing import Any, Iterable, Optional
|
||||
from typing import Any, Iterable, Optional, Literal, Union
|
||||
|
||||
import numpy as np
|
||||
from ifcopenshell import entity_instance
|
||||
from ifcopenshell.util import placement
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
from numpy.typing import NDArray
|
||||
|
||||
import bcf.v3.model as mdl
|
||||
@@ -133,6 +133,15 @@ class VisualizationInfoHandler:
|
||||
self._save_bitmaps(bcf_zip, topic_dir)
|
||||
|
||||
def _save_snapshot(self, bcf_zip: ZipFileInterface, topic_dir: str, filename: Optional[str]) -> None:
|
||||
if bool(self.snapshot) ^ bool(filename):
|
||||
data = ["data (VisualizationInfoHandler.snapshot)", "filename (ViewPoint.snapshot)"]
|
||||
provided_data, missing_data = data if self.snapshot else data[::-1]
|
||||
print(
|
||||
f"WARNING. Snapshot with viewpoint guid '{self.guid}' won't be saved to bcf. "
|
||||
f"Only snapshot {provided_data} is provided but snapshot {missing_data} is missing."
|
||||
)
|
||||
return
|
||||
|
||||
if self.snapshot and filename:
|
||||
bcf_zip.writestr(f"{topic_dir}/{filename}", self.snapshot)
|
||||
|
||||
@@ -195,13 +204,110 @@ class VisualizationInfoHandler:
|
||||
visualization_info=build_viewpoint_from_position_and_guids(position, *guids), xml_handler=xml_handler
|
||||
)
|
||||
|
||||
def get_selected_guids(self) -> Union[list[str], None]:
|
||||
"""
|
||||
Return viewpoint selected elements IFC guids.
|
||||
|
||||
Returns:
|
||||
If viewpoint has no selection settings, return `None`.
|
||||
Otherwise return a list of selected elements IFC guids.
|
||||
"""
|
||||
visualization_info = self.visualization_info
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
return None
|
||||
|
||||
selection = components.selection
|
||||
if not selection:
|
||||
return None
|
||||
return [guid for c in selection.component if (guid := c.ifc_guid)]
|
||||
|
||||
def set_selected_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
visualization_info = self.visualization_info
|
||||
|
||||
guids = [e.GlobalId for e in elements]
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=True)
|
||||
components = mdl.Components(visibility=visibility)
|
||||
visualization_info.components = components
|
||||
|
||||
selection = components.selection
|
||||
components_list = [mdl.Component(ifc_guid=guid) for guid in guids]
|
||||
if not selection:
|
||||
selection = mdl.ComponentSelection()
|
||||
components.selection = selection
|
||||
selection.component = components_list
|
||||
|
||||
def set_visible_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
self.set_visibility(elements, elements_visibility="VISIBLE")
|
||||
|
||||
def set_hidden_elements(self, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
self.set_visibility(elements, elements_visibility="HIDDEN")
|
||||
|
||||
def set_visibility(
|
||||
self, elements: list[ifcopenshell.entity_instance], elements_visibility: Literal["VISIBLE", "HIDDEN"]
|
||||
) -> None:
|
||||
visualization_info = self.visualization_info
|
||||
default_visibility = elements_visibility == "HIDDEN"
|
||||
|
||||
guids = [e.GlobalId for e in elements]
|
||||
components_list = [mdl.Component(ifc_guid=guid) for guid in guids]
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=default_visibility)
|
||||
components = mdl.Components(visibility=visibility)
|
||||
visualization_info.components = components
|
||||
|
||||
visibility = components.visibility
|
||||
if not visibility:
|
||||
visibility = mdl.ComponentVisibility(default_visibility=default_visibility)
|
||||
components.visibility = visibility
|
||||
elif visibility.default_visibility != default_visibility:
|
||||
visibility.default_visibility = default_visibility
|
||||
|
||||
exceptions = visibility.exceptions
|
||||
if not exceptions:
|
||||
exceptions = mdl.ComponentVisibilityExceptions()
|
||||
visibility.exceptions = exceptions
|
||||
exceptions.component = components_list
|
||||
|
||||
def get_elements_visibility(self) -> Union[tuple[bool, list[str]], None]:
|
||||
"""
|
||||
Return viewpoint elements visibility settings.
|
||||
|
||||
Returns:
|
||||
If viewpoint has no visibility settings, return `None`.
|
||||
Otherwise return a tuple containing the default visibility
|
||||
and a list of IFC element GUIDs listed as exceptions.
|
||||
|
||||
If default visibility is `True`, all elements are visible except the exceptions.
|
||||
|
||||
If default visibility is `False`, all elements are hidden except the exceptions.
|
||||
"""
|
||||
|
||||
visualization_info = self.visualization_info
|
||||
components = visualization_info.components
|
||||
if not components:
|
||||
return None
|
||||
|
||||
visibility = components.visibility
|
||||
if not visibility:
|
||||
return None
|
||||
default_visibility = visibility.default_visibility or False
|
||||
|
||||
exceptions = visibility.exceptions
|
||||
if not exceptions:
|
||||
return default_visibility, []
|
||||
guids = [guid for c in exceptions.component if (guid := c.ifc_guid)]
|
||||
return default_visibility, guids
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
This function is cached to speedudp the creation of multiple BCF topics regarding the same element.
|
||||
This function is cached to speed up the creation of multiple BCF topics regarding the same element.
|
||||
|
||||
Args:
|
||||
element: The IFC element to point at.
|
||||
@@ -209,7 +315,12 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
Returns:
|
||||
The BCF viewpoint definition.
|
||||
"""
|
||||
elem_placement = placement.get_local_placement(element.ObjectPlacement)
|
||||
ifc_file = element.wrapped_data.file
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
elem_placement[0][3] *= unit_scale
|
||||
elem_placement[1][3] *= unit_scale
|
||||
elem_placement[2][3] *= unit_scale
|
||||
|
||||
return mdl.VisualizationInfo(
|
||||
guid=str(uuid.uuid4()),
|
||||
@@ -222,7 +333,7 @@ def build_viewpoint_from_position_and_guids(position: NDArray[np.float64], *guid
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
This function is cached to speedudp the creation of multiple BCF topics regarding the same element.
|
||||
This function is cached to speed up the creation of multiple BCF topics regarding the same element.
|
||||
|
||||
Args:
|
||||
position: target point coordinates.
|
||||
@@ -243,7 +354,7 @@ def build_components(*guids: str) -> mdl.Components:
|
||||
Return the BCF components from an IFC element GUID.
|
||||
|
||||
Args:
|
||||
*guids: One or more IFC element GUID.
|
||||
*guids: One or more selected IFC element GUID.
|
||||
|
||||
Returns:
|
||||
The BCF components definition.
|
||||
|
||||
@@ -55,9 +55,32 @@ def test_save_maximum_information() -> None:
|
||||
|
||||
def assert_everything_in_place(bcf: BcfXml):
|
||||
assert bcf.version.version_id == "2.1"
|
||||
assert bcf.project
|
||||
assert bcf.project.name == "BCF API Implementation"
|
||||
assert bcf.project_info
|
||||
assert bcf.project_info.extension_schema == "extensions.xsd"
|
||||
|
||||
assert bcf.extensions
|
||||
assert bcf.extensions.topic_types
|
||||
assert bcf.extensions.topic_types.topic_type == ["Architecture", "Hidden Type", "Structural"]
|
||||
assert bcf.extensions.topic_statuses
|
||||
assert bcf.extensions.topic_statuses.topic_status == ["Finished status", "Open", "Closed"]
|
||||
assert bcf.extensions.priorities
|
||||
assert bcf.extensions.priorities.priority == ["Low", "High", "Medium"]
|
||||
assert bcf.extensions.topic_labels
|
||||
assert bcf.extensions.topic_labels.topic_label == [
|
||||
"Architecture",
|
||||
"IT Development",
|
||||
"Management",
|
||||
"Mechanical",
|
||||
"Structural",
|
||||
]
|
||||
assert bcf.extensions.users
|
||||
assert bcf.extensions.users.user == ["dangl@iabi.eu", "linhard@iabi.eu"]
|
||||
assert bcf.extensions.snippet_types
|
||||
assert bcf.extensions.snippet_types.snippet_type == ["IFC2X3", "PDF", "XLSX"]
|
||||
assert bcf.extensions.stages is None
|
||||
|
||||
assert len(bcf.topics) == 2
|
||||
assert_first_topic_handler(bcf.topics["7ddc3ef0-0ab7-43f1-918a-45e38b42369c"])
|
||||
second_th = bcf.topics["d1068c81-af04-4546-b63c-348810f6c716"]
|
||||
|
||||
@@ -39,6 +39,22 @@ def assert_everything_in_place(bcf: BcfXml):
|
||||
assert bcf.project.name == "BCF 3.0 test cases"
|
||||
assert bcf.project.project_id == "de894a86-3a08-4ea0-b2d1-6c222b5602d1"
|
||||
|
||||
assert bcf.extensions
|
||||
assert bcf.extensions.topic_types
|
||||
assert bcf.extensions.topic_types.topic_type == ["ERROR", "WARNING", "INFORMATION", "CLASH", "OTHER"]
|
||||
assert bcf.extensions.topic_statuses
|
||||
assert bcf.extensions.topic_statuses.topic_status == ["OPEN", "IN_PROGRESS", "SOLVED", "CLOSED"]
|
||||
assert bcf.extensions.priorities
|
||||
assert bcf.extensions.priorities.priority == ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
||||
assert bcf.extensions.topic_labels
|
||||
assert bcf.extensions.topic_labels.topic_label == []
|
||||
assert bcf.extensions.users
|
||||
assert bcf.extensions.users.user == ["Architect@example.com", "Engineer@example.com", "MEPDesigner@example.com"]
|
||||
assert bcf.extensions.snippet_types
|
||||
assert bcf.extensions.snippet_types.snippet_type == []
|
||||
assert bcf.extensions.stages
|
||||
assert bcf.extensions.stages.stage == []
|
||||
|
||||
assert len(bcf.topics) == 1
|
||||
topic_handler = bcf.topics["8ac9822a-761a-4deb-9f39-f61286acbf6a"]
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# BlenderBIM Add-on
|
||||
|
||||
An add-on to Blender to allow BIM functionality.
|
||||
|
||||
More information on the [BlenderBIM Add-on website](https://blenderbim.org).
|
||||
@@ -1,181 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import webbrowser
|
||||
|
||||
blenderbim_lib_path = os.environ.get("BLENDERBIM_LIB_PATH")
|
||||
blenderbim_version = os.environ.get("BLENDERBIM_VERSION")
|
||||
|
||||
if blenderbim_lib_path:
|
||||
sys.path.insert(0, blenderbim_lib_path)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# represents a caching for blender messages
|
||||
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}")
|
||||
if blender_messages:
|
||||
await self.send_cached_messages(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.get("blenderId", None),
|
||||
)
|
||||
|
||||
async def on_get_svg(self, sid, data):
|
||||
print("hello world!")
|
||||
file_path = data["path"]
|
||||
with open(file_path, "r") as file:
|
||||
svg_data = file.read()
|
||||
await sio.emit("svg_data", svg_data, room=sid, namespace="/web")
|
||||
|
||||
async def send_cached_messages(self, sid):
|
||||
# Send cached messages to the connected web client
|
||||
for blenderId, messages in blender_messages.items():
|
||||
if "csv_data" in messages:
|
||||
await self.emit("csv_data", {"blenderId": blenderId, "data": messages["csv_data"]}, room=sid)
|
||||
if "gantt_data" in messages:
|
||||
await self.emit("gantt_data", {"blenderId": blenderId, "data": messages["gantt_data"]}, room=sid)
|
||||
|
||||
|
||||
# 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")
|
||||
|
||||
async def on_drawings_data(self, sid, data):
|
||||
print(f"Drawings directory from Blender client {sid}")
|
||||
print(data)
|
||||
blender_messages[sid]["drwings_data"] = data
|
||||
await sio.emit("drawings_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, "version": blenderbim_version})
|
||||
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, "version": blenderbim_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def drawings(request):
|
||||
with open("templates/drawings.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 on_startup(app):
|
||||
pid_file = "running_pid.json"
|
||||
|
||||
if os.path.exists(pid_file):
|
||||
with open(pid_file, "r") as f:
|
||||
pids = json.load(f)
|
||||
else:
|
||||
pids = {}
|
||||
|
||||
pids[str(os.getpid())] = sio_port
|
||||
|
||||
with open(pid_file, "w") as f:
|
||||
json.dump(pids, f, indent=4)
|
||||
|
||||
|
||||
app.router.add_get("/", index)
|
||||
app.router.add_get("/drawings", drawings)
|
||||
app.router.add_get("/gantt", gantt)
|
||||
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
||||
app.router.add_static("/static/", path="./static", name="static")
|
||||
app.on_startup.append(on_startup)
|
||||
|
||||
|
||||
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)
|
||||
@@ -1,249 +0,0 @@
|
||||
:root {
|
||||
--font-family: Arial, sans-serif;
|
||||
--base-font-size: 16px;
|
||||
--margin-tiny: 0.125rem;
|
||||
--margin-small: 0.625rem;
|
||||
--margin-medium: 1.25rem;
|
||||
--margin-large: 2.5rem;
|
||||
--padding-tiny: 0.125rem;
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
color-scheme: dark;
|
||||
--bg-color: #252525;
|
||||
--text-color: #e0e0e0;
|
||||
--nav-bg-color: #121212;
|
||||
--nav-border-color: #25682a;
|
||||
--nav-link-color: #fff;
|
||||
--nav-link-hover-color: #3fb449;
|
||||
--warning-color: #FFDB8F;
|
||||
--border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
color-scheme: light;
|
||||
--bg-color: #ffffff;
|
||||
--text-color: #000000;
|
||||
--nav-bg-color: #f8f8f8;
|
||||
--nav-border-color: #cccccc;
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--border-color: #222;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: var(--base-font-size);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-small);
|
||||
margin-right: var(--margin-small);
|
||||
margin-bottom: var(--margin-medium);
|
||||
height: calc(100vh - var(--nav-height));
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
height: var(--nav-height);
|
||||
padding: var(--padding-medium) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border-bottom: 2px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: var(--margin-large);
|
||||
height: var(--logo-height);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 0.0625rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 3.125rem;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--nav-link-color);
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--nav-link-hover-color);
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-large);
|
||||
margin-right: var(--margin-medium);
|
||||
}
|
||||
|
||||
#toggle-theme:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#client-list {
|
||||
position: absolute;
|
||||
top: calc(0 + var(--nav-height));
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
border: 1px solid var(--table-border-color);
|
||||
background-color: var(--nav-bg-color);
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#client-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#connected-list-div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: var(--padding-small);
|
||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||
background-color: var(--nav-bg-color);
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.client-details.show {
|
||||
max-height: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
.scroll-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scroll-button {
|
||||
font-size: var(--base-font-size);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
background-color: var(--bg-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 4;
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
#svg-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: var(--nav-bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 10px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-header,
|
||||
.card-body {
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--nav-bg-color);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
li.svg-name {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#dropdown-menu:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* remove bootstrap css confilcts */
|
||||
*,
|
||||
::after,
|
||||
::before {
|
||||
box-sizing: unset;
|
||||
}
|
||||
|
||||
.btn,
|
||||
.btn:hover {
|
||||
color: var(--nav-link-hover-color);
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
:root {
|
||||
--font-family: Arial, sans-serif;
|
||||
--base-font-size: 16px;
|
||||
--margin-tiny: 0.125rem;
|
||||
--margin-small: 0.625rem;
|
||||
--margin-medium: 1.25rem;
|
||||
--margin-large: 2.5rem;
|
||||
--padding-tiny: 0.125rem;
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
color-scheme: dark;
|
||||
--bg-color: #252525;
|
||||
--text-color: #e0e0e0;
|
||||
--nav-bg-color: #121212;
|
||||
--nav-border-color: #25682a;
|
||||
--nav-link-color: #fff;
|
||||
--nav-link-hover-color: #3fb449;
|
||||
--warning-color: #FFDB8F;
|
||||
--table-border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
color-scheme: light;
|
||||
--bg-color: #ffffff;
|
||||
--text-color: #000000;
|
||||
--nav-bg-color: #f8f8f8;
|
||||
--nav-border-color: #cccccc;
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--table-border-color: #222;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#container {
|
||||
flex: 1;
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-small);
|
||||
margin-right: var(--margin-small);
|
||||
margin-bottom: var(--margin-medium);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
height: var(--nav-height);
|
||||
padding: var(--padding-medium) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border-bottom: 2px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: var(--margin-large);
|
||||
height: var(--logo-height);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 0.0625rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 3.125rem;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--nav-link-color);
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--nav-link-hover-color);
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-large);
|
||||
margin-right: var(--margin-medium);
|
||||
}
|
||||
|
||||
#toggle-theme:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#client-list {
|
||||
position: absolute;
|
||||
top: calc(0 + var(--nav-height));
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
border: 1px solid var(--table-border-color);
|
||||
background-color: var(--nav-bg-color);
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#client-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#connected-list-div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: var(--padding-small);
|
||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||
background-color: var(--nav-bg-color);
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.client-details.show {
|
||||
max-height: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
.scroll-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scroll-button {
|
||||
font-size: var(--base-font-size);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--nav-bg-color);
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: var(--margin-large);
|
||||
}
|
||||
|
||||
.csv-table {
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||
|
||||
:root.light .tabulator-header,
|
||||
:root.light .tabulator .tabulator-header .tabulator-col {
|
||||
background: var(--nav-bg-color) !important;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
:root.light .tabulator {
|
||||
border-top: 1px solid var(--table-border-color);
|
||||
border-bottom: 2px solid var(--table-border-color);
|
||||
}
|
||||
@@ -1,466 +0,0 @@
|
||||
// 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 () {
|
||||
var systemTheme = window.matchMedia("(prefers-color-scheme: light)").matches
|
||||
? "light"
|
||||
: "dark";
|
||||
var theme = localStorage.getItem("theme") || systemTheme;
|
||||
setTheme(theme);
|
||||
|
||||
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);
|
||||
socket.on("default_data", handleDefaultData);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) - 1;
|
||||
});
|
||||
}
|
||||
|
||||
// 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,
|
||||
ifc_file: filename,
|
||||
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,
|
||||
ifc_file: filename,
|
||||
ganttTasks: ganttTasks,
|
||||
workSchedule: ganttWorkSched,
|
||||
};
|
||||
addGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDefaultData(data) {
|
||||
const blenderId = data["blenderId"];
|
||||
const isDirty = data["data"]["is_dirty"];
|
||||
showWarning(blenderId, isDirty);
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
// Function to add a new gantt with data and filename
|
||||
function addGanttElement(blenderId, tasks, workSched, filename) {
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) + 1;
|
||||
});
|
||||
|
||||
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 warning = $("<div></div>")
|
||||
.attr("id", "warning-" + blenderId)
|
||||
.html(
|
||||
"⚠ Warning: This Gantt Chart may contain outdated data due to recent changes in Blender."
|
||||
)
|
||||
.addClass("warning no-print");
|
||||
|
||||
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(warning);
|
||||
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);
|
||||
$("#warning-" + blenderId).css("display", "none");
|
||||
}
|
||||
|
||||
// Function to remove gantt element
|
||||
function removeGanttElement(blenderId) {
|
||||
$("#container-" + blenderId).remove();
|
||||
}
|
||||
|
||||
function showWarning(blenderId, isDirty) {
|
||||
$("#warning-" + blenderId).css("display", "block");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
if (theme === "light") {
|
||||
$("html").removeClass("dark").addClass("light");
|
||||
$("#toggle-theme").html('<i class="fas fa-sun"></i>');
|
||||
} else {
|
||||
$("html").removeClass("light").addClass("dark");
|
||||
$("#toggle-theme").html('<i class="fas fa-moon"></i>');
|
||||
}
|
||||
localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
if ($("html").hasClass("dark")) {
|
||||
setTheme("light");
|
||||
} else {
|
||||
setTheme("dark");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClientList() {
|
||||
var clientList = $("#client-list");
|
||||
|
||||
if (clientList.hasClass("show")) {
|
||||
clientList.removeClass("show");
|
||||
return;
|
||||
}
|
||||
|
||||
clientList.empty();
|
||||
|
||||
$.each(connectedClients, function (id, client) {
|
||||
if (!client.shown) return;
|
||||
|
||||
const dropdownIcon = $("<i>")
|
||||
.addClass("fas fa-chevron-down")
|
||||
.css("margin-left", "0.5rem");
|
||||
|
||||
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||
|
||||
clientDiv.append(dropdownIcon);
|
||||
|
||||
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||
|
||||
if (id) {
|
||||
const clientId = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Blender ID: ${id}`);
|
||||
clientDetailsDiv.append(clientId);
|
||||
}
|
||||
|
||||
if (client.workSchedule && client.gantt) {
|
||||
const clientScheduleName = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Schedule Name: ${client.workSchedule.Name}`);
|
||||
|
||||
const clientScheduleDate = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(
|
||||
`Schedule Date: ${new Date(
|
||||
client.workSchedule.CreationDate
|
||||
).toLocaleDateString()}`
|
||||
);
|
||||
|
||||
const scrollButton = $("<button>")
|
||||
.addClass("scroll-button")
|
||||
.text("Scroll to Gantt Chart")
|
||||
.on("click", function () {
|
||||
$("html, body").animate(
|
||||
{
|
||||
scrollTop: $("#gantt-" + id).offset().top,
|
||||
},
|
||||
600
|
||||
);
|
||||
clientList.removeClass("show");
|
||||
});
|
||||
|
||||
clientDetailsDiv.append(clientScheduleName);
|
||||
clientDetailsDiv.append(clientScheduleDate);
|
||||
clientDetailsDiv.append(scrollButton);
|
||||
}
|
||||
|
||||
clientDiv.append(clientDetailsDiv);
|
||||
|
||||
clientDiv.on("click", function () {
|
||||
clientDetailsDiv.toggleClass("show");
|
||||
});
|
||||
|
||||
clientList.append(clientDiv);
|
||||
});
|
||||
clientList.addClass("show");
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<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" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.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>
|
||||
<li><a href="/drawings">Drawings</a></li>
|
||||
</ul>
|
||||
<button id="toggle-theme" onclick="toggleTheme()">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</nav>
|
||||
<div id="connected-list-div">
|
||||
<button id="show-connected-button" onclick="toggleClientList()">
|
||||
Connected Blenders:
|
||||
<span id="blender-count">0</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div id="client-list"></div>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
<footer>
|
||||
<p>BlenderBIM Version: {{version}}</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ceiling": {
|
||||
"Black Ceiling 1": "void plastic black_ceiling\n0\n0\n5 0.0460 0.0456 0.0463 0.0052 0.2000",
|
||||
"Black Ceiling 2": "void plastic black_ceiling\n0\n0\n5 0.1116 0.1085 0.0938 0.0000 0.0000",
|
||||
"Plastic Ceiling Vent E14 526": "void plastic plastic_ceiling_vent_e14_526\n0\n0\n5 0.7384 0.7195 0.6540 0.0110 0.0500",
|
||||
"Specular Reflective Ceiling Panels": "void plastic specular_reflective_ceiling_panels\n0\n0\n5 0.7708 0.7673 0.6819 0.0741 0.0500"
|
||||
},
|
||||
"Door": {
|
||||
"Wooden Door": "void plastic wooden_door\n0\n0\n5 0.6164 0.4257 0.2156 0.0185 0.2000",
|
||||
"Wooden Door 2": "void plastic wooden_door_2\n0\n0\n5 0.0403 0.0388 0.0406 0.0121 0.2000",
|
||||
"White Wooden Door": "void plastic white_wooden_door\n0\n0\n5 0.7893 0.7502 0.6413 0.0080 0.2000",
|
||||
"Off White Door": "void plastic off_white_door\n0\n0\n5 0.8607 0.8611 0.7852 0.0258 0.2000",
|
||||
"Bedroom Door": "void plastic bedroom_door\n0\n0\n5 0.2508 0.0782 0.0111 0.0279 0.2000"
|
||||
},
|
||||
"Floor": {
|
||||
"Light Blue Ceramic": "void plastic light_blue_ceramic\n0\n0\n5 0.7477 0.7776 0.7807 0.0323 0.1000",
|
||||
"Light Grey Ceramic": "void plastic light_grey_ceramic\n0\n0\n5 0.6386 0.5941 0.4982 0.0106 0.2000",
|
||||
"Purple Carpet": "void plastic purple_carpet\n0\n0\n5 0.0425 0.0179 0.0371 0.0001 0.3000"
|
||||
},
|
||||
"Wall": {
|
||||
"Dirty Dark Pink Painted Wall": "void plastic dirty_dark_pink_painted_wall\n0\n0\n5 0.3533 0.1751 0.1690 0.0000 0.2000",
|
||||
"Blue Green Painted Walls": "void plastic blue_green_painted_walls\n0\n0\n5 -0.0013 0.2092 0.2601 0.0140 0.2000",
|
||||
"Red Painted Wall": "void plastic red_painted_wall\n0\n0\n5 0.4621 0.0156 0.0091 0.0396 0.2000"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,293 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gpu
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
from math import sin, cos, radians
|
||||
from bpy.types import SpaceView3D
|
||||
from mathutils import Vector, Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from typing import Union
|
||||
|
||||
|
||||
def transparent_color(color, alpha=0.1):
|
||||
color = [i for i in color]
|
||||
color[3] = alpha
|
||||
return color
|
||||
|
||||
|
||||
class ProfileDecorator:
|
||||
installed = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
|
||||
"""Note that operators that change mesh in `exit_edit_mode_callback` can freeze blender.
|
||||
The workaround is to move their code to function and use it for callback.
|
||||
|
||||
Example: https://devtalk.blender.org/t/calling-operator-that-saves-bmesh-freezes-blender-forever/28595"""
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.installed = SpaceView3D.draw_handler_add(
|
||||
handler, (context, get_custom_bmesh, draw_faces, exit_edit_mode_callback), "WINDOW", "POST_VIEW"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.installed = None
|
||||
|
||||
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_faces(self, bm, vertices_coords):
|
||||
"""mutates original bm (triangulates it)
|
||||
so the triangulation edges will be shown too
|
||||
"""
|
||||
traingulated_bm = bm
|
||||
bmesh.ops.triangulate(traingulated_bm, faces=traingulated_bm.faces)
|
||||
|
||||
face_indices = [[v.index for v in f.verts] for f in traingulated_bm.faces]
|
||||
faces_color = transparent_color(self.addon_prefs.decorator_color_special)
|
||||
self.draw_batch("TRIS", vertices_coords, faces_color, face_indices)
|
||||
|
||||
def __call__(self, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
|
||||
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
|
||||
error_elements_color = self.addon_prefs.decorator_color_error
|
||||
background_elements_color = self.addon_prefs.decorator_color_background
|
||||
|
||||
obj = context.active_object
|
||||
|
||||
if obj.mode != "EDIT":
|
||||
if exit_edit_mode_callback:
|
||||
ProfileDecorator.uninstall()
|
||||
exit_edit_mode_callback()
|
||||
return
|
||||
|
||||
if get_custom_bmesh:
|
||||
bm = get_custom_bmesh()
|
||||
else:
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
### Actually drawing
|
||||
all_vertices = []
|
||||
error_vertices = []
|
||||
selected_vertices = []
|
||||
unselected_vertices = []
|
||||
# special = associated with arcs/circles
|
||||
special_vertices = []
|
||||
special_vertex_indices = {}
|
||||
selected_edges = []
|
||||
unselected_edges = []
|
||||
arc_edges = []
|
||||
roof_angle_edges = []
|
||||
preview_edges = []
|
||||
|
||||
arc_groups = []
|
||||
circle_groups = []
|
||||
for i, group in enumerate(obj.vertex_groups):
|
||||
if "IFCARCINDEX" in group.name:
|
||||
arc_groups.append(i)
|
||||
elif "IFCCIRCLE" in group.name:
|
||||
circle_groups.append(i)
|
||||
|
||||
arcs = {}
|
||||
circles = {}
|
||||
|
||||
# https://docs.blender.org/api/blender_python_api_2_63_8/bmesh.html#CustomDataAccess
|
||||
# This is how we access vertex groups via bmesh, apparently, it's not very intuitive
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
angle_layer = bm.edges.layers.float.get("BBIM_gable_roof_angles")
|
||||
preview_layer = bm.edges.layers.int.get("BBIM_preview")
|
||||
|
||||
for vertex in bm.verts:
|
||||
co = tuple(obj.matrix_world @ vertex.co)
|
||||
all_vertices.append(co)
|
||||
if vertex.hide:
|
||||
continue
|
||||
|
||||
is_arc, is_circle = False, False
|
||||
# deform_layer is None if there are no verts assigned to vertex groups
|
||||
# even if there are vertex groups in the obj.vertex_groups
|
||||
if deform_layer:
|
||||
is_arc, group_index = tool.Blender.bmesh_check_vertex_in_groups(vertex, deform_layer, arc_groups)
|
||||
if is_arc:
|
||||
arcs.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
is_circle, group_index = tool.Blender.bmesh_check_vertex_in_groups(vertex, deform_layer, circle_groups)
|
||||
if is_circle:
|
||||
circles.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
if vertex.select:
|
||||
selected_vertices.append(co)
|
||||
else:
|
||||
if len(vertex.link_edges) > 1 and is_circle:
|
||||
error_vertices.append(co)
|
||||
elif is_circle:
|
||||
special_vertices.append(co)
|
||||
elif len(vertex.link_edges) != 2:
|
||||
error_vertices.append(co)
|
||||
elif is_arc:
|
||||
special_vertices.append(co)
|
||||
else:
|
||||
unselected_vertices.append(co)
|
||||
|
||||
for edge in bm.edges:
|
||||
edge_indices = [v.index for v in edge.verts]
|
||||
if edge.hide:
|
||||
continue
|
||||
if edge.select:
|
||||
selected_edges.append(edge_indices)
|
||||
else:
|
||||
i1, i2 = edge.verts[0].index, edge.verts[1].index
|
||||
# making sure that both vertices are in the same group
|
||||
if i1 in special_vertex_indices and special_vertex_indices[i1] == special_vertex_indices.get(i2, None):
|
||||
arc_edges.append(edge_indices)
|
||||
elif angle_layer and edge[angle_layer] > 0:
|
||||
roof_angle_edges.append(edge_indices)
|
||||
elif preview_layer and edge[preview_layer] == 1:
|
||||
preview_edges.append(edge_indices)
|
||||
else:
|
||||
unselected_edges.append(edge_indices)
|
||||
|
||||
### Actually drawing
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
# Draw faces
|
||||
if draw_faces:
|
||||
self.draw_faces(bm, all_vertices)
|
||||
|
||||
self.draw_batch("LINES", all_vertices, transparent_color(unselected_elements_color), unselected_edges)
|
||||
self.draw_batch("LINES", all_vertices, selected_elements_color, selected_edges)
|
||||
self.draw_batch("LINES", all_vertices, background_elements_color, arc_edges)
|
||||
self.draw_batch("LINES", all_vertices, special_elements_color, preview_edges)
|
||||
self.draw_batch("LINES", all_vertices, special_elements_color, roof_angle_edges)
|
||||
|
||||
self.draw_batch("POINTS", unselected_vertices, transparent_color(unselected_elements_color, 0.5))
|
||||
self.draw_batch("POINTS", error_vertices, error_elements_color)
|
||||
self.draw_batch("POINTS", special_vertices, special_elements_color)
|
||||
self.draw_batch("POINTS", selected_vertices, selected_elements_color)
|
||||
|
||||
# Draw arcs
|
||||
arc_centroids = []
|
||||
arc_segments = []
|
||||
for arc in arcs.values():
|
||||
if len(arc) != 3:
|
||||
continue
|
||||
sorted_arc = [None, None, None]
|
||||
for v1 in arc:
|
||||
connections = 0
|
||||
for link_edge in v1.link_edges:
|
||||
v2 = link_edge.other_vert(v1)
|
||||
if v2 in arc:
|
||||
connections += 1
|
||||
if connections == 2: # Midpoint
|
||||
sorted_arc[1] = v1
|
||||
else:
|
||||
sorted_arc[2 if sorted_arc[2] is None else 0] = v1
|
||||
points = [tuple(obj.matrix_world @ v.co) for v in sorted_arc]
|
||||
centroid = tool.Cad.get_center_of_arc(points)
|
||||
if centroid:
|
||||
arc_centroids.append(tuple(centroid))
|
||||
arc_segments.append(tool.Cad.create_arc_segments(pts=points, num_verts=17, make_edges=True))
|
||||
|
||||
self.draw_batch("POINTS", arc_centroids, background_elements_color)
|
||||
for verts, edges in arc_segments:
|
||||
self.draw_batch("LINES", verts, special_elements_color, edges)
|
||||
|
||||
# Draw circles
|
||||
circle_centroids = []
|
||||
circle_segments = []
|
||||
for circle in circles.values():
|
||||
if len(circle) != 2:
|
||||
continue
|
||||
p1 = obj.matrix_world @ circle[0].co
|
||||
p2 = obj.matrix_world @ circle[1].co
|
||||
radius = (p2 - p1).length / 2
|
||||
centroid = p1.lerp(p2, 0.5)
|
||||
circle_centroids.append(tuple(centroid))
|
||||
segments = self.create_circle_segments(360, 20, radius)
|
||||
matrix = obj.matrix_world.copy()
|
||||
matrix.translation = centroid
|
||||
segments = [[list(matrix @ Vector(v)) for v in segments[0]], segments[1]]
|
||||
circle_segments.append(segments)
|
||||
|
||||
self.draw_batch("POINTS", circle_centroids, background_elements_color)
|
||||
for verts, edges in circle_segments:
|
||||
self.draw_batch("LINES", verts, special_elements_color, edges)
|
||||
|
||||
def create_matrix(self, p, x, y, z):
|
||||
return Matrix([x, y, z, p]).to_4x4().transposed()
|
||||
|
||||
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
|
||||
# This function is taken from Sverchok, licensed under GPL v2-or-later.
|
||||
# This is a combination of the make_verts and make_edges function.
|
||||
def create_circle_segments(self, Angle, Vertices, Radius):
|
||||
if Angle < 360:
|
||||
theta = Angle / (Vertices - 1)
|
||||
else:
|
||||
theta = Angle / Vertices
|
||||
listVertX = []
|
||||
listVertY = []
|
||||
for i in range(Vertices):
|
||||
listVertX.append(Radius * cos(radians(theta * i)))
|
||||
listVertY.append(Radius * sin(radians(theta * i)))
|
||||
|
||||
if Angle < 360 and self.mode_ == 0:
|
||||
sigma = radians(Angle)
|
||||
listVertX[-1] = Radius * cos(sigma)
|
||||
listVertY[-1] = Radius * sin(sigma)
|
||||
elif Angle < 360 and self.mode_ == 1:
|
||||
listVertX.append(0.0)
|
||||
listVertY.append(0.0)
|
||||
|
||||
points = list((x, y, 0) for x, y in zip(listVertX, listVertY))
|
||||
|
||||
listEdg = [(i, i + 1) for i in range(Vertices - 1)]
|
||||
|
||||
if Angle < 360 and self.mode_ == 1:
|
||||
listEdg.append((0, Vertices))
|
||||
listEdg.append((Vertices - 1, Vertices))
|
||||
else:
|
||||
listEdg.append((Vertices - 1, 0))
|
||||
|
||||
return points, listEdg
|
||||
@@ -1,90 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.core.aggregate
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class OpenPieClass(bpy.types.Operator):
|
||||
bl_idname = "bim.open_pie_class"
|
||||
bl_label = "Open Pie Class"
|
||||
bl_description = "Assign the IFC Class to the selected objects"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.active_object and not context.selected_objects:
|
||||
cls.poll_message_set("No object selected.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_bim_class")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PieAddOpening(bpy.types.Operator):
|
||||
bl_idname = "bim.pie_add_opening"
|
||||
bl_label = "Add Opening"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
if len(context.selected_objects) == 2:
|
||||
opening_name = None
|
||||
obj_name = None
|
||||
for obj in context.selected_objects:
|
||||
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
|
||||
opening_name = obj.name
|
||||
elif len(obj.children) == 1 and not obj.children[0].BIMObjectProperties.ifc_definition_id:
|
||||
opening_name = obj.children[0].name
|
||||
else:
|
||||
obj_name = obj.name
|
||||
bpy.ops.bim.add_opening(obj=obj_name, opening=opening_name)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
bl_label = "Geometry"
|
||||
|
||||
def draw(self, context):
|
||||
pie = self.layout.menu_pie()
|
||||
pie.operator("bim.edit_object_placement")
|
||||
pie.operator("bim.update_representation").ifc_representation_class = ""
|
||||
pie.operator("bim.pie_add_opening")
|
||||
pie.operator("bim.open_pie_class", text="Assign IFC Class")
|
||||
pie.operator("bim.aggregate_assign_object", text="Assign Aggregation")
|
||||
pie.operator("bim.aggregate_unassign_object", text="Unassign Aggregation")
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim_class(bpy.types.Menu):
|
||||
bl_label = "Class"
|
||||
|
||||
def draw(self, context):
|
||||
pie = self.layout.menu_pie()
|
||||
pie.operator("bim.assign_class", text="IfcWall").ifc_class = "IfcWall"
|
||||
pie.operator("bim.assign_class", text="IfcSlab").ifc_class = "IfcSlab"
|
||||
pie.operator("bim.assign_class", text="IfcStair").ifc_class = "IfcStair"
|
||||
pie.operator("bim.assign_class", text="IfcDoor").ifc_class = "IfcDoor"
|
||||
pie.operator("bim.assign_class", text="IfcWindow").ifc_class = "IfcWindow"
|
||||
pie.operator("bim.assign_class", text="IfcColumn").ifc_class = "IfcColumn"
|
||||
pie.operator("bim.assign_class", text="IfcBeam").ifc_class = "IfcBeam"
|
||||
@@ -1,64 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 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/>.
|
||||
|
||||
from blenderbim.tool.aggregate import Aggregate
|
||||
from blenderbim.tool.blender import Blender
|
||||
from blenderbim.tool.boundary import Boundary
|
||||
from blenderbim.tool.brick import Brick
|
||||
from blenderbim.tool.bsdd import Bsdd
|
||||
from blenderbim.tool.cad import Cad
|
||||
from blenderbim.tool.clash import Clash
|
||||
from blenderbim.tool.classification import Classification
|
||||
from blenderbim.tool.collector import Collector
|
||||
from blenderbim.tool.context import Context
|
||||
from blenderbim.tool.debug import Debug
|
||||
from blenderbim.tool.demo import Demo
|
||||
from blenderbim.tool.document import Document
|
||||
from blenderbim.tool.drawing import Drawing
|
||||
from blenderbim.tool.geometry import Geometry
|
||||
from blenderbim.tool.georeference import Georeference
|
||||
from blenderbim.tool.ifc import Ifc
|
||||
from blenderbim.tool.ifcgit import IfcGit
|
||||
from blenderbim.tool.ifcgit import IfcGitRepo
|
||||
from blenderbim.tool.library import Library
|
||||
from blenderbim.tool.loader import Loader
|
||||
from blenderbim.tool.material import Material
|
||||
from blenderbim.tool.misc import Misc
|
||||
from blenderbim.tool.model import Model
|
||||
from blenderbim.tool.nest import Nest
|
||||
from blenderbim.tool.owner import Owner
|
||||
from blenderbim.tool.patch import Patch
|
||||
from blenderbim.tool.project import Project
|
||||
from blenderbim.tool.profile import Profile
|
||||
from blenderbim.tool.pset import Pset
|
||||
from blenderbim.tool.qto import Qto
|
||||
from blenderbim.tool.resource import Resource
|
||||
from blenderbim.tool.root import Root
|
||||
from blenderbim.tool.sequence import Sequence
|
||||
from blenderbim.tool.spatial import Spatial
|
||||
from blenderbim.tool.covering import Covering
|
||||
from blenderbim.tool.structural import Structural
|
||||
from blenderbim.tool.style import Style
|
||||
from blenderbim.tool.surveyor import Surveyor
|
||||
from blenderbim.tool.system import System
|
||||
from blenderbim.tool.tester import Tester
|
||||
from blenderbim.tool.type import Type
|
||||
from blenderbim.tool.unit import Unit
|
||||
from blenderbim.tool.search import Search
|
||||
from blenderbim.tool.cost import Cost
|
||||
from blenderbim.tool.web import Web
|
||||
@@ -1,432 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>, 2022 Yassine Oualid <yassine@sigmadimensions.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 blenderbim.bim.module.web.data import WebData
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.api.sequence
|
||||
from typing import Any, Dict, Optional
|
||||
import time
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import errno
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import asyncio
|
||||
import socketio
|
||||
import threading
|
||||
import queue
|
||||
import json
|
||||
from time import sleep
|
||||
from pathlib import Path
|
||||
|
||||
sio = None
|
||||
ws_process = None
|
||||
ws_thread = None
|
||||
web_operator_queue = queue.Queue()
|
||||
|
||||
RECONNECTION_ATTEMPTS = 3
|
||||
RECONNECTION_DELAY = 2
|
||||
IFC_TASK_ATTRIBUTE_MAP = {
|
||||
"pStart": "ScheduleStart",
|
||||
"pEnd": "ScheduleFinish",
|
||||
"pName": "Name",
|
||||
}
|
||||
|
||||
|
||||
class Web(blenderbim.core.tool.Web):
|
||||
@classmethod
|
||||
def generate_port_number(cls) -> int:
|
||||
"""
|
||||
Generate a free port number.
|
||||
|
||||
This method creates a temporary socket to bind to a free port.
|
||||
It then retrieves the port number, and returns it.
|
||||
|
||||
Returns:
|
||||
int: The port number that was generated.
|
||||
"""
|
||||
print("Generating port number")
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("localhost", 0)) # Bind to a free port
|
||||
port = s.getsockname()[1] # get the bound port
|
||||
print(f"Port number: {port}")
|
||||
return port
|
||||
|
||||
@classmethod
|
||||
def is_port_available(cls, port: int) -> bool:
|
||||
"""
|
||||
Attempts to connect to the specified port on localhost.
|
||||
|
||||
If the connection is refused, the port is available for use; otherwise, it is in use.
|
||||
|
||||
Args:
|
||||
- port (int): The port number to check.
|
||||
|
||||
Returns:
|
||||
bool: True if the port is available, False if it is in use.
|
||||
"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
# connect_ex returns errno.SUCCESS (0) if the connection succeeds
|
||||
# otherwise returns errno.ECONNREFUSED (111 or 10061) if no server is listening
|
||||
return s.connect_ex(("localhost", port)) == errno.ECONNREFUSED
|
||||
|
||||
@classmethod
|
||||
def start_websocket_server(cls, port: int) -> None:
|
||||
"""
|
||||
Starts a WebSocket server on the specified port.
|
||||
|
||||
This method sets up the environment, locates paths, and starts
|
||||
the WebSocket server process. It also handles the creation and updating of a PID file to keep track
|
||||
of running server instances.
|
||||
|
||||
Args:
|
||||
- port (int): The port number on which to start the WebSocket server.
|
||||
"""
|
||||
import addon_utils
|
||||
|
||||
global ws_process
|
||||
|
||||
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
||||
ws_path = os.path.join(webui_path, "sioserver.py")
|
||||
|
||||
py_version = sys.version_info
|
||||
|
||||
if bpy.app.version >= (4, 2, 0):
|
||||
blenderbim_lib_path = (
|
||||
Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||
/ ".local"
|
||||
/ "lib"
|
||||
/ f"python{py_version.major}.{py_version.minor}"
|
||||
/ "site-packages"
|
||||
)
|
||||
else:
|
||||
addon = [a for a in addon_utils.modules() if a.bl_info["name"] == "BlenderBIM"][0]
|
||||
blenderbim_path = os.path.dirname(addon.__file__)
|
||||
blenderbim_lib_path = os.path.join(blenderbim_path, "libs", "site", "packages")
|
||||
|
||||
env = os.environ.copy()
|
||||
env["BLENDERBIM_LIB_PATH"] = str(blenderbim_lib_path)
|
||||
env["BLENDERBIM_VERSION"] = tool.Blender.get_blenderbim_version()
|
||||
|
||||
ws_process = subprocess.Popen(
|
||||
[sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"],
|
||||
cwd=webui_path,
|
||||
env=env,
|
||||
)
|
||||
|
||||
cls.set_is_running(True)
|
||||
|
||||
@classmethod
|
||||
def connect_websocket_server(cls, port: int) -> None:
|
||||
"""
|
||||
Connect to a WebSocket server on the specified port.
|
||||
|
||||
This method sets up an asynchronous Socket.IO client with
|
||||
reconnection attempts, starts an asyncio thread, connects to the WebSocket server, and sets the connection status.
|
||||
|
||||
Args:
|
||||
- port (int): The port number to connect to the WebSocket server.
|
||||
"""
|
||||
global ws_thread, sio
|
||||
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
print(f"Already connected to websocket server on port: {port}")
|
||||
return
|
||||
|
||||
sio = socketio.AsyncClient(
|
||||
reconnection=True,
|
||||
reconnection_attempts=RECONNECTION_ATTEMPTS,
|
||||
reconnection_delay=RECONNECTION_DELAY,
|
||||
logger=True,
|
||||
)
|
||||
|
||||
ws_thread = AsyncioThread()
|
||||
ws_thread.daemon = True
|
||||
ws_thread.start()
|
||||
|
||||
ws_url = f"ws://localhost:{port}/blender"
|
||||
ws_thread.run_coro(cls.sio_connect(ws_url))
|
||||
cls.set_is_connected(True)
|
||||
bpy.app.timers.register(cls.check_operator_queue)
|
||||
|
||||
@classmethod
|
||||
def disconnect_websocket_server(cls) -> None:
|
||||
"""
|
||||
Disconnects the WebSocket server and stops the associated thread.
|
||||
|
||||
This method is responsible for disconnecting the WebSocket server, stopping the asyncio thread,
|
||||
and resetting the global variables related to the WebSocket connection.
|
||||
"""
|
||||
global ws_thread, sio
|
||||
ws_thread.run_coro(cls.sio_disconnect())
|
||||
ws_thread.stop()
|
||||
ws_thread = None
|
||||
sio = None
|
||||
cls.set_is_connected(False)
|
||||
|
||||
@classmethod
|
||||
def kill_websocket_server(cls) -> None:
|
||||
"""
|
||||
Terminate the currently running WebSocket server.
|
||||
|
||||
This method checks if there is an active WebSocket server process. If so, it disconnects it (if connected),
|
||||
removes its PID from the PID file, terminates the process, and updates the server's running status.
|
||||
"""
|
||||
global ws_process
|
||||
|
||||
if ws_process is None:
|
||||
print("No Websocket server running")
|
||||
return
|
||||
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
cls.disconnect_websocket_server()
|
||||
|
||||
# sleep(0.5)
|
||||
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
||||
pid_file = os.path.join(webui_path, "running_pid.json")
|
||||
with open(pid_file, "r") as f:
|
||||
pids = json.load(f)
|
||||
|
||||
if str(ws_process.pid) in pids:
|
||||
del pids[str(ws_process.pid)]
|
||||
|
||||
# Write the updated PIDs back to the file
|
||||
with open(pid_file, "w") as f:
|
||||
json.dump(pids, f, indent=4)
|
||||
|
||||
ws_process.kill()
|
||||
ws_process = None
|
||||
|
||||
cls.set_is_running(False)
|
||||
print("Websocket server killed successfully")
|
||||
|
||||
@classmethod
|
||||
def has_started(cls, port):
|
||||
max_time = 5
|
||||
start = time.time()
|
||||
while True:
|
||||
if time.time() - start > max_time:
|
||||
return False
|
||||
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
||||
pid_file = os.path.join(webui_path, "running_pid.json")
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
data = json.load(f)
|
||||
if port in data.values():
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
time.sleep(0.1)
|
||||
|
||||
@classmethod
|
||||
def send_webui_data(
|
||||
cls,
|
||||
data: Optional[Any] = None,
|
||||
data_key: str = "data",
|
||||
event: str = "data",
|
||||
namespace: str = "/blender",
|
||||
use_web_data: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Sends data to the Web UI via Websocket connection.
|
||||
|
||||
Args:
|
||||
- data (Optional[Any]): The data to send. If None, just sends data from WebData.
|
||||
- data_key (str): The key under which to store the data in the payload. Defaults to "data".
|
||||
- event (str): The WebSocket event to emit. Defaults to "data".
|
||||
- namespace (str): The namespace for the WebSocket event. Defaults to "/blender".
|
||||
- use_web_data (bool): Whether to use data from WebData. Defaults to True.
|
||||
"""
|
||||
|
||||
global ws_thread
|
||||
payload = {}
|
||||
|
||||
if use_web_data:
|
||||
if not WebData.is_loaded:
|
||||
WebData.load()
|
||||
payload = WebData.data
|
||||
|
||||
if data is not None:
|
||||
payload[data_key] = data
|
||||
|
||||
if ws_thread is not None and bpy.context.scene.WebProperties.is_connected:
|
||||
ws_thread.run_coro(cls.sio_send(payload, event, namespace))
|
||||
|
||||
@classmethod
|
||||
def check_operator_queue(cls) -> None | float:
|
||||
if not bpy.context.scene.WebProperties.is_connected:
|
||||
with web_operator_queue.mutex:
|
||||
web_operator_queue.queue.clear()
|
||||
return None # unregister timer if not connected
|
||||
|
||||
while not web_operator_queue.empty():
|
||||
operator = web_operator_queue.get_nowait()
|
||||
if not operator:
|
||||
continue
|
||||
if operator["sourcePage"] == "csv":
|
||||
cls.handle_csv_operator(operator["operator"])
|
||||
elif operator["sourcePage"] == "gantt":
|
||||
cls.handle_gantt_operator(operator["operator"])
|
||||
elif operator["sourcePage"] == "drawings":
|
||||
cls.handle_drawings_operator(operator["operator"])
|
||||
return 1.0
|
||||
|
||||
@classmethod
|
||||
def handle_csv_operator(cls, operator_data: dict) -> None:
|
||||
if operator_data["type"] == "selection":
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
guid = operator_data["globalId"]
|
||||
ele = tool.Ifc.get().by_guid(guid)
|
||||
obj = tool.Ifc.get_object(ele)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def handle_gantt_operator(cls, operator_data: dict) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
if operator_data["type"] == "editTask":
|
||||
task_id = int(operator_data["taskId"])
|
||||
task = ifc_file.by_id(task_id)
|
||||
task_time = task.TaskTime
|
||||
column = operator_data["column"]
|
||||
new_value = operator_data["value"]
|
||||
|
||||
try:
|
||||
ifcopenshell.api.sequence.edit_task(
|
||||
ifc_file, task, attributes={IFC_TASK_ATTRIBUTE_MAP[column]: str(new_value)}
|
||||
)
|
||||
except AttributeError:
|
||||
if task_time is None:
|
||||
ifcopenshell.api.sequence.add_task_time(ifc_file, task)
|
||||
task_time = task.TaskTime
|
||||
ifcopenshell.api.sequence.edit_task_time(
|
||||
ifc_file, task_time=task_time, attributes={IFC_TASK_ATTRIBUTE_MAP[column]: str(new_value)}
|
||||
)
|
||||
|
||||
bpy.ops.bim.load_task_properties()
|
||||
|
||||
# after updating, send new gantt data to handle the case where
|
||||
# changing a task cascades and changes other tasks. as this wouldn't
|
||||
# be reflected in the web ui
|
||||
work_schedule = ifc_file.by_id(operator_data["workScheduleId"])
|
||||
task_json = tool.Sequence.create_tasks_json(work_schedule)
|
||||
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
||||
cls.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
||||
|
||||
@classmethod
|
||||
def handle_drawings_operator(cls, operator_data: dict) -> None:
|
||||
if operator_data["type"] == "getDrawings":
|
||||
drawings_data = []
|
||||
sheets_data = []
|
||||
ifc_file_dir = os.path.dirname(bpy.context.scene.BIMProperties.ifc_file)
|
||||
|
||||
sheets = [d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SHEET"]
|
||||
for sheet in sorted(sheets, key=lambda s: getattr(s, "Identification", getattr(s, "DocumentId", None))):
|
||||
for reference in tool.Drawing.get_document_references(sheet):
|
||||
reference_description = tool.Drawing.get_reference_description(reference)
|
||||
reference_location = tool.Drawing.get_reference_location(reference)
|
||||
reference_name = os.path.basename(reference_location)
|
||||
reference_path = os.path.join(ifc_file_dir, reference_location)
|
||||
if reference_description == "SHEET":
|
||||
sheets_data.append({"name": reference_name, "path": reference_path})
|
||||
if reference_description == "DRAWING":
|
||||
drawings_data.append({"name": reference_name, "path": reference_path})
|
||||
|
||||
cls.send_webui_data(
|
||||
data={"drawings": drawings_data, "sheets": sheets_data}, data_key="drawings_data", event="drawings_data"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def open_web_browser(cls, port: int) -> None:
|
||||
webbrowser.open(f"http://127.0.0.1:{port}/")
|
||||
|
||||
@classmethod
|
||||
async def sio_connect(cls, url: str) -> None:
|
||||
await sio.connect(url, transports=["websocket"], namespaces="/blender")
|
||||
sio.on("web_operator", cls.sio_listen_web_operator, namespace="/blender")
|
||||
|
||||
@classmethod
|
||||
async def sio_disconnect(cls) -> None:
|
||||
await sio.disconnect()
|
||||
|
||||
@classmethod
|
||||
async def sio_send(cls, data: Any, event: str = "data", namespace: str = "/blender") -> None:
|
||||
await sio.emit(event, data, namespace=namespace)
|
||||
|
||||
@classmethod
|
||||
async def sio_listen_web_operator(cls, data: dict) -> None:
|
||||
try:
|
||||
web_operator_queue.put_nowait(data)
|
||||
except queue.Full:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def set_is_running(cls, is_running: bool) -> None:
|
||||
bpy.context.scene.WebProperties.is_running = is_running
|
||||
|
||||
@classmethod
|
||||
def set_is_connected(cls, is_connected: bool) -> None:
|
||||
bpy.context.scene.WebProperties.is_connected = is_connected
|
||||
|
||||
|
||||
class AsyncioThread(threading.Thread):
|
||||
def __init__(self, *args, loop=None, **kwargs):
|
||||
"""
|
||||
Initialize an instance of AsyncioThread.
|
||||
|
||||
This class represents a thread that runs an asyncio event loop. It is used to handle asynchronous tasks
|
||||
in a separate thread from the main thread.
|
||||
|
||||
Args:
|
||||
- *args: Variable length argument list. These arguments are passed to the superclass constructor.
|
||||
- loop: An existing asyncio event loop. If None, a new event loop is created.
|
||||
- **kwargs: Arbitrary keyword arguments. These arguments are passed to the superclass constructor.
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.loop = loop or asyncio.new_event_loop()
|
||||
self.running = False
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Start the asyncio event loop and mark the thread as running.
|
||||
"""
|
||||
self.running = True
|
||||
self.loop.run_forever()
|
||||
|
||||
def run_coro(self, coro) -> Any:
|
||||
"""
|
||||
Run a coroutine in the asyncio event loop from a separate thread.
|
||||
|
||||
Args:
|
||||
- coro: The coroutine to be run.
|
||||
|
||||
Returns:
|
||||
The result of the coroutine.
|
||||
"""
|
||||
return asyncio.run_coroutine_threadsafe(coro, loop=self.loop).result()
|
||||
|
||||
def stop(self) -> None:
|
||||
"""
|
||||
Stop the asyncio event loop and join the thread.
|
||||
"""
|
||||
self.loop.call_soon_threadsafe(self.loop.stop)
|
||||
self.join()
|
||||
self.running = False
|
||||
@@ -1,20 +0,0 @@
|
||||
Code style
|
||||
============
|
||||
|
||||
|
||||
Black code formatter
|
||||
-------------------------------
|
||||
For Python code formatting, we use `Black code formatter <https://pypi.org/project/black/>`__ with ``--line-length 120``.
|
||||
|
||||
``black`` can be installed using ``pip install black`` and files can be formatted with the following example command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
black --line-length 120 src/blenderbim/blenderbim/bim/module/qto/operator.py
|
||||
|
||||
Using PowerShell, you can run the Black formatter on the last commit in the repository.
|
||||
You can change ``~1`` to ``~n`` to affect ``n`` commits.
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
git diff HEAD HEAD~1 --name-only | where {$_ -like "*.py"} | foreach-object { start $_ && black --line-length 120 $_ }
|
||||
@@ -1,29 +0,0 @@
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
BlenderBIM is an open-source project, and its development is driven by the contributions of a dedicated community of developers,
|
||||
architects, engineers, and enthusiasts. If you're interested in contributing to the project, whether by submitting bug reports,
|
||||
suggesting new features, or contributing code, your involvement is highly encouraged and appreciated.
|
||||
|
||||
This part of the documentation covers various aspects of the BlenderBIM development process, including:
|
||||
|
||||
- :doc:`Writing User Documentation </contribute/writing_docs>`
|
||||
- :doc:`Translations and Internationalisation </contribute/translations>`
|
||||
- Contributing Code
|
||||
- :doc:`Installation and Setting up a Development Environment </devs/installation>`
|
||||
- Understanding the Project Structure and Codebase
|
||||
- :doc:`Hello, World! </devs/hello_world>`
|
||||
- :doc:`Undo System </devs/undo_system>`
|
||||
- :doc:`Code Style Guidelines and Best Practices </devs/code_style>`
|
||||
|
||||
..
|
||||
- :doc:`Submitting Pull Requests and Contributing Code </devs/contributing_code>`
|
||||
|
||||
- :doc:`Testing and Quality Assurance </devs/running_tests>`
|
||||
- :doc:`Running Tests </devs/running_tests>`
|
||||
- :doc:`System Support and Multiplatform Compatibility </devs/system_support>`
|
||||
- :doc:`User Experience and User Interface Guidelines </devs/ux_guidelines>`
|
||||
|
||||
The BlenderBIM Developer Documentation is a living resource maintained by the core development team and the open-source community. It serves as a central hub for developers who want to get involved in the project, ensuring a consistent and efficient development process.
|
||||
|
||||
By contributing to BlenderBIM, you'll not only be helping to improve and expand the capabilities of this powerful open-source BIM authoring platform but also be part of a vibrant community driving innovation in the AEC industry.
|
||||
@@ -1,179 +0,0 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
There are different methods of installation, depending on your situation.
|
||||
|
||||
1. **Unstable installation** is recommended for power users helping with testing.
|
||||
2. **Bundling for Blender** is recommended for distributing the add-on.
|
||||
3. **Live development environment** is recommended for developers who are actively coding.
|
||||
4. **Packaged installation** is recommended for those who use a package manager.
|
||||
|
||||
Unstable installation
|
||||
---------------------
|
||||
|
||||
**Unstable installation** is almost the same as **Stable installation**, except
|
||||
that they are typically updated every day. Simply download a daily build from
|
||||
the `GitHub releases page
|
||||
<https://github.com/IfcOpenShell/IfcOpenShell/releases?q=blenderbim&expanded=true>`__,
|
||||
then follow the usual :doc:`installation
|
||||
instructions</users/quickstart/installation>`.
|
||||
|
||||
The BlenderBIM Add-on officially supports all major 64-bit platforms, as well as
|
||||
the Python version shipped by the Blender Foundation for the most recent three
|
||||
major Blender versions:
|
||||
|
||||
- 64-bit Linux (``linux-x64``)
|
||||
- 64-bit MacOS Intel (``macos-x64``)
|
||||
- 64-bit MacOS Silicon (``macos-arm64``)
|
||||
- 64-bit Windows (``windows-x64``)
|
||||
- Blender 4.2 with Python 3.11
|
||||
|
||||
Due to significant changes in the Blender extensions system, Blender versions
|
||||
<4.2 are not supported.
|
||||
|
||||
Developer builds may exist for different versions of Python but there will be
|
||||
no guarantee of the uptime or stability of these builds.
|
||||
|
||||
Other system specifications match the `Blender Requirements
|
||||
<https://www.blender.org/download/requirements/>`_ and the `VFX Platform
|
||||
<https://vfxplatform.com/>`_ standard.
|
||||
|
||||
Sometimes, a build may be delayed, or contain broken code. We try to avoid this,
|
||||
but it happens.
|
||||
|
||||
Bundling for Blender
|
||||
--------------------
|
||||
|
||||
Instead of waiting for an official release on the BlenderBIM Add-on website, it
|
||||
is possible to make your own Blender add-on from the bleeding edge source code
|
||||
of BlenderBIM. BlenderBIM is coded in Python and doesn't require any
|
||||
compilation, so this is a relatively easy process.
|
||||
|
||||
Note that the BlenderBIM Add-on does depend on IfcOpenShell, and IfcOpenShell
|
||||
does require compilation. The following instructions will use a pre-built
|
||||
IfcOpenShell (using an IfcOpenBot build) for convenience. Instructions on how to
|
||||
compile IfcOpenShell is out of scope of this document.
|
||||
|
||||
You can create your own package by using the Makefile as shown below. You can
|
||||
choose between a ``PLATFORM`` of ``linux``, ``macos``, ``macosm1``, and ``win``.
|
||||
You can choose between a ``PYVERSION`` of ``py312``, ``py311``, ``py310``, or
|
||||
``py39``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd src/blenderbim
|
||||
make dist PLATFORM=linux PYVERSION=py311
|
||||
ls dist/
|
||||
|
||||
This will give you a fully packaged Blender add-on zip that you can distribute
|
||||
and install.
|
||||
|
||||
Live development environment
|
||||
----------------------------
|
||||
|
||||
One option for developers who want to actively develop from source is to follow
|
||||
the instructions from :ref:`devs/installation:Bundling for Blender`. However,
|
||||
creating a build, uninstalling the old add-on, and installing a new build is a
|
||||
slow process. Although it works, it is very slow, so we do not recommend it.
|
||||
|
||||
A more rapid approach is to follow the :ref:`devs/installation:Unstable
|
||||
installation` method, as this provides all dependencies for you out of the box.
|
||||
|
||||
Once you've done this, you can replace certain Python files that tend to be
|
||||
updated frequently with those from the Git repository. We're going to use
|
||||
symbolic links, so we can code in our Git repository, and see the changes in
|
||||
our Blender installation (you will need to restart Blender to see changes).
|
||||
|
||||
For Linux or Mac:
|
||||
|
||||
.. literalinclude:: ../../scripts/installation/dev_environment.sh
|
||||
:language: bash
|
||||
:caption: dev_environment.sh
|
||||
|
||||
Or, if you're on Windows, you can use the batch script below. You need to run
|
||||
it as an administrator. Before running it follow the instructions descibed
|
||||
in the `rem` tags.
|
||||
|
||||
.. literalinclude:: ../../scripts/installation/dev_environment.bat
|
||||
:language: bat
|
||||
:caption: dev_environment.bat
|
||||
|
||||
After you modify your code in the Git repository, you will need to restart
|
||||
Blender for the changes to take effect.
|
||||
|
||||
The downside with this approach is that if a new dependency is added, or a
|
||||
compiled dependency version requirement has changed, or the build system
|
||||
changes, you'll need to fix your setup manually. But this is relatively rare.
|
||||
Reviewing the Makefile history, `here <https://github.com/IfcOpenShell/IfcOpenShell/commits/v0.8.0/src/blenderbim/Makefile>`__, is one quick way to see if a dependency has changed.
|
||||
|
||||
.. seealso::
|
||||
|
||||
There is a `useful Blender Addon
|
||||
<https://blenderartists.org/uploads/short-url/yto1sjw7pqDRVNQzpVLmn51PEDN.zip>`__
|
||||
(see `forum thread
|
||||
<https://blenderartists.org/t/reboot-blender-addon/640465/13>`__) that adds
|
||||
a Reboot button in File menu. In this way, it's possible to directly
|
||||
restart Blender and test the modified source code. There is also a VS Code
|
||||
add-on called `Blender Development
|
||||
<https://marketplace.visualstudio.com/items?itemName=JacquesLucke.blender-development>`__
|
||||
that has a similar functionality.
|
||||
|
||||
|
||||
Packaged installation
|
||||
---------------------
|
||||
|
||||
- **Arch Linux**: `Direct from Git <https://aur.archlinux.org/packages/ifcopenshell-git/>`__.
|
||||
- **Chocolatey on Windows**: `Unstable <https://community.chocolatey.org/packages/blenderbim-nightly/>`__.
|
||||
|
||||
Tips for package managers
|
||||
-------------------------
|
||||
|
||||
The BlenderBIM Add-on is fully contained in the ``blenderbim/`` subfolder of the
|
||||
Blender add-ons directory. This is typically distributed as a zipfile as per
|
||||
Blender add-on conventions. Within this folder, you'll find the following file
|
||||
structure:
|
||||
::
|
||||
|
||||
core/ (Blender agnostic core logic)
|
||||
tool/ (Blender specific shared functionality)
|
||||
bim/ (Blender specific UI)
|
||||
libs/ (other assets)
|
||||
wheels/ (dependencies)
|
||||
__init__.py
|
||||
|
||||
This corresponds to the structure found in the source code `here
|
||||
<https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/blenderbim/blenderbim>`__.
|
||||
|
||||
The BlenderBIM Add-on is complex, and requires many dependencies, including
|
||||
Python modules, binaries, and static assets. When packaged for users, these
|
||||
dependencies are bundled with the add-on for convenience.
|
||||
|
||||
If you choose to install the BlenderBIM Add-on and use your own system
|
||||
dependencies, the source of truth for how dependencies are bundled are found in
|
||||
the `Makefile
|
||||
<https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.8.0/src/blenderbim/Makefile>`__
|
||||
in the ``dist`` target.
|
||||
|
||||
Add-on compatibility
|
||||
--------------------
|
||||
|
||||
The BlenderBIM Add-on is a non-trivial add-on. By turning Blender into a
|
||||
graphical front-end to a native IFC authoring platform, some fundamental Blender
|
||||
features (such as hotkeys for basic functionality like object deletion or
|
||||
duplication) have been patched and many dependencies have been introduced.
|
||||
|
||||
Other add-ons may no longer work as intended when the BlenderBIM Add-on is
|
||||
enabled, or vice versa, the BlenderBIM Add-on may no longer work as intended
|
||||
when other add-ons are enabled.
|
||||
|
||||
Known scenarios which will lead to add-on incompatibility include:
|
||||
|
||||
- The add-on also overrides the same hotkeys. For example, if an add-on
|
||||
overrides the "X" key to delete an object, you will need to manually trigger
|
||||
(either via menu or custom hotkey) the BlenderBIM Add-on equivalent operator
|
||||
(e.g. IFC Delete).
|
||||
- The add-on uses object deletion or duplication macros with dictionary
|
||||
override. Note that this is also deprecated in Blender, so the other add-on
|
||||
should be updated to fix this.
|
||||
- The add-on requires a conflicting dependency, or a conflicting version of the
|
||||
same dependency. Neither add-on may work simultaneously.
|
||||
@@ -1,178 +0,0 @@
|
||||
User Experience and User Interface guidelines
|
||||
=============================================
|
||||
|
||||
This is intended as a reference document for developers working on the BlenderBim addon.
|
||||
|
||||
Operators
|
||||
^^^^^^^^^
|
||||
|
||||
BIM will by definition contain a (big) number of submodules or subschemas. Granting access to the features of these submodules to the users needs to be considered with several things in minds :
|
||||
|
||||
1. Ease of use : How do I achieve my goal with the minimal number of clicks ? How many different menu items do I have to go through to get to my goal ? How long do I have to scroll a menu to get to the specific thing I have in mind ?
|
||||
|
||||
2. Clarity : How long do I have to visually parse the interface to find the item or button I'm looking for ? Are the buttons correctly labeled and are the icons descriptive enough ? If something is forbidden, can I easily know why ? Can I know what a button does before clicking on it ?
|
||||
|
||||
3. Completeness : I need to have access to all the items that are related to the topic I'm interested in. If it is tied to another submodule, I need to have access to it.
|
||||
|
||||
* **Blender**
|
||||
|
||||
These considerations must be adapted to the Python API, to the general paradigm we have chosen to access, modify or create IFC data, and to the general philosophy of Blender. There are many ways and places where an add-on creator can choose to present information to the user. BlenderBim currently works that way :
|
||||
|
||||
- Information about the current context is displayed inside the properties editor. We leverage the fact that it is already built around displaying different types of information whether we are in the Scene, Material, Mesh, Object, Curve, Texture, etc. subpanels. It fits really well with the way things are already organized in the IFC classification.
|
||||
|
||||
- Tools to create, modify or delete items are in the dedicated Toolbar (The left one) in the 3D viewport
|
||||
|
||||
- Some Misc tools and information are displayed in the N panel of the 3D viewport . They're shortcuts to things that are already present elsewhere in the UI, but are handy to have when modifying objects or geometry in the Viewport.
|
||||
|
||||
- Adding specific Import / Export IFC operators (arguably a bit misleading since BlenderBim does more than import / export in these operations)
|
||||
|
||||
* **Interface panels**
|
||||
|
||||
- The good : Every submodule is visually separated from each other. The user can customize its interface to only show what's relevant to them at any one point by folding in or out panels. Development / Debugging is facilitated because each submodule's interface is separated from the others.
|
||||
|
||||
- The bad : Having many interface panels clutters the interface, leading to an overwhelming experience for newcomers especially.
|
||||
|
||||
- Proposal : The addon should in the long run provide the user with dynamic UI depending on what's their usecase. Preferably accessed in the Addon Preferences interface, with pre-made options that dynamically enable or disable specific parts of the UI. It should also provide the option to selectively add or remove specific parts of the UI on top of that. Additionally, when it does not make sense to display a specific panel, it should be hidden from the UI. (eg if an object has no IFC Class, it can't be spatially contained, so the IFC Spatial Container panel should be hidden until the user sets the IFC class)
|
||||
In the long term it could also be interesting to think about some kind of tutorial mode where more information is conveyed through the interface for new users.
|
||||
|
||||
* **Item Lists**
|
||||
|
||||
An item list should provide right off the bat a few features :
|
||||
|
||||
- Add a new item.
|
||||
|
||||
- Modify an item, whether by adding a button to each item in the list, or adding a button on the list header to modify the selected item.
|
||||
|
||||
- Delete an item, whether by adding a button to each item in the list, or adding a button on the list header to delete the selected item.
|
||||
|
||||
- Modify an item's name by double-clicking on the field if applicable
|
||||
|
||||
- The currently selected item should be highlighted
|
||||
|
||||
- There should be a search field to filter through specific items in the list, with buttons to invert selection, sort alphabetically and revert display. (Features that are provided by the `UIList` class)
|
||||
|
||||
* **Operators**
|
||||
|
||||
- Some buttons should be dynamically disabled (grayed out and impossible to click on) when the context to execute them does not make sense. It is important to note there should not be any heavy computation to determine this since this is executed several times per second for each button where it is implemented. It should only test for simple things and not rely on retrieving information from other modules too much. eg : `Is an item selected ?` `Is the z position of the selected object > 0 ?` or `Is the selected object an IfcOpeningElement ?` and **not** `Is there an IfcOpeningElement in the current file ?` or `How many different classes of IfcBeam are implemented in the file ?`
|
||||
In Blender this is done in two ways :
|
||||
|
||||
- Directly in the operator `poll` method which is executed when the button is displayed in the interface. The drawback is that some operators rely on custom attributes which are provided only when the user clicks on the button, and cannot be test in the `poll` method. Note in V3.0 we can implement custom messages when the `poll` method fails, depending on the step where the context was not right.
|
||||
|
||||
- In the UI drawing code, where specific layout parts can be disabled.
|
||||
|
||||
- Some buttons or entire interface layout rows or columns may be disabled or hidden at once. It may be desirable when it relies on specific things or combinations of things not being met in the project. In order to avoid UI flicker or the user wondering where the button went, dynamically hiding UI elements should have a minimal interference with the rest of the UI by :
|
||||
|
||||
(1) Hiding the last row(s) of a panel
|
||||
|
||||
(2) Dynamically resizing the rest of the UI when it's not possible to (1)
|
||||
|
||||
The user should be advised why they are prevented to do things via either :
|
||||
|
||||
- A custom message using Blender's report system
|
||||
|
||||
- A label in the UI replacing the missing UI elements
|
||||
|
||||
- A helpful tooltip on an operator
|
||||
|
||||
Generally the context itself should be used to the best of its ability to convey why it's possible to do some things or not. The separation of concerns in multiple different dynamically hidden-or-shown sub-panels should help limit confusion.
|
||||
|
||||
- The user should be forbidden to click on buttons when its execution returns early and does nothing. If a button is clickable and the user clicks on it, there should always be some kind of feedback.
|
||||
|
||||
Icons
|
||||
^^^^^
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137270452-bf6c03ea-2d3e-4ae3-8121-6799e8beb335.png
|
||||
|
||||
**Icon name** : `"CANCEL"`
|
||||
|
||||
**Use** : To disable the edition state of a particular item.
|
||||
|
||||
**Expected result** : A part of the interface showing the item attributes is either shrunk down or hidden. The button is usually replaced by a button to delete this item.
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137271575-dcae9e56-3c64-4d8b-89b0-6cf103b999f2.png
|
||||
|
||||
**Icon name** : `"X"`
|
||||
|
||||
**Use** : To delete a particular item, for instance an element of a list.
|
||||
|
||||
**Expected result** : The interface element referencing the deleted item is hidden from the interface. If the item was in a list, and the deleted element was selected, the next element becomes selected, or if there is no next element, no element is selected.
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137271601-e79012e5-fc61-402e-830c-b34b4ca757a0.png
|
||||
|
||||
**Icon name** : `"GREASEPENCIL"`
|
||||
|
||||
**Use** : To Enable the edition of a particular item.
|
||||
|
||||
**Behaviour** : Usually only one element of a particular context can be edited at a time.
|
||||
|
||||
**Expected result** : The interface should either expand or spawn new elements to display the chosen item's editable attributes. If the editem item is part of a list and its interface elements contained an operator to delete it, it is swapped for an operator to disable the edition of that item.
|
||||
|
||||
**Proposal** : Currently all the edition buttons are hidden from the interface when editing a particular item. This results on buttons jumping to the right slightly. My proposal is to instead show but disable the other buttons to prevent interface flicker.
|
||||
|
||||
**Current** : https://user-images.githubusercontent.com/25156105/137273603-f9ac39e5-7616-4fb9-89e2-4dae453e650a.gif
|
||||
|
||||
**Proposal** : https://user-images.githubusercontent.com/25156105/137273329-532d6d59-e39d-483e-980b-b8405062eaa8.gif
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/25156105/137273981-26fa664a-a29e-402e-9d7e-67b8b71c2d5d.png
|
||||
|
||||
* **Icon name** : `"ADD"`
|
||||
|
||||
**Use** : To add an item to a particular context, either initializing it (eg. adding a coordinate system) or adding an element to a list.
|
||||
|
||||
**Expected result** : The interface should either expand to display the new item or a new line should be shown if the item is added in a list.
|
||||
|
||||
**Proposal** : In some instances adding an element in a list automatically enables the edition of this item's attributes. (eg IFC Groups) but in other instances it does not (eg IFC strucural load cases). It is arguable but I do not think the user should expect a new item to be in editable state right off the bat when they add it to a list.
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137274869-0d0b876a-545c-4a58-a386-90f85764b7cc.png
|
||||
|
||||
**Icon name** : `"RESTRICT_SELECT_OFF"`
|
||||
|
||||
**Use** : To select all the instances related to a particular item.
|
||||
|
||||
**Expected result** : All the objects relating to a particular item should be selected in the 3D viewport and/or in the outline.
|
||||
|
||||
**Proposal** : Whether or not previously selected objects should be deselected beforehand is up for debate I think, because both behaviours can be desirable. It also raises the question of which object should be set to active. (Random ? First one ? Biggest one ?...)
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137275582-98c1dfcc-fc96-41d6-80dd-fdb82796e66e.png
|
||||
|
||||
**Icon name** : `"IMPORT"`
|
||||
|
||||
**Use** : to import a particular file from the user's computer's directory to the current context
|
||||
|
||||
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type should be loaded in (eg adding a file extension, adding information in the filebrowser UI). When the file is loaded, the interface should expand to show the attributes or information or new context-sensitive operations provided by the imported file.
|
||||
|
||||
**Proposal** : Currently there are some instances where I don't know if the elements are supposed to require an external file, eg
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/25156105/137275887-e0f0b694-842b-422f-bc09-cdcefef64107.png
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137276333-4d083f6a-36b9-494f-b0de-ec1d321cbd3d.png
|
||||
|
||||
**Icon name** : `"EXPORT"`
|
||||
|
||||
**Use** : To export a particular file to the user's computer's directory
|
||||
|
||||
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type is going to be saved (eg adding a file extension, adding information in the filebrowser UI).
|
||||
|
||||
**Proposal** : When there is no actual file to be saved in the user's directory or the filepath is already set in another place in the UI :
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/25156105/137276840-3ceda0cd-87bd-45f5-8488-bfce86bf3d84.png
|
||||
|
||||
It may be better to use a save icon of some sort instead, eg
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/25156105/137276758-5d60f0ca-5a93-4af3-9c4f-30c5794e0657.png
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137277047-bd45df5b-3b47-4b2c-aa29-a340b0710345.png
|
||||
|
||||
**Icon name** : `"FILEBROWSER"`
|
||||
|
||||
**Use** : To Provide the current context with the path to a particular file or folder
|
||||
|
||||
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type is required (eg adding a file extension, adding information in the filebrowser UI). Upon file or folder selection, this should populate a field in the current interface context with the path to the selected file or folder, or at least let the user know that the required information is now correctly filled in.
|
||||
|
||||
* .. image:: https://user-images.githubusercontent.com/25156105/137277374-fa7ff310-0b41-49a9-98bd-7c4686da05fd.png
|
||||
|
||||
**Icon name** : `"EYEDROPPER"`
|
||||
|
||||
**Use** : To select a particular object in the interface to fill the field it is attached to
|
||||
|
||||
**Expected result** : The cursor should change to an eyedropper until the user clicks on the required item. The user should be made aware after selection if it was successful (associated field being filled) or unsuccessful (message ? or field staying empty)
|
||||
@@ -1,214 +0,0 @@
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
BlenderBIM Reference Manual
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Welcome to the manual for `BlenderBIM <https://blenderbim.org>`__,
|
||||
the free and open source BIM add-on for Blender.
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/quickstart/introduction_to_bim` (2-minute read)
|
||||
Brief overview of BIM concepts and BlenderBIM's role.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/quickstart/installation` (5 to 10-minute process)
|
||||
Installation guide and system requirements.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/quickstart/explore_model` (15-minute guide)
|
||||
Load and navigate an IFC model.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/quickstart/create_model` (20-minute guide)
|
||||
Create a simple BIM project.
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Quickstart
|
||||
:maxdepth: 1
|
||||
|
||||
users/quickstart/introduction_to_bim
|
||||
users/quickstart/installation
|
||||
users/quickstart/explore_model
|
||||
users/quickstart/create_model
|
||||
users/quickstart/next_steps
|
||||
|
||||
Sections
|
||||
========
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/modeling/interface`
|
||||
Guide to the BlenderBIM interface.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/modeling/ifc_modeling`
|
||||
IFC modeling techniques and concepts.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/git_support`
|
||||
Collaborate on projects using Git
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/drawing/index`
|
||||
2D drawing generation and annotation.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/structural_analysis/index`
|
||||
Structural analysis capabilities in BlenderBIM.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/costing_and_scheduling/index`
|
||||
Cost estimation and project scheduling features.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/advanced/index`
|
||||
Advanced topics and large-scale modeling.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/tutorials/index`
|
||||
Real-world BIM project tutorials.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/user_interface`
|
||||
Detailed UI reference.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/other_addons`
|
||||
Other complementary add-ons
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/troubleshooting`
|
||||
Troubleshooting when things go wrong
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Sections
|
||||
:maxdepth: 2
|
||||
|
||||
users/modeling/interface
|
||||
users/modeling/ifc_modeling
|
||||
users/git_support
|
||||
users/drawing/index
|
||||
users/structural_analysis/index
|
||||
users/costing_and_scheduling/index
|
||||
users/advanced/index
|
||||
users/tutorials/index
|
||||
users/user_interface
|
||||
users/other_addons
|
||||
users/troubleshooting
|
||||
|
||||
Get Involved
|
||||
============
|
||||
|
||||
This manual is maintained largely by volunteers.
|
||||
|
||||
Please consider joining the effort and :doc:`/contribute/writing_docs`.
|
||||
|
||||
You can also can help with :doc:`Translations and Internationalisation </contribute/translations>`
|
||||
|
||||
For users who want to contribute to the BlenderBIM codebase,
|
||||
please refer to the :doc:`Documentation for Developers </devs/getting_started>`.
|
||||
This part of the documentation covers various aspects of the BlenderBIM development process.
|
||||
|
||||
You can get in touch by `joining the chat <https://osarch.org/chat/>`__.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Get Involved
|
||||
:maxdepth: 1
|
||||
|
||||
contribute/writing_docs
|
||||
contribute/translations
|
||||
|
||||
|
||||
Developer Documentation
|
||||
=======================
|
||||
|
||||
BlenderBIM is an open-source project, and its development is driven by the contributions of a dedicated community of developers,
|
||||
architects, engineers, and enthusiasts. If you're interested in contributing to the project, whether by submitting bug reports,
|
||||
suggesting new features, or contributing code, your involvement is highly encouraged and appreciated.
|
||||
|
||||
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/getting_started`
|
||||
Introduction to contributing to BlenderBIM.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/installation`
|
||||
Setting up a development environment.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/hello_world`
|
||||
Understanding the project structure.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/undo_system`
|
||||
Working with BlenderBIM's undo system.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/code_style`
|
||||
Code style guidelines and best practices.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/running_tests`
|
||||
Testing and quality assurance.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/devs/ux_guidelines`
|
||||
User experience and interface guidelines.
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Developer Documentation
|
||||
:maxdepth: 1
|
||||
|
||||
devs/getting_started
|
||||
devs/installation
|
||||
devs/hello_world
|
||||
devs/undo_system
|
||||
devs/code_style
|
||||
devs/running_tests
|
||||
devs/ux_guidelines
|
||||
@@ -1,10 +0,0 @@
|
||||
Advanced Use Cases
|
||||
==================
|
||||
|
||||
Advanced topics and large-scale modeling.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
georeferencing
|
||||
dealing_with_large_models
|
||||
@@ -1,51 +0,0 @@
|
||||
============================
|
||||
Advanded Modeling Techniques
|
||||
============================
|
||||
|
||||
Complex modeling techniques for advanced users.
|
||||
|
||||
.. note::
|
||||
This page is a stub. More detailed content will be added in future updates.
|
||||
|
||||
Custom Parametric Wall Types
|
||||
----------------------------
|
||||
|
||||
[Content about creating custom parametric wall types]
|
||||
|
||||
Material assignment
|
||||
-------------------
|
||||
|
||||
:doc:`material_assignment`
|
||||
|
||||
Modeling Furniture and Fixtures
|
||||
-------------------------------
|
||||
|
||||
[Content about modeling furniture and fixtures]
|
||||
|
||||
Structural Elements
|
||||
-------------------
|
||||
|
||||
.. seealso::
|
||||
|
||||
:doc:`/users/structural_analysis/index`
|
||||
|
||||
[Content about modeling structural elements]
|
||||
|
||||
Multi-Story Buildings
|
||||
---------------------
|
||||
|
||||
[Content about modeling multi-story buildings]
|
||||
|
||||
Complex Structures
|
||||
------------------
|
||||
|
||||
[Content about modeling complex structures]
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Sections
|
||||
:maxdepth: 2
|
||||
|
||||
material_assignment
|
||||
parametric_geometry
|
||||
@@ -1,44 +0,0 @@
|
||||
=========================
|
||||
Basic Modeling Techniques
|
||||
=========================
|
||||
|
||||
Fundamental techniques for IFC modeling.
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`creating_walls`
|
||||
Learn how to create and modify walls in your IFC model.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`openings/index`
|
||||
Techniques for adding and customizing doors, windows and other openings.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`modeling_slabs_roofs`
|
||||
Guide to modeling slabs and different types of roofs.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`defining_rooms_spaces`
|
||||
Methods for defining and managing rooms and spaces in your model.
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Basic Modeling Techniques
|
||||
:maxdepth: 1
|
||||
|
||||
creating_walls
|
||||
openings/index
|
||||
modeling_slabs_roofs
|
||||
defining_rooms_spaces
|
||||
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
Openings
|
||||
========
|
||||
|
||||
This section covers the creation and management of openings in BlenderBIM.
|
||||
Openings are crucial elements in building design, serving various purposes such as passage, ventilation, and lighting.
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`door`
|
||||
Learn how to add and customize doors in your BIM model.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`window`
|
||||
Discover the process of creating and modifying windows.
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`opening`
|
||||
Create openings without fillings for special architectural features.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
In the context of Building Information Modeling (BIM) and the Industry Foundation Classes (IFC)
|
||||
standard, openings are represented through a combination of elements:
|
||||
|
||||
1. Voids: Represented by IfcOpeningElement, these are the actual cut-outs in the wall.
|
||||
2. Fillings: These are the elements that occupy the voids, such as doors (IfcDoor) or windows (IfcWindow).
|
||||
3. Relationships: These are abstract objects that connect fillings to voids and voids to elements in which they're created.
|
||||
|
||||
BlenderBIM provides tools to create and manage these elements:
|
||||
|
||||
- Door Creation Tool: For adding doors to walls.
|
||||
- Window Creation Tool: For adding windows to walls.
|
||||
- Wall Creation Tool > Void Application: For creating openings without fillings
|
||||
(currently achieved by creating a door or window and removing it
|
||||
or by using "Add Void" feature of the Create Wall tool or any element).
|
||||
|
||||
These tools allow you to:
|
||||
|
||||
- Create openings with precise dimensions and positions.
|
||||
- Modify opening properties and geometries.
|
||||
- Manage the relationships between walls, voids, and fillings.
|
||||
|
||||
The following pages provide detailed guides on working with each type of wall opening in BlenderBIM.
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Wall Openings
|
||||
:maxdepth: 1
|
||||
|
||||
door
|
||||
window
|
||||
opening
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
- :doc:`../creating_walls`
|
||||
@@ -1,4 +0,0 @@
|
||||
Importing and Viewing IFC Models
|
||||
================================
|
||||
|
||||
[Content about importing and viewing IFC models]
|
||||
@@ -1,92 +0,0 @@
|
||||
============================
|
||||
BlenderBIM Interface Guide
|
||||
============================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The BlenderBIM interface extends Blender's powerful 3D environment with specialized tools for Building Information Modeling (BIM). This guide will help you navigate the BlenderBIM interface and understand its key components.
|
||||
|
||||
BlenderBIM Workspace
|
||||
====================
|
||||
|
||||
When you start Blender with BlenderBIM installed, you can switch to the BlenderBIM workspace
|
||||
by clicking on the BIM workspace tab at the top of the Blender window.
|
||||
|
||||
This workspace is preconfigured with the most commonly used panels and tools for BIM workflows.
|
||||
|
||||
|
||||
Key Interface Elements
|
||||
======================
|
||||
|
||||
Properties Extended
|
||||
-------------------
|
||||
|
||||
The Properties Editor is extended to provide IFC-specific properties and settings.
|
||||
|
||||
- Scene
|
||||
- Project Information
|
||||
- Tool
|
||||
- Object
|
||||
|
||||
BIM Toolbar
|
||||
-----------
|
||||
|
||||
The BIM toolbar, typically located at the left of the 3D Viewport, contains shortcuts to frequently used BIM tools:
|
||||
|
||||
- Explore tool
|
||||
- Create various building elements
|
||||
- Create annotations, measure distance and angles, calculate volumes, etc
|
||||
|
||||
|
||||
IFC Tree View
|
||||
-------------
|
||||
|
||||
Found in the Outliner, the IFC Tree View displays the hierarchical structure of your BIM model:
|
||||
|
||||
- Spatial Structure (Site, Building, Storey)
|
||||
- Building Elements
|
||||
- Types
|
||||
|
||||
Customizing the Interface
|
||||
=========================
|
||||
|
||||
BlenderBIM respects Blender's highly customizable interface. You can:
|
||||
|
||||
- Rearrange editors and panels
|
||||
- Save custom workspace layouts
|
||||
- Create custom shortcuts for BIM operations
|
||||
|
||||
Tips for Efficient Use
|
||||
======================
|
||||
|
||||
1. Familiarize yourself with IFC classes and their properties.
|
||||
2. Utilize Blender's search function (F3) to quickly access BlenderBIM tools.
|
||||
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
Now that you're familiar with the BlenderBIM interface, you have two main paths to continue your learning:
|
||||
|
||||
1. Proceed with IFC Modeling:
|
||||
If you're ready to start creating and working with BIM models, you can move on to:
|
||||
|
||||
- :doc:`IFC Modeling Basics </users/modeling/ifc_modeling>`
|
||||
|
||||
2. Dive deeper into the User Interface:
|
||||
If you want to explore more details about the BlenderBIM interface, you can refer to:
|
||||
|
||||
- :doc:`User Interface Reference </users/user_interface>`
|
||||
|
||||
Choose the path that best suits your current needs and learning style. You can always come back to explore the other option later.
|
||||
|
||||
Remember, as you become more comfortable with the interface and basic modeling, you can explore more advanced topics such as:
|
||||
|
||||
- :doc:`Generating Documentation </users/drawing/index>`
|
||||
- :doc:`Advanced BIM Techniques </users/advanced/index>`
|
||||
|
||||
The BlenderBIM interface is designed to integrate seamlessly with Blender while providing powerful BIM-specific functionality.
|
||||
As you progress, you'll find your BIM workflow becoming increasingly efficient and productive.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 332 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 309 KiB |
|
Before Width: | Height: | Size: 91 KiB |
@@ -1,140 +0,0 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/videoseries?list=PLMDcOjMJYxUPHHvEHqAsOuBdSPsp6or32" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
|
||||
|
||||
1. **Download and install Blender**
|
||||
|
||||
Blender is a free and open-source program for 3D authoring. It works on
|
||||
Linux, Mac, and Windows. It is developed by the Blender community.
|
||||
|
||||
.. container:: blockbutton
|
||||
|
||||
`Download Blender <https://www.blender.org/download/>`__
|
||||
|
||||
.. tip::
|
||||
|
||||
No administrator rights on Windows? Choose the "Portable .zip" option when
|
||||
downloading from the Blender website.
|
||||
|
||||
2. **Download the BlenderBIM Add-on**
|
||||
|
||||
The BlenderBIM Add-on extends Blender with OpenBIM related capabilities.
|
||||
|
||||
.. container:: blockbutton
|
||||
|
||||
`Download BlenderBIM Add-on <https://blenderbim.org/download.html>`__
|
||||
|
||||
3. **Install the BlenderBIM Add-on**
|
||||
|
||||
Open up Blender, and click on ``Edit > Preferences``.
|
||||
|
||||
.. image:: images/install-blenderbim-1.png
|
||||
|
||||
Select the **Add-ons** tab, and press **Install...** on the top right. Navigate
|
||||
to the .zip you downloaded in Step 2, and press **Install Add-on**.
|
||||
|
||||
.. image:: images/install-blenderbim-2.png
|
||||
|
||||
.. warning::
|
||||
|
||||
You do not need to unzip the add-on file. You should install it as a zipped file.
|
||||
|
||||
You should now see **System: BlenderBIM** available in your add-ons list. Enable the add-on by pressing the checkbox.
|
||||
|
||||
.. image:: images/install-blenderbim-3.png
|
||||
|
||||
All done! Your interface will now look similar to below. If you check the ``File`` menu you should also see an option to ``Open IFC Project``.
|
||||
|
||||
.. image:: images/install-blenderbim-4.png
|
||||
|
||||
You can enable add-ons permanently by using ``Save User Settings`` from the Addons menu.
|
||||
|
||||
.. seealso::
|
||||
|
||||
If you are a poweruser, you may be interested in the **Unstable installation** to help with testing. :doc:`Read more </devs/installation>`
|
||||
|
||||
.. _where is the add-on installed:
|
||||
|
||||
Updating
|
||||
--------
|
||||
|
||||
First follow the `Uninstalling`_ section below, then install the latest version.
|
||||
|
||||
Uninstalling
|
||||
------------
|
||||
|
||||
Navigate to ``Edit > Preferences > Add-ons``. Due to a limitation in Blender,
|
||||
you have to **first disable the BlenderBIM Add-on in your Blender preferences**
|
||||
by pressing the checkbox next to the add-on, then **restart Blender**. It is
|
||||
critical to follow this sequence of disabling first, and then restarting.
|
||||
|
||||
After restarting, you can uninstall the BlenderBIM Add-on by pressing the
|
||||
``Remove`` button in the Blender preferences window.
|
||||
|
||||
Alternatively, you may uninstall manually by deleting the ``blenderbim``
|
||||
directory in :ref:`your Blender add-ons directory<where is the add-on
|
||||
installed>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
It is important to follow the sequence of disabling, restarting, then removing.
|
||||
If you do not restart Blender, the add-on will fail to remove correctly, and you
|
||||
will need to uninstall manually.
|
||||
|
||||
Where is the add-on installed?
|
||||
------------------------------
|
||||
|
||||
Upon installation, the BlenderBIM Add-on is stored in Blender configuration
|
||||
folder. However, the location of your Blender configuration folder depends on
|
||||
how you have installed Blender.
|
||||
|
||||
If you downloaded Blender as a ``.zip`` file without running an installer, the
|
||||
BlenderBIM Add-on will be installed in the following directory, where ``X.XX``
|
||||
is the Blender version:
|
||||
|
||||
::
|
||||
|
||||
/path/to/blender/X.XX/
|
||||
|
||||
Otherwise, if you installed Blender using an installation package, the Blender
|
||||
configuration folder depends on which operating system you use.
|
||||
|
||||
On Linux, if you are installing the add-on as a user:
|
||||
|
||||
::
|
||||
|
||||
~/.config/blender/X.XX/
|
||||
|
||||
On Linux, if you are deploying the add-on system-wide (this may also depend on
|
||||
your Linux distribution):
|
||||
|
||||
::
|
||||
|
||||
/usr/share/blender/X.XX/
|
||||
|
||||
On Mac, if you are installing the add-on as a user:
|
||||
|
||||
::
|
||||
|
||||
/Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/
|
||||
|
||||
On Mac, if you are deploying the add-on system-wide:
|
||||
|
||||
::
|
||||
|
||||
/Library/Application Support/Blender/X.XX/
|
||||
|
||||
On Windows:
|
||||
|
||||
::
|
||||
|
||||
C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\X.XX\
|
||||
|
||||
Inside the Blender configuration folder, the BlenderBIM Add-on is stored in two
|
||||
different locations. The extension itself is stored in
|
||||
``extensions/blender_org/blenderbim`` whereas the Python packages are installed
|
||||
into ``extensions/.local/lib/pythonX.XX/site-packages/``.
|
||||
@@ -1,98 +0,0 @@
|
||||
Introduction to BIM
|
||||
===================
|
||||
|
||||
**Building Information Modeling**, or **BIM**, is a way of digitally describing
|
||||
our built environment to computers. Aspects of our built environment that can be
|
||||
described are:
|
||||
|
||||
- **Products**, like walls, doors, and windows
|
||||
- **Processes**, like construction or maintenance tasks, and procedures
|
||||
- **Resources**, like labour, materials, and equipment
|
||||
- **Controls**, like permits, orders, costs, or calendar availability
|
||||
- **Actors**, like occupants, clients, architects, and liable parties
|
||||
- **Groups**, like systems, inventories, or zones
|
||||
|
||||
These objects may have lots of data and relationships. Examples of data might be
|
||||
classification systems, physical materials, associated documents, simulation
|
||||
results, and construction types. The data may be relevant to multiple
|
||||
disciplines, such as architecture, engineering, and construction.
|
||||
|
||||
.. note::
|
||||
|
||||
BIM data is very different from a regular 3D model. In fact, geometry is
|
||||
optional, and most data is non-geometric. This means that it is not simply a
|
||||
3D format that you can import or export from and expect meaningful results.
|
||||
|
||||
**Industry Foundation Classes**, or **IFC**, is an international standard for
|
||||
**BIM**. **IFC** is the most well-established open digital language for our
|
||||
built environment. Most software will be able to describe their **BIM** data
|
||||
using **IFC**. Most commonly, **IFC** models will be shared as a ``.ifc`` file.
|
||||
|
||||
For example, **IFC** will define a wall as an object that can have a name,
|
||||
construction type, and quantities. **IFC** will also describe that a wall that
|
||||
be associated with a location, like a building storey, or have an associated
|
||||
cost item in a schedule.
|
||||
|
||||
When you use the BlenderBIM Add-on, you will be able to view and create **BIM**
|
||||
objects and relationships using the **IFC** standard.
|
||||
|
||||
Things you can do
|
||||
-----------------
|
||||
|
||||
The BlenderBIM Add-on is designed to be a comprehensive and truly native
|
||||
BIM authoring platform. Its capabilities include a wide range of tasks
|
||||
and workflows typically found across various BIM and CAD software, costing
|
||||
programs, scheduling tools, and simulation applications. While not
|
||||
an exhaustive list, some of the key things you can do with BlenderBIM include:
|
||||
|
||||
- View and explore IFC models, including spaces, properties, and relationships
|
||||
- Edit and extract attributes, properties, and metadata directly from IFC data
|
||||
- Move, rotate, and modify the geometry of objects while preserving IFC semantics
|
||||
- Create new objects using predefined library elements or custom parametric types
|
||||
- Manage classification systems, document references, and link to external libraries
|
||||
- Generate 2D drawing views like plans, sections, and elevations with customizable annotations
|
||||
- Investigate and edit structural analysis models with support for various steel profiles
|
||||
- Model and manage complex distribution systems like HVAC, plumbing, and electrical (MEP)
|
||||
- Create construction schedules, perform critical path analysis, and generate sequence animations
|
||||
- Derive quantities from model elements and create cost schedules with formulas
|
||||
- Perform clash detection and coordinate models, managing issues across disciplines
|
||||
- Integrate non-geometric data like costing, scheduling, and asset management
|
||||
|
||||
|
||||
Roadmap and Upcoming Features
|
||||
-----------------------------
|
||||
|
||||
While BlenderBIM already offers a comprehensive set of BIM authoring capabilities,
|
||||
the development team and community are continuously working to expand its functionality and improve existing workflows.
|
||||
Some of the most ambitious features and enhancements on the roadmap include:
|
||||
|
||||
Usability and Workflow Enhancements
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Improve user-friendliness to equip average "Joe" with capabilities to model simple projects like single family homes, making BlenderBIM a viable alternative to SketchUp.
|
||||
- Make BlenderBIM more approachable for users across different skill levels, from advanced BIM experts to architects working on smaller residential projects.
|
||||
|
||||
Improved Drawing and Documentation Workflows
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Continued enhancements to the drawing generation capabilities, including better dimensioning tools and support for associative dimensions linked to model geometry.
|
||||
- Advanced annotation tools with customizable tags, callouts, and the ability to define reusable annotation styles and templates.
|
||||
- Streamlined sheet layout management and improved integration with external tools like Inkscape for creating title blocks and sheet compositions.
|
||||
- Continued improvement of BlenderBIM's ability to generate comprehensive documentation sets, including drawing annotations, door schedules, wall schedules, and material tagging.
|
||||
|
||||
Expanded Parametric Modeling Capabilities
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Native modeling tools for complex building elements like curtain wall systems and facade panels.
|
||||
- Expanded libraries with support for country-specific and manufacturer-provided object types.
|
||||
- Enhanced support for multi-story modeling, enabling efficient duplication and coordination of building elements across various levels.
|
||||
- Expanded parametric relationships and automating more common BIM tasks to reduce manual effort and enhance productivity.
|
||||
|
||||
Enhanced Coordination and Collaboration Features
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Robust model merge and coordination workflows, with detailed clash detection capabilities across different disciplines and data sources.
|
||||
- Built-in support for version control and model sharing using Git repositories, enabling better team collaboration and change tracking.
|
||||
|
||||
|
||||
The development roadmap is continuously updated based on user feedback, industry requirements,
|
||||
and contributions from the open-source community. By embracing an open and collaborative approach,
|
||||
BlenderBIM aims to push the boundaries of what's possible in BIM authoring,
|
||||
ensuring that it remains at the forefront of innovation in the AEC industry.
|
||||
@@ -1,98 +0,0 @@
|
||||
=======================
|
||||
Tutorials
|
||||
=======================
|
||||
|
||||
Project Series
|
||||
==============
|
||||
|
||||
Step-by-step guides for real-world BIM projects.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:hidden:
|
||||
|
||||
project0
|
||||
project1
|
||||
project2
|
||||
project3
|
||||
project4
|
||||
project5
|
||||
|
||||
.. only:: builder_html and (not singlehtml)
|
||||
|
||||
.. container:: toc-cards
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 0: Designing a Room <project0>`
|
||||
* Basic walls and openings
|
||||
* Slabs
|
||||
* MEP
|
||||
* Basic furniture
|
||||
* Basic floor plan drawings
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 1: Designing a Flat <project1>`
|
||||
* Advanced walls and openings
|
||||
* Profiles
|
||||
* Rooms and spaces
|
||||
* Furniture/fixture libraries
|
||||
* Elevation/section drawings
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 2: Building a Bungalow <project2>`
|
||||
* Roof
|
||||
* Advanced foundation
|
||||
* Structural elements
|
||||
* Roof openings
|
||||
* Underground services
|
||||
* Running services in subfloor space
|
||||
* Drawing details
|
||||
* Basic costing and scheduling
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 3: Multi-Storey Single-Family Home <project3>`
|
||||
* Templating
|
||||
* Types
|
||||
* Complex roofs
|
||||
* Complex fixtures (e.g. solar panels)
|
||||
* Complex HVAC, plumbing, electrical
|
||||
* Basic site landscaping
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 4: Terraced/Town Houses/Duplexes <project4>`
|
||||
* Shared walls
|
||||
* Shared roofs
|
||||
* Complex landscaping
|
||||
* Duplicating units
|
||||
* Basic parametric floor plans
|
||||
* Generating drawings parametrically
|
||||
* Clash detection
|
||||
* Coordination with other stakeholders
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`Project 5: Apartment Block <project5>`
|
||||
* Multiple levels
|
||||
* Typical floor
|
||||
* Shared amenities and common areas
|
||||
* Elevators
|
||||
* Parametric services
|
||||
* Complex structural elements
|
||||
* Advanced exporting
|
||||
* Merging
|
||||
* Coordination
|
||||
* Analysis
|
||||
* Advanced costing and scheduling
|
||||
* External tools
|
||||
|
||||
Quickstart Video Tutorial
|
||||
=========================
|
||||
|
||||
Learn how to model a small building and create simple architectural plans, sections, 2D details, and sheet layouts in this short tutorial series.
|
||||
|
||||
`View all tutorial videos <https://www.youtube.com/playlist?list=PLMDcOjMJYxUPHHvEHqAsOuBdSPsp6or32>`__
|
||||
@@ -1,23 +0,0 @@
|
||||
User Interface Reference
|
||||
========================
|
||||
|
||||
This section covers the general user interface of BlenderBIM. User interface documentation does not cover any specific use case but provides a general reference of BlenderBIM UI.
|
||||
|
||||
The documentation doesn't cover Blender UI, but makes references to `Blender documentation <https://docs.blender.org/manual/en/latest/interface/index.html>`__ where appropriate.
|
||||
|
||||
.. * Navigating the Project Browser and Outliner
|
||||
.. * Using the Property Panels
|
||||
.. * Toolbars and Menus
|
||||
.. * Customizing the Interface
|
||||
.. * Keyboard Shortcuts and Preferences
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
user_interface/topbar
|
||||
user_interface/workspace
|
||||
user_interface/toolbar/index
|
||||
user_interface/property-editor
|
||||
|
||||
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 513 KiB |
@@ -1,60 +0,0 @@
|
||||
Property Editor Scene Properties
|
||||
================================
|
||||
|
||||
.. container:: location-scene
|
||||
|
||||
|location| Scene Properties
|
||||
|
||||
.. |location| image:: /images/location-scene.svg
|
||||
|
||||
BlenderBIM adds new functionality to the `Property Editor` -> `Scene` tab.
|
||||
|
||||
.. figure:: images/interface_property-editor_project-overview_start-up.png
|
||||
:alt: Property editor on Blender start-up
|
||||
|
||||
The property editor on Blender startup.
|
||||
|
||||
Most of these sub-tabs become available with a created or loaded IFC file.
|
||||
Don't worry, the default Blender scene properties are still reachable under their own dedicated sub-tab.
|
||||
|
||||
.. figure:: images/interface_property-editor_icons.png
|
||||
:alt: Overview over the added property sub-tabs by BlenderBIM
|
||||
|
||||
Overview over the added property sub-tabs by BlenderBIM.
|
||||
|
||||
1. Project Overview
|
||||
2. Object Information
|
||||
3. Geometry and Materials
|
||||
4. Drawings and Documents
|
||||
5. Services and Systems
|
||||
6. Structural Analyses
|
||||
7. Costing and Scheduling
|
||||
8. Facility Management
|
||||
9. Quality and Coordination
|
||||
10. Blender Properties
|
||||
11. Switch Tab
|
||||
|
||||
You can also select the needed panel via the drop-down menue.
|
||||
|
||||
.. figure:: images/interface_property-editor_panel-dropdown.png
|
||||
:alt: BlenderBIM property editor sub-tabs drop-down menue
|
||||
|
||||
Switching between BlenderBIM property editor sub-tabs via the drop-down menue.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
property_editor/scene_editor/project_overview/index
|
||||
property_editor/scene_editor/object_information/index
|
||||
property_editor/scene_editor/geometry_and_materials/index
|
||||
property_editor/scene_editor/drawings_and_documents/index
|
||||
property_editor/scene_editor/services_and_systems/index
|
||||
property_editor/scene_editor/structural_analysis/index
|
||||
property_editor/scene_editor/costing_and_scheduling/index
|
||||
property_editor/scene_editor/facility_management/index
|
||||
property_editor/scene_editor/quality_and_coordination/index
|
||||
property_editor/scene_editor/blender
|
||||
property_editor/scene_editor/switch_tab
|
||||
@@ -1,44 +0,0 @@
|
||||
Topbar
|
||||
======
|
||||
|
||||
For general informations about the Blender Topbar see the respective
|
||||
`Workspaces <https://docs.blender.org/manual/en/latest/interface/window_system/topbar.html>`__ section in Blender documentation.
|
||||
|
||||
Menus
|
||||
-----
|
||||
|
||||
The `File` menu in the Topbar adds menu entries to Create/Open and Save an IFC project directly.
|
||||
|
||||
.. figure:: images/interface_window_file-menue.png
|
||||
:alt: Additional file menu options related to IFC projects
|
||||
|
||||
Additional file menu options related to IFC projects.
|
||||
|
||||
New IFC Project
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The New IFC Project submenu offers several options for creating a new IFC project:
|
||||
|
||||
.. figure:: images/file_new-ifc-project-submenu.png
|
||||
:alt: New IFC Project submenu options
|
||||
|
||||
New IFC Project submenu options
|
||||
|
||||
- New Metric (m) Project: Creates a new IFC project using meters as the base unit.
|
||||
- New Metric (mm) Project: Creates a new IFC project using millimeters as the base unit.
|
||||
- New Imperial (ft) Project: Creates a new IFC project using feet as the base unit.
|
||||
- New Demo Project: Creates a new IFC project with pre-populated demo content.
|
||||
- New Project Wizard: Creates a default Blender project (including the default cube)
|
||||
and opens the Scene Properties > Project Overview > Project Info tab.
|
||||
This allows users to customize their project settings before creation.
|
||||
Clicking the "Create Project" button in this panel will delete the default cube and set up the new IFC project with the specified settings.
|
||||
|
||||
These options provide flexibility in setting up new IFC projects,
|
||||
allowing users to choose between quick starts with predefined units
|
||||
or a more customizable approach through the Project Wizard.
|
||||
|
||||
|
||||
BIM Workspace
|
||||
-------------
|
||||
|
||||
BlenderBIM adds the **BIM** Workspace to the Topbar Workspaces tabs.
|
||||
@@ -1,26 +0,0 @@
|
||||
BIM Workspace
|
||||
=============
|
||||
|
||||
For general informations about the Blender Workspaces see the respective
|
||||
`Workspaces <https://docs.blender.org/manual/en/latest/interface/window_system/workspaces.html>`__ section in Blender documentation.
|
||||
|
||||
After installation of BlenderBIM the default Blender start-up screen looks different from the standard Blender interface.
|
||||
|
||||
BlenderBIM adds the **BIM** Workspace to the interface and by default activates this workspace on start-up.
|
||||
It also modifies the layout of views and sidebars and adds custom functionality to the toolbar.
|
||||
The left **Outliner** panel shows a tree of geometric objects. The centre main **Viewport** panel shows 3D geometry.
|
||||
The right **Properties** panel shows data and relationships.
|
||||
|
||||
.. figure:: images/interface_window_start-up.png
|
||||
:alt: Blender start-up window with installed BlenderBIM add-on
|
||||
|
||||
Blender start-up window with installed BlenderBIM add-on.
|
||||
|
||||
1. **BIM** Workspace tab
|
||||
2. Outliner with the document tree
|
||||
3. Main 3D viewport
|
||||
4. Property editor
|
||||
5. Toolbar with added BIM functionality
|
||||
6. Sidebar with BlenderBIM & 4D/5D Toolkit panels
|
||||
7. Status bar with information about the currently used Blender and BlenderBIM version
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
@georeference
|
||||
Feature: Georeference
|
||||
|
||||
Scenario: Add georeferencing
|
||||
Given an empty IFC project
|
||||
When I press "bim.add_georeferencing"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable editing georeferencing
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_georeferencing"
|
||||
When I press "bim.enable_editing_georeferencing"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Remove georeferencing
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_georeferencing"
|
||||
When I press "bim.remove_georeferencing"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Edit georeferencing
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_georeferencing"
|
||||
And I press "bim.enable_editing_georeferencing"
|
||||
When I press "bim.edit_georeferencing"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Disable editing georeferencing
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_georeferencing"
|
||||
And I press "bim.enable_editing_georeferencing"
|
||||
When I press "bim.disable_editing_georeferencing"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Get cursor location
|
||||
Given an empty IFC project
|
||||
When I press "bim.get_cursor_location"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Convert local to map
|
||||
Given an empty IFC project
|
||||
When I set "scene.BIMGeoreferenceProperties.local_coordinates" to "0,0,0"
|
||||
Then "scene.BIMGeoreferenceProperties.map_coordinates" is "0.0,0.0,0.0"
|
||||
|
||||
Scenario: Convert map to local
|
||||
Given an empty IFC project
|
||||
When I set "scene.BIMGeoreferenceProperties.map_coordinates" to "0,0,0"
|
||||
Then "scene.BIMGeoreferenceProperties.local_coordinates" is "0.0,0.0,0.0"
|
||||
@@ -1,57 +0,0 @@
|
||||
@qto
|
||||
Feature: Qto
|
||||
|
||||
Scenario: Calculate circle radius
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
When I press "bim.calculate_circle_radius"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "1.732"
|
||||
|
||||
Scenario: Calculate edge lengths
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
When I press "bim.calculate_edge_lengths"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "24.0"
|
||||
|
||||
Scenario: Calculate face areas
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
When I press "bim.calculate_face_areas"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "24.0"
|
||||
|
||||
Scenario: Calculate object volumes
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
When I press "bim.calculate_object_volumes"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "8.0"
|
||||
|
||||
Scenario: Execute qto method - formwork areas
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And I add a cube of size "1" at "1,0,0"
|
||||
And the object "Cube" is selected
|
||||
And additionally the object "Cube.001" is selected
|
||||
When I press "bim.calculate_formwork_area"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "21.5"
|
||||
|
||||
Scenario: Execute qto method - side formwork areas
|
||||
Given an empty Blender session
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
When I press "bim.calculate_side_formwork_area"
|
||||
Then "scene.BIMQtoProperties.qto_result" is "16.0"
|
||||
|
||||
Scenario: Calculate all quantities
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I press "bim.assign_class(ifc_class='IfcWall', predefined_type='SOLIDWALL')"
|
||||
When I press "bim.perform_quantity_take_off"
|
||||
And the variable "qset_id" is "{ifc}.by_type('IfcElementQuantity')[0].id()"
|
||||
And I press "bim.enable_pset_editing(pset_id={qset_id}, obj='IfcWall/Cube', obj_type='Object')"
|
||||
Then "active_object.PsetProperties.active_pset_name" is "Qto_WallBaseQuantities"
|
||||
And "active_object.PsetProperties.properties['Length'].metadata.float_value" is roughly "2000.0"
|
||||
@@ -1,16 +0,0 @@
|
||||
@search
|
||||
Feature: Search
|
||||
|
||||
Scenario: Select all walls
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_filter_group(module='search')"
|
||||
And I set "scene.BIMSearchProperties.facet" to "entity"
|
||||
And I press "bim.add_filter(index=0, type='entity', module='search')"
|
||||
And I set "scene.BIMSearchProperties.filter_groups[0].filters[0].value" to "IfcWall"
|
||||
When I press "bim.search(property_group='BIMSearchProperties')"
|
||||
Then the object "IfcWall/Cube" is selected
|
||||
@@ -1,17 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 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/>.
|
||||
@@ -1,17 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 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/>.
|
||||
@@ -1,17 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 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/>.
|
||||
@@ -2,7 +2,7 @@
|
||||
# Visit https://bit.ly/cffinit to generate yours today!
|
||||
|
||||
cff-version: 1.2.0
|
||||
title: BlenderBIM Add-on
|
||||
title: Bonsai
|
||||
message: >-
|
||||
If you use this software, please cite it using the
|
||||
metadata from this file.
|
||||
@@ -10,9 +10,9 @@ type: software
|
||||
authors:
|
||||
- name: "IfcOpenShell contributors"
|
||||
repository-code: >-
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/blenderbim
|
||||
url: 'https://blenderbim.org/'
|
||||
repository-artifact: 'https://blenderbim.org/download.html'
|
||||
https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/bonsai
|
||||
url: 'https://bonsaibim.org/'
|
||||
repository-artifact: 'https://bonsaibim.org/download.html'
|
||||
abstract: >-
|
||||
Add-on to Blender providing a graphical native IFC
|
||||
authoring platform
|
||||
@@ -1,20 +1,20 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020-2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# Bonsai 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,
|
||||
# Bonsai 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/>.
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
PYTHON:=python3.11
|
||||
PIP:=pip3.11
|
||||
@@ -49,11 +49,6 @@ LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
PYVERSION:=py310
|
||||
PYPI_IMP:=cp
|
||||
|
||||
ifeq ($(PYVERSION), py310)
|
||||
PYLIBDIR:=python3.10
|
||||
PYNUMBER:=310
|
||||
PYPI_VERSION:=3.10
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
PYLIBDIR:=python3.11
|
||||
PYNUMBER:=311
|
||||
@@ -86,7 +81,7 @@ BLENDER_PLATFORM:=windows-x64
|
||||
endif
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=d51fa2c
|
||||
OLD:=03935a9
|
||||
.PHONY: bump
|
||||
bump:
|
||||
cd . && $(SED) -b "s/$(OLD)/$(NEW)/" Makefile
|
||||
@@ -100,7 +95,7 @@ endif
|
||||
rm -rf build
|
||||
mkdir -p build
|
||||
mkdir -p dist
|
||||
cp -r blenderbim build/
|
||||
cp -r bonsai build/
|
||||
|
||||
# To scope any build-time dependencies
|
||||
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
||||
@@ -108,18 +103,18 @@ endif
|
||||
|
||||
mkdir -p build/wheels
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd ../ifcopenshell-python && make dist PLATFORM=$(PLATFORM)64 PYVERSION=$(PYVERSION) && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../bcf && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcclash && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifctester && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcfm && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../bsdd && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcdiff && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifccsv && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcpatch && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifc4d && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifc5d && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifccityjson && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcopenshell-python && make dist PLATFORM=$(PLATFORM)64 PYVERSION=$(PYVERSION) && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../bcf && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifcclash && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifctester && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifcfm && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../bsdd && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifcdiff && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifccsv && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifcpatch && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifc4d && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifc5d && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd ../ifccityjson && make dist && mv dist/*.whl ../bonsai/build/wheels/
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download GitPython --dest=./wheels
|
||||
# Provides audio playback for costing
|
||||
# This is a REALLY IMPORTANT feature
|
||||
@@ -135,6 +130,8 @@ endif
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) wheel odfpy --wheel-dir=./wheels
|
||||
# Required by IFCCityJSON
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download cjio --dest=./wheels
|
||||
# Required in general for sorting all sorts of stuff in a nice way
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download natsort --dest=./wheels
|
||||
# Provides express rule validation for ifcopenshell.validate
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pytest --dest=./wheels
|
||||
# Provides Brickschema functionality
|
||||
@@ -184,14 +181,10 @@ else ifeq ($(PLATFORM), macos)
|
||||
else
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
endif
|
||||
# tomllib replacement for < Python 3.11, used to parse blender_manifest
|
||||
ifeq ($(PYVERSION), py310)
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download tomli --dest=./wheels
|
||||
endif
|
||||
|
||||
# Provides jsgantt-improved supports for web-based construction sequencing gantt charts
|
||||
cd build/blenderbim/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||
cd build/blenderbim/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||
cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
|
||||
# Provides IFCJSON functionality
|
||||
# TODO: replace with main repo if https://github.com/IFCJSON-Team/IFC2JSON_python/pull/3 is merged.
|
||||
@@ -212,7 +205,7 @@ endif
|
||||
# Brickschema requires pkg_resources which is provided by Blender.
|
||||
# Provides Brickschema functionality
|
||||
# For now lets bundle the latest nightly schema
|
||||
cd build/blenderbim/bim/schema && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||
cd build/bonsai/bim/schema && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||
|
||||
# Required for hipped roof generation
|
||||
cd build && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
|
||||
@@ -227,11 +220,11 @@ endif
|
||||
cp -r build/bpypolyskel-master/dist/*.whl build/wheels/
|
||||
|
||||
# Required for Desktop icon and file association
|
||||
cp -r blenderbim/libs/desktop build/blenderbim/libs/
|
||||
cp -r bonsai/libs/desktop build/bonsai/libs/
|
||||
|
||||
# Generate translations module for BBIM build
|
||||
git clone https://github.com/IfcOpenShell/blenderbim-translations.git build/working
|
||||
$(PYTHON) scripts/bbim_translations.py -i "build/working" -o "build/blenderbim"
|
||||
# Generate translations module for Bonsai build
|
||||
git clone https://github.com/IfcOpenShell/bonsai-translations.git build/working
|
||||
$(PYTHON) scripts/bonsai_translations.py -i "build/working" -o "build/bonsai"
|
||||
rm -rf build/working
|
||||
|
||||
# Remove dependencies also bundled with Blender
|
||||
@@ -239,17 +232,17 @@ endif
|
||||
|
||||
cp pyproject.toml build/
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
$(SED) "s/0.0.0/$(VERSION)/" build/blenderbim/blender_manifest.toml
|
||||
$(SED) "s/0.0.0/$(VERSION)/" build/bonsai/blender_manifest.toml
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||
else
|
||||
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/blenderbim/blender_manifest.toml
|
||||
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/blenderbim/__init__.py
|
||||
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/bonsai/blender_manifest.toml
|
||||
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/bonsai/__init__.py
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
|
||||
endif
|
||||
|
||||
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/blenderbim/blender_manifest.toml
|
||||
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/bonsai/blender_manifest.toml
|
||||
|
||||
# Provides BlenderBIM Add-on functionality
|
||||
# Provides bonsai Add-on functionality
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||
else
|
||||
@@ -266,7 +259,7 @@ endif
|
||||
echo "Error: non-wheel dependencies are not supported by Blender!"; \
|
||||
exit 1; \
|
||||
fi
|
||||
mv build/wheels/*.whl build/blenderbim/wheels/
|
||||
mv build/wheels/*.whl build/bonsai/wheels/
|
||||
|
||||
# Ugly patch to accomodate MacOS universal builds not being recognized by Blender.
|
||||
# Blender doesn't look into the wheel contents, it just checks the name.
|
||||
@@ -275,41 +268,41 @@ endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
# Has universal and x86_64 builds.
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/MarkupSafe-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/MarkupSafe-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
|
||||
# Has universal and x86_64 builds.
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/lxml-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/lxml-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/tzfpy-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/tzfpy-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2/macosx_10_12_arm64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
|
||||
# Has only universal build.
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/greenlet-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/greenlet-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
endif
|
||||
ifeq ($(PLATFORM), macos)
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/tzfpy-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/tzfpy-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2/macosx_10_9_x86_64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/greenlet-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/greenlet-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_x86_64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
|
||||
# has universal and amr64 builds.
|
||||
prev_whl_name=$$(find build/blenderbim/wheels/fonttools-*.whl); \
|
||||
prev_whl_name=$$(find build/bonsai/wheels/fonttools-*.whl); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_x86_64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
endif
|
||||
|
||||
# Safeguard for unhandled universal files.
|
||||
wheels=$$(find build/blenderbim/wheels/*universal2*.whl); \
|
||||
wheels=$$(find build/bonsai/wheels/*universal2*.whl); \
|
||||
if [ -n "$$wheels" ]; then \
|
||||
echo "Found universal2 wheel files:"; \
|
||||
echo "$$wheels"; \
|
||||
@@ -320,7 +313,7 @@ endif
|
||||
ifneq ($(PLATFORM), linux)
|
||||
# Safeguard: in case one of `pip download` will break,
|
||||
# it will produce a linux wheel for non-linux build (our github action machine is using linux).
|
||||
wheels=$$(find build/blenderbim/wheels/*manylinux_*.whl); \
|
||||
wheels=$$(find build/bonsai/wheels/*manylinux_*.whl); \
|
||||
if [ -n "$$wheels" ]; then \
|
||||
echo "Found linux wheel files in non-linux build:"; \
|
||||
echo "$$wheels"; \
|
||||
@@ -329,18 +322,18 @@ ifneq ($(PLATFORM), linux)
|
||||
fi
|
||||
endif
|
||||
|
||||
$(PYTHON) scripts/get_wheels.py build/blenderbim/wheels build/blenderbim/blender_manifest.toml
|
||||
rm -rf build/blenderbim/bim/
|
||||
rm -rf build/blenderbim/core/
|
||||
rm -rf build/blenderbim/tool/
|
||||
$(PYTHON) scripts/get_wheels.py build/bonsai/wheels build/bonsai/blender_manifest.toml
|
||||
rm -rf build/bonsai/bim/
|
||||
rm -rf build/bonsai/core/
|
||||
rm -rf build/bonsai/tool/
|
||||
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
cd build && zip -r blenderbim_$(PYVERSION)-$(VERSION)-$(BLENDER_PLATFORM).zip ./blenderbim
|
||||
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-$(BLENDER_PLATFORM).zip ./bonsai
|
||||
else
|
||||
cd build && zip -r blenderbim_$(PYVERSION)-$(VERSION)-alpha$(VERSION_DATE)-$(BLENDER_PLATFORM).zip ./blenderbim
|
||||
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-alpha$(VERSION_DATE)-$(BLENDER_PLATFORM).zip ./bonsai
|
||||
endif
|
||||
|
||||
mv build/blenderbim*.zip dist/
|
||||
mv build/bonsai*.zip dist/
|
||||
|
||||
rm -rf build
|
||||
|
||||
@@ -383,13 +376,13 @@ qa:
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
coverage run --source blenderbim.core -m pytest -p no:pytest-blender test/core
|
||||
coverage run --source bonsai.core -m pytest -p no:pytest-blender test/core
|
||||
coverage html
|
||||
xdg-open htmlcov/index.html
|
||||
|
||||
.PHONY: license
|
||||
license:
|
||||
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "BlenderBIM Add-on" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
|
||||
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "Bonsai" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -0,0 +1,5 @@
|
||||
# Bonsai
|
||||
|
||||
An add-on to Blender to allow BIM functionality.
|
||||
|
||||
More information on the [Bonsai website](https://bonsaibim.org).
|
||||
@@ -1,25 +1,25 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# Bonsai 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,
|
||||
# Bonsai 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/>.
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure we don't try to import bpy or blenderbim.bim
|
||||
# Ensure we don't try to import bpy or bonsai.bim
|
||||
# to support running core tests.
|
||||
# We assume if bpy was never loaded in current python session
|
||||
# then we're not in Blender. It's still possible to use
|
||||
@@ -51,14 +51,15 @@ def get_last_commit_hash() -> Union[str, None]:
|
||||
return last_commit_hash[:7]
|
||||
|
||||
|
||||
# Accessed from blenderbim extension:
|
||||
# Accessed from bonsai extension:
|
||||
bbim_semver: dict[str, Any] = {}
|
||||
|
||||
# Accessed from blenderbim dependency:
|
||||
# Accessed from bonsai dependency:
|
||||
last_error = None
|
||||
last_actions: deque = deque(maxlen=10)
|
||||
FIRST_INSTALLED_BBIM_VERSION: Union[str, None] = None
|
||||
REINSTALLED_BBIM_VERSION: Union[str, None] = None
|
||||
REGISTERED_BBIM_PACKAGE: str
|
||||
|
||||
|
||||
def initialize_bbim_semver():
|
||||
@@ -68,14 +69,11 @@ def initialize_bbim_semver():
|
||||
in `addon_utils.modules()->bl_info['version']`,
|
||||
therefore we just parse it from .toml.
|
||||
"""
|
||||
if sys.version_info >= (3, 11):
|
||||
import tomllib as toml
|
||||
else:
|
||||
import tomli as toml
|
||||
import tomllib
|
||||
|
||||
toml_path = Path(__file__).parent / "blender_manifest.toml"
|
||||
with open(toml_path, "rb") as f:
|
||||
manifest = toml.load(f)
|
||||
manifest = tomllib.load(f)
|
||||
semver_pattern = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||
version_str = manifest["version"]
|
||||
re_version = re.match(semver_pattern, version_str)
|
||||
@@ -96,8 +94,8 @@ def get_debug_info():
|
||||
"machine": platform.machine(),
|
||||
"processor": platform.processor(),
|
||||
"blender_version": bpy.app.version_string,
|
||||
"blenderbim_version": bbim_version,
|
||||
"blenderbim_commit_hash": get_last_commit_hash(),
|
||||
"bonsai_version": bbim_version,
|
||||
"bonsai_commit_hash": get_last_commit_hash(),
|
||||
"last_actions": last_actions,
|
||||
"last_error": last_error,
|
||||
}
|
||||
@@ -129,7 +127,7 @@ def safe_link_dlls() -> None:
|
||||
# Then, Blender won't have a problem unlinking unloaded dlls as they are still linked somewhere.
|
||||
# On register() we clean up our temp directory with binaries.
|
||||
#
|
||||
# TODO: If user uninstalls BlenderBIM to never use it again, temporary directory won't be cleared.
|
||||
# TODO: If user uninstalls Bonsai to never use it again, temporary directory won't be cleared.
|
||||
#
|
||||
# See: https://projects.blender.org/blender/blender/issues/125049
|
||||
import bpy
|
||||
@@ -215,7 +213,7 @@ if IN_BLENDER:
|
||||
try:
|
||||
import git
|
||||
|
||||
# We can't just use __file__ as blenderbim/__init__.py is typically not symlinked
|
||||
# We can't just use __file__ as bonsai/__init__.py is typically not symlinked
|
||||
# as Blender have errors symlinking main addon package file.
|
||||
path = Path(__file__).resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
@@ -241,23 +239,27 @@ if IN_BLENDER:
|
||||
if platform.system() == "Windows":
|
||||
clean_up_dlls_safe_links()
|
||||
|
||||
import blenderbim.bim
|
||||
import bonsai
|
||||
|
||||
bonsai.REGISTERED_BBIM_PACKAGE = __package__
|
||||
|
||||
import bonsai.bim
|
||||
|
||||
current_version = bbim_semver["version"]
|
||||
if blenderbim.FIRST_INSTALLED_BBIM_VERSION is None:
|
||||
blenderbim.FIRST_INSTALLED_BBIM_VERSION = current_version
|
||||
elif not blenderbim.REINSTALLED_BBIM_VERSION and blenderbim.FIRST_INSTALLED_BBIM_VERSION != current_version:
|
||||
blenderbim.REINSTALLED_BBIM_VERSION = current_version
|
||||
if bonsai.FIRST_INSTALLED_BBIM_VERSION is None:
|
||||
bonsai.FIRST_INSTALLED_BBIM_VERSION = current_version
|
||||
elif not bonsai.REINSTALLED_BBIM_VERSION and bonsai.FIRST_INSTALLED_BBIM_VERSION != current_version:
|
||||
bonsai.REINSTALLED_BBIM_VERSION = current_version
|
||||
|
||||
blenderbim.bim.register()
|
||||
bonsai.bim.register()
|
||||
|
||||
def unregister():
|
||||
if platform.system() == "Windows":
|
||||
safe_link_dlls()
|
||||
|
||||
import blenderbim.bim
|
||||
import bonsai.bim
|
||||
|
||||
blenderbim.bim.unregister()
|
||||
bonsai.bim.unregister()
|
||||
|
||||
except:
|
||||
|
||||
@@ -279,10 +281,10 @@ if IN_BLENDER:
|
||||
|
||||
print(last_error)
|
||||
print(format_debug_info(get_debug_info()))
|
||||
print("\nFATAL ERROR: Unable to load the BlenderBIM Add-on")
|
||||
print("\nFATAL ERROR: Unable to load Bonsai")
|
||||
|
||||
class BIM_PT_fatal_error(bpy.types.Panel):
|
||||
bl_label = "BlenderBIM Fatal Error"
|
||||
bl_label = "Bonsai Fatal Error"
|
||||
bl_idname = "SCENE_PT_error_message"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
@@ -293,7 +295,7 @@ if IN_BLENDER:
|
||||
|
||||
layout = self.layout
|
||||
layout.alert = True
|
||||
layout.label(text="BlenderBIM could not load.", icon="ERROR")
|
||||
layout.label(text="Bonsai could not load.", icon="ERROR")
|
||||
if info["os"] == "Windows":
|
||||
layout.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
|
||||
else:
|
||||
@@ -303,10 +305,10 @@ if IN_BLENDER:
|
||||
b3d = ".".join(info["blender_version"].split(".")[0:2])
|
||||
box.label(text="System Information:")
|
||||
box.label(text=f"Blender {b3d} {info['os']} {info['machine']}", icon="BLENDER")
|
||||
blenderbim_version = info["blenderbim_version"]
|
||||
if commit_hash := info.get("blenderbim_commit_hash"):
|
||||
blenderbim_version += f"-{commit_hash}"
|
||||
box.label(text=f"Python {py} BBIM {info['blenderbim_version']}", icon="SCRIPTPLUGINS")
|
||||
bonsai_version = info["bonsai_version"]
|
||||
if commit_hash := info.get("bonsai_commit_hash"):
|
||||
bonsai_version += f"-{commit_hash}"
|
||||
box.label(text=f"Python {py} BBIM {info['bonsai_version']}", icon="SCRIPTPLUGINS")
|
||||
|
||||
binary_py = get_binary_info().get("binary_python_version")
|
||||
if binary_py and py != binary_py:
|
||||
@@ -314,7 +316,7 @@ if IN_BLENDER:
|
||||
# From wrong-platform-build issues we're guarded by Blender extension installation.
|
||||
# But Blender currently doesn't support separate builds for different Python version,
|
||||
# so those issues might still slip in.
|
||||
box.label(text="BlenderBIM installed for wrong Python version.")
|
||||
box.label(text="Bonsai installed for wrong Python version.")
|
||||
box.label(text=f"Expected: {py}. Got: {binary_py}.")
|
||||
# On reinstallation, dependencies versions doesn't change, so Blender will just ignore new dependencies.
|
||||
# So, we need to make user will disable an extension (just uninstallation won't remove dependencies).
|
||||
@@ -322,16 +324,16 @@ if IN_BLENDER:
|
||||
# as dependencies failed to load due to Python version mismatch.
|
||||
box.label(text="Try reinstalling with the correct Python version.")
|
||||
box.label(text="Before reinstallation make sure to")
|
||||
box.label(text="DISABLE BlenderBIM (uninstallation won't help).")
|
||||
box.label(text="DISABLE Bonsai (uninstallation won't help).")
|
||||
box.label(text="You can download correct version below.")
|
||||
|
||||
layout.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
|
||||
op = layout.operator("bim.open_uri", text="How Can I Fix This?")
|
||||
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html#installation-issues"
|
||||
op.uri = "https://docs.bonsaibim.org/users/troubleshooting.html#installation-issues"
|
||||
|
||||
layout.label(text="Try Reinstalling:", icon="IMPORT")
|
||||
op = layout.operator("bim.open_uri", text="Re-download Add-on")
|
||||
bbim_version = info["blenderbim_version"]
|
||||
bbim_version = info["bonsai_version"]
|
||||
py_tag = py.replace(".", "")
|
||||
if "Linux" in info["os"]:
|
||||
os = "linux-x64"
|
||||
@@ -342,7 +344,7 @@ if IN_BLENDER:
|
||||
os = "macos-x64"
|
||||
else:
|
||||
os = "windows-x64"
|
||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_version}/blenderbim_py{py_tag}-{bbim_version}-{os}.zip"
|
||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/bonsai-{bbim_version}/bonsai_py{py_tag}-{bbim_version}-{os}.zip"
|
||||
|
||||
class OpenUri(bpy.types.Operator):
|
||||
bl_idname = "bim.open_uri"
|
||||
@@ -1,20 +1,20 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# Bonsai 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,
|
||||
# Bonsai 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/>.
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import bpy
|
||||
@@ -25,7 +25,7 @@ from . import handler, ui, prop, operator, helper
|
||||
from typing import Callable, Union
|
||||
|
||||
try:
|
||||
from blenderbim.translations import translations_dict
|
||||
from bonsai.translations import translations_dict
|
||||
except ImportError:
|
||||
translations_dict = {}
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -91,7 +91,7 @@ modules = {
|
||||
|
||||
|
||||
for name in modules.keys():
|
||||
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
|
||||
modules[name] = importlib.import_module(f"bonsai.bim.module.{name}")
|
||||
|
||||
|
||||
classes = [
|
||||
@@ -254,7 +254,7 @@ def register():
|
||||
icon_preview.load(icon_name, icon_path, "IMAGE")
|
||||
|
||||
icons = icon_preview
|
||||
bpy.app.translations.register("blenderbim", translations_dict)
|
||||
bpy.app.translations.register("bonsai", translations_dict)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -291,10 +291,10 @@ def unregister():
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
import blenderbim.tool as tool
|
||||
import bonsai.tool as tool
|
||||
|
||||
# use tuple() as method will be removing keys from dict
|
||||
for panel in tuple(original_scene_panels_polls.keys()):
|
||||
tool.Blender.remove_scene_panel_override(panel)
|
||||
|
||||
bpy.app.translations.unregister("blenderbim")
|
||||
bpy.app.translations.unregister("bonsai")
|
||||
@@ -1,21 +1,21 @@
|
||||
/*
|
||||
* BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
* Bonsai - OpenBIM Blender Add-on
|
||||
* Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
*
|
||||
* This file is part of BlenderBIM Add-on.
|
||||
* This file is part of Bonsai.
|
||||
*
|
||||
* BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
* Bonsai 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,
|
||||
* Bonsai 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/>.
|
||||
* long with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
* { stroke-linecap: round; stroke-linejoin: round; }
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |