Merge remote-tracking branch 'origin/v0.8.0' into tfk-unify-variant-storage

This commit is contained in:
Thomas Krijnen
2024-08-23 16:10:14 +02:00
1049 changed files with 63763 additions and 9958 deletions
-85
View File
@@ -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: [py39, 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
+149
View File
@@ -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
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310, py311, py312]
pyver: [py310, py311, py312]
config:
- {
name: "Windows Build",
@@ -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
+3 -1
View File
@@ -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
+4 -4
View File
@@ -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 \
+9 -12
View File
@@ -73,19 +73,16 @@ src/ifcopenshell-python/test/build
# mypy cache
.mypy_cache
# blenderbim libs
src/blenderbim/blenderbim/libs
# bonsai i18n
src/bonsai/bonsai/translations.py
# blenderbim i18n
src/blenderbim/blenderbim/translations.py
# bonsai test temp files
src/bonsai/test/files/temp
src/bonsai/test/files/basic.ifc.cache.blend
src/bonsai/test/files/basic.ifc.cache.sqlite
# blenderbim test temp files
src/blenderbim/test/files/temp
src/blenderbim/test/files/basic.ifc.cache.blend
src/blenderbim/test/files/basic.ifc.cache.sqlite
src/blenderbim/drawings
src/blenderbim/layouts
src/bonsai/drawings
src/bonsai/layouts
# ifcopenshell swig and compiled files
src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper.so
@@ -100,4 +97,4 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
.cache
# Brickschema
src/blenderbim/blenderbim/bim/schema/Brick.ttl
src/bonsai/bonsai/bim/schema/Brick.ttl
+2 -2
View File
@@ -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 \
+5 -5
View File
@@ -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 | [![PyPI](https://img.shields.io/pypi/v/bcf-client?label=PyPI&color=006dad)](https://pypi.org/project/bcf-client/) |
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later | [![Official](https://img.shields.io/badge/BlenderBIM.org-Download-70ba35)](https://blenderbim.org/download.html) [![GitHub Unstable](https://img.shields.io/github/v/release/ifcopenshell/ifcopenshell?filter=blenderbim-*&label=GitHub-Unstable&color=f6f8fa)](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=blenderbim&expanded=true) [![Chocolatey](https://img.shields.io/chocolatey/v/blenderbim-nightly?label=Chocolatey&color=5c9fd8)](https://community.chocolatey.org/packages/blenderbim-nightly/) |
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later | [![Official](https://img.shields.io/badge/BonsaiBIM.org-Download-70ba35)](https://bonsaibim.org/download.html) [![GitHub Unstable](https://img.shields.io/github/v/release/ifcopenshell/ifcopenshell?filter=blenderbim-*&label=GitHub-Unstable&color=f6f8fa)](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=blenderbim&expanded=true) [![Chocolatey](https://img.shields.io/chocolatey/v/blenderbim-nightly?label=Chocolatey&color=5c9fd8)](https://community.chocolatey.org/packages/blenderbim-nightly/) |
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later | [![PyPI](https://img.shields.io/pypi/v/bsdd?label=PyPI&color=006dad)](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 | [![PyPI](https://img.shields.io/pypi/v/ifc4d?label=PyPI&color=006dad)](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
+1 -1
View File
@@ -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)
+8 -8
View File
@@ -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>
+2 -2
View File
@@ -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:
+4
View File
@@ -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.
+28
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
import bcf.v2.model
import bcf.v3.model
from typing import Union
BimSnippet = Union[bcf.v2.model.BimSnippet, bcf.v3.model.BimSnippet]
BitMap = Union[bcf.v2.model.VisualizationInfoBitmap, bcf.v3.model.Bitmap]
DocumentReference = Union[bcf.v2.model.TopicDocumentReference, bcf.v3.model.DocumentReference]
HeaderFile = Union[bcf.v2.model.HeaderFile, bcf.v3.model.File]
Topic = Union[bcf.v2.model.Topic, bcf.v3.model.Topic]
ViewPoint = Union[bcf.v2.model.ViewPoint, bcf.v3.model.ViewPoint]
+101
View File
@@ -0,0 +1,101 @@
import tempfile
import bcf.v2.bcfxml
import bcf.v2.model
import bcf.v2.topic
import bcf.v3.bcfxml
import bcf.v3.model
import bcf.v3.topic
import bcf.agnostic.model as mdl
from pathlib import Path
from typing import Union, Optional
from typing_extensions import assert_never
TopicHandler = Union[bcf.v2.topic.TopicHandler, bcf.v3.topic.TopicHandler]
def extract_file(
topic: TopicHandler,
entity: Union[mdl.HeaderFile, mdl.BimSnippet, mdl.DocumentReference, mdl.BitMap],
bcfxml: Optional[Union[bcf.v2.bcfxml.BcfXml, bcf.v3.bcfxml.BcfXml]] = None,
outfile: Optional[Path] = None,
) -> Union[Path, str, None]:
"""Extracts an element with a file into a temporary directory
These include header files, bim snippets, document references, and
viewpoint bitmaps. External reference are not downloaded. Instead, the
URI reference is returned.
:param entity: The entity with a file reference to extract
:param outfile: If provided, save the header file to that location.
Otherwise, a temporary directory is created and the filename is
derived from the header's original filename.
:param bcfxml: The BCF XML file to use for resolving document references files.
Required only for BCF v3 document references (in BCF v3 internal documents
are stored at BCF root, not in the topic).
:return: The filepath of the extracted file. It may be a URL if the
header file is external.
"""
if isinstance(entity, mdl.DocumentReference):
if isinstance(entity, bcf.v2.model.TopicDocumentReference):
reference = entity.referenced_document
else:
reference = entity.document_guid
else:
reference = entity.reference
if not reference:
return None
# For v3 document references external documents are detected by empty document_guid.
# External bitmaps are not supported by bcf.
if not isinstance(entity, (bcf.v3.model.DocumentReference, mdl.BitMap)) and entity.is_external:
return reference
if isinstance(entity, bcf.v3.model.DocumentReference):
# Extract document reference filename and contents.
if not bcfxml:
raise TypeError("bcfxml is required for BCF v3 document references.")
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
error_msg = f"BCF XML is missing document with guid '{reference}'."
if not bcfxml.documents:
raise Exception(error_msg)
definition_docs = bcfxml.documents.definition.documents
if not definition_docs:
raise Exception(error_msg)
docs = next((doc for doc in definition_docs.document if doc.guid == reference), None)
if not docs:
raise Exception(error_msg)
filename = docs.filename
bytes_data = bcfxml.documents.documents[filename]
else:
if isinstance(entity, mdl.BimSnippet):
bytes_data = topic.bim_snippet
assert isinstance(bytes_data, bytes)
elif isinstance(entity, mdl.HeaderFile):
bytes_data = topic.reference_files[reference]
elif isinstance(entity, mdl.BitMap):
bytes_data = next(
byte_data
for vp in topic.viewpoints.values()
for data_reference, byte_data in vp.bitmaps.items()
if data_reference == reference
)
elif isinstance(entity, bcf.v2.model.TopicDocumentReference):
assert isinstance(topic, bcf.v2.topic.TopicHandler)
bytes_data = topic.document_references[reference]
else:
assert_never(entity)
# We don't really need it if 'outfile' is None, just keeping type checker happy.
if isinstance(entity, mdl.HeaderFile) and entity.filename:
filename = entity.filename
else:
filename = Path(reference).name
if not outfile:
outfile = Path(tempfile.mkdtemp()) / filename
with open(outfile, "wb") as f:
f.write(bytes_data)
return outfile
+5
View File
@@ -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]
+4 -3
View File
@@ -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.
+50 -8
View 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
+197
View File
@@ -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": "",
},
)
+46 -64
View File
@@ -5,7 +5,7 @@ import tempfile
import uuid
import zipfile
from pathlib import Path
from typing import Any, NoReturn, Optional
from typing import Any, NoReturn, Optional, Union
import numpy as np
from ifcopenshell import entity_instance
@@ -27,9 +27,9 @@ class TopicHandler:
xml_handler: Optional[AbstractXmlParserSerializer] = None,
) -> None:
self._markup: Optional[mdl.Markup] = None
self._viewpoints: dict[str, VisualizationInfoHandler] = {}
self._reference_files: dict[str, bytes] = {}
self._document_references: dict[str, bytes] = {}
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
self._reference_files: Optional[dict[str, bytes]] = None
self._document_references: Optional[dict[str, bytes]] = None
self._bim_snippet: Optional[bytes] = None
self._xml_handler = xml_handler or XmlParserSerializer()
self._topic_dir = topic_dir
@@ -63,11 +63,21 @@ class TopicHandler:
"""Return the header of the topic."""
return self.markup.header if self.markup else None
@header.setter
def header(self, header: mdl.Header) -> None:
"""Set the header of the topic."""
self.markup.header = header
@property
def comments(self) -> list[mdl.Comment]:
"""Return the comments of the topic."""
return self.markup.comment if self.markup else []
@comments.setter
def comments(self, comments: list[mdl.Comment]) -> None:
assert self.markup
self.markup.comment = comments
@property
def bim_snippet(self) -> Optional[bytes]:
if not self._bim_snippet and self._topic_dir:
@@ -80,14 +90,24 @@ class TopicHandler:
@property
def viewpoints(self) -> dict[str, VisualizationInfoHandler]:
if not self._viewpoints and self._topic_dir:
if self._viewpoints is None:
self._viewpoints = self._load_viewpoints()
return self._viewpoints
def _load_viewpoints(self) -> dict[str, VisualizationInfoHandler]:
if self._topic_dir and self.markup and (viewpoints := self.markup.viewpoints):
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
return {}
@property
def reference_files(self) -> dict[str, bytes]:
if self._reference_files or not self.header:
if self._reference_files is not None:
return self._reference_files
self._reference_files = {}
if not self.header:
return self._reference_files
for ref in self.header.file:
if ref.is_external:
continue
@@ -99,8 +119,13 @@ class TopicHandler:
@property
def document_references(self) -> dict[str, bytes]:
if self._document_references or not self.topic:
if self._document_references is not None:
return self._document_references
self._document_references = {}
if not self.topic:
return self._document_references
for doc in self.topic.document_reference:
if doc.is_external or not doc.referenced_document:
continue
@@ -118,11 +143,6 @@ class TopicHandler:
return bim_snippet_path.read_bytes()
return None
def _load_viewpoints(self) -> dict[str, VisualizationInfoHandler]:
if self.markup and (viewpoints := self.markup.viewpoints):
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
return {}
@classmethod
def create_new(
cls,
@@ -220,55 +240,7 @@ class TopicHandler:
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
destination_zip.writestr(real_path.at, self.document_references[doc.referenced_document])
def extract_file(self, entity, outfile: Optional[Path] = None) -> Path:
"""Extracts an element with a file into a temporary directory
These include header files, bim snippets, document references, and
viewpoint bitmaps. External reference are not downloaded. Instead, the
URI reference is returned.
:param entity: The entity with a file reference to extract
:type entity: bcf.v2.model.HeaderFile,bcf.v2.model.BimSnippet,bcf.v2.model.TopicDocumentReference
:param outfile: If provided, save the header file to that location.
Otherwise, a temporary directory is created and the filename is
derived from the header's original filename.
:type outfile: pathlib.Path,optional
:return: The filepath of the extracted file. It may be a URL if the
header file is external.
:rtype: Path
"""
if hasattr(entity, "reference"):
reference = entity.reference
else:
reference = entity.referenced_document
if not reference:
return
if getattr(entity, "is_external", False):
return entity.reference
resolved_reference = self._topic_dir
for part in Path(reference).parts:
if part == "..":
resolved_reference = resolved_reference.parent
else:
resolved_reference = resolved_reference.joinpath(part)
if not outfile:
if getattr(entity, "filename", None):
filename = entity.filename
else:
filename = resolved_reference.name
outfile = Path(tempfile.mkdtemp()) / filename
with open(outfile, "wb") as f:
f.write(resolved_reference.read_bytes())
return outfile
def add_viewpoint(self, element: entity_instance) -> None:
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
"""Add a viewpoint pointed at the placement of an IFC element to the topic.
Args:
@@ -278,7 +250,9 @@ class TopicHandler:
self.add_visinfo_handler(new_viewpoint)
return new_viewpoint
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
def add_viewpoint_from_point_and_guids(
self, position: NDArray[np.float64], *guids: str
) -> VisualizationInfoHandler:
"""Add a viewpoint pointing at an XYZ point in space
Args:
@@ -291,9 +265,17 @@ class TopicHandler:
self.add_visinfo_handler(vi_handler)
return vi_handler
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
def add_visinfo_handler(
self, new_viewpoint: VisualizationInfoHandler, snapshot_filename: Optional[str] = None
) -> mdl.ViewPoint:
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
self.markup.viewpoints.append(mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid))
viewpoint = mdl.ViewPoint(
viewpoint=new_viewpoint.guid + ".bcfv",
snapshot=snapshot_filename,
guid=new_viewpoint.guid,
)
self.markup.viewpoints.append(viewpoint)
return viewpoint
def __eq__(self, other: object) -> bool | NoReturn:
return (
+111 -6
View File
@@ -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.
+9 -5
View File
@@ -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]:
+75 -15
View File
@@ -26,7 +26,8 @@ class TopicHandler:
xml_handler: Optional[AbstractXmlParserSerializer] = None,
) -> None:
self._markup: Optional[mdl.Markup] = None
self._viewpoints: dict[str, VisualizationInfoHandler] = {}
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
self._reference_files: Optional[dict[str, bytes]] = None
self._bim_snippet: Optional[bytes] = None
self._xml_handler = xml_handler or XmlParserSerializer()
self._topic_dir = topic_dir
@@ -49,22 +50,36 @@ class TopicHandler:
return self.markup.topic
@property
def guid(self) -> Optional[str]:
def guid(self) -> str:
"""Return the GUID of the topic."""
if self._markup:
return self.topic.guid
return self._topic_dir.name if self._topic_dir else None
return self._topic_dir.name if self._topic_dir else ""
@property
def header(self) -> Optional[mdl.Header]:
"""Return the header of the topic."""
return self.markup.header
@header.setter
def header(self, header: mdl.Header) -> None:
"""Set the header of the topic."""
self.markup.header = header
@property
def comments(self) -> list[mdl.Comment]:
"""Return the comments of the topic."""
return self.topic.comments.comment if self.topic.comments else []
@comments.setter
def comments(self, comments: list[mdl.Comment]) -> None:
topic_comments = self.topic.comments
if topic_comments is None:
if not comments:
return
self.topic.comments = (topic_comments := mdl.TopicComments())
topic_comments.comment = comments
@property
def bim_snippet(self) -> Optional[bytes]:
if not self._bim_snippet and self._topic_dir:
@@ -77,15 +92,15 @@ class TopicHandler:
@property
def viewpoints(self) -> dict[str, "VisualizationInfoHandler"]:
if (
not self._viewpoints
and self._topic_dir
and self.topic.viewpoints
and (viewpoints := self.topic.viewpoints.view_point)
):
self._viewpoints = VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
if self._viewpoints is None:
self._viewpoints = self._load_viewpoints()
return self._viewpoints
def _load_viewpoints(self) -> dict[str, "VisualizationInfoHandler"]:
if self._topic_dir and self.topic.viewpoints and (viewpoints := self.topic.viewpoints.view_point):
return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints)
return {}
def _load_bim_snippet(self) -> Optional[bytes]:
bim_snippet_obj = self.topic.bim_snippet
if bim_snippet_obj and not bim_snippet_obj.is_external and self._topic_dir:
@@ -94,6 +109,27 @@ class TopicHandler:
return bim_snippet_path.read_bytes()
return None
@property
def reference_files(self) -> dict[str, bytes]:
if self._reference_files is not None:
return self._reference_files
self._reference_files = {}
if not self.header:
return self._reference_files
if not self.header.files:
return self._reference_files
for ref in self.header.files.file:
if ref.is_external:
continue
real_path = self._topic_dir
for path_part in ref.reference.split("/"):
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
self._reference_files[ref.reference] = real_path.read_bytes()
return self._reference_files
@classmethod
def create_new(
cls,
@@ -145,6 +181,7 @@ class TopicHandler:
self._save_xml(destination_zip, self._markup, "markup.bcf")
self._save_viewpoints(destination_zip, topic_dir)
self._save_bim_snippet(destination_zip)
self._save_reference_files(destination_zip)
def _save_viewpoints(self, destination_zip: ZipFileInterface, topic_dir: str) -> None:
if not self.topic.viewpoints or not (viewpoints := self.topic.viewpoints.view_point):
@@ -165,7 +202,20 @@ class TopicHandler:
if self.bim_snippet:
destination_zip.writestr(f"{self.topic.guid}/{ref_filename}", self.bim_snippet)
def add_viewpoint(self, element: entity_instance) -> None:
def _save_reference_files(self, destination_zip: ZipFileInterface) -> None:
if not self.header:
return
if not self.header.files:
return
for ref in self.header.files.file:
if ref.is_external or not ref.reference:
continue
real_path = self._topic_dir
for path_part in ref.reference.split("/"):
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
destination_zip.writestr(real_path.at, self.reference_files[ref.reference])
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
"""
Add a viewpoint tergeting an IFC element to the topic.
@@ -174,8 +224,11 @@ class TopicHandler:
"""
new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler)
self.add_visinfo_handler(new_viewpoint)
return new_viewpoint
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
def add_viewpoint_from_point_and_guids(
self, position: NDArray[np.float64], *guids: str
) -> VisualizationInfoHandler:
"""
Add a viewpoint tergeting an IFC element to the topic.
@@ -186,14 +239,21 @@ class TopicHandler:
position, *guids, xml_handler=self._xml_handler
)
self.add_visinfo_handler(vi_handler)
return vi_handler
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
def add_visinfo_handler(
self, new_viewpoint: VisualizationInfoHandler, snapshot_filename: Optional[str] = None
) -> mdl.ViewPoint:
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
if self.topic.viewpoints is None:
self.topic.viewpoints = mdl.TopicViewpoints()
self.topic.viewpoints.view_point.append(
mdl.ViewPoint(viewpoint=new_viewpoint.guid + ".bcfv", guid=new_viewpoint.guid)
viewpoint = mdl.ViewPoint(
viewpoint=new_viewpoint.guid + ".bcfv",
snapshot=snapshot_filename,
guid=new_viewpoint.guid,
)
self.topic.viewpoints.view_point.append(viewpoint)
return viewpoint
def __eq__(self, other: object) -> bool | NoReturn:
return (
+119 -8
View File
@@ -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.
+23
View File
@@ -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"]
+16
View File
@@ -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"]
-5
View File
@@ -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,224 +0,0 @@
:root {
--font-family: Arial, sans-serif;
--base-font-size: 16px;
--margin-tiny: 0.125rem;
--margin-small: 0.625rem;
--margin-medium: 1.25rem;
--margin-large: 2.5rem;
--padding-tiny: 0.125rem;
--padding-small: 0.625rem;
--padding-medium: 1rem;
--font-size-large: 1.2rem;
--logo-height: 2.5rem;
--nav-height: 2.5rem;
}
:root.dark {
color-scheme: dark;
--bg-color: #252525;
--text-color: #e0e0e0;
--nav-bg-color: #121212;
--nav-border-color: #25682a;
--nav-link-color: #fff;
--nav-link-hover-color: #3fb449;
--warning-color: #FFDB8F;
--table-border-color: #464444;
}
:root.light {
color-scheme: light;
--bg-color: #ffffff;
--text-color: #000000;
--nav-bg-color: #f8f8f8;
--nav-border-color: #cccccc;
--nav-link-color: #000000;
--nav-link-hover-color: #38a63d;
--warning-color: #FF4500;
--table-border-color: #222;
}
html {
font-size: var(--base-font-size);
}
body {
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
font-family: var(--font-family);
display: flex;
flex-direction: column;
min-height: 100vh;
}
#container {
flex: 1;
margin-top: var(--margin-medium);
margin-left: var(--margin-small);
margin-right: var(--margin-small);
margin-bottom: var(--margin-medium);
}
h3 {
margin-top: 0;
}
nav {
background-color: var(--nav-bg-color);
height: var(--nav-height);
padding: var(--padding-medium) 0;
display: flex;
align-items: center;
position: relative;
border-bottom: 2px solid var(--nav-border-color);
}
nav .logo {
margin-left: var(--margin-large);
height: var(--logo-height);
}
nav ul {
list-style-type: none;
margin: 0;
margin-left: 0.0625rem;
display: flex;
flex-grow: 1;
justify-content: center;
}
nav ul li {
margin-right: 3.125rem;
}
nav ul li a {
text-decoration: none;
color: var(--nav-link-color);
font-size: var(--font-size-large);
}
nav ul li a:hover,
nav ul li a.active {
color: var(--nav-link-hover-color);
}
.warning {
color: var(--warning-color);
margin-bottom: var(--margin-small);
padding: var(--padding-tiny);
display: none;
}
#toggle-theme {
border: none;
background: none;
cursor: pointer;
font-size: var(--font-size-large);
margin-right: var(--margin-medium);
}
#toggle-theme:focus {
outline: none;
}
#client-list {
position: absolute;
top: calc(0 + var(--nav-height));
overflow-y: auto;
overflow: hidden;
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
border: 1px solid var(--table-border-color);
background-color: var(--nav-bg-color);
z-index: 1;
opacity: 0;
visibility: hidden;
width: auto;
}
#client-list.show {
opacity: 1;
visibility: visible;
}
#connected-list-div {
display: inline-block;
}
.client {
padding: var(--margin-small);
border-bottom: 1px solid var(--table-border-color);
cursor: pointer;
}
.client-details {
max-height: 0;
overflow: hidden;
padding-left: var(--padding-small);
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
background-color: var(--nav-bg-color);
margin-top: var(--margin-tiny);
opacity: 0;
}
.client-details.show {
max-height: none;
opacity: 1;
}
.client-detail {
border-bottom: 1px solid var(--table-border-color);
padding: var(--padding-small);
}
#show-connected-button {
width: auto;
background-color: var(--bg-color);
border: none;
font-size: var(--base-font-size);
}
#show-connected-button:hover,
.scroll-button:hover {
cursor: pointer;
}
.scroll-button {
font-size: var(--base-font-size);
margin-left: var(--margin-tiny);
margin-top: var(--margin-small);
}
footer {
background-color: var(--nav-bg-color);
text-align: right;
padding: var(--padding-tiny);
border-top: 1px solid var(--nav-border-color);
}
footer p {
margin: 0;
color: var(--text-color);
margin-right: var(--margin-small);
font-size: 0.8rem;
}
.table-container {
margin-bottom: var(--margin-large);
}
.csv-table {
margin-top: var(--margin-small);
}
/* ------------ Overwriting Tabulator CSS rules ------------ */
:root.light .tabulator-header,
:root.light .tabulator .tabulator-header .tabulator-col {
background: var(--nav-bg-color) !important;
color: var(--text-color);
}
:root.light .tabulator {
border-top: 1px solid var(--table-border-color);
border-bottom: 2px solid var(--table-border-color);
}
@@ -1,466 +0,0 @@
// keeps track of blenders connected in form of
// shown:bool, workSchedule: {}, ganttTasks: {},
const connectedClients = {};
let socket;
// print options
const pageSizes = [
{ value: "210,297", text: "A4 Portrait" },
{ value: "297,210", text: "A4 Landscape" },
{ value: "297,420", text: "A3 Portrait" },
{ value: "420,297", text: "A3 Landscape" },
{ value: "420,594", text: "A2 Portrait" },
{ value: "594,420", text: "A2 Landscape" },
{ value: "594,841", text: "A1 Portrait" },
{ value: "841,594", text: "A1 Landscape" },
{ value: "841,1189", text: "A0 Portrait" },
{ value: "1189,841", text: "A0 Landscape" },
];
// Document ready function
$(document).ready(function () {
var systemTheme = window.matchMedia("(prefers-color-scheme: light)").matches
? "light"
: "dark";
var theme = localStorage.getItem("theme") || systemTheme;
setTheme(theme);
connectSocket();
});
// Function to connect to Socket.IO server
function connectSocket() {
const url = "ws://localhost:" + SOCKET_PORT + "/web";
socket = io(url);
console.log("socket: ", socket);
// Register socket event handlers
socket.on("blender_connect", handleBlenderConnect);
socket.on("blender_disconnect", handleBlenderDisconnect);
socket.on("gantt_data", handleGanttData);
socket.on("default_data", handleDefaultData);
}
// Function to handle 'blender_connect' event
function handleBlenderConnect(blenderId) {
console.log("blender_connect: ", blenderId);
if (!connectedClients.hasOwnProperty(blenderId)) {
connectedClients[blenderId] = {
shown: false,
workSchedule: {},
ganttTasks: {},
};
}
}
// Function to handle 'blender_disconnect' event
function handleBlenderDisconnect(blenderId) {
console.log("blender_disconnect: ", blenderId);
if (connectedClients.hasOwnProperty(blenderId)) {
delete connectedClients[blenderId];
removeGanttElement(blenderId);
}
$("#blender-count").text(function (i, text) {
return parseInt(text, 10) - 1;
});
}
// Function to handle 'gantt_data' event
function handleGanttData(data) {
const blenderId = data["blenderId"];
console.log(data);
const filename = data["data"]["ifc_file"];
const ganttTasks = data["data"]["gantt_data"]["tasks"];
const ganttWorkSched = data["data"]["gantt_data"]["work_schedule"];
// const ganttWorkSched = {};
if (connectedClients.hasOwnProperty(blenderId)) {
if (!connectedClients[blenderId].shown) {
connectedClients[blenderId] = {
shown: true,
ifc_file: filename,
ganttTasks: ganttTasks,
workSchedule: ganttWorkSched,
};
addGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
} else {
updateGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
connectedClients[blenderId].workSchedule = ganttWorkSched;
connectedClients[blenderId].ganttTasks = ganttTasks;
}
} else {
connectedClients[blenderId] = {
shown: true,
ifc_file: filename,
ganttTasks: ganttTasks,
workSchedule: ganttWorkSched,
};
addGanttElement(blenderId, ganttTasks, ganttWorkSched, filename);
}
}
function handleDefaultData(data) {
const blenderId = data["blenderId"];
const isDirty = data["data"]["is_dirty"];
showWarning(blenderId, isDirty);
console.log(data);
}
// Function to add a new gantt with data and filename
function addGanttElement(blenderId, tasks, workSched, filename) {
$("#blender-count").text(function (i, text) {
return parseInt(text, 10) + 1;
});
const ganttContainer = $("<div></div>")
.addClass("gantt-container")
.attr("id", "container-" + blenderId);
const ganttTitle = $("<h3></h3>")
.attr("id", "title-" + blenderId)
.text(filename)
.addClass("no-print")
.css("margin-bottom", "10px");
const warning = $("<div></div>")
.attr("id", "warning-" + blenderId)
.html(
"&#9888; 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,293 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import gpu
import bmesh
import blenderbim.tool as tool
from math import sin, cos, radians
from bpy.types import SpaceView3D
from mathutils import Vector, Matrix
from gpu_extras.batch import batch_for_shader
from typing import Union
def transparent_color(color, alpha=0.1):
color = [i for i in color]
color[3] = alpha
return color
class ProfileDecorator:
installed = None
@classmethod
def install(cls, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
"""Note that operators that change mesh in `exit_edit_mode_callback` can freeze blender.
The workaround is to move their code to function and use it for callback.
Example: https://devtalk.blender.org/t/calling-operator-that-saves-bmesh-freezes-blender-forever/28595"""
if cls.installed:
cls.uninstall()
handler = cls()
cls.installed = SpaceView3D.draw_handler_add(
handler, (context, get_custom_bmesh, draw_faces, exit_edit_mode_callback), "WINDOW", "POST_VIEW"
)
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
except ValueError:
pass
cls.installed = None
def draw_batch(self, shader_type, content_pos, color, indices=None):
shader = self.line_shader if shader_type == "LINES" else self.shader
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
shader.uniform_float("color", color)
batch.draw(shader)
def draw_faces(self, bm, vertices_coords):
"""mutates original bm (triangulates it)
so the triangulation edges will be shown too
"""
traingulated_bm = bm
bmesh.ops.triangulate(traingulated_bm, faces=traingulated_bm.faces)
face_indices = [[v.index for v in f.verts] for f in traingulated_bm.faces]
faces_color = transparent_color(self.addon_prefs.decorator_color_special)
self.draw_batch("TRIS", vertices_coords, faces_color, face_indices)
def __call__(self, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
self.addon_prefs = tool.Blender.get_addon_preferences()
selected_elements_color = self.addon_prefs.decorator_color_selected
unselected_elements_color = self.addon_prefs.decorator_color_unselected
special_elements_color = self.addon_prefs.decorator_color_special
error_elements_color = self.addon_prefs.decorator_color_error
background_elements_color = self.addon_prefs.decorator_color_background
obj = context.active_object
if obj.mode != "EDIT":
if exit_edit_mode_callback:
ProfileDecorator.uninstall()
exit_edit_mode_callback()
return
if get_custom_bmesh:
bm = get_custom_bmesh()
else:
bm = bmesh.from_edit_mesh(obj.data)
gpu.state.point_size_set(6)
gpu.state.blend_set("ALPHA")
### Actually drawing
all_vertices = []
error_vertices = []
selected_vertices = []
unselected_vertices = []
# special = associated with arcs/circles
special_vertices = []
special_vertex_indices = {}
selected_edges = []
unselected_edges = []
arc_edges = []
roof_angle_edges = []
preview_edges = []
arc_groups = []
circle_groups = []
for i, group in enumerate(obj.vertex_groups):
if "IFCARCINDEX" in group.name:
arc_groups.append(i)
elif "IFCCIRCLE" in group.name:
circle_groups.append(i)
arcs = {}
circles = {}
# https://docs.blender.org/api/blender_python_api_2_63_8/bmesh.html#CustomDataAccess
# This is how we access vertex groups via bmesh, apparently, it's not very intuitive
deform_layer = bm.verts.layers.deform.active
angle_layer = bm.edges.layers.float.get("BBIM_gable_roof_angles")
preview_layer = bm.edges.layers.int.get("BBIM_preview")
for vertex in bm.verts:
co = tuple(obj.matrix_world @ vertex.co)
all_vertices.append(co)
if vertex.hide:
continue
is_arc, is_circle = False, False
# deform_layer is None if there are no verts assigned to vertex groups
# even if there are vertex groups in the obj.vertex_groups
if deform_layer:
is_arc, group_index = tool.Blender.bmesh_check_vertex_in_groups(vertex, deform_layer, arc_groups)
if is_arc:
arcs.setdefault(group_index, []).append(vertex)
special_vertex_indices[vertex.index] = group_index
is_circle, group_index = tool.Blender.bmesh_check_vertex_in_groups(vertex, deform_layer, circle_groups)
if is_circle:
circles.setdefault(group_index, []).append(vertex)
special_vertex_indices[vertex.index] = group_index
if vertex.select:
selected_vertices.append(co)
else:
if len(vertex.link_edges) > 1 and is_circle:
error_vertices.append(co)
elif is_circle:
special_vertices.append(co)
elif len(vertex.link_edges) != 2:
error_vertices.append(co)
elif is_arc:
special_vertices.append(co)
else:
unselected_vertices.append(co)
for edge in bm.edges:
edge_indices = [v.index for v in edge.verts]
if edge.hide:
continue
if edge.select:
selected_edges.append(edge_indices)
else:
i1, i2 = edge.verts[0].index, edge.verts[1].index
# making sure that both vertices are in the same group
if i1 in special_vertex_indices and special_vertex_indices[i1] == special_vertex_indices.get(i2, None):
arc_edges.append(edge_indices)
elif angle_layer and edge[angle_layer] > 0:
roof_angle_edges.append(edge_indices)
elif preview_layer and edge[preview_layer] == 1:
preview_edges.append(edge_indices)
else:
unselected_edges.append(edge_indices)
### Actually drawing
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
self.line_shader.bind()
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
self.line_shader.uniform_float("lineWidth", 2.0)
# general shader
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader.bind()
# Draw faces
if draw_faces:
self.draw_faces(bm, all_vertices)
self.draw_batch("LINES", all_vertices, transparent_color(unselected_elements_color), unselected_edges)
self.draw_batch("LINES", all_vertices, selected_elements_color, selected_edges)
self.draw_batch("LINES", all_vertices, background_elements_color, arc_edges)
self.draw_batch("LINES", all_vertices, special_elements_color, preview_edges)
self.draw_batch("LINES", all_vertices, special_elements_color, roof_angle_edges)
self.draw_batch("POINTS", unselected_vertices, transparent_color(unselected_elements_color, 0.5))
self.draw_batch("POINTS", error_vertices, error_elements_color)
self.draw_batch("POINTS", special_vertices, special_elements_color)
self.draw_batch("POINTS", selected_vertices, selected_elements_color)
# Draw arcs
arc_centroids = []
arc_segments = []
for arc in arcs.values():
if len(arc) != 3:
continue
sorted_arc = [None, None, None]
for v1 in arc:
connections = 0
for link_edge in v1.link_edges:
v2 = link_edge.other_vert(v1)
if v2 in arc:
connections += 1
if connections == 2: # Midpoint
sorted_arc[1] = v1
else:
sorted_arc[2 if sorted_arc[2] is None else 0] = v1
points = [tuple(obj.matrix_world @ v.co) for v in sorted_arc]
centroid = tool.Cad.get_center_of_arc(points)
if centroid:
arc_centroids.append(tuple(centroid))
arc_segments.append(tool.Cad.create_arc_segments(pts=points, num_verts=17, make_edges=True))
self.draw_batch("POINTS", arc_centroids, background_elements_color)
for verts, edges in arc_segments:
self.draw_batch("LINES", verts, special_elements_color, edges)
# Draw circles
circle_centroids = []
circle_segments = []
for circle in circles.values():
if len(circle) != 2:
continue
p1 = obj.matrix_world @ circle[0].co
p2 = obj.matrix_world @ circle[1].co
radius = (p2 - p1).length / 2
centroid = p1.lerp(p2, 0.5)
circle_centroids.append(tuple(centroid))
segments = self.create_circle_segments(360, 20, radius)
matrix = obj.matrix_world.copy()
matrix.translation = centroid
segments = [[list(matrix @ Vector(v)) for v in segments[0]], segments[1]]
circle_segments.append(segments)
self.draw_batch("POINTS", circle_centroids, background_elements_color)
for verts, edges in circle_segments:
self.draw_batch("LINES", verts, special_elements_color, edges)
def create_matrix(self, p, x, y, z):
return Matrix([x, y, z, p]).to_4x4().transposed()
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
# This function is taken from Sverchok, licensed under GPL v2-or-later.
# This is a combination of the make_verts and make_edges function.
def create_circle_segments(self, Angle, Vertices, Radius):
if Angle < 360:
theta = Angle / (Vertices - 1)
else:
theta = Angle / Vertices
listVertX = []
listVertY = []
for i in range(Vertices):
listVertX.append(Radius * cos(radians(theta * i)))
listVertY.append(Radius * sin(radians(theta * i)))
if Angle < 360 and self.mode_ == 0:
sigma = radians(Angle)
listVertX[-1] = Radius * cos(sigma)
listVertY[-1] = Radius * sin(sigma)
elif Angle < 360 and self.mode_ == 1:
listVertX.append(0.0)
listVertY.append(0.0)
points = list((x, y, 0) for x, y in zip(listVertX, listVertY))
listEdg = [(i, i + 1) for i in range(Vertices - 1)]
if Angle < 360 and self.mode_ == 1:
listEdg.append((0, Vertices))
listEdg.append((Vertices - 1, Vertices))
else:
listEdg.append((Vertices - 1, 0))
return points, listEdg
@@ -1,90 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
import blenderbim.core.spatial
import blenderbim.core.aggregate
from blenderbim.bim.ifc import IfcStore
class OpenPieClass(bpy.types.Operator):
bl_idname = "bim.open_pie_class"
bl_label = "Open Pie Class"
bl_description = "Assign the IFC Class to the selected objects"
@classmethod
def poll(cls, context):
if not context.active_object and not context.selected_objects:
cls.poll_message_set("No object selected.")
return False
return True
def execute(self, context):
bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_bim_class")
return {"FINISHED"}
class PieAddOpening(bpy.types.Operator):
bl_idname = "bim.pie_add_opening"
bl_label = "Add Opening"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
if len(context.selected_objects) == 2:
opening_name = None
obj_name = None
for obj in context.selected_objects:
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
opening_name = obj.name
elif len(obj.children) == 1 and not obj.children[0].BIMObjectProperties.ifc_definition_id:
opening_name = obj.children[0].name
else:
obj_name = obj.name
bpy.ops.bim.add_opening(obj=obj_name, opening=opening_name)
return {"FINISHED"}
class VIEW3D_MT_PIE_bim(bpy.types.Menu):
bl_label = "Geometry"
def draw(self, context):
pie = self.layout.menu_pie()
pie.operator("bim.edit_object_placement")
pie.operator("bim.update_representation").ifc_representation_class = ""
pie.operator("bim.pie_add_opening")
pie.operator("bim.open_pie_class", text="Assign IFC Class")
pie.operator("bim.aggregate_assign_object", text="Assign Aggregation")
pie.operator("bim.aggregate_unassign_object", text="Unassign Aggregation")
class VIEW3D_MT_PIE_bim_class(bpy.types.Menu):
bl_label = "Class"
def draw(self, context):
pie = self.layout.menu_pie()
pie.operator("bim.assign_class", text="IfcWall").ifc_class = "IfcWall"
pie.operator("bim.assign_class", text="IfcSlab").ifc_class = "IfcSlab"
pie.operator("bim.assign_class", text="IfcStair").ifc_class = "IfcStair"
pie.operator("bim.assign_class", text="IfcDoor").ifc_class = "IfcDoor"
pie.operator("bim.assign_class", text="IfcWindow").ifc_class = "IfcWindow"
pie.operator("bim.assign_class", text="IfcColumn").ifc_class = "IfcColumn"
pie.operator("bim.assign_class", text="IfcBeam").ifc_class = "IfcBeam"
File diff suppressed because it is too large Load Diff
@@ -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
-20
View File
@@ -1,20 +0,0 @@
Code style
============
Black code formatter
-------------------------------
For Python code formatting, we use `Black code formatter <https://pypi.org/project/black/>`__ with ``--line-length 120``.
``black`` can be installed using ``pip install black`` and files can be formatted with the following example command:
.. code-block:: bash
black --line-length 120 src/blenderbim/blenderbim/bim/module/qto/operator.py
Using PowerShell, you can run the Black formatter on the last commit in the repository.
You can change ``~1`` to ``~n`` to affect ``n`` commits.
.. code-block:: powershell
git diff HEAD HEAD~1 --name-only | where {$_ -like "*.py"} | foreach-object { start $_ && black --line-length 120 $_ }
@@ -1,29 +0,0 @@
Getting Started
===============
BlenderBIM is an open-source project, and its development is driven by the contributions of a dedicated community of developers,
architects, engineers, and enthusiasts. If you're interested in contributing to the project, whether by submitting bug reports,
suggesting new features, or contributing code, your involvement is highly encouraged and appreciated.
This part of the documentation covers various aspects of the BlenderBIM development process, including:
- :doc:`Writing User Documentation </contribute/writing_docs>`
- :doc:`Translations and Internationalisation </contribute/translations>`
- Contributing Code
- :doc:`Installation and Setting up a Development Environment </devs/installation>`
- Understanding the Project Structure and Codebase
- :doc:`Hello, World! </devs/hello_world>`
- :doc:`Undo System </devs/undo_system>`
- :doc:`Code Style Guidelines and Best Practices </devs/code_style>`
..
- :doc:`Submitting Pull Requests and Contributing Code </devs/contributing_code>`
- :doc:`Testing and Quality Assurance </devs/running_tests>`
- :doc:`Running Tests </devs/running_tests>`
- :doc:`System Support and Multiplatform Compatibility </devs/system_support>`
- :doc:`User Experience and User Interface Guidelines </devs/ux_guidelines>`
The BlenderBIM Developer Documentation is a living resource maintained by the core development team and the open-source community. It serves as a central hub for developers who want to get involved in the project, ensuring a consistent and efficient development process.
By contributing to BlenderBIM, you'll not only be helping to improve and expand the capabilities of this powerful open-source BIM authoring platform but also be part of a vibrant community driving innovation in the AEC industry.
-191
View File
@@ -1,191 +0,0 @@
Installation
============
There are different methods of installation, depending on your situation.
1. **Unstable installation** is recommended for power users helping with testing.
2. **Bundling for Blender** is recommended for distributing the add-on.
3. **Live development environment** is recommended for developers who are actively coding.
4. **Packaged installation** is recommended for those who use a package manager.
Unstable installation
---------------------
**Unstable installation** is almost the same as **Stable installation**, except
that they are typically updated every day. Simply download a daily build from
the `GitHub releases page
<https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, then follow the
usual :doc:`installation instructions</users/quickstart/installation>`.
You will need to choose which build to download.
- If you are on Blender >=4.1, choose py311
- If you are on Blender >=3.1 and <=4.0, choose py310
- If you are on Blender >=2.93 and <3.1, choose py39
- Choose ``linux-x64``, ``macos-x64`` (Apple Intel), ``macos-arm64`` (Apple Silicon), or
``windows-x64`` depending on your operating system
For users who don't follow the `VFX Platform <https://vfxplatform.com/>`_
standard, we also provide py312 builds.
Sometimes, a build may be delayed, or contain broken code. We try to avoid this,
but it happens.
Bundling for Blender
--------------------
Instead of waiting for an official release on the BlenderBIM Add-on website, it
is possible to make your own Blender add-on from the bleeding edge source code
of BlenderBIM. BlenderBIM is coded in Python and doesn't require any
compilation, so this is a relatively easy process.
Note that the BlenderBIM Add-on does depend on IfcOpenShell, and IfcOpenShell
does require compilation. The following instructions will use a pre-built
IfcOpenShell (using an IfcOpenBot build) for convenience. Instructions on how to
compile IfcOpenShell is out of scope of this document.
You can create your own package by using the Makefile as shown below. You can
choose between a ``PLATFORM`` of ``linux``, ``macos``, ``macosm1``, and ``win``.
You can choose between a ``PYVERSION`` of ``py311``, ``py310``, or ``py39``.
.. code-block:: bash
cd src/blenderbim
make dist PLATFORM=linux PYVERSION=py311
ls dist/
This will give you a fully packaged Blender add-on zip that you can distribute
and install.
Live development environment
----------------------------
One option for developers who want to actively develop from source is to follow
the instructions from **Bundling for Blender**. However, creating a build,
uninstalling the old add-on, and installing a new build is a slow process.
Although it works, it is very slow, so we do not recommend it.
A more rapid approach is to follow the **Unstable installation** method, as this
provides all dependencies for you out of the box.
Once you've done this, you can replace certain Python files that tend to be
updated frequently with those from the Git repository. We're going to use
symlinks (Windows users can use ``mklink``), so we can code in our Git
repository, and see the changes in our Blender installation (you will need to
restart Blender to see changes).
For Linux or Mac:
.. literalinclude:: ../../scripts/installation/dev_environment.sh
:language: bash
:caption: dev_environment.sh
Or, if you're on Windows, you can use the batch script below. You need to run
it as an administrator. Before running it follow the instructions descibed
in the `rem` tags.
.. literalinclude:: ../../scripts/installation/dev_environment.bat
:language: bat
:caption: dev_environment.bat
After you modify your code in the Git repository, you will need to restart
Blender for the changes to take effect.
The downside with this approach is that if a new dependency is added, or a
compiled dependency version requirement has changed, or the build system
changes, you'll need to fix your setup manually. But this is relatively rare.
Reviewing the Makefile history, `here <https://github.com/IfcOpenShell/IfcOpenShell/commits/v0.8.0/src/blenderbim/Makefile>`__, is one quick way to see if a dependency has changed.
.. seealso::
There is a `useful Blender Addon
<https://blenderartists.org/uploads/short-url/yto1sjw7pqDRVNQzpVLmn51PEDN.zip>`__
(see `forum thread
<https://blenderartists.org/t/reboot-blender-addon/640465/13>`__) that adds
a Reboot button in File menu. In this way, it's possible to directly
restart Blender and test the modified source code. There is also a VS Code
add-on called `Blender Development
<https://marketplace.visualstudio.com/items?itemName=JacquesLucke.blender-development>`__
that has a similar functionality.
Packaged installation
---------------------
- **Arch Linux**: `Direct from Git <https://aur.archlinux.org/packages/ifcopenshell-git/>`__.
- **Chocolatey on Windows**: `Unstable <https://community.chocolatey.org/packages/blenderbim-nightly/>`__.
Tips for package managers
-------------------------
If you are interested in packaging the BlenderBIM Add-on for a packaging
manager, read on.
The BlenderBIM Add-on is fully contained in the ``blenderbim/`` subfolder of the
Blender add-ons directory. This is typically distributed as a zipfile as per
Blender add-on conventions. Within this folder, you'll find the following file
structure:
::
core/ (Blender agnostic core code)
tool/ (Blender specific logic)
bim/ (Blender specific UI)
libs/ (dependencies)
__init__.py
This corresponds to the structure found in the source code `here
<https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/blenderbim/blenderbim>`__.
The BlenderBIM Add-on is complex, and requires many dependencies, including
Python modules, binaries, and static assets. When packaged for users, these
dependencies are bundled with the add-on for convenience.
If you choose to install the BlenderBIM Add-on and use your own system
dependencies, the source of truth for how dependencies are bundled are found in
the `Makefile
<https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.8.0/src/blenderbim/Makefile>`__.
Required Python modules to be stored in ``libs/site/packages/`` are:
::
ifcopenshell
bcf
ifcclash
ifccobie
ifccsv
ifcdiff
ifc4d
ifc5d
ifcpatch
ifctester
pystache
svgwrite
dateutil
isodate
networkx
https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
deepdiff
jsonpickle
ordered_set
pyparsing
xmlschema
elementpath
six
lark-parser
parse
parse_type
xlsxwriter
odfpy
defusedxml
jmespath
ifcjson
Notes:
1. ``ifcopenshell`` almost always requires the latest version due to the fast paced nature of the add-on development.
2. ``ifcjson`` can be found `here <https://github.com/IFCJSON-Team/IFC2JSON_python/tree/master/file_converters>`__.
Required static assets are:
::
bim/data/gantt/jsgantt.js (from jsgantt-improved)
bim/data/gantt/jsgantt.css (from jsgantt-improved)
@@ -1,45 +0,0 @@
System support
==============
The BlenderBIM Add-on officially supports all major 64-bit platforms, as well as
the Python version shipped by the Blender Foundation for the most recent three
major Blender versions:
- 64-bit Linux on Python 3.10
- 64-bit Linux on Python 3.11
- 64-bit MacOS Intel on Python 3.10
- 64-bit MacOS Intel on Python 3.11
- 64-bit MacOS Silicon on Python 3.10
- 64-bit MacOS Silicon on Python 3.11
- 64-bit Windows on Python 3.10
- 64-bit Windows on Python 3.11
Note that developer builds may exist for older versions of Python but there will
be no guarantee of the uptime or stability of these builds.
Other system specifications match the `Blender Requirements
<https://www.blender.org/download/requirements/>`_.
Add-on compatibility
--------------------
The BlenderBIM Add-on is a non-trivial add-on. By turning Blender into a
graphical front-end to a native IFC authoring platform, some fundamental Blender
features (such as hotkeys for basic functionality like object deletion or
duplication) have been patched and many dependencies have been introduced.
Other add-ons may no longer work as intended when the BlenderBIM Add-on is
enabled, or vice versa, the BlenderBIM Add-on may no longer work as intended
when other add-ons are enabled.
Known scenarios which will lead to add-on incompatibility include:
- The add-on also overrides the same hotkeys. For example, if an add-on
overrides the "X" key to delete an object, you will need to manually trigger
(either via menu or custom hotkey) the BlenderBIM Add-on equivalent operator
(e.g. IFC Delete).
- The add-on uses object deletion or duplication macros with dictionary
override. Note that this is also deprecated in Blender, so the other add-on
should be updated to fix this.
- The add-on requires a conflicting dependency, or a conflicting version of the
same dependency. Neither add-on may work simultaneously.
-178
View File
@@ -1,178 +0,0 @@
User Experience and User Interface guidelines
=============================================
This is intended as a reference document for developers working on the BlenderBim addon.
Operators
^^^^^^^^^
BIM will by definition contain a (big) number of submodules or subschemas. Granting access to the features of these submodules to the users needs to be considered with several things in minds :
1. Ease of use : How do I achieve my goal with the minimal number of clicks ? How many different menu items do I have to go through to get to my goal ? How long do I have to scroll a menu to get to the specific thing I have in mind ?
2. Clarity : How long do I have to visually parse the interface to find the item or button I'm looking for ? Are the buttons correctly labeled and are the icons descriptive enough ? If something is forbidden, can I easily know why ? Can I know what a button does before clicking on it ?
3. Completeness : I need to have access to all the items that are related to the topic I'm interested in. If it is tied to another submodule, I need to have access to it.
* **Blender**
These considerations must be adapted to the Python API, to the general paradigm we have chosen to access, modify or create IFC data, and to the general philosophy of Blender. There are many ways and places where an add-on creator can choose to present information to the user. BlenderBim currently works that way :
- Information about the current context is displayed inside the properties editor. We leverage the fact that it is already built around displaying different types of information whether we are in the Scene, Material, Mesh, Object, Curve, Texture, etc. subpanels. It fits really well with the way things are already organized in the IFC classification.
- Tools to create, modify or delete items are in the dedicated Toolbar (The left one) in the 3D viewport
- Some Misc tools and information are displayed in the N panel of the 3D viewport . They're shortcuts to things that are already present elsewhere in the UI, but are handy to have when modifying objects or geometry in the Viewport.
- Adding specific Import / Export IFC operators (arguably a bit misleading since BlenderBim does more than import / export in these operations)
* **Interface panels**
- The good : Every submodule is visually separated from each other. The user can customize its interface to only show what's relevant to them at any one point by folding in or out panels. Development / Debugging is facilitated because each submodule's interface is separated from the others.
- The bad : Having many interface panels clutters the interface, leading to an overwhelming experience for newcomers especially.
- Proposal : The addon should in the long run provide the user with dynamic UI depending on what's their usecase. Preferably accessed in the Addon Preferences interface, with pre-made options that dynamically enable or disable specific parts of the UI. It should also provide the option to selectively add or remove specific parts of the UI on top of that. Additionally, when it does not make sense to display a specific panel, it should be hidden from the UI. (eg if an object has no IFC Class, it can't be spatially contained, so the IFC Spatial Container panel should be hidden until the user sets the IFC class)
In the long term it could also be interesting to think about some kind of tutorial mode where more information is conveyed through the interface for new users.
* **Item Lists**
An item list should provide right off the bat a few features :
- Add a new item.
- Modify an item, whether by adding a button to each item in the list, or adding a button on the list header to modify the selected item.
- Delete an item, whether by adding a button to each item in the list, or adding a button on the list header to delete the selected item.
- Modify an item's name by double-clicking on the field if applicable
- The currently selected item should be highlighted
- There should be a search field to filter through specific items in the list, with buttons to invert selection, sort alphabetically and revert display. (Features that are provided by the `UIList` class)
* **Operators**
- Some buttons should be dynamically disabled (grayed out and impossible to click on) when the context to execute them does not make sense. It is important to note there should not be any heavy computation to determine this since this is executed several times per second for each button where it is implemented. It should only test for simple things and not rely on retrieving information from other modules too much. eg : `Is an item selected ?` `Is the z position of the selected object > 0 ?` or `Is the selected object an IfcOpeningElement ?` and **not** `Is there an IfcOpeningElement in the current file ?` or `How many different classes of IfcBeam are implemented in the file ?`
In Blender this is done in two ways :
- Directly in the operator `poll` method which is executed when the button is displayed in the interface. The drawback is that some operators rely on custom attributes which are provided only when the user clicks on the button, and cannot be test in the `poll` method. Note in V3.0 we can implement custom messages when the `poll` method fails, depending on the step where the context was not right.
- In the UI drawing code, where specific layout parts can be disabled.
- Some buttons or entire interface layout rows or columns may be disabled or hidden at once. It may be desirable when it relies on specific things or combinations of things not being met in the project. In order to avoid UI flicker or the user wondering where the button went, dynamically hiding UI elements should have a minimal interference with the rest of the UI by :
(1) Hiding the last row(s) of a panel
(2) Dynamically resizing the rest of the UI when it's not possible to (1)
The user should be advised why they are prevented to do things via either :
- A custom message using Blender's report system
- A label in the UI replacing the missing UI elements
- A helpful tooltip on an operator
Generally the context itself should be used to the best of its ability to convey why it's possible to do some things or not. The separation of concerns in multiple different dynamically hidden-or-shown sub-panels should help limit confusion.
- The user should be forbidden to click on buttons when its execution returns early and does nothing. If a button is clickable and the user clicks on it, there should always be some kind of feedback.
Icons
^^^^^
* .. image:: https://user-images.githubusercontent.com/25156105/137270452-bf6c03ea-2d3e-4ae3-8121-6799e8beb335.png
**Icon name** : `"CANCEL"`
**Use** : To disable the edition state of a particular item.
**Expected result** : A part of the interface showing the item attributes is either shrunk down or hidden. The button is usually replaced by a button to delete this item.
* .. image:: https://user-images.githubusercontent.com/25156105/137271575-dcae9e56-3c64-4d8b-89b0-6cf103b999f2.png
**Icon name** : `"X"`
**Use** : To delete a particular item, for instance an element of a list.
**Expected result** : The interface element referencing the deleted item is hidden from the interface. If the item was in a list, and the deleted element was selected, the next element becomes selected, or if there is no next element, no element is selected.
* .. image:: https://user-images.githubusercontent.com/25156105/137271601-e79012e5-fc61-402e-830c-b34b4ca757a0.png
**Icon name** : `"GREASEPENCIL"`
**Use** : To Enable the edition of a particular item.
**Behaviour** : Usually only one element of a particular context can be edited at a time.
**Expected result** : The interface should either expand or spawn new elements to display the chosen item's editable attributes. If the editem item is part of a list and its interface elements contained an operator to delete it, it is swapped for an operator to disable the edition of that item.
**Proposal** : Currently all the edition buttons are hidden from the interface when editing a particular item. This results on buttons jumping to the right slightly. My proposal is to instead show but disable the other buttons to prevent interface flicker.
**Current** : https://user-images.githubusercontent.com/25156105/137273603-f9ac39e5-7616-4fb9-89e2-4dae453e650a.gif
**Proposal** : https://user-images.githubusercontent.com/25156105/137273329-532d6d59-e39d-483e-980b-b8405062eaa8.gif
.. image:: https://user-images.githubusercontent.com/25156105/137273981-26fa664a-a29e-402e-9d7e-67b8b71c2d5d.png
* **Icon name** : `"ADD"`
**Use** : To add an item to a particular context, either initializing it (eg. adding a coordinate system) or adding an element to a list.
**Expected result** : The interface should either expand to display the new item or a new line should be shown if the item is added in a list.
**Proposal** : In some instances adding an element in a list automatically enables the edition of this item's attributes. (eg IFC Groups) but in other instances it does not (eg IFC strucural load cases). It is arguable but I do not think the user should expect a new item to be in editable state right off the bat when they add it to a list.
* .. image:: https://user-images.githubusercontent.com/25156105/137274869-0d0b876a-545c-4a58-a386-90f85764b7cc.png
**Icon name** : `"RESTRICT_SELECT_OFF"`
**Use** : To select all the instances related to a particular item.
**Expected result** : All the objects relating to a particular item should be selected in the 3D viewport and/or in the outline.
**Proposal** : Whether or not previously selected objects should be deselected beforehand is up for debate I think, because both behaviours can be desirable. It also raises the question of which object should be set to active. (Random ? First one ? Biggest one ?...)
* .. image:: https://user-images.githubusercontent.com/25156105/137275582-98c1dfcc-fc96-41d6-80dd-fdb82796e66e.png
**Icon name** : `"IMPORT"`
**Use** : to import a particular file from the user's computer's directory to the current context
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type should be loaded in (eg adding a file extension, adding information in the filebrowser UI). When the file is loaded, the interface should expand to show the attributes or information or new context-sensitive operations provided by the imported file.
**Proposal** : Currently there are some instances where I don't know if the elements are supposed to require an external file, eg
.. image:: https://user-images.githubusercontent.com/25156105/137275887-e0f0b694-842b-422f-bc09-cdcefef64107.png
* .. image:: https://user-images.githubusercontent.com/25156105/137276333-4d083f6a-36b9-494f-b0de-ec1d321cbd3d.png
**Icon name** : `"EXPORT"`
**Use** : To export a particular file to the user's computer's directory
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type is going to be saved (eg adding a file extension, adding information in the filebrowser UI).
**Proposal** : When there is no actual file to be saved in the user's directory or the filepath is already set in another place in the UI :
.. image:: https://user-images.githubusercontent.com/25156105/137276840-3ceda0cd-87bd-45f5-8488-bfce86bf3d84.png
It may be better to use a save icon of some sort instead, eg
.. image:: https://user-images.githubusercontent.com/25156105/137276758-5d60f0ca-5a93-4af3-9c4f-30c5794e0657.png
* .. image:: https://user-images.githubusercontent.com/25156105/137277047-bd45df5b-3b47-4b2c-aa29-a340b0710345.png
**Icon name** : `"FILEBROWSER"`
**Use** : To Provide the current context with the path to a particular file or folder
**Expected result** : A new file browser window should open, with sufficient information provided to the user to know which file type is required (eg adding a file extension, adding information in the filebrowser UI). Upon file or folder selection, this should populate a field in the current interface context with the path to the selected file or folder, or at least let the user know that the required information is now correctly filled in.
* .. image:: https://user-images.githubusercontent.com/25156105/137277374-fa7ff310-0b41-49a9-98bd-7c4686da05fd.png
**Icon name** : `"EYEDROPPER"`
**Use** : To select a particular object in the interface to fill the field it is attached to
**Expected result** : The cursor should change to an eyedropper until the user clicks on the required item. The user should be made aware after selection if it was successful (associated field being filled) or unsuccessful (message ? or field staying empty)
-220
View File
@@ -1,220 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
BlenderBIM Reference Manual
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Welcome to the manual for `BlenderBIM <https://blenderbim.org>`__,
the free and open source BIM add-on for Blender.
Quickstart
==========
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`/users/quickstart/introduction_to_bim` (2-minute read)
Brief overview of BIM concepts and BlenderBIM's role.
.. container:: card
:doc:`/users/quickstart/installation` (5 to 10-minute process)
Installation guide and system requirements.
.. container:: card
:doc:`/users/quickstart/explore_model` (15-minute guide)
Load and navigate an IFC model.
.. container:: card
:doc:`/users/quickstart/create_model` (20-minute guide)
Create a simple BIM project.
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Quickstart
:maxdepth: 1
users/quickstart/introduction_to_bim
users/quickstart/installation
users/quickstart/explore_model
users/quickstart/create_model
users/quickstart/next_steps
Sections
========
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`/users/modeling/interface`
Guide to the BlenderBIM interface.
.. container:: card
:doc:`/users/modeling/ifc_modeling`
IFC modeling techniques and concepts.
.. container:: card
:doc:`/users/git_support`
Collaborate on projects using Git
.. container:: card
:doc:`/users/drawing/index`
2D drawing generation and annotation.
.. container:: card
:doc:`/users/structural_analysis/index`
Structural analysis capabilities in BlenderBIM.
.. container:: card
:doc:`/users/costing_and_scheduling/index`
Cost estimation and project scheduling features.
.. container:: card
:doc:`/users/advanced/index`
Advanced topics and large-scale modeling.
.. container:: card
:doc:`/users/tutorials/index`
Real-world BIM project tutorials.
.. container:: card
:doc:`/users/user_interface`
Detailed UI reference.
.. container:: card
:doc:`/users/other_addons`
Other complementary add-ons
.. container:: card
:doc:`/users/troubleshooting`
Troubleshooting when things go wrong
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Sections
:maxdepth: 2
users/modeling/interface
users/modeling/ifc_modeling
users/git_support
users/drawing/index
users/structural_analysis/index
users/costing_and_scheduling/index
users/advanced/index
users/tutorials/index
users/user_interface
users/other_addons
users/troubleshooting
Get Involved
============
This manual is maintained largely by volunteers.
Please consider joining the effort and :doc:`/contribute/writing_docs`.
You can also can help with :doc:`Translations and Internationalisation </contribute/translations>`
For users who want to contribute to the BlenderBIM codebase,
please refer to the :doc:`Documentation for Developers </devs/getting_started>`.
This part of the documentation covers various aspects of the BlenderBIM development process.
You can get in touch by `joining the chat <https://osarch.org/chat/>`__.
.. toctree::
:hidden:
:caption: Get Involved
:maxdepth: 1
contribute/writing_docs
contribute/translations
Developer Documentation
=======================
BlenderBIM is an open-source project, and its development is driven by the contributions of a dedicated community of developers,
architects, engineers, and enthusiasts. If you're interested in contributing to the project, whether by submitting bug reports,
suggesting new features, or contributing code, your involvement is highly encouraged and appreciated.
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`/devs/getting_started`
Introduction to contributing to BlenderBIM.
.. container:: card
:doc:`/devs/installation`
Setting up a development environment.
.. container:: card
:doc:`/devs/hello_world`
Understanding the project structure.
.. container:: card
:doc:`/devs/undo_system`
Working with BlenderBIM's undo system.
.. container:: card
:doc:`/devs/code_style`
Code style guidelines and best practices.
.. container:: card
:doc:`/devs/running_tests`
Testing and quality assurance.
.. container:: card
:doc:`/devs/system_support`
Multiplatform compatibility.
.. container:: card
:doc:`/devs/ux_guidelines`
User experience and interface guidelines.
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Developer Documentation
:maxdepth: 1
devs/getting_started
devs/installation
devs/hello_world
devs/undo_system
devs/code_style
devs/running_tests
devs/system_support
devs/ux_guidelines
@@ -1,10 +0,0 @@
Advanced Use Cases
==================
Advanced topics and large-scale modeling.
.. toctree::
:maxdepth: 1
georeferencing
dealing_with_large_models
@@ -1,51 +0,0 @@
============================
Advanded Modeling Techniques
============================
Complex modeling techniques for advanced users.
.. note::
This page is a stub. More detailed content will be added in future updates.
Custom Parametric Wall Types
----------------------------
[Content about creating custom parametric wall types]
Material assignment
-------------------
:doc:`material_assignment`
Modeling Furniture and Fixtures
-------------------------------
[Content about modeling furniture and fixtures]
Structural Elements
-------------------
.. seealso::
:doc:`/users/structural_analysis/index`
[Content about modeling structural elements]
Multi-Story Buildings
---------------------
[Content about modeling multi-story buildings]
Complex Structures
------------------
[Content about modeling complex structures]
.. toctree::
:hidden:
:caption: Sections
:maxdepth: 2
material_assignment
parametric_geometry
@@ -1,44 +0,0 @@
=========================
Basic Modeling Techniques
=========================
Fundamental techniques for IFC modeling.
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`creating_walls`
Learn how to create and modify walls in your IFC model.
.. container:: card
:doc:`openings/index`
Techniques for adding and customizing doors, windows and other openings.
.. container:: card
:doc:`modeling_slabs_roofs`
Guide to modeling slabs and different types of roofs.
.. container:: card
:doc:`defining_rooms_spaces`
Methods for defining and managing rooms and spaces in your model.
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Basic Modeling Techniques
:maxdepth: 1
creating_walls
openings/index
modeling_slabs_roofs
defining_rooms_spaces
@@ -1,66 +0,0 @@
Openings
========
This section covers the creation and management of openings in BlenderBIM.
Openings are crucial elements in building design, serving various purposes such as passage, ventilation, and lighting.
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`door`
Learn how to add and customize doors in your BIM model.
.. container:: card
:doc:`window`
Discover the process of creating and modifying windows.
.. container:: card
:doc:`opening`
Create openings without fillings for special architectural features.
Overview
--------
In the context of Building Information Modeling (BIM) and the Industry Foundation Classes (IFC)
standard, openings are represented through a combination of elements:
1. Voids: Represented by IfcOpeningElement, these are the actual cut-outs in the wall.
2. Fillings: These are the elements that occupy the voids, such as doors (IfcDoor) or windows (IfcWindow).
3. Relationships: These are abstract objects that connect fillings to voids and voids to elements in which they're created.
BlenderBIM provides tools to create and manage these elements:
- Door Creation Tool: For adding doors to walls.
- Window Creation Tool: For adding windows to walls.
- Wall Creation Tool > Void Application: For creating openings without fillings
(currently achieved by creating a door or window and removing it
or by using "Add Void" feature of the Create Wall tool or any element).
These tools allow you to:
- Create openings with precise dimensions and positions.
- Modify opening properties and geometries.
- Manage the relationships between walls, voids, and fillings.
The following pages provide detailed guides on working with each type of wall opening in BlenderBIM.
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Wall Openings
:maxdepth: 1
door
window
opening
See Also
--------
- :doc:`../creating_walls`
@@ -1,4 +0,0 @@
Importing and Viewing IFC Models
================================
[Content about importing and viewing IFC models]
@@ -1,92 +0,0 @@
============================
BlenderBIM Interface Guide
============================
Introduction
============
The BlenderBIM interface extends Blender's powerful 3D environment with specialized tools for Building Information Modeling (BIM). This guide will help you navigate the BlenderBIM interface and understand its key components.
BlenderBIM Workspace
====================
When you start Blender with BlenderBIM installed, you can switch to the BlenderBIM workspace
by clicking on the BIM workspace tab at the top of the Blender window.
This workspace is preconfigured with the most commonly used panels and tools for BIM workflows.
Key Interface Elements
======================
Properties Extended
-------------------
The Properties Editor is extended to provide IFC-specific properties and settings.
- Scene
- Project Information
- Tool
- Object
BIM Toolbar
-----------
The BIM toolbar, typically located at the left of the 3D Viewport, contains shortcuts to frequently used BIM tools:
- Explore tool
- Create various building elements
- Create annotations, measure distance and angles, calculate volumes, etc
IFC Tree View
-------------
Found in the Outliner, the IFC Tree View displays the hierarchical structure of your BIM model:
- Spatial Structure (Site, Building, Storey)
- Building Elements
- Types
Customizing the Interface
=========================
BlenderBIM respects Blender's highly customizable interface. You can:
- Rearrange editors and panels
- Save custom workspace layouts
- Create custom shortcuts for BIM operations
Tips for Efficient Use
======================
1. Familiarize yourself with IFC classes and their properties.
2. Utilize Blender's search function (F3) to quickly access BlenderBIM tools.
Next Steps
==========
Now that you're familiar with the BlenderBIM interface, you have two main paths to continue your learning:
1. Proceed with IFC Modeling:
If you're ready to start creating and working with BIM models, you can move on to:
- :doc:`IFC Modeling Basics </users/modeling/ifc_modeling>`
2. Dive deeper into the User Interface:
If you want to explore more details about the BlenderBIM interface, you can refer to:
- :doc:`User Interface Reference </users/user_interface>`
Choose the path that best suits your current needs and learning style. You can always come back to explore the other option later.
Remember, as you become more comfortable with the interface and basic modeling, you can explore more advanced topics such as:
- :doc:`Generating Documentation </users/drawing/index>`
- :doc:`Advanced BIM Techniques </users/advanced/index>`
The BlenderBIM interface is designed to integrate seamlessly with Blender while providing powerful BIM-specific functionality.
As you progress, you'll find your BIM workflow becoming increasingly efficient and productive.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

@@ -1,143 +0,0 @@
Installation
============
.. raw:: html
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/videoseries?list=PLMDcOjMJYxUPHHvEHqAsOuBdSPsp6or32" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
1. **Download and install Blender**
Blender is a free and open-source program for 3D authoring. It works on
Linux, Mac, and Windows. It is developed by the Blender community.
.. container:: blockbutton
`Download Blender <https://www.blender.org/download/>`__
.. tip::
No administrator rights on Windows? Choose the "Portable .zip" option when
downloading from the Blender website.
2. **Download the BlenderBIM Add-on**
The BlenderBIM Add-on extends Blender with OpenBIM related capabilities.
.. container:: blockbutton
`Download BlenderBIM Add-on <https://blenderbim.org/download.html>`__
.. warning::
If you are not using Blender version >=4.1, please follow the **Unstable installation** instructions. :doc:`Read more </devs/installation>`
3. **Install the BlenderBIM Add-on**
Open up Blender, and click on ``Edit > Preferences``.
.. image:: images/install-blenderbim-1.png
Select the **Add-ons** tab, and press **Install...** on the top right. Navigate
to the .zip you downloaded in Step 2, and press **Install Add-on**.
.. image:: images/install-blenderbim-2.png
.. warning::
You do not need to unzip the add-on file. You should install it as a zipped file.
You should now see **System: BlenderBIM** available in your add-ons list. Enable the add-on by pressing the checkbox.
.. image:: images/install-blenderbim-3.png
All done! Your interface will now look similar to below. If you check the ``File`` menu you should also see an option to ``Open IFC Project``.
.. image:: images/install-blenderbim-4.png
You can enable add-ons permanently by using ``Save User Settings`` from the Addons menu.
.. seealso::
If you are a poweruser, you may be interested in the **Unstable installation** to help with testing. :doc:`Read more </devs/installation>`
.. _where is the add-on installed:
Updating
--------
First follow the `Uninstalling`_ section below, then install the latest version.
Uninstalling
------------
Navigate to ``Edit > Preferences > Add-ons``. Due to a limitation in Blender,
you have to **first disable the BlenderBIM Add-on in your Blender preferences**
by pressing the checkbox next to the add-on, then **restart Blender**. It is
critical to follow this sequence of disabling first, and then restarting.
After restarting, you can uninstall the BlenderBIM Add-on by pressing the
``Remove`` button in the Blender preferences window.
Alternatively, you may uninstall manually by deleting the ``blenderbim``
directory in :ref:`your Blender add-ons directory<where is the add-on
installed>`.
.. warning::
It is important to follow the sequence of disabling, restarting, then removing.
If you do not restart Blender, the add-on will fail to remove correctly, and you
will need to uninstall manually.
.. _installation_location:
Where is the add-on installed?
------------------------------
Upon installation, the BlenderBIM Add-on is stored in the
``scripts/addons/blenderbim/`` directory, within your Blender configuration
folder. However, the location of your Blender configuration folder depends on
how you have installed Blender.
If you downloaded Blender as a ``.zip`` file without running an installer, the
BlenderBIM Add-on will be installed in the following directory, where ``X.XX``
is the Blender version:
::
/path/to/blender/X.XX/scripts/addons/blenderbim/
Otherwise, if you installed Blender using an installation package, the Blender
configuration folder depends on which operating system you use.
On Linux, if you are installing the add-on as a user:
::
~/.config/blender/X.XX/scripts/addons/blenderbim/
On Linux, if you are deploying the add-on system-wide (this may also depend on
your Linux distribution):
::
/usr/share/blender/X.XX/scripts/addons/blenderbim/
On Mac, if you are installing the add-on as a user:
::
/Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/scripts/addons/blenderbim/
On Mac, if you are deploying the add-on system-wide:
::
/Library/Application Support/Blender/X.XX/scripts/addons/blenderbim/
On Windows:
::
C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\X.XX\scripts\addons\blenderbim\
@@ -1,98 +0,0 @@
Introduction to BIM
===================
**Building Information Modeling**, or **BIM**, is a way of digitally describing
our built environment to computers. Aspects of our built environment that can be
described are:
- **Products**, like walls, doors, and windows
- **Processes**, like construction or maintenance tasks, and procedures
- **Resources**, like labour, materials, and equipment
- **Controls**, like permits, orders, costs, or calendar availability
- **Actors**, like occupants, clients, architects, and liable parties
- **Groups**, like systems, inventories, or zones
These objects may have lots of data and relationships. Examples of data might be
classification systems, physical materials, associated documents, simulation
results, and construction types. The data may be relevant to multiple
disciplines, such as architecture, engineering, and construction.
.. note::
BIM data is very different from a regular 3D model. In fact, geometry is
optional, and most data is non-geometric. This means that it is not simply a
3D format that you can import or export from and expect meaningful results.
**Industry Foundation Classes**, or **IFC**, is an international standard for
**BIM**. **IFC** is the most well-established open digital language for our
built environment. Most software will be able to describe their **BIM** data
using **IFC**. Most commonly, **IFC** models will be shared as a ``.ifc`` file.
For example, **IFC** will define a wall as an object that can have a name,
construction type, and quantities. **IFC** will also describe that a wall that
be associated with a location, like a building storey, or have an associated
cost item in a schedule.
When you use the BlenderBIM Add-on, you will be able to view and create **BIM**
objects and relationships using the **IFC** standard.
Things you can do
-----------------
The BlenderBIM Add-on is designed to be a comprehensive and truly native
BIM authoring platform. Its capabilities include a wide range of tasks
and workflows typically found across various BIM and CAD software, costing
programs, scheduling tools, and simulation applications. While not
an exhaustive list, some of the key things you can do with BlenderBIM include:
- View and explore IFC models, including spaces, properties, and relationships
- Edit and extract attributes, properties, and metadata directly from IFC data
- Move, rotate, and modify the geometry of objects while preserving IFC semantics
- Create new objects using predefined library elements or custom parametric types
- Manage classification systems, document references, and link to external libraries
- Generate 2D drawing views like plans, sections, and elevations with customizable annotations
- Investigate and edit structural analysis models with support for various steel profiles
- Model and manage complex distribution systems like HVAC, plumbing, and electrical (MEP)
- Create construction schedules, perform critical path analysis, and generate sequence animations
- Derive quantities from model elements and create cost schedules with formulas
- Perform clash detection and coordinate models, managing issues across disciplines
- Integrate non-geometric data like costing, scheduling, and asset management
Roadmap and Upcoming Features
-----------------------------
While BlenderBIM already offers a comprehensive set of BIM authoring capabilities,
the development team and community are continuously working to expand its functionality and improve existing workflows.
Some of the most ambitious features and enhancements on the roadmap include:
Usability and Workflow Enhancements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Improve user-friendliness to equip average "Joe" with capabilities to model simple projects like single family homes, making BlenderBIM a viable alternative to SketchUp.
- Make BlenderBIM more approachable for users across different skill levels, from advanced BIM experts to architects working on smaller residential projects.
Improved Drawing and Documentation Workflows
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Continued enhancements to the drawing generation capabilities, including better dimensioning tools and support for associative dimensions linked to model geometry.
- Advanced annotation tools with customizable tags, callouts, and the ability to define reusable annotation styles and templates.
- Streamlined sheet layout management and improved integration with external tools like Inkscape for creating title blocks and sheet compositions.
- Continued improvement of BlenderBIM's ability to generate comprehensive documentation sets, including drawing annotations, door schedules, wall schedules, and material tagging.
Expanded Parametric Modeling Capabilities
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Native modeling tools for complex building elements like curtain wall systems and facade panels.
- Expanded libraries with support for country-specific and manufacturer-provided object types.
- Enhanced support for multi-story modeling, enabling efficient duplication and coordination of building elements across various levels.
- Expanded parametric relationships and automating more common BIM tasks to reduce manual effort and enhance productivity.
Enhanced Coordination and Collaboration Features
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Robust model merge and coordination workflows, with detailed clash detection capabilities across different disciplines and data sources.
- Built-in support for version control and model sharing using Git repositories, enabling better team collaboration and change tracking.
The development roadmap is continuously updated based on user feedback, industry requirements,
and contributions from the open-source community. By embracing an open and collaborative approach,
BlenderBIM aims to push the boundaries of what's possible in BIM authoring,
ensuring that it remains at the forefront of innovation in the AEC industry.
@@ -1,93 +0,0 @@
Troubleshooting
===============
The BlenderBIM Add-on is alpha software. There are many bugs! When something
goes wrong, you may see some computer code flash up on your screen. You may
also see an error message:
.. image:: images/error-message.png
**Don't panic!** Click on the button that says **Copy Error Message To
Clipboard**. You will need to paste this text in a bug report.
If you do not have a GitHub account, you will need to sign up to report a bug.
In addition to pasting the error message text, please also describe what you
were doing, and attach your IFC file or screenshots if relevant.
.. container:: blockbutton
`Report a bug <https://github.com/IfcOpenShell/IfcOpenShell/issues/new>`__
If your issue is particularly complex, you can also chat live with developers
or other powerusers.
.. container:: blockbutton
`Chat live with a developer <https://osarch.org/chat>`_
Installation issues
-------------------
If you are unable to install the BlenderBIM Add-on, make sure you are using
**Blender 4.1** installed from https://blender.org/ and are installing the
latest version from https://blenderbim.org.
Other common solutions are listed below. If none of these fix the problem, you
can `report a bug <https://github.com/ifcopenshell/ifcopenshell/issues>`_ or
`live chat with a developer <https://osarch.org/chat/>`_.
1. **Some other error prevents me from installing or doing basic functions with
the add-on. Is it specific to my environment?**
Try installing and using the BlenderBIM Add-on on a "clean environment". A
clean environment is a fresh Blender installation with no other add-ons
enabled with factory settings.
To quickly test in a clean environment, first :ref:`find your Blender
configuration folder<installation_location>`.
Rename the folder from ``X.XX`` to something else like ``X.XX_backup``, then
restart Blender and try follow the :doc:`installation
instructions</users/quickstart/installation>` again.
If this fixes your issue, consider disabling other add-ons one by one until
you find a conflict as a next step to isolating the issue.
2. **I get an error similar to "ImportError: IfcOpenShell not built for 'linux/64bit/python3.10'"**
If you are using a Mac, be sure to use the Mac Silicon version if you have a
newer Mac. The only exception is if you have installed Blender using Steam
on a Mac, in which case you need to use the Mac Intel download.
For all other scenarios, check the BlenderBIM Add-on zip file which you
downloaded. The zip will have either ``py39``, ``py310``, or ``py311`` in
the name. See the instructions in the :ref:`devs/installation:unstable
installation` section to check that you have installed the correct version.
3. **I am on Ubuntu and get an error similar to "ImportError:
/lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found"**
Our latest package which uses IfcOpenShell v0.8.0 is built using Ubuntu 20 LTS.
If you have an older Ubuntu version, you can either upgrade to 19.10 or above,
or you'll need to compile IfcOpenShell yourself.
4. **I get an error saying "ModuleNotFoundError: No module named 'numpy'"**"
If you have installed Blender from another source instead of from
`Blender.org <https://www.blender.org/download/>`__, such as from your
distro's package repositories, then you may be missing some modules like
``numpy``. Try installing it manually like ``apt install python-numpy``.
Common issues
-------------
1. **I get an error similar to RuntimeError: Instance #1234 not found**
Blender saves and loads projects to a ``.blend`` file. However. the
BlenderBIM Add-on works with native IFC, and this means instead of saving
and loading ``.blend`` files, you should instead save and load the ``.ifc``
project.
If you have opened a ``.blend`` file, there is a risk that the contents of
the ``.blend`` session do not correlate to the contents of the ``.ifc``,
which can cause this error. Unless you are an advanced user, only save and
load ``.ifc`` files.
@@ -1,98 +0,0 @@
=======================
Tutorials
=======================
Project Series
==============
Step-by-step guides for real-world BIM projects.
.. toctree::
:maxdepth: 2
:hidden:
project0
project1
project2
project3
project4
project5
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
:doc:`Project 0: Designing a Room <project0>`
* Basic walls and openings
* Slabs
* MEP
* Basic furniture
* Basic floor plan drawings
.. container:: card
:doc:`Project 1: Designing a Flat <project1>`
* Advanced walls and openings
* Profiles
* Rooms and spaces
* Furniture/fixture libraries
* Elevation/section drawings
.. container:: card
:doc:`Project 2: Building a Bungalow <project2>`
* Roof
* Advanced foundation
* Structural elements
* Roof openings
* Underground services
* Running services in subfloor space
* Drawing details
* Basic costing and scheduling
.. container:: card
:doc:`Project 3: Multi-Storey Single-Family Home <project3>`
* Templating
* Types
* Complex roofs
* Complex fixtures (e.g. solar panels)
* Complex HVAC, plumbing, electrical
* Basic site landscaping
.. container:: card
:doc:`Project 4: Terraced/Town Houses/Duplexes <project4>`
* Shared walls
* Shared roofs
* Complex landscaping
* Duplicating units
* Basic parametric floor plans
* Generating drawings parametrically
* Clash detection
* Coordination with other stakeholders
.. container:: card
:doc:`Project 5: Apartment Block <project5>`
* Multiple levels
* Typical floor
* Shared amenities and common areas
* Elevators
* Parametric services
* Complex structural elements
* Advanced exporting
* Merging
* Coordination
* Analysis
* Advanced costing and scheduling
* External tools
Quickstart Video Tutorial
=========================
Learn how to model a small building and create simple architectural plans, sections, 2D details, and sheet layouts in this short tutorial series.
`View all tutorial videos <https://www.youtube.com/playlist?list=PLMDcOjMJYxUPHHvEHqAsOuBdSPsp6or32>`__
@@ -1,23 +0,0 @@
User Interface Reference
========================
This section covers the general user interface of BlenderBIM. User interface documentation does not cover any specific use case but provides a general reference of BlenderBIM UI.
The documentation doesn't cover Blender UI, but makes references to `Blender documentation <https://docs.blender.org/manual/en/latest/interface/index.html>`__ where appropriate.
.. * Navigating the Project Browser and Outliner
.. * Using the Property Panels
.. * Toolbars and Menus
.. * Customizing the Interface
.. * Keyboard Shortcuts and Preferences
.. toctree::
:hidden:
:maxdepth: 1
:caption: Contents:
user_interface/topbar
user_interface/workspace
user_interface/toolbar/index
user_interface/property-editor
@@ -1,60 +0,0 @@
Property Editor Scene Properties
================================
.. container:: location-scene
|location| Scene Properties
.. |location| image:: /images/location-scene.svg
BlenderBIM adds new functionality to the `Property Editor` -> `Scene` tab.
.. figure:: images/interface_property-editor_project-overview_start-up.png
:alt: Property editor on Blender start-up
The property editor on Blender startup.
Most of these sub-tabs become available with a created or loaded IFC file.
Don't worry, the default Blender scene properties are still reachable under their own dedicated sub-tab.
.. figure:: images/interface_property-editor_icons.png
:alt: Overview over the added property sub-tabs by BlenderBIM
Overview over the added property sub-tabs by BlenderBIM.
1. Project Overview
2. Object Information
3. Geometry and Materials
4. Drawings and Documents
5. Services and Systems
6. Structural Analyses
7. Costing and Scheduling
8. Facility Management
9. Quality and Coordination
10. Blender Properties
11. Switch Tab
You can also select the needed panel via the drop-down menue.
.. figure:: images/interface_property-editor_panel-dropdown.png
:alt: BlenderBIM property editor sub-tabs drop-down menue
Switching between BlenderBIM property editor sub-tabs via the drop-down menue.
.. toctree::
:hidden:
:maxdepth: 1
:caption: Contents:
property_editor/scene_editor/project_overview/index
property_editor/scene_editor/object_information/index
property_editor/scene_editor/geometry_and_materials/index
property_editor/scene_editor/drawings_and_documents/index
property_editor/scene_editor/services_and_systems/index
property_editor/scene_editor/structural_analysis/index
property_editor/scene_editor/costing_and_scheduling/index
property_editor/scene_editor/facility_management/index
property_editor/scene_editor/quality_and_coordination/index
property_editor/scene_editor/blender
property_editor/scene_editor/switch_tab
@@ -1,70 +0,0 @@
@echo off
echo SETUP BLENDERBIM ADD-ON LIVE DEVELOPMENT ENVIRONMENT
echo Update BLENDER, BLENDER_VERSION, PYTHON_VERSION, BBIM_REPO, BLENDER_USER_NAME in the script bellow.
echo Put the script at the root of your IfcOpenShell git repository
echo This script needs to be run as administrator (to create symbolic links)
echo Make sure you have followed these steps before proceeding :)
pause
SET BLENDER_VERSION=4.2
SET PYTHON_VERSION=3.11
SET BBIM_REPO=user_default
rem Can be useful e.g. if you're running script from administrator account
rem and need to setup dev environment for some other user.
SET BLENDER_USER_NAME=%USERNAME%
SET BLENDER=%HOMEDRIVE%\Users\%BLENDER_USER_NAME%\AppData\Roaming\Blender Foundation\Blender\%BLENDER_VERSION%
SET BLENDER_LOCAL=%BLENDER%\extensions\.local\lib\python%PYTHON_VERSION%\site-packages
SET BLENDERBIM=%BLENDER_LOCAL%\blenderbim
SET BLENDER_EXT=%BLENDER%\extensions\%BBIM_REPO%\blenderbim
echo Removing the Blender add-on Python code...
del "%BLENDER_EXT%\__init__.py"
rd /S /Q "%BLENDERBIM%"
echo Replacing them with links to the Git repository...
mklink "%BLENDER_EXT%\__init__.py" "%CD%\src\blenderbim\blenderbim\__init__.py"
mklink /D "%BLENDERBIM%" "%CD%\src\blenderbim\blenderbim"
echo Copy over compiled IfcOpenShell files...
copy "%BLENDER_LOCAL%\ifcopenshell\*_wrapper*" "%CD%\src\ifcopenshell-python\ifcopenshell\"
echo Remove the IfcOpenShell dependency...
rd /S /Q "%BLENDER_LOCAL%\ifcopenshell"
echo Replace them with links to the Git repository...
mklink /D "%BLENDER_LOCAL%\ifcopenshell" "%CD%\src\ifcopenshell-python\ifcopenshell"
echo Remove and link other IfcOpenShell utilities...
del "%BLENDER_LOCAL%\ifccsv.py"
del "%BLENDER_LOCAL%\ifcdiff.py"
del "%BLENDER_LOCAL%\bsdd.py"
rd /S /Q "%BLENDER_LOCAL%\bcf"
rd /S /Q "%BLENDER_LOCAL%\ifc4d"
rd /S /Q "%BLENDER_LOCAL%\ifc5d"
rd /S /Q "%BLENDER_LOCAL%\ifccityjson"
rd /S /Q "%BLENDER_LOCAL%\ifcclash"
rd /S /Q "%BLENDER_LOCAL%\ifcpatch"
rd /S /Q "%BLENDER_LOCAL%\ifctester"
rd /S /Q "%BLENDER_LOCAL%\ifcfm"
mklink "%BLENDER_LOCAL%\ifccsv.py" "%CD%\src\ifccsv\ifccsv.py"
mklink "%BLENDER_LOCAL%\ifcdiff.py" "%CD%\src\ifcdiff\ifcdiff.py"
mklink "%BLENDER_LOCAL%\bsdd.py" "%CD%\src\bsdd\bsdd.py"
mklink /D "%BLENDER_LOCAL%\bcf" "%CD%\src\bcf\bcf"
mklink /D "%BLENDER_LOCAL%\ifc4d" "%CD%\src\ifc4d\ifc4d"
mklink /D "%BLENDER_LOCAL%\ifc5d" "%CD%\src\ifc5d\ifc5d"
mklink /D "%BLENDER_LOCAL%\ifccityjson" "%CD%\src\ifccityjson\ifccityjson"
mklink /D "%BLENDER_LOCAL%\ifcclash" "%CD%\src\ifcclash\ifcclash"
mklink /D "%BLENDER_LOCAL%\ifcpatch" "%CD%\src\ifcpatch\ifcpatch"
mklink /D "%BLENDER_LOCAL%\ifctester" "%CD%\src\ifctester\ifctester"
mklink /D "%BLENDER_LOCAL%\ifcfm" "%CD%\src\ifcfm\ifcfm"
echo Manually downloading some third party dependencies...
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js -o "%BLENDERBIM%\bim\data\gantt\jsgantt.js"
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css -o "%BLENDERBIM%\bim\data\gantt\jsgantt.css"
curl -L https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl -o "%BLENDERBIM%\bim\schema\Brick.ttl"
pause
@@ -1,60 +0,0 @@
git clone https://github.com/IfcOpenShell/IfcOpenShell.git
cd IfcOpenShell
# path to BlenderBIM addon
# default path on Mac: "/Users/$USER/Library/Application Support/Blender/X.X/scripts/addons/blenderbim"
# default path on Linux: "$HOME/.config/blender/X.X/"
BLENDER_ADDON_PATH="/path/to/blender/X.XX/scripts/addons/blenderbim"
# Remove the Blender add-on Python code
rm -r $BLENDER_ADDON_PATH/core/
rm -r $BLENDER_ADDON_PATH/tool/
rm -r $BLENDER_ADDON_PATH/bim/
# Replace them with links to the Git repository
ln -s $PWD/src/blenderbim/blenderbim/core $BLENDER_ADDON_PATH/core
ln -s $PWD/src/blenderbim/blenderbim/tool $BLENDER_ADDON_PATH/tool
ln -s $PWD/src/blenderbim/blenderbim/bim $BLENDER_ADDON_PATH/bim
# Copy over compiled IfcOpenShell files
cp $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/*_wrapper* $PWD/src/ifcopenshell-python/ifcopenshell/
# Remove the IfcOpenShell dependency
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
# Replace them with links to the Git repository
ln -s $PWD/src/ifcopenshell-python/ifcopenshell $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
# Remove and link other IfcOpenShell utilities
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bcf
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifctester
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
rm -r $BLENDER_ADDON_PATH/libs/Desktop
ln -s $PWD/src/ifccsv/ifccsv.py $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
ln -s $PWD/src/ifcdiff/ifcdiff.py $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
ln -s $PWD/src/bsdd/bsdd.py $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
ln -s $PWD/src/bcf/bcf $BLENDER_ADDON_PATH/libs/site/packages/bcf
ln -s $PWD/src/ifc4d/ifc4d $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
ln -s $PWD/src/ifc5d/ifc5d $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
ln -s $PWD/src/ifccityjson/ifccityjson $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
ln -s $PWD/src/ifcclash/ifcclash $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
ln -s $PWD/src/ifcpatch/ifcpatch $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
ln -s $PWD/src/ifctester/ifctester $BLENDER_ADDON_PATH/libs/site/packages/ifctester
ln -s $PWD/src/ifcfm/ifcfm $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
ln -s $PWD/src/blenderbim/blenderbim/libs/desktop $BLENDER_ADDON_PATH/libs/Desktop
# Manually download some third party dependencies
cd $BLENDER_ADDON_PATH/bim/data/gantt
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
cd $BLENDER_ADDON_PATH/bim/schema
wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
@@ -1,76 +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: Set IFC grid north
Given an empty IFC project
And I press "bim.add_georeferencing"
And I press "bim.enable_editing_georeferencing"
When I press "bim.set_ifc_grid_north"
Then nothing happens
Scenario: Set Blender grid north
Given an empty IFC project
And I press "bim.add_georeferencing"
And I press "bim.enable_editing_georeferencing"
And I press "bim.set_ifc_grid_north"
When I press "bim.set_blender_grid_north"
Then nothing happens
Scenario: Get cursor location
Given an empty IFC project
When I press "bim.get_cursor_location"
Then nothing happens
Scenario: Set IFC true north
Given an empty IFC project
When I press "bim.set_ifc_true_north"
Then nothing happens
Scenario: Set Blender true north
Given an empty IFC project
And I press "bim.set_ifc_true_north"
When I press "bim.set_blender_true_north"
Then nothing happens
Scenario: Convert local to global
Given an empty IFC project
When I set "scene.BIMGeoreferenceProperties.local_coordinates" to "0,0,0"
And I press "bim.convert_local_to_global"
Then "scene.BIMGeoreferenceProperties.map_coordinates" is "0.0,0.0,0.0"
Scenario: Convert global to local
Given an empty IFC project
When I set "scene.BIMGeoreferenceProperties.map_coordinates" to "0,0,0"
And I press "bim.convert_global_to_local"
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/>.
-17
View File
@@ -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,112 +0,0 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import blenderbim.core.georeference as subject
from test.core.bootstrap import ifc, georeference
class TestAddGeoreferencing:
def test_run(self, ifc):
ifc.run("georeference.add_georeferencing").should_be_called()
subject.add_georeferencing(ifc)
class TestEnableGeoreferencing:
def test_run(self, georeference):
georeference.import_projected_crs().should_be_called()
georeference.import_map_conversion().should_be_called()
georeference.import_true_north().should_be_called()
georeference.enable_editing().should_be_called()
subject.enable_editing_georeferencing(georeference)
class TestRemoveGeoreferencing:
def test_run(self, ifc):
ifc.run("georeference.remove_georeferencing").should_be_called()
subject.remove_georeferencing(ifc)
class TestEditGeoreferencing:
def test_run(self, ifc, georeference):
georeference.get_projected_crs_attributes().should_be_called().will_return("projected_crs_attributes")
georeference.get_map_conversion_attributes().should_be_called().will_return("map_conversion_attributes")
georeference.get_true_north_attributes().should_be_called().will_return("true_north_attributes")
ifc.run(
"georeference.edit_georeferencing",
projected_crs="projected_crs_attributes",
map_conversion="map_conversion_attributes",
true_north="true_north_attributes",
).should_be_called()
georeference.disable_editing().should_be_called()
subject.edit_georeferencing(ifc, georeference)
class TestSetIfcGridNorth:
def test_run(self, georeference):
georeference.set_ifc_grid_north().should_be_called()
subject.set_ifc_grid_north(georeference)
class TestSetBlenderGridNorth:
def test_run(self, georeference):
georeference.set_blender_grid_north().should_be_called()
subject.set_blender_grid_north(georeference)
class TestSetIfcTrueNorth:
def test_run(self, georeference):
georeference.set_ifc_true_north().should_be_called()
subject.set_ifc_true_north(georeference)
class TestSetBlenderTrueNorth:
def test_run(self, georeference):
georeference.set_blender_true_north().should_be_called()
subject.set_blender_true_north(georeference)
class TestGetCursorLocation:
def test_run(self, georeference):
georeference.get_cursor_location().should_be_called().will_return("coordinates")
georeference.set_coordinates("input", "coordinates").should_be_called()
subject.get_cursor_location(georeference)
class TestSetCursorLocation:
def test_run(self, georeference):
georeference.get_coordinates("output").should_be_called().will_return("coordinates")
georeference.set_cursor_location("coordinates").should_be_called()
subject.set_cursor_location(georeference)
class TestConvertLocalToGlobal:
def test_run(self, georeference):
georeference.get_coordinates("input").should_be_called().will_return("coordinates")
georeference.xyz2enh("coordinates").should_be_called().will_return("enh")
georeference.set_coordinates("output", "enh").should_be_called()
georeference.set_cursor_location("enh").should_be_called()
subject.convert_local_to_global(georeference)
class TestConvertGlobalToLocal:
def test_run(self, georeference):
georeference.get_coordinates("input").should_be_called().will_return("coordinates")
georeference.enh2xyz("coordinates").should_be_called().will_return("xyz")
georeference.set_coordinates("output", "xyz").should_be_called()
georeference.set_cursor_location("xyz").should_be_called()
subject.convert_global_to_local(georeference)
-17
View File
@@ -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
+60 -62
View File
@@ -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,16 +49,6 @@ LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
PYVERSION:=py310
PYPI_IMP:=cp
ifeq ($(PYVERSION), py39)
PYLIBDIR:=python3.9
PYNUMBER:=39
PYPI_VERSION:=3.9
endif
ifeq ($(PYVERSION), py310)
PYLIBDIR:=python3.10
PYNUMBER:=310
PYPI_VERSION:=3.10
endif
ifeq ($(PYVERSION), py311)
PYLIBDIR:=python3.11
PYNUMBER:=311
@@ -105,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
@@ -113,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
@@ -140,6 +130,8 @@ endif
cd build && . env/$(VENV_ACTIVATE) && $(PIP) wheel odfpy --wheel-dir=./wheels
# Required by IFCCityJSON
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download cjio --dest=./wheels
# Required in general for sorting all sorts of stuff in a nice way
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download natsort --dest=./wheels
# Provides express rule validation for ifcopenshell.validate
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pytest --dest=./wheels
# Provides Brickschema functionality
@@ -191,8 +183,8 @@ else
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.
@@ -213,7 +205,7 @@ endif
# Brickschema requires pkg_resources which is provided by Blender.
# Provides Brickschema functionality
# For now lets bundle the latest nightly schema
cd build/blenderbim/bim/schema && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
cd build/bonsai/bim/schema && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
# Required for hipped roof generation
cd build && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
@@ -228,11 +220,11 @@ endif
cp -r build/bpypolyskel-master/dist/*.whl build/wheels/
# Required for Desktop icon and file association
cp -r blenderbim/libs/desktop build/blenderbim/libs/
cp -r bonsai/libs/desktop build/bonsai/libs/
# Generate translations module for BBIM build
git clone https://github.com/IfcOpenShell/blenderbim-translations.git build/working
$(PYTHON) scripts/bbim_translations.py -i "build/working" -o "build/blenderbim"
# Generate translations module for Bonsai build
git clone https://github.com/IfcOpenShell/bonsai-translations.git build/working
$(PYTHON) scripts/bonsai_translations.py -i "build/working" -o "build/bonsai"
rm -rf build/working
# Remove dependencies also bundled with Blender
@@ -240,17 +232,17 @@ endif
cp pyproject.toml build/
ifeq ($(IS_STABLE), TRUE)
$(SED) "s/0.0.0/$(VERSION)/" build/blenderbim/blender_manifest.toml
$(SED) "s/0.0.0/$(VERSION)/" build/bonsai/blender_manifest.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/blenderbim/blender_manifest.toml
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/blenderbim/__init__.py
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/bonsai/blender_manifest.toml
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/bonsai/__init__.py
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
endif
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/blenderbim/blender_manifest.toml
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/bonsai/blender_manifest.toml
# Provides BlenderBIM Add-on functionality
# Provides bonsai Add-on functionality
ifeq ($(IS_STABLE), TRUE)
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
@@ -267,7 +259,7 @@ endif
echo "Error: non-wheel dependencies are not supported by Blender!"; \
exit 1; \
fi
mv build/wheels/*.whl build/blenderbim/wheels/
mv build/wheels/*.whl build/bonsai/wheels/
# Ugly patch to accomodate MacOS universal builds not being recognized by Blender.
# Blender doesn't look into the wheel contents, it just checks the name.
@@ -276,41 +268,41 @@ endif
ifeq ($(PLATFORM), macosm1)
# Has universal and x86_64 builds.
prev_whl_name=$$(find build/blenderbim/wheels/MarkupSafe-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/MarkupSafe-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
mv "$$prev_whl_name" "$$whl_name";
# Has universal and x86_64 builds.
prev_whl_name=$$(find build/blenderbim/wheels/lxml-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/lxml-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
mv "$$prev_whl_name" "$$whl_name";
prev_whl_name=$$(find build/blenderbim/wheels/tzfpy-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/tzfpy-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2/macosx_10_12_arm64/"); \
mv "$$prev_whl_name" "$$whl_name";
# Has only universal build.
prev_whl_name=$$(find build/blenderbim/wheels/greenlet-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/greenlet-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_arm64/"); \
mv "$$prev_whl_name" "$$whl_name";
endif
ifeq ($(PLATFORM), macos)
prev_whl_name=$$(find build/blenderbim/wheels/tzfpy-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/tzfpy-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2/macosx_10_9_x86_64/"); \
mv "$$prev_whl_name" "$$whl_name";
prev_whl_name=$$(find build/blenderbim/wheels/greenlet-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/greenlet-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_x86_64/"); \
mv "$$prev_whl_name" "$$whl_name";
# has universal and amr64 builds.
prev_whl_name=$$(find build/blenderbim/wheels/fonttools-*.whl); \
prev_whl_name=$$(find build/bonsai/wheels/fonttools-*.whl); \
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_x86_64/"); \
mv "$$prev_whl_name" "$$whl_name";
endif
# Safeguard for unhandled universal files.
wheels=$$(find build/blenderbim/wheels/*universal2*.whl); \
wheels=$$(find build/bonsai/wheels/*universal2*.whl); \
if [ -n "$$wheels" ]; then \
echo "Found universal2 wheel files:"; \
echo "$$wheels"; \
@@ -321,7 +313,7 @@ endif
ifneq ($(PLATFORM), linux)
# Safeguard: in case one of `pip download` will break,
# it will produce a linux wheel for non-linux build (our github action machine is using linux).
wheels=$$(find build/blenderbim/wheels/*manylinux_*.whl); \
wheels=$$(find build/bonsai/wheels/*manylinux_*.whl); \
if [ -n "$$wheels" ]; then \
echo "Found linux wheel files in non-linux build:"; \
echo "$$wheels"; \
@@ -330,26 +322,32 @@ 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
.PHONY: test
test:
make test-core
make test-tool
make test-bim
@echo "Running all tests..."
@ERROR=0; \
make test-core || ERROR=1; \
make test-tool || ERROR=1; \
make test-bim || ERROR=1; \
if [ $$ERROR -ne 0 ]; then \
echo "One or more tests failed"; \
exit 1; \
fi
.PHONY: test-core
test-core:
@@ -378,13 +376,13 @@ qa:
.PHONY: coverage
coverage:
coverage run --source blenderbim.core -m pytest -p no:pytest-blender test/core
coverage run --source bonsai.core -m pytest -p no:pytest-blender test/core
coverage html
xdg-open htmlcov/index.html
.PHONY: license
license:
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "BlenderBIM Add-on" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "Bonsai" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
.PHONY: clean
clean:
+5
View File
@@ -0,0 +1,5 @@
# Bonsai
An add-on to Blender to allow BIM functionality.
More information on the [Bonsai website](https://bonsaibim.org).
@@ -1,25 +1,25 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
# This file is part of Bonsai.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
# Ensure we don't try to import bpy or blenderbim.bim
# Ensure we don't try to import bpy or bonsai.bim
# to support running core tests.
# We assume if bpy was never loaded in current python session
# then we're not in Blender. It's still possible to use
@@ -51,14 +51,15 @@ def get_last_commit_hash() -> Union[str, None]:
return last_commit_hash[:7]
# Accessed from blenderbim extension:
# Accessed from bonsai extension:
bbim_semver: dict[str, Any] = {}
# Accessed from blenderbim dependency:
# Accessed from bonsai dependency:
last_error = None
last_actions: deque = deque(maxlen=10)
FIRST_INSTALLED_BBIM_VERSION: Union[str, None] = None
REINSTALLED_BBIM_VERSION: Union[str, None] = None
REGISTERED_BBIM_PACKAGE: str
def initialize_bbim_semver():
@@ -82,9 +83,6 @@ def initialize_bbim_semver():
bbim_semver["version"] = version_str
initialize_bbim_semver()
def get_debug_info():
bbim_version = bbim_semver["version"]
@@ -96,8 +94,8 @@ def get_debug_info():
"machine": platform.machine(),
"processor": platform.processor(),
"blender_version": bpy.app.version_string,
"blenderbim_version": bbim_version,
"blenderbim_commit_hash": get_last_commit_hash(),
"bonsai_version": bbim_version,
"bonsai_commit_hash": get_last_commit_hash(),
"last_actions": last_actions,
"last_error": last_error,
}
@@ -129,7 +127,7 @@ def safe_link_dlls() -> None:
# Then, Blender won't have a problem unlinking unloaded dlls as they are still linked somewhere.
# On register() we clean up our temp directory with binaries.
#
# TODO: If user uninstalls BlenderBIM to never use it again, temporary directory won't be cleared.
# TODO: If user uninstalls Bonsai to never use it again, temporary directory won't be cleared.
#
# See: https://projects.blender.org/blender/blender/issues/125049
import bpy
@@ -177,6 +175,7 @@ def clean_up_dlls_safe_links() -> None:
if IN_BLENDER:
initialize_bbim_semver()
def get_binary_info() -> dict[str, Any]:
info = {}
@@ -214,7 +213,7 @@ if IN_BLENDER:
try:
import git
# We can't just use __file__ as blenderbim/__init__.py is typically not symlinked
# We can't just use __file__ as bonsai/__init__.py is typically not symlinked
# as Blender have errors symlinking main addon package file.
path = Path(__file__).resolve().parent
repo = git.Repo(str(path), search_parent_directories=True)
@@ -240,23 +239,27 @@ if IN_BLENDER:
if platform.system() == "Windows":
clean_up_dlls_safe_links()
import blenderbim.bim
import bonsai
bonsai.REGISTERED_BBIM_PACKAGE = __package__
import bonsai.bim
current_version = bbim_semver["version"]
if blenderbim.FIRST_INSTALLED_BBIM_VERSION is None:
blenderbim.FIRST_INSTALLED_BBIM_VERSION = current_version
elif not blenderbim.REINSTALLED_BBIM_VERSION and blenderbim.FIRST_INSTALLED_BBIM_VERSION != current_version:
blenderbim.REINSTALLED_BBIM_VERSION = current_version
if bonsai.FIRST_INSTALLED_BBIM_VERSION is None:
bonsai.FIRST_INSTALLED_BBIM_VERSION = current_version
elif not bonsai.REINSTALLED_BBIM_VERSION and bonsai.FIRST_INSTALLED_BBIM_VERSION != current_version:
bonsai.REINSTALLED_BBIM_VERSION = current_version
blenderbim.bim.register()
bonsai.bim.register()
def unregister():
if platform.system() == "Windows":
safe_link_dlls()
import blenderbim.bim
import bonsai.bim
blenderbim.bim.unregister()
bonsai.bim.unregister()
except:
@@ -278,10 +281,10 @@ if IN_BLENDER:
print(last_error)
print(format_debug_info(get_debug_info()))
print("\nFATAL ERROR: Unable to load the BlenderBIM Add-on")
print("\nFATAL ERROR: Unable to load Bonsai")
class BIM_PT_fatal_error(bpy.types.Panel):
bl_label = "BlenderBIM Fatal Error"
bl_label = "Bonsai Fatal Error"
bl_idname = "SCENE_PT_error_message"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
@@ -292,7 +295,7 @@ if IN_BLENDER:
layout = self.layout
layout.alert = True
layout.label(text="BlenderBIM could not load.", icon="ERROR")
layout.label(text="Bonsai could not load.", icon="ERROR")
if info["os"] == "Windows":
layout.operator("wm.console_toggle", text="View the console for full logs.", icon="CONSOLE")
else:
@@ -302,10 +305,10 @@ if IN_BLENDER:
b3d = ".".join(info["blender_version"].split(".")[0:2])
box.label(text="System Information:")
box.label(text=f"Blender {b3d} {info['os']} {info['machine']}", icon="BLENDER")
blenderbim_version = info["blenderbim_version"]
if commit_hash := info.get("blenderbim_commit_hash"):
blenderbim_version += f"-{commit_hash}"
box.label(text=f"Python {py} BBIM {info['blenderbim_version']}", icon="SCRIPTPLUGINS")
bonsai_version = info["bonsai_version"]
if commit_hash := info.get("bonsai_commit_hash"):
bonsai_version += f"-{commit_hash}"
box.label(text=f"Python {py} BBIM {info['bonsai_version']}", icon="SCRIPTPLUGINS")
binary_py = get_binary_info().get("binary_python_version")
if binary_py and py != binary_py:
@@ -313,7 +316,7 @@ if IN_BLENDER:
# From wrong-platform-build issues we're guarded by Blender extension installation.
# But Blender currently doesn't support separate builds for different Python version,
# so those issues might still slip in.
box.label(text="BlenderBIM installed for wrong Python version.")
box.label(text="Bonsai installed for wrong Python version.")
box.label(text=f"Expected: {py}. Got: {binary_py}.")
# On reinstallation, dependencies versions doesn't change, so Blender will just ignore new dependencies.
# So, we need to make user will disable an extension (just uninstallation won't remove dependencies).
@@ -321,16 +324,16 @@ if IN_BLENDER:
# as dependencies failed to load due to Python version mismatch.
box.label(text="Try reinstalling with the correct Python version.")
box.label(text="Before reinstallation make sure to")
box.label(text="DISABLE BlenderBIM (uninstallation won't help).")
box.label(text="DISABLE Bonsai (uninstallation won't help).")
box.label(text="You can download correct version below.")
layout.operator("bim.copy_debug_information", text="Copy Error Message To Clipboard")
op = layout.operator("bim.open_uri", text="How Can I Fix This?")
op.uri = "https://docs.blenderbim.org/users/troubleshooting.html#installation-issues"
op.uri = "https://docs.bonsaibim.org/users/troubleshooting.html#installation-issues"
layout.label(text="Try Reinstalling:", icon="IMPORT")
op = layout.operator("bim.open_uri", text="Re-download Add-on")
bbim_version = info["blenderbim_version"]
bbim_version = info["bonsai_version"]
py_tag = py.replace(".", "")
if "Linux" in info["os"]:
os = "linux-x64"
@@ -341,7 +344,7 @@ if IN_BLENDER:
os = "macos-x64"
else:
os = "windows-x64"
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_version}/blenderbim_py{py_tag}-{bbim_version}-{os}.zip"
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/bonsai-{bbim_version}/bonsai_py{py_tag}-{bbim_version}-{os}.zip"
class OpenUri(bpy.types.Operator):
bl_idname = "bim.open_uri"
@@ -1,20 +1,20 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
# This file is part of Bonsai.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
@@ -25,7 +25,7 @@ from . import handler, ui, prop, operator, helper
from typing import Callable, Union
try:
from blenderbim.translations import translations_dict
from bonsai.translations import translations_dict
except ImportError:
translations_dict = {}
cwd = os.path.dirname(os.path.realpath(__file__))
@@ -91,7 +91,7 @@ modules = {
for name in modules.keys():
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
modules[name] = importlib.import_module(f"bonsai.bim.module.{name}")
classes = [
@@ -103,6 +103,7 @@ classes = [
operator.BIM_OT_show_description,
operator.BIM_OT_multiple_file_selector,
operator.ClippingPlaneCutWithCappings,
operator.CloseBlendWarning,
operator.CloseError,
operator.EditBlenderCollection,
operator.FileAssociate,
@@ -253,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():
@@ -290,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.);

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