mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 20:45:59 +00:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0f2ac592e | |||
| 41f1d66fee | |||
| aa31436c67 | |||
| e95e92356f | |||
| e27980f746 | |||
| 3f3f3b6aff | |||
| 2b3035ec02 | |||
| cc0760d505 | |||
| f683fd220f | |||
| 889d3c5451 | |||
| c16cfc8afd | |||
| 4d698deb21 | |||
| 3bbe598dff | |||
| e88bd3af80 | |||
| a428ae5c86 | |||
| 05e202059e | |||
| ab97f23b47 | |||
| 4f0a65baea | |||
| e88abd881d | |||
| d6d5dbfaa3 | |||
| 3ea1f95fcf | |||
| 48026c7a50 | |||
| 8f2e8ec64d | |||
| 15945d854a | |||
| f69c42aaae | |||
| b88e35e3e7 | |||
| 6befa2dc12 | |||
| c25bb03a9c | |||
| 4fdf8145f0 | |||
| bb910e42ab | |||
| 13dce9d663 | |||
| bda69555f6 | |||
| e50f88a91c | |||
| fc7288b9e7 | |||
| 1355f3ab6b | |||
| d60c03b632 | |||
| b39020ce74 | |||
| cb23b00913 | |||
| 268ab107e2 | |||
| 734b4901fd | |||
| 0ecc3d3e2a | |||
| 237cd99e3b | |||
| 31acd0a39b | |||
| 7fb04c7d8f | |||
| ad05434777 | |||
| 99cb50d365 | |||
| d29a201814 | |||
| fb702afee6 | |||
| 657906a2dd | |||
| 5b96b4a414 | |||
| 30623ba333 | |||
| 34652aa2b7 | |||
| e7fd06a6d6 | |||
| f0077b9e22 | |||
| 71aa30a25f | |||
| d96f960313 | |||
| 5a2a809061 | |||
| 2710148276 | |||
| 7ff1c535e5 | |||
| 71575905cb | |||
| 56573160d1 | |||
| 49555eb81e | |||
| 9ed286660e | |||
| 3d2dfdf9cb | |||
| c3d4847b29 | |||
| 9bf68db080 | |||
| 798cc6069f | |||
| 0efa36940c | |||
| 6fbb1a286c | |||
| 7c606fea9b | |||
| ae97350e64 | |||
| 3e7dbcaeb6 | |||
| 4ad426a5c5 | |||
| eeb200a19d | |||
| 766eb75e3a | |||
| 607f79f2f5 | |||
| 70f252988a | |||
| f956545743 | |||
| 6f8aac96f0 | |||
| a2018a22a6 | |||
| 7a10edbd73 | |||
| 669b0b8c83 | |||
| 70336f179d | |||
| 7fc7a73db8 | |||
| 41065f77d1 | |||
| 010f854a07 | |||
| c0635afb64 | |||
| dedbf7014a | |||
| d8cf6ca4fc | |||
| f1ab63dda9 | |||
| 3a5c3d856a | |||
| 54fe0f8ff9 | |||
| 483516a902 | |||
| 83085ba3c1 | |||
| 215866cf30 | |||
| 09d5fafdf8 | |||
| 54cc4192bd | |||
| eff778d5be | |||
| 66877c227b | |||
| 7e117bca90 | |||
| 9f084558f6 | |||
| c715d9a81b | |||
| 8923d02f68 | |||
| 447a13b6e7 |
@@ -3,7 +3,7 @@ name: ci-blenderbim-daily
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-blenderbim-matrix.yml'
|
||||
- '.github/workflows/ci-blenderbim-daily.yml'
|
||||
- 'src/blenderbim/**'
|
||||
- 'src/ifcopenshell-python/ifcopenshell/**'
|
||||
- 'src/bcf/src/bcf/**'
|
||||
@@ -43,15 +43,15 @@ jobs:
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux
|
||||
short_name: linux,
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos
|
||||
short_name: macos,
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm1
|
||||
short_name: macosm1,
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -67,15 +67,19 @@ jobs:
|
||||
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
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: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
release_name: "blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
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
|
||||
|
||||
@@ -90,7 +90,10 @@ jobs:
|
||||
return
|
||||
|
||||
# sort the versions in descending order
|
||||
versions = sorted(versions, reverse=True)
|
||||
print(f"Before reversal: {versions=}")
|
||||
versions.reverse()
|
||||
print(f"After reversal: {versions=}")
|
||||
|
||||
releases = versions[number_of_supported_versions:]
|
||||
|
||||
for release in releases:
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
- name: Version + date str
|
||||
id: verdate
|
||||
run: echo "verdate='${{ steps.version.outputs.version }}.${{ steps.date.outputs.date }}'" >> $GITHUB_OUTPUT
|
||||
run: echo "verdate='${{ steps.version.outputs.version }}alpha${{ steps.date.outputs.date }}'" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
test:
|
||||
|
||||
+2
-16
@@ -16,11 +16,8 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
VERSION:=$(shell cat ../../VERSION)
|
||||
SED:=sed -i
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED:=sed -i '' -e
|
||||
endif
|
||||
PACKAGE_NAME:=bcf
|
||||
include ../common.mk
|
||||
|
||||
.PHONY: ci
|
||||
ci:
|
||||
@@ -36,17 +33,6 @@ 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
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -rf dist
|
||||
cp pyproject.toml pyproject.toml.bak
|
||||
cp src/bcf/__init__.py __init__.py.bak
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' pyproject.toml
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' src/bcf/__init__.py
|
||||
python -m build
|
||||
mv pyproject.toml.bak pyproject.toml
|
||||
mv __init__.py.bak src/bcf/__init__.py
|
||||
|
||||
# .PHONY
|
||||
# api:
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
|
||||
+194
-193
@@ -20,8 +20,12 @@ PYTHON:=python3.11
|
||||
PIP:=pip3.11
|
||||
PATCH:=patch
|
||||
SED:=sed -i
|
||||
VENV_ACTIVATE:=bin/activate
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
HOSTOS:=win
|
||||
PYTHON:=python
|
||||
VENV_ACTIVATE:=Scripts/activate
|
||||
else
|
||||
UNAME_S:=$(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
@@ -68,18 +72,22 @@ endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
|
||||
BLENDER_PLATFORM:=linux-x64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
PYPI_PLATFORM:=--platform macosx_10_10_x86_64
|
||||
BLENDER_PLATFORM:=macos-x64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
PYPI_PLATFORM:=--platform macosx_11_0_arm64
|
||||
BLENDER_PLATFORM:=macos-arm64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), win)
|
||||
PYPI_PLATFORM:=--platform win_amd64
|
||||
BLENDER_PLATFORM:=windows-x64
|
||||
endif
|
||||
|
||||
# Current build commit hash.
|
||||
@@ -95,221 +103,214 @@ dist:
|
||||
ifndef PLATFORM
|
||||
$(error PLATFORM is not set)
|
||||
endif
|
||||
rm -rf dist
|
||||
mkdir -p dist/working
|
||||
mkdir -p dist/blenderbim
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
rm -rf build
|
||||
mkdir -p build
|
||||
mkdir -p dist
|
||||
cp -r blenderbim build/
|
||||
|
||||
# To scope any build-time dependencies
|
||||
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) install wheel
|
||||
|
||||
mkdir -p build/wheels
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.9-${OLD}-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
# See bug #812
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/blenderbim/occ_utils.py
|
||||
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides dependencies that are part of IfcOpenShell
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/refs/heads/v0.8.0.zip
|
||||
cd dist/working && unzip v0.8.0.zip
|
||||
# IfcOpenBot sometimes lags behind, so we only use its binaries
|
||||
mv dist/blenderbim/libs/site/packages/ifcopenshell/*ifcopenshell_wrapper* dist/working/
|
||||
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
mv dist/working/*ifcopenshell_wrapper* dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
# Provides bcf functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCClash functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/
|
||||
# Provides BIMTester functionality
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pybabel
|
||||
cd dist/working && . env/bin/activate && $(PIP) install babel
|
||||
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.8.0/src/ifcbimtester/bimtester/locale/
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCTester functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifctester/ifctester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCFM functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
|
||||
# Provides bSDD functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/bsdd/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCDiff functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCSV functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCPatch functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
|
||||
# Provides IFC4D functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifc4d/ifc4d dist/blenderbim/libs/site/packages/
|
||||
# Provides IFC5D functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCityJSON functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifccityjson dist/blenderbim/libs/site/packages/
|
||||
# Provides IfcGit functionality
|
||||
cd dist/working && . env/bin/activate && $(PIP) install GitPython --target=./site-packages
|
||||
cp -r dist/working/site-packages/smmap dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/site-packages/gitdb dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/site-packages/git dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
cd ../ifcopenshell-python && make dist PLATFORM=$(PLATFORM)64 PYVERSION=$(PYVERSION) && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../bcf && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcclash && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcbimtester && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifctester && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcfm && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../bsdd && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcdiff && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifccsv && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifcpatch && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifc4d && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifc5d && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd ../ifccityjson && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download GitPython --dest=./wheels
|
||||
# Provides audio playback for costing
|
||||
mkdir -p dist/working
|
||||
git clone https://github.com/Andrej730/aud.git --branch master-reduced-size --depth 1 dist/working/aud
|
||||
cd dist/working/aud && git checkout 529af208fbcb707c387bfb681d2605c0b6112de7
|
||||
cp -r dist/working/aud/aud dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
# This is a REALLY IMPORTANT feature
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) wheel git+https://github.com/Andrej730/aud.git --wheel-dir=./wheels
|
||||
# IfcOpenShell dependency - support for new typing features
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download typing_extensions --dest=./wheels
|
||||
# Required by IfcCSV
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pandas $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Pandas ships with very heavy tests
|
||||
# cd build/site-packages/pandas && rm -rf tests
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download openpyxl --dest=./wheels
|
||||
# odfpy doesn't come with its own wheel, so whee'l (get it?) create it ourselves!
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) wheel odfpy --wheel-dir=./wheels
|
||||
# Required by IFCCityJSON
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download cjio --dest=./wheels
|
||||
# Provides express rule validation for ifcopenshell.validate
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pytest --dest=./wheels
|
||||
# Provides Brickschema functionality
|
||||
# Is not platform specific but has platform specific dependencies.
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download "brickschema[persistence]==0.7.6a2" $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Required for SVG to DXF conversion
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download ezdxf $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Required by bcf
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download xsdata --dest=./wheels
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download xmlschema --dest=./wheels
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download elementpath --dest=./wheels
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download six --dest=./wheels
|
||||
# Required by drawing module
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download lxml $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Required by qto and drawing module
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download shapely $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Required by the BIM type manager thumbnail generator
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pillow $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Provides mustache templating in construction docs and web UI data
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pystache --dest=./wheels
|
||||
# Provides SVG export in construction documentation
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download svgwrite --dest=./wheels
|
||||
# Provides fuzzy date parsing for construction sequencing
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download python-dateutil --dest=./wheels
|
||||
# Provides duration parsing for construction sequencing
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download isodate --dest=./wheels
|
||||
# Provides networkx graph analysis for project dependency calculations
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download networkx --dest=./wheels
|
||||
# Required by IFCDiff
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download deepdiff --dest=./wheels
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download lark --dest=./wheels
|
||||
# Required by IFC4D
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download PyP6Xer --dest=./wheels
|
||||
# Required by web module
|
||||
# Is not platform specific but has platform specific dependencies.
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download python-socketio[asyncio_client] $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download aiohttp $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# Required by light module
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pytz --dest=./wheels
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download tzfpy $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
# pyradiance is using different platform versions than defaults in our makefile.
|
||||
ifeq ($(PLATFORM), linux)
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance --platform manylinux_2_35_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
else ifeq ($(PLATFORM), macos)
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance --platform macosx_10_13_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
else
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
endif
|
||||
|
||||
# Provides jsgantt-improved supports for web-based construction sequencing gantt charts
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
|
||||
rm -rf dist/working
|
||||
|
||||
mkdir dist/working
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && mkdir site-packages
|
||||
# IfcOpenShell dependency - support for new typing features
|
||||
cd dist/working && . env/bin/activate && $(PIP) install typing_extensions --target=./site-packages
|
||||
# Required by IfcCSV
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pandas $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Pandas ships with very heavy tests
|
||||
cd dist/working/site-packages/pandas && rm -rf tests
|
||||
cd dist/working && . env/bin/activate && $(PIP) install openpyxl --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install odfpy --target=./site-packages
|
||||
# Required by IFCCityJSON
|
||||
cd dist/working && . env/bin/activate && $(PIP) install cjio --target=./site-packages
|
||||
# Provides express rule validation for ifcopenshell.validate
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pytest --target=./site-packages
|
||||
# Provides Brickschema functionality
|
||||
cd dist/working && . env/bin/activate && $(PIP) install "brickschema[persistence]==0.7.6a2" --target=./site-packages
|
||||
# Required for SVG to DXF conversion
|
||||
cd dist/working && . env/bin/activate && $(PIP) install ezdxf --target=./site-packages
|
||||
# Required by bcf
|
||||
cd dist/working && . env/bin/activate && $(PIP) install xsdata --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install xmlschema --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install elementpath --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install six --target=./site-packages
|
||||
# Required by drawing module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install lxml $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Required by qto and drawing module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install shapely $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Required by the BIM type manager thumbnail generator
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pillow $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
# Provides mustache templating in construction docs and web UI data
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pystache --target=./site-packages
|
||||
# Provides SVG export in construction documentation
|
||||
cd dist/working && . env/bin/activate && $(PIP) install svgwrite --target=./site-packages
|
||||
# Provides fuzzy date parsing for construction sequencing
|
||||
cd dist/working && . env/bin/activate && $(PIP) install python-dateutil --target=./site-packages
|
||||
# Provides duration parsing for construction sequencing
|
||||
cd dist/working && . env/bin/activate && $(PIP) install isodate --target=./site-packages
|
||||
# Provides networkx graph analysis for project dependency calculations
|
||||
cd dist/working && . env/bin/activate && $(PIP) install networkx --target=./site-packages
|
||||
# Required by IFCDiff
|
||||
cd dist/working && . env/bin/activate && $(PIP) install deepdiff --target=./site-packages
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
cd dist/working && . env/bin/activate && $(PIP) install lark --target=./site-packages
|
||||
# Required by IFC4D
|
||||
cd dist/working && . env/bin/activate && $(PIP) install PyP6Xer --target=./site-packages
|
||||
# Required by web module
|
||||
cd dist/working && . env/bin/activate && $(PIP) install python-socketio[asyncio_client] --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install aiohttp --target=./site-packages
|
||||
# Required by light module
|
||||
ifeq ($(PLATFORM), linux)
|
||||
# See https://github.com/LBNL-ETA/pyradiance/issues/34
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pyradiance --platform manylinux1_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
else ifneq ($(PLATFORM), macos)
|
||||
# No support for MacOS Intel from upstream. Too bad :(
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
endif
|
||||
|
||||
cp -r dist/working/site-packages/* dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by BIMTester
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
|
||||
cd dist/working && tar -xzvf behave*
|
||||
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
|
||||
# See bug #1294
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/model_core.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/runner_util.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/model_core.py < model_core.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
|
||||
# See bug #2159
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/junit.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/reporter/junit.py < junit.patch
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by behave
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
|
||||
cd dist/working && tar -xzvf parse*
|
||||
cd dist/working/parse-1.15.0/ && cp parse.py ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by behave
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/2e/79/81bebd1b0446d46733db99d74543b4bb80646ef4c988584bae0862e706bc/parse_type-0.5.2.tar.gz
|
||||
cd dist/working && tar -xzvf parse_type*
|
||||
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
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
|
||||
|
||||
# Provides IFCJSON functionality
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
||||
cd dist/working && unzip master.zip
|
||||
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
cd build && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
||||
cd build && unzip master.zip && rm master.zip
|
||||
# IFCJSON doesn't have pyproject.toml, so we use python command.
|
||||
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-master/file_converters && \
|
||||
$(PYTHON) -c "from setuptools import setup; \
|
||||
setup( \
|
||||
name='ifcjson', \
|
||||
version='0.0.0', \
|
||||
author='Jan Brouwer', \
|
||||
author_email='jan@brewsky.nl', \
|
||||
packages=['ifcjson'], \
|
||||
)" bdist_wheel
|
||||
cp -r build/IFC2JSON_python-master/file_converters/dist/*.whl build/wheels/
|
||||
|
||||
# Brickschema requires pkg_resources which is provided by Blender.
|
||||
# Provides Brickschema functionality
|
||||
mkdir dist/working
|
||||
# For now lets bundle the latest nightly schema
|
||||
cd dist/working && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||
cd dist/working && cp Brick.ttl ../blenderbim/bim/schema/Brick.ttl
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by brickschema
|
||||
# This is a bit of a dodgy one, it should be provided by setuptools which Blender doesn't ship.
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/raw/v0.8.0/src/blenderbim/scripts/pkg_resources.tar.gz
|
||||
cd dist/working && tar -xzvf pkg_resources*
|
||||
cd dist/working/ && cp -r pkg_resources ../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
cd build/blenderbim/bim/schema && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||
|
||||
# Required for hipped roof generation
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
|
||||
cd dist/working && unzip master.zip
|
||||
cp -r dist/working/bpypolyskel-master/bpypolyskel dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
cd build && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
|
||||
cd build && unzip master.zip && rm master.zip
|
||||
cd build && . env/$(VENV_ACTIVATE) && cd bpypolyskel-master && \
|
||||
$(PYTHON) -c "from setuptools import setup; \
|
||||
setup( \
|
||||
name='bpypolyskel', \
|
||||
version='0.0.0', \
|
||||
packages=['bpypolyskel'], \
|
||||
)" bdist_wheel
|
||||
cp -r build/bpypolyskel-master/dist/*.whl build/wheels/
|
||||
|
||||
# Required for Desktop icon and file association
|
||||
cp -r blenderbim/libs/desktop dist/blenderbim/libs/
|
||||
cp -r blenderbim/libs/desktop build/blenderbim/libs/
|
||||
|
||||
# Generate translations module for BBIM build
|
||||
git clone https://github.com/IfcOpenShell/blenderbim-translations.git dist/working
|
||||
$(PYTHON) scripts/bbim_translations.py -i "dist/working" -o "dist/blenderbim"
|
||||
rm -rf dist/working
|
||||
git clone https://github.com/IfcOpenShell/blenderbim-translations.git build/working
|
||||
$(PYTHON) scripts/bbim_translations.py -i "build/working" -o "build/blenderbim"
|
||||
rm -rf build/working
|
||||
|
||||
# Remove dependencies also bundled with Blender
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy.libs
|
||||
rm -rf build/wheels/numpy*
|
||||
|
||||
cp pyproject.toml build/
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
$(SED) "s/0.0.0/$(VERSION)/" build/blenderbim/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/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
|
||||
endif
|
||||
|
||||
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/blenderbim/blender_manifest.toml
|
||||
|
||||
# Provides BlenderBIM Add-on functionality
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
||||
cp build/dist/*.whl build/wheels/
|
||||
mv build/wheels/*.whl build/blenderbim/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.
|
||||
# So this hack works without working out some way to repack those wheels.
|
||||
# See: https://projects.blender.org/blender/blender/issues/125091
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
# Has universal and x86_64 builds.
|
||||
prev_whl_name=$$(find build/blenderbim/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); \
|
||||
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); \
|
||||
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); \
|
||||
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); \
|
||||
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); \
|
||||
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); \
|
||||
whl_name=$$(echo $$prev_whl_name | sed "s/_universal2/_x86_64/"); \
|
||||
mv "$$prev_whl_name" "$$whl_name";
|
||||
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/
|
||||
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
cd dist/blenderbim && $(SED) "s/0, 0, 0/$(VERSION_MAJOR), $(VERSION_MINOR), $(VERSION_PATCH)/" __init__.py
|
||||
cd dist/blenderbim && $(SED) "s/0.0.0/$(VERSION)/" blender_manifest.toml
|
||||
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||
cd build && zip -r blenderbim_$(PYVERSION)-$(VERSION)-$(BLENDER_PLATFORM).zip ./blenderbim
|
||||
else
|
||||
cd dist/blenderbim && $(SED) "s/0, 0, 0/$(VERSION_MAJOR), $(VERSION_MINOR), $(VERSION_PATCH), $(VERSION_DATE)/" __init__.py
|
||||
cd dist/blenderbim && $(SED) "s/0.0.0/$(VERSION).$(VERSION_DATE)/" blender_manifest.toml
|
||||
cd dist/blenderbim/bim && $(SED) "s/8888888/$(LAST_COMMIT_HASH)/" __init__.py
|
||||
cd dist && zip -r blenderbim-$(VERSION).$(VERSION_DATE)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||
cd build && zip -r blenderbim_$(PYVERSION)-$(VERSION)-alpha$(VERSION_DATE)-$(BLENDER_PLATFORM).zip ./blenderbim
|
||||
endif
|
||||
rm -rf dist/blenderbim
|
||||
|
||||
mv build/blenderbim*.zip dist/
|
||||
|
||||
rm -rf build
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
||||
@@ -28,24 +28,27 @@ IN_BLENDER = sys.modules.get("bpy", None)
|
||||
if IN_BLENDER:
|
||||
import bpy
|
||||
import addon_utils
|
||||
|
||||
import re
|
||||
import platform
|
||||
import traceback
|
||||
import webbrowser
|
||||
from collections import deque
|
||||
from pathlib import Path
|
||||
from typing import Union, Any
|
||||
|
||||
|
||||
last_commit_hash = "8888888"
|
||||
|
||||
|
||||
def get_last_commit_hash() -> Union[str, None]:
|
||||
# Using this weird way to write 8888888,
|
||||
# so makefile won't accidentally replace it here
|
||||
# we'll be able to distinguish commit hash from placeholder value.
|
||||
if last_commit_hash == str(8_888888):
|
||||
return None
|
||||
return last_commit_hash[:7]
|
||||
|
||||
# NOTE: bl_info is superseded by blender_manifest.toml
|
||||
# if addon is installed as an extension (Blender 4.2+)
|
||||
bl_info = {
|
||||
"name": "BlenderBIM",
|
||||
"description": "Transforms Blender into a native Building Information Model authoring platform using IFC.",
|
||||
"author": "IfcOpenShell Contributors",
|
||||
"blender": (3, 1, 0),
|
||||
"version": (0, 0, 0),
|
||||
"location": "File Menu, Scene Properties Tab. See documentation for more.",
|
||||
"doc_url": "https://docs.blenderbim.org/",
|
||||
"tracker_url": "https://github.com/IfcOpenShell/IfcOpenShell/issues",
|
||||
"category": "System",
|
||||
}
|
||||
|
||||
last_error = None
|
||||
last_actions: deque = deque(maxlen=10)
|
||||
@@ -62,6 +65,7 @@ def get_debug_info():
|
||||
][0]
|
||||
]
|
||||
)
|
||||
|
||||
return {
|
||||
"os": platform.system(),
|
||||
"os_version": platform.version(),
|
||||
@@ -71,6 +75,7 @@ def get_debug_info():
|
||||
"processor": platform.processor(),
|
||||
"blender_version": bpy.app.version_string,
|
||||
"blenderbim_version": version,
|
||||
"blenderbim_commit_hash": get_last_commit_hash(),
|
||||
"last_actions": last_actions,
|
||||
"last_error": last_error,
|
||||
}
|
||||
@@ -88,10 +93,51 @@ def format_debug_info(info: dict):
|
||||
|
||||
|
||||
if IN_BLENDER:
|
||||
# Process *.pth in /libs/site/packages to setup globally importable modules
|
||||
# This is 3 levels deep as required by the static RPATH of ../../ from dependencies taken from Anaconda
|
||||
# site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
|
||||
|
||||
def get_binary_info() -> dict[str, Any]:
|
||||
info = {}
|
||||
py_version = sys.version_info
|
||||
site_path = (
|
||||
Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||
/ ".local"
|
||||
/ "lib"
|
||||
/ f"python{py_version.major}.{py_version.minor}"
|
||||
/ "site-packages"
|
||||
)
|
||||
lib = site_path / "ifcopenshell"
|
||||
binary = next((i for i in lib.glob("_ifcopenshell_wrapper.*")), None)
|
||||
if binary is None:
|
||||
info["binary_error"] = "Couldn't find ifcopenshell wrapper binary."
|
||||
return info
|
||||
|
||||
# Examples:
|
||||
# _ifcopenshell_wrapper.cp311-win_amd64.pyd
|
||||
# _ifcopenshell_wrapper.cpython-311-darwin.so
|
||||
# _ifcopenshell_wrapper.cpython-311-x86_64-linux-gnu.so
|
||||
binary = binary.name
|
||||
info["binary_file_name"] = binary
|
||||
pattern = re.compile(r"cp(\d+)|cpython-(\d+)")
|
||||
match = pattern.search(binary)
|
||||
if not match:
|
||||
info["binary_error"] = f"Couldn't parse binary version from '{binary}'."
|
||||
return info
|
||||
|
||||
version = match.group(1) or match.group(2)
|
||||
version = f"{version[0]}.{version[1:]}"
|
||||
info["binary_python_version"] = version
|
||||
return info
|
||||
|
||||
try:
|
||||
import git
|
||||
|
||||
# We can't just use __file__ as blenderbim/__init__.py is typically not symlinked
|
||||
# as Blender have errors symlinking main addon package file.
|
||||
path = Path(__file__).parent / "bim" / "__init__.py"
|
||||
path = path.resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
last_commit_hash = repo.head.object.hexsha
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
import ifcopenshell.api
|
||||
@@ -108,14 +154,6 @@ if IN_BLENDER:
|
||||
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
|
||||
|
||||
def register():
|
||||
global BLENDER_PACKAGE_NAME, __name__, __package__
|
||||
BLENDER_PACKAGE_NAME = __name__
|
||||
if bpy.app.version >= (4, 2, 0):
|
||||
sys.modules["blenderbim"] = sys.modules[__name__]
|
||||
__name__ = "blenderbim"
|
||||
# Only renaming __name__ is actually required to make imports work.
|
||||
# We renaming __package__ just for consistency.
|
||||
__package__ = "blenderbim"
|
||||
import blenderbim.bim
|
||||
|
||||
blenderbim.bim.register()
|
||||
@@ -126,6 +164,21 @@ if IN_BLENDER:
|
||||
blenderbim.bim.unregister()
|
||||
|
||||
except:
|
||||
|
||||
def show_scene_properties() -> None:
|
||||
# By default in Blender object properties are selected.
|
||||
# Or user may have some other properties selected in their startup file.
|
||||
# Select scene properties to ensure user will see our error handler.
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type != "PROPERTIES":
|
||||
continue
|
||||
space = area.spaces.active
|
||||
assert isinstance(space, bpy.types.SpaceProperties)
|
||||
space.context = "SCENE"
|
||||
|
||||
# Use a timer as we're not allowed to make changes to data during register().
|
||||
bpy.app.timers.register(show_scene_properties, first_interval=0.1)
|
||||
|
||||
last_error = traceback.format_exc()
|
||||
|
||||
print(last_error)
|
||||
@@ -152,15 +205,28 @@ if IN_BLENDER:
|
||||
box = layout.box()
|
||||
py = ".".join(info["python_version"].split(".")[0:2])
|
||||
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")
|
||||
|
||||
binary_py = get_binary_info().get("binary_python_version")
|
||||
if binary_py and py != binary_py:
|
||||
box.separator()
|
||||
box.label(text="BlenderBIM installed for wrong Python version.")
|
||||
box.label(text=f"Expected: {py}. Got: {binary_py}.")
|
||||
box.label(text="Try reinstalling with the correct Python version.")
|
||||
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"
|
||||
|
||||
layout.label(text="Try Reinstalling:", icon="IMPORT")
|
||||
op = layout.operator("bim.open_uri", text="Re-download Add-on")
|
||||
bbim_date = info["blenderbim_version"].split(".")[-1]
|
||||
bbim_version = info["blenderbim_version"]
|
||||
py_tag = py.replace(".", "")
|
||||
if "Linux" in info["os"]:
|
||||
os = "linux"
|
||||
@@ -171,7 +237,7 @@ if IN_BLENDER:
|
||||
os = "macos"
|
||||
else:
|
||||
os = "win"
|
||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_date}/blenderbim-{bbim_date}-py{py_tag}-{os}.zip"
|
||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_version}/blenderbim-{bbim_version}-py{py_tag}-{os}.zip"
|
||||
|
||||
class OpenUri(bpy.types.Operator):
|
||||
bl_idname = "bim.open_uri"
|
||||
@@ -188,7 +254,9 @@ if IN_BLENDER:
|
||||
bl_description = "Copies debugging information to your clipboard for use in bugreports"
|
||||
|
||||
def execute(self, context):
|
||||
info = format_debug_info(get_debug_info())
|
||||
info = get_debug_info()
|
||||
info.update(get_binary_info())
|
||||
info = format_debug_info(info)
|
||||
context.window_manager.clipboard = info
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import os
|
||||
import bpy
|
||||
import bpy.utils.previews
|
||||
import blenderbim
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from . import handler, ui, prop, operator, helper
|
||||
@@ -75,7 +74,6 @@ modules = {
|
||||
"clash": None,
|
||||
"csv": None,
|
||||
"tester": None,
|
||||
"bimtester": None,
|
||||
"diff": None,
|
||||
"patch": None,
|
||||
"gis": None,
|
||||
@@ -169,6 +167,8 @@ classes = [
|
||||
# Services and systems
|
||||
ui.BIM_PT_tab_services,
|
||||
ui.BIM_PT_tab_zones,
|
||||
ui.BIM_PT_tab_solar_analysis,
|
||||
ui.BIM_PT_tab_lighting,
|
||||
# Structural analysis
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
@@ -253,17 +253,6 @@ def register():
|
||||
icon_preview.load(icon_name, icon_path, "IMAGE")
|
||||
|
||||
icons = icon_preview
|
||||
|
||||
global last_commit_hash
|
||||
try:
|
||||
import git
|
||||
|
||||
path = Path(__file__).resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
last_commit_hash = repo.head.object.hexsha
|
||||
except:
|
||||
pass
|
||||
|
||||
bpy.app.translations.register("blenderbim", translations_dict)
|
||||
|
||||
|
||||
|
||||
@@ -178,8 +178,8 @@ class IfcExporter:
|
||||
][0]
|
||||
]
|
||||
)
|
||||
if blenderbim.bim.last_commit_hash != "8888888":
|
||||
version += f"-{blenderbim.bim.last_commit_hash[:7]}"
|
||||
if commit_hash := tool.Blender.get_last_commit_hash():
|
||||
version += f"-{commit_hash}"
|
||||
return version
|
||||
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, tool.Ifc.Operato
|
||||
for part in parts:
|
||||
pset = ifcopenshell.util.element.get_pset(part, "BBIM_Linked_Aggregate")
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=part, pset=pset)
|
||||
|
||||
linked_aggregate_group = [
|
||||
r.RelatingGroup
|
||||
|
||||
@@ -1,42 +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
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExecuteBIMTester,
|
||||
operator.BIMTesterPurge,
|
||||
operator.SelectFeature,
|
||||
operator.SelectSteps,
|
||||
operator.SelectBIMTesterIfcFile,
|
||||
operator.RejectElement,
|
||||
operator.ApproveClass,
|
||||
operator.RejectClass,
|
||||
operator.SelectAudited,
|
||||
prop.BimTesterProperties,
|
||||
ui.BIM_PT_qa,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BimTesterProperties = bpy.props.PointerProperty(type=prop.BimTesterProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BimTesterProperties
|
||||
@@ -1,238 +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 os
|
||||
import bpy
|
||||
import tempfile
|
||||
import webbrowser
|
||||
|
||||
try:
|
||||
import bimtester
|
||||
import bimtester.run
|
||||
import bimtester.reports
|
||||
except:
|
||||
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
|
||||
|
||||
from pathlib import Path
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class ExecuteBIMTester(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_bim_tester"
|
||||
bl_label = "Execute BIMTester"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BimTesterProperties
|
||||
return (props.ifc_file or props.should_load_from_memory) and props.feature
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BimTesterProperties
|
||||
|
||||
with tempfile.TemporaryDirectory() as dirpath:
|
||||
report = os.path.join(dirpath, "{}.html".format(props.feature))
|
||||
args = {
|
||||
"action": "run",
|
||||
"advanced_arguments": "",
|
||||
"console": False,
|
||||
"feature": props.feature,
|
||||
"ifc": props.ifc_file,
|
||||
"path": "",
|
||||
"report": report,
|
||||
"schema_file": "",
|
||||
"schema_name": "",
|
||||
"lang": "en",
|
||||
"steps": props.steps,
|
||||
}
|
||||
use_stored_ifc = props.should_load_from_memory and IfcStore.get_file()
|
||||
runner = bimtester.run.TestRunner(args["ifc"], ifc=IfcStore.get_file() if use_stored_ifc else None)
|
||||
report_json = runner.run(args)
|
||||
bimtester.reports.ReportGenerator().generate(report_json, args["report"])
|
||||
webbrowser.open("file://" + report)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIMTesterPurge(bpy.types.Operator):
|
||||
bl_idname = "bim.bim_tester_purge"
|
||||
bl_label = "Purge Tests"
|
||||
|
||||
def execute(self, context):
|
||||
cwd = os.getcwd()
|
||||
os.chdir(context.scene.BimTesterProperties.features_dir)
|
||||
bimtester.clean.TestPurger().purge()
|
||||
os.chdir(cwd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectFeature(bpy.types.Operator):
|
||||
bl_idname = "bim.select_feature"
|
||||
bl_label = "Select Feature / IDS"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".feature"
|
||||
filter_glob: bpy.props.StringProperty(default="*.feature;*.xml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BimTesterProperties.feature = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectSteps(bpy.types.Operator):
|
||||
bl_idname = "bim.select_steps"
|
||||
bl_label = "Select Steps"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".py"
|
||||
filter_glob: bpy.props.StringProperty(default="*.py", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BimTesterProperties.steps = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectBIMTesterIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_bimtester_ifc_file"
|
||||
bl_label = "Select BIMTester IFC File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".ifc"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BimTesterProperties.ifc_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class RejectElement(bpy.types.Operator):
|
||||
bl_idname = "bim.reject_element"
|
||||
bl_label = "Reject Element"
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
lines.append(
|
||||
" * The element {} should not exist because {}".format(
|
||||
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
|
||||
context.scene.BimTesterProperties.qa_reject_element_reason,
|
||||
)
|
||||
)
|
||||
QAHelper.append_to_scenario(lines, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ApproveClass(bpy.types.Operator):
|
||||
bl_idname = "bim.approve_class"
|
||||
bl_label = "Approve Class"
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
lines.append(" * The element {} is an {}".format(element.GlobalId, element.is_a()))
|
||||
QAHelper.append_to_scenario(lines, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RejectClass(bpy.types.Operator):
|
||||
bl_idname = "bim.reject_class"
|
||||
bl_label = "Reject Class"
|
||||
|
||||
def execute(self, context):
|
||||
lines = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
lines.append(
|
||||
" * The element {} is an {}".format(
|
||||
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
|
||||
context.scene.BimTesterProperties.audit_ifc_class,
|
||||
)
|
||||
)
|
||||
QAHelper.append_to_scenario(lines, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectAudited(bpy.types.Operator):
|
||||
bl_idname = "bim.select_audited"
|
||||
bl_label = "Select Audited"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
audited_global_ids = []
|
||||
self.file = IfcStore.get_file()
|
||||
for filename in Path(context.scene.BimTesterProperties.features_dir).glob("*.feature"):
|
||||
with open(filename, "r") as feature_file:
|
||||
lines = feature_file.readlines()
|
||||
for line in lines:
|
||||
words = line.strip().split()
|
||||
for word in words:
|
||||
if self.is_a_global_id(word):
|
||||
audited_global_ids.append(word)
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
if self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId in audited_global_ids:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
def is_a_global_id(self, word):
|
||||
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
|
||||
|
||||
|
||||
class QAHelper:
|
||||
@classmethod
|
||||
def append_to_scenario(cls, lines, context):
|
||||
filename = os.path.join(
|
||||
context.scene.BimTesterProperties.features_dir,
|
||||
context.scene.BimTesterProperties.features_file + ".feature",
|
||||
)
|
||||
if os.path.exists(filename + "~"):
|
||||
os.remove(filename + "~")
|
||||
os.rename(filename, filename + "~")
|
||||
with open(filename, "w") as destination:
|
||||
with open(filename + "~", "r") as source:
|
||||
is_in_scenario = False
|
||||
for source_line in source:
|
||||
if (
|
||||
"Scenario: " in source_line
|
||||
and context.scene.BimTesterProperties.scenario == source_line.strip()[len("Scenario: ") :]
|
||||
):
|
||||
is_in_scenario = True
|
||||
elif is_in_scenario:
|
||||
for line in lines:
|
||||
destination.write(line + "\n")
|
||||
is_in_scenario = False
|
||||
destination.write(source_line)
|
||||
os.remove(filename + "~")
|
||||
@@ -1,71 +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 os
|
||||
from pathlib import Path
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from blenderbim.bim.module.root.prop import get_ifc_classes
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
scenarios_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global scenarios_enum
|
||||
scenarios_enum = []
|
||||
|
||||
|
||||
def getScenarios(self, context):
|
||||
global scenarios_enum
|
||||
if len(scenarios_enum) < 1:
|
||||
scenarios_enum.clear()
|
||||
|
||||
if context.scene.BimTesterProperties.feature != "":
|
||||
with open(context.scene.BimTesterProperties.feature, "r") as feature_file:
|
||||
lines = feature_file.readlines()
|
||||
for line in lines:
|
||||
if "Scenario:" in line:
|
||||
s = line.strip()[len("Scenario: ") :]
|
||||
scenarios_enum.append((s, s, ""))
|
||||
return scenarios_enum
|
||||
|
||||
|
||||
def refreshScenarios(self, context):
|
||||
global scenarios_enum
|
||||
scenarios_enum.clear()
|
||||
getScenarios(self, context)
|
||||
|
||||
|
||||
class BimTesterProperties(PropertyGroup):
|
||||
feature: StringProperty(default="", name="Feature", update=refreshScenarios)
|
||||
steps: StringProperty(default="", name="Custom Steps")
|
||||
ifc_file: StringProperty(default="", name="IFC File")
|
||||
audit_ifc_class: EnumProperty(items=get_ifc_classes, name="Audit Class")
|
||||
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||
scenario: EnumProperty(items=getScenarios, name="Scenario")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
@@ -1,81 +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/>.
|
||||
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_qa(Panel):
|
||||
bl_label = "BIMTester"
|
||||
bl_idname = "BIM_PT_qa"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_quality_control"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.use_property_split = True
|
||||
|
||||
props = context.scene.BimTesterProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "ifc_file")
|
||||
row.operator("bim.select_bimtester_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "feature")
|
||||
row.operator("bim.select_feature", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "steps")
|
||||
row.operator("bim.select_steps", icon="FILE_FOLDER", text="")
|
||||
|
||||
has_ifc_file = (IfcStore.get_file() and props.should_load_from_memory) or (
|
||||
props.ifc_file and not props.should_load_from_memory
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.execute_bim_tester")
|
||||
row.operator("bim.bim_tester_purge")
|
||||
|
||||
if props.feature and has_ifc_file:
|
||||
self.layout.label(text="Scenario Authoring:")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "scenario")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "qa_reject_element_reason")
|
||||
row = self.layout.row()
|
||||
row.operator("bim.reject_element")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "audit_ifc_class")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.approve_class")
|
||||
row.operator("bim.reject_class")
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.select_audited")
|
||||
@@ -410,6 +410,7 @@ class AnnotationData:
|
||||
|
||||
results = [("0", "Untyped", "")]
|
||||
results.extend([(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in relating_types])
|
||||
results.sort(key=lambda x: x[1].lower())
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -36,7 +36,10 @@ import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.drawing as core
|
||||
import blenderbim.bim.module.drawing.svgwriter as svgwriter
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import blenderbim.core.type
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import WorkSpaceTool
|
||||
|
||||
@@ -1000,7 +1000,8 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
if r.is_a("IfcRelAssignsToGroup")
|
||||
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
|
||||
]
|
||||
tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], products=[new[0]])
|
||||
if linked_aggregate_group:
|
||||
tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], products=[new[0]])
|
||||
|
||||
|
||||
class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro):
|
||||
@@ -1282,8 +1283,9 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
pset = ifcopenshell.util.element.get_pset(part, self.pset_name)
|
||||
except:
|
||||
continue
|
||||
index = pset["Index"]
|
||||
original_names[group][index] = tool.Ifc.get_object(part).name
|
||||
if pset:
|
||||
index = pset["Index"]
|
||||
original_names[group][index] = tool.Ifc.get_object(part).name
|
||||
|
||||
return original_names
|
||||
|
||||
|
||||
@@ -37,10 +37,6 @@ classes = (
|
||||
operator.ImportPlot,
|
||||
operator.RemoveGeoreferencing,
|
||||
operator.RemoveTrueNorth,
|
||||
operator.SetBlenderGridNorth,
|
||||
operator.SetBlenderTrueNorth,
|
||||
operator.SetIfcGridNorth,
|
||||
operator.SetIfcTrueNorth,
|
||||
prop.BIMGeoreferenceProperties,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_gis_blender,
|
||||
|
||||
@@ -119,16 +119,11 @@ class GeoreferenceData:
|
||||
def true_north(cls):
|
||||
ifc = tool.Ifc.get()
|
||||
true_north = {}
|
||||
|
||||
if ifc.schema == "IFC2X3":
|
||||
return
|
||||
|
||||
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if not context.TrueNorth:
|
||||
continue
|
||||
true_north = context.TrueNorth.DirectionRatios
|
||||
break
|
||||
|
||||
return true_north
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -73,26 +73,6 @@ class DisableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.disable_editing_georeferencing(tool.Georeference)
|
||||
|
||||
|
||||
class SetIfcGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_ifc_grid_north"
|
||||
bl_label = "Set IFC Grid North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set IFC grid north based on current Blender North Offset"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_ifc_grid_north(tool.Georeference)
|
||||
|
||||
|
||||
class SetBlenderGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_blender_grid_north"
|
||||
bl_label = "Set Blender Grid North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set Blender North Offset based on current IFC grid north"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_blender_grid_north(tool.Georeference)
|
||||
|
||||
|
||||
class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.get_cursor_location"
|
||||
bl_label = "Get Cursor Location"
|
||||
@@ -107,26 +87,6 @@ class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.get_cursor_location(tool.Georeference)
|
||||
|
||||
|
||||
class SetIfcTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_ifc_true_north"
|
||||
bl_label = "Set IFC True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set IFC true north based on current Blender North Offset"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_ifc_true_north(tool.Georeference)
|
||||
|
||||
|
||||
class SetBlenderTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_blender_true_north"
|
||||
bl_label = "Set Blender True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set Blender North Offset based on current IFC true north"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_blender_true_north(tool.Georeference)
|
||||
|
||||
|
||||
class ConvertLocalToGlobal(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_local_to_global"
|
||||
bl_label = "Convert Local To Global"
|
||||
|
||||
@@ -74,10 +74,6 @@ class BIM_PT_gis(Panel):
|
||||
row.prop(
|
||||
props, "x_axis_is_null", icon="RADIOBUT_OFF" if props.x_axis_is_null else "RADIOBUT_ON", text=""
|
||||
)
|
||||
if hasattr(context.scene, "sun_pos_properties"):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_grid_north", text="Set Blender North")
|
||||
else:
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
@@ -160,10 +156,6 @@ class BIM_PT_gis_true_north(Panel):
|
||||
row.prop(self.props, "true_north_abscissa", text="Abscissa")
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "true_north_ordinate", text="Ordinate")
|
||||
if hasattr(context.scene, "sun_pos_properties"):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.set_ifc_true_north", text="Set IFC North")
|
||||
row.operator("bim.set_blender_true_north", text="Set Blender North")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_true_north", icon="CHECKMARK")
|
||||
|
||||
@@ -22,13 +22,21 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExportOBJ,
|
||||
operator.ImportLatLong,
|
||||
operator.ImportTrueNorth,
|
||||
operator.MoveSunPathTo3DCursor,
|
||||
operator.RadianceRender,
|
||||
operator.ViewFromSun,
|
||||
prop.BIMSolarProperties,
|
||||
prop.RadianceExporterProperties,
|
||||
ui.BIM_PT_radiance_exporter,
|
||||
ui.BIM_PT_solar,
|
||||
)
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.radiance_exporter_properties = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
|
||||
bpy.types.Scene.BIMSolarProperties = bpy.props.PointerProperty(type=prop.BIMSolarProperties)
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.radiance_exporter_properties
|
||||
del bpy.types.Scene.radiance_exporter_properties
|
||||
del bpy.types.Scene.BIMSolarProperties
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 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/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.geolocation
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
def refresh():
|
||||
SolarData.is_loaded = False
|
||||
|
||||
|
||||
class SolarData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {
|
||||
"sites": cls.sites(),
|
||||
"true_north": cls.true_north(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def sites(cls):
|
||||
results = []
|
||||
for site in tool.Ifc.get().by_type("IfcSite"):
|
||||
if site.RefLatitude and site.RefLongitude:
|
||||
name = site.Name or "Unnamed"
|
||||
description = site.LongName or site.Description or ""
|
||||
lat = round(ifcopenshell.util.geolocation.dms2dd(*site.RefLatitude), 3)
|
||||
long = round(ifcopenshell.util.geolocation.dms2dd(*site.RefLongitude), 3)
|
||||
results.append((str(site.id()), f"{name} - {lat},{long}", description))
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def true_north(cls):
|
||||
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if not context.TrueNorth:
|
||||
continue
|
||||
value = context.TrueNorth.DirectionRatios
|
||||
return round(ifcopenshell.util.geolocation.yaxis2angle(*value[:2]), 4)
|
||||
@@ -0,0 +1,190 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import SpaceView3D
|
||||
from math import radians
|
||||
from mathutils import Vector, Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
from blenderbim.bim.module.light.data import SolarData
|
||||
|
||||
sun_position = tool.Blender.get_sun_position_addon()
|
||||
|
||||
|
||||
class SolarDecorator:
|
||||
is_installed = False
|
||||
handlers = []
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.is_installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_text, (context,), "WINDOW", "POST_PIXEL"))
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_geometry, (context,), "WINDOW", "POST_VIEW"))
|
||||
cls.is_installed = True
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
for handler in cls.handlers:
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(handler, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.is_installed = False
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_text(self, context):
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
|
||||
props = bpy.context.scene.BIMSolarProperties
|
||||
origin = Matrix.Translation(props.sun_path_origin)
|
||||
location = origin @ (props.sun_position * 1.05)
|
||||
|
||||
self.font_id = 0
|
||||
blf.size(self.font_id, 12)
|
||||
blf.color(self.font_id, *self.addon_prefs.decorations_colour)
|
||||
self.draw_text_at_position(context, f"{props.hour:02}:{props.minute:02}", location)
|
||||
|
||||
self.tn_angle = props.true_north
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
grid_north_p = origin @ angle @ (Vector((0, 0.8, 0)) * props.sun_path_size)
|
||||
self.draw_text_at_position(context, "True North", grid_north_p)
|
||||
|
||||
def draw_text_at_position(self, context, text, position):
|
||||
coords_2d = location_3d_to_region_2d(context.region, context.region_data, position)
|
||||
if not coords_2d:
|
||||
return
|
||||
for i, line in enumerate(text.split("\n")):
|
||||
w, h = blf.dimensions(self.font_id, line)
|
||||
co = coords_2d.copy()
|
||||
co -= Vector((w * 0.5, 15 * i))
|
||||
blf.position(self.font_id, co[0], co[1], 0)
|
||||
blf.draw(self.font_id, line)
|
||||
|
||||
|
||||
def draw_geometry(self, context):
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
decorator_color_special = self.addon_prefs.decorator_color_special
|
||||
decorator_color_error = self.addon_prefs.decorator_color_error
|
||||
decorator_color_background = self.addon_prefs.decorator_color_background
|
||||
|
||||
gpu.state.point_size_set(12)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
vertex_shader = """
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
in vec3 pos;
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
gl_PointSize = 10.0;
|
||||
}
|
||||
"""
|
||||
|
||||
fragment_shader = """
|
||||
uniform vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main()
|
||||
{
|
||||
float dist = length(gl_PointCoord - vec2(0.5));
|
||||
if (dist < 0.5) {
|
||||
fragColor = color;
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
self.shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
|
||||
props = bpy.context.scene.BIMSolarProperties
|
||||
|
||||
origin = Matrix.Translation(props.sun_path_origin)
|
||||
|
||||
location = origin @ props.sun_position
|
||||
if location.z <= 0:
|
||||
gpu.state.point_size_set(6)
|
||||
self.draw_batch("POINTS", [location], decorator_color_error)
|
||||
else:
|
||||
self.draw_batch("POINTS", [location], (0.882353, 0.588235, 0.345098, 1))
|
||||
gpu.state.point_size_set(6)
|
||||
|
||||
self.draw_batch("LINES", [location, origin @ Vector((0, 0, 0))], decorator_color_background, [[0, 1]])
|
||||
|
||||
coords = []
|
||||
indices = []
|
||||
coord_offset = 0
|
||||
for h in range(24):
|
||||
analemma_verts = sun_position.sun_calc.calc_analemma(context, h)
|
||||
coords.extend([origin @ v for v in analemma_verts])
|
||||
for i in range(len(analemma_verts) - 1):
|
||||
indices.append((coord_offset + i,
|
||||
coord_offset + i+1))
|
||||
coord_offset += len(analemma_verts)
|
||||
|
||||
self.draw_batch("LINES", coords, decorator_color_special, indices)
|
||||
|
||||
# True north
|
||||
self.tn_angle = props.true_north
|
||||
points = [Vector((0, 0, 0)), Vector((0, 0.75, 0))]
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
points = [origin @ angle @ (v * props.sun_path_size) for v in points]
|
||||
self.draw_batch("POINTS", points, decorator_color_special)
|
||||
|
||||
verts = [Vector((0, 0, 0)), Vector((0, 0.75, 0))]
|
||||
edges = [[0, 1]]
|
||||
angle = Matrix.Rotation(radians(self.tn_angle), 4, "Z")
|
||||
verts = [origin @ angle @ (v * props.sun_path_size) for v in verts]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
verts = [Vector((0, 0.75, 0)), Vector((-0.15, 0.6, 0)), Vector((0.15, 0.6, 0))]
|
||||
edges = [[0, 1], [0, 2]]
|
||||
verts = [origin @ angle @ (v * props.sun_path_size) for v in verts]
|
||||
self.line_shader.uniform_float("lineWidth", 5.0)
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
arc_start = Vector((0, 0.25, 0)) * props.sun_path_size
|
||||
arc_end = angle @ arc_start
|
||||
angle_half = Matrix.Rotation(radians(self.tn_angle / 2), 4, "Z")
|
||||
arc_mid = angle_half @ arc_start
|
||||
arc_segments = tool.Cad.create_arc_segments(
|
||||
pts=[origin @ v for v in [arc_start, arc_mid, arc_end]], num_verts=12, make_edges=True
|
||||
)
|
||||
verts, edges = arc_segments
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
@@ -25,15 +25,15 @@ except ImportError:
|
||||
pr = None
|
||||
|
||||
import bpy
|
||||
|
||||
import blenderbim.tool as tool
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional, Sequence
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import multiprocessing
|
||||
import shutil
|
||||
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.light.data import SolarData
|
||||
|
||||
|
||||
class ExportOBJ(bpy.types.Operator):
|
||||
@@ -43,80 +43,67 @@ class ExportOBJ(bpy.types.Operator):
|
||||
bl_description = "Export the IFC to OBJ"
|
||||
|
||||
def execute(self, context):
|
||||
# Get the resolution from the user input
|
||||
resolution_x, resolution_y = self.getResolution(context)
|
||||
quality = context.scene.radiance_exporter_properties.radiance_quality.upper()
|
||||
detail = context.scene.radiance_exporter_properties.radiance_detail.upper()
|
||||
variability = context.scene.radiance_exporter_properties.radiance_variability.upper()
|
||||
|
||||
# Calculate the aspect ratio
|
||||
aspect_ratio = resolution_x / resolution_y
|
||||
|
||||
# Get the blend file path and create the "Radiance Rendering" directory
|
||||
self.report({'INFO'}, "Exporting Radiance files...")
|
||||
blend_file_path = bpy.data.filepath
|
||||
if not blend_file_path:
|
||||
self.report({'ERROR'}, "Please save the Blender file before exporting.")
|
||||
return {'CANCELLED'}
|
||||
# Get the output directory
|
||||
should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory
|
||||
output_dir = context.scene.radiance_exporter_properties.output_dir
|
||||
json_file = context.scene.radiance_exporter_properties.json_file
|
||||
|
||||
blend_file_dir = os.path.dirname(blend_file_path)
|
||||
radiance_dir = os.path.join(blend_file_dir, "RadianceRendering")
|
||||
|
||||
self.report({'INFO'}, "Radiance directory: {}".format(radiance_dir))
|
||||
context.scene.radiance_exporter_properties.is_exporting = True
|
||||
|
||||
if not os.path.exists(radiance_dir):
|
||||
os.makedirs(radiance_dir)
|
||||
|
||||
# IFC file export and processing
|
||||
settings = ifcopenshell.geom.settings()
|
||||
# Conversion from IFC to OBJ
|
||||
# Settings for obj
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
serializer_settings = ifcopenshell.geom.serializer_settings()
|
||||
|
||||
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.SURFACES_AND_SOLIDS)
|
||||
settings.set("apply-default-materials", True)
|
||||
serializer_settings.set("use-element-guids", True)
|
||||
settings.set("use-world-coords", True)
|
||||
|
||||
ifc_file_name = context.scene.radiance_exporter_properties.ifc_file_name
|
||||
ifc_file_path = os.path.join(blend_file_dir, f"{ifc_file_name}.ifc")
|
||||
if should_load_from_memory:
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
else:
|
||||
ifc_file_path = context.scene.radiance_exporter_properties.ifc_file
|
||||
ifc_file = ifcopenshell.open(ifc_file_path)
|
||||
|
||||
if not os.path.exists(ifc_file_path):
|
||||
self.report({'ERROR'}, f"IFC file not found: {ifc_file_path}")
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
ifc_file = ifcopenshell.open(ifc_file_path)
|
||||
for material in ifc_file.by_type("IfcMaterial"):
|
||||
self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}")
|
||||
|
||||
obj_file_path = os.path.join(radiance_dir, "model.obj")
|
||||
mtl_file_path = os.path.join(radiance_dir, "model.mtl")
|
||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
||||
mtl_file_path = os.path.join(output_dir, "model.mtl")
|
||||
|
||||
|
||||
# serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, ifcopenshell.geom.serializer_settings())
|
||||
serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, serializer_settings)
|
||||
serialiser.setFile(ifc_file)
|
||||
serialiser.setUnitNameAndMagnitude("METER", 1.0)
|
||||
serialiser.writeHeader()
|
||||
|
||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
|
||||
if ifc_file.schema in ("IFC2X3", "IFC4"):
|
||||
elements = ifc_file.by_type("IfcElement") + ifc_file.by_type("IfcProxy")
|
||||
else:
|
||||
elements = ifc_file.by_type("IfcElement")
|
||||
|
||||
elements = [e for e in elements if not e.is_a("IfcFeatureElement") or e.is_a("IfcSurfaceFeature")]
|
||||
|
||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
serialiser.write(iterator.get())
|
||||
shape = iterator.get()
|
||||
materials = shape.geometry.materials
|
||||
material_ids = shape.geometry.material_ids
|
||||
for material in materials:
|
||||
print(material, dir(material))
|
||||
print(material_ids)
|
||||
serialiser.write(shape)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
serialiser.finalize()
|
||||
context.scene.radiance_exporter_properties.is_exporting = False
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def getResolution(self, context):
|
||||
scene = context.scene
|
||||
props = scene.radiance_exporter_properties
|
||||
resolution_x = props.radiance_resolution_x
|
||||
resolution_y = props.radiance_resolution_y
|
||||
return resolution_x, resolution_y
|
||||
|
||||
|
||||
class RadianceRender(bpy.types.Operator):
|
||||
@@ -135,46 +122,43 @@ class RadianceRender(bpy.types.Operator):
|
||||
quality = context.scene.radiance_exporter_properties.radiance_quality.upper()
|
||||
detail = context.scene.radiance_exporter_properties.radiance_detail.upper()
|
||||
variability = context.scene.radiance_exporter_properties.radiance_variability.upper()
|
||||
|
||||
should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory
|
||||
output_dir = context.scene.radiance_exporter_properties.output_dir
|
||||
json_file = context.scene.radiance_exporter_properties.json_file
|
||||
|
||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
||||
|
||||
|
||||
# Get the blend file path and create the "Radiance Rendering" directory
|
||||
blend_file_path = bpy.data.filepath
|
||||
if not blend_file_path:
|
||||
self.report({'ERROR'}, "Please save the Blender file before rendering.")
|
||||
camera = self.get_active_camera(context)
|
||||
if camera is None:
|
||||
self.report({'ERROR'}, "No active camera found in the scene. Please add a camera and set it as active.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
blend_file_dir = os.path.dirname(blend_file_path)
|
||||
radiance_dir = os.path.join(blend_file_dir, "RadianceRendering")
|
||||
|
||||
# Get camera position and direction
|
||||
camera_position, camera_direction = self.get_camera_data(camera)
|
||||
|
||||
# Material processing
|
||||
style = []
|
||||
obj_file_path = os.path.join(radiance_dir, "model.obj")
|
||||
|
||||
with open(obj_file_path, "r") as obj_file:
|
||||
for line in obj_file:
|
||||
if line.startswith("usemtl"):
|
||||
l = line.strip().split(" ")
|
||||
style.append(l[1])
|
||||
|
||||
# json_file_path = os.path.join(blend_file_dir, "material_mapping.json")
|
||||
|
||||
json_file_path = context.scene.radiance_exporter_properties.json_file_path
|
||||
self.report({'INFO'}, f"Selected JSON file: {json_file_path}")
|
||||
if json_file_path:
|
||||
# self.report({'INFO'}, f"Selected JSON file: {json_file_name}")
|
||||
json_dest_path = os.path.join(blend_file_dir, json_file_path.split("\\")[-1])
|
||||
shutil.copy(json_file_path, json_dest_path)
|
||||
self.report({'INFO'}, f"JSON file saved to: {json_dest_path}")
|
||||
# Check if json file is empty or not
|
||||
if json_file:
|
||||
with open(json_file, 'r') as file:
|
||||
data = json.load(file)
|
||||
else:
|
||||
self.report({'WARNING'}, "No JSON file selected")
|
||||
|
||||
|
||||
with open(json_file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
data = {}
|
||||
|
||||
# Create materials.rad file
|
||||
materials_file = os.path.join(radiance_dir, "materials.rad")
|
||||
materials_file = os.path.join(output_dir, "materials.rad")
|
||||
with open(materials_file, "w") as file:
|
||||
file.write("void plastic white\n0\n0\n5 1 1 1 0 0\n")
|
||||
file.write("void plastic white\n0\n0\n5 0.6 0.6 0.6 0 0\n")
|
||||
file.write("void plastic blue_plastic\n0\n0\n5 0.1 0.2 0.8 0.05 0.1\n")
|
||||
file.write("void plastic red_plastic\n0\n0\n5 0.8 0.1 0.2 0.05 0.1\n")
|
||||
file.write("void metal silver_metal\n0\n0\n5 0.8 0.8 0.8 0.9 0.1\n")
|
||||
@@ -188,11 +172,11 @@ class RadianceRender(bpy.types.Operator):
|
||||
self.report({'INFO'}, "Exported Materials Rad file to: {}".format(materials_file))
|
||||
|
||||
# Run obj2mesh
|
||||
rtm_file_path = os.path.join(radiance_dir, "model.rtm")
|
||||
rtm_file_path = os.path.join(output_dir, "model.rtm")
|
||||
mesh_file_path = save_obj2mesh_output(obj_file_path, rtm_file_path, matfiles=[materials_file])
|
||||
# subprocess.run(["obj2mesh", "-a", materials_file, obj_file_path, rtm_file_path])
|
||||
self.report({'INFO'}, "obj2mesh output: {}".format(mesh_file_path))
|
||||
scene_file = os.path.join(radiance_dir, "scene.rad")
|
||||
scene_file = os.path.join(output_dir, "scene.rad")
|
||||
with open(scene_file, "w") as file:
|
||||
file.write("void mesh model\n1 " + rtm_file_path + "\n0\n0\n")
|
||||
|
||||
@@ -201,26 +185,42 @@ class RadianceRender(bpy.types.Operator):
|
||||
# Py Radiance Rendering code
|
||||
scene = pr.Scene("ascene")
|
||||
|
||||
material_path = os.path.join(radiance_dir, "materials.rad")
|
||||
scene_path = os.path.join(radiance_dir, "scene.rad")
|
||||
material_path = os.path.join(output_dir, "materials.rad")
|
||||
scene_path = os.path.join(output_dir, "scene.rad")
|
||||
|
||||
scene.add_material(material_path)
|
||||
scene.add_surface(scene_path)
|
||||
|
||||
aview = pr.View(position=(1, 1.5, 1), direction=(1, 0, 0))
|
||||
aview = pr.View(position=camera_position, direction=camera_direction)
|
||||
scene.add_view(aview)
|
||||
|
||||
octpath = os.path.join(blend_file_dir, "ascene.oct")
|
||||
print("Reached here")
|
||||
image = pr.render(scene, ambbounce=1, resolution=(resolution_x, resolution_y),
|
||||
quality=quality, detail=detail, variability=variability)
|
||||
|
||||
raw_hdr_path = os.path.join(radiance_dir, "raw.hdr")
|
||||
raw_hdr_path = os.path.join(output_dir, "raw.hdr")
|
||||
with open(raw_hdr_path, "wb") as wtr:
|
||||
wtr.write(image)
|
||||
|
||||
self.report({'INFO'}, "Radiance rendering completed. Output: {}".format(raw_hdr_path))
|
||||
return {'FINISHED'}
|
||||
|
||||
def get_active_camera(self, context):
|
||||
if context.scene.camera:
|
||||
return context.scene.camera
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'CAMERA':
|
||||
return obj
|
||||
return None
|
||||
|
||||
def get_camera_data(self, camera):
|
||||
# Get camera position
|
||||
position = camera.matrix_world.to_translation()
|
||||
|
||||
# Get camera direction
|
||||
direction = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
direction.normalize()
|
||||
|
||||
return (position.x, position.y, position.z), (direction.x, direction.y, direction.z)
|
||||
|
||||
def getResolution(self, context):
|
||||
scene = context.scene
|
||||
@@ -234,4 +234,74 @@ def save_obj2mesh_output(inp: Union[bytes, str, Path], output_file: str, **kwarg
|
||||
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(output_bytes)
|
||||
return output_file
|
||||
return output_file
|
||||
|
||||
|
||||
class ImportTrueNorth(bpy.types.Operator):
|
||||
bl_idname = "bim.import_true_north"
|
||||
bl_label = "Import True North"
|
||||
bl_description = "Imports the True North from your IFC geometric context"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not tool.Ifc.get():
|
||||
return False
|
||||
if not SolarData.is_loaded:
|
||||
SolarData.load()
|
||||
return SolarData.data["true_north"] is not None
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSolarProperties
|
||||
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if not context.TrueNorth:
|
||||
continue
|
||||
value = context.TrueNorth.DirectionRatios
|
||||
props.true_north = ifcopenshell.util.geolocation.yaxis2angle(*value[:2])
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportLatLong(bpy.types.Operator):
|
||||
bl_idname = "bim.import_lat_long"
|
||||
bl_label = "Import Latitude / Longitude"
|
||||
bl_description = "Imports the latitude / longitude from an IfcSite"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSolarProperties
|
||||
site = tool.Ifc.get().by_id(int(props.sites))
|
||||
if site.RefLatitude and site.RefLongitude:
|
||||
props.latitude = ifcopenshell.util.geolocation.dms2dd(*site.RefLatitude)
|
||||
props.longitude = ifcopenshell.util.geolocation.dms2dd(*site.RefLongitude)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class MoveSunPathTo3DCursor(bpy.types.Operator):
|
||||
bl_idname = "bim.move_sun_path_to_3d_cursor"
|
||||
bl_label = "Move Sun Path To 3D Cursor"
|
||||
bl_description = "Shifts the visualisation of the Sun Path to the 3D cursor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSolarProperties
|
||||
props.sun_path_origin = bpy.context.scene.cursor.location
|
||||
tool.Blender.update_viewport()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ViewFromSun(bpy.types.Operator):
|
||||
bl_idname = "bim.view_from_sun"
|
||||
bl_label = "View From Sun"
|
||||
bl_description = "Views your model as if you were looking from the perspective of the sun"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
if not (camera := bpy.data.objects.get("SunPathCamera")):
|
||||
camera = bpy.data.objects.new("SunPathCamera", bpy.data.cameras.new("SunPathCamera"))
|
||||
camera.data.type = "ORTHO"
|
||||
camera.data.ortho_scale = 100 # The default of 6m is too small
|
||||
bpy.context.scene.collection.objects.link(camera)
|
||||
tool.Blender.activate_camera(camera)
|
||||
props = context.scene.BIMSolarProperties
|
||||
props.hour = props.hour # Just to refresh camera position
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,17 +17,150 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bpy.props import IntProperty, StringProperty, EnumProperty
|
||||
import pytz
|
||||
import tzfpy
|
||||
import datetime
|
||||
import blenderbim.tool as tool
|
||||
from math import radians, pi
|
||||
from mathutils import Euler, Vector, Matrix, Quaternion
|
||||
from bpy.props import IntProperty, StringProperty, EnumProperty, FloatProperty, FloatVectorProperty, BoolProperty
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.bim.module.light.data import SolarData
|
||||
from blenderbim.bim.module.light.decorator import SolarDecorator
|
||||
|
||||
sun_position = tool.Blender.get_sun_position_addon()
|
||||
|
||||
|
||||
def get_sites(self, context):
|
||||
if not SolarData.is_loaded:
|
||||
SolarData.load()
|
||||
return SolarData.data["sites"]
|
||||
|
||||
|
||||
def update_latlong(self, context):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
sun_props.latitude = self.latitude
|
||||
sun_props.longitude = self.longitude
|
||||
update_sun_path()
|
||||
|
||||
|
||||
def update_hourminute(self, context):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
sun_props.time = self.hour + (self.minute / 60)
|
||||
update_sun_path()
|
||||
|
||||
|
||||
def update_date(self, context):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
sun_props.month = self.month
|
||||
sun_props.day = self.day
|
||||
update_sun_path()
|
||||
|
||||
|
||||
def update_true_north(self, context):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
# Preserve IFC sign convention
|
||||
sun_props.north_offset = radians(self.true_north * -1)
|
||||
update_sun_path()
|
||||
|
||||
|
||||
def update_sun_path_size(self, context):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
sun_props.sun_distance = self.sun_path_size
|
||||
update_sun_path()
|
||||
|
||||
|
||||
def update_display_shadows(self, context):
|
||||
if self.display_shadows:
|
||||
update_sun_path()
|
||||
context.scene.render.engine = "BLENDER_WORKBENCH"
|
||||
context.scene.display.shading.light = "FLAT"
|
||||
context.scene.display.shading.show_shadows = True
|
||||
context.scene.display.shading.show_object_outline = True
|
||||
context.scene.display.shadow_focus = 1.0
|
||||
context.scene.view_settings.view_transform = "Standard" # Preserve shading colours
|
||||
space = tool.Blender.get_view3d_space()
|
||||
space.shading.type = "RENDERED"
|
||||
else:
|
||||
space = tool.Blender.get_view3d_space()
|
||||
space.shading.type = "SOLID"
|
||||
|
||||
|
||||
def update_display_sun_path(self, context):
|
||||
if self.display_sun_path:
|
||||
update_sun_path()
|
||||
SolarDecorator.install(bpy.context)
|
||||
else:
|
||||
SolarDecorator.uninstall()
|
||||
|
||||
|
||||
def update_sun_path():
|
||||
props = bpy.context.scene.BIMSolarProperties
|
||||
sun_props = bpy.context.scene.sun_pos_properties
|
||||
props.timezone = tzfpy.get_tz(props.longitude, props.latitude)
|
||||
timezone = pytz.timezone(props.timezone)
|
||||
dt = datetime.datetime(sun_props.year, sun_props.month, sun_props.day, props.hour, props.minute)
|
||||
local_time = timezone.localize(dt, is_dst=None)
|
||||
sun_props.use_daylight_savings = bool(local_time.dst())
|
||||
sun_props.UTC_zone = local_time.utcoffset().total_seconds() / 3600
|
||||
zone = -sun_props.UTC_zone
|
||||
if sun_props.use_daylight_savings:
|
||||
zone -= 1
|
||||
azimuth, elevation = sun_position.sun_calc.get_sun_coordinates(
|
||||
sun_props.time,
|
||||
sun_props.latitude,
|
||||
sun_props.longitude,
|
||||
zone,
|
||||
sun_props.month,
|
||||
sun_props.day,
|
||||
sun_props.year,
|
||||
)
|
||||
sun_vector = sun_position.sun_calc.get_sun_vector(azimuth, elevation) * sun_props.sun_distance
|
||||
props.sun_position = sun_vector
|
||||
# sun_vector.z = max(0, sun_vector.z)
|
||||
# Light direction is a bit weird?
|
||||
mat = Matrix(((-1.0, 0.0, 0.0, 0.0), (0.0, 0, 1.0, 0.0), (-0.0, -1.0, 0, 0.0), (0.0, 0.0, 0.0, 1.0))).inverted()
|
||||
rotation_euler = Euler((elevation - pi / 2, 0, -azimuth))
|
||||
rotation_quaternion = rotation_euler.to_quaternion()
|
||||
|
||||
if sun_vector.z < 0:
|
||||
bpy.context.scene.display.light_direction = mat @ Vector((0, 0, 1))
|
||||
else:
|
||||
bpy.context.scene.display.light_direction = mat @ (rotation_quaternion @ Vector((0, 0, -1)))
|
||||
SolarData.data["sun"] = sun_vector
|
||||
|
||||
if obj := bpy.data.objects.get("SunPathCamera"):
|
||||
obj.matrix_world.translation = sun_vector
|
||||
z180_quaternion = Quaternion((0, 0, 1), radians(180))
|
||||
obj.rotation_mode = "QUATERNION"
|
||||
obj.rotation_quaternion = rotation_quaternion @ z180_quaternion
|
||||
obj.data.ortho_scale = props.sun_path_size * 2
|
||||
|
||||
|
||||
class RadianceExporterProperties(PropertyGroup):
|
||||
|
||||
def update_json_file_path(self, context):
|
||||
if self.json_file_path:
|
||||
self.json_file_path = bpy.path.abspath(self.json_file_path)
|
||||
def update_json_file(self, context):
|
||||
if self.json_file:
|
||||
self.json_file = bpy.path.abspath(self.json_file)
|
||||
|
||||
def update_output_dir(self, context):
|
||||
if self.output_dir:
|
||||
self.output_dir = bpy.path.abspath(self.output_dir)
|
||||
|
||||
def update_ifc_file(self, context):
|
||||
if self.ifc_file:
|
||||
self.ifc_file = bpy.path.abspath(self.ifc_file)
|
||||
|
||||
is_exporting: bpy.props.BoolProperty(
|
||||
name="Is Exporting",
|
||||
description="Whether the OBJ export is in progress",
|
||||
default=False
|
||||
)
|
||||
|
||||
should_load_from_memory: BoolProperty(
|
||||
name="Load from Memory",
|
||||
default=False,
|
||||
)
|
||||
|
||||
radiance_resolution_x: IntProperty(
|
||||
name="X",
|
||||
@@ -41,18 +174,26 @@ class RadianceExporterProperties(PropertyGroup):
|
||||
default=1080,
|
||||
min=1
|
||||
)
|
||||
ifc_file_name: StringProperty(
|
||||
name="IFC File Name",
|
||||
description="Name of the IFC file to use (without .ifc extension)",
|
||||
default=""
|
||||
output_dir: StringProperty(
|
||||
name="Output Directory",
|
||||
description="Directory to output Radiance files",
|
||||
default="",
|
||||
subtype="DIR_PATH",
|
||||
update=lambda self, context: self.update_output_dir(context)
|
||||
)
|
||||
|
||||
json_file_path: StringProperty(
|
||||
ifc_file: StringProperty(
|
||||
name="IFC File",
|
||||
description="Path to the IFC file",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
update=lambda self, context: self.update_ifc_file(context)
|
||||
)
|
||||
json_file: StringProperty(
|
||||
name="JSON File",
|
||||
description="Path to the JSON file",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
update=lambda self, context: self.update_json_file_path(context)
|
||||
update=lambda self, context: self.update_json_file(context)
|
||||
)
|
||||
|
||||
radiance_quality: EnumProperty(
|
||||
@@ -83,5 +224,32 @@ class RadianceExporterProperties(PropertyGroup):
|
||||
('MEDIUM', "Medium", "Medium variability"),
|
||||
('HIGH', "High", "High variability")
|
||||
],
|
||||
default='MEDIUM'
|
||||
)
|
||||
default="MEDIUM",
|
||||
)
|
||||
|
||||
|
||||
class BIMSolarProperties(PropertyGroup):
|
||||
sites: EnumProperty(items=get_sites, name="Sites")
|
||||
latitude: FloatProperty(name="Latitude", min=-90, max=90, update=update_latlong)
|
||||
longitude: FloatProperty(name="Longitude", min=-180, max=180, update=update_latlong)
|
||||
timezone: StringProperty(name="Timezone", default="Etc/GMT")
|
||||
true_north: FloatProperty(name="True North", min=-180, max=180, update=update_true_north)
|
||||
month: IntProperty(name="Month", min=1, max=12, default=1, update=update_date)
|
||||
day: IntProperty(name="Date", min=1, max=31, default=1, update=update_date)
|
||||
hour: IntProperty(name="Hour", min=0, max=23, default=12, update=update_hourminute)
|
||||
minute: IntProperty(name="Minute", min=0, max=59, update=update_hourminute)
|
||||
sun_position: FloatVectorProperty(name="Sun Position", subtype="XYZ", default=(0, 0, 0))
|
||||
sun_path_origin: FloatVectorProperty(name="Sun Path Origin", subtype="XYZ", default=(0, 0, 0))
|
||||
sun_path_size: FloatProperty(name="Sun Path Size", min=0.1, default=50, update=update_sun_path_size)
|
||||
display_shadows: BoolProperty(
|
||||
name="Display Shadows",
|
||||
default=False,
|
||||
description="Enables a visual style to display shadows easily",
|
||||
update=update_display_shadows,
|
||||
)
|
||||
display_sun_path: BoolProperty(
|
||||
name="Display Sun Path",
|
||||
default=False,
|
||||
description="Displays analemmas and sun position",
|
||||
update=update_display_sun_path,
|
||||
)
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.module.light.data import SolarData
|
||||
|
||||
|
||||
sun_position = tool.Blender.get_sun_position_addon()
|
||||
|
||||
|
||||
class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
"""Creates a Panel in the render properties window"""
|
||||
@@ -26,19 +32,28 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_services"
|
||||
bl_parent_id = "BIM_PT_tab_lighting"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
|
||||
props = scene.radiance_exporter_properties
|
||||
|
||||
if tool.Ifc.get():
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "ifc_file_name")
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "ifc_file")
|
||||
# row.operator("bim.select_ifctester_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "json_file_path")
|
||||
row.prop(props, "output_dir")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "json_file")
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Resolution")
|
||||
@@ -58,7 +73,113 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
row.prop(props, "radiance_variability")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("export_scene.radiance", text="Export to OBJ")
|
||||
row.operator("export_scene.radiance", text="Export Geometry for Simulation")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("render_scene.radiance", text="Radiance Render")
|
||||
row.operator("render_scene.radiance", text="Radiance Render")
|
||||
row.enabled = not props.is_exporting
|
||||
|
||||
class BIM_PT_solar(bpy.types.Panel):
|
||||
"""Creates a Panel in the render properties window"""
|
||||
bl_label = "Solar Access / Shadow"
|
||||
bl_idname = "BIM_PT_solar"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_solar_analysis"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
def draw(self, context):
|
||||
if sun_position is None:
|
||||
self.layout.label(text="Enable 'Sun Position' addon to continue.")
|
||||
return
|
||||
|
||||
if not SolarData.is_loaded:
|
||||
SolarData.load()
|
||||
|
||||
props = context.scene.BIMSolarProperties
|
||||
|
||||
if SolarData.data["sites"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "sites")
|
||||
row.operator("bim.import_lat_long", icon="IMPORT", text="")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Sites With Lat/Longs Found", icon="ERROR")
|
||||
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(sun_props, "coordinates", icon='URL')
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "latitude")
|
||||
row.prop(props, "longitude")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "true_north")
|
||||
if SolarData.data["true_north"] is not None:
|
||||
row.operator("bim.import_true_north", icon="IMPORT", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "month", text={
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
}[props.month])
|
||||
row.prop(props, "day")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "hour")
|
||||
row.prop(props, "minute")
|
||||
|
||||
col = self.layout.column(align=True)
|
||||
box = col.box()
|
||||
row = box.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text=props.timezone)
|
||||
|
||||
row = col.row(align=True)
|
||||
box = row.box()
|
||||
|
||||
local_time = sun_position.sun_calc.format_time(sun_props.time, sun_props.use_daylight_savings)
|
||||
utc_time = sun_position.sun_calc.format_time(sun_props.time, sun_props.use_daylight_savings, sun_props.UTC_zone)
|
||||
|
||||
row2 = box.row()
|
||||
row2.label(text=f"Local Time: {local_time}")
|
||||
row2 = box.row()
|
||||
row2.label(text=f"UTC Time: {utc_time}")
|
||||
|
||||
box = row.box()
|
||||
|
||||
sunrise = sun_position.sun_calc.format_hms(sun_position.sun_calc.sun.sunrise)
|
||||
sunset = sun_position.sun_calc.format_hms(sun_position.sun_calc.sun.sunset)
|
||||
|
||||
row2 = box.row()
|
||||
row2.label(text=f"Sunrise: {sunrise}")
|
||||
row2 = box.row()
|
||||
row2.label(text=f"Sunset: {sunset}")
|
||||
|
||||
col = self.layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "display_sun_path", icon="LIGHT_SUN")
|
||||
row.prop(props, "sun_path_size")
|
||||
|
||||
if props.display_sun_path:
|
||||
row = col.row()
|
||||
row.operator("bim.move_sun_path_to_3d_cursor")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "display_shadows", icon="SHADING_RENDERED")
|
||||
row.prop(context.scene.display.shading, "shadow_intensity", text="Shadow Intensity")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.view_from_sun", icon="LIGHT_HEMI")
|
||||
|
||||
@@ -24,7 +24,6 @@ classes = (
|
||||
operator.GetConnectedSystemElements,
|
||||
operator.ResizeToStorey,
|
||||
operator.SetOverrideColour,
|
||||
operator.SetViewportShadowFromSun,
|
||||
operator.SnapSpacesTogether,
|
||||
operator.SplitAlongEdge,
|
||||
prop.BIMMiscProperties,
|
||||
|
||||
@@ -45,25 +45,6 @@ class SetOverrideColour(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetViewportShadowFromSun(bpy.types.Operator):
|
||||
bl_idname = "bim.set_viewport_shadow_from_sun"
|
||||
bl_label = "Set Viewport Shadow from Sun"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
# Does this belong in the drawing module? Perhaps.
|
||||
# The vector used for the light direction is a bit funny
|
||||
mat = Matrix(((-1.0, 0.0, 0.0, 0.0), (0.0, 0, 1.0, 0.0), (-0.0, -1.0, 0, 0.0), (0.0, 0.0, 0.0, 1.0)))
|
||||
context.scene.display.light_direction = mat.inverted() @ (
|
||||
context.active_object.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SnapSpacesTogether(bpy.types.Operator):
|
||||
bl_idname = "bim.snap_spaces_together"
|
||||
bl_label = "Snap Spaces Together"
|
||||
|
||||
@@ -35,8 +35,6 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
|
||||
row.prop(props, "override_colour", text="")
|
||||
row.operator("bim.set_override_colour")
|
||||
row = layout.row()
|
||||
row.operator("bim.set_viewport_shadow_from_sun")
|
||||
row = layout.row()
|
||||
row.operator("bim.snap_spaces_together")
|
||||
row = layout.split(factor=0.2, align=True)
|
||||
row.prop(props, "total_storeys", text="")
|
||||
|
||||
@@ -245,7 +245,7 @@ class AuthoringData:
|
||||
if not ifc_class and ifc_classes:
|
||||
ifc_class = ifc_classes[0][0]
|
||||
if ifc_class:
|
||||
elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name or "Unnamed")
|
||||
elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: (s.Name or "Unnamed").lower())
|
||||
results.extend(elements)
|
||||
return [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in results]
|
||||
return []
|
||||
|
||||
@@ -26,7 +26,6 @@ import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
import blenderbim
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.geometry as core
|
||||
|
||||
@@ -144,6 +144,10 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
if self.from_invoke and str(self.relating_type_id) in AuthoringData.data["relating_type_id"]:
|
||||
props.relating_type_id = str(self.relating_type_id)
|
||||
|
||||
self.container_obj = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
|
||||
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
ifc_class = relating_type.is_a()
|
||||
instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, tool.Ifc.get().schema)[0]
|
||||
@@ -192,10 +196,6 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
|
||||
obj.location = context.scene.cursor.location
|
||||
|
||||
self.container_obj = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
|
||||
tool.Blender.remove_data_block(mesh) # Remove "Instance" mesh
|
||||
|
||||
|
||||
@@ -24,10 +24,9 @@ import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.door import bm_sort_out_geom
|
||||
from blenderbim.bim.module.model.data import RailingData, refresh
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import convert_property_group_from_si
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
|
||||
@@ -224,7 +224,7 @@ def updateTaskDuration(self, context):
|
||||
else:
|
||||
task_time = tool.Ifc.run("sequence.add_task_time", task=task)
|
||||
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
|
||||
blenderbim.core.sequence.load_task_properties(tool.Sequence)
|
||||
core.load_task_properties(tool.Sequence)
|
||||
tool.Sequence.refresh_task_resources()
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ classes = (
|
||||
operator.SelectDecomposedElements,
|
||||
operator.SelectProduct,
|
||||
operator.SelectSimilarContainer,
|
||||
operator.SetContainerVisibility,
|
||||
operator.SetDefaultContainer,
|
||||
prop.SpatialElement,
|
||||
prop.Element,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.spatial as core
|
||||
import blenderbim.core.geometry
|
||||
@@ -146,9 +147,17 @@ class SelectContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_container"
|
||||
bl_label = "Select Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
container: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_container(tool.Ifc, tool.Spatial, obj=context.active_object)
|
||||
if self.container:
|
||||
container = tool.Ifc.get().by_id(self.container)
|
||||
elif element := tool.Ifc.get_entity(context.active_object):
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
else:
|
||||
return
|
||||
if container:
|
||||
core.select_container(tool.Ifc, tool.Spatial, container=container)
|
||||
|
||||
|
||||
class SelectSimilarContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -228,16 +237,70 @@ class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_decomposed_elements"
|
||||
bl_label = "Select Children"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
container: bpy.props.IntProperty()
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, operator):
|
||||
return f"Select all elements contained in this {operator.ifc_class}"
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_decomposed_elements(tool.Spatial)
|
||||
core.select_decomposed_elements(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
|
||||
|
||||
|
||||
class SetDefaultContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_default_container"
|
||||
bl_label = "Set Default Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set this as the default container that all new elements will be contained in"
|
||||
container: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_default_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
|
||||
|
||||
|
||||
class SetContainerVisibility(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_container_visibility"
|
||||
bl_label = "Set Container Visibility"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
container: bpy.props.IntProperty()
|
||||
should_include_children: bpy.props.BoolProperty(name="Should Include Children", default=True, options={"SKIP_SAVE"})
|
||||
mode: bpy.props.StringProperty(name="Mode")
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, operator):
|
||||
if operator.mode == "HIDE":
|
||||
return "Hides the selected container and all children.\n" + "ALT+CLICK to ignore children"
|
||||
elif operator.mode == "SHOW":
|
||||
return "Shows the selected container and all children.\n" + "ALT+CLICK to ignore children"
|
||||
return "Isolate the selected container and all children.\n" + "ALT+CLICK to ignore children"
|
||||
|
||||
def invoke(self, context, event):
|
||||
if event.type == "LEFTMOUSE" and event.alt:
|
||||
self.should_include_children = False
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
if self.mode == "ISOLATE":
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
containers = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||
elif tool.Ifc.get_schema() != "IFC2X3":
|
||||
containers = set(tool.Ifc.get().by_type("IfcSpatialElement"))
|
||||
containers -= set(tool.Ifc.get().by_type("IfcSpatialZone"))
|
||||
for container in containers:
|
||||
if obj := tool.Ifc.get_object(container):
|
||||
if collection := obj.BIMObjectProperties.collection:
|
||||
collection.hide_viewport = True
|
||||
should_hide = False
|
||||
else:
|
||||
should_hide = self.mode == "HIDE"
|
||||
|
||||
container = tool.Ifc.get().by_id(self.container)
|
||||
queue = [container]
|
||||
while queue:
|
||||
container = queue.pop()
|
||||
if obj := tool.Ifc.get_object(container):
|
||||
if collection := obj.BIMObjectProperties.collection:
|
||||
collection.hide_viewport = should_hide
|
||||
if self.should_include_children:
|
||||
queue.extend(ifcopenshell.util.element.get_parts(container))
|
||||
|
||||
@@ -59,6 +59,10 @@ def update_active_container_index(self, context):
|
||||
tool.Spatial.load_contained_elements()
|
||||
|
||||
|
||||
def update_should_include_children(self, context):
|
||||
tool.Spatial.load_contained_elements()
|
||||
|
||||
|
||||
def update_relating_container_from_object(self, context):
|
||||
if self.relating_container_object is None or context.active_object is None:
|
||||
return
|
||||
@@ -132,6 +136,9 @@ class BIMSpatialDecompositionProperties(PropertyGroup):
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
|
||||
default_container: IntProperty(name="Default Container", default=0)
|
||||
should_include_children: BoolProperty(
|
||||
name="Should Include Children", default=True, update=update_should_include_children
|
||||
)
|
||||
|
||||
@property
|
||||
def active_container(self):
|
||||
|
||||
@@ -61,7 +61,7 @@ class BIM_PT_spatial(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
if SpatialData.data["label"]:
|
||||
row.label(text=SpatialData.data["label"])
|
||||
row.operator("bim.select_container", icon="OBJECT_DATA", text="")
|
||||
row.operator("bim.select_container", icon="OBJECT_DATA", text="").container = 0
|
||||
row.operator("bim.select_similar_container", icon="RESTRICT_SELECT_OFF", text="")
|
||||
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
||||
if SpatialData.data["is_directly_contained"]:
|
||||
@@ -115,14 +115,13 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
text=f"Default: {SpatialDecompositionData.data['default_container']}",
|
||||
icon="OUTLINER_COLLECTION",
|
||||
)
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Warning: No Default Container", icon="ERROR")
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
|
||||
if self.props.active_container:
|
||||
ifc_definition_id = self.props.active_container.ifc_definition_id if self.props.active_container else 0
|
||||
ifc_definition_id = self.props.active_container.ifc_definition_id
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "subelement_class", text="")
|
||||
op = row.operator("bim.add_part_to_object", icon="ADD", text="")
|
||||
@@ -134,7 +133,16 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
row.enabled = False
|
||||
op = row.operator("bim.set_default_container", icon="OUTLINER_COLLECTION", text="Set Default")
|
||||
op.container = ifc_definition_id
|
||||
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text=f"Isolate {self.props.active_container.ifc_class}")
|
||||
op = row.operator("bim.set_container_visibility", icon="FULLSCREEN_EXIT", text="Isolate")
|
||||
op.mode = "ISOLATE"
|
||||
op.container = ifc_definition_id
|
||||
op = row.operator("bim.set_container_visibility", icon="HIDE_OFF", text="")
|
||||
op.mode = "SHOW"
|
||||
op.container = ifc_definition_id
|
||||
op = row.operator("bim.set_container_visibility", icon="HIDE_ON", text="")
|
||||
op.mode = "HIDE"
|
||||
op.container = ifc_definition_id
|
||||
row.operator("bim.select_container", icon="OBJECT_DATA", text="").container = ifc_definition_id
|
||||
op = row.operator("bim.delete_container", icon="X", text="")
|
||||
op.container = ifc_definition_id
|
||||
|
||||
@@ -152,13 +160,17 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
return
|
||||
|
||||
if not self.props.total_elements:
|
||||
row = self.layout.row()
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Contained Elements", icon="FILE_3D")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{self.props.total_elements} Contained Elements", icon="FILE_3D")
|
||||
row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
op = row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.ifc_class = self.props.active_container.ifc_class
|
||||
op.container = ifc_definition_id
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_elements",
|
||||
@@ -198,18 +210,22 @@ class BIM_UL_containers_manager(UIList):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.ifc_class == "IfcProject":
|
||||
row.label(text="", icon="FILE")
|
||||
elif item.ifc_class == "IfcSite":
|
||||
row.label(text="", icon="WORLD")
|
||||
elif item.ifc_class == "IfcBuilding":
|
||||
row.label(text="", icon="HOME")
|
||||
elif item.ifc_class == "IfcBuildingStorey":
|
||||
row.label(text="", icon="LINENUMBERS_OFF")
|
||||
elif item.ifc_class == "IfcSpace":
|
||||
row.label(text="", icon="ANTIALIASED")
|
||||
elif "Part" in item.ifc_class:
|
||||
row.label(text="", icon="MOD_FLUID")
|
||||
else:
|
||||
row.label(text="", icon="META_PLANE")
|
||||
icon = {
|
||||
"IfcSite": "WORLD",
|
||||
"IfcBuilding": "HOME",
|
||||
"IfcBuildingStorey": "LINENUMBERS_OFF",
|
||||
"IfcSpace": "ANTIALIASED",
|
||||
"IfcFacilityPart": "MOD_FLUID",
|
||||
"IfcBridgePart": "MOD_FLUID",
|
||||
"IfcFacilityPartCommon": "MOD_FLUID",
|
||||
"IfcMarinePart": "MOD_FLUID",
|
||||
"IfcRailwayPart": "MOD_FLUID",
|
||||
"IfcRoadPart": "MOD_FLUID",
|
||||
}.get(item.ifc_class, "META_PLANE")
|
||||
op = row.operator("bim.select_decomposed_elements", icon=icon, text="", emboss=False)
|
||||
op.ifc_class = item.ifc_class
|
||||
op.container = item.ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_elements(UIList):
|
||||
|
||||
@@ -279,7 +279,6 @@ class BIM_PT_structural_connection(Panel):
|
||||
class BIM_PT_structural_analysis_models(Panel):
|
||||
bl_label = "Structural Analysis Models"
|
||||
bl_idname = "BIM_PT_structural_analysis_models"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
@@ -354,7 +353,6 @@ class BIM_UL_structural_analysis_models(UIList):
|
||||
class BIM_PT_structural_load_cases(Panel):
|
||||
bl_label = "Structural Load Cases"
|
||||
bl_idname = "BIM_PT_structural_load_cases"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
@@ -450,7 +448,6 @@ class BIM_UL_structural_activities(UIList):
|
||||
class BIM_PT_structural_loads(Panel):
|
||||
bl_label = "Structural Loads"
|
||||
bl_idname = "BIM_PT_structural_loads"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
@@ -520,7 +517,6 @@ class BIM_UL_structural_loads(UIList):
|
||||
class BIM_PT_boundary_conditions(Panel):
|
||||
bl_label = "Boundary Conditions"
|
||||
bl_idname = "BIM_PT_boundary_conditions"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@@ -23,6 +23,7 @@ import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.root
|
||||
import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
|
||||
|
||||
@@ -373,7 +373,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(context.scene.DocProperties, "magic_font_scale")
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "imperial_precision")
|
||||
|
||||
|
||||
|
||||
# Scene panel groups
|
||||
@@ -694,6 +693,19 @@ class BIM_PT_tab_services(Panel):
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
class BIM_PT_tab_lighting(Panel):
|
||||
bl_label = "Lighting"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_zones(Panel):
|
||||
bl_label = "Zones"
|
||||
@@ -709,6 +721,20 @@ class BIM_PT_tab_zones(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_solar_analysis(Panel):
|
||||
bl_label = "Solar Analysis"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_quality_control(Panel):
|
||||
bl_label = "Quality Control"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -2,11 +2,33 @@ schema_version = "1.0.0"
|
||||
id = "blenderbim"
|
||||
version = "0.0.0"
|
||||
name = "BlenderBIM"
|
||||
tagline = "Transforms Blender into a native Building Information Model authoring platform using IFC."
|
||||
tagline = "A native Building Information Model authoring platform using IFC"
|
||||
maintainer = "Dion Moult <dion@thinkmoult.com>"
|
||||
type = "add-on"
|
||||
permissions = ["files"]
|
||||
website = "https://blenderbim.org/"
|
||||
tags = ["System"]
|
||||
blender_version_min = "3.5.0"
|
||||
tags = [
|
||||
"3D View",
|
||||
"Add Mesh",
|
||||
"Animation",
|
||||
"Camera",
|
||||
"Development",
|
||||
"Import-Export",
|
||||
"Lighting",
|
||||
"Material",
|
||||
"Modeling",
|
||||
"Mesh",
|
||||
"Node",
|
||||
"Object",
|
||||
"Pipeline",
|
||||
"Render",
|
||||
"Scene",
|
||||
"System",
|
||||
"User Interface",
|
||||
"UV"
|
||||
]
|
||||
blender_version_min = "4.2.0"
|
||||
license = ["SPDX:GPL-3.0-or-later"]
|
||||
platforms = ["os-arch"]
|
||||
permissions.network = "Augin / Cove.Tool integration for VR and environmental analysis"
|
||||
permissions.files = "Load and save IFCs, IDS, BCF, etc from disk"
|
||||
permissions.clipboard = "Copy and paste error logs"
|
||||
|
||||
@@ -45,22 +45,6 @@ def edit_georeferencing(ifc, georeference):
|
||||
georeference.set_model_origin()
|
||||
|
||||
|
||||
def set_ifc_grid_north(georeference):
|
||||
georeference.set_ifc_grid_north()
|
||||
|
||||
|
||||
def set_blender_grid_north(georeference):
|
||||
georeference.set_blender_grid_north()
|
||||
|
||||
|
||||
def set_ifc_true_north(georeference):
|
||||
georeference.set_ifc_true_north()
|
||||
|
||||
|
||||
def set_blender_true_north(georeference):
|
||||
georeference.set_blender_true_north()
|
||||
|
||||
|
||||
def get_cursor_location(georeference):
|
||||
georeference.set_coordinates("local", georeference.get_cursor_location())
|
||||
|
||||
|
||||
@@ -109,11 +109,8 @@ def copy_to_container(
|
||||
return result_objs
|
||||
|
||||
|
||||
def select_container(ifc: tool.Ifc, spatial: tool.Spatial, obj: bpy.types.Object) -> None:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
spatial.set_active_object(ifc.get_object(spatial.get_container(element)))
|
||||
def select_container(ifc: tool.Ifc, spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
|
||||
spatial.set_active_object(ifc.get_object(container))
|
||||
|
||||
|
||||
def select_similar_container(ifc: tool.Ifc, spatial: tool.Spatial, obj: bpy.types.Object) -> None:
|
||||
@@ -152,10 +149,8 @@ def delete_container(
|
||||
spatial.import_spatial_decomposition()
|
||||
|
||||
|
||||
def select_decomposed_elements(spatial: tool.Spatial) -> None:
|
||||
container = spatial.get_active_container()
|
||||
if container:
|
||||
spatial.select_products(spatial.get_decomposed_elements(container))
|
||||
def select_decomposed_elements(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(spatial.get_decomposed_elements(container))
|
||||
|
||||
|
||||
def generate_space(ifc: tool.Ifc, model: tool.Model, root: tool.Root, spatial: tool.Spatial, type: tool.Type) -> None:
|
||||
|
||||
@@ -27,6 +27,7 @@ import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim
|
||||
import addon_utils
|
||||
import types
|
||||
from mathutils import Vector
|
||||
from pathlib import Path
|
||||
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
|
||||
@@ -246,6 +247,13 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
if area.type == "VIEW_3D":
|
||||
return area
|
||||
|
||||
@classmethod
|
||||
def get_view3d_space(cls):
|
||||
if area := cls.get_view3d_area():
|
||||
for space in area.spaces:
|
||||
if space.type == "VIEW_3D":
|
||||
return space
|
||||
|
||||
@classmethod
|
||||
def get_blender_prop_default_value(cls, props, prop_name: str) -> Any:
|
||||
prop_bl_rna = props.bl_rna.properties[prop_name]
|
||||
@@ -909,6 +917,18 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
if child_obj:
|
||||
yield child_obj
|
||||
|
||||
@classmethod
|
||||
def get_last_commit_hash(cls) -> Union[str, None]:
|
||||
"""Get 8 symbols of last commit hash if it's present or return None otherwise."""
|
||||
commit_hash = blenderbim.bim.last_commit_hash
|
||||
|
||||
# Commit hash is unset - user is using __init__ from repo
|
||||
# without setting up git repository.
|
||||
if commit_hash == "8888888":
|
||||
return None
|
||||
|
||||
return commit_hash[:7]
|
||||
|
||||
@classmethod
|
||||
def get_blenderbim_version(cls):
|
||||
version = ".".join(
|
||||
@@ -921,8 +941,8 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
][0]
|
||||
]
|
||||
)
|
||||
if blenderbim.bim.last_commit_hash != "8888888":
|
||||
version += f"-{blenderbim.bim.last_commit_hash[:7]}"
|
||||
if commit_hash := cls.get_last_commit_hash():
|
||||
version += f"-{commit_hash}"
|
||||
return version
|
||||
|
||||
@classmethod
|
||||
@@ -1089,11 +1109,38 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
|
||||
@classmethod
|
||||
def get_blender_addon_package_name(cls) -> str:
|
||||
if bpy.app.version >= (4, 2, 0):
|
||||
return blenderbim.BLENDER_PACKAGE_NAME
|
||||
for package_name in bpy.context.preferences.addons.keys():
|
||||
if package_name.endswith(".blenderbim"):
|
||||
return package_name
|
||||
return "blenderbim"
|
||||
|
||||
@classmethod
|
||||
def get_addon_preferences(cls) -> blenderbim.bim.ui.BIM_ADDON_preferences:
|
||||
blender_package_name = cls.get_blender_addon_package_name()
|
||||
return bpy.context.preferences.addons[blender_package_name].preferences
|
||||
|
||||
@classmethod
|
||||
def get_sun_position_addon(cls) -> Union[types.ModuleType, None]:
|
||||
# Check if it's installed as legacy Blender addon.
|
||||
import importlib
|
||||
|
||||
try:
|
||||
sun_position = importlib.import_module("sun_position")
|
||||
except ImportError:
|
||||
sun_position = None
|
||||
|
||||
if sun_position:
|
||||
return sun_position
|
||||
|
||||
# No extensions prior to 4.2.
|
||||
if bpy.app.version < (4, 2, 0):
|
||||
return sun_position
|
||||
|
||||
if sun_position:
|
||||
return sun_position
|
||||
|
||||
for package_name in bpy.context.preferences.addons.keys():
|
||||
if package_name.endswith(".sun_position"):
|
||||
sun_position = importlib.import_module(package_name)
|
||||
|
||||
return sun_position
|
||||
|
||||
@@ -245,21 +245,6 @@ class Georeference(blenderbim.core.tool.Georeference):
|
||||
scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
bpy.context.scene.cursor.location = [co * scale for co in coordinates]
|
||||
|
||||
@classmethod
|
||||
def set_ifc_true_north(cls):
|
||||
y_angle = -bpy.context.scene.sun_pos_properties.north_offset + radians(90)
|
||||
bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(cos(y_angle))
|
||||
bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(sin(y_angle))
|
||||
|
||||
@classmethod
|
||||
def set_blender_true_north(cls):
|
||||
bpy.context.scene.sun_pos_properties.north_offset = -radians(
|
||||
ifcopenshell.util.geolocation.yaxis2angle(
|
||||
float(bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa),
|
||||
float(bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate),
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def xyz2enh(cls, coordinates, should_return_in_map_units=True):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
@@ -295,22 +280,6 @@ class Georeference(blenderbim.core.tool.Georeference):
|
||||
)
|
||||
return coordinates
|
||||
|
||||
@classmethod
|
||||
def set_ifc_grid_north(cls):
|
||||
x_angle = bpy.context.scene.sun_pos_properties.north_offset
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props.coordinate_operation.get("XAxisAbscissa").string_value = str(cos(x_angle))
|
||||
props.coordinate_operation.get("XAxisOrdinate").string_value = str(sin(x_angle))
|
||||
|
||||
@classmethod
|
||||
def set_blender_grid_north(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
angle = ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.coordinate_operation.get("XAxisAbscissa").string_value),
|
||||
float(props.coordinate_operation.get("XAxisOrdinate").string_value),
|
||||
)
|
||||
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
|
||||
|
||||
@classmethod
|
||||
def angle2coords(cls, angle, type):
|
||||
if type == "rel_x":
|
||||
|
||||
@@ -23,6 +23,7 @@ import bpy
|
||||
import bmesh
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.shape
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
|
||||
@@ -235,7 +235,13 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
container = tool.Ifc.get().by_id(container.ifc_definition_id)
|
||||
|
||||
results = {}
|
||||
for element in ifcopenshell.util.element.get_contained(container):
|
||||
if props.should_include_children:
|
||||
elements = ifcopenshell.util.element.get_decomposition(container, is_recursive=True)
|
||||
else:
|
||||
elements = ifcopenshell.util.element.get_contained(container)
|
||||
for element in elements:
|
||||
if not element.is_a("IfcElement"):
|
||||
continue
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
ifc_class = element.is_a()
|
||||
type_name = element_type.Name or "Unnamed" if element_type else "Untyped"
|
||||
|
||||
@@ -62,6 +62,11 @@ Sections
|
||||
: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`
|
||||
@@ -94,8 +99,13 @@ Sections
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/appendix/index`
|
||||
Troubleshooting and additional resources.
|
||||
:doc:`/users/other_addons`
|
||||
Other complementary add-ons
|
||||
|
||||
.. container:: card
|
||||
|
||||
:doc:`/users/troubleshooting`
|
||||
Troubleshooting when things go wrong
|
||||
|
||||
.. container:: global-index-toc
|
||||
|
||||
@@ -106,13 +116,15 @@ Sections
|
||||
|
||||
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/appendix/index
|
||||
users/other_addons
|
||||
users/troubleshooting
|
||||
|
||||
Get Involved
|
||||
============
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
Appendix
|
||||
========
|
||||
|
||||
This section provides troubleshooting, integration with other tools, and additional external resources.
|
||||
|
||||
..
|
||||
* Integration with Other Tools
|
||||
* Data Exchange and Compatibility
|
||||
..
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
troubleshooting
|
||||
git_support
|
||||
other_addons
|
||||
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
@@ -1,251 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell 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 #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import random
|
||||
import operator
|
||||
import warnings
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
try: # python 3.3+
|
||||
from collections.abc import Iterable
|
||||
except ModuleNotFoundError: # python 2
|
||||
from collections import Iterable
|
||||
|
||||
try:
|
||||
from OCC.Core import TopoDS, gp, Quantity, BRepTools
|
||||
|
||||
try:
|
||||
from OCC.Core import V3d, AIS, Graphic3d
|
||||
except ImportError:
|
||||
pass
|
||||
except ImportError:
|
||||
from OCC import TopoDS, gp, Quantity, BRepTools
|
||||
|
||||
try:
|
||||
from OCC import V3d, AIS, Graphic3d
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
shape_tuple = namedtuple("shape_tuple", ("data", "geometry", "styles"))
|
||||
|
||||
handle, main_loop, add_menu, add_function_to_menu = None, None, None, None
|
||||
|
||||
DEFAULT_STYLES = {
|
||||
"DEFAULT": (0.7, 0.7, 0.7),
|
||||
"IfcWall": (0.8, 0.8, 0.8),
|
||||
"IfcSite": (0.75, 0.8, 0.65),
|
||||
"IfcSlab": (0.4, 0.4, 0.4),
|
||||
"IfcWallStandardCase": (0.9, 0.9, 0.9),
|
||||
"IfcWall": (0.9, 0.9, 0.9),
|
||||
"IfcWindow": (0.75, 0.8, 0.75, 0.3),
|
||||
"IfcDoor": (0.55, 0.3, 0.15),
|
||||
"IfcBeam": (0.75, 0.7, 0.7),
|
||||
"IfcRailing": (0.65, 0.6, 0.6),
|
||||
"IfcMember": (0.65, 0.6, 0.6),
|
||||
"IfcPlate": (0.8, 0.8, 0.8),
|
||||
}
|
||||
|
||||
|
||||
def initialize_display():
|
||||
import OCC.Display.SimpleGui
|
||||
|
||||
global handle, main_loop, add_menu, add_function_to_menu
|
||||
handle, main_loop, add_menu, add_function_to_menu = OCC.Display.SimpleGui.init_display()
|
||||
|
||||
def setup():
|
||||
viewer_handle = handle.GetViewer()
|
||||
viewer = viewer_handle.GetObject()
|
||||
|
||||
def lights():
|
||||
viewer.InitActiveLights()
|
||||
while True:
|
||||
try:
|
||||
active_light = viewer.ActiveLight()
|
||||
except BaseException:
|
||||
break
|
||||
yield active_light
|
||||
viewer.NextActiveLights()
|
||||
|
||||
lights = list(lights())
|
||||
for l in lights:
|
||||
viewer.DelLight(l)
|
||||
|
||||
for dir in [(3, 2, 1), (-1, -2, -3)]:
|
||||
light = V3d.V3d_DirectionalLight(viewer_handle)
|
||||
light.SetDirection(*dir)
|
||||
viewer.SetLightOn(light.GetHandle())
|
||||
|
||||
setup()
|
||||
return handle
|
||||
|
||||
|
||||
def yield_subshapes(shape):
|
||||
it = TopoDS.TopoDS_Iterator(shape)
|
||||
while it.More():
|
||||
yield it.Value()
|
||||
it.Next()
|
||||
|
||||
|
||||
def display_shape(shape, clr=None, viewer_handle=None):
|
||||
if viewer_handle is None:
|
||||
viewer_handle = handle
|
||||
|
||||
if isinstance(shape, shape_tuple):
|
||||
shape, representation = shape.geometry, shape
|
||||
else:
|
||||
representation = None
|
||||
|
||||
material = Graphic3d.Graphic3d_MaterialAspect(Graphic3d.Graphic3d_NOM_PLASTER)
|
||||
material.SetDiffuse(1)
|
||||
|
||||
if representation and not clr:
|
||||
if len(set(representation.styles)) == 1:
|
||||
clr = representation.styles[0]
|
||||
if min(clr) < 0.0 or max(clr) > 1.0:
|
||||
clr = DEFAULT_STYLES.get(representation.data.type, DEFAULT_STYLES["DEFAULT"])
|
||||
|
||||
if clr:
|
||||
ais = AIS.AIS_Shape(shape)
|
||||
ais.SetMaterial(material)
|
||||
|
||||
if isinstance(clr, str):
|
||||
qclr = getattr(
|
||||
Quantity, "Quantity_NOC_%s" % clr.upper(), getattr(Quantity, "Quantity_NOC_%s1" % clr.upper(), None)
|
||||
)
|
||||
if qclr is None:
|
||||
raise Exception("No color named '%s'" % clr.upper())
|
||||
elif isinstance(clr, Iterable):
|
||||
clr = tuple(clr)
|
||||
if len(clr) < 3 or len(clr) > 4:
|
||||
raise Exception("Need 3 or 4 color components. Got '%r'." % len(clr))
|
||||
qclr = Quantity.Quantity_Color(clr[0], clr[1], clr[2], Quantity.Quantity_TOC_RGB)
|
||||
elif isinstance(clr, Quantity.Quantity_Color):
|
||||
qclr = clr
|
||||
else:
|
||||
raise Exception("Object of type %r cannot be used as a color." % type(clr))
|
||||
|
||||
ais.SetColor(qclr)
|
||||
if isinstance(clr, tuple) and len(clr) == 4 and clr[3] < 1.0:
|
||||
ais.SetTransparency(1.0 - clr[3])
|
||||
|
||||
elif representation and hasattr(AIS, "AIS_MultipleConnectedShape"):
|
||||
default_style_applied = None
|
||||
|
||||
ais = AIS.AIS_MultipleConnectedShape(shape)
|
||||
|
||||
subshapes = list(yield_subshapes(shape))
|
||||
lens = len(representation.styles), len(subshapes)
|
||||
if lens[0] != lens[1]:
|
||||
warnings.warn("Unable to assign styles to subshapes. Encountered %d styles for %d shapes." % lens)
|
||||
else:
|
||||
for shp, stl in zip(subshapes, representation.styles):
|
||||
subshape = AIS.AIS_Shape(shp)
|
||||
if min(stl) < 0.0 or max(stl) > 1.0:
|
||||
default_style_applied = stl = DEFAULT_STYLES.get(
|
||||
representation.data.type, DEFAULT_STYLES["DEFAULT"]
|
||||
)
|
||||
subshape.SetColor(Quantity.Quantity_Color(stl[0], stl[1], stl[2], Quantity.Quantity_TOC_RGB))
|
||||
subshape.SetMaterial(material)
|
||||
if len(stl) == 4 and stl[3] < 1.0:
|
||||
subshape.SetTransparency(1.0 - stl[3])
|
||||
ais.Connect(subshape.GetHandle())
|
||||
|
||||
# For some reason it is necessary to set transparency here again
|
||||
# in order for transparency to be rendered on the subshape.
|
||||
applied_styles = representation.styles
|
||||
if default_style_applied:
|
||||
if len(default_style_applied) == 3:
|
||||
default_style_applied += (1.0,)
|
||||
applied_styles += (default_style_applied,)
|
||||
|
||||
if len(applied_styles):
|
||||
# The only way for this not to be true if is the entire shape is NULL
|
||||
min_transp = min(map(operator.itemgetter(3), applied_styles))
|
||||
if min_transp < 1.0:
|
||||
ais.SetTransparency(1.0)
|
||||
|
||||
else:
|
||||
ais = AIS.AIS_Shape(shape)
|
||||
ais.SetMaterial(material)
|
||||
|
||||
def r():
|
||||
return random.random() * 0.3 + 0.7
|
||||
|
||||
clr = Quantity.Quantity_Color(r(), r(), r(), Quantity.Quantity_TOC_RGB)
|
||||
ais.SetColor(clr)
|
||||
|
||||
ais_handle = ais.GetHandle()
|
||||
viewer_handle.Context.Display(ais_handle, False)
|
||||
|
||||
return ais_handle
|
||||
|
||||
|
||||
def set_shape_transparency(ais, t):
|
||||
handle.Context.SetTransparency(ais, t)
|
||||
|
||||
|
||||
def get_bounding_box_center(bbox):
|
||||
bbmin = [0.0] * 3
|
||||
bbmax = [0.0] * 3
|
||||
bbmin[0], bbmin[1], bbmin[2], bbmax[0], bbmax[1], bbmax[2] = bbox.Get()
|
||||
return gp.gp_Pnt(*map(lambda xy: (xy[0] + xy[1]) / 2.0, zip(bbmin, bbmax)))
|
||||
|
||||
|
||||
def serialize_shape(shape):
|
||||
shapes = BRepTools.BRepTools_ShapeSet()
|
||||
shapes.Add(shape)
|
||||
return shapes.WriteToString()
|
||||
|
||||
|
||||
def create_shape_from_serialization(brep_object):
|
||||
brep_data, occ_shape, styles = None, None, ()
|
||||
|
||||
is_product_shape = True
|
||||
try:
|
||||
brep_data = brep_object.geometry.brep_data
|
||||
styles = brep_object.geometry.surface_styles
|
||||
except BaseException:
|
||||
try:
|
||||
brep_data = brep_object.brep_data
|
||||
styles = brep_object.surface_styles
|
||||
is_product_shape = False
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
styles = tuple(styles[i : i + 4] for i in range(0, len(styles), 4))
|
||||
|
||||
if not brep_data:
|
||||
return shape_tuple(brep_object, None, styles)
|
||||
|
||||
try:
|
||||
ss = BRepTools.BRepTools_ShapeSet()
|
||||
ss.ReadFromString(brep_data)
|
||||
occ_shape = ss.Shape(ss.NbShapes())
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
if is_product_shape:
|
||||
return shape_tuple(brep_object, occ_shape, styles)
|
||||
else:
|
||||
return occ_shape
|
||||
@@ -0,0 +1,30 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "blenderbim"
|
||||
version = "0.0.0"
|
||||
authors = [
|
||||
{ name="Dion Moult", email="dion@thinkmoult.com" },
|
||||
]
|
||||
description = "A native Building Information Model authoring platform using IFC"
|
||||
keywords = ["IFC", "Blender", "BIM"]
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
]
|
||||
dependencies = [
|
||||
"ifcopenshell",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "http://blenderbim.org"
|
||||
Documentation = "https://docs.blenderbim.org"
|
||||
Issues = "https://github.com/IfcOpenShell/IfcOpenShell/issues"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["blenderbim*"]
|
||||
exclude = ["test*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"*" = ["*.*"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user