Compare commits

...

104 Commits

Author SHA1 Message Date
Thomas Krijnen c0f2ac592e Consider edge orientation in faceset helper for advanced planar breps #4895 2024-07-22 13:07:40 +02:00
Thomas Krijnen 41f1d66fee Fix 2d transform op + fixed ref column order #5048 2024-07-22 04:33:35 +02:00
Dion Moult aa31436c67 Move git/other addons/troubleshooting to top level of docs 2024-07-22 12:13:46 +10:00
Thomas Krijnen e95e92356f Update src/ifcopenshell-python/ifcopenshell/util/shape.py
Co-authored-by: Vyacheslav Dobranov <112861900+VDobranov@users.noreply.github.com>
2024-07-22 03:51:12 +02:00
VDobranov e27980f746 Update shape.py
Following the discussion in Live Chat about length of all edges in a given geometry:
https://matrix.to/#/!ssOmSJysZpXBupsgjO:matrix.org/$0ZHjIFCtpwQl06xdGWlQ9ymsiKWpPuLMczVJnNYmN84?via=matrix.org&via=crossbach.de&via=infeeeee.duckdns.org
2024-07-22 03:51:12 +02:00
Ryan Schultz 3f3f3b6aff whoops
Revert "Update installation.rst"

This reverts commit a428ae5c86.
2024-07-21 12:50:49 -05:00
Thomas Krijnen 2b3035ec02 Lookup styles on (arbitrarily) more taxonomy kinds 2024-07-21 11:56:02 +02:00
Thomas Krijnen cc0760d505 profile_helper() returns loop, not face, take into account in hollow profile def 2024-07-21 11:55:18 +02:00
Thomas Krijnen f683fd220f Take into account tapered extrusion position 2024-07-21 11:54:48 +02:00
Thomas Krijnen 889d3c5451 Don't fail on tapered extrusion over non-derived profile def #5046 2024-07-21 11:53:42 +02:00
Thomas Krijnen c16cfc8afd Support extrusion over composite profile 2024-07-21 11:52:48 +02:00
Thomas Krijnen 4d698deb21 Various improvements to USD serializer related to object naming 2024-07-21 02:41:43 +02:00
Thomas Krijnen 3bbe598dff Minor formatting improvements 2024-07-21 02:40:56 +02:00
Thomas Krijnen e88bd3af80 Fix profile fillets #4993 2024-07-21 02:40:09 +02:00
Ryan Schultz a428ae5c86 Update installation.rst 2024-07-20 16:08:16 -05:00
Ryan Schultz 05e202059e fix #5047 - order IfcAnnotationTypes alphabetically.
and order types regardless of case.
2024-07-20 13:02:21 -05:00
Ryan Schultz ab97f23b47 Fix #5039 - Assure count is an integer 2024-07-20 10:36:26 -05:00
Kristoffer Andersen 4f0a65baea Update ci-ifcopenshell-conda-cleaner.yml
fix conda cleaner version ordering
2024-07-20 09:45:55 +02:00
Chirag Singh e88abd881d Fixed Issue #5033 2024-07-20 12:02:57 +05:30
Dion Moult d6d5dbfaa3 Fix semver syntax for BBIM GH daily builds 2024-07-20 15:51:26 +10:00
Dion Moult 3ea1f95fcf Bump version as we've already released 0.7.10
This makes the -alpha suffix actually work
2024-07-20 15:49:37 +10:00
Dion Moult 48026c7a50 Fix bug where adding a duct failed due to no container obj being calculated 2024-07-20 15:49:11 +10:00
Andrej730 8f2e8ec64d fix builds for macos 2024-07-19 22:22:22 +05:00
Andrej730 15945d854a fix bbim build missing very important aud wheel 2024-07-19 22:22:22 +05:00
Andrej730 f69c42aaae makefiles - windows support 2024-07-19 22:22:22 +05:00
Kristoffer Andersen b88e35e3e7 Update ci-ifcopenshell-conda-daily.yml
remove illegal conda version character -
2024-07-19 13:50:08 +02:00
Kristoffer Andersen 6befa2dc12 Update ci-ifcopenshell-conda-daily.yml
making legal semver by using -alpha
2024-07-19 13:45:12 +02:00
Andrej730 c25bb03a9c Blenderbim startup error handler - switch to scene properties
Noticed that by default Blender is using object properties (and users may have something else their startup file), so no we switch to the scene properties so they could see our error handler immediately.
2024-07-19 14:23:26 +05:00
Andrej730 4fdf8145f0 Fix incorrect python version detection for Blender 4.2
Basically this error message is back - https://i.imgur.com/ANQe0eg.png
2024-07-19 14:21:57 +05:00
Andrej730 bb910e42ab fix dependencies that should be downloaded for specific platform
Builds are finally working (atleast tested on windows) 🥳

Thanks to Blender now we'll always now if some dependency will become platform specific.

It shows errors like:
Skipping wheel for other system (manylinux_2_24_x86_64.manylinux_2_28_x86_64 != win_amd64): greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): ezdxf-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Skipping wheel for other system (manylinux_2_17_x86_64.manylinux2014_x86_64 != win_amd64): multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
2024-07-18 22:05:36 +05:00
Andrej730 13dce9d663 github action - fix finding zip files with new naming
encapsulated zip archive name generation in the makefile, so we wouldn't be recreating it multiple times
2024-07-18 21:30:09 +05:00
Andrej730 bda69555f6 blenderbim makefile - fix bbim translations build 2024-07-18 21:30:09 +05:00
Dion Moult e50f88a91c Whoops 2024-07-19 00:59:58 +10:00
Dion Moult fc7288b9e7 Fix missing bdist wheel during build 2024-07-19 00:56:16 +10:00
Andrej730 1355f3ab6b common.mk - windows support 2024-07-18 19:46:18 +05:00
Andrej730 d60c03b632 fix bbim daily github workflow not being triggered by changes in itself 2024-07-18 19:39:16 +05:00
Dion Moult b39020ce74 Fix yaml syntax 2024-07-19 00:26:56 +10:00
Dion Moult cb23b00913 Separate build/dist dir for bbim makefile 2024-07-19 00:26:07 +10:00
Dion Moult 268ab107e2 Forgot to update bimtester makefile 2024-07-18 23:57:44 +10:00
Dion Moult 734b4901fd Don't copy over the entire blenderbim dir when building 2024-07-18 23:52:24 +10:00
Dion Moult 0ecc3d3e2a Share common makefile instructions, use venv for running build, and separate dist/build dirs 2024-07-18 23:43:12 +10:00
Dion Moult 237cd99e3b Make bcf follow the same folder hierarchy as everybody else 2024-07-18 23:41:30 +10:00
Dion Moult 31acd0a39b Forgot PYTHON var 2024-07-18 22:44:25 +10:00
Dion Moult 7fb04c7d8f Wrap IOS make in venv 2024-07-18 22:40:43 +10:00
Dion Moult ad05434777 Add prerelease info, inline make dist-blenderbim 2024-07-18 22:29:08 +10:00
Dion Moult 99cb50d365 Forgot to commit IfcBIMTester pyproject 2024-07-18 22:24:25 +10:00
Andrej730 d29a201814 makefile - create wheels dir 2024-07-18 15:56:50 +05:00
Andrej730 fb702afee6 makefile - install build module globally
as we run make dist for ifcopenshell packages not inside venv
2024-07-18 15:48:18 +05:00
Andrej730 657906a2dd makefile - fix missing build module 2024-07-18 15:36:51 +05:00
Dion Moult 5b96b4a414 Bundle BBIM as a wheel
Yes we really did it
2024-07-18 20:24:17 +10:00
Dion Moult 30623ba333 Fix missing imports prior to deciding 4.2 import strategy 2024-07-18 20:24:17 +10:00
Andrej730 34652aa2b7 Create blenderbim_redirect.py 2024-07-18 20:24:17 +10:00
Dion Moult e7fd06a6d6 Fix non-valid Blender manifest (strings too long apparently) 2024-07-18 20:24:17 +10:00
Dion Moult f0077b9e22 Fix ifcopenshell-python wheel packaging missing util schema json files 2024-07-18 20:24:17 +10:00
Dion Moult 71aa30a25f Fix ifc5d packaging missing json mapping files 2024-07-18 20:24:17 +10:00
Dion Moult d96f960313 Add blender platform support 2024-07-18 20:24:17 +10:00
Dion Moult 5a2a809061 Bump permissions and tags in blender manifest 2024-07-18 20:24:17 +10:00
Andrej730 2710148276 fix bpypolyskel and ifcjson wheels 2024-07-18 20:24:17 +10:00
Andrej730 7ff1c535e5 build wheel for bpypolyskel 2024-07-18 20:24:17 +10:00
Dion Moult 71575905cb Remove behave and bimtester module - only used by advanced users and confuses users with ifctester 2024-07-18 20:24:17 +10:00
Andrej730 56573160d1 makefile - do not download pkg_resources as it's provided by blender
It seems Blender started providing pkg_resources by default at some point, see:
blender-4.2.0/4.2/python/lib/site-packages/pkg_resources
2024-07-18 20:24:17 +10:00
Andrej730 49555eb81e pack ifcjson to wheel 2024-07-18 20:24:17 +10:00
Dion Moult 9ed286660e WIP BlenderBIM makefile update 2024-07-18 20:24:17 +10:00
Dion Moult 3d2dfdf9cb Fix ifccityjson wheel 2024-07-18 20:24:17 +10:00
Dion Moult c3d4847b29 Minor fix to sun_position < Blender 4.2 2024-07-18 18:26:39 +10:00
Richard Brice 9bf68db080 Fixes mapping of cant segments without superelevations 2024-07-17 15:46:36 -07:00
Bruno Perdigão 798cc6069f Fix bug related to Linked Aggregates 2024-07-17 15:45:10 -03:00
Dion Moult 0efa36940c Package BIMTester as a wheel 2024-07-17 20:50:36 +10:00
Andrej730 6fbb1a286c support sun position addon on blender 4.2 2024-07-17 15:26:12 +05:00
Andrej730 7c606fea9b make sun_position an optional addon for bbim 2024-07-17 13:18:42 +05:00
Andrej730 ae97350e64 blender_manifest - fix version
Blender requires blender_manifest version to be semver compatible otherwise it fails with an error below if you try to install BBIM as an extension.

Error:
Error in manifest key "version" invalid: to be a semantic-version, found '0.7.10.240717'
2024-07-17 13:16:17 +05:00
Andrej730 3e7dbcaeb6 blender_manifest - bump min blender version
as manifest is used only for installing addon as blender extension in Blender 4.2+
2024-07-17 13:16:17 +05:00
Thomas Krijnen 4ad426a5c5 Update IfcParse.cpp #4998 2024-07-17 09:45:34 +02:00
Dion Moult eeb200a19d Minor UI polish to show structural panels by default 2024-07-17 17:37:20 +10:00
Dion Moult 766eb75e3a Deprecate old set viewport shadow from sun in favour of new solar analysis tools 2024-07-17 17:37:09 +10:00
Dion Moult 607f79f2f5 New feature to set view from sun for solar analysis 2024-07-17 17:36:50 +10:00
Dion Moult 70f252988a Purge deprecated "set north" operators from georeference superseded by new visualisation features 2024-07-17 17:36:03 +10:00
Dion Moult f956545743 Fix tzfpy packaging 2024-07-17 16:45:35 +10:00
Dion Moult 6f8aac96f0 Fix #5027. Forgot to commit some files for solar analysis. 2024-07-17 14:57:06 +10:00
Dion Moult a2018a22a6 Add sun path visualisation for solar analysis 2024-07-17 13:59:01 +10:00
Thomas Krijnen 7a10edbd73 Fix ambiguous overload after dedbf7014a 2024-07-17 03:43:55 +02:00
Thomas Krijnen 669b0b8c83 Fix triangulated face set #5020 2024-07-17 03:26:23 +02:00
Thomas Krijnen 70336f179d Keep identities on indexed face sets #5020 2024-07-17 03:12:35 +02:00
Dion Moult 7fc7a73db8 New panel to manage solar analysis setup (location, date, and time) 2024-07-16 23:13:02 +10:00
Andrej730 41065f77d1 BBIM startup error handler - fix error redownloading addon after 5e6fe5e 2024-07-16 18:02:26 +05:00
Andrej730 010f854a07 BBIM startup error handler - message on python version mismatch
Example - https://i.imgur.com/U5CBdqU.png
Logs (only startup error logs) now also have information about binary python version and binary filename.
2024-07-16 18:02:26 +05:00
Andrej730 c0635afb64 BBIM error handler - include commit hash
Include commit hash to the copied logs and add it to the startup error handler (https://imgur.com/9yVMBnR)
2024-07-16 18:02:26 +05:00
Andrej730 dedbf7014a fill context attribute of the shape elements (similar to v0.7.0) #4832 2024-07-16 18:02:26 +05:00
Dion Moult d8cf6ca4fc Bundle pytz and tzfpy for timezone calculations for solar analysis 2024-07-16 22:56:51 +10:00
Andrej730 f1ab63dda9 pyradiance macos support + linux fix
See:
https://github.com/LBNL-ETA/pyradiance/issues/35
https://github.com/LBNL-ETA/pyradiance/issues/34
2024-07-16 12:11:11 +05:00
Andrej730 3a5c3d856a quick fix for 54fe0f8ff 2024-07-16 11:17:36 +05:00
Andrej730 54fe0f8ff9 Remove blenderbim/occ_utils.py #812
Goodbye duplicated file 😁
2024-07-16 11:06:49 +05:00
Dion Moult 483516a902 Implement show / hide / isolate in spatial decomposition panel 2024-07-16 14:23:30 +10:00
Dion Moult 83085ba3c1 You can now toggle recursive children in the spatial tree 2024-07-15 23:56:35 +10:00
Dion Moult 215866cf30 You can now make a spatial element the active object from the spatial decomposition panel 2024-07-15 23:40:40 +10:00
Andrej730 09d5fafdf8 note on OCC imports 2024-07-15 18:34:39 +05:00
Andrej730 54cc4192bd typing for pythonocc types 2024-07-15 18:34:39 +05:00
Andrej730 eff778d5be support pythonocc >= 7.8.0 #4942
Related: https://github.com/tpaviot/pythonocc-core/issues/1350
Also added a print so this type of errors won't go silently.
2024-07-15 18:34:38 +05:00
Andrej730 66877c227b typing 2024-07-15 18:34:38 +05:00
Andrej730 7e117bca90 Move supported OCCT version in docs to general dependencies 2024-07-15 18:34:38 +05:00
Andrej730 9f084558f6 mathutils note 2024-07-15 18:34:38 +05:00
Dion Moult c715d9a81b A spatial tree's icon can now be hovered to show the class and clicked to select children 2024-07-15 23:29:53 +10:00
Dion Moult 8923d02f68 Update to latest Sphinx AutoAPI 2024-07-15 09:20:56 +10:00
Dion Moult 447a13b6e7 Forgot to do the same for PyPI 2024-07-14 23:31:34 +10:00
148 changed files with 1881 additions and 1579 deletions
+15 -11
View File
@@ -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:
+1 -1
View File
@@ -1 +1 @@
0.7.10
0.7.11
+2 -16
View File
@@ -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
View File
@@ -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:
+96 -28
View File
@@ -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"}
+2 -13
View File
@@ -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)
+2 -2
View File
@@ -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
+27 -1
View File
@@ -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())
+4 -9
View File
@@ -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:
+51 -4
View File
@@ -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":
+1
View File
@@ -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
+7 -1
View File
@@ -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"
+15 -3
View File
@@ -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

-251
View File
@@ -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
+30
View File
@@ -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