Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 773605330b | |||
| a9a45f886b | |||
| 6b6f70694e | |||
| 9a5e37c82b | |||
| 5625f62bf5 | |||
| d12f59fa72 | |||
| 13fcc62406 | |||
| 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 |
@@ -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,149 @@
|
||||
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
|
||||
|
||||
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,19 @@ src/ifcopenshell-python/test/build
|
||||
# mypy cache
|
||||
.mypy_cache
|
||||
|
||||
# blenderbim libs
|
||||
src/blenderbim/blenderbim/libs
|
||||
# bonsai libs
|
||||
src/bonsai/bonsai/libs
|
||||
|
||||
# blenderbim i18n
|
||||
src/blenderbim/blenderbim/translations.py
|
||||
# bonsai i18n
|
||||
src/bonsai/bonsai/translations.py
|
||||
|
||||
# blenderbim test temp files
|
||||
src/blenderbim/test/files/temp
|
||||
src/blenderbim/test/files/basic.ifc.cache.blend
|
||||
src/blenderbim/test/files/basic.ifc.cache.sqlite
|
||||
# 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
|
||||
|
||||
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 +100,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 \
|
||||
|
||||
@@ -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/) |
|
||||
| blenderbim | 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=blenderbim&expanded=true) [](https://community.chocolatey.org/packages/blenderbim-nightly/) |
|
||||
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later | [](https://pypi.org/project/bsdd/) |
|
||||
| ifc2ca | Utility to convert IFC structural analysis models to Code_Aster | LGPL-3.0-or-later |
|
||||
| ifc4d | Convert to and from IFC and project management software | LGPL-3.0-or-later | [](https://pypi.org/project/ifc4d/) |
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,5 @@
|
||||
import bcf.v2.topic
|
||||
import bcf.v3.topic
|
||||
from typing import Union
|
||||
|
||||
TopicHandler = Union[bcf.v2.topic.TopicHandler, bcf.v3.topic.TopicHandler]
|
||||
@@ -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
|
||||
@@ -80,14 +80,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 +109,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 +133,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,7 +230,9 @@ 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:
|
||||
def extract_file(
|
||||
self, entity: Union[mdl.HeaderFile, mdl.BimSnippet, mdl.TopicDocumentReference], 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
|
||||
@@ -228,11 +240,9 @@ class TopicHandler:
|
||||
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
|
||||
@@ -268,7 +278,7 @@ class TopicHandler:
|
||||
|
||||
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 +288,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 +303,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,7 @@ 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._bim_snippet: Optional[bytes] = None
|
||||
self._xml_handler = xml_handler or XmlParserSerializer()
|
||||
self._topic_dir = topic_dir
|
||||
@@ -77,15 +77,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:
|
||||
@@ -165,7 +165,7 @@ 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 add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
|
||||
"""
|
||||
Add a viewpoint tergeting an IFC element to the topic.
|
||||
|
||||
@@ -174,8 +174,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 +189,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,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,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,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,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Wrapper script to launch BlenderBIM.
|
||||
|
||||
# If the `blender` executable can't be found in your PATH environment, set
|
||||
# BLENDER_EXE to the location of your preferred `blender` executable.
|
||||
|
||||
#BLENDER_EXE=/opt/blender-3.3/blender
|
||||
|
||||
: ${BLENDER_EXE:=blender}
|
||||
|
||||
if [[ $# -eq 1 && -f "$@" ]]; then
|
||||
"${BLENDER_EXE}" --python-expr 'import bpy; bpy.data.batch_remove(bpy.data.objects); bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True); bpy.ops.bim.load_project(filepath="'"$@"'")'
|
||||
else
|
||||
"${BLENDER_EXE}" --python-expr 'import bpy; bpy.data.batch_remove(bpy.data.objects); bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True); bpy.ops.bim.create_project()'
|
||||
fi;
|
||||
@@ -1,9 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Exec=blenderbim %f
|
||||
Name=BlenderBIM
|
||||
Icon=blenderbim
|
||||
X-Desktop-File-Install-Version=0.26
|
||||
GenericName=Blender BIM add-on
|
||||
MimeType=application/x-ifc;
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-ifc">
|
||||
<sub-class-of type="text/plain"/>
|
||||
<comment xml:lang="en">Industry Foundation Classes</comment>
|
||||
<magic priority="50">
|
||||
<match type="string" value="ISO-10303-21;" offset="0"/>
|
||||
</magic>
|
||||
<generic-icon name="x-ifc"/>
|
||||
<glob pattern="*.ifc"/>
|
||||
<glob pattern="*.ifczip"/>
|
||||
<glob pattern="*.ifcxml"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -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,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,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
|
||||
@@ -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
|
||||
@@ -184,14 +179,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 +203,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 +218,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 +230,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 +257,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 +266,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 +311,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 +320,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 +374,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,10 +51,10 @@ 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
|
||||
@@ -68,14 +68,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 +93,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 +126,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 +212,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 +238,23 @@ if IN_BLENDER:
|
||||
if platform.system() == "Windows":
|
||||
clean_up_dlls_safe_links()
|
||||
|
||||
import blenderbim.bim
|
||||
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 +276,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 +290,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 +300,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 +311,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 +319,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 +339,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 |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
@@ -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/>.
|
||||
*/
|
||||
|
||||
.cut { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -43,3 +43,9 @@
|
||||
}
|
||||
|
||||
text, tspan { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT', 'DejaVu Sans Condensed', 'Liberation Sans', 'Arial Narrow', 'Arial'; }
|
||||
|
||||
.border {
|
||||
stroke: #000000;
|
||||
stroke-width:.125;
|
||||
fill: none; /* will be overriden if background color is set in .ods, or .xlsx */
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -5,8 +5,8 @@ FILE_NAME('/dev/null','2024-03-03T16:03:58+11:00',(),(),'IfcOpenShell v0.7.0-eaa
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('3NJUxHyTzEv96oezyDYEqf',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0D39k7hbT5ce$jWaCd2niG',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#1=IFCPROJECT('3NJUxHyTzEv96oezyDYEqf',$,'Bonsai Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0D39k7hbT5ce$jWaCd2niG',$,'Bonsai Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('2RWVohyP57PBHT9zCs4H2O',$,$,$,#1,(#2));
|
||||
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
@@ -5,8 +5,8 @@ FILE_NAME('/dev/null','2024-03-03T16:03:58+11:00',(),(),'IfcOpenShell v0.7.0-eaa
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('3NJUxHyTzEv96oezyDYEqf',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0D39k7hbT5ce$jWaCd2niG',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#1=IFCPROJECT('3NJUxHyTzEv96oezyDYEqf',$,'Bonsai Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0D39k7hbT5ce$jWaCd2niG',$,'Bonsai Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('2RWVohyP57PBHT9zCs4H2O',$,$,$,#1,(#2));
|
||||
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
@@ -24,7 +24,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/bonsai/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
|
||||
id="rect1036"
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -24,7 +24,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/bonsai/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
y="3.1753578e-05"
|
||||
x="-4.3300333e-05"
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -24,7 +24,7 @@
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<desc
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/bonsai/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||
<rect
|
||||
y="3.1753578e-05"
|
||||
x="-4.3300333e-05"
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -2,11 +2,11 @@ import sys
|
||||
import os
|
||||
import webbrowser
|
||||
|
||||
blenderbim_lib_path = os.environ.get("BLENDERBIM_LIB_PATH")
|
||||
blenderbim_version = os.environ.get("BLENDERBIM_VERSION")
|
||||
bonsai_lib_path = os.environ.get("BONSAI_LIB_PATH")
|
||||
bonsai_version = os.environ.get("BONSAI_VERSION")
|
||||
|
||||
if blenderbim_lib_path:
|
||||
sys.path.insert(0, blenderbim_lib_path)
|
||||
if bonsai_lib_path:
|
||||
sys.path.insert(0, bonsai_lib_path)
|
||||
|
||||
import argparse
|
||||
from aiohttp import web
|
||||
@@ -20,22 +20,17 @@ 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 = {}
|
||||
blender_theme = {}
|
||||
|
||||
|
||||
# we define two namespaces, one for Bonsai and one for Web UI
|
||||
# each namespace has its own event handlers that are called when an event is emitted by a client
|
||||
# Web namespace
|
||||
class WebNamespace(socketio.AsyncNamespace):
|
||||
def __init__(self, namespace):
|
||||
@@ -44,6 +39,8 @@ class WebNamespace(socketio.AsyncNamespace):
|
||||
async def on_connect(self, sid, environ):
|
||||
print(f"Web client connected: {sid}")
|
||||
if blender_messages:
|
||||
await sio.emit("connected_clients", list(blender_messages.keys()), namespace="/web", room=sid)
|
||||
await sio.emit("theme_data", blender_theme, namespace="/web", room=sid)
|
||||
await self.send_cached_messages(sid)
|
||||
|
||||
async def on_disconnect(self, sid):
|
||||
@@ -71,7 +68,8 @@ class WebNamespace(socketio.AsyncNamespace):
|
||||
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)
|
||||
|
||||
if "demo_data" in messages:
|
||||
await self.emit("demo_data", {"blenderId": blenderId, "data": messages["demo_data"]}, room=sid)
|
||||
|
||||
# Blender namespace
|
||||
class BlenderNamespace(socketio.AsyncNamespace):
|
||||
@@ -108,36 +106,50 @@ class BlenderNamespace(socketio.AsyncNamespace):
|
||||
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)
|
||||
print(f"Drawings data from Blender client {sid}")
|
||||
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"))
|
||||
async def on_theme_data(self, sid, data):
|
||||
global blender_theme
|
||||
blender_theme = data
|
||||
await sio.emit("theme_data", data, namespace="/web")
|
||||
|
||||
|
||||
# Define a route to render the index.html template
|
||||
async def index(request):
|
||||
# this function will be called when the event demo_data is emitted
|
||||
async def on_demo_data(self, sid, data):
|
||||
print(f"Demo data from Blender client {sid}")
|
||||
blender_messages[sid]["demo_data"] = data
|
||||
await sio.emit("demo_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
|
||||
async def schedules(request):
|
||||
with open("templates/index.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def gantt(request):
|
||||
async def sequencing(request):
|
||||
with open("templates/gantt.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def drawings(request):
|
||||
async def documentation(request):
|
||||
with open("templates/drawings.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port})
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
# This is a request handler for the /demo URL endpoint.
|
||||
# It serves the demo HTML file after using pystache to render the variables
|
||||
async def demo(request):
|
||||
with open("templates/demo.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
@@ -156,12 +168,22 @@ async def on_startup(app):
|
||||
json.dump(pids, f, indent=4)
|
||||
|
||||
|
||||
app.router.add_get("/", index)
|
||||
app.router.add_get("/drawings", drawings)
|
||||
app.router.add_get("/gantt", gantt)
|
||||
# Add on startup function
|
||||
app.on_startup.append(on_startup)
|
||||
|
||||
# Attach namespaces
|
||||
sio.register_namespace(WebNamespace("/web"))
|
||||
sio.register_namespace(BlenderNamespace("/blender"))
|
||||
|
||||
# Regsier routes
|
||||
app.router.add_get("/", schedules)
|
||||
app.router.add_get("/documentation", documentation)
|
||||
app.router.add_get("/sequencing", sequencing)
|
||||
app.router.add_get("/demo", demo)
|
||||
|
||||
# Add static files
|
||||
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():
|
||||
@@ -0,0 +1,348 @@
|
||||
: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: 1.5rem;
|
||||
--nav-height: 0.625rem;
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
: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);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
color: var(--blender-text, 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(--blender-top-bar-header, 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(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: var(--margin-large);
|
||||
height: var(--logo-height);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: var(--margin-large);
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-large);
|
||||
margin-right: var(--margin-medium);
|
||||
}
|
||||
|
||||
#toggle-theme:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
background-color: var(--blender-button-background, var(--bg-color));
|
||||
color: var(--blender-button-text, var(--text-color));
|
||||
border-color: var(--blender-button-border, var(--border-color));
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
/* =========== styling for code blocks and rendering =========== */
|
||||
/* ================== This is auto-generated ================== */
|
||||
|
||||
|
||||
#explanation {
|
||||
font-family: "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
|
||||
font-size: 16px;
|
||||
padding: 0 26px;
|
||||
line-height: 22px;
|
||||
word-wrap: break-word;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#code-csp-warning {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
margin: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
background-color: #444444;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
#code-csp-warning:hover {
|
||||
text-decoration: none;
|
||||
background-color: #007acc;
|
||||
box-shadow: 2px 2px 2px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
#explanation.scrollBeyondLastLine {
|
||||
margin-bottom: calc(100vh - 22px);
|
||||
}
|
||||
|
||||
#explanation.showEditorSelection .code-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#explanation.showEditorSelection .code-active-line:before,
|
||||
#explanation.showEditorSelection .code-line:hover:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#explanation.showEditorSelection li.code-active-line:before,
|
||||
#explanation.showEditorSelection li.code-line:hover:before {
|
||||
left: -30px;
|
||||
}
|
||||
|
||||
#explanation a:focus,
|
||||
#explanation input:focus,
|
||||
#explanation select:focus,
|
||||
#explanation textarea:focus {
|
||||
outline: 1px solid -webkit-focus-ring-color;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
#explanation hr {
|
||||
border: 0;
|
||||
height: 2px;
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
|
||||
#explanation h1 {
|
||||
padding-bottom: 0.3em;
|
||||
line-height: 1.2;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
#explanation h1,
|
||||
#explanation h2,
|
||||
#explanation h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#explanation code {
|
||||
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
||||
font-size: 1em;
|
||||
line-height: 1.357em;
|
||||
}
|
||||
|
||||
#explanation .wordWrap pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#explanation pre:not(.hljs),
|
||||
#explanation pre.hljs code>div {
|
||||
padding: 16px;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#explanation pre code {
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
#explanation .hljs-comment,
|
||||
#explanation .hljs-quote {
|
||||
color: #B6B18B;
|
||||
}
|
||||
|
||||
#explanation .hljs-variable,
|
||||
#explanation .hljs-template-variable,
|
||||
#explanation .hljs-tag,
|
||||
#explanation .hljs-name,
|
||||
#explanation .hljs-selector-id,
|
||||
#explanation .hljs-selector-class,
|
||||
#explanation .hljs-regexp,
|
||||
#explanation .hljs-deletion {
|
||||
color: #EB3C54;
|
||||
}
|
||||
|
||||
#explanation .hljs-number,
|
||||
#explanation .hljs-built_in,
|
||||
#explanation .hljs-builtin-name,
|
||||
#explanation .hljs-literal,
|
||||
#explanation .hljs-type,
|
||||
#explanation .hljs-params,
|
||||
#explanation .hljs-meta,
|
||||
#explanation .hljs-link {
|
||||
color: #E7CE56;
|
||||
}
|
||||
|
||||
#explanation .hljs-attribute {
|
||||
color: #EE7C2B;
|
||||
}
|
||||
|
||||
#explanation .hljs-string,
|
||||
#explanation .hljs-symbol,
|
||||
#explanation .hljs-bullet,
|
||||
#explanation .hljs-addition {
|
||||
color: #4FB4D7;
|
||||
}
|
||||
|
||||
#explanation .hljs-title,
|
||||
#explanation .hljs-section {
|
||||
color: #78BB65;
|
||||
}
|
||||
|
||||
#explanation .hljs-keyword,
|
||||
#explanation .hljs-selector-tag {
|
||||
color: #B45EA4;
|
||||
}
|
||||
|
||||
#explanation .hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #1C1D21;
|
||||
color: #c0c5ce;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#explanation .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#explanation .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#explanation {
|
||||
font-family: "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
#explanation pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 3px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#explanation pre:not(.hljs) {
|
||||
padding: 23px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
#explanation blockquote {
|
||||
background: rgba(127, 127, 127, 0.1);
|
||||
border-color: rgba(0, 122, 204, 0.5);
|
||||
}
|
||||
|
||||
#explanation .emoji {
|
||||
height: 1.4em;
|
||||
}
|
||||
|
||||
#explanation code {
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
#explanation :not(pre):not(.hljs)>code {
|
||||
color: #C9AE75;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
#explanation .page {
|
||||
page-break-after: always;
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
: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: 1.5rem;
|
||||
--nav-height: 0.625rem;
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
: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;
|
||||
}
|
||||
|
||||
* {
|
||||
border-radius: var(--border-radius) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: var(--base-font-size);
|
||||
color: var(--blender-text, var(--text-color));
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
color: var(--blender-text, var(--text-color));
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-right: var(--margin-tiny);
|
||||
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(--blender-top-bar-header, 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(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: var(--margin-large);
|
||||
height: var(--logo-height);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: var(--margin-large);
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none !important;
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--blender-info-warning, 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(--blender-tab-outline, var(--border-color));
|
||||
background-color: var(--blender-panel-background, 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(--blender-tab-outline, var(--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(--blender-panel-background, 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(--blender-tab-outline, var(--border-color));
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--blender-panel-header, var(--bg-color));
|
||||
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(--blender-top-bar-header, var(--nav-bg-color));
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
button,
|
||||
select {
|
||||
background-color: var(--blender-button-background, var(--bg-color));
|
||||
color: var(--blender-button-text, var(--text-color));
|
||||
border-color: var(--blender-button-border, var(--border-color));
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--blender-odd-row, var(--bg-color));
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 4;
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
}
|
||||
|
||||
#svg-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.svg-name {
|
||||
margin-bottom: var(--margin-small);
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
padding: var(--padding-small);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--blender-even-row, var(--nav-bg-color));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: var(--blender-panel-header, var(--bg-color));
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
background-color: var(--blender-panel-background, var(--bg-color));
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
li.svg-name {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.selected-svg {
|
||||
background-color: var(--blender-active-highlight, var(--bg-color));
|
||||
color: var(--blender-active-text-highlight, var(--nav-link-hover-color));
|
||||
}
|
||||
|
||||
|
||||
#dropdown-menu:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* remove bootstrap css confilcts */
|
||||
|
||||
*,
|
||||
::after,
|
||||
::before {
|
||||
box-sizing: unset;
|
||||
}
|
||||
|
||||
.btn,
|
||||
.btn:hover {
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* a:not([href]):not([class]):hover {
|
||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]) {
|
||||
text-decoration: none !important;
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
} */
|
||||
@@ -10,8 +10,9 @@
|
||||
--padding-medium: 1rem;
|
||||
--font-size-small: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
--logo-height: 1.5rem;
|
||||
--nav-height: 0.625rem;
|
||||
--border-radius: 0.3rem;
|
||||
--folder-collapse-font-size: 0.75rem;
|
||||
--folder-collapse-font-family: Courier, "Courier New", monospace;
|
||||
--box-shadow: 0 0 0.7rem #5f5f5f66;
|
||||
@@ -35,7 +36,7 @@
|
||||
--group-item-bg-color: #4b4b4b;
|
||||
--input-bg-color: #3b3b3b;
|
||||
--input-border-color: #000;
|
||||
--details-border-color: #464444;
|
||||
--border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@@ -47,7 +48,12 @@
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--details-border-color: #222;
|
||||
--border-color: #222;
|
||||
}
|
||||
|
||||
|
||||
*:not(td, tr, td, .gmain) {
|
||||
border-radius: var(--border-radius) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -55,8 +61,8 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--primary-text-color);
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
color: var(--blender-text, var(--primary-text-color));
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
display: flex;
|
||||
@@ -77,12 +83,13 @@ h3 {
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
background-color: var(--blender-top-bar-header, 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);
|
||||
border-bottom: 2px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
@@ -93,32 +100,28 @@ nav .logo {
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 0.0625rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 3.125rem;
|
||||
margin-right: var(--margin-large);
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--nav-link-color);
|
||||
font-size: var(--font-size-large);
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--nav-link-hover-color);
|
||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
.warning,
|
||||
.warning-icon {
|
||||
color: var(--blender-info-warning, var(--warning-color));
|
||||
}
|
||||
|
||||
.table-description {
|
||||
@@ -127,8 +130,8 @@ nav ul li a.active {
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-color: var(--border-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-color: var(--blender-tab-outline, var(--border-color));
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: var(--box-shadow);
|
||||
text-align: center;
|
||||
@@ -136,7 +139,15 @@ nav ul li a.active {
|
||||
|
||||
.table-description tr {
|
||||
display: table-row;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
}
|
||||
|
||||
.table-description tr:nth-child(even) {
|
||||
background-color: var(--blender-even-row);
|
||||
}
|
||||
|
||||
.table-description tr:nth-child(odd) {
|
||||
background-color: var(--blender-odd-row);
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
@@ -157,8 +168,8 @@ nav ul li a.active {
|
||||
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);
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
@@ -176,7 +187,7 @@ nav ul li a.active {
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -185,7 +196,7 @@ nav ul li a.active {
|
||||
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);
|
||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -196,14 +207,13 @@ nav ul li a.active {
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
background-color: var(--blender-panel-header, var(--bg-color));
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
@@ -219,15 +229,15 @@ nav ul li a.active {
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--nav-bg-color);
|
||||
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--nav-border-color);
|
||||
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
@@ -250,7 +260,22 @@ footer p {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
button,
|
||||
select {
|
||||
background-color: var(--blender-button-background, var(--bg-color));
|
||||
color: var(--blender-button-text, var(--text-color));
|
||||
border-color: var(--blender-button-border, var(--border-color));
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
/* ------------ Overwriting JSGantt CSS rules ------------ */
|
||||
/* ------------------- for dark theme ------------------- */
|
||||
|
||||
:root.dark div.gantt {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--secondary-text-color);
|
||||
@@ -323,12 +348,6 @@ footer p {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* .gTaskInfo {
|
||||
background-color: #2e2e2e;
|
||||
color: #c7c7c7;
|
||||
border-color: #c7c7c7;;
|
||||
} */
|
||||
|
||||
/* Dark mode scrollbar */
|
||||
:root.dark .frame::-webkit-scrollbar-thumb,
|
||||
:root.dark .frame::-webkit-scrollbar-track {
|
||||
@@ -407,4 +426,160 @@ footer p {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--input-border-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
/* ------------------- for blender generated theme ------------------- */
|
||||
|
||||
|
||||
:root.blender div.gantt,
|
||||
:root.blender .gantt table,
|
||||
:root.blender .gantt td {
|
||||
color: var(--blender-text);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gchartgrid,
|
||||
:root.blender .gmain.gmainright,
|
||||
:root.blender .gtaskbarcontainer.gplan,
|
||||
:root.blender .gchartlbl.gcontainercol,
|
||||
:root.blender .gminorheading,
|
||||
:root.blender .gtaskcell {
|
||||
background-color: var(--blender-odd-row);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
|
||||
/* Headings and cell defaults */
|
||||
:root.blender .gtaskheading,
|
||||
:root.blender .gchartlbl.gcontainercol .gmajorheading,
|
||||
:root.blender .gchartlbl.gcontainercol .gminorheading,
|
||||
:root.blender .gchartlbl.gcontainercol .gminorheadingwkend,
|
||||
:root.blender .gchartlbl.gcontainercol .gtaskcell,
|
||||
:root.blender .gchartlbl.gcontainercol .gtaskcellcurrent,
|
||||
:root.blender .gchartlbl.gcontainercol .gtaskcellwkend,
|
||||
:root.blender .gchartlbl.gcontainercol .gname,
|
||||
:root.blender .gchartlbl.gcontainercol .ggroupitem,
|
||||
:root.blender .glistlbl.gcontainercol .gtaskname,
|
||||
:root.blender .glistlbl.gcontainercol .gtasklist {
|
||||
background-color: var(--blender-panel-header);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gtaskcellcurrent {
|
||||
background-color: var(--blender-odd-row);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gtaskheading,
|
||||
:root.blender .gname,
|
||||
:root.blender .gtaskname,
|
||||
:root.blender .gres,
|
||||
:root.blender .gdur,
|
||||
:root.blender .gcomp,
|
||||
:root.blender .gstartdate,
|
||||
:root.blender .gplanstartdate,
|
||||
:root.blender .gplanenddate,
|
||||
:root.blender .gcost,
|
||||
:root.blender .gchartlbl,
|
||||
:root.blender .gcontainercol,
|
||||
:root.blender .genddate {
|
||||
color: var(--blender-text);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gtaskname>div,
|
||||
:root.blender .gtaskname div,
|
||||
:root.blender .gtaskheading div,
|
||||
:root.blender .gtaskname span {
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
:root.blender .gantt-chart tr:not(.gname.glineitem):not(.gname.ggroupitem) {
|
||||
background-color: var(--blender-odd-row);
|
||||
}
|
||||
|
||||
:root.blender .gtasklist,
|
||||
:root.blender .gadditional {
|
||||
border: var(--blender-tab-outline) 1px solid;
|
||||
}
|
||||
|
||||
:root.blender .glistgrid,
|
||||
:root.blender .glistlbl {
|
||||
/* This is a border that needs background color, somehow. */
|
||||
background-color: var(--blender-tab-outline);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
|
||||
/* Highlight row */
|
||||
:root.blender .gitemhighlight td,
|
||||
:root.blender .gitemhighlight .gantt-inputtable,
|
||||
:root.blender .gitemhighlight .gcaption,
|
||||
:root.blender .gitemhighlight .ggroupcaption {
|
||||
background-color: var(--blender-active-highlight) !important;
|
||||
color: var(--blender-active-text-highlight) !important;
|
||||
}
|
||||
|
||||
/* Differentiate Group, Milestone and Ordinary task items (applied to row) */
|
||||
:root.blender .ggroupitem {
|
||||
font-weight: bold;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
:root.blender .gmileitem,
|
||||
:root.blender .glineitem {
|
||||
background-color: var(--blender-odd-row);
|
||||
}
|
||||
|
||||
/* Task bar caption text styles */
|
||||
:root.blender .gmilecaption,
|
||||
:root.blender .ggroupcaption,
|
||||
:root.blender .gcaption {
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
/* Milestones */
|
||||
:root.blender .gmdtop,
|
||||
:root.blender .gmdbottom {
|
||||
border-bottom: 5px solid var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gfoldercollapse {
|
||||
color: var(--blender-text);
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: var(--folder-collapse-font-size);
|
||||
font-family: var(--folder-collapse-font-family);
|
||||
}
|
||||
|
||||
/* Highlight for collapsible row */
|
||||
:root.blender .gname.ggroupitem {
|
||||
background-color: var(--blender-panel-background);
|
||||
}
|
||||
|
||||
/* Form label and selected highlighting */
|
||||
:root.blender .gformlabel {
|
||||
background-color: var(--blender-panel-header);
|
||||
color: var(--blender-text);
|
||||
border: var(--blender-tab-outline) 1px solid;
|
||||
}
|
||||
|
||||
:root.blender span.gformlabel:hover,
|
||||
:root.blender span.gselected {
|
||||
background-color: var(--blender-active-highlight);
|
||||
color: var(--blender-active-text-highlight);
|
||||
border-color: var(--blender-tab-outline);
|
||||
}
|
||||
|
||||
:root.blender .gantt-inputtable {
|
||||
background-color: var(--blender-panel-header);
|
||||
box-sizing: border-box;
|
||||
border: 2px solid var(--blender-button-border);
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
/* Dark mode scrollbar */
|
||||
:root.blender .frame::-webkit-scrollbar-thumb,
|
||||
:root.blender .frame::-webkit-scrollbar-track {
|
||||
background-color: var(--blender-window-background);
|
||||
}
|
||||
@@ -9,8 +9,9 @@
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
--logo-height: 1.5rem;
|
||||
--nav-height: 0.625rem;
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
@@ -22,7 +23,7 @@
|
||||
--nav-link-color: #fff;
|
||||
--nav-link-hover-color: #3fb449;
|
||||
--warning-color: #FFDB8F;
|
||||
--table-border-color: #464444;
|
||||
--border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@@ -34,7 +35,11 @@
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--table-border-color: #222;
|
||||
--border-color: #222;
|
||||
}
|
||||
|
||||
*:not(.tabulator-cell) {
|
||||
border-radius: var(--border-radius) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -42,8 +47,8 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
background-color: var(--blender-window-background, var(--bg-color));
|
||||
color: var(--blender-text, var(--text-color));
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
display: flex;
|
||||
@@ -64,13 +69,13 @@ h3 {
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
background-color: var(--blender-top-bar-header, 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);
|
||||
border-bottom: 2px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
@@ -81,32 +86,28 @@ nav .logo {
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 0.0625rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 3.125rem;
|
||||
margin-right: var(--margin-large);
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--nav-link-color);
|
||||
font-size: var(--font-size-large);
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--nav-link-hover-color);
|
||||
color: var(--blender-selected-object, var(--nav-link-hover-color));
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
.warning,
|
||||
.warning-icon {
|
||||
color: var(--blender-info-warning, var(--warning-color));
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
@@ -127,8 +128,8 @@ nav ul li a.active {
|
||||
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);
|
||||
border: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
@@ -146,7 +147,7 @@ nav ul li a.active {
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -155,7 +156,7 @@ nav ul li a.active {
|
||||
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);
|
||||
background-color: var(--blender-panel-background, var(--nav-bg-color));
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -166,15 +167,15 @@ nav ul li a.active {
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
border-bottom: 1px solid var(--blender-tab-outline, var(--border-color));
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
background-color: var(--blender-panel-header, var(--bg-color));
|
||||
font-size: var(--base-font-size);
|
||||
color: var(--blender-text, var(--text-color))
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
@@ -189,15 +190,15 @@ nav ul li a.active {
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--nav-bg-color);
|
||||
background-color: var(--blender-top-bar-header, var(--nav-bg-color));
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--nav-border-color);
|
||||
border-top: 1px solid var(--blender-tab-outline, var(--nav-border-color));
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
color: var(--blender-text, var(--nav-link-color));
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
@@ -210,6 +211,18 @@ footer p {
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
background-color: var(--blender-button-background, var(--bg-color));
|
||||
color: var(--blender-button-text, var(--text-color));
|
||||
border-color: var(--blender-button-border, var(--border-color));
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||
|
||||
:root.light .tabulator-header,
|
||||
@@ -219,6 +232,69 @@ footer p {
|
||||
}
|
||||
|
||||
:root.light .tabulator {
|
||||
border-top: 1px solid var(--table-border-color);
|
||||
border-bottom: 2px solid var(--table-border-color);
|
||||
border-top: 1px solid var(--border-color);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
:root.blender .tabulator-table-holder,
|
||||
:root.blender .tabulator {
|
||||
background-color: var(--blender-panel-background) !important;
|
||||
}
|
||||
|
||||
:root.blender .tabulator-header {
|
||||
border-bottom-color: var(--blender-tab-outline);
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
:root.blender .tabulator-header,
|
||||
:root.blender .tabulator .tabulator-header .tabulator-col {
|
||||
background-color: var(--blender-panel-header) !important;
|
||||
}
|
||||
|
||||
:root.blender .tabulator {
|
||||
border-top-color: var(--blender-tab-outline);
|
||||
border-bottom-color: var(--blender-tab-outline);
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
:root.blender .tabulator-row-odd .tabulator-cell {
|
||||
background-color: var(--blender-odd-row);
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
:root.blender .tabulator-row-even .tabulator-cell {
|
||||
background-color: var(--blender-even-row);
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
:root.blender .tabulator-row-odd:hover .tabulator-cell,
|
||||
:root.blender .tabulator-row-even:hover .tabulator-cell {
|
||||
filter: brightness(1.4);
|
||||
}
|
||||
|
||||
:root.blender .tabulator-row,
|
||||
:root.blender .tabulator-cell {
|
||||
color: var(--blender-text);
|
||||
}
|
||||
|
||||
|
||||
|
||||
:root.blender .tabulator-selected .tabulator-cell {
|
||||
background-color: var(--blender-active-highlight) !important;
|
||||
color: var(--blender-active-text-highlight) !important;
|
||||
}
|
||||
|
||||
|
||||
:root.blender .tabulator-selected:hover .tabulator-cell {
|
||||
filter: brightness(1.2)
|
||||
}
|
||||
|
||||
.tabulator-menu-item {
|
||||
color: var(--blender-text) !important;
|
||||
background-color: var(--blender-panel-background) !important;
|
||||
transition: filter 0.1s ease;
|
||||
}
|
||||
|
||||
.tabulator-menu-item:hover {
|
||||
filter: brightness(1.4);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// A global variable to hold the WebSocket connection
|
||||
let socket;
|
||||
|
||||
// Document ready function
|
||||
// This function is triggered once the DOM is fully loaded.
|
||||
// It sets the theme for the UI and
|
||||
// establishes the WebSocket connection by calling connectSocket().
|
||||
$(document).ready(function () {
|
||||
var defaultTheme = "blender"; // Default theme to be applied
|
||||
var theme = localStorage.getItem("theme") || defaultTheme; // Retrieve the stored theme or use the default
|
||||
setTheme(theme); // Apply the theme
|
||||
|
||||
connectSocket();
|
||||
});
|
||||
|
||||
// Function to establish WebSocket connection
|
||||
// This function constructs the WebSocket connection URL using the SOCKET_PORT variable
|
||||
// and initializes the socket connection. It also registers event handlers to listen
|
||||
// for various events emitted by the server, such as 'blender_connect', 'blender_disconnect',
|
||||
// 'theme_data', and 'demo_data'.
|
||||
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("theme_data", handleThemeData);
|
||||
socket.on("demo_data", handleDemoData);
|
||||
}
|
||||
|
||||
// Function to handle 'blender_connect' event
|
||||
// This function is triggered when a new Bonsai instance connects to the server.
|
||||
// The 'blenderId' is a unique identifier assigned by the server to each Bonsai instance.
|
||||
// It helps distinguish between different instances of Bonsai connected to the server.
|
||||
// The 'blenderId' is used in various parts of the code to ensure that messages and data
|
||||
// are correctly associated with the appropriate Bonsai instance.
|
||||
function handleBlenderConnect(blenderId) {
|
||||
console.log("blender connected: ", blenderId);
|
||||
}
|
||||
|
||||
// Function to handle 'blender_disconnect' event
|
||||
// This function is triggered when a Bonsai instance disconnects from the server.
|
||||
// The 'blenderId' is used here to log which specific Bonsai instance has disconnected.
|
||||
function handleBlenderDisconnect(blenderId) {
|
||||
console.log("blender disconnected: ", blenderId);
|
||||
}
|
||||
|
||||
// Function to handle 'demo_data' event
|
||||
// This function processes the demo data received from the server.
|
||||
// It extracts the message and the Bonsai instance ID ('blenderId'),
|
||||
// and updates the DOM to display the message. The 'blenderId' is used
|
||||
// to differentiate between messages from different Bonsai instances.
|
||||
// If the message for the specific Bonsai instance is already displayed, it updates the content;
|
||||
// otherwise, it creates a new element to display the message.
|
||||
function handleDemoData(demoData) {
|
||||
console.log(demoData);
|
||||
const message = demoData["data"]["demo_message"];
|
||||
const blenderId = demoData["blenderId"];
|
||||
const id = "message-" + blenderId;
|
||||
const messageHeader = $("#" + id);
|
||||
const messageText = `Bonsai instance with ID: ${blenderId} sent the message: ${message}`;
|
||||
|
||||
if (messageHeader[0] === undefined) {
|
||||
const newMessageElement = $("<h3>", { id: id }).text(messageText);
|
||||
$("#message-container").prepend(newMessageElement);
|
||||
} else {
|
||||
messageHeader.text(messageText);
|
||||
messageHeader.prependTo("#message-container");
|
||||
}
|
||||
}
|
||||
|
||||
// Function to send a message to the server
|
||||
// This function is triggered when the user submits a message.
|
||||
// It collects the message from the input field, constructs a message object,
|
||||
// and emits the 'web_operator' event to the server with the message data.
|
||||
// If 'blenderId' is specified, the message is sent to a specific Bonsai instance.
|
||||
function sendMessage() {
|
||||
const inputMessage = $("#input-message").val();
|
||||
|
||||
const msg = {
|
||||
sourcePage: "demo",
|
||||
// blenderId: BlenderId, // specify the target Bonsai instance
|
||||
operator: {
|
||||
type: "message",
|
||||
message: inputMessage,
|
||||
},
|
||||
};
|
||||
socket.emit("web_operator", msg);
|
||||
}
|
||||
|
||||
function handleThemeData(themeData) {
|
||||
// console.log(themeData);
|
||||
|
||||
function arrayToRgbString(arr) {
|
||||
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
|
||||
if (a !== undefined) {
|
||||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||
}
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
|
||||
function generateCssVariableRule(theme) {
|
||||
let cssVariables = ":root.blender {\n";
|
||||
for (const key in theme) {
|
||||
const cssVariableName = `--blender-${key.replace(/_/g, "-")}`;
|
||||
const cssVariableValue = arrayToRgbString(theme[key]);
|
||||
cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`;
|
||||
}
|
||||
cssVariables += "}";
|
||||
return cssVariables;
|
||||
}
|
||||
|
||||
const cssRule = generateCssVariableRule(themeData.theme);
|
||||
console.log(cssRule);
|
||||
|
||||
var styleElement = $("#demo-stylesheet")[0];
|
||||
if (styleElement) {
|
||||
var sheet = styleElement.sheet || styleElement.styleSheet;
|
||||
sheet.insertRule(cssRule, sheet.cssRules.length);
|
||||
}
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
$("html").removeClass("light dark blender").addClass(theme);
|
||||
$(":root").css("color-scheme", theme);
|
||||
if (theme === "light") {
|
||||
$("#toggle-theme").html('<i class="fas fa-sun"></i>');
|
||||
} else if (theme === "dark") {
|
||||
$("#toggle-theme").html('<i class="fas fa-moon"></i>');
|
||||
} else if (theme === "blender") {
|
||||
$("#toggle-theme").html('<i class="fas fa-adjust"></i>');
|
||||
}
|
||||
localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
if ($("html").hasClass("light")) {
|
||||
setTheme("dark");
|
||||
} else if ($("html").hasClass("dark")) {
|
||||
setTheme("blender");
|
||||
} else {
|
||||
setTheme("light");
|
||||
}
|
||||
}
|
||||