mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 16:01:36 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b709f355ae | |||
| de2d2c4e3b | |||
| 901306a3fd | |||
| b52a23cb93 | |||
| b45cbefdd9 | |||
| a95c9fcaab | |||
| c078b5f255 | |||
| 7197d79dd9 | |||
| c3ca969e32 | |||
| 49bda7d8ba | |||
| 4c1f79f6ab | |||
| 5de76cf5f9 | |||
| 76f073d9e7 | |||
| c23dfcf82e | |||
| 80922fcd9a | |||
| 75cfbd40b1 | |||
| cd8199d3c4 | |||
| ad2dc8c38e | |||
| 4d564f00aa | |||
| 5b3960c9e5 | |||
| 01d7c4dea8 | |||
| 1d56736d75 | |||
| 06548c5db2 | |||
| b02321e3ed | |||
| 3d2104d689 | |||
| 9ecc587cb9 | |||
| e42acdfa86 | |||
| bf5b48d037 | |||
| 1cb298fa9c | |||
| 1801a768c1 | |||
| 66f0b71c65 | |||
| f4d7fda255 | |||
| c112c115ce | |||
| 2a2ba45676 | |||
| fd69c0534b | |||
| 1226370b9e | |||
| 6e3eaa9b39 | |||
| 3ed2da23be | |||
| 249c498112 | |||
| 2dde62803d | |||
| d4ac3c36ce | |||
| fecd4a810a | |||
| 060afb94b5 | |||
| b1f1de954d | |||
| 12ed2d1ec3 | |||
| c77983ffe5 | |||
| 78f26c2432 | |||
| 39161e7256 |
+2
-3
@@ -1,8 +1,7 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/0.24.0/gersemi/configuration.schema.json
|
||||
|
||||
# Gersemi doesn't support autodetection of macros/functions from other files or from the current one
|
||||
# and requires to explicitly list directories/cmake files that define them.
|
||||
definitions: ["./cmake", "./src"]
|
||||
# Needed for gersemi to detect custom functions and macros.
|
||||
definitions: ["./cmake"]
|
||||
disable_formatting: false
|
||||
extensions: []
|
||||
indent: 4
|
||||
|
||||
@@ -40,8 +40,6 @@ jobs:
|
||||
# preinstalled: xz, cmake
|
||||
brew install git bison autoconf automake libffi findutils
|
||||
echo "$(brew --prefix findutils)/libexec/gnubin" >> $GITHUB_PATH
|
||||
# Mac is using bison 2.5 by default, but we need 3.5+ for swig.
|
||||
echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install aws cli
|
||||
run: |
|
||||
@@ -49,11 +47,11 @@ jobs:
|
||||
|
||||
- name: Unpack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python ../nix/cache_dependencies.py unpack
|
||||
install_root=$(find ./build -maxdepth 4 -type d -name install 2>/dev/null | head -n 1 || true)
|
||||
[ -n "$install_root" ] && find "$install_root" -type f -name 'cache-*.tar.gz' -maxdepth 1 -exec tar -xzf {} -C "$install_root" \; || true
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: mac-${{ matrix.arch }}
|
||||
|
||||
@@ -83,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Upload Build Logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-logs-osx-${{ matrix.arch }}
|
||||
path: |
|
||||
@@ -95,7 +93,9 @@ jobs:
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python ../nix/cache_dependencies.py pack
|
||||
for install_dir in $(find $(find . -maxdepth 4 -name install) -mindepth 1 -maxdepth 1 -type d); do
|
||||
test -f $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz || tar -czf $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz -C $(dirname "$install_dir") $(basename "$install_dir");
|
||||
done
|
||||
|
||||
- name: Commit and Push Changes to Build Repository
|
||||
run: |
|
||||
|
||||
@@ -26,10 +26,10 @@ jobs:
|
||||
- name: Unpack Dependencies
|
||||
run: |
|
||||
cd ifcopenshell_build
|
||||
python ../IfcOpenShell/nix/cache_dependencies.py unpack
|
||||
python ../IfcOpenShell/pyodide/cache_dependencies.py unpack
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ubuntu-22.04-${{ runner.arch }}
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Upload Build Logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-logs-pyodide
|
||||
path: |
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd ifcopenshell_build
|
||||
python ../IfcOpenShell/nix/cache_dependencies.py pack
|
||||
python ../IfcOpenShell/pyodide/cache_dependencies.py pack
|
||||
|
||||
- name: Commit and Push Changes to Build Repository
|
||||
run: |
|
||||
|
||||
@@ -6,13 +6,13 @@ on:
|
||||
jobs:
|
||||
build_ifcopenshell:
|
||||
runs-on: ubuntu-22.04
|
||||
container: rockylinux:9
|
||||
container: rockylinux:8
|
||||
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
dnf update -y
|
||||
dnf install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 python3-pip \
|
||||
yum update -y
|
||||
yum install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 \
|
||||
bzip2 patch mesa-libGL-devel libffi-devel fontconfig-devel \
|
||||
sqlite-devel bzip2-devel zlib-devel openssl-devel xz-devel \
|
||||
readline-devel ncurses-devel libffi-devel libuuid-devel git-lfs \
|
||||
@@ -38,19 +38,20 @@ jobs:
|
||||
with:
|
||||
repository: IfcOpenShell/build-outputs
|
||||
path: ./build
|
||||
ref: rockylinux9-x64
|
||||
ref: rockylinux8-x64
|
||||
lfs: true
|
||||
token: ${{ secrets.BUILD_REPO_TOKEN }}
|
||||
|
||||
- name: Unpack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python3 ../nix/cache_dependencies.py unpack
|
||||
install_root=$(find ./build -maxdepth 4 -type d -name install 2>/dev/null | head -n 1 || true)
|
||||
[ -n "$install_root" ] && find "$install_root" -type f -name 'cache-*.tar.gz' -maxdepth 1 -exec tar -xzf {} -C "$install_root" \; || true
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
# TODO: Use tag after 1.2.20 releases.
|
||||
uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639
|
||||
with:
|
||||
key: ubuntu-22.04-${{ runner.arch }}-rockylinux9
|
||||
key: ubuntu-22.04-${{ runner.arch }}-rockylinux8
|
||||
|
||||
- name: Run Build Script
|
||||
shell: bash
|
||||
@@ -60,7 +61,7 @@ jobs:
|
||||
|
||||
- name: Upload Build Logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-logs-rocky
|
||||
path: |
|
||||
@@ -71,7 +72,9 @@ jobs:
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python3 ../nix/cache_dependencies.py pack
|
||||
for install_dir in $(find $(find . -maxdepth 4 -name install) -mindepth 1 -maxdepth 1 -type d); do
|
||||
test -f $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz || tar -czf $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz -C $(dirname "$install_dir") $(basename "$install_dir");
|
||||
done
|
||||
|
||||
- name: Commit and Push Changes to Build Repository
|
||||
run: |
|
||||
|
||||
@@ -6,13 +6,13 @@ on:
|
||||
jobs:
|
||||
build_ifcopenshell:
|
||||
runs-on: ubuntu-22.04-arm
|
||||
container: arm64v8/rockylinux:9
|
||||
container: arm64v8/rockylinux:8
|
||||
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
dnf update -y
|
||||
dnf install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 python3-pip \
|
||||
yum update -y
|
||||
yum install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 \
|
||||
bzip2 patch mesa-libGL-devel libffi-devel fontconfig-devel \
|
||||
sqlite-devel bzip2-devel zlib-devel openssl-devel xz-devel \
|
||||
readline-devel ncurses-devel libffi-devel libuuid-devel git-lfs \
|
||||
@@ -38,19 +38,20 @@ jobs:
|
||||
with:
|
||||
repository: IfcOpenShell/build-outputs
|
||||
path: ./build
|
||||
ref: rockylinux9-arm64
|
||||
ref: rockylinux8-arm64
|
||||
lfs: true
|
||||
token: ${{ secrets.BUILD_REPO_TOKEN }}
|
||||
|
||||
- name: Unpack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python3 ../nix/cache_dependencies.py unpack
|
||||
install_root=$(find ./build -maxdepth 4 -type d -name install 2>/dev/null | head -n 1 || true)
|
||||
[ -n "$install_root" ] && find "$install_root" -type f -name 'cache-*.tar.gz' -maxdepth 1 -exec tar -xzf {} -C "$install_root" \; || true
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
# TODO: Use tag after 1.2.20 releases.
|
||||
uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639
|
||||
with:
|
||||
key: ubuntu-22.04-${{ runner.arch }}-rockylinux9
|
||||
key: ubuntu-22.04-${{ runner.arch }}-rockylinux8
|
||||
|
||||
- name: Run Build Script
|
||||
shell: bash
|
||||
@@ -60,7 +61,7 @@ jobs:
|
||||
|
||||
- name: Upload Build Logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-logs-rocky-arm64
|
||||
path: |
|
||||
@@ -71,7 +72,9 @@ jobs:
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd build
|
||||
python3 ../nix/cache_dependencies.py pack
|
||||
for install_dir in $(find $(find . -maxdepth 4 -name install) -mindepth 1 -maxdepth 1 -type d); do
|
||||
test -f $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz || tar -czf $(dirname "$install_dir")/cache-$(basename "$install_dir").tar.gz -C $(dirname "$install_dir") $(basename "$install_dir");
|
||||
done
|
||||
|
||||
- name: Commit and Push Changes to Build Repository
|
||||
run: |
|
||||
|
||||
@@ -5,26 +5,11 @@ on:
|
||||
|
||||
jobs:
|
||||
build_ifcopenshell:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
runs_on: windows-2022
|
||||
deps_dir: _deps-vs2022-x64-installed
|
||||
vcvars: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"'
|
||||
build_branch: windows-x64
|
||||
zip_suffix: win64
|
||||
|
||||
- arch: ARM64
|
||||
runs_on: windows-11-arm
|
||||
deps_dir: _deps-vs2022-ARM64-installed
|
||||
vcvars: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsarm64.bat"'
|
||||
build_branch: windows-arm64
|
||||
zip_suffix: win-arm64
|
||||
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
|
||||
arch: ['x64']
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
@@ -35,8 +20,8 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: IfcOpenShell/build-outputs
|
||||
path: ${{ matrix.deps_dir }}
|
||||
ref: ${{ matrix.build_branch }}
|
||||
path: _deps-vs2022-x64-installed
|
||||
ref: windows-${{ matrix.arch }}
|
||||
lfs: true
|
||||
token: ${{ secrets.BUILD_REPO_TOKEN }}
|
||||
|
||||
@@ -46,13 +31,14 @@ jobs:
|
||||
|
||||
- name: Unpack Dependencies
|
||||
run: |
|
||||
cd ${{ matrix.deps_dir }}
|
||||
cd _deps-vs2022-x64-installed
|
||||
Get-ChildItem -Path . -Filter 'cache-*.zip' | ForEach-Object {
|
||||
7z x $_.FullName
|
||||
}
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
# TODO: Use tag after 1.2.20 releases.
|
||||
uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639
|
||||
with:
|
||||
key: win-${{ matrix.arch }}
|
||||
# Windows ccache needs ~1GB
|
||||
@@ -61,16 +47,14 @@ jobs:
|
||||
|
||||
- name: Run Build Script And Pack .zip Archives
|
||||
shell: cmd
|
||||
env:
|
||||
TARGET_ARCH: ${{ matrix.arch }} # lets the Python script know which arch to target (optional override)
|
||||
run: |
|
||||
call ${{ matrix.vcvars }}
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
cd win
|
||||
python build-all-win.py
|
||||
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd ${{ matrix.deps_dir }}
|
||||
cd _deps-vs2022-x64-installed
|
||||
Get-ChildItem -Path . -Directory | ForEach-Object {
|
||||
$cacheFile = "cache-$($_.Name).zip"
|
||||
echo $cacheFile
|
||||
@@ -81,13 +65,12 @@ jobs:
|
||||
|
||||
- name: Commit and Push Changes to Build Repository
|
||||
run: |
|
||||
cd ${{ matrix.deps_dir }}
|
||||
cd _deps-vs2022-x64-installed
|
||||
git config user.name "IfcOpenBot"
|
||||
git config user.email "ifcopenbot@ifcopenshell.org"
|
||||
git checkout -B ${{ matrix.build_branch }}
|
||||
git add *.zip
|
||||
git commit -m "Update build artifacts [skip ci]" || echo "No changes to commit"
|
||||
git push --set-upstream origin ${{ matrix.build_branch }} || echo "Push failed"
|
||||
git push || echo "Push failed"
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
|
||||
@@ -7,9 +7,6 @@ on:
|
||||
jobs:
|
||||
lint-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MIN_IOS_PY_VERSION: "3.10"
|
||||
MIN_BLENDER_PY_VERSION: "3.11"
|
||||
steps:
|
||||
- name: Action - checkout repository
|
||||
uses: actions/checkout@v6
|
||||
@@ -17,12 +14,12 @@ jobs:
|
||||
- name: Action - install python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.MIN_IOS_PY_VERSION }}
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Action - install python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.MIN_BLENDER_PY_VERSION }}
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -36,10 +33,8 @@ jobs:
|
||||
id: syntax-errors
|
||||
run: |
|
||||
ERROR=0
|
||||
# Using 2 Python versions - one minimum required for IfcOpenShell
|
||||
# and other that's used by Blender currently.
|
||||
python${{ env.MIN_IOS_PY_VERSION }} -W error -m compileall -q src/ifcopenshell-python || ERROR=1
|
||||
python${{ env.MIN_BLENDER_PY_VERSION }} -W error -m compileall -q src/bonsai || ERROR=1
|
||||
python3.10 -W error -m compileall -q src/ifcopenshell-python || ERROR=1
|
||||
python3.11 -W error -m compileall -q src/bonsai || ERROR=1
|
||||
exit $ERROR
|
||||
continue-on-error: true
|
||||
|
||||
|
||||
@@ -24,15 +24,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
outputs:
|
||||
timestamp: ${{ steps.timestamp.outputs.timestamp }}
|
||||
steps:
|
||||
- name: Get current timestamp
|
||||
id: timestamp
|
||||
# Include hours and minutes to release tag
|
||||
# to avoid possibility of unstable repo's index.json
|
||||
# pointing to the new file when index.json itself wasn't yet updated.
|
||||
run: echo "timestamp=$(date +'%y%m%d%H%M')" >> $GITHUB_OUTPUT
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
@@ -73,6 +67,12 @@ jobs:
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Get current date
|
||||
id: date
|
||||
# Include hours and minutes to release tag
|
||||
# to avoid possibility of unstable repo's index.json
|
||||
# pointing to the new file when index.json itself wasn't yet updated.
|
||||
run: echo "date=$(date +'%y%m%d%H%M')" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cd src/bonsai && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
@@ -88,8 +88,8 @@ jobs:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.find_zip.outputs.filepath }}
|
||||
asset_name: ${{ steps.find_zip.outputs.filename }}
|
||||
release_name: "bonsai-${{steps.version.outputs.version}}-alpha${{ needs.activate.outputs.timestamp }} (unstable)"
|
||||
tag: "bonsai-${{steps.version.outputs.version}}-alpha${{ needs.activate.outputs.timestamp }}"
|
||||
release_name: "bonsai-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "bonsai-${{steps.version.outputs.version}}-alpha${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
body: "See README in https://github.com/IfcOpenShell/bonsai_unstable_repo/ on how to setup autoupdates for daily Bonsai builds."
|
||||
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
# Ensure Bonsai and ifcsverchok enable/disable works before uploading to extensions repo.
|
||||
|
||||
# Download Blender.
|
||||
wget -q -O blender.tar.xz https://download.blender.org/release/Blender5.0/blender-5.1.0-linux-x64.tar.xz
|
||||
wget -q -O blender.tar.xz https://download.blender.org/release/Blender5.0/blender-5.0.1-linux-x64.tar.xz
|
||||
tar -xf blender.tar.xz
|
||||
|
||||
# Setup Blender.
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
name: ci-ifcedit-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcedit &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcedit/dist
|
||||
@@ -1,36 +0,0 @@
|
||||
name: ci-ifcmcp-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcmcp &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcmcp/dist
|
||||
verbose: true
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- uses: mamba-org/setup-micromamba@v3 # https://github.com/mamba-org/setup-micromamba
|
||||
- uses: mamba-org/setup-micromamba@v2 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
environment-name: test-env
|
||||
create-args: >-
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
run: |
|
||||
curl -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz | tar -xvJf - -C /Users/runner/work/
|
||||
|
||||
- uses: mamba-org/setup-micromamba@v3 # https://github.com/mamba-org/setup-micromamba
|
||||
- uses: mamba-org/setup-micromamba@v2 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
environment-name: test-env
|
||||
create-args: >-
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
-
|
||||
name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
-
|
||||
name: Build ifcopenshell
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
make package
|
||||
working-directory: build
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: ifcos-artifacts
|
||||
@@ -91,26 +91,26 @@ jobs:
|
||||
lfs: true
|
||||
|
||||
- name: Download
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: ifcos-artifacts
|
||||
path: artifacts/
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to Dockerhub
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: aecgeeks
|
||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
-
|
||||
name: Build container image
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: artifacts
|
||||
repository: aecgeeks/ifcopenshell
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py310, py311, py312, py313, py314]
|
||||
pyver: [py39, py310, py311, py312, py313, py314]
|
||||
config:
|
||||
- {
|
||||
name: "Windows 64bit",
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py310, py311, py312, py313, py314]
|
||||
pyver: [py39, py310, py311, py312, py313, py314]
|
||||
config:
|
||||
- {
|
||||
name: "Windows 64bit",
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
name: ci-ifcquery-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcquery &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcquery/dist
|
||||
@@ -79,7 +79,10 @@ jobs:
|
||||
libhdf5-dev libcgal-dev libeigen3-dev
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.22
|
||||
# TODO: temporarily pointing to 1.2.19 to get notified by dependabot when 1.2.20 is released
|
||||
# to update hardcoded references to commits in some other workflows.
|
||||
# Then we can switch back to 1.2 in all actions.
|
||||
uses: hendrikmuhs/ccache-action@v1.2.19
|
||||
with:
|
||||
key: ubuntu-22.04-${{ runner.arch }}
|
||||
|
||||
@@ -181,7 +184,6 @@ jobs:
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DWITH_ROCKSDB=On \
|
||||
-DBUILD_EXAMPLES=ON \
|
||||
../cmake
|
||||
sudo make -j $(nproc)
|
||||
sudo make install
|
||||
@@ -254,7 +256,6 @@ jobs:
|
||||
cd ../ifcpatch && make test || ERROR=1
|
||||
pip install -e ../ifctester --no-deps
|
||||
cd ../ifctester && make test || ERROR=1
|
||||
make build-ids-docs || ERROR=1
|
||||
# Run mathutils related tests at the end to ensure no other code is relying on mathutils.
|
||||
cd ../ifcopenshell-python
|
||||
pip install mathutils
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
name: Deploy AI chat App to static page repo
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
pages: write
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/ifcchat/**'
|
||||
- '.github/workflows/publish-aichat-app.yaml'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout (recursive)
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Checkout intermediate Pages repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: IfcOpenShell/aichat_ifcopenshell_org_static_html
|
||||
ref: gh-pages
|
||||
path: output
|
||||
token: ${{ secrets.WEBSITE_PUBLISH }}
|
||||
- name: Sync demo app into target subfolder
|
||||
run: |
|
||||
rsync -av --delete --exclude='.git/' src/ifcchat/ output/
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Download wheels
|
||||
working-directory: output/
|
||||
run: |
|
||||
pip download ifcquery==0.8.5 ifcopenshell-mcp==0.8.5 ifcedit==0.8.5 lark==1.3.1 isodate==0.7.2 --no-deps -d ./dist
|
||||
- name: Commit and push if changed
|
||||
working-directory: output
|
||||
run: |
|
||||
git config --global user.name 'IfcOpenBot'
|
||||
git config --global user.email 'IfcOpenBot@users.noreply.github.com'
|
||||
|
||||
git add .
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "$(git log --oneline -1)"
|
||||
git push origin gh-pages
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Deploy Pyodide Demo App to static page repo
|
||||
name: Deploy Pyodide Demo App to GitHub Pages
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -11,7 +11,6 @@ on:
|
||||
- '.github/workflows/publish-pyodide-demo-app.yml'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
@@ -31,27 +30,21 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Checkout intermediate Pages repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload static files as artifact
|
||||
id: deployment
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
repository: IfcOpenShell/wasm_ifcopenshell_org_static_html
|
||||
ref: gh-pages
|
||||
path: output
|
||||
token: ${{ secrets.WEBSITE_PUBLISH }}
|
||||
- name: Sync demo app into target subfolder
|
||||
run: |
|
||||
rsync -av --delete --exclude='.git/' src/pyodide/demo-app/ output/
|
||||
- name: Commit and push if changed
|
||||
working-directory: output
|
||||
run: |
|
||||
git config --global user.name 'IfcOpenBot'
|
||||
git config --global user.email 'IfcOpenBot@users.noreply.github.com'
|
||||
path: src/pyodide/demo-app/
|
||||
|
||||
git add .
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "$(git log --oneline -1)"
|
||||
git push origin gh-pages
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
+2
-16
@@ -5,8 +5,6 @@
|
||||
/_installed-vs*-x*/
|
||||
/build/
|
||||
/src/examples/build/
|
||||
# ifctester docs output
|
||||
/src/ifctester/test/build/
|
||||
|
||||
# output directories
|
||||
/cmake/out/
|
||||
@@ -14,7 +12,6 @@
|
||||
/src/ifcmax/out/
|
||||
/src/ifcwrap/out/
|
||||
/src/qtviewer/out/
|
||||
/src/ifctester/webapp/public/pyodide/
|
||||
|
||||
/win/BuildDepsCache*.txt
|
||||
|
||||
@@ -83,14 +80,8 @@ src/ifcopenshell-python/test/build
|
||||
# bonsai i18n
|
||||
src/bonsai/bonsai/translations.py
|
||||
|
||||
# bonsai external dependencies (cloned for just ty checks)
|
||||
src/bonsai/external_dependencies/
|
||||
|
||||
# bonsai test temp/cache files
|
||||
# bonsai test temp files
|
||||
src/bonsai/test/files/temp
|
||||
src/bonsai/test/files/*.cache.blend
|
||||
src/bonsai/test/files/*.cache.json
|
||||
src/bonsai/test/files/*.cache.sqlite
|
||||
|
||||
# bonsai data
|
||||
src/bonsai/bonsai/bim/data/build/
|
||||
@@ -122,9 +113,4 @@ dev_environment.bat
|
||||
|
||||
src/ifcopenshell-python/ifcopenshell/express/*.exp
|
||||
src/ifcopenshell-python/ifcopenshell/express/*.exp.cache.dat
|
||||
|
||||
|
||||
# temp files from AI coding tools
|
||||
*.claude
|
||||
*.py.tmp*
|
||||
*.json.tmp*
|
||||
src/ifcquery/build/
|
||||
|
||||
@@ -50,13 +50,10 @@ Contents
|
||||
| [ifcconvert](https://docs.ifcopenshell.org/ifcconvert.html) | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcconvert&expanded=true)
|
||||
| [ifccsv](https://docs.ifcopenshell.org/ifccsv.html) | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccsv/) |
|
||||
| [ifcdiff](https://docs.ifcopenshell.org/ifcdiff.html) | Compare changes between IFC models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcdiff/) |
|
||||
| [ifcedit](https://docs.ifcopenshell.org/ifcedit.html) | CLI wrapper for ifcopenshell.api IFC model mutation functions | LGPL-3.0-or-later | [](https://pypi.org/project/ifcedit/) |
|
||||
| [ifcfm](https://docs.ifcopenshell.org/ifcfm.html) | Extract IFC data for FM handover requirements | LGPL-3.0-or-later | [](https://pypi.org/project/ifcfm/) |
|
||||
| [ifcmax](https://docs.ifcopenshell.org/ifcmax.html) | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcmax.html)
|
||||
| [ifcmcp](https://docs.ifcopenshell.org/ifcmcp.html) | MCP server for querying and editing IFC building models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcmcp/) |
|
||||
| [ifcopenshell-python](https://docs.ifcopenshell.org/ifcopenshell-python.html) | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) [Pyodide WASM Wheels](https://github.com/IfcOpenShell/wasm-wheels#pyodide-test-wheels) |
|
||||
| [ifcpatch](https://docs.ifcopenshell.org/ifcpatch.html) | Utility to run pre-packaged scripts to manipulate IFCs | LGPL-3.0-or-later | [](https://pypi.org/project/ifcpatch/) |
|
||||
| [ifcquery](https://docs.ifcopenshell.org/ifcquery.html) | CLI tool for querying and inspecting IFC building models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcquery/) |
|
||||
| [ifcsverchok](https://docs.ifcopenshell.org/ifcsverchok.html) | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcsverchok&expanded=true)
|
||||
| [ifctester](https://docs.ifcopenshell.org/ifctester.html) | Library, CLI and webapp for IDS model auditing | LGPL-3.0-or-later | [](https://pypi.org/project/ifctester/) |
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import hashlib
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
from typing import NoReturn
|
||||
from urllib import request
|
||||
|
||||
@@ -21,7 +20,7 @@ from github import Github
|
||||
|
||||
|
||||
def get_repo_tag_names() -> list[str]:
|
||||
git_return = subprocess.check_output("git tag -l", text=True)
|
||||
git_return = os.popen("git tag -l").read()
|
||||
tag_names = [tag_name for tag_name in git_return.split("\n") if tag_name]
|
||||
print(f"{len(tag_names)} tag_names found in repo")
|
||||
return tag_names
|
||||
@@ -79,10 +78,6 @@ def get_release_zip(tag: str) -> tuple[str, str]:
|
||||
raise Exception(f"Couldn't find the release matching '{python_version}' and '{TARGET_OS}' in tag '{tag}'.")
|
||||
|
||||
|
||||
def run(command: str) -> None:
|
||||
subprocess.check_output(command)
|
||||
|
||||
|
||||
start = datetime.datetime.now()
|
||||
|
||||
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
|
||||
@@ -102,7 +97,7 @@ should_release = False
|
||||
target_release_tag = ""
|
||||
TARGET_OS = "windows-x64"
|
||||
|
||||
git_status = subprocess.check_output("git status", text=True)
|
||||
git_status = os.popen("git status").read()
|
||||
print(git_status)
|
||||
|
||||
for tag_name in get_repo_tag_names():
|
||||
@@ -152,7 +147,7 @@ blenderbim_build_version = target_release_tag.replace("blenderbim-", "")
|
||||
|
||||
# url_blenderbim_py3x_win_zip
|
||||
release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag)
|
||||
subprocess.check_call(f"wget {url_blenderbim_py3x_win_zip} --no-verbose")
|
||||
os.popen(f"wget {url_blenderbim_py3x_win_zip} --no-verbose").read()
|
||||
|
||||
# sha256sum_blenderbim_py310_win_zip
|
||||
sha256sum_blenderbim_py3x_win_zip = get_file_sha256_hash(release_zip_file_name)
|
||||
@@ -206,13 +201,13 @@ print("[INFO] inserting dynamic chocolatey package parameters successful")
|
||||
print("\n_____ build choco.exe with mono")
|
||||
|
||||
choco_version = "1.1.0"
|
||||
run(f"wget https://github.com/chocolatey/choco/archive/refs/tags/{choco_version}.tar.gz --quiet")
|
||||
run(f"tar -xzf {choco_version}.tar.gz")
|
||||
os.popen(f"wget https://github.com/chocolatey/choco/archive/refs/tags/{choco_version}.tar.gz --quiet").read()
|
||||
os.popen(f"tar -xzf {choco_version}.tar.gz").read()
|
||||
print("choco tar unpack successful")
|
||||
os.chdir("choco-1.1.0")
|
||||
run("./build.sh")
|
||||
os.popen("./build.sh").read()
|
||||
|
||||
run("cp -r build_output/chocolatey /opt/chocolatey")
|
||||
os.popen("cp -r build_output/chocolatey /opt/chocolatey").read()
|
||||
os.chdir(BLENDERBIM_DIR)
|
||||
|
||||
if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
||||
@@ -220,15 +215,11 @@ if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
||||
|
||||
print("\n_____ build choco pack")
|
||||
|
||||
run("mono /opt/chocolatey/choco.exe pack --allow-unofficial")
|
||||
run(
|
||||
'mono /opt/chocolatey/choco.exe setapikey --key="{choco_token}" --source="https://push.chocolatey.org/" --allow-unofficial'
|
||||
)
|
||||
os.popen("mono /opt/chocolatey/choco.exe pack --allow-unofficial").read()
|
||||
os.popen('mono /opt/chocolatey/choco.exe setapikey --key="{choco_token}" --source="https://push.chocolatey.org/" --allow-unofficial').read()
|
||||
|
||||
print("\n_____ build choco push")
|
||||
run(
|
||||
'mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose'
|
||||
)
|
||||
os.popen('mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose').read()
|
||||
|
||||
print(f"choco push of version: {target_release_tag} successful!")
|
||||
print(f"it took: {datetime.datetime.now() - start}")
|
||||
|
||||
+10
-18
@@ -80,7 +80,7 @@ option(BUILD_IFCGEOM "Build IfcGeom." ON)
|
||||
option(BUILD_IFCPYTHON "Build IfcPython." ON)
|
||||
option(BUILD_CONVERT "Build IfcConvert executable." ON)
|
||||
option(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
|
||||
option(BUILD_EXAMPLES "Build example applications." OFF)
|
||||
option(BUILD_EXAMPLES "Build example applications." ON)
|
||||
option(BUILD_GEOMSERVER "Build IfcGeomServer executable (Open CASCADE is required)." ON)
|
||||
option(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." OFF)
|
||||
option(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer" OFF) # QtViewer requires Qt6
|
||||
@@ -258,10 +258,10 @@ if(WITH_ROCKSDB)
|
||||
set(ROCKSDB_LIBRARIES "IFCOPENSHELL_RocksDB")
|
||||
target_compile_definitions(IFCOPENSHELL_RocksDB INTERFACE IFOPSH_WITH_ROCKSDB)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_ROCKSDB)
|
||||
# Shared binaries for `rocksdb` only support limited API (only `c.h`), but we use `db.h` API.
|
||||
# So rocksdb supported only as a static library.
|
||||
# See https://github.com/facebook/rocksdb/issues/981.
|
||||
target_link_libraries(IFCOPENSHELL_RocksDB INTERFACE RocksDB::rocksdb)
|
||||
target_link_libraries(
|
||||
IFCOPENSHELL_RocksDB
|
||||
INTERFACE $<IF:$<TARGET_EXISTS:RocksDB::rocksdb-shared>,RocksDB::rocksdb-shared,RocksDB::rocksdb>
|
||||
)
|
||||
|
||||
if(WITH_ZSTD)
|
||||
# @todo do we actually need the zstd include dir or rather just pass
|
||||
@@ -368,20 +368,12 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
|
||||
# Linker
|
||||
# /OPT:REF enables also /OPT:ICF and disables INCREMENTAL
|
||||
set(LINKER_FLAGS_RELEASE "/LTCG /OPT:REF")
|
||||
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
|
||||
set(LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /OPT:NOICF")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
|
||||
)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
"${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
|
||||
)
|
||||
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
else()
|
||||
# GCC-like: Release should use O3 but RelWithDebInfo 02 so enforce 03. Anything other useful that could be added here?
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
|
||||
@@ -43,17 +43,6 @@ if(NOT OCC_INCLUDE_DIR AND NOT OCC_LIBRARY_DIR)
|
||||
set_target_properties(TKernel PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenCASCADE_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
if(
|
||||
OpenCASCADE_VERSION VERSION_LESS "7.9.0"
|
||||
AND CMAKE_VERSION GREATER_EQUAL "3.24"
|
||||
AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
||||
)
|
||||
# Before 7.9.0 targets in OCCT cmake configs are not linked to each other
|
||||
# leading to missing symbols on Unix. Link them as a single group as a workaround.
|
||||
# Only needed for gcc, because other compilers (e.g. Apple Clang, MSVC) do rescan automatically.
|
||||
set(OpenCASCADE_LIBRARIES "$<LINK_GROUP:RESCAN,${OpenCASCADE_LIBRARIES}>")
|
||||
endif()
|
||||
|
||||
if(OpenCASCADE_VERSION VERSION_LESS "7.9.0" AND WIN32)
|
||||
# Bug in OCCT cmake configs < 7.9.0 - missing linked library.
|
||||
list(APPEND OpenCASCADE_LIBRARIES WSOCK32.lib)
|
||||
|
||||
+77
-60
@@ -75,30 +75,27 @@ Used environment variables:
|
||||
# #
|
||||
# for python37 to install correctly additionally: #
|
||||
# * libffi(-dev[el]) #
|
||||
# for Python build we also needs ssl and zlib #
|
||||
# for Python build we also needs ssl #
|
||||
# (since we do `pip install numpy` at the end) #
|
||||
# * libssl-dev #
|
||||
# #
|
||||
# on debian 7.8 these can be obtained with: #
|
||||
# $ apt-get install git gcc g++ autoconf bison bzip2 cmake #
|
||||
# mesa-common-dev libffi-dev libfontconfig1-dev #
|
||||
# libssl-dev xz zlib1g-dev #
|
||||
# libssl-dev xz #
|
||||
# #
|
||||
# on ubuntu 14.04: #
|
||||
# $ apt-get install git gcc g++ autoconf bison make cmake #
|
||||
# mesa-common-dev libffi-dev libfontconfig1-dev #
|
||||
# libssl-dev xz-utils zlib1g-dev #
|
||||
# libssl-dev xz-utils #
|
||||
# #
|
||||
# on OS X El Capitan with homebrew: #
|
||||
# $ brew install git bison autoconf automake libffi cmake #
|
||||
# $ # `bison` shipped with Mac is too old for swig build, #
|
||||
# $ # so we use `brew`. #
|
||||
# $ export PATH=$(brew --prefix bison)/bin:$PATH #
|
||||
# #
|
||||
# on RHEL-related distros: #
|
||||
# $ dnf install git gcc gcc-c++ autoconf bison make cmake #
|
||||
# $ yum install git gcc gcc-c++ autoconf bison make cmake #
|
||||
# mesa-libGL-devel libffi-devel fontconfig-devel bzip2 #
|
||||
# automake patch byacc xz zlib-devel openssl-devel #
|
||||
# automake patch byacc xz #
|
||||
|
||||
"""
|
||||
|
||||
@@ -150,8 +147,9 @@ OCCT_VERSION = "7.8.1"
|
||||
BOOST_VERSION = "1.86.0"
|
||||
EIGEN_VERSION = "3.4.0"
|
||||
PCRE_VERSION = "8.41"
|
||||
PCRE2_VERSION = "10.32"
|
||||
LIBXML2_VERSION = "2.13.8"
|
||||
SWIG_VERSION = "4.2.1"
|
||||
SWIG_VERSION = "4.1.0"
|
||||
OPENCOLLADA_VERSION = "v1.6.68"
|
||||
HDF5_VERSION = "1.13.1"
|
||||
|
||||
@@ -248,8 +246,15 @@ if WASM:
|
||||
# https://github.com/pyodide/pyodide-build/pull/249
|
||||
WASM_CMAKE_IS_USING_INIT_VARS = get_pyodide_build_version() >= (99, 0, 0)
|
||||
|
||||
# 0.31 is required for SIDE_MODULE_CXXFLAGS to be provided.
|
||||
assert get_pyodide_build_version() >= (0, 31)
|
||||
# pyodide provide empty `CXXFLAGS`, leading to issues using C++ files compiled with `-fexceptions`
|
||||
# which is used by OCCT.
|
||||
# https://github.com/pyodide/pyodide-build/issues/251
|
||||
side_module_cxx_flags = os.environ.get("SIDE_MODULE_CXXFLAGS", "")
|
||||
if side_module_cxx_flags.strip():
|
||||
print("SIDE_MODULE_CXXFLAGS are already passed from pyodide build ('{side_module_cxx_flags}').")
|
||||
print("Maybe it's time to stop overriding them in the script?")
|
||||
|
||||
os.environ["SIDE_MODULE_CXXFLAGS"] = os.environ["SIDE_MODULE_CFLAGS"]
|
||||
|
||||
# Set defaults for missing empty environment variables
|
||||
|
||||
@@ -289,7 +294,6 @@ DEPS_DIR = os.getenv("DEPS_DIR", DEFAULT_DEPS_DIR)
|
||||
if not os.path.exists(DEPS_DIR):
|
||||
os.makedirs(DEPS_DIR)
|
||||
|
||||
INSTALL_DIR = Path(DEPS_DIR) / "install"
|
||||
BUILD_CFG = os.getenv("BUILD_CFG", "RelWithDebInfo")
|
||||
|
||||
|
||||
@@ -315,18 +319,24 @@ cecho(f"* Build Directory = {BUILD_DIR}", MAGENTA)
|
||||
cecho(f"* Dependency Directory = {DEPS_DIR}", MAGENTA)
|
||||
cecho(f" - The directory where {PROJECT_NAME} dependencies are installed.")
|
||||
cecho(f"* Build Config Type = {BUILD_CFG}", MAGENTA)
|
||||
cecho(""" - The used build configuration type for the dependencies.
|
||||
Defaults to RelWithDebInfo if not specified.""")
|
||||
cecho(
|
||||
""" - The used build configuration type for the dependencies.
|
||||
Defaults to RelWithDebInfo if not specified."""
|
||||
)
|
||||
|
||||
if BUILD_CFG == "MinSizeRel":
|
||||
cecho(" WARNING: MinSizeRel build can suffer from a significant performance loss.", RED)
|
||||
|
||||
cecho(f"* IFCOS_NUM_BUILD_PROCS = {IFCOS_NUM_BUILD_PROCS}", MAGENTA)
|
||||
cecho(""" - How many compiler processes may be run in parallel.
|
||||
""")
|
||||
cecho(
|
||||
""" - How many compiler processes may be run in parallel.
|
||||
"""
|
||||
)
|
||||
cecho(f" * IFCOS_SCHEMAS = '{os.environ.get('IFCOS_SCHEMAS')}'", MAGENTA)
|
||||
cecho(""" - IFC Schemas to compile. If not provided, fallback to default provided in cmake.
|
||||
""")
|
||||
cecho(
|
||||
""" - IFC Schemas to compile. If not provided, fallback to default provided in cmake.
|
||||
"""
|
||||
)
|
||||
|
||||
dependency_tree: "dict[str, tuple[str, ...]]" = {
|
||||
"IfcParse": ("boost", "libxml2", "hdf5", "rocksdb"),
|
||||
@@ -335,12 +345,13 @@ dependency_tree: "dict[str, tuple[str, ...]]" = {
|
||||
"OpenCOLLADA": ("libxml2", "pcre"),
|
||||
"IfcGeomServer": ("IfcGeom",),
|
||||
"IfcOpenShell-Python": ("python", "swig", "IfcGeom"),
|
||||
"swig": (),
|
||||
"swig": ("pcre2",),
|
||||
"boost": (),
|
||||
"libxml2": (),
|
||||
"python": (),
|
||||
"occ": (),
|
||||
"pcre": (),
|
||||
"pcre2": (),
|
||||
"json": (),
|
||||
"hdf5": (),
|
||||
"cgal": (),
|
||||
@@ -407,6 +418,7 @@ if WASM:
|
||||
"opencollada",
|
||||
"swig",
|
||||
"pcre",
|
||||
"pcre2",
|
||||
"IfcGeom",
|
||||
"IfcConvert",
|
||||
"IfcGeomServer",
|
||||
@@ -421,16 +433,13 @@ print("Building:", *sorted(targets, key=lambda t: len(list(gather_dependencies(t
|
||||
|
||||
# Check that required tools are in PATH
|
||||
yacc = "yacc" # Used during swig building process, installed with `bison` on Debian / `byacc` on Red Hat.
|
||||
bison = "bison"
|
||||
|
||||
missing_commands: "list[str]" = []
|
||||
required_commands = [git, bunzip2, tar, cc, cplusplus, autoconf, automake, make, "patch", "cmake", yacc, xz, bison]
|
||||
required_commands = [git, bunzip2, tar, cc, cplusplus, autoconf, automake, make, "patch", "cmake", yacc, xz]
|
||||
if "wasm" in flags:
|
||||
# Skip swig build for WASM.
|
||||
required_commands.append("swig")
|
||||
required_commands.append("pyodide")
|
||||
required_commands.remove(yacc)
|
||||
required_commands.remove(bison)
|
||||
|
||||
for cmd in required_commands:
|
||||
if shutil.which(cmd) is None:
|
||||
@@ -489,7 +498,7 @@ def run(cmds: "Sequence[str]", cwd: "Union[str, None]" = None, can_fail: bool =
|
||||
collector.append(line)
|
||||
pipe.close()
|
||||
|
||||
logger.debug(f"running command `{' '.join(cmds)}` in directory '{cwd}'")
|
||||
logger.debug(f"running command {' '.join(cmds)} in directory {cwd}")
|
||||
stdout: list[str] = []
|
||||
stderr: list[str] = []
|
||||
|
||||
@@ -535,14 +544,14 @@ BOOST_LOCATION = f"https://github.com/boostorg/boost/releases/download/boost-{BO
|
||||
# Helper functions
|
||||
|
||||
|
||||
def run_autoconf(dependency_name: str, configure_args: "list[str]", cwd: str) -> None:
|
||||
def run_autoconf(arg1: str, configure_args: "list[str]", cwd: str) -> None:
|
||||
configure_path = os.path.realpath(os.path.join(cwd, "..", "configure"))
|
||||
if not os.path.exists(configure_path):
|
||||
run(
|
||||
[bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))
|
||||
) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
|
||||
# Using `sh` over `bash` fixes issues with building swig
|
||||
prefix = os.path.realpath(f"{DEPS_DIR}/install/{dependency_name}")
|
||||
prefix = os.path.realpath(f"{DEPS_DIR}/install/{arg1}")
|
||||
|
||||
wasm = []
|
||||
if "wasm" in flags:
|
||||
@@ -921,15 +930,20 @@ if "pcre" in targets:
|
||||
restore_env("CC", OLD_CC)
|
||||
restore_env("CXX", OLD_CXX)
|
||||
|
||||
if "swig" in targets:
|
||||
dependency_name = f"swig-{SWIG_VERSION}"
|
||||
if "pcre2" in targets:
|
||||
build_dependency(
|
||||
name=dependency_name,
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
"-DWITH_PCRE=OFF",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/{dependency_name}",
|
||||
],
|
||||
name=f"pcre2-{PCRE2_VERSION}",
|
||||
mode="autoconf",
|
||||
build_tool_args=[DISABLE_FLAG],
|
||||
download_url=f"https://downloads.sourceforge.net/project/pcre/pcre2/{PCRE2_VERSION}/",
|
||||
download_name=f"pcre2-{PCRE2_VERSION}.tar.bz2",
|
||||
)
|
||||
|
||||
if "swig" in targets:
|
||||
build_dependency(
|
||||
name=f"swig-{SWIG_VERSION}",
|
||||
mode="autoconf",
|
||||
build_tool_args=["--disable-ccache", f"--with-pcre2-prefix={DEPS_DIR}/install/pcre2-{PCRE2_VERSION}"],
|
||||
download_url="https://github.com/swig/swig.git",
|
||||
download_name="swig",
|
||||
download_tool=download_tool_git,
|
||||
@@ -937,18 +951,21 @@ if "swig" in targets:
|
||||
)
|
||||
|
||||
if USE_OCCT and "occ" in targets:
|
||||
occt_args: "list[str]" = []
|
||||
patches: "list[str]" = []
|
||||
patches = []
|
||||
if OCCT_VERSION < "7.4":
|
||||
patches.append("./patches/occt/enable-exception-handling.patch")
|
||||
|
||||
# Skip ExpToCasExe as we don't need it and it requires additional dependencies.
|
||||
# Before 7.7.2 ExpToCasExe is part of DataExchange, DETools doesn't exist yet.
|
||||
# Since we do need DataExchange (used for IgesSerializer), we use a patch to skip only ExpToCasExe.
|
||||
if "7.7.2" > OCCT_VERSION >= "7.7":
|
||||
if OCCT_VERSION == "7.7.1":
|
||||
patches.append("./patches/occt/no_ExpToCasExe.patch")
|
||||
elif OCCT_VERSION >= "7.7.2":
|
||||
occt_args.append("-DBUILD_MODULE_DETools=OFF")
|
||||
|
||||
if OCCT_VERSION == "7.7.2":
|
||||
patches.append("./patches/occt/no_ExpToCasExe_7_7_2.patch")
|
||||
|
||||
if OCCT_VERSION == "7.8.1":
|
||||
patches.append("./patches/occt/no_ExpToCasExe_7_8_1.patch")
|
||||
|
||||
if OCCT_VERSION == "7.9.1":
|
||||
patches.append("./patches/occt/no_ExpToCasExe_7_9_1.patch")
|
||||
|
||||
if "wasm" in flags:
|
||||
patches.append("./patches/occt/no_em_js.patch")
|
||||
@@ -969,7 +986,6 @@ if USE_OCCT and "occ" in targets:
|
||||
f"-DUSE_GLES2=OFF",
|
||||
f"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
*MAC_CROSS_COMPILE_INTEL_ARGS,
|
||||
*occt_args,
|
||||
],
|
||||
download_url="https://github.com/Open-Cascade-SAS/OCCT",
|
||||
download_name="occt",
|
||||
@@ -1085,19 +1101,23 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
|
||||
PYTHON_CONFIGURE_ARGS.extend(["--with-universal-archs=intel-64", "--enable-universalsdk"])
|
||||
|
||||
for PYTHON_VERSION in PYTHON_VERSIONS:
|
||||
# Don't fail silently on missing Python dependencies (e.g. openssl or zlib),
|
||||
# because later ifcopenshell-python build will fail too but in a more confusing way.
|
||||
build_dependency(
|
||||
f"python-{PYTHON_VERSION}",
|
||||
"autoconf",
|
||||
PYTHON_CONFIGURE_ARGS,
|
||||
f"http://www.python.org/ftp/python/{PYTHON_VERSION}/",
|
||||
f"Python-{PYTHON_VERSION}.tgz",
|
||||
)
|
||||
python_bin = INSTALL_DIR / f"python-{PYTHON_VERSION}" / "bin" / "python3"
|
||||
# `_ssl` module is present -> we will be able to install `numpy` later
|
||||
# to verify IfcOpenShell installation
|
||||
run([str(python_bin), "-c", "import _ssl"])
|
||||
try:
|
||||
build_dependency(
|
||||
f"python-{PYTHON_VERSION}",
|
||||
"autoconf",
|
||||
PYTHON_CONFIGURE_ARGS,
|
||||
f"http://www.python.org/ftp/python/{PYTHON_VERSION}/",
|
||||
f"Python-{PYTHON_VERSION}.tgz",
|
||||
)
|
||||
except RuntimeError as e:
|
||||
# Sometimes setting up modules such as pip/lzma can cause
|
||||
# the python installer script to return a non zero exit
|
||||
# code where actually the headers and dynamic libraries
|
||||
# are installed correctly. This is all we need so we catch
|
||||
# the exception and only reraise if a partially successful
|
||||
# install is not detected.
|
||||
if not os.path.exists(os.path.join(DEPS_DIR, "install", f"python-{PYTHON_VERSION}")):
|
||||
raise e
|
||||
|
||||
if MAC_CROSS_COMPILE_INTEL:
|
||||
assert original_path
|
||||
@@ -1515,16 +1535,13 @@ if "IfcOpenShell-Python" in targets:
|
||||
)
|
||||
# Copy setup.py where pyodide build system expects it.
|
||||
shutil.copy(REPO_PATH / "pyodide" / "setup.py", REPO_PATH)
|
||||
# Empty pyproject so it's contents won't affect the resulting wheelthe the
|
||||
# otherwise the wheel will use version and dependencies from toml, not setup.py.
|
||||
(REPO_PATH / "pyproject.toml").write_text("")
|
||||
|
||||
elif USE_CURRENT_PYTHON_VERSION:
|
||||
python_info = sysconfig.get_paths()
|
||||
compile_python_wrapper(platform.python_version(), python_info["include"], sys.executable)
|
||||
else:
|
||||
for python_version in PYTHON_VERSIONS:
|
||||
python_path = INSTALL_DIR / f"python-{python_version}"
|
||||
python_path = Path(DEPS_DIR) / "install" / f"python-{python_version}"
|
||||
module_dir = compile_python_wrapper(python_version, python_path=python_path)
|
||||
assert module_dir
|
||||
# Not sure why, but added after reading this in the logs
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
--- a/adm/MODULES
|
||||
+++ b/adm/MODULES
|
||||
@@ -3,5 +3,5 @@ ModelingData TKG2d TKG3d TKGeomBase TKBRep
|
||||
ModelingAlgorithms TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing
|
||||
Visualization TKService TKV3d TKOpenGl TKOpenGles TKMeshVS TKIVtk TKD3DHost
|
||||
ApplicationFramework TKCDF TKLCAF TKCAF TKBinL TKXmlL TKBin TKXml TKStdL TKStd TKTObj TKBinTObj TKXmlTObj TKVCAF
|
||||
-DataExchange TKXDE TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh TKXDECascade TKExpress ExpToCasExe
|
||||
+DataExchange TKXDE TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh TKXDECascade TKExpress
|
||||
Draw TKDraw TKTopTest TKOpenGlTest TKOpenGlesTest TKD3DHostTest TKViewerTest TKXSDRAW TKDCAF TKXDEDRAW TKTObjDRAW TKQADraw TKIVtkDraw DRAWEXE
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index fd17283f77..6cecf9dad3 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -826,6 +826,8 @@ if (EMSCRIPTEN)
|
||||
list (REMOVE_ITEM BUILD_TOOLKITS ExpToCasExe)
|
||||
endif()
|
||||
|
||||
+list (REMOVE_ITEM BUILD_TOOLKITS ExpToCasExe)
|
||||
+
|
||||
# bison
|
||||
if (BUILD_YACCLEX)
|
||||
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/bison")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 1bacca1a48..11f931ad39 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -820,6 +820,8 @@ else()
|
||||
OCCT_CHECK_AND_UNSET ("3RDPARTY_DOT_EXECUTABLE")
|
||||
endif()
|
||||
|
||||
+list (REMOVE_ITEM BUILD_TOOLKITS ExpToCasExe)
|
||||
+
|
||||
# bison
|
||||
if (BUILD_YACCLEX)
|
||||
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/bison")
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 86905287dc..9d0bce984c 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -828,6 +828,8 @@ else()
|
||||
OCCT_CHECK_AND_UNSET ("3RDPARTY_DOT_EXECUTABLE")
|
||||
endif()
|
||||
|
||||
+list (REMOVE_ITEM BUILD_TOOLKITS ExpToCasExe)
|
||||
+
|
||||
# bison
|
||||
if (BUILD_YACCLEX)
|
||||
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/bison")
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 34300d41ad..09b2e0d45f 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -721,6 +721,8 @@ else()
|
||||
OCCT_CHECK_AND_UNSET ("3RDPARTY_DOT_EXECUTABLE")
|
||||
endif()
|
||||
|
||||
+list (REMOVE_ITEM BUILD_TOOLKITS ExpToCasExe)
|
||||
+
|
||||
# bison
|
||||
if (BUILD_YACCLEX)
|
||||
list (APPEND OCCT_3RDPARTY_CMAKE_LIST "adm/cmake/bison")
|
||||
@@ -1,12 +1,9 @@
|
||||
#!/usr/bin/bash
|
||||
set -ex
|
||||
|
||||
# Script is assuming that it will be possible to execute it multiple times
|
||||
# therefore we're clearing venv each time and ignoring existing 'emsdk' folder.
|
||||
|
||||
# Install uv.
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv venv --python 3.13 --clear
|
||||
uv venv --python 3.13
|
||||
source .venv/bin/activate
|
||||
|
||||
# Install pyodide cross build environment.
|
||||
@@ -16,9 +13,7 @@ uv pip install pyodide-build
|
||||
uv run pyodide xbuildenv install
|
||||
|
||||
# Emscripten doesn't come with xbuildenv.
|
||||
if [ ! -d emsdk ]; then
|
||||
git clone https://github.com/emscripten-core/emsdk
|
||||
fi
|
||||
git clone https://github.com/emscripten-core/emsdk
|
||||
pushd emsdk
|
||||
PYODIDE_EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
|
||||
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
|
||||
|
||||
@@ -5,13 +5,9 @@ This script is finding common install directory and either
|
||||
packs each folder into a tar.gz archive, if it wasn't packed before,
|
||||
or unpacks existing archives.
|
||||
|
||||
Expected to be executed from 'build' directory (e.g. that might contain 'Linux/x86_64/install').
|
||||
|
||||
Usage: python cache_dependencies.py [pack|unpack]
|
||||
"""
|
||||
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
@@ -21,35 +17,23 @@ CACHE_PREFIX = "cache-"
|
||||
|
||||
|
||||
def get_install_dir() -> Path:
|
||||
if platform.system() == "Darwin":
|
||||
pattern = "Darwin/*/*/install"
|
||||
else:
|
||||
pattern = "*/*/install"
|
||||
for data in Path.cwd().glob(pattern):
|
||||
for data in Path.cwd().glob("*/*/install"):
|
||||
return data
|
||||
raise Exception("No install dir found")
|
||||
|
||||
|
||||
def run(cmd: str) -> None:
|
||||
print(f"Running command: `{cmd}`")
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
|
||||
|
||||
def pack_dependencies(install_dir: Path) -> None:
|
||||
# Process each install_dir
|
||||
for dependency_path in install_dir.iterdir():
|
||||
if not dependency_path.is_dir():
|
||||
continue
|
||||
dependency_name = dependency_path.name
|
||||
# Skip ifcopenshell - it's a build output, not a dependency to reuse across builds.
|
||||
if dependency_name == "ifcopenshell":
|
||||
continue
|
||||
tar_path = install_dir / f"{CACHE_PREFIX}{dependency_name}.tar.gz"
|
||||
if tar_path.exists():
|
||||
print(f"Skipping existing cache: '{tar_path}'")
|
||||
else:
|
||||
# Python's `tarfile` is 10x slower than `tar` cli, so we use `tar`.
|
||||
run(f'tar -czf "{tar_path}" -C "{install_dir}" "{dependency_name}"')
|
||||
with tarfile.open(tar_path, "w:gz") as tar:
|
||||
tar.add(dependency_path, arcname=dependency_path.name)
|
||||
print(f"Created cache: '{tar_path}'")
|
||||
|
||||
|
||||
+4
-185
@@ -2,10 +2,10 @@
|
||||
name = "IfcOpenShell"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"black==26.3.1",
|
||||
"ruff==0.15.8",
|
||||
"black==26.1.0",
|
||||
"ruff==0.15.0",
|
||||
"poethepoet",
|
||||
"gersemi==0.26.1",
|
||||
"gersemi==0.25.4",
|
||||
]
|
||||
|
||||
[tool.black]
|
||||
@@ -15,8 +15,7 @@ include = '''
|
||||
|nix/.*.pyi?$
|
||||
'''
|
||||
extend-exclude = '''
|
||||
src/ifcopenshell-python/ifcopenshell/express/rules/*
|
||||
|src/ifcopenshell-python/ifcopenshell/express/express_parser.py
|
||||
src/ifcopenshell-python/ifcopenshell/express/*
|
||||
|src/ifcopenshell-python/ifcopenshell/mvd/*
|
||||
|src/ifcopenshell-python/ifcopenshell/simple_spf/*
|
||||
|src/ifc2ca/templates/*
|
||||
@@ -28,12 +27,6 @@ extend-exclude = '''
|
||||
reportInvalidTypeForm = false
|
||||
disableBytesTypePromotions = true
|
||||
reportUnnecessaryTypeIgnoreComment = true
|
||||
# Pylance doesn't respect gitignore, so we have to exclude files manually here
|
||||
# to avoid VS Code slowing down.
|
||||
# https://github.com/microsoft/pylance-release/issues/5169
|
||||
exclude = [
|
||||
"_deps",
|
||||
]
|
||||
|
||||
# Define here general ruff settings,
|
||||
# then they will be inherited by projects' .toml files.
|
||||
@@ -78,139 +71,6 @@ ignore = [
|
||||
"UP032", # Replace .format with f-string
|
||||
]
|
||||
|
||||
[tool.ty.rules]
|
||||
all = "ignore"
|
||||
|
||||
# Structural rules (no deep type inference needed, easier to adapt).
|
||||
abstract-method-in-final-class = "error"
|
||||
ambiguous-protocol-member = "error"
|
||||
byte-string-type-annotation = "error"
|
||||
conflicting-declarations = "error"
|
||||
conflicting-metaclass = "error"
|
||||
cyclic-class-definition = "error"
|
||||
cyclic-type-alias-definition = "error"
|
||||
dataclass-field-order = "error"
|
||||
duplicate-base = "error"
|
||||
duplicate-kw-only = "error"
|
||||
empty-body = "error"
|
||||
escape-character-in-forward-annotation = "error"
|
||||
final-on-non-method = "error"
|
||||
final-without-value = "error"
|
||||
fstring-type-annotation = "error"
|
||||
ignore-comment-unknown-rule = "error"
|
||||
implicit-concatenated-string-type-annotation = "error"
|
||||
inconsistent-mro = "error"
|
||||
ineffective-final = "error"
|
||||
instance-layout-conflict = "error"
|
||||
invalid-dataclass = "error"
|
||||
invalid-dataclass-override = "error"
|
||||
invalid-enum-member-annotation = "error"
|
||||
invalid-explicit-override = "error"
|
||||
invalid-frozen-dataclass-subclass = "error"
|
||||
invalid-generic-class = "error"
|
||||
invalid-generic-enum = "error"
|
||||
invalid-ignore-comment = "error"
|
||||
invalid-legacy-positional-parameter = "error"
|
||||
invalid-legacy-type-variable = "error"
|
||||
invalid-named-tuple = "error"
|
||||
invalid-newtype = "error"
|
||||
invalid-overload = "error"
|
||||
invalid-paramspec = "error"
|
||||
invalid-protocol = "error"
|
||||
invalid-syntax-in-forward-annotation = "error"
|
||||
invalid-total-ordering = "error"
|
||||
invalid-type-alias-type = "error"
|
||||
invalid-type-checking-constant = "error"
|
||||
invalid-type-guard-definition = "error"
|
||||
invalid-type-variable-bound = "error"
|
||||
invalid-type-variable-constraints = "error"
|
||||
invalid-typed-dict-header = "error"
|
||||
invalid-typed-dict-statement = "error"
|
||||
override-of-final-method = "error"
|
||||
override-of-final-variable = "error"
|
||||
possibly-missing-import = "error"
|
||||
possibly-missing-submodule = "error"
|
||||
# Has false positives due to ty walrus operator bug.
|
||||
# possibly-unresolved-reference = "error"
|
||||
raw-string-type-annotation = "error"
|
||||
redundant-final-classvar = "error"
|
||||
shadowed-type-variable = "error"
|
||||
subclass-of-final-class = "error"
|
||||
super-call-in-named-tuple-method = "error"
|
||||
unavailable-implicit-super-arguments = "error"
|
||||
unbound-type-variable = "error"
|
||||
undefined-reveal = "error"
|
||||
unresolved-global = "error"
|
||||
unresolved-import = "error"
|
||||
unresolved-reference = "error"
|
||||
unused-ignore-comment = "error"
|
||||
unused-type-ignore-comment = "error"
|
||||
useless-overload-body = "error"
|
||||
|
||||
# Non-structural rules:
|
||||
deprecated = "error"
|
||||
zero-stepsize-in-slice = "error"
|
||||
possibly-missing-implicit-call = "error"
|
||||
unused-awaitable = "error"
|
||||
|
||||
# Function argument rules:
|
||||
# Conflicts with `ifcopenshell.api.geometry.add_representation` type of callables we have, confusing them with a module.
|
||||
# call-non-callable = "error"
|
||||
conflicting-argument-forms = "error"
|
||||
# Too many false positives.
|
||||
# invalid-argument-type = "error"
|
||||
missing-argument = "error"
|
||||
parameter-already-assigned = "error"
|
||||
positional-only-parameter-as-kwarg = "error"
|
||||
too-many-positional-arguments = "error"
|
||||
unknown-argument = "error"
|
||||
# Has a lot of warnings due to current ty walrus operator issues.
|
||||
# index-out-of-bounds = "error"
|
||||
# unresolved-attribute = "error"
|
||||
|
||||
[tool.ty.environment]
|
||||
extra-paths = [
|
||||
"src/bonsai/external_dependencies",
|
||||
"src/bcf",
|
||||
"src/bsdd",
|
||||
"src/bonsai",
|
||||
"src/ifc4d",
|
||||
"src/ifc5d",
|
||||
"src/ifccityjson",
|
||||
"src/ifcclash",
|
||||
"src/ifccsv",
|
||||
"src/ifcdiff",
|
||||
"src/ifcfm",
|
||||
"src/ifcopenshell-python",
|
||||
"src/ifcpatch",
|
||||
"src/ifctester",
|
||||
]
|
||||
|
||||
[tool.ty.src]
|
||||
exclude = [
|
||||
# External dependencies cloned for type checking only.
|
||||
"src/bonsai/external_dependencies",
|
||||
# Submodules.
|
||||
"src/ifcopenshell-python/ifcopenshell/express",
|
||||
"src/ifcopenshell-python/ifcopenshell/mvd",
|
||||
"src/ifcopenshell-python/ifcopenshell/simple_spf",
|
||||
"src/svgfill/3rdparty",
|
||||
# Has special dependencies.
|
||||
"src/ifcopenshell-python/ifcopenshell/geom/app.py",
|
||||
"src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py",
|
||||
"src/ifcopenshell-python/ifcopenshell/util/doc.py",
|
||||
"src/ifcopenshell-python/ifcopenshell/util/generate_pset_templates.py",
|
||||
"src/ifcopenshell-python/ifcopenshell/util/ifc4x3dev_scrape_data_for_docs.py",
|
||||
# Too esoteric.
|
||||
"src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py",
|
||||
"src/ifc2ca/templates",
|
||||
# Too dev.
|
||||
"src/bcf/setup.py",
|
||||
"src/bsdd/yml_to_classes.py",
|
||||
# Deprecated.
|
||||
"src/ifc2ca/_deprecated",
|
||||
]
|
||||
|
||||
[tool.poe.tasks]
|
||||
|
||||
ruff-main = "ruff check --extend-exclude nix/build-all.py"
|
||||
@@ -220,47 +80,6 @@ ruff.sequence = ["ruff-main", "ruff-old"]
|
||||
|
||||
black = "black ."
|
||||
|
||||
ty.sequence = ["ty-bonsai", "ty-ios"]
|
||||
ty.help = "Run ty type checker. Requires ty-venv to be set up first."
|
||||
ty-bonsai = "ty check src/bonsai --python=src/bonsai/.venv"
|
||||
|
||||
ty-venv.sequence = ["ty-venv-bonsai", "ty-venv-ios"]
|
||||
|
||||
ty-venv-bonsai.sequence = [
|
||||
{cmd = "uv venv src/bonsai/.venv --python=3.11 --allow-existing"},
|
||||
{cmd = "uv pip install -r src/bonsai/type-check-requirements.txt --python=src/bonsai/.venv"},
|
||||
]
|
||||
|
||||
ty-venv-ios.sequence = [
|
||||
{cmd = "uv venv src/ifcopenshell-python/.venv --python=3.10 --allow-existing"},
|
||||
{cmd = "uv pip install -r src/ifcopenshell-python/type-check-requirements.txt --python=src/ifcopenshell-python/.venv"},
|
||||
]
|
||||
|
||||
format.sequence = ["black", "ruff-main", "ruff-old"]
|
||||
|
||||
cmake-format = "gersemi . --in-place"
|
||||
|
||||
[tool.poe.tasks.ty-ios]
|
||||
# --ignore unresolved-reference: walrus operator false positives in ty.
|
||||
cmd = """
|
||||
ty check
|
||||
src/bcf
|
||||
src/bsdd
|
||||
src/ifc2ca
|
||||
src/ifc4d
|
||||
src/ifc5d
|
||||
src/ifccityjson
|
||||
src/ifcclash
|
||||
src/ifccsv
|
||||
src/ifcdiff
|
||||
src/ifcfm
|
||||
src/ifcopenshell-python
|
||||
src/ifcpatch
|
||||
src/ifctester
|
||||
--python=src/ifcopenshell-python/.venv
|
||||
--ignore unresolved-reference
|
||||
"""
|
||||
|
||||
[tool.poe.tasks.bonsai-deps]
|
||||
help = "Clone or update Bonsai external dependencies."
|
||||
cmd = "python src/bonsai/scripts/bonsai_deps.py"
|
||||
|
||||
@@ -34,8 +34,8 @@ client_id, client_secret = "", ""
|
||||
class OAuthReceiver(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self) -> None:
|
||||
query = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
|
||||
self.server.auth_code = query.get("code", [""])[0]
|
||||
self.server.auth_state = query.get("state", [""])[0]
|
||||
self.server.auth_code = query.get("code", [""])[0] # type:ignore
|
||||
self.server.auth_state = query.get("state", [""])[0] # type:ignore
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
@@ -255,7 +255,7 @@ class BcfClient:
|
||||
project_id: str = "",
|
||||
topics: str = "",
|
||||
query_string: Optional[str] = None,
|
||||
) -> None:
|
||||
) -> list[Any]:
|
||||
# return self.get(
|
||||
# f"/projects/{project_id}/topics",
|
||||
# {
|
||||
|
||||
@@ -173,17 +173,16 @@ def assert_viewpoints(viewpoints):
|
||||
assert viewpoint.snapshot is not None
|
||||
|
||||
|
||||
# TODO: dead code - ported from v2 but buildingSMART/BCF-XML has no v3 MaximumInformation.bcf equivalent
|
||||
def assert_second_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
|
||||
expected_vp = mdl.VisualizationInfo(
|
||||
components=mdl.Components(
|
||||
view_setup_hints=mdl.ViewSetupHints(
|
||||
spaces_visible=False,
|
||||
space_boundaries_visible=False,
|
||||
openings_visible=False,
|
||||
),
|
||||
selection=expected_selection,
|
||||
visibility=mdl.ComponentVisibility(
|
||||
view_setup_hints=mdl.ViewSetupHints(
|
||||
spaces_visible=False,
|
||||
space_boundaries_visible=False,
|
||||
openings_visible=False,
|
||||
),
|
||||
exceptions=expected_exception,
|
||||
default_visibility=False,
|
||||
),
|
||||
@@ -194,7 +193,6 @@ def assert_second_viewpoint(viewpoint, expected_selection, expected_exception, e
|
||||
camera_direction=mdl.Direction(x=0.6745243072509766, y=-0.6599355936050415, z=-0.33091068267822266),
|
||||
camera_up_vector=mdl.Direction(x=0.2271970510482788, y=-0.24091780185699463, z=0.9435783624649048),
|
||||
field_of_view=60,
|
||||
aspect_ratio=1.0,
|
||||
),
|
||||
guid="21dd4807-e9af-439e-a980-04d913a6b1ce",
|
||||
)
|
||||
@@ -202,17 +200,16 @@ def assert_second_viewpoint(viewpoint, expected_selection, expected_exception, e
|
||||
assert viewpoint.snapshot is not None
|
||||
|
||||
|
||||
# TODO: dead code - ported from v2 but buildingSMART/BCF-XML has no v3 MaximumInformation.bcf equivalent
|
||||
def assert_third_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
|
||||
expected_vp = mdl.VisualizationInfo(
|
||||
components=mdl.Components(
|
||||
view_setup_hints=mdl.ViewSetupHints(
|
||||
spaces_visible=False,
|
||||
space_boundaries_visible=False,
|
||||
openings_visible=True,
|
||||
),
|
||||
selection=expected_selection,
|
||||
visibility=mdl.ComponentVisibility(
|
||||
view_setup_hints=mdl.ViewSetupHints(
|
||||
spaces_visible=False,
|
||||
space_boundaries_visible=False,
|
||||
openings_visible=True,
|
||||
),
|
||||
exceptions=expected_exception,
|
||||
default_visibility=True,
|
||||
),
|
||||
@@ -223,7 +220,6 @@ def assert_third_viewpoint(viewpoint, expected_selection, expected_exception, ex
|
||||
camera_direction=mdl.Direction(x=0.7232745289802551, y=0.5967116951942444, z=-0.3475759029388428),
|
||||
camera_up_vector=mdl.Direction(x=0.27662187814712524, y=0.21082592010498047, z=0.937567412853241),
|
||||
field_of_view=60,
|
||||
aspect_ratio=1.0,
|
||||
),
|
||||
guid="81daa431-bf01-4a49-80a2-1ab07c177717",
|
||||
)
|
||||
|
||||
+28
-10
@@ -48,7 +48,6 @@ VERSION_PATCH:=$(shell cat '../../VERSION' | cut -d '.' -f 3)
|
||||
VERSION_DATE:=$(shell date '+%y%m%d')
|
||||
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
LAST_COMMIT_DATE:=$(shell git show -s --format=%cI)
|
||||
LAST_GIT_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
PYPI_IMP:=cp
|
||||
|
||||
ifdef PYVERSION
|
||||
@@ -64,7 +63,6 @@ PYNUMBER:=3$(PYMINOR)
|
||||
PYPI_VERSION:=3.$(PYMINOR)
|
||||
endif # def PYVERSION
|
||||
|
||||
IFCMERGE_VERSION:=2026-04-02
|
||||
|
||||
ifdef PLATFORM
|
||||
SUPPORTED_PLATFORMS := linux macos macosm1 win
|
||||
@@ -106,7 +104,7 @@ endif
|
||||
endif # def PLATFORM
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=1c5b825
|
||||
OLD:=e8eb5e4
|
||||
.PHONY: bump
|
||||
bump:
|
||||
ifndef NEW
|
||||
@@ -225,8 +223,19 @@ endif
|
||||
cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
|
||||
# Provides IFCJSON functionality
|
||||
# TODO: Use official repo, once https://github.com/IFCJSON-Team/IFC2JSON_python/pull/8 is merged.
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/Andrej730/IFC2JSON_python.git@pyproject_toml" --no-deps -w wheels/
|
||||
cd build && wget -O ifc2json.zip https://github.com/IFCJSON-Team/IFC2JSON_python/archive/refs/heads/master.zip
|
||||
cd build && unzip ifc2json.zip && rm ifc2json.zip
|
||||
# IFCJSON doesn't have pyproject.toml, so we use python command.
|
||||
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-*/file_converters && \
|
||||
$(PYTHON) -c "from setuptools import setup; \
|
||||
setup( \
|
||||
name='ifcjson', \
|
||||
version='0.0.1', \
|
||||
author='Jan Brouwer', \
|
||||
author_email='jan@brewsky.nl', \
|
||||
packages=['ifcjson'], \
|
||||
)" bdist_wheel
|
||||
cp -r build/IFC2JSON_python-*/file_converters/dist/*.whl build/wheels/
|
||||
|
||||
# Brickschema requires pkg_resources which is provided by Blender.
|
||||
# Provides Brickschema functionality
|
||||
@@ -234,16 +243,26 @@ endif
|
||||
cd build/bonsai/bim/data/brick/ && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||
|
||||
# Required for hipped roof generation
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/prochitecture/bpypolyskel" --no-deps -w wheels/
|
||||
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/
|
||||
|
||||
# folder for executable files
|
||||
mkdir -p build/bonsai/libs/bin
|
||||
|
||||
# required for three-way git merging
|
||||
ifeq ($(PLATFORM), win)
|
||||
cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/$(IFCMERGE_VERSION)/ifcmerge.exe
|
||||
cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/2025-01-26/ifcmerge.zip
|
||||
cd build/bonsai/libs/bin && unzip ifcmerge.zip && rm ifcmerge.zip
|
||||
else
|
||||
cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/$(IFCMERGE_VERSION)/ifcmerge && chmod +x ifcmerge
|
||||
cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/main/ifcmerge && chmod +x ifcmerge
|
||||
endif
|
||||
|
||||
# Generate translations module for Bonsai build
|
||||
@@ -262,7 +281,6 @@ else
|
||||
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/bonsai/blender_manifest.toml
|
||||
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/bonsai/__init__.py
|
||||
$(SED) "s/9999999/$(LAST_COMMIT_DATE)/" build/bonsai/__init__.py
|
||||
$(SED) "s/7777777/$(LAST_GIT_BRANCH)/" build/bonsai/__init__.py
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
|
||||
endif
|
||||
|
||||
@@ -353,7 +371,7 @@ test-tool:
|
||||
ifndef MODULE
|
||||
pytest test/tool
|
||||
else
|
||||
pytest test/tool/test_$(MODULE).py --maxfail=1
|
||||
pytest test/tool/test_$(MODULE).py
|
||||
endif
|
||||
|
||||
# Reregistering test is not added to the standard test suite because during unregister
|
||||
|
||||
@@ -34,16 +34,17 @@ IN_PACKAGE = __package__ == "bonsai"
|
||||
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import traceback
|
||||
import uuid
|
||||
import webbrowser
|
||||
from collections import deque
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Union
|
||||
from typing import Any, Union
|
||||
|
||||
last_commit_hash = "8888888"
|
||||
last_commit_date = "9999999"
|
||||
last_git_branch = "7777777"
|
||||
|
||||
|
||||
def get_last_commit_hash() -> Union[str, None]:
|
||||
@@ -61,15 +62,6 @@ def get_last_commit_date() -> Union[str, None]:
|
||||
return last_commit_date
|
||||
|
||||
|
||||
def get_git_branch() -> Union[str, None]:
|
||||
# Using this weird way to write 7777777,
|
||||
# so makefile won't accidentally replace it here
|
||||
# we'll be able to distinguish branch from placeholder value.
|
||||
if last_git_branch == str(7_777777):
|
||||
return None
|
||||
return last_git_branch
|
||||
|
||||
|
||||
# Accessed from bonsai extension:
|
||||
bbim_semver: dict[str, Any] = {}
|
||||
|
||||
@@ -81,21 +73,6 @@ REINSTALLED_BBIM_VERSION: Union[str, None] = None
|
||||
REGISTERED_BBIM_PACKAGE: str
|
||||
|
||||
|
||||
def is_registering() -> bool:
|
||||
"""
|
||||
During addon registration ``bpy.context`` and ``bpy.data`` are restricted
|
||||
and you can't access their properties.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
if TYPE_CHECKING or bpy.app.version >= (5, 0, 0):
|
||||
import _bpy_restrict_state as bpy_restrict_state
|
||||
else:
|
||||
import bpy_restrict_state
|
||||
|
||||
return isinstance(bpy.context, bpy_restrict_state._RestrictContext)
|
||||
|
||||
|
||||
def initialize_bbim_semver():
|
||||
"""Initialize `bbim_semver` dictionary.
|
||||
|
||||
@@ -117,13 +94,9 @@ def initialize_bbim_semver():
|
||||
bbim_semver["version"] = version_str
|
||||
|
||||
|
||||
def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]:
|
||||
import bpy
|
||||
|
||||
def get_debug_info():
|
||||
bbim_version = bbim_semver["version"]
|
||||
|
||||
# All data here should be gettable even in case of `bpy.context` and `bpy.data` being inaccessible
|
||||
# and Bonsai completely failed to load.
|
||||
debug_info = {
|
||||
"os": platform.system(),
|
||||
"os_version": platform.version(),
|
||||
@@ -135,19 +108,10 @@ def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]:
|
||||
"bonsai_version": bbim_version,
|
||||
"bonsai_commit_hash": get_last_commit_hash(),
|
||||
"bonsai_commit_date": get_last_commit_date(),
|
||||
"bonsai_git_branch": get_git_branch(),
|
||||
"last_actions": last_actions,
|
||||
"last_error": last_error,
|
||||
}
|
||||
|
||||
# Can't access blend data or context during registration.
|
||||
# If Bonsai failed to load we cannot safely access any of its properties or its tools
|
||||
# as they may not be registered yet and acessing them will break Bonsai Fatal Error UI.
|
||||
if is_registering() or bonsai_failed_to_load:
|
||||
return debug_info
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
# Add .blend file save information
|
||||
if bpy.data.is_saved:
|
||||
debug_info["blend_file_path"] = bpy.data.filepath
|
||||
@@ -168,7 +132,7 @@ def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]:
|
||||
return debug_info
|
||||
|
||||
|
||||
def format_debug_info(info: dict[str, Any]) -> str:
|
||||
def format_debug_info(info: dict):
|
||||
last_actions = ""
|
||||
for action in info["last_actions"]:
|
||||
last_actions += f"\n# {action['type']}: {action['name']}"
|
||||
@@ -186,10 +150,33 @@ def get_binaries(path: Path) -> Generator[Path, None, None]:
|
||||
yield from path.glob("**/*.so")
|
||||
|
||||
|
||||
# TODO: remove before 0.8.6 release.
|
||||
# On Windows issues with removing extensions were resolved in Blender 4.3,
|
||||
# but we removed our workaround that was producing some junk only in 0.8.5 release.
|
||||
# So we're temporarily keeping the part that's cleaning up outputs from previous releases.
|
||||
def safe_link_dlls() -> None:
|
||||
# Blender 4.2+ has a problem on Windows for disabling/enabling/reinstalling extensions
|
||||
# with loaded binary dependencies (on Windows you can't remove a binary if it's loaded by some program).
|
||||
# To avoid this issue we temporary hard link dlls to our temp directory on unregister()
|
||||
# (unregister is executed before Blender will try to uninstall dependencies and the issue will arise).
|
||||
# Then, Blender won't have a problem unlinking unloaded dlls as they are still linked somewhere.
|
||||
# On register() we clean up our temp directory with binaries.
|
||||
#
|
||||
# TODO: If user uninstalls Bonsai to never use it again, temporary directory won't be cleared.
|
||||
#
|
||||
# See: https://projects.blender.org/blender/blender/issues/125049
|
||||
import bpy
|
||||
|
||||
ext_path = Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||
local_path = ext_path / ".local"
|
||||
|
||||
# We use random hash subfolder as user may try to enable/disable addon multiple times.
|
||||
random_hash = uuid.uuid4().hex[:8]
|
||||
temp_local = ext_path / ".local_temp" / random_hash
|
||||
temp_local.mkdir(parents=True)
|
||||
|
||||
for filepath in get_binaries(local_path):
|
||||
dest_path = temp_local / filepath.relative_to(local_path)
|
||||
dest_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
os.link(filepath, dest_path)
|
||||
|
||||
|
||||
def clean_up_dlls_safe_links() -> None:
|
||||
import bpy
|
||||
|
||||
@@ -219,8 +206,6 @@ def clean_up_dlls_safe_links() -> None:
|
||||
|
||||
|
||||
if IN_BLENDER:
|
||||
import bpy
|
||||
|
||||
initialize_bbim_semver()
|
||||
|
||||
def get_binary_info() -> dict[str, Any]:
|
||||
@@ -262,12 +247,10 @@ if IN_BLENDER:
|
||||
|
||||
global last_commit_hash
|
||||
global last_commit_date
|
||||
global last_git_branch
|
||||
path = Path(__file__).resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
last_commit_hash = repo.head.object.hexsha
|
||||
last_commit_date = repo.head.object.committed_datetime.isoformat()
|
||||
last_git_branch = repo.active_branch.name
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -314,6 +297,9 @@ if IN_BLENDER:
|
||||
purge_cache()
|
||||
|
||||
def unregister():
|
||||
if platform.system() == "Windows":
|
||||
safe_link_dlls()
|
||||
|
||||
import bonsai.bim
|
||||
|
||||
bonsai.bim.unregister()
|
||||
@@ -348,7 +334,7 @@ if IN_BLENDER:
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
info = get_debug_info(bonsai_failed_to_load=True)
|
||||
info = get_debug_info()
|
||||
|
||||
layout = self.layout
|
||||
layout.alert = True
|
||||
@@ -424,7 +410,7 @@ if IN_BLENDER:
|
||||
bl_description = "Copies debugging information to your clipboard for use in bugreports"
|
||||
|
||||
def execute(self, context):
|
||||
info = get_debug_info(bonsai_failed_to_load=True)
|
||||
info = get_debug_info()
|
||||
info.update(get_binary_info())
|
||||
info = format_debug_info(info)
|
||||
context.window_manager.clipboard = info
|
||||
|
||||
@@ -24,14 +24,21 @@ import os
|
||||
import tempfile
|
||||
import zipfile
|
||||
from logging import Logger
|
||||
from math import radians
|
||||
from typing import Union
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.unit
|
||||
from mathutils import Vector
|
||||
|
||||
import bonsai.core.aggregate
|
||||
import bonsai.core.geometry
|
||||
import bonsai.core.spatial
|
||||
import bonsai.core.style
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
@@ -45,6 +52,7 @@ class IfcExporter:
|
||||
self.set_header()
|
||||
IfcStore.update_cache()
|
||||
self.sync_all_objects()
|
||||
tool.Project.save_linked_models_to_ifc()
|
||||
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
|
||||
if extension == "ifczip":
|
||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||
@@ -72,7 +80,9 @@ class IfcExporter:
|
||||
|
||||
def set_header(self):
|
||||
self.file.header.file_name.name = os.path.basename(self.ifc_export_settings.output_file)
|
||||
self.file.header.file_name.time_stamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
|
||||
self.file.header.file_name.time_stamp = (
|
||||
datetime.datetime.utcnow().replace(tzinfo=datetime.UTC).astimezone().replace(microsecond=0).isoformat()
|
||||
)
|
||||
self.file.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||
self.file.header.file_name.originating_system = "{} {}".format(
|
||||
self.get_application_name(), tool.Blender.get_bonsai_version()
|
||||
|
||||
@@ -45,19 +45,16 @@ from bonsai.bim.module.nest.decorator import NestDecorator
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
global_subscription_owner = object()
|
||||
# Separate owner for per-object msgbus subscriptions (name, active_material_index).
|
||||
# Using a dedicated owner allows clearing all per-object subscriptions at once
|
||||
# during undo/redo without affecting other global subscriptions.
|
||||
object_subscription_owner = object()
|
||||
|
||||
|
||||
def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -> None:
|
||||
try:
|
||||
obj.name
|
||||
except:
|
||||
# The object is invalid but somehow still has a callback.
|
||||
# This can occur during undo/redo when the Python wrapper is stale.
|
||||
return
|
||||
# The object is invalid but somehow still has a callback. Clear all
|
||||
# msgbus subscriptions to prevent useless further triggers.
|
||||
bpy.msgbus.clear_by_owner(obj)
|
||||
return # In case the object RNA is gone during an undo / redo operation
|
||||
# Blender names are up to 63 UTF-8 bytes
|
||||
if len(bytes(obj.name, "utf-8")) >= 63:
|
||||
return
|
||||
@@ -192,7 +189,7 @@ def subscribe_to(obj: bpy.types.ID, data_path: str, callback: Callable[[bpy.type
|
||||
return
|
||||
bpy.msgbus.subscribe_rna(
|
||||
key=subscribe_to,
|
||||
owner=object_subscription_owner,
|
||||
owner=obj,
|
||||
args=(
|
||||
obj,
|
||||
data_path,
|
||||
|
||||
@@ -28,6 +28,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.doc import (
|
||||
get_attribute_doc,
|
||||
|
||||
@@ -316,8 +316,11 @@ class IfcStore:
|
||||
del IfcStore.id_map[data["id"]]
|
||||
if "guid" in data:
|
||||
del IfcStore.guid_map[data["guid"]]
|
||||
# Note: msgbus subscriptions are cleared globally during
|
||||
# rebuild_element_maps which runs after every undo/redo.
|
||||
obj = IfcStore.get_object_by_name(data["obj"])
|
||||
if obj is None:
|
||||
# obj was just created during this step and didn't existed before.
|
||||
return
|
||||
bpy.msgbus.clear_by_owner(obj)
|
||||
|
||||
@staticmethod
|
||||
def commit_link_element(data: OperationData) -> None:
|
||||
@@ -364,8 +367,10 @@ class IfcStore:
|
||||
del IfcStore.id_map[data["id"]]
|
||||
if "guid" in data:
|
||||
del IfcStore.guid_map[data["guid"]]
|
||||
# Note: msgbus subscriptions are cleared globally during
|
||||
# rebuild_element_maps which runs after every undo/redo.
|
||||
obj = IfcStore.get_object_by_name(data["obj"])
|
||||
# obj might be removed after unlink.
|
||||
if not obj:
|
||||
bpy.msgbus.clear_by_owner(obj)
|
||||
|
||||
@staticmethod
|
||||
def unlink_element(
|
||||
|
||||
@@ -32,6 +32,7 @@ import ifcopenshell.api.pset
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape
|
||||
@@ -64,8 +65,8 @@ class MaterialCreator:
|
||||
mesh: Union[OBJECT_DATA_TYPE, None],
|
||||
shape_has_openings: bool,
|
||||
) -> None:
|
||||
if ((rep := getattr(element, "Representation", ...)) is not ... and not rep) or (
|
||||
(rep := getattr(element, "RepresentationMaps", ...)) is not ... and not rep
|
||||
if ((rep := getattr(element, "Representation", ...) is not ...) and not rep) or (
|
||||
(rep := getattr(element, "RepresentationMaps", ...) is not ...) and not rep
|
||||
):
|
||||
return
|
||||
|
||||
@@ -84,7 +85,7 @@ class MaterialCreator:
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
self.parse_element_type_material_styles(element)
|
||||
self.parsed_meshes.add(self.mesh.name)
|
||||
if self.ifc_import_settings.load_indexed_maps:
|
||||
if not self.ifc_import_settings.load_indexed_maps:
|
||||
self.load_texture_maps(shape_has_openings)
|
||||
self.assign_material_slots_to_faces()
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
@@ -116,6 +117,7 @@ class MaterialCreator:
|
||||
for texture in texture_style.Textures or []:
|
||||
if coords := getattr(texture, "IsMappedBy", None):
|
||||
coords = coords[0]
|
||||
# IfcTextureCoordinateGenerator handled in the style shader graph
|
||||
if coords.is_a("IfcIndexedTextureMap"):
|
||||
return coords
|
||||
# TODO: support IfcTextureMap
|
||||
@@ -133,10 +135,6 @@ class MaterialCreator:
|
||||
if shape_has_openings and coords.is_a("IfcIndexedTextureMap"):
|
||||
continue
|
||||
tool.Loader.load_indexed_map(coords, self.mesh)
|
||||
elif tool.Style.get_texture_style(material):
|
||||
# No explicit coordinate mapping (e.g. IFC2X3 has no IsMappedBy,
|
||||
# and IFC4 COORD uses generated UVs). Bake XY→UV as fallback.
|
||||
tool.Loader.load_generated_uv_map(self.mesh)
|
||||
|
||||
def assign_material_slots_to_faces(self) -> None:
|
||||
if not self.mesh["ios_materials"]:
|
||||
@@ -747,7 +745,6 @@ class IfcImporter:
|
||||
self.update_progress((percent_average / 100 * progress_range) + start_progress)
|
||||
shape = iterator.get()
|
||||
if shape:
|
||||
assert isinstance(shape, W.TriangulationElement)
|
||||
product = self.file.by_id(shape.id)
|
||||
self.create_product(product, shape)
|
||||
results.add(product)
|
||||
@@ -895,6 +892,48 @@ class IfcImporter:
|
||||
obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
|
||||
)
|
||||
|
||||
if element.is_a("IfcAnnotation") and getattr(element, "ObjectType", None) == "IMAGE":
|
||||
image = None
|
||||
if obj.data and obj.data.materials and obj.data.materials[0]:
|
||||
material = obj.data.materials[0]
|
||||
if material.use_nodes and material.node_tree:
|
||||
for node in material.node_tree.nodes:
|
||||
if node.type == "TEX_IMAGE" and node.image:
|
||||
image = node.image
|
||||
break
|
||||
if image:
|
||||
import bmesh
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
if not bm.loops.layers.uv:
|
||||
uv_layer = bm.loops.layers.uv.new()
|
||||
else:
|
||||
uv_layer = bm.loops.layers.uv.active
|
||||
|
||||
if bm.verts:
|
||||
min_x = min(v.co.x for v in bm.verts)
|
||||
max_x = max(v.co.x for v in bm.verts)
|
||||
min_y = min(v.co.y for v in bm.verts)
|
||||
max_y = max(v.co.y for v in bm.verts)
|
||||
|
||||
width = max_x - min_x
|
||||
height = max_y - min_y
|
||||
|
||||
for face in bm.faces:
|
||||
for loop in face.loops:
|
||||
vert = loop.vert
|
||||
u = (vert.co.x - min_x) / width if width > 0 else 0.5
|
||||
v = (vert.co.y - min_y) / height if height > 0 else 0.5
|
||||
|
||||
u = max(0.0, min(1.0, u))
|
||||
v = max(0.0, min(1.0, v))
|
||||
|
||||
loop[uv_layer].uv = (u, v)
|
||||
|
||||
bm.to_mesh(obj.data)
|
||||
bm.free()
|
||||
obj.data.update()
|
||||
return obj
|
||||
|
||||
def load_existing_meshes(self) -> None:
|
||||
@@ -1021,8 +1060,7 @@ class IfcImporter:
|
||||
obj.hide_select = True
|
||||
obj.hide_viewport = True
|
||||
self.project["blender"].objects.link(obj)
|
||||
collection_props = tool.Blender.get_collection_props(self.project["blender"])
|
||||
collection_props.obj = obj
|
||||
self.project["blender"].BIMCollectionProperties.obj = obj
|
||||
props = tool.Blender.get_object_bim_props(obj)
|
||||
props.collection = self.collections[project.GlobalId] = self.project["blender"]
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import blf
|
||||
import bpy
|
||||
import gpu
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
from bpy.types import SpaceView3D
|
||||
from bpy_extras import view3d_utils
|
||||
@@ -26,6 +27,7 @@ from gpu_extras.batch import batch_for_shader
|
||||
from mathutils import Vector
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.geometry.decorator import ItemDecorator
|
||||
|
||||
|
||||
def transparent_color(color, alpha=0.1):
|
||||
@@ -101,7 +103,7 @@ class AggregateDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def dotted_line_shader(self):
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("FLOAT", "v_ArcLength")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.group
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.api.root
|
||||
|
||||
@@ -23,7 +23,12 @@ import ifcopenshell.util.element
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -32,6 +37,8 @@ from bonsai.bim.module.aggregate.decorator import (
|
||||
AggregateDecorator,
|
||||
AggregateModeDecorator,
|
||||
)
|
||||
from bonsai.bim.module.spatial.data import SpatialData
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
|
||||
|
||||
def can_aggregate(relating_obj: bpy.types.Object, related_obj: bpy.types.Object) -> bool:
|
||||
|
||||
@@ -21,6 +21,7 @@ from bpy.types import Panel
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.aggregate.data import AggregateData
|
||||
from bonsai.bim.module.group.data import GroupsData, ObjectGroupsData
|
||||
|
||||
|
||||
class BIM_PT_aggregate(Panel):
|
||||
|
||||
@@ -18,14 +18,24 @@
|
||||
|
||||
# pyright: reportUnnecessaryTypeIgnoreComment=error
|
||||
|
||||
import calendar
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api.alignment
|
||||
import ifcopenshell.api.spatial
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.sequence
|
||||
import isodate
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from dateutil import parser, relativedelta
|
||||
|
||||
import bonsai.bim.module.sequence.helper as helper
|
||||
import bonsai.core.sequence as core
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
|
||||
@@ -23,12 +23,16 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
|
||||
|
||||
class BIMAttributeProperties(PropertyGroup):
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
import bpy.props
|
||||
import bpy.types
|
||||
|
||||
|
||||
class AuginProperties(bpy.types.PropertyGroup):
|
||||
@@ -28,11 +27,3 @@ class AuginProperties(bpy.types.PropertyGroup):
|
||||
project_name: bpy.props.StringProperty(name="Project Name")
|
||||
project_filename: bpy.props.StringProperty(name="IFC Filename")
|
||||
is_success: bpy.props.BoolProperty(name="Is Successful Upload", default=False)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
username: str
|
||||
password: str
|
||||
token: str
|
||||
project_name: str
|
||||
project_filename: str
|
||||
is_success: bool
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
import bcf
|
||||
import bcf.bcfxml
|
||||
import bcf.v2.bcfxml
|
||||
import bpy
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -16,18 +16,22 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import uuid
|
||||
import webbrowser
|
||||
from math import atan, degrees, radians, tan
|
||||
from math import atan, cos, degrees, radians, sin, tan
|
||||
from pathlib import Path
|
||||
|
||||
import bcf
|
||||
import bcf.agnostic.topic
|
||||
import bcf.agnostic.visinfo
|
||||
import bcf.bcfxml
|
||||
import bcf.v2.bcfxml
|
||||
import bcf.v2.model
|
||||
import bcf.v2.topic
|
||||
import bcf.v2.visinfo
|
||||
import bcf.v3
|
||||
import bcf.v3.bcfxml
|
||||
import bcf.v3.document
|
||||
import bcf.v3.model
|
||||
@@ -39,10 +43,11 @@ import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.unit
|
||||
import numpy as np
|
||||
from bpy_extras.io_utils import ExportHelper, ImportHelper
|
||||
from mathutils import Matrix, Vector
|
||||
from mathutils import Euler, Matrix, Vector, geometry
|
||||
from xsdata.models.datatype import XmlDateTime
|
||||
|
||||
import bonsai.bim.module.bcf.bcfstore as bcfstore
|
||||
import bonsai.bim.module.bcf.prop as bcf_prop
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -1253,8 +1258,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
else:
|
||||
obj.data.show_background_images = False
|
||||
|
||||
assert (space := tool.Blender.get_view3d_space())
|
||||
space.region_3d.view_perspective = "CAMERA"
|
||||
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].region_3d.view_perspective = "CAMERA"
|
||||
|
||||
if self.file:
|
||||
self.set_viewpoint_components(viewpoint, context)
|
||||
|
||||
@@ -24,6 +24,8 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
@@ -230,7 +232,7 @@ class BcfTopic(PropertyGroup):
|
||||
|
||||
|
||||
def get_related_topics(self: "BCFProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
global RELATED_TOPICS_ENUM_ITEMS # ty: ignore[unresolved-global]
|
||||
global RELATED_TOPICS_ENUM_ITEMS
|
||||
props = self
|
||||
active_topic = props.active_topic
|
||||
active_related_topics = active_topic.related_topics.keys()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
|
||||
@@ -20,6 +20,7 @@ import bmesh
|
||||
import gpu
|
||||
from bpy.types import SpaceView3D
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from mathutils import Vector
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -27,18 +27,20 @@ import ifcopenshell.api
|
||||
import ifcopenshell.api.boundary
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape
|
||||
import ifcopenshell.util.unit
|
||||
import mathutils
|
||||
import numpy as np
|
||||
import shapely
|
||||
import shapely.ops
|
||||
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||
from mathutils import Matrix, Vector
|
||||
|
||||
import bonsai.bim.import_ifc as import_ifc
|
||||
import bonsai.core
|
||||
import bonsai.core.geometry
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
@@ -377,8 +379,6 @@ class EnableEditingBoundary(bpy.types.Operator):
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if entity and obj:
|
||||
setattr(bprops, blender_property, obj)
|
||||
bprops.physical_or_virtual = boundary.PhysicalOrVirtualBoundary or "NOTDEFINED"
|
||||
bprops.internal_or_external = boundary.InternalOrExternalBoundary or "NOTDEFINED"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -394,8 +394,6 @@ class DisableEditingBoundary(bpy.types.Operator):
|
||||
bprops.is_editing = False
|
||||
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
|
||||
setattr(bprops, blender_property, None)
|
||||
bprops.physical_or_virtual = "NOTDEFINED"
|
||||
bprops.internal_or_external = "NOTDEFINED"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -415,8 +413,6 @@ class EditBoundaryAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = getattr(bprops, blender_property, None)
|
||||
entity = tool.Ifc.get_entity(obj)
|
||||
attributes[blender_property] = entity
|
||||
attributes["physical_or_virtual"] = bprops.physical_or_virtual
|
||||
attributes["internal_or_external"] = bprops.internal_or_external
|
||||
ifcopenshell.api.boundary.edit_attributes(tool.Ifc.get(), entity=boundary, **attributes)
|
||||
bpy.ops.bim.disable_editing_boundary()
|
||||
return {"FINISHED"}
|
||||
@@ -708,7 +704,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
while True:
|
||||
tree.add_element(iterator.get_native())
|
||||
shape = iterator.get()
|
||||
assert isinstance(shape, W.TriangulationElement)
|
||||
shapes[shape.id] = {
|
||||
"verts": ifcopenshell.util.shape.get_vertices(shape.geometry),
|
||||
"faces": ifcopenshell.util.shape.get_faces(shape.geometry),
|
||||
|
||||
@@ -21,8 +21,13 @@ from typing import TYPE_CHECKING, Union
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -51,43 +56,12 @@ def element_filter(self: "BIMObjectBoundaryProperties", object: bpy.types.Object
|
||||
return False
|
||||
|
||||
|
||||
def get_internal_or_external_items(
|
||||
self: "BIMObjectBoundaryProperties", context: bpy.types.Context | None
|
||||
) -> list[tuple[str, str, str]]:
|
||||
items = [
|
||||
("INTERNAL", "Internal", ""),
|
||||
("EXTERNAL", "External", ""),
|
||||
]
|
||||
ifc = tool.Ifc.get()
|
||||
if not ifc or ifc.schema != "IFC2X3":
|
||||
items += [
|
||||
("EXTERNAL_EARTH", "External Earth", ""),
|
||||
("EXTERNAL_WATER", "External Water", ""),
|
||||
("EXTERNAL_FIRE", "External Fire", ""),
|
||||
]
|
||||
items.append(("NOTDEFINED", "Not Defined", ""))
|
||||
return items
|
||||
|
||||
|
||||
class BIMObjectBoundaryProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_space: PointerProperty(name="RelatingSpace", type=bpy.types.Object, poll=space_filter)
|
||||
related_building_element: PointerProperty(name="RelatedBuildingElement", type=bpy.types.Object, poll=element_filter)
|
||||
parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter)
|
||||
corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter)
|
||||
physical_or_virtual: EnumProperty(
|
||||
name="PhysicalOrVirtualBoundary",
|
||||
items=[
|
||||
("PHYSICAL", "Physical", ""),
|
||||
("VIRTUAL", "Virtual", ""),
|
||||
("NOTDEFINED", "Not Defined", ""),
|
||||
],
|
||||
default="NOTDEFINED",
|
||||
)
|
||||
internal_or_external: EnumProperty(
|
||||
name="InternalOrExternalBoundary",
|
||||
items=get_internal_or_external_items,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
is_editing: bool
|
||||
@@ -95,8 +69,6 @@ class BIMObjectBoundaryProperties(PropertyGroup):
|
||||
related_building_element: Union[bpy.types.Object, None]
|
||||
parent_boundary: Union[bpy.types.Object, None]
|
||||
corresponding_boundary: Union[bpy.types.Object, None]
|
||||
physical_or_virtual: str
|
||||
internal_or_external: str # values depend on schema: IFC2X3 omits EXTERNAL_EARTH/WATER/FIRE
|
||||
|
||||
|
||||
class BIMBoundaryProperties(PropertyGroup):
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.boundary.data import SpaceBoundariesData
|
||||
@@ -77,10 +78,6 @@ class BIM_PT_Boundary(Panel):
|
||||
self.draw_relation_editor(boundary, "RelatedBuildingElement", "related_building_element")
|
||||
self.draw_relation_editor(boundary, "ParentBoundary", "parent_boundary")
|
||||
self.draw_relation_editor(boundary, "CorrespondingBoundary", "corresponding_boundary")
|
||||
row = self.layout.row()
|
||||
row.prop(self.bprops, "physical_or_virtual")
|
||||
row = self.layout.row()
|
||||
row.prop(self.bprops, "internal_or_external")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.enable_editing_boundary", icon="GREASEPENCIL", text="Edit")
|
||||
@@ -88,8 +85,6 @@ class BIM_PT_Boundary(Panel):
|
||||
self.draw_relation_data(boundary, "RelatedBuildingElement")
|
||||
self.draw_relation_data(boundary, "ParentBoundary")
|
||||
self.draw_relation_data(boundary, "CorrespondingBoundary")
|
||||
self.draw_enum_data(boundary, "PhysicalOrVirtualBoundary")
|
||||
self.draw_enum_data(boundary, "InternalOrExternalBoundary")
|
||||
if hasattr(boundary, "InnerBoundaries"):
|
||||
for i, inner_boundary in enumerate(getattr(boundary, "InnerBoundaries", ())):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -116,11 +111,6 @@ class BIM_PT_Boundary(Panel):
|
||||
else:
|
||||
row.label(text="")
|
||||
|
||||
def draw_enum_data(self, boundary, ifc_attribute: str):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=ifc_attribute)
|
||||
row.label(text=getattr(boundary, ifc_attribute, "") or "")
|
||||
|
||||
def draw_relation_editor(self, boundary, ifc_attribute: str, blender_property: str):
|
||||
if hasattr(boundary, ifc_attribute):
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -63,7 +63,8 @@ class BrickschemaData:
|
||||
if namespace == "https://brickschema.org/schema/Brick":
|
||||
return []
|
||||
results = []
|
||||
query = BrickStore.graph.query("""
|
||||
query = BrickStore.graph.query(
|
||||
"""
|
||||
PREFIX brick: <https://brickschema.org/schema/Brick#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||
@@ -80,7 +81,10 @@ class BrickschemaData:
|
||||
}
|
||||
}
|
||||
GROUP BY ?object
|
||||
""".replace("{uri}", uri))
|
||||
""".replace(
|
||||
"{uri}", uri
|
||||
)
|
||||
)
|
||||
for row in query:
|
||||
predicate_uri = row.get("predicate")
|
||||
predicate_name = predicate_uri.toPython().split("#")[-1]
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import os
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
from bpy_extras.io_utils import ExportHelper, ImportHelper
|
||||
|
||||
import bonsai.bim.handler
|
||||
|
||||
@@ -23,7 +23,10 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -31,7 +34,7 @@ from bpy.types import PropertyGroup
|
||||
import bonsai.core.brick as core
|
||||
import bonsai.tool.brick as tool
|
||||
from bonsai.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
||||
from bonsai.bim.prop import StrProperty
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
from bonsai.tool.brick import BrickStore
|
||||
|
||||
|
||||
@@ -46,26 +49,26 @@ def get_libraries(self, context):
|
||||
|
||||
|
||||
def get_namespaces(self, context):
|
||||
global NAMESPACES_ENUM_ITEMS # ty: ignore[unresolved-global]
|
||||
global NAMESPACES_ENUM_ITEMS
|
||||
NAMESPACES_ENUM_ITEMS = [(uri, f"{alias}: {uri}", "") for alias, uri in BrickStore.namespaces]
|
||||
return NAMESPACES_ENUM_ITEMS
|
||||
|
||||
|
||||
def get_brick_entity_classes(self, context):
|
||||
global ENTITY_CLASSES_ENUM_ITEMS # ty: ignore[unresolved-global]
|
||||
global ENTITY_CLASSES_ENUM_ITEMS
|
||||
entity = self.brick_entity_create_type
|
||||
ENTITY_CLASSES_ENUM_ITEMS = [(uri, uri.split("#")[-1], "") for uri in BrickStore.entity_classes[entity]]
|
||||
return ENTITY_CLASSES_ENUM_ITEMS
|
||||
|
||||
|
||||
def get_brick_roots(self, context):
|
||||
global BRICK_ROOTS_ENUM_ITEMS # ty: ignore[unresolved-global]
|
||||
global BRICK_ROOTS_ENUM_ITEMS
|
||||
BRICK_ROOTS_ENUM_ITEMS = [(root, root, "") for root in BrickStore.root_classes]
|
||||
return BRICK_ROOTS_ENUM_ITEMS
|
||||
|
||||
|
||||
def get_brick_relations(self, context):
|
||||
global BRICK_RELATIONS_ENUM_ITEMS # ty: ignore[unresolved-global]
|
||||
global BRICK_RELATIONS_ENUM_ITEMS
|
||||
BRICK_RELATIONS_ENUM_ITEMS = [(uri, uri.split("#")[-1], "") for uri in BrickStore.relationships]
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
if relation["predicate_name"] == "label":
|
||||
|
||||
@@ -16,18 +16,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.brick.prop import Brick
|
||||
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
|
||||
from bonsai.tool.brick import BrickStore
|
||||
@@ -283,9 +274,7 @@ class BIM_PT_brickschema_viewport(Panel):
|
||||
class BIM_UL_bricks(UIList):
|
||||
split_screen = False
|
||||
|
||||
def draw_item(
|
||||
self, context, layout: bpy.types.UILayout, data, item: Brick, icon, active_data, active_propname
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
split = layout.split(factor=0.85, align=True)
|
||||
row = split.row()
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.classification
|
||||
import ifcopenshell.util.date
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def refresh():
|
||||
|
||||
@@ -19,6 +19,7 @@ import textwrap
|
||||
from typing import Any
|
||||
|
||||
import bpy
|
||||
import bsdd
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -31,7 +34,7 @@ from bpy.types import PropertyGroup
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.bsdd.data import BSDDData
|
||||
from bonsai.bim.module.classification.data import ClassificationsData
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
|
||||
|
||||
def get_active_dictionary(self: "BIMBSDDProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import math
|
||||
from math import pi, sqrt
|
||||
from math import cos, pi, radians, sin, sqrt
|
||||
from typing import Union
|
||||
|
||||
import bmesh
|
||||
import bpy
|
||||
import bpy_extras
|
||||
import ifcopenshell.util.unit
|
||||
import mathutils
|
||||
from mathutils import Matrix, Vector
|
||||
|
||||
@@ -37,7 +39,6 @@ messages = {
|
||||
class CadTrimExtend(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_trim_extend"
|
||||
bl_label = "CAD Trim / Extend"
|
||||
bl_description = "Extends/reduces element to 3D cursor"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -83,7 +84,6 @@ class CadTrimExtend(bpy.types.Operator):
|
||||
class CadMitre(bpy.types.Operator):
|
||||
bl_idname = "bim.cad_mitre"
|
||||
bl_label = "CAD Mitre"
|
||||
bl_description = "Joins two non-parallel paths at their intersection"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -22,6 +22,8 @@ from typing import TYPE_CHECKING
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
|
||||
|
||||
class BIMCadProperties(PropertyGroup):
|
||||
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
|
||||
|
||||
@@ -20,10 +20,12 @@ import os
|
||||
from functools import partial
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.unit
|
||||
from bpy.types import WorkSpaceTool
|
||||
|
||||
import bonsai.bim.module.type.prop as type_prop
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.model.data import RailingData, RoofData
|
||||
from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData
|
||||
|
||||
|
||||
def load_custom_icons():
|
||||
@@ -106,37 +108,23 @@ class CadTool(WorkSpaceTool):
|
||||
)
|
||||
|
||||
row = layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Extend", "S_E", bpy.ops.bim.cad_trim_extend.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Join", "S_T", bpy.ops.bim.cad_mitre.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context
|
||||
)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Rectangle", "S_R", bpy.ops.bim.add_rectangle.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Rectangle", "S_R", bpy.ops.bim.add_rectangle.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Circle", "S_C", bpy.ops.bim.add_ifccircle.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Circle", "S_C", bpy.ops.bim.add_ifccircle.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "3-Point Arc", "S_V", bpy.ops.bim.set_arc_index.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "3-Point Arc", "S_V", bpy.ops.bim.set_arc_index.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Reset Vertex", "S_X", bpy.ops.bim.reset_vertex.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Reset Vertex", "S_X", bpy.ops.bim.reset_vertex.__doc__, ui_context)
|
||||
|
||||
elif (
|
||||
isinstance(data, tool.Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
@@ -146,21 +134,15 @@ class CadTool(WorkSpaceTool):
|
||||
layout, "Edit Axis", "bim.edit_extrusion_axis", "bim.disable_editing_extrusion_axis", ui_context
|
||||
)
|
||||
row = layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Extend", "S_E", bpy.ops.bim.cad_trim_extend.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Join", "S_T", bpy.ops.bim.cad_mitre.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context
|
||||
)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Fillet", "S_F", bpy.ops.bim.cad_fillet.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.cad_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
|
||||
else:
|
||||
if (
|
||||
@@ -188,37 +170,19 @@ class CadTool(WorkSpaceTool):
|
||||
add_layout_hotkey_operator(row, "Set Gable Roof Angle", "S_R", "Set Gable Roof Angle", ui_context)
|
||||
|
||||
row = layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Extend", "S_E", bpy.ops.bim.cad_trim_extend.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Extend", "S_E", "Extends/reduces element to 3D cursor", ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Join", "S_T", bpy.ops.bim.cad_mitre.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
row, "Join", "S_T", "Joins two non-parallel paths at their intersection", ui_context
|
||||
)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Fillet", "S_F", bpy.ops.bim.add_ifcarcindex_fillet.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__.split("\n", 1)[1].strip(), ui_context
|
||||
)
|
||||
add_layout_hotkey_operator(row, "Offset", "S_O", bpy.ops.bim.cad_offset.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row,
|
||||
"2-Point Arc",
|
||||
"S_C",
|
||||
bpy.ops.bim.cad_arc_from_2_points.__doc__.split("\n", 1)[1].strip(),
|
||||
ui_context,
|
||||
)
|
||||
add_layout_hotkey_operator(row, "2-Point Arc", "S_C", bpy.ops.bim.cad_arc_from_2_points.__doc__, ui_context)
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(
|
||||
row,
|
||||
"3-Point Arc",
|
||||
"S_V",
|
||||
bpy.ops.bim.cad_arc_from_3_points.__doc__.split("\n", 1)[1].strip(),
|
||||
ui_context,
|
||||
)
|
||||
add_layout_hotkey_operator(row, "3-Point Arc", "S_V", bpy.ops.bim.cad_arc_from_3_points.__doc__, ui_context)
|
||||
|
||||
|
||||
class CadHotkey(bpy.types.Operator):
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
import json
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blf
|
||||
import bmesh
|
||||
import gpu
|
||||
from bpy.types import SpaceView3D
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from math import radians
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bmesh
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import numpy as np
|
||||
from bpy_extras.io_utils import ExportHelper, ImportHelper
|
||||
from mathutils import Matrix, Vector
|
||||
|
||||
@@ -457,7 +460,9 @@ class HideClash(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
ClashDecorator.uninstall()
|
||||
tool.Blender.update_all_viewports(context)
|
||||
for area in context.screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
area.tag_redraw()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from bpy.props import (
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -33,7 +34,7 @@ from ifcopenshell.geom.main import CLASH_TYPE_ITEMS, ClashType
|
||||
from mathutils import Vector
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.prop import BIMFilterGroup, StrProperty
|
||||
from bonsai.bim.prop import Attribute, BIMFilterGroup, StrProperty
|
||||
|
||||
|
||||
class ClashSource(PropertyGroup):
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.classification
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.classification
|
||||
|
||||
@@ -23,7 +23,10 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -25,6 +25,7 @@ import ifcopenshell.util.classification
|
||||
from bpy.types import Panel, UIList
|
||||
|
||||
import bonsai.bim.helper
|
||||
import bonsai.bim.module.classification.prop as classification_prop
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.classification.data import (
|
||||
ClassificationsData,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.constraint
|
||||
|
||||
import bonsai.bim.helper
|
||||
|
||||
@@ -20,13 +20,19 @@ from typing import TYPE_CHECKING, Literal
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
from ifcopenshell.util.doc import get_entity_doc
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.constraint.data import ConstraintsData
|
||||
from bonsai.bim.prop import Attribute
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -20,13 +20,19 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.module.context.data import ContextData
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
|
||||
|
||||
class BIMContextProperties(PropertyGroup):
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
|
||||
from typing import Any, Union
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -987,10 +987,10 @@ class ExportCostSchedulesToPDF(bpy.types.Operator, ExportHelper):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
try:
|
||||
import typst # noqa: F401
|
||||
import typst
|
||||
|
||||
return True
|
||||
except ModuleNotFoundError:
|
||||
except:
|
||||
cls.poll_message_set(
|
||||
"Typst not available.\nIt can be installed from Quality and\nControl -> Debug and using 'typst' with Pip Install.\n(Run Blender as Administrator)"
|
||||
)
|
||||
|
||||
@@ -19,13 +19,16 @@
|
||||
from typing import TYPE_CHECKING, Literal, Union
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.cost
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -26,11 +26,10 @@ from bpy.types import Panel, UIList
|
||||
import bonsai.bim.helper
|
||||
import bonsai.bim.module.cost.prop as CostProp
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.cost.data import CostItem, CostSchedulesData
|
||||
from bonsai.bim.module.cost.data import CostSchedulesData
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
|
||||
from bonsai.bim.prop import StrProperty
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
@@ -399,8 +398,8 @@ class BIM_PT_cost_item_types(Panel):
|
||||
op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC")
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
|
||||
rtprops = context.scene.BIMResourceTreeProperties
|
||||
rprops = tool.Resource.get_resource_props()
|
||||
rtprops = rprops.tree
|
||||
if rtprops.resources and rprops.active_resource_index < len(rtprops.resources):
|
||||
if has_quantity_names:
|
||||
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
|
||||
@@ -662,18 +661,9 @@ class BIM_UL_cost_items_trait:
|
||||
split2.alignment = "LEFT"
|
||||
split2.label(text="Rate")
|
||||
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMCostProperties,
|
||||
item: CostProp.CostItem,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
self.props = data
|
||||
self.props = tool.Cost.get_cost_props()
|
||||
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -704,7 +694,7 @@ class BIM_UL_cost_items_trait:
|
||||
|
||||
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
|
||||
|
||||
def draw_parent_operator(self, row: bpy.types.UILayout, cost_item_id: int) -> None:
|
||||
def draw_parent_operator(self, row, cost_item_id):
|
||||
if self.props.active_cost_item_id:
|
||||
if self.props.active_cost_item_id != cost_item_id:
|
||||
op = row.operator("bim.change_parent_cost_item", text="", icon="LINKED", emboss=False).new_parent = (
|
||||
@@ -713,7 +703,7 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
def draw_hierarchy(self, row: bpy.types.UILayout, item: CostProp.CostItem) -> None:
|
||||
def draw_hierarchy(self, row, item):
|
||||
for i in range(0, item.level_index):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
@@ -759,7 +749,7 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
row.label(text="-")
|
||||
|
||||
def draw_value_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
|
||||
def draw_value_column(self, layout, cost_item):
|
||||
if cost_item["TotalAppliedValue"]:
|
||||
text = "{0:,.2f}".format(cost_item["TotalAppliedValue"]).replace(",", " ")
|
||||
if cost_item["UnitBasisValueComponent"] not in [None, 1]:
|
||||
@@ -770,13 +760,13 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
layout.label(text="-")
|
||||
|
||||
def draw_total_cost_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
|
||||
currency = CostSchedulesData.data["currency"]
|
||||
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
|
||||
layout.label(text=text)
|
||||
|
||||
def draw_order_operator(self, row: bpy.types.UILayout, ifc_definition_id: int, cost_item: CostItem) -> None:
|
||||
def draw_order_operator(self, row, ifc_definition_id, cost_item):
|
||||
if cost_item["NestingIndex"] is not None:
|
||||
if cost_item["NestingIndex"] == 0:
|
||||
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_DOWN", text="")
|
||||
@@ -803,14 +793,12 @@ class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
|
||||
def draw_quantity_column(self, layout, cost_item):
|
||||
self.draw_uom_column(layout, cost_item)
|
||||
|
||||
def draw_total_cost_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
pass # No such thing as a total cost in a schedule of rates
|
||||
|
||||
|
||||
class BIM_UL_cost_columns(UIList):
|
||||
def draw_item(
|
||||
self, context, layout: bpy.types.UILayout, data, item: StrProperty, icon, active_data, active_propname
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
@@ -818,17 +806,9 @@ class BIM_UL_cost_columns(UIList):
|
||||
|
||||
|
||||
class BIM_UL_cost_item_types(UIList):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMCostProperties,
|
||||
item: CostProp.CostItemType,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
cost_item = data.cost_items[data.active_cost_item_index]
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
props = tool.Cost.get_cost_props()
|
||||
cost_item = props.cost_items[props.active_cost_item_index]
|
||||
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
@@ -864,16 +844,7 @@ class BIM_UL_cost_item_quantities(UIList):
|
||||
|
||||
|
||||
class BIM_UL_product_cost_items(UIList):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data,
|
||||
item: CostItemQuantity,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.highlight_product_cost_item", text="", icon="STYLUS_PRESSURE")
|
||||
|
||||
@@ -21,6 +21,7 @@ import os
|
||||
from functools import partial
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
from bpy.types import WorkSpaceTool
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -20,6 +20,7 @@ import json
|
||||
from math import atan2, degrees
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from collections import Counter
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
import ifccsv
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
from bpy_extras.io_utils import ExportHelper, ImportHelper
|
||||
|
||||
@@ -23,11 +23,15 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
from bonsai.bim.prop import BIMFilterGroup
|
||||
from bonsai.bim.prop import BIMFilterGroup, StrProperty
|
||||
|
||||
|
||||
class CsvAttribute(PropertyGroup):
|
||||
|
||||
@@ -29,6 +29,7 @@ from typing import TYPE_CHECKING, Any, Literal, Union, assert_never, get_args
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
|
||||
@@ -20,9 +20,13 @@ from typing import TYPE_CHECKING, Literal, get_args
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -32,10 +32,18 @@
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
|
||||
# Properties have many different data types. We won't use all of them in this
|
||||
# demo module, but this is a list for your reference.
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -98,8 +98,8 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
obj.color = (0.0, 1.0, 0.0, 1.0)
|
||||
elif global_id in diff["changed"]:
|
||||
obj.color = (0.0, 0.0, 1.0, 1.0)
|
||||
assert (space := tool.Blender.get_view3d_space())
|
||||
space.shading.color_type = "OBJECT"
|
||||
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].shading.color_type = "OBJECT"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -23,12 +23,16 @@ from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
from bonsai.bim.module.diff.data import DiffData
|
||||
from bonsai.bim.prop import BIMFilterGroup
|
||||
from bonsai.bim.prop import BIMFilterGroup, StrProperty
|
||||
|
||||
|
||||
def update_diff_json_file(self: "DiffProperties", context: bpy.types.Context) -> None:
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
import os
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
from natsort import natsorted
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
import json
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
|
||||
import bonsai.bim.handler
|
||||
import bonsai.core.document as core
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.document.data import ObjectDocumentData
|
||||
from bonsai.bim.module.document.data import DocumentData, ObjectDocumentData
|
||||
|
||||
|
||||
class LoadProjectDocuments(bpy.types.Operator):
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
from typing import TYPE_CHECKING, Literal, Union
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.document.data import DocumentData, refresh
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
|
||||
|
||||
def update_document_name(self: "Document", context: bpy.types.Context) -> None:
|
||||
@@ -66,7 +69,7 @@ class Document(PropertyGroup):
|
||||
tree_depth: int
|
||||
has_children: bool
|
||||
is_expanded: bool
|
||||
document_type: Literal["PROJECT", "INFORMATION", "REFERENCE"]
|
||||
document_type: str
|
||||
|
||||
|
||||
class DocumentObject(PropertyGroup):
|
||||
|
||||
@@ -16,22 +16,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.document.prop import (
|
||||
BIMDocumentProperties,
|
||||
Document,
|
||||
DocumentObject,
|
||||
)
|
||||
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.document.data import DocumentData, ObjectDocumentData
|
||||
|
||||
@@ -220,16 +208,7 @@ class BIM_PT_object_documents(Panel):
|
||||
|
||||
|
||||
class BIM_UL_documents(UIList):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMDocumentProperties,
|
||||
item: Document,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
indent_depth = 0
|
||||
@@ -274,22 +253,16 @@ class BIM_UL_documents(UIList):
|
||||
|
||||
|
||||
class BIM_UL_document_objects(UIList):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMDocumentProperties,
|
||||
item: DocumentObject,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
) -> None:
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.prop(item, "name", text="", emboss=False, icon="OBJECT_DATA")
|
||||
row.operator("bim.select_object", text="", icon="RESTRICT_SELECT_OFF").obj_name = item.name
|
||||
|
||||
if document := data.active_document:
|
||||
props = tool.Document.get_document_props()
|
||||
if props.active_document:
|
||||
document = props.active_document
|
||||
|
||||
op = row.operator("bim.unassign_document", text="", icon="X")
|
||||
op.document = document.ifc_definition_id
|
||||
op.obj = item.name
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import bmesh
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
from mathutils import Vector
|
||||
from mathutils import Matrix, Vector
|
||||
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ from pathlib import Path
|
||||
from typing import Any, Union
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.classification
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.unit
|
||||
from natsort import natsorted
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ from functools import cache
|
||||
from math import acos, atan, cos, degrees, pi, radians, sin
|
||||
from pathlib import Path
|
||||
from timeit import default_timer as timer
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
import blf
|
||||
import bmesh
|
||||
@@ -34,6 +34,7 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.unit
|
||||
import numpy as np
|
||||
import shapely
|
||||
from bpy.types import SpaceView3D
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
@@ -70,9 +71,9 @@ class profile_consequential:
|
||||
cls.start_time = None
|
||||
lines = "\n".join(cls.lines)
|
||||
print(lines)
|
||||
import pyperclip
|
||||
|
||||
assert (wm := bpy.context.window_manager)
|
||||
wm.clipboard = lines
|
||||
pyperclip.copy(lines)
|
||||
cls.lines = []
|
||||
|
||||
|
||||
@@ -1787,7 +1788,7 @@ class CutDecorator:
|
||||
|
||||
# Handle both old float64 and new float32 checksums for version compatibility
|
||||
rot_checksum_bytes: bytes = eval(DecoratorData.camera_rotation_checksum)
|
||||
rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9).reshape(3, 3)
|
||||
rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9)
|
||||
rot_real = tool.Blender.np_array_legacy(obj.matrix_world.to_3x3())
|
||||
rot_dot = np.dot(rot_check, rot_real.T)
|
||||
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
|
||||
|
||||
@@ -151,7 +151,7 @@ from bpy_extras.view3d_utils import (
|
||||
)
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from ifcopenshell.util.unit import si_conversions
|
||||
from mathutils import Matrix, Vector, geometry
|
||||
from mathutils import Matrix, Vector
|
||||
from mathutils.geometry import intersect_line_line
|
||||
from mathutils.kdtree import KDTree
|
||||
|
||||
@@ -1243,7 +1243,9 @@ class SnapManager:
|
||||
@staticmethod
|
||||
def _redraw_viewport() -> None:
|
||||
"""Force 3D viewport redraw."""
|
||||
tool.Blender.update_all_viewports()
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
area.tag_redraw()
|
||||
|
||||
def build_snap_cache(
|
||||
self, context: bpy.types.Context, active_obj: bpy.types.Object, include_active: bool = False
|
||||
@@ -1285,7 +1287,7 @@ class SnapManager:
|
||||
continue
|
||||
|
||||
coords = np.empty(vertex_count * 3, dtype=np.float32)
|
||||
mesh.vertices.foreach_get("co", coords)
|
||||
mesh.vertices.foreach_get("co", coords) # type: ignore[arg-type]
|
||||
coords = coords.reshape(-1, 3)
|
||||
|
||||
matrix = np.array(obj_eval.matrix_world, dtype=np.float32)
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import math
|
||||
from typing import Union
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import mathutils.geometry
|
||||
@@ -456,8 +458,7 @@ def format_distance(
|
||||
tx_dist = fmt % d_cm
|
||||
|
||||
else:
|
||||
assert f"Unexpected unit_system - '{unit_system}'."
|
||||
# tx_dist = fmt % value
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
@@ -41,8 +41,8 @@ from typing import (
|
||||
import bmesh
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.document
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.geom
|
||||
@@ -50,18 +50,19 @@ import ifcopenshell.ifcopenshell_wrapper
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.shape_builder
|
||||
import ifcopenshell.util.unit
|
||||
import numpy as np
|
||||
import shapely
|
||||
import shapely.ops
|
||||
from bpy_extras.image_utils import load_image
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from lxml import etree
|
||||
from mathutils import Color, Vector
|
||||
from mathutils import Color, Matrix, Vector
|
||||
|
||||
import bonsai.bim.export_ifc
|
||||
import bonsai.bim.handler
|
||||
import bonsai.bim.import_ifc
|
||||
import bonsai.bim.helper
|
||||
import bonsai.bim.module.drawing.annotation as annotation
|
||||
import bonsai.bim.module.drawing.sheeter as sheeter
|
||||
import bonsai.bim.module.drawing.svgwriter as svgwriter
|
||||
import bonsai.core.drawing as core
|
||||
@@ -137,7 +138,7 @@ class AddAnnotationType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element.ApplicableOccurrence = f"IfcAnnotation/{object_type}"
|
||||
|
||||
if props.create_representation_for_type and object_type == "IMAGE":
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", existing_object_by_name=obj.name)
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name)
|
||||
|
||||
|
||||
class EnableAddAnnotationType(bpy.types.Operator):
|
||||
@@ -927,7 +928,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.get_loaded_links_for_drawings():
|
||||
files[link.filepath] = self.get_linked_file(link)
|
||||
files[link.name] = self.get_linked_file(link)
|
||||
|
||||
target_view = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]["TargetView"]
|
||||
self.setup_serialiser(target_view)
|
||||
@@ -1373,7 +1374,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def get_linked_file(self, link: "Link") -> ifcopenshell.file:
|
||||
link_path = link.filepath
|
||||
link_path = link.name
|
||||
ifc_file = IfcStore.session_files.get(link_path, None)
|
||||
if ifc_file is not None:
|
||||
return ifc_file
|
||||
@@ -1426,7 +1427,6 @@ class CreateDrawing(bpy.types.Operator):
|
||||
"/Pset_.*Common/.Status",
|
||||
"EPset_Status.Status",
|
||||
"EPset_Status.UserDefinedStatus",
|
||||
"Material.Name",
|
||||
]
|
||||
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
@@ -1759,7 +1759,7 @@ class AddAnnotation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
enable_editing=True,
|
||||
)
|
||||
if props.object_type == "IMAGE":
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", existing_object_by_name=obj.name)
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name)
|
||||
|
||||
|
||||
class AddSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -2896,16 +2896,12 @@ class AddSchedule(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Add an .ods, .xls or .xlsx file as a schedule"
|
||||
|
||||
files: bpy.props.CollectionProperty(name="Files", type=bpy.types.OperatorFileListElement)
|
||||
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ods;*.xls;*.xlsx", options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
|
||||
def _execute(self, context):
|
||||
for filepath in tool.Blender.get_selected_files(
|
||||
self.directory, self.files, use_relative_path=self.use_relative_path
|
||||
):
|
||||
core.add_document(tool.Ifc, tool.Drawing, "SCHEDULE", uri=filepath)
|
||||
filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path)
|
||||
core.add_document(tool.Ifc, tool.Drawing, "SCHEDULE", uri=filepath)
|
||||
|
||||
|
||||
class RemoveSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -3094,16 +3090,23 @@ class AddReference(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_description = "Import a .svg file to the project as a reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
files: bpy.props.CollectionProperty(name="Files", type=bpy.types.OperatorFileListElement)
|
||||
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.svg", options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
filename_ext = ".svg"
|
||||
|
||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement)
|
||||
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
||||
|
||||
def _execute(self, context):
|
||||
for filepath in tool.Blender.get_selected_files(
|
||||
self.directory, self.files, use_relative_path=self.use_relative_path
|
||||
):
|
||||
# Handle both single and multiple file selection
|
||||
if self.files:
|
||||
for file_elem in self.files:
|
||||
filepath = os.path.join(self.directory, file_elem.name)
|
||||
uri = tool.Ifc.get_uri(filepath, use_relative_path=self.use_relative_path)
|
||||
core.add_document(tool.Ifc, tool.Drawing, "REFERENCE", uri=uri)
|
||||
else:
|
||||
# Fallback for single file (backward compatibility)
|
||||
filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path)
|
||||
core.add_document(tool.Ifc, tool.Drawing, "REFERENCE", uri=filepath)
|
||||
|
||||
|
||||
@@ -3162,7 +3165,6 @@ class EditTextPopup(bpy.types.Operator):
|
||||
bpy.ops.bim.disable_editing_text()
|
||||
|
||||
def execute(self, context):
|
||||
# TODO: check for possible subtle undo bug here
|
||||
# can't use invoke() because this operator
|
||||
# will be run indirectly by hotkey
|
||||
# so we use execute() and track whether it's the first run of the operator
|
||||
@@ -3306,8 +3308,9 @@ class AddTextLiteral(bpy.types.Operator):
|
||||
attr.data_type = "string"
|
||||
attr.string_value = literal_attr_values[attr_name]
|
||||
|
||||
literal_props.align_vertical = "bottom"
|
||||
literal_props.align_horizontal = "left"
|
||||
box_alignment_mask = [False] * 9
|
||||
box_alignment_mask[6] = True # bottom_left box_alignment
|
||||
literal_props.box_alignment = box_alignment_mask
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -3365,55 +3368,57 @@ class OrderTextLiteralDown(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignSelectedObjectAsProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# Ifc Operator is unnecessary, because suboperator is handling IFC changes.
|
||||
class AssignSelectedObjectAsProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_selected_as_product"
|
||||
bl_label = "Assign Selected Object As Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.selected_objects) < 2:
|
||||
cls.poll_message_set("At least 2 objects need to be selected")
|
||||
if len(context.selected_objects) != 2:
|
||||
cls.poll_message_set("2 objects need to be selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
def execute(self, context):
|
||||
assert bpy.context.view_layer
|
||||
objs = context.selected_objects[:]
|
||||
ifc_objs = [(o, tool.Ifc.get_entity(o)) for o in objs if tool.Ifc.get_entity(o)]
|
||||
obj1, obj2 = objs
|
||||
element1 = tool.Ifc.get_entity(obj1)
|
||||
element2 = tool.Ifc.get_entity(obj2)
|
||||
assert element1 and element2
|
||||
|
||||
annotations = [(o, e) for o, e in ifc_objs if e.is_a("IfcAnnotation")]
|
||||
non_annotations = [(o, e) for o, e in ifc_objs if not e.is_a("IfcAnnotation")]
|
||||
# Check if at least one object is an IfcAnnotation
|
||||
is_annotation1 = element1.is_a("IfcAnnotation")
|
||||
is_annotation2 = element2.is_a("IfcAnnotation")
|
||||
|
||||
if not annotations:
|
||||
self.report({"ERROR"}, "At least one selected object must be an IfcAnnotation.")
|
||||
if not (is_annotation1 or is_annotation2):
|
||||
self.report({"ERROR"}, "At least one of the selected objects must be IfcAnnotation.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if len(non_annotations) == 1:
|
||||
# One product, one or more annotations — assign all annotations to the product.
|
||||
product = non_annotations[0][1]
|
||||
elif len(non_annotations) == 0 and len(annotations) == 2:
|
||||
# Both objects are annotations — use the non-active one as the relating product.
|
||||
# If both are annotations, use the currently active object as relating product
|
||||
if is_annotation1 and is_annotation2:
|
||||
active_obj = context.active_object
|
||||
if annotations[0][0] == active_obj:
|
||||
annotation_obj, annotation = annotations[0]
|
||||
product = annotations[1][1]
|
||||
if active_obj == obj1:
|
||||
other_selected_object = obj1
|
||||
bpy.context.view_layer.objects.active = obj2
|
||||
else:
|
||||
annotation_obj, annotation = annotations[1]
|
||||
product = annotations[0][1]
|
||||
core.edit_assigned_product(tool.Ifc, tool.Drawing, obj=annotation_obj, product=product)
|
||||
tool.Blender.update_viewport()
|
||||
return
|
||||
other_selected_object = obj2
|
||||
bpy.context.view_layer.objects.active = obj1
|
||||
# If only one is an annotation, make it the active object
|
||||
elif is_annotation1:
|
||||
other_selected_object = obj2
|
||||
bpy.context.view_layer.objects.active = obj1
|
||||
else:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"Select exactly one product object and one or more IfcAnnotation objects.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
other_selected_object = obj1
|
||||
bpy.context.view_layer.objects.active = obj2
|
||||
|
||||
for annotation_obj, _ in annotations:
|
||||
core.edit_assigned_product(tool.Ifc, tool.Drawing, obj=annotation_obj, product=product)
|
||||
|
||||
tool.Blender.update_viewport()
|
||||
assert (active_obj := context.active_object)
|
||||
props = tool.Drawing.get_object_assigned_product_props(active_obj)
|
||||
props.relating_product = other_selected_object
|
||||
bpy.ops.bim.edit_assigned_product()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -3799,51 +3804,83 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
filter_image: bpy.props.BoolProperty(default=True, options={"HIDDEN", "SKIP_SAVE"})
|
||||
filter_folder: bpy.props.BoolProperty(default=True, options={"HIDDEN", "SKIP_SAVE"})
|
||||
|
||||
override_existing_image: bpy.props.BoolProperty(
|
||||
name="Override Existing Image",
|
||||
default=True,
|
||||
description=(
|
||||
"Override image if it was previously loaded to Blender. If disabled, will always create a new image"
|
||||
),
|
||||
)
|
||||
use_existing_object_by_name: bpy.props.StringProperty(
|
||||
name="Use Existing Object By Name",
|
||||
description="Existing object name to add a style with reference image to. If not provided will create a new object.",
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
x_length: bpy.props.FloatProperty(
|
||||
name="X Length",
|
||||
description="Width of the reference image",
|
||||
description="Width of the reference image in project units",
|
||||
default=1.0,
|
||||
min=0.001,
|
||||
soft_min=0.01,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
)
|
||||
y_length: bpy.props.FloatProperty(
|
||||
name="Y Length",
|
||||
description="Height of the reference image",
|
||||
description="Height of the reference image in project units",
|
||||
default=1.0,
|
||||
min=0.001,
|
||||
soft_min=0.01,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
)
|
||||
show_texture_solid_mode: bpy.props.BoolProperty(
|
||||
name="Show Texture in Solid mode (slow)",
|
||||
description="Show Texture in Solid mode (slow)",
|
||||
default=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not tool.Ifc.get():
|
||||
cls.poll_message_set("No IFC project is loaded.")
|
||||
return False
|
||||
return True
|
||||
show_dimensions_dialog: bpy.props.BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
if getattr(self, "show_dimensions_dialog", False):
|
||||
if tool.Ifc.get():
|
||||
length_unit = ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "LENGTHUNIT")
|
||||
if length_unit:
|
||||
unit_name = ifcopenshell.util.unit.get_full_unit_name(length_unit).lower()
|
||||
else:
|
||||
unit_name = "project units"
|
||||
layout.label(text=f"Set Reference Image Dimensions (in {unit_name}):")
|
||||
else:
|
||||
layout.label(text="Set Reference Image Dimensions (in project units):")
|
||||
layout.separator()
|
||||
layout.prop(self, "x_length")
|
||||
layout.prop(self, "y_length")
|
||||
else:
|
||||
if Path(tool.Ifc.get_path()).is_file():
|
||||
layout.prop(self, "use_relative_path")
|
||||
else:
|
||||
self.use_relative_path = False
|
||||
layout.label(text="Save the .ifc file first ")
|
||||
layout.label(text="to use relative paths.")
|
||||
layout.prop(self, "override_existing_image")
|
||||
layout.prop(self, "use_existing_object_by_name")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self._last_filepath = ""
|
||||
return super().invoke(context, event)
|
||||
|
||||
def check(self, context):
|
||||
if not hasattr(self, "_last_filepath"):
|
||||
self._last_filepath = ""
|
||||
|
||||
if self.filepath and self.filepath != self._last_filepath:
|
||||
self._last_filepath = self.filepath
|
||||
if not getattr(self, "show_dimensions_dialog", False):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
else:
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
if not getattr(self, "show_dimensions_dialog", False):
|
||||
abs_path = Path(self.filepath).absolute().resolve()
|
||||
if abs_path.exists() and abs_path.is_file():
|
||||
image = load_image(abs_path.name, str(abs_path.parent), check_existing=False)
|
||||
if self.override_existing_image:
|
||||
params = {"check_existing": True, "force_reload": True}
|
||||
else:
|
||||
params = {"check_existing": False}
|
||||
|
||||
try:
|
||||
image = load_image(abs_path.name, str(abs_path.parent), **params)
|
||||
|
||||
image_width_px = image.size[0]
|
||||
image_height_px = image.size[1]
|
||||
aspect_ratio = image_width_px / image_height_px
|
||||
@@ -3856,23 +3893,17 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
self.y_length = 1.0
|
||||
|
||||
bpy.data.images.remove(image)
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
self.report({"ERROR"}, f"Failed to load image: {str(e)}")
|
||||
return {"CANCELLED"}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
if Path(tool.Ifc.get_path()).is_file():
|
||||
layout.prop(self, "use_relative_path")
|
||||
else:
|
||||
self.use_relative_path = False
|
||||
layout.prop(self, "show_texture_solid_mode")
|
||||
layout.prop(self, "x_length")
|
||||
layout.prop(self, "y_length")
|
||||
self.show_dimensions_dialog = True
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
return self._execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
project_props = tool.Project.get_project_props()
|
||||
project_props.load_indexed_maps = self.show_texture_solid_mode
|
||||
space = tool.Blender.get_view3d_space()
|
||||
if space.shading.color_type != "TEXTURE":
|
||||
space.shading.color_type = "TEXTURE"
|
||||
@@ -3885,65 +3916,121 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
image_filepath = Path(tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path))
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
image = load_image(abs_path.name, str(abs_path.parent), check_existing=False)
|
||||
if self.override_existing_image:
|
||||
params = {"check_existing": True, "force_reload": True}
|
||||
else:
|
||||
params = {"check_existing": False}
|
||||
image = load_image(abs_path.name, str(abs_path.parent), **params)
|
||||
|
||||
mesh = bpy.data.meshes.new(image_filepath.stem)
|
||||
obj = bpy.data.objects.new(image_filepath.stem, mesh)
|
||||
element = tool.Drawing.run_root_assign_class(
|
||||
obj=obj, ifc_class="IfcAnnotation", predefined_type="IMAGE", should_add_representation=False
|
||||
)
|
||||
def bm_add_image_plane(mesh):
|
||||
bm = tool.Blender.get_bmesh_for_mesh(mesh, clean=True)
|
||||
|
||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc_file)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
hx = self.x_length * 0.5 / unit_scale
|
||||
hy = self.y_length * 0.5 / unit_scale
|
||||
verts = [(-hx, -hy, 0.0), (hx, -hy, 0.0), (hx, hy, 0.0), (-hx, hy, 0.0)]
|
||||
item = builder.mesh(verts, [[0, 1, 2, 3]])
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
plane_scale = Vector((self.x_length * unit_scale / 2.0, self.y_length * unit_scale / 2.0, 1.0))
|
||||
matrix = Matrix.LocRotScale(None, None, plane_scale)
|
||||
bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=1, matrix=matrix, calc_uvs=False)
|
||||
|
||||
ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
representation = builder.get_representation(ifc_context, [item])
|
||||
ifcopenshell.api.geometry.assign_representation(ifc_file, element, representation)
|
||||
if not bm.loops.layers.uv:
|
||||
uv_layer = bm.loops.layers.uv.new()
|
||||
else:
|
||||
uv_layer = bm.loops.layers.uv.active
|
||||
|
||||
style = ifcopenshell.api.style.add_style(tool.Ifc.get(), name=image_filepath.stem)
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
ifc_file, shape_representation=representation, styles=[style]
|
||||
)
|
||||
min_x = min(v.co.x for v in bm.verts)
|
||||
max_x = max(v.co.x for v in bm.verts)
|
||||
min_y = min(v.co.y for v in bm.verts)
|
||||
max_y = max(v.co.y for v in bm.verts)
|
||||
|
||||
width = max_x - min_x
|
||||
height = max_y - min_y
|
||||
|
||||
for face in bm.faces:
|
||||
for loop in face.loops:
|
||||
vert = loop.vert
|
||||
u = (vert.co.x - min_x) / width if width > 0 else 0.5
|
||||
v = (vert.co.y - min_y) / height if height > 0 else 0.5
|
||||
|
||||
u = max(0.0, min(1.0, u))
|
||||
v = max(0.0, min(1.0, v))
|
||||
loop[uv_layer].uv = (u, v)
|
||||
|
||||
tool.Blender.apply_bmesh(mesh, bm)
|
||||
|
||||
if self.use_existing_object_by_name:
|
||||
obj = bpy.data.objects[self.use_existing_object_by_name]
|
||||
bm_add_image_plane(obj.data)
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="")
|
||||
else:
|
||||
temp_mesh = bpy.data.meshes.new("temp_mesh")
|
||||
bm_add_image_plane(temp_mesh)
|
||||
obj = bpy.data.objects.new(image_filepath.stem, temp_mesh)
|
||||
tool.Drawing.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="IMAGE",
|
||||
should_add_representation=True,
|
||||
context=ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW"),
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
tool.Blender.remove_data_block(temp_mesh)
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element and isinstance(obj.data, bpy.types.Mesh):
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if representation and representation.Items:
|
||||
item_id = representation.Items[0].id()
|
||||
num_faces = len(obj.data.polygons)
|
||||
obj.data["ios_item_ids"] = [item_id] * num_faces
|
||||
tool.Blender.Attribute.fill_attribute(obj.data, "ios_item_ids", "FACE", "INT", [item_id] * num_faces)
|
||||
|
||||
for item in representation.Items:
|
||||
if item.is_a("IfcPolygonalFaceSet") and item.Coordinates:
|
||||
new_coords = []
|
||||
for vertex in obj.data.vertices:
|
||||
co = obj.matrix_world @ vertex.co
|
||||
new_coords.append([co.x, co.y, co.z])
|
||||
item.Coordinates.CoordList = new_coords
|
||||
|
||||
tool.Blender.set_active_object(obj)
|
||||
|
||||
material = bpy.data.materials.new(name=image_filepath.stem)
|
||||
obj.data.materials.append(None) # new slot
|
||||
obj.material_slots[0].material = material
|
||||
bpy.ops.bim.add_style()
|
||||
|
||||
style = tool.Ifc.get_entity(material)
|
||||
assert style
|
||||
tool.Style.assign_style_to_object(style, obj)
|
||||
|
||||
# TODO: IfcSurfaceStyleRendering is unnecessary here, added it only because
|
||||
# we don't support IfcSurfaceStyleWithTextures without Rendering yet
|
||||
shading_attributes = {
|
||||
"SurfaceColour": {"Red": 1.0, "Green": 1.0, "Blue": 1.0},
|
||||
"SurfaceColour": {
|
||||
"Red": 1.0,
|
||||
"Green": 1.0,
|
||||
"Blue": 1.0,
|
||||
},
|
||||
"Transparency": 0.0,
|
||||
"ReflectanceMethod": "NOTDEFINED",
|
||||
}
|
||||
ifcopenshell.api.style.add_surface_style(
|
||||
tool.Ifc.get(), style=style, ifc_class="IfcSurfaceStyleRendering", attributes=shading_attributes
|
||||
tool.Ifc.get(),
|
||||
style=style,
|
||||
ifc_class="IfcSurfaceStyleRendering",
|
||||
attributes=shading_attributes,
|
||||
)
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
texture = ifc_file.create_entity(
|
||||
"IfcImageTexture",
|
||||
RepeatS=True,
|
||||
RepeatT=True,
|
||||
TextureType="TEXTURE",
|
||||
UrlReference=image_filepath.as_posix(),
|
||||
)
|
||||
else:
|
||||
texture = ifc_file.create_entity("IfcImageTexture", Mode="DIFFUSE", URLReference=image_filepath.as_posix())
|
||||
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=[texture], Mode="COORD")
|
||||
|
||||
texture = ifc_file.create_entity("IfcImageTexture", Mode="DIFFUSE", URLReference=image_filepath.as_posix())
|
||||
textures = [texture]
|
||||
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD") # UV map
|
||||
ifcopenshell.api.style.add_surface_style(
|
||||
ifc_file, style=style, ifc_class="IfcSurfaceStyleWithTextures", attributes={"Textures": textures}
|
||||
ifc_file,
|
||||
style=style,
|
||||
ifc_class="IfcSurfaceStyleWithTextures",
|
||||
attributes={"Textures": textures},
|
||||
)
|
||||
tool.Style.reload_material_from_ifc(material)
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = bonsai.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
|
||||
ifc_importer = bonsai.bim.import_ifc.IfcImporter(ifc_import_settings)
|
||||
ifc_importer.file = tool.Ifc.get()
|
||||
ifc_importer.create_style(style)
|
||||
|
||||
bonsai.core.geometry.switch_representation(tool.Ifc, tool.Geometry, obj=obj, representation=representation)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ConvertSVGToDXF(bpy.types.Operator):
|
||||
@@ -4175,7 +4262,10 @@ class SelectSimilarTextLiteralValue(bpy.types.Operator):
|
||||
should_select = True
|
||||
break
|
||||
elif self.attribute_type == "box_alignment":
|
||||
if literal.get_box_alignment() == self.literal_value:
|
||||
box_alignment_attr = next(
|
||||
(attr for attr in literal.attributes if attr.name == "BoxAlignment"), None
|
||||
)
|
||||
if box_alignment_attr and box_alignment_attr.string_value == self.literal_value:
|
||||
should_select = True
|
||||
break
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user