mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 16:01:36 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c9e4bae2f | |||
| 777dea3756 | |||
| 2cc583b88b |
@@ -1,24 +0,0 @@
|
||||
# We'll use defaults from the LLVM style with modifications
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: 0 # we do not enforce column lenght, please format responsibly
|
||||
IndentWidth: 4
|
||||
---
|
||||
Language: Cpp
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
IncludeBlocks: Regroup
|
||||
InsertBraces: true
|
||||
InsertNewlineAtEOF: true
|
||||
PackConstructorInitializers: CurrentLine
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false # keep licenze comment blocks
|
||||
SortIncludes: CaseInsensitive
|
||||
SpacesInLineCommentPrefix:
|
||||
Maximum: 1
|
||||
TabWidth: 4
|
||||
@@ -1,5 +0,0 @@
|
||||
Checks: 'bugprone-*,cert-*,clang-analyzer-*,readability-*'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: none
|
||||
@@ -1,10 +1,7 @@
|
||||
name: ci-ifcopenshell-docker
|
||||
name: CD
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- v0.**
|
||||
|
||||
|
||||
jobs:
|
||||
@@ -32,7 +29,7 @@ jobs:
|
||||
sudo apt-get install --no-install-recommends \
|
||||
git cmake gcc g++ libboost-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
|
||||
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
|
||||
libhdf5-dev libcgal-dev nlohmann-json3-dev libeigen3-dev
|
||||
libhdf5-dev libcgal-dev nlohmann-json3-dev
|
||||
|
||||
-
|
||||
name: ccache
|
||||
@@ -53,8 +50,8 @@ jobs:
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.10 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.10.so \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
-DCOLLADA_SUPPORT=Off \
|
||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES=/usr/lib/x86_64-linux-gnu/libxml2.so \
|
||||
@@ -66,7 +63,6 @@ jobs:
|
||||
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
-DEIGEN_DIR=/usr/include/eigen3 \
|
||||
../cmake
|
||||
make -j $(nproc)
|
||||
make install
|
||||
+14
-12
@@ -1,4 +1,4 @@
|
||||
name: ci-blenderbim-daily
|
||||
name: Publish-blenderbim-multiplatform
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -16,8 +16,12 @@ on:
|
||||
- 'src/ifc5d/ifc5d/**'
|
||||
- 'src/ifccityjson/**'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
- v0.7.0
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: blenderbim
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
@@ -59,23 +63,21 @@ jobs:
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=date::$(date +'%y%m%d')"
|
||||
- name: Compile
|
||||
run: |
|
||||
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
- name: Upload zip file to release
|
||||
- name: Upload Zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
release_name: "blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}} (unstable)"
|
||||
tag: "blenderbim-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blenderbim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: blenderbim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
tag: "blenderbim-${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
body: "Daily developer testing build blenderbim-${{steps.date.outputs.date}}"
|
||||
@@ -1,62 +0,0 @@
|
||||
name: ci-blenderbim
|
||||
|
||||
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
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py39, py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm1
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blenderbim-${{steps.version.outputs.version}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: blenderbim-${{steps.version.outputs.version}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
release_name: "blenderbim-${{steps.version.outputs.version}}"
|
||||
tag: "blenderbim-${{steps.version.outputs.version}}"
|
||||
overwrite: true
|
||||
@@ -1,23 +1,19 @@
|
||||
name: ci-ifcopenshell-conda-daily-v0.8.0
|
||||
name: ci-ifcopenshell-conda-daily
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- v0.8.0
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
# │ │ ┌───────────── day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# * * * * *
|
||||
- cron: "49 23 * * *" # 11min before utc midnight every day
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
test:
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}
|
||||
needs: activate
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}-${{ matrix.variant }}
|
||||
runs-on: ${{ matrix.platform.distver }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -30,45 +26,42 @@ jobs:
|
||||
{ name: py312, distver: '3.12'}
|
||||
]
|
||||
platform: [
|
||||
{ name: win, distver: windows-latest, pkg_dir: 'win-64' },
|
||||
{ name: linux, distver: ubuntu-latest, pkg_dir: 'linux-64' },
|
||||
{ name: macOS, distver: macos-latest, pkg_dir: 'osx-64' }
|
||||
{ name: Windows, distver: windows-2022, upload: 'true' },
|
||||
{ name: Linux, distver: ubuntu-22.04, upload: 'true' },
|
||||
{ name: macOS, distver: macos-12, upload: 'true' }
|
||||
]
|
||||
variant: [
|
||||
'novtk',
|
||||
# 'all'
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Download and extract MacOSX SDK
|
||||
- name: Download MacOSX SDK
|
||||
if: ${{ matrix.platform.name == 'macOS' }}
|
||||
run: |
|
||||
curl -o MacOSX10.13.sdk.tar.xz -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz && \
|
||||
tar xf MacOSX10.13.sdk.tar.xz && \
|
||||
sudo mv -v MacOSX10.13.sdk /opt/ && \
|
||||
ls /opt/
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
|
||||
- uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
environment-file: conda/environment.build.yml
|
||||
environment-name: build-env
|
||||
cache-environment: true
|
||||
condarc: |
|
||||
channels:
|
||||
- conda-forge
|
||||
channel_priority: strict
|
||||
create-args: >-
|
||||
python=${{ matrix.pyver.distver }}
|
||||
python=3.11
|
||||
anaconda-client
|
||||
boa
|
||||
|
||||
- name: create conda package dist dir
|
||||
- name: build, test and upload ifcopenshell
|
||||
if: ${{ matrix.platform.upload == 'true' }}
|
||||
run: |
|
||||
mkdir -p ${{ github.workspace }}/dist
|
||||
|
||||
- name: build & test ifcopenshell
|
||||
run: |
|
||||
boa build . --python ${{ matrix.pyver.distver }} --no-remove-work-dir --output-folder '${{ github.workspace }}/dist'
|
||||
conda mambabuild . --python ${{ matrix.pyver.distver }} -c conda-forge --variants "{variant: ${{ matrix.variant }}}" --token ${{ secrets.ANACONDA_TOKEN }} --user ifcopenshell
|
||||
working-directory: ./conda
|
||||
|
||||
- name: upload to anaconda
|
||||
if: ${{ matrix.platform.name == 'win' }}
|
||||
run: |
|
||||
anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell '${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2'
|
||||
|
||||
- name: upload to anaconda
|
||||
if: ${{ matrix.platform.name != 'win' }}
|
||||
run: |
|
||||
anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell ${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2
|
||||
+17
-5
@@ -1,7 +1,19 @@
|
||||
name: ci-ifcopenshell-python-pypi
|
||||
name: ci-ifcopenshell-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
# │ │ ┌───────────── day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# * * * * *
|
||||
- cron: "0 0 18 * *"
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-ifcopenshell-pypi.yml'
|
||||
|
||||
|
||||
env:
|
||||
major: 0
|
||||
@@ -28,19 +40,19 @@ jobs:
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win64,
|
||||
short_name: win,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux64
|
||||
short_name: linux
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos64
|
||||
short_name: macos
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm164
|
||||
short_name: macosm1
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -1,63 +0,0 @@
|
||||
name: ci-ifcopenshell-python
|
||||
|
||||
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
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py39, py310, py311, py312]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Linux Build",
|
||||
short_name: linux64
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos64
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm164
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cp -r src/ifcopenshell-python src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
cd src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||
make zip PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/ifcopenshell-python-${{ steps.version.outputs.version }}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: ifcopenshell-python-${{ steps.version.outputs.version }}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
||||
release_name: "ifcopenshell-python-${{steps.version.outputs.version}}"
|
||||
tag: "ifcopenshell-python-${{steps.version.outputs.version}}"
|
||||
overwrite: true
|
||||
+14
-12
@@ -1,13 +1,17 @@
|
||||
name: ci-ifcsverchok-daily
|
||||
name: Publish-ifcsverchok
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-ifcsverchok-build.yml'
|
||||
- 'src/ifcsverchok/*'
|
||||
branches:
|
||||
- v0.8.0
|
||||
- v0.7.0
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: ifcsverchok
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
@@ -30,22 +34,20 @@ jobs:
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=date::$(date +'%y%m%d')"
|
||||
- name: Compile
|
||||
run: |
|
||||
cd src/ifcsverchok
|
||||
make dist
|
||||
- name: Upload zip file to release
|
||||
- name: Upload Zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcsverchok/dist/ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}.zip
|
||||
asset_name: ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}.zip
|
||||
release_name: "ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
tag: "ifcsverchok-${{steps.version.outputs.version}}.${{steps.date.outputs.date}}"
|
||||
file: src/ifcsverchok/dist/ifcsverchok-${{steps.date.outputs.date}}.zip
|
||||
asset_name: ifcsverchok-${{steps.date.outputs.date}}.zip
|
||||
tag: "ifcsverchok-${{steps.date.outputs.date}}"
|
||||
overwrite: true
|
||||
body: "ifcsverchok build for ${{steps.date.outputs.date}}"
|
||||
@@ -1,42 +0,0 @@
|
||||
name: ci-ifcsverchok
|
||||
|
||||
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
|
||||
name: ifcsverchok
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.11'
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cd src/ifcsverchok
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcsverchok/dist/ifcsverchok-${{steps.version.outputs.version}}.zip
|
||||
asset_name: ifcsverchok-${{steps.version.outputs.version}}.zip
|
||||
release_name: "ifcsverchok-${{steps.version.outputs.version}}"
|
||||
tag: "ifcsverchok-${{steps.version.outputs.version}}"
|
||||
overwrite: true
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
swig libpcre3-dev libxml2-dev \
|
||||
libtbb-dev nlohmann-json3-dev \
|
||||
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
|
||||
libhdf5-dev libcgal-dev libeigen3-dev
|
||||
libhdf5-dev libcgal-dev
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
|
||||
-DCOLLADA_SUPPORT=Off \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3;4x3_add1;4x3_add2" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
-DCGAL_INCLUDE_DIR=/usr/include \
|
||||
@@ -99,7 +99,6 @@ jobs:
|
||||
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
|
||||
-DEIGEN_DIR=/usr/include/eigen3 \
|
||||
../cmake
|
||||
sudo make -j $(nproc)
|
||||
sudo make install
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
|
||||
path = src/ifcopenshell-python/test/Sample-BIM-Files
|
||||
url = https://github.com/IfcOpenShell/ids-test-files
|
||||
[submodule "src/ifcconvert/cityjson"]
|
||||
path = src/ifcconvert/cityjson
|
||||
url = https://github.com/IfcOpenShell/ifc-to-cityjson
|
||||
[submodule "docs/cpp-api/assets/doxygen-awesome-css"]
|
||||
path = docs/cpp-api/assets/doxygen-awesome-css
|
||||
url = https://github.com/jothepro/doxygen-awesome-css.git
|
||||
|
||||
+178
-242
@@ -17,33 +17,24 @@
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
endif()
|
||||
|
||||
project(IfcOpenShell VERSION 0.8.0)
|
||||
project(IfcOpenShell VERSION 0.7.0)
|
||||
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
cmake_policy(SET CMP0078 OLD)
|
||||
cmake_policy(SET CMP0086 NEW)
|
||||
if (POLICY CMP0144)
|
||||
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
|
||||
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
# Include utility macros and functions
|
||||
include(utilities.cmake)
|
||||
|
||||
# use extra version to make pre-release using eg semver
|
||||
if(NOT DEFINED EXTRA_VERSION)
|
||||
set(EXTRA_VERSION "-alpha.3")
|
||||
@@ -69,16 +60,12 @@ 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
|
||||
option(BUILD_PACKAGE "" OFF)
|
||||
|
||||
option(WITH_OPENCASCADE "Enable geometry interpretation using Open CASCADE" ON)
|
||||
option(WITH_CGAL "Enable geometry interpretation using CGAL" ON)
|
||||
option(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
|
||||
option(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
|
||||
option(HDF5_SUPPORT "Enable HDF5 support (requires HDF5, zlib)" ON)
|
||||
option(WITH_PROJ "Enable output of Earth-Centered Earth-Fixed glTF output using the PROJ library" OFF)
|
||||
option(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON)
|
||||
option(USD_SUPPORT "Build IfcConvert with USD support (requires pixar's USD library)." OFF)
|
||||
option(CITYJSON_SUPPORT "Build IfcConvert with CityJSON support (requires CityJSON library)." ON)
|
||||
option(WITH_RELATIONSHIP_VALIDATION "Build IfcConvert with option to validate geometrical relationships." OFF)
|
||||
|
||||
option(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
|
||||
option(ADD_COMMIT_SHA "Add commit sha and branch in version number, warning results in many rebuilds, requires git" OFF)
|
||||
@@ -87,7 +74,6 @@ if(MINIMAL_BUILD)
|
||||
message(STATUS "Setting options for minimal build")
|
||||
set(BUILD_GEOMSERVER OFF)
|
||||
set(BUILD_IFCPYTHON OFF)
|
||||
set(WITH_CGAL OFF)
|
||||
set(COLLADA_SUPPORT OFF)
|
||||
set(GLTF_SUPPORT OFF)
|
||||
set(HDF5_SUPPORT OFF)
|
||||
@@ -153,11 +139,17 @@ if(BUILD_SHARED_LIBS)
|
||||
set(IFCOPENSHELL_LIBRARY_DIR "${LIBDIR}")
|
||||
endif()
|
||||
|
||||
# Create cache entries if absent for environment variables
|
||||
macro(UNIFY_ENVVARS_AND_CACHE VAR)
|
||||
if((NOT DEFINED ${VAR}) AND(NOT "$ENV{${VAR}}" STREQUAL ""))
|
||||
set(${VAR} "$ENV{${VAR}}" CACHE STRING "${VAR}" FORCE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
UNIFY_ENVVARS_AND_CACHE(OCC_INCLUDE_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(OCC_LIBRARY_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(BOOST_ROOT)
|
||||
UNIFY_ENVVARS_AND_CACHE(BOOST_LIBRARYDIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(EIGEN_DIR)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
UNIFY_ENVVARS_AND_CACHE(OPENCOLLADA_INCLUDE_DIR)
|
||||
@@ -177,14 +169,6 @@ if(NOT MINIMAL_BUILD)
|
||||
UNIFY_ENVVARS_AND_CACHE(MPFR_LIBRARY_DIR)
|
||||
endif()
|
||||
|
||||
# Get a list of all OPTION flags from the CMakeLists.txt
|
||||
get_all_option_flags(option_flags)
|
||||
|
||||
# Loop through the list of OPTION flags and convert the corresponding environment variables
|
||||
foreach(option_flag IN LISTS option_flags)
|
||||
convert_env_var_to_bool("${option_flag}")
|
||||
endforeach()
|
||||
|
||||
if(WASM_BUILD)
|
||||
# when using the nix/build-all.py build script we should not
|
||||
# look into the sysroot for most of the dependencies but rather
|
||||
@@ -193,24 +177,7 @@ if(WASM_BUILD)
|
||||
set(CMAKE_FIND_ROOT_PATH "")
|
||||
endif()
|
||||
|
||||
if(WITH_CGAL)
|
||||
add_definitions(-DIFOPSH_WITH_CGAL)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_CGAL)
|
||||
|
||||
if(CITYJSON_SUPPORT)
|
||||
add_definitions(-DIFOPSH_WITH_CITYJSON)
|
||||
endif()
|
||||
|
||||
list(APPEND GEOMETRY_KERNELS cgal)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENCASCADE)
|
||||
add_definitions(-DIFOPSH_WITH_OPENCASCADE)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_OPENCASCADE)
|
||||
list(APPEND GEOMETRY_KERNELS opencascade)
|
||||
endif()
|
||||
|
||||
if(GLTF_SUPPORT OR CITYJSON_SUPPORT)
|
||||
if(GLTF_SUPPORT AND BUILD_CONVERT)
|
||||
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
|
||||
find_file(json_hpp "json.hpp" ${JSON_INCLUDE_DIR}/nlohmann)
|
||||
|
||||
@@ -224,7 +191,7 @@ if(GLTF_SUPPORT OR CITYJSON_SUPPORT)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
|
||||
endif()
|
||||
|
||||
# Add USD support to serializers
|
||||
# Add USD support to serializers
|
||||
if(USD_SUPPORT)
|
||||
UNIFY_ENVVARS_AND_CACHE(USD_INCLUDE_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(USD_LIBRARY_DIR)
|
||||
@@ -247,13 +214,13 @@ if(USD_SUPPORT)
|
||||
endif()
|
||||
|
||||
set(USD_LIBRARIES
|
||||
usd_usd
|
||||
usd_usd
|
||||
usd_usdGeom
|
||||
usd_usdShade
|
||||
usd_usdLux
|
||||
usd_vt
|
||||
usd_sdf
|
||||
usd_tf
|
||||
usd_usdShade
|
||||
usd_usdLux
|
||||
usd_vt
|
||||
usd_sdf
|
||||
usd_tf
|
||||
usd_gf
|
||||
)
|
||||
|
||||
@@ -269,7 +236,23 @@ if(USD_SUPPORT)
|
||||
|
||||
add_definitions(-DWITH_USD)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_USD)
|
||||
endif(USD_SUPPORT)
|
||||
endif()
|
||||
|
||||
# Set INSTALL_RPATH for target
|
||||
macro(SET_INSTALL_RPATHS _target _paths)
|
||||
set(${_target}_rpaths "")
|
||||
|
||||
foreach(_path ${_paths})
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_path}" isSystemDir)
|
||||
|
||||
if("${isSystemDir}" STREQUAL "-1")
|
||||
list(APPEND ${_target}_rpaths ${_path})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Set INSTALL_RPATH for ${_target}: ${${_target}_rpaths}")
|
||||
set_target_properties(${_target} PROPERTIES INSTALL_RPATH "${${_target}_rpaths}")
|
||||
endmacro()
|
||||
|
||||
# Find Boost: On win32 the (hardcoded) default is to use static libraries and
|
||||
# runtime, when doing running conda-build we pick what conda prepared for us.
|
||||
@@ -299,7 +282,7 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WASM_BUILD)
|
||||
if (WASM_BUILD)
|
||||
set(BOOST_COMPONENTS)
|
||||
else()
|
||||
# @todo review this, shouldn't this be all possible header-only now?
|
||||
@@ -332,13 +315,50 @@ if(IFCXML_SUPPORT)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_IFCXML)
|
||||
endif()
|
||||
|
||||
# Usage:
|
||||
# set(SOME_LIRARIES foo bar)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" d)
|
||||
# "foo bar" -> "optimized foo debug food optimized bar debug bard"
|
||||
# or
|
||||
# set(SOME_LIRARIES path/foo.lib)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" "d")
|
||||
# "path/foo.lib" -> "optimized path/foo.lib debug path/food.lib"
|
||||
# TODO Could be refined: take the library file extension as a parameter and
|
||||
# make sure the lib variable ends with not just contains it.
|
||||
function(add_debug_variants NAME LIBRARIES POSTFIX)
|
||||
set(LIBRARIES_STR "${LIBRARIES}")
|
||||
set(LIBRARIES "")
|
||||
|
||||
# the result, "optimized <lib> debug <lib>", needs to be a list instead of a string
|
||||
foreach(lib ${LIBRARIES_STR})
|
||||
list(APPEND LIBRARIES optimized)
|
||||
|
||||
if("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib})
|
||||
endif()
|
||||
|
||||
list(APPEND LIBRARIES debug)
|
||||
|
||||
if("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${NAME} ${LIBRARIES} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
if(MSVC)
|
||||
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
|
||||
endif()
|
||||
|
||||
# Open CASCADE
|
||||
if(WITH_OPENCASCADE)
|
||||
if("${OCC_INCLUDE_DIR}" STREQUAL "")
|
||||
find_path(OCC_INCLUDE_DIR Standard_Version.hxx
|
||||
PATHS
|
||||
@@ -370,7 +390,7 @@ if(BUILD_IFCGEOM)
|
||||
file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAINT
|
||||
REGEX "#define OCC_VERSION_MAINTENANCE.*"
|
||||
)
|
||||
string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT})
|
||||
string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT})
|
||||
set(OCC_VERSION_STRING "${OCC_MAJOR}.${OCC_MINOR}.${OCC_MAINT}")
|
||||
endif(OCC_INCLUDE_DIR)
|
||||
|
||||
@@ -381,7 +401,6 @@ if(BUILD_IFCGEOM)
|
||||
# @todo investigate the exact conditions when this is necessary
|
||||
TKBin
|
||||
)
|
||||
|
||||
if(OCC_VERSION_STRING VERSION_LESS 7.8.0)
|
||||
list(APPEND OPENCASCADE_LIBRARY_NAMES TKIGES TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP)
|
||||
else(OCC_VERSION_STRING VERSION_LESS 7.8.0)
|
||||
@@ -442,7 +461,7 @@ if(BUILD_IFCGEOM)
|
||||
|
||||
if(OCCT_STATIC)
|
||||
find_package(Threads)
|
||||
|
||||
|
||||
if(WASM_BUILD)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
else()
|
||||
@@ -454,15 +473,15 @@ if(BUILD_IFCGEOM)
|
||||
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
|
||||
if (NOT APPLE AND NOT WIN32)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
|
||||
endif()
|
||||
if(NOT WIN32)
|
||||
if (NOT WIN32)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "dl")
|
||||
endif()
|
||||
endif()
|
||||
endif(WITH_OPENCASCADE)
|
||||
|
||||
endif(BUILD_IFCGEOM)
|
||||
|
||||
if(COLLADA_SUPPORT)
|
||||
@@ -547,7 +566,7 @@ if(COLLADA_SUPPORT)
|
||||
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find OpenCOLLADA headers. "
|
||||
"Disable COLLADA_SUPPORT or fix OpenCOLLADA paths to proceed.")
|
||||
endif()
|
||||
endif(COLLADA_SUPPORT)
|
||||
endif()
|
||||
|
||||
if(HDF5_SUPPORT)
|
||||
if("${HDF5_INCLUDE_DIR}" STREQUAL "")
|
||||
@@ -592,23 +611,31 @@ if(HDF5_SUPPORT)
|
||||
|
||||
if(WIN32)
|
||||
# Windows
|
||||
set(zlib_post zlib)
|
||||
set(lib_ext lib)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# macOS
|
||||
set(zlib_post libz)
|
||||
set(lib_ext dylib)
|
||||
set(HDF5_LIBRARIES)
|
||||
find_package(HDF5 COMPONENTS CXX)
|
||||
if (HDF5_FOUND)
|
||||
set(HDF5_LIBRARIES ${HDF5_CXX_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND HDF5_LIBRARIES
|
||||
"${HDF5_LIBRARY_DIR}/zlib.lib"
|
||||
)
|
||||
else()
|
||||
# linux
|
||||
set(zlib_post libz)
|
||||
set(lib_ext so)
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# macOS
|
||||
set(zlib_post libz)
|
||||
set(lib_ext dylib)
|
||||
else()
|
||||
# linux
|
||||
set(zlib_post libz)
|
||||
set(lib_ext so)
|
||||
endif()
|
||||
|
||||
set(HDF5_LIBRARIES
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
|
||||
)
|
||||
set(HDF5_LIBRARIES
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
|
||||
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -625,7 +652,7 @@ if(HDF5_SUPPORT)
|
||||
|
||||
add_definitions(-DWITH_HDF5)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_HDF5)
|
||||
endif(HDF5_SUPPORT)
|
||||
endif()
|
||||
|
||||
if(WASM_BUILD)
|
||||
# reset to use sysroot
|
||||
@@ -656,17 +683,15 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
endif()
|
||||
else()
|
||||
# @tfk I commented this out as this kind of defeats the purpose of RelWithDebInfo. For the
|
||||
# best debugging experience simply use Debug. Note that in MSVC you can selectively toggle
|
||||
# optimization on a specific file if you're investigating a specific issue.
|
||||
# if(MSVC)
|
||||
# set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od")
|
||||
# endif()
|
||||
endif(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# warning due to virtual inheritance
|
||||
add_definitions(-wd4250)
|
||||
|
||||
# warning due to select definitions in the schema being redundant
|
||||
add_definitions(-wd4584)
|
||||
|
||||
@@ -702,22 +727,20 @@ if(MSVC)
|
||||
add_definitions(-permissive-)
|
||||
endif()
|
||||
|
||||
# Link against the static VC runtime
|
||||
# TODO Make this configurable
|
||||
# if("$ENV{CONDA_BUILD}" STREQUAL "")
|
||||
# foreach(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
# CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
# CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
|
||||
# if(${flag} MATCHES "/MD")
|
||||
# STRING(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
|
||||
# endif()
|
||||
# if(${flag} MATCHES "/MDd")
|
||||
# STRING(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
|
||||
# endif()
|
||||
# endforeach()
|
||||
# endif()
|
||||
|
||||
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
# Link against the static VC runtime
|
||||
# TODO Make this configurable
|
||||
# IF("$ENV{CONDA_BUILD}" STREQUAL "")
|
||||
# FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
# CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
# CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
|
||||
# IF(${flag} MATCHES "/MD")
|
||||
# STRING(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
|
||||
# ENDIF()
|
||||
# IF(${flag} MATCHES "/MDd")
|
||||
# STRING(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
|
||||
# ENDIF()
|
||||
# ENDFOREACH()
|
||||
# ENDIF()
|
||||
else()
|
||||
add_definitions(-Wall -Wextra)
|
||||
|
||||
@@ -736,19 +759,29 @@ else()
|
||||
if(UNIX)
|
||||
add_definitions(-fPIC)
|
||||
endif()
|
||||
endif(MSVC)
|
||||
endif()
|
||||
|
||||
include_directories(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR} ${HDF5_INCLUDE_DIR}
|
||||
${EIGEN_DIR} ${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${USD_INCLUDE_DIR}
|
||||
${USD_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
|
||||
set(${RESULT_NAME}
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endfunction()
|
||||
|
||||
if(NOT SCHEMA_VERSIONS)
|
||||
if(WASM_BUILD)
|
||||
# super arbitrarily try to keep size down at least a little bit
|
||||
set(SCHEMA_VERSIONS "2x3" "4")
|
||||
else()
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3" "4x3_tc1" "4x3_add1" "4x3_add2")
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4" "4x3" "4x3_tc1" "4x3_add1" "4x3_add2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -821,7 +854,7 @@ if(COMPILE_SCHEMA)
|
||||
list(REMOVE_ITEM IFC_RELEASE_NOT_USED "${schema}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif(COMPILE_SCHEMA)
|
||||
endif()
|
||||
|
||||
# Boost >= 1.58 requires BOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE to build on some Linux distros.
|
||||
if(NOT Boost_VERSION LESS 105800)
|
||||
@@ -829,37 +862,22 @@ if(NOT Boost_VERSION LESS 105800)
|
||||
endif()
|
||||
|
||||
set(IFCOPENSHELL_LIBRARIES IfcParse)
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
if (BUILD_IFCGEOM)
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} geometry_mapping_ifc${schema})
|
||||
set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom_ifc${schema})
|
||||
endforeach()
|
||||
if(WASM_BUILD)
|
||||
if (WASM_BUILD)
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
|
||||
else()
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
if (BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${schema})
|
||||
endforeach()
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
|
||||
|
||||
if(WITH_OPENCASCADE)
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
set(GEOM_SERIALIZER_SCHEMA_LIBRARIES ${GEOM_SERIALIZER_SCHEMA_LIBRARIES} GeometrySerializers_ifc${schema})
|
||||
|
||||
add_library(geometry_serializer_ifc${schema} STATIC ../src/ifcgeom/Serialization/schema/Serialization.cpp)
|
||||
set_target_properties(geometry_serializer_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
|
||||
list(APPEND geometry_serializer_libraries geometry_serializer_ifc${schema})
|
||||
endforeach()
|
||||
|
||||
add_library(geometry_serializer STATIC ../src/ifcgeom/Serialization/Serialization.cpp)
|
||||
target_link_libraries(geometry_serializer ${geometry_serializer_libraries})
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} geometry_serializer ${geometry_serializer_libraries})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# IfcParse
|
||||
@@ -898,70 +916,29 @@ set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
|
||||
add_library(IfcParse ${IFCPARSE_FILES})
|
||||
set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
|
||||
|
||||
if(WASM_BUILD)
|
||||
if (WASM_BUILD)
|
||||
target_link_libraries(IfcParse ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
if(WITH_CGAL)
|
||||
find_library(libGMP NAMES gmp mpir PATHS ${GMP_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
find_library(libMPFR NAMES mpfr PATHS ${MPFR_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
if(NOT libGMP)
|
||||
message(FATAL_ERROR "Unable to find GMP library files, aborting")
|
||||
endif()
|
||||
if(NOT libMPFR)
|
||||
message(FATAL_ERROR "Unable to find MPFR library files, aborting")
|
||||
endif()
|
||||
|
||||
list(APPEND CGAL_LIBRARIES "${libMPFR}")
|
||||
list(APPEND CGAL_LIBRARIES "${libGMP}")
|
||||
endif()
|
||||
|
||||
foreach(kernel ${GEOMETRY_KERNELS})
|
||||
string(TOUPPER ${kernel} KERNEL_UPPER)
|
||||
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h)
|
||||
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/kernels/${kernel}/*.cpp)
|
||||
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
|
||||
|
||||
add_library(geometry_kernel_${kernel} ${IFCGEOM_FILES})
|
||||
set_property(TARGET geometry_kernel_${kernel} APPEND PROPERTY COMPILE_FLAGS "-DIFC_GEOM_EXPORTS")
|
||||
# needed?
|
||||
# if(NOT WASM_BUILD)
|
||||
# endif()
|
||||
target_link_libraries(geometry_kernel_${kernel} ${${KERNEL_UPPER}_LIBRARIES})
|
||||
list(APPEND kernel_libraries geometry_kernel_${kernel})
|
||||
|
||||
if(${kernel} STREQUAL "cgal")
|
||||
set_property(TARGET geometry_kernel_${kernel} APPEND_STRING PROPERTY COMPILE_FLAGS " -DCGAL_HAS_THREADS")
|
||||
|
||||
add_library(geometry_kernel_${kernel}_simple ${IFCGEOM_FILES})
|
||||
set_target_properties(geometry_kernel_${kernel}_simple PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIFOPSH_SIMPLE_KERNEL -DCGAL_HAS_THREADS")
|
||||
# needed?
|
||||
# if(NOT WASM_BUILD)
|
||||
# endif()
|
||||
target_link_libraries(geometry_kernel_${kernel}_simple ${${KERNEL_UPPER}_LIBRARIES})
|
||||
list(APPEND kernel_libraries geometry_kernel_${kernel}_simple)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# IfcGeom
|
||||
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h ../src/ifcgeom/*.i)
|
||||
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/*.cpp)
|
||||
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
|
||||
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
file(GLOB IFCGEOM_I_FILES ../src/ifcgeom/mapping/*.i)
|
||||
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/mapping/*.h)
|
||||
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/mapping/*.cpp)
|
||||
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES} ${IFCGEOM_I_FILES})
|
||||
|
||||
add_library(geometry_mapping_ifc${schema} STATIC ${IFCGEOM_FILES})
|
||||
set_target_properties(geometry_mapping_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
|
||||
target_link_libraries(geometry_mapping_ifc${schema} IfcParse)
|
||||
list(APPEND mapping_libraries geometry_mapping_ifc${schema})
|
||||
add_library(IfcGeom_ifc${schema} STATIC ${IFCGEOM_FILES})
|
||||
set_target_properties(IfcGeom_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
|
||||
if (NOT WASM_BUILD)
|
||||
target_link_libraries(IfcGeom_ifc${schema} IfcParse ${OPENCASCADE_LIBRARIES})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# IfcGeom (schema agnostic)
|
||||
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/*.h)
|
||||
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/*.cpp)
|
||||
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom_schema_agnostic/*.h)
|
||||
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom_schema_agnostic/*.cpp)
|
||||
set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES})
|
||||
|
||||
add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES})
|
||||
@@ -971,10 +948,10 @@ if(BUILD_IFCGEOM)
|
||||
find_package(Threads)
|
||||
endif()
|
||||
|
||||
if(WASM_BUILD)
|
||||
target_link_libraries(IfcGeom ${kernel_libraries} ${mapping_libraries} ${CMAKE_THREAD_LIBS_INIT})
|
||||
if (WASM_BUILD)
|
||||
target_link_libraries(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
else()
|
||||
target_link_libraries(IfcGeom IfcParse ${kernel_libraries} ${mapping_libraries} ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
endif(BUILD_IFCGEOM)
|
||||
@@ -991,8 +968,8 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
add_library(Serializers_ifc${schema} STATIC ${SERIALIZERS_S_FILES})
|
||||
set_target_properties(Serializers_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
|
||||
|
||||
if(WASM_BUILD)
|
||||
|
||||
if (WASM_BUILD)
|
||||
target_link_libraries(Serializers_ifc${schema} ${HDF5_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(Serializers_ifc${schema} IfcGeom ${OPENCASCADE_LIBRARIES} ${HDF5_LIBRARIES})
|
||||
@@ -1002,61 +979,27 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
add_library(Serializers ${SERIALIZERS_FILES})
|
||||
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS" VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
|
||||
|
||||
if(WITH_PROJ)
|
||||
target_compile_definitions(Serializers PRIVATE "WITH_PROJ")
|
||||
if (PROJ_STATIC)
|
||||
target_compile_definitions(Serializers PRIVATE "PROJ_DLL=")
|
||||
endif()
|
||||
target_include_directories(Serializers PRIVATE ${PROJ_INCLUDE_DIR} ${SQLITE_INCLUDE_DIR})
|
||||
target_link_libraries(Serializers ${PROJ_LIBRARIES})
|
||||
endif()
|
||||
if (WITH_PROJ)
|
||||
target_compile_definitions(Serializers PRIVATE "WITH_PROJ")
|
||||
if (PROJ_STATIC)
|
||||
target_compile_definitions(Serializers PRIVATE "PROJ_DLL=")
|
||||
endif()
|
||||
target_include_directories(Serializers PRIVATE ${PROJ_INCLUDE_DIR} ${SQLITE_INCLUDE_DIR})
|
||||
target_link_libraries(Serializers ${PROJ_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(Serializers ${SERIALIZER_SCHEMA_LIBRARIES} ${OPENCOLLADA_LIBRARIES} ${USD_LIBRARIES})
|
||||
|
||||
endif(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
|
||||
if(BUILD_CONVERT)
|
||||
if(WITH_CGAL AND CITYJSON_SUPPORT)
|
||||
message(STATUS "Building CityJSON support")
|
||||
set(CITYJSON_CONVERT_FILES
|
||||
../src/ifcconvert/cityjson/geobim.cpp
|
||||
../src/ifcconvert/cityjson/global_execution_context.cpp
|
||||
../src/ifcconvert/cityjson/opening_collector.cpp
|
||||
../src/ifcconvert/cityjson/processing.cpp
|
||||
../src/ifcconvert/cityjson/radius_comparison.cpp
|
||||
../src/ifcconvert/cityjson/radius_execution_context.cpp
|
||||
../src/ifcconvert/cityjson/settings.cpp
|
||||
../src/ifcconvert/cityjson/writer.cpp
|
||||
)
|
||||
add_library(cityjson_converter ${CITYJSON_CONVERT_FILES})
|
||||
target_include_directories(cityjson_converter PRIVATE ../src)
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} cityjson_converter)
|
||||
|
||||
add_executable(cityjson_converter_exe ${CITYJSON_CONVERT_FILES})
|
||||
set_target_properties(cityjson_converter_exe PROPERTIES COMPILE_FLAGS "-DCITYJSON_EXECUTABLE")
|
||||
target_include_directories(cityjson_converter_exe PRIVATE ../src)
|
||||
target_link_libraries(cityjson_converter_exe ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${HDF5_LIBRARIES} ${USD_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# IfcConvert
|
||||
if (WITH_RELATIONSHIP_VALIDATION)
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
|
||||
else()
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/IfcConvert.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES)
|
||||
endif()
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
|
||||
set(IFCCONVERT_FILES ${IFCCONVERT_CPP_FILES} ${IFCCONVERT_H_FILES})
|
||||
add_executable(IfcConvert ${IFCCONVERT_FILES})
|
||||
|
||||
target_link_libraries(IfcConvert ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${HDF5_LIBRARIES} ${USD_LIBRARIES})
|
||||
if (WITH_RELATIONSHIP_VALIDATION)
|
||||
set_property(TARGET IfcConvert APPEND_STRING PROPERTY COMPILE_FLAGS " -DWITH_RELATIONSHIP_VALIDATION")
|
||||
endif()
|
||||
|
||||
if(WITH_CGAL AND CITYJSON_SUPPORT)
|
||||
set_property(TARGET IfcConvert APPEND_STRING PROPERTY COMPILE_FLAGS " -DIFOPSH_WITH_CITYJSON")
|
||||
endif()
|
||||
|
||||
if((NOT WIN32) AND BUILD_SHARED_LIBS)
|
||||
# Only set RPATHs when building shared libraries (i.e. IfcParse and
|
||||
@@ -1089,7 +1032,7 @@ if(BUILD_GEOMSERVER)
|
||||
LIBRARY DESTINATION ${LIBDIR}
|
||||
RUNTIME DESTINATION ${BINDIR}
|
||||
)
|
||||
endif(BUILD_GEOMSERVER)
|
||||
endif()
|
||||
|
||||
if(ADD_COMMIT_SHA)
|
||||
find_package(Git)
|
||||
@@ -1106,7 +1049,7 @@ if(ADD_COMMIT_SHA)
|
||||
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
|
||||
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
|
||||
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
|
||||
if(NOT git_branch_candidate_2 MATCHES "^HEAD$")
|
||||
if (NOT git_branch_candidate_2 MATCHES "^HEAD$")
|
||||
string(REPLACE "/" ";" git_branch_candidate_2_list "${git_branch_candidate_2}")
|
||||
list(GET git_branch_candidate_2_list -1 git_branch)
|
||||
endif()
|
||||
@@ -1122,13 +1065,6 @@ if(ADD_COMMIT_SHA)
|
||||
add_definitions(-DIFCOPENSHELL_BRANCH=${git_branch})
|
||||
add_definitions(-DIFCOPENSHELL_COMMIT=${git_sha})
|
||||
endif()
|
||||
endif(ADD_COMMIT_SHA)
|
||||
|
||||
if(MSVC)
|
||||
# @todo still needs to be understood better, but the cgal and cgal-simple kernel cause multiply defined boost lambda placeholders _1 ... _3
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||
endif()
|
||||
|
||||
# Documentation
|
||||
@@ -1149,8 +1085,8 @@ if(BUILD_IFCMAX)
|
||||
add_subdirectory(../src/ifcmax ifcmax)
|
||||
endif()
|
||||
|
||||
if(WITH_CGAL)
|
||||
add_subdirectory(../src/svgfill svgfill)
|
||||
if(NOT MINIMAL_BUILD)
|
||||
add_subdirectory(../src/svgfill svgfill)
|
||||
endif()
|
||||
|
||||
if(BUILD_QTVIEWER)
|
||||
@@ -1182,7 +1118,7 @@ if(BUILD_IFCGEOM)
|
||||
LIBRARY DESTINATION ${LIBDIR}
|
||||
RUNTIME DESTINATION ${BINDIR}
|
||||
)
|
||||
endif(BUILD_IFCGEOM)
|
||||
endif()
|
||||
|
||||
if(BUILD_CONVERT)
|
||||
install(TARGETS Serializers ${SERIALIZER_SCHEMA_LIBRARIES}
|
||||
@@ -1198,7 +1134,7 @@ if(BUILD_CONVERT)
|
||||
install(FILES ${SERIALIZERS_S_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/serializers/schema_dependent
|
||||
)
|
||||
endif(BUILD_CONVERT)
|
||||
endif()
|
||||
|
||||
# Cmake uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
@@ -1246,6 +1182,6 @@ set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}")
|
||||
set(CPACK_DEBIAN_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/cmake/debian/postinst")
|
||||
|
||||
# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/cmake/debian/postinst")
|
||||
include(CPack)
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
# Create a cache entry if absent for environment variables
|
||||
macro(UNIFY_ENVVARS_AND_CACHE VAR)
|
||||
if((NOT DEFINED ${VAR}) AND(NOT "$ENV{${VAR}}" STREQUAL ""))
|
||||
set(${VAR} "$ENV{${VAR}}" CACHE STRING "${VAR}" FORCE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Set INSTALL_RPATH for target with given paths
|
||||
macro(SET_INSTALL_RPATHS _target _paths)
|
||||
set(${_target}_rpaths "")
|
||||
|
||||
foreach(_path ${_paths})
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_path}" isSystemDir)
|
||||
|
||||
if("${isSystemDir}" STREQUAL "-1")
|
||||
list(APPEND ${_target}_rpaths ${_path})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Set INSTALL_RPATH for ${_target}: ${${_target}_rpaths}")
|
||||
set_target_properties(${_target} PROPERTIES INSTALL_RPATH "${${_target}_rpaths}")
|
||||
endmacro()
|
||||
|
||||
# Get a list of all OPTION flags from the CMakeLists.txt and store in an output LIST
|
||||
function(get_all_option_flags output_list)
|
||||
# Read the contents of the CMakeLists.txt
|
||||
file(READ "${CMAKE_SOURCE_DIR}/CMakeLists.txt" cmake_contents)
|
||||
|
||||
# Find all OPTION flags using a regular expression
|
||||
string(REGEX MATCHALL "[oO][pP][tT][iI][oO][nN]\\s*\\(\\s*([A-Za-z0-9_]+)" matches "${cmake_contents}")
|
||||
|
||||
# Extract the variable names from the matches
|
||||
set(option_flags)
|
||||
foreach(match IN LISTS matches)
|
||||
string(REGEX REPLACE "[oO][pP][tT][iI][oO][nN]\\s*\\(\\s*([A-Za-z0-9_]+)" "\\1" option_flag "${match}")
|
||||
list(APPEND option_flags "${option_flag}")
|
||||
endforeach()
|
||||
|
||||
# Return the list of OPTION flags
|
||||
set(${output_list} "${option_flags}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Loop through a LIST of OPTION flags and convert to corresponding environment variables
|
||||
function(convert_env_var_to_bool var_name)
|
||||
if(DEFINED ENV{${var_name}})
|
||||
string(TOUPPER "$ENV{${var_name}}" bool_value)
|
||||
if(bool_value STREQUAL "ON" OR bool_value STREQUAL "TRUE" OR bool_value STREQUAL "1")
|
||||
set(${var_name} ON CACHE BOOL "${var_name} as boolean" FORCE)
|
||||
elseif(bool_value STREQUAL "OFF" OR bool_value STREQUAL "FALSE" OR bool_value STREQUAL "0")
|
||||
set(${var_name} OFF CACHE BOOL "${var_name} as boolean" FORCE)
|
||||
else()
|
||||
# Not a bool, leave it as a string
|
||||
endif()
|
||||
else()
|
||||
# Not defined, leave it as a string
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Usage:
|
||||
# set(SOME_LIRARIES foo bar)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" d)
|
||||
# "foo bar" -> "optimized foo debug food optimized bar debug bard"
|
||||
# or
|
||||
# set(SOME_LIRARIES path/foo.lib)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" "d")
|
||||
# "path/foo.lib" -> "optimized path/foo.lib debug path/food.lib"
|
||||
# TODO Could be refined: take the library file extension as a parameter and
|
||||
# make sure the lib variable ends with not just contains it.
|
||||
function(add_debug_variants NAME LIBRARIES POSTFIX)
|
||||
set(LIBRARIES_STR "${LIBRARIES}")
|
||||
set(LIBRARIES "")
|
||||
|
||||
# the result, "optimized <lib> debug <lib>", needs to be a list instead of a string
|
||||
foreach(lib ${LIBRARIES_STR})
|
||||
list(APPEND LIBRARIES optimized)
|
||||
|
||||
if("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib})
|
||||
endif()
|
||||
|
||||
list(APPEND LIBRARIES debug)
|
||||
|
||||
if("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${NAME} ${LIBRARIES} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
|
||||
set(${RESULT_NAME}
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endfunction()
|
||||
@@ -1 +0,0 @@
|
||||
.env
|
||||
+3
-7
@@ -1,7 +1,7 @@
|
||||
mkdir build && cd build
|
||||
|
||||
set MY_PY_VER=%PY_VER:.=%
|
||||
rem set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
cmake -G "Ninja" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" ^
|
||||
@@ -12,9 +12,6 @@ cmake -G "Ninja" ^
|
||||
-D OCC_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include\opencascade" ^
|
||||
-D OCC_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
-D CGAL_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D EIGEN_DIR:FILEPATH="%LIBRARY_PREFIX%\include\eigen3" ^
|
||||
-D LIBXML2_INCLUDE_DIR=%LIBRARY_PREFIX%/include/libxml2 ^
|
||||
-D LIBXML2_LIBRARIES=%LIBRARY_PREFIX%/lib/libxml2.lib ^
|
||||
-D GMP_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D GMP_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
-D MPFR_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
@@ -35,10 +32,9 @@ cmake -G "Ninja" ^
|
||||
-D BUILD_CONVERT:BOOL=ON ^
|
||||
-D BUILD_IFCMAX:BOOL=OFF ^
|
||||
-D IFCXML_SUPPORT:BOOL=ON ^
|
||||
-D Boost_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
-D Boost_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D Boost_LIBRARYDIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
-D Boost_INCLUDEDIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D Boost_USE_STATIC_LIBS:BOOL=OFF ^
|
||||
-D CITYJSON_SUPPORT:BOOL=OFF ^
|
||||
%SRC_DIR%/cmake
|
||||
|
||||
if errorlevel 1 exit 1
|
||||
|
||||
+9
-15
@@ -1,18 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# IF osx use file lib suffix .dylib
|
||||
# IF linux use file lib suffix .so
|
||||
# IF windows use file lib suffix .dll
|
||||
declare -a CMAKE_PLATFORM_FLAGS
|
||||
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
export FSUFFIX=dylib
|
||||
export CFLAGS="$CFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export CXXFLAGS="$CXXFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export LDFLAGS="$LDFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
||||
export FSUFFIX=so
|
||||
if [[ ${HOST} =~ .*linux.* ]]; then
|
||||
CMAKE_PLATFORM_FLAGS+=(-DCMAKE_TOOLCHAIN_FILE=$RECIPE_DIR/cross-linux.cmake)
|
||||
fi
|
||||
|
||||
if [ `uname` == Darwin ]; then
|
||||
export CFLAGS="$CFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export CXXFLAGS="$CXXFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
export LDFLAGS="$LDFLAGS -Wl,-flat_namespace,-undefined,suppress"
|
||||
fi
|
||||
|
||||
cmake -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" \
|
||||
@@ -31,10 +29,7 @@ cmake -G Ninja \
|
||||
-DHDF5_LIBRARY_DIR=$PREFIX/lib \
|
||||
-DJSON_INCLUDE_DIR=$PREFIX/include \
|
||||
-DCGAL_INCLUDE_DIR=$PREFIX/include \
|
||||
-DLIBXML2_INCLUDE_DIR=$PREFIX/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES=$PREFIX/lib/libxml2.$FSUFFIX \
|
||||
-DEIGEN_DIR:FILEPATH=$PREFIX/include/eigen3 \
|
||||
-DCOLLADA_SUPPORT:BOOL=OFF \
|
||||
-DCOLLADA_SUPPORT=0 \
|
||||
-DBUILD_EXAMPLES:BOOL=OFF \
|
||||
-DIFCXML_SUPPORT:BOOL=ON \
|
||||
-DGLTF_SUPPORT:BOOL=ON \
|
||||
@@ -43,7 +38,6 @@ cmake -G Ninja \
|
||||
-DBUILD_IFCGEOM:BOOL=ON \
|
||||
-DBUILD_GEOMSERVER:BOOL=OFF \
|
||||
-DBOOST_USE_STATIC_LIBS:BOOL=OFF \
|
||||
-DCITYJSON_SUPPORT:BOOL=OFF \
|
||||
./cmake
|
||||
|
||||
ninja
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
CONDA_BUILD_SYSROOT: # used for github actions conda daily build
|
||||
- /Users/runner/work/MacOSX10.13.sdk # [osx]
|
||||
CONDA_BUILD_SYSROOT:
|
||||
- /opt/MacOSX10.13.sdk # [osx]
|
||||
|
||||
occt:
|
||||
- 7.7.1
|
||||
|
||||
c_compiler:
|
||||
- vs2022 # [win]
|
||||
|
||||
cxx_compiler:
|
||||
- vs2022 # [win]
|
||||
variant:
|
||||
- novtk
|
||||
- all
|
||||
@@ -0,0 +1,16 @@
|
||||
# this one is important
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_PLATFORM Linux)
|
||||
|
||||
# specify the cross compiler
|
||||
set(CMAKE_C_COMPILER $ENV{CC})
|
||||
set(CMAKE_CXX_COMPILER $ENV{CXX})
|
||||
|
||||
# where is the target environment
|
||||
set(CMAKE_FIND_ROOT_PATH $ENV{PREFIX} $ENV{BUILD_PREFIX}/$ENV{HOST}/sysroot)
|
||||
|
||||
# search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
# for libraries and headers in the target directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
@@ -1,58 +0,0 @@
|
||||
@echo off
|
||||
|
||||
:: This is a batch file to set the environment variables for the project
|
||||
:: It is not necessary for conda compilation, but it provides you with type hints when working with the c++ libraries
|
||||
:: OpenCascade, CGAL, Eigen, etc distributed using conda-forge in your IDE.
|
||||
::
|
||||
:: mamba env update -f environment.build.yml --prune
|
||||
:: mamba activate ifcopenshell-build
|
||||
::
|
||||
:: Note!
|
||||
:: You have to add a .env file next to this env.bat file where you set PREFIX=<path to your conda env>
|
||||
set CONDA_BUILD=1
|
||||
set MY_PY_VER=311
|
||||
|
||||
:: set this file's parent directory as a variable
|
||||
set THIS_DIR=%~dp0
|
||||
|
||||
:: read the .env file located in THIS_DIR and set the environment variables.
|
||||
:: the .env file should contain a line like this:
|
||||
:: PREFIX=C:\Users\your_user_name\mambaforge3\envs\ifcopenshell-build
|
||||
for /f "tokens=*" %%i in (%THIS_DIR%.env) do set %%i
|
||||
|
||||
set LIBRARY_PREFIX=%PREFIX%/Library
|
||||
set CMAKE_PREFIX_PATH=%PREFIX%;%LIBRARY_PREFIX%/include;%LIBRARY_PREFIX%/lib;%LIBRARY_PREFIX%/bin
|
||||
|
||||
set OCC_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set OCC_INCLUDE_DIR=%LIBRARY_PREFIX%/include/opencascade
|
||||
|
||||
set CGAL_DIR=%LIBRARY_PREFIX%/lib/cmake/CGAL
|
||||
set CGAL_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set EIGEN_DIR=%LIBRARY_PREFIX%/include/eigen3
|
||||
set LIBXML2_INCLUDE_DIR=%LIBRARY_PREFIX%/include/libxml2
|
||||
set LIBXML2_LIBRARIES=%LIBRARY_PREFIX%/lib/libxml2.lib
|
||||
set Boost_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set Boost_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set Boost_USE_STATIC_LIBS=OFF
|
||||
|
||||
set GMP_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set GMP_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set MPFR_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
|
||||
set HDF5_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
set HDF5_LIBRARY_DIR=%LIBRARY_PREFIX%/lib
|
||||
set JSON_INCLUDE_DIR=%LIBRARY_PREFIX%/include
|
||||
|
||||
set HDF5_SUPPORT=ON
|
||||
set BUILD_IFCPYTHON=ON
|
||||
set BUILD_IFCGEOM=ON
|
||||
set COLLADA_SUPPORT=OFF
|
||||
set BUILD_EXAMPLES=OFF
|
||||
set BUILD_GEOMSERVER=OFF
|
||||
set GLTF_SUPPORT=ON
|
||||
set BUILD_CONVERT=ON
|
||||
set BUILD_IFCMAX=OFF
|
||||
set IFCXML_SUPPORT=ON
|
||||
|
||||
set PYTHON_EXECUTABLE=%PREFIX%/python.exe
|
||||
set PYTHON_LIBRARY=%PREFIX%/libs/python%MY_PY_VER%.lib
|
||||
@@ -1,17 +0,0 @@
|
||||
name: ifcopenshell-build
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- boa
|
||||
- boost-cpp
|
||||
- occt
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- mpfr
|
||||
- nlohmann_json
|
||||
- swig
|
||||
- zlib
|
||||
# These needs to be installed per platform
|
||||
#- sel(unix): gmp
|
||||
#- sel(win): mpir
|
||||
@@ -1,21 +1,27 @@
|
||||
context:
|
||||
name: ifcopenshell
|
||||
version: 0.8.0alpha1
|
||||
{% set name = "ifcopenshell" %}
|
||||
{% set version = "0.7.0" %}
|
||||
{% set build = 1 %}
|
||||
|
||||
# Higher number -> Always prioritize "novtk" variant over "all" variant
|
||||
{% set build = build + 200 %} # [variant == "novtk"]
|
||||
{% set build = build + 100 %} # [variant == "all"]
|
||||
|
||||
package:
|
||||
name: '{{ name|lower }}'
|
||||
version: '{{ version }}'
|
||||
name: {{ name }}
|
||||
version: {{ version }}
|
||||
|
||||
source:
|
||||
path: ..
|
||||
|
||||
build:
|
||||
number: 1
|
||||
string: py{{ CONDA_PY }}_{{ variant }}_h{{ PKG_HASH }}_{{ build }}
|
||||
binary_relocation: false [osx]
|
||||
number: {{ build }}
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- "{{ compiler('c') }}"
|
||||
- "{{ compiler('cxx') }}"
|
||||
- {{ compiler('c') }}
|
||||
- {{ compiler('cxx') }}
|
||||
- ninja >=1.10.2
|
||||
- cmake
|
||||
- swig 4.1.1
|
||||
@@ -23,28 +29,26 @@ requirements:
|
||||
host:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- occt 7.7.2 *{{ variant }}*
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- eigen
|
||||
- mpfr
|
||||
- sel(unix): gmp
|
||||
- sel(win): mpir
|
||||
- gmp # [unix]
|
||||
- mpir # [win]
|
||||
- nlohmann_json
|
||||
- zlib
|
||||
|
||||
run:
|
||||
- python
|
||||
- {{ pin_compatible('occt', max_pin='x.x.x') }}
|
||||
- occt 7.7.2 *{{ variant }}*
|
||||
- {{ pin_compatible('cgal-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('boost-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('hdf5', max_pin='x.x.x') }}
|
||||
- libxml2
|
||||
- hdf5
|
||||
- eigen
|
||||
- mpfr
|
||||
- sel(unix): gmp
|
||||
- sel(win): mpir
|
||||
- gmp # [unix]
|
||||
- mpir # [win]
|
||||
- nlohmann_json
|
||||
- zlib
|
||||
|
||||
@@ -60,5 +64,5 @@ about:
|
||||
description: |
|
||||
IfcOpenShell is an open source (LGPL) software library for
|
||||
working with the Industry Foundation Classes (IFC) file format.
|
||||
doc_url: https://blenderbim.org/docs-python/
|
||||
doc_url: https://ifcopenshell.org/
|
||||
dev_url: https://github.com/IfcOpenShell/IfcOpenShell
|
||||
+5
-74
@@ -51,7 +51,6 @@
|
||||
###############################################################################
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess as sp
|
||||
import shutil
|
||||
@@ -80,7 +79,7 @@ ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
|
||||
PYTHON_VERSIONS = ["3.9.11", "3.10.3", "3.11.8", "3.12.1"]
|
||||
JSON_VERSION = "v3.6.1"
|
||||
OCE_VERSION = "0.18.3"
|
||||
OCCT_VERSION = "7.7.1"
|
||||
OCCT_VERSION = "7.5.3"
|
||||
BOOST_VERSION = "1.80.0"
|
||||
PCRE_VERSION = "8.41"
|
||||
LIBXML2_VERSION = "2.9.11"
|
||||
@@ -91,8 +90,6 @@ HDF5_VERSION = "1.12.1"
|
||||
GMP_VERSION = "6.2.1"
|
||||
MPFR_VERSION = "3.1.6" # latest is 4.1.0
|
||||
CGAL_VERSION = "5.3"
|
||||
USD_VERSION = "23.05"
|
||||
TBB_VERSION = "2021.9.0"
|
||||
|
||||
# binaries
|
||||
cp = "cp"
|
||||
@@ -189,7 +186,7 @@ cecho(""" - How many compiler processes may be run in parallel.
|
||||
|
||||
dependency_tree = {
|
||||
'IfcParse': ('boost', 'libxml2', 'hdf5'),
|
||||
'IfcGeom': ('IfcParse', 'occ', 'json', 'cgal', 'eigen'),
|
||||
'IfcGeom': ('IfcParse', 'occ', 'json', 'cgal'),
|
||||
'IfcConvert': ('IfcGeom',),
|
||||
'OpenCOLLADA': ('libxml2', 'pcre'),
|
||||
'IfcGeomServer': ('IfcGeom',),
|
||||
@@ -203,9 +200,7 @@ dependency_tree = {
|
||||
'json': (),
|
||||
'hdf5': (),
|
||||
'cgal': (),
|
||||
'eigen': (),
|
||||
'freetype': (),
|
||||
# 'usd': ('boost', 'oneTBB')
|
||||
}
|
||||
|
||||
def v(dep):
|
||||
@@ -472,8 +467,6 @@ if "wasm" in flags:
|
||||
# If the linker supports GC sections, set it up to reduce binary file size
|
||||
# -fPIC is required for the shared libraries to work
|
||||
|
||||
compiler_flags = "CFLAGS", "CXXFLAGS", "LDFLAGS"
|
||||
|
||||
CXXFLAGS = os.environ.get("CXXFLAGS", "")
|
||||
CFLAGS = os.environ.get("CFLAGS", "")
|
||||
LDFLAGS = os.environ.get("LDFLAGS", "")
|
||||
@@ -499,11 +492,7 @@ else:
|
||||
CXXFLAGS=CXXFLAGS_MINIMAL
|
||||
CFLAGS=CFLAGS_MINIMAL
|
||||
LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}"
|
||||
|
||||
if "lto" in flags:
|
||||
for f in compiler_flags:
|
||||
locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}"
|
||||
|
||||
|
||||
os.environ["CXXFLAGS"] = CXXFLAGS
|
||||
os.environ["CFLAGS"] = CFLAGS
|
||||
os.environ["LDFLAGS"] = LDFLAGS
|
||||
@@ -513,11 +502,6 @@ os.environ["LDFLAGS"] = LDFLAGS
|
||||
# build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
|
||||
|
||||
if 'hdf5' in targets:
|
||||
# not supported
|
||||
orig = [os.environ[f] for f in compiler_flags]
|
||||
for f in compiler_flags:
|
||||
os.environ[f] = re.sub("-flto(=\w+)?", "", os.environ[f])
|
||||
|
||||
HDF5_MAJOR = ".".join(HDF5_VERSION.split(".")[:-1])
|
||||
build_dependency(
|
||||
name=f"hdf5-{HDF5_VERSION}",
|
||||
@@ -528,10 +512,6 @@ if 'hdf5' in targets:
|
||||
ctest_result=f"HDF5-{HDF5_VERSION}-{platform.system()}",
|
||||
ctest_result_path=f"HDF_Group/HDF5/{HDF5_VERSION}"
|
||||
)
|
||||
|
||||
for f, o in zip(compiler_flags, orig):
|
||||
os.environ[f] = o
|
||||
|
||||
|
||||
if "json" in targets:
|
||||
json_url = f"https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp"
|
||||
@@ -540,9 +520,6 @@ if "json" in targets:
|
||||
os.makedirs(os.path.dirname(json_install_path))
|
||||
if not os.path.exists(json_install_path):
|
||||
urlretrieve(json_url, json_install_path)
|
||||
|
||||
if "eigen" in targets:
|
||||
git_clone_or_pull_repository("https://gitlab.com/libeigen/eigen.git", f"{DEPS_DIR}/install/eigen-3.3.9", revision="3.3.9")
|
||||
|
||||
if "pcre" in targets:
|
||||
build_dependency(
|
||||
@@ -582,9 +559,6 @@ if USE_OCCT and "occ" in targets:
|
||||
patches = []
|
||||
if OCCT_VERSION < "7.4":
|
||||
patches.append("./patches/occt/enable-exception-handling.patch")
|
||||
|
||||
if OCCT_VERSION == "7.7.1":
|
||||
patches.append("./patches/occt/no_ExpToCasExe.patch")
|
||||
|
||||
if "wasm" in flags:
|
||||
patches.append("./patches/occt/no_em_js.patch")
|
||||
@@ -599,7 +573,7 @@ if USE_OCCT and "occ" in targets:
|
||||
"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off",
|
||||
f"-D3RDPARTY_FREETYPE_DIR={DEPS_DIR}/install/freetype"
|
||||
],
|
||||
download_url = "https://git.dev.opencascade.org/repos/occt.git",
|
||||
download_url = "https://github.com/Open-Cascade-SAS/OCCT",
|
||||
download_name = "occt",
|
||||
download_tool=download_tool_git,
|
||||
patch=patches,
|
||||
@@ -773,42 +747,7 @@ if "cgal" in targets:
|
||||
download_tool=download_tool_git,
|
||||
revision=f"v{CGAL_VERSION}"
|
||||
)
|
||||
|
||||
if "usd" in targets:
|
||||
build_dependency(
|
||||
name=f"oneTBB-{TBB_VERSION}",
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/tbb-{TBB_VERSION}",
|
||||
f"-DTBB_TEST=OFF"
|
||||
],
|
||||
download_url="https://github.com/oneapi-src/oneTBB",
|
||||
download_name="oneTBB",
|
||||
download_tool=download_tool_git,
|
||||
revision=f"v{TBB_VERSION}"
|
||||
)
|
||||
|
||||
build_dependency(
|
||||
name=f"usd-{USD_VERSION}",
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/usd-{USD_VERSION}",
|
||||
f"-DBOOST_ROOT={DEPS_DIR}/install/boost-{BOOST_VERSION}",
|
||||
f"-DTBB_ROOT_DIR={DEPS_DIR}/install/tbb-{TBB_VERSION}",
|
||||
f"-DPXR_ENABLE_PYTHON_SUPPORT=FALSE",
|
||||
f"-DPXR_ENABLE_GL_SUPPORT=FALSE",
|
||||
f"-DPXR_BUILD_IMAGING=FALSE",
|
||||
f"-DPXR_BUILD_TUTORIALS=FALSE",
|
||||
f"-DPXR_BUILD_EXAMPLES=FALSE",
|
||||
f"-DPXR_BUILD_USD_TOOLS=FALSE",
|
||||
f"-DPXR_BUILD_TESTS=FALSE"
|
||||
],
|
||||
download_url="https://github.com/PixarAnimationStudios/USD",
|
||||
download_name="USD",
|
||||
download_tool=download_tool_git,
|
||||
revision=f"v{USD_VERSION}"
|
||||
)
|
||||
|
||||
|
||||
cecho("Building IfcOpenShell:", GREEN)
|
||||
|
||||
IFCOS_DIR = os.path.join(DEPS_DIR, "build", "ifcopenshell")
|
||||
@@ -828,7 +767,6 @@ cmake_args = [
|
||||
"-DBOOST_ROOT=" f"{DEPS_DIR}/install/boost-{BOOST_VERSION}",
|
||||
"-DGLTF_SUPPORT=" "ON",
|
||||
"-DJSON_INCLUDE_DIR=" f"{DEPS_DIR}/install/json",
|
||||
"-DEIGEN_DIR=" f"{DEPS_DIR}/install/eigen-3.3.9",
|
||||
"-DBoost_NO_BOOST_CMAKE=" "On",
|
||||
"-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off")
|
||||
]
|
||||
@@ -892,13 +830,6 @@ if "hdf5" in targets:
|
||||
else:
|
||||
cmake_args.append("-DHDF5_SUPPORT=Off")
|
||||
|
||||
if "usd" in targets:
|
||||
cmake_args.extend([
|
||||
"-DUSD_SUPPORT=" "On",
|
||||
"-DUSD_INCLUDE_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/include",
|
||||
"-DUSD_LIBRARY_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/lib"
|
||||
])
|
||||
|
||||
if not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets):
|
||||
logger.info("\rConfiguring executables...")
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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")
|
||||
+31
-42
@@ -16,8 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
PYTHON:=python3.11
|
||||
PIP:=pip3.11
|
||||
PYTHON:=python3.10
|
||||
PIP:=pip3.10
|
||||
PATCH:=patch
|
||||
SED:=sed -i
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@@ -35,12 +35,7 @@ SED:=sed -i '' -e
|
||||
endif
|
||||
endif
|
||||
|
||||
IS_STABLE:=FALSE
|
||||
VERSION:=$(shell cat ../../VERSION)
|
||||
VERSION_MAJOR:=$(shell cat '../../VERSION' | cut -d '.' -f 1)
|
||||
VERSION_MINOR:=$(shell cat '../../VERSION' | cut -d '.' -f 2)
|
||||
VERSION_PATCH:=$(shell cat '../../VERSION' | cut -d '.' -f 3)
|
||||
VERSION_DATE:=$(shell date '+%y%m%d')
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
PYVERSION:=py310
|
||||
PYPI_IMP:=cp
|
||||
@@ -100,54 +95,54 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.8.0-90ae709-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-c7830e9-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
# See bug #812
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/blenderbim/occ_utils.py
|
||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.7.0/src/blenderbim/occ_utils.py
|
||||
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides dependencies that are part of IfcOpenShell
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/refs/heads/v0.8.0.zip
|
||||
cd dist/working && unzip v0.8.0.zip
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.7.0.zip
|
||||
cd dist/working && unzip v0.7.0.zip
|
||||
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
|
||||
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/util/
|
||||
rm -rf dist/blenderbim/libs/site/packages/ifcopenshell/api/
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/util dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/*.py dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.8.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/util dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/*.py dist/blenderbim/libs/site/packages/ifcopenshell/
|
||||
cp dist/working/IfcOpenShell-0.7.0/src/ifcopenshell-python/ifcopenshell/express/rule_executor.py dist/blenderbim/libs/site/packages/ifcopenshell/express/
|
||||
# Provides bcf functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCClash functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/
|
||||
# Provides BIMTester functionality
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pybabel
|
||||
cd dist/working && . env/bin/activate && $(PIP) install babel
|
||||
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.8.0/src/ifcbimtester/bimtester/locale/
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
cd dist/working && . env/bin/activate && ./env/bin/pybabel compile -d ./IfcOpenShell-0.7.0/src/ifcbimtester/bimtester/locale/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCTester functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifctester/ifctester dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifctester/ifctester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCFM functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
|
||||
# Provides bSDD functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/bsdd/* dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/bsdd/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCDiff functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCSV functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCPatch functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
|
||||
# Provides IFC4D functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifc4d/ifc4d dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifc4d/ifc4d dist/blenderbim/libs/site/packages/
|
||||
# Provides IFC5D functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifc5d/ifc5d dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCityJSON functionality
|
||||
cp -r dist/working/IfcOpenShell-0.8.0/src/ifccityjson dist/blenderbim/libs/site/packages/
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccityjson dist/blenderbim/libs/site/packages/
|
||||
# Provides IfcGit functionality
|
||||
cd dist/working && . env/bin/activate && $(PIP) install GitPython --target=./site-packages
|
||||
cp -r dist/working/site-packages/smmap dist/blenderbim/libs/site/packages/
|
||||
@@ -222,12 +217,12 @@ endif
|
||||
cd dist/working && tar -xzvf behave*
|
||||
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
|
||||
# See bug #1294
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/model_core.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/runner_util.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.7.0/src/ifcbimtester/patch/model_core.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.7.0/src/ifcbimtester/patch/runner_util.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/model_core.py < model_core.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
|
||||
# See bug #2159
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.8.0/src/ifcbimtester/patch/junit.patch
|
||||
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.7.0/src/ifcbimtester/patch/junit.patch
|
||||
cd dist/working/ && $(PATCH) ../blenderbim/libs/site/packages/behave/reporter/junit.py < junit.patch
|
||||
rm -rf dist/working
|
||||
|
||||
@@ -262,7 +257,7 @@ endif
|
||||
# Required by brickschema
|
||||
# This is a bit of a dodgy one, it should be provided by setuptools which Blender doesn't ship.
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/raw/v0.8.0/src/blenderbim/scripts/pkg_resources.tar.gz
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/raw/v0.7.0/src/blenderbim/scripts/pkg_resources.tar.gz
|
||||
cd dist/working && tar -xzvf pkg_resources*
|
||||
cd dist/working/ && cp -r pkg_resources ../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
@@ -286,16 +281,10 @@ endif
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy.libs
|
||||
|
||||
ifeq ($(IS_STABLE), TRUE)
|
||||
cd dist/blenderbim && $(SED) "s/0, 0, 0/$(VERSION_MAJOR), $(VERSION_MINOR), $(VERSION_PATCH)/" __init__.py
|
||||
cd dist/blenderbim && $(SED) "s/0.0.0/$(VERSION)/" blender_manifest.toml
|
||||
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||
else
|
||||
cd dist/blenderbim && $(SED) "s/0, 0, 0/$(VERSION_MAJOR), $(VERSION_MINOR), $(VERSION_PATCH), $(VERSION_DATE)/" __init__.py
|
||||
cd dist/blenderbim && $(SED) "s/0.0.0/$(VERSION).$(VERSION_DATE)/" blender_manifest.toml
|
||||
cd dist/blenderbim && $(SED) "s/999999/$(VERSION)/" __init__.py
|
||||
cd dist/blenderbim && $(SED) "s/999999/$(VERSION)/" blender_manifest.toml
|
||||
cd dist/blenderbim/bim && $(SED) "s/8888888/$(LAST_COMMIT_HASH)/" __init__.py
|
||||
cd dist && zip -r blenderbim-$(VERSION).$(VERSION_DATE)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||
endif
|
||||
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||
rm -rf dist/blenderbim
|
||||
|
||||
.PHONY: test
|
||||
|
||||
@@ -29,6 +29,7 @@ if IN_BLENDER:
|
||||
import bpy
|
||||
import platform
|
||||
import traceback
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import addon_utils
|
||||
from collections import deque
|
||||
@@ -38,7 +39,7 @@ bl_info = {
|
||||
"description": "Transforms Blender into a native Building Information Model authoring platform using IFC.",
|
||||
"author": "IfcOpenShell Contributors",
|
||||
"blender": (3, 1, 0),
|
||||
"version": (0, 0, 0),
|
||||
"version": (0, 0, 999999),
|
||||
"location": "File Menu, Scene Properties Tab. See documentation for more.",
|
||||
"doc_url": "https://docs.blenderbim.org/",
|
||||
"tracker_url": "https://github.com/IfcOpenShell/IfcOpenShell/issues",
|
||||
|
||||
@@ -99,6 +99,7 @@ class IfcExporter:
|
||||
continue
|
||||
if obj.library:
|
||||
continue
|
||||
tool.Collector.sync(obj, skip_unlinking)
|
||||
result = self.sync_object_placement(obj)
|
||||
if result:
|
||||
results.append(result)
|
||||
|
||||
@@ -100,6 +100,7 @@ class IfcStore:
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
|
||||
cache_settings = ifcopenshell.geom.settings()
|
||||
cache_settings.set(cache_settings.STRICT_TOLERANCE, True)
|
||||
try:
|
||||
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
|
||||
except:
|
||||
@@ -156,7 +157,7 @@ class IfcStore:
|
||||
bpy.context.scene.BIMProjectProperties.export_schema
|
||||
)
|
||||
elif IfcStore.schema is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema_identifier)
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
|
||||
return IfcStore.schema
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -218,6 +218,7 @@ class IfcImporter:
|
||||
self.gross_elements: set[ifcopenshell.entity_instance] = set()
|
||||
self.element_types: set[ifcopenshell.entity_instance] = set()
|
||||
self.spatial_elements: set[ifcopenshell.entity_instance] = set()
|
||||
self.type_collection: bpy.types.Collection = None
|
||||
self.type_products = {}
|
||||
self.meshes = {}
|
||||
self.mesh_shapes = {}
|
||||
@@ -231,19 +232,19 @@ class IfcImporter:
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
|
||||
def profile_code(self, message: str) -> None:
|
||||
def profile_code(self, message):
|
||||
if not self.time:
|
||||
self.time = time.time()
|
||||
print("{} :: {:.2f}".format(message, time.time() - self.time))
|
||||
self.time = time.time()
|
||||
self.update_progress(self.progress + 1)
|
||||
|
||||
def update_progress(self, progress: float) -> None:
|
||||
def update_progress(self, progress):
|
||||
if progress <= 100:
|
||||
self.progress = progress
|
||||
bpy.context.window_manager.progress_update(self.progress)
|
||||
|
||||
def execute(self) -> None:
|
||||
def execute(self):
|
||||
bpy.context.window_manager.progress_begin(0, 100)
|
||||
self.profile_code("Starting import process")
|
||||
self.load_file()
|
||||
@@ -262,6 +263,8 @@ class IfcImporter:
|
||||
self.profile_code("Create project")
|
||||
self.process_element_filter()
|
||||
self.profile_code("Process element filter")
|
||||
self.create_collections()
|
||||
self.profile_code("Create collections")
|
||||
self.create_materials()
|
||||
self.profile_code("Create materials")
|
||||
self.create_styles()
|
||||
@@ -299,9 +302,7 @@ class IfcImporter:
|
||||
self.setup_viewport_camera()
|
||||
self.setup_arrays()
|
||||
self.profile_code("Setup arrays")
|
||||
tool.Spatial.run_spatial_import_spatial_decomposition()
|
||||
if default_container := tool.Spatial.guess_default_container():
|
||||
tool.Spatial.set_default_container(default_container)
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
@@ -323,7 +324,7 @@ class IfcImporter:
|
||||
coords = getattr(point, "Coordinates", point)
|
||||
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
|
||||
|
||||
def process_context_filter(self) -> None:
|
||||
def process_context_filter(self):
|
||||
# Annotation ContextType is to accommodate broken Revit files
|
||||
# See https://github.com/Autodesk/revit-ifc/issues/187
|
||||
type_priority = ["Model", "Plan", "Annotation"]
|
||||
@@ -395,21 +396,23 @@ class IfcImporter:
|
||||
|
||||
for context in self.contexts:
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("mesher-linear-deflection", self.ifc_import_settings.deflection_tolerance)
|
||||
settings.set("mesher-angular-deflection", self.ifc_import_settings.angular_tolerance)
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set("context-ids", [context.id()])
|
||||
settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
settings.set_context_ids([context.id()])
|
||||
self.context_settings.append(settings)
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("mesher-linear-deflection", self.ifc_import_settings.deflection_tolerance)
|
||||
settings.set("mesher-angular-deflection", self.ifc_import_settings.angular_tolerance)
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set("disable-opening-subtractions", True)
|
||||
settings.set("context-ids", [context.id()])
|
||||
settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
settings.set_context_ids([context.id()])
|
||||
self.gross_context_settings.append(settings)
|
||||
|
||||
def process_element_filter(self) -> None:
|
||||
def process_element_filter(self):
|
||||
offset = self.ifc_import_settings.element_offset
|
||||
offset_limit = offset + self.ifc_import_settings.element_limit
|
||||
|
||||
@@ -476,7 +479,7 @@ class IfcImporter:
|
||||
break
|
||||
return results
|
||||
|
||||
def parse_native_elements(self) -> None:
|
||||
def parse_native_elements(self):
|
||||
if not self.ifc_import_settings.should_load_geometry:
|
||||
return
|
||||
for element in self.elements:
|
||||
@@ -484,13 +487,13 @@ class IfcImporter:
|
||||
self.native_elements.add(element)
|
||||
self.elements -= self.native_elements
|
||||
|
||||
def is_native(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
def is_native(self, element):
|
||||
if (
|
||||
not element.Representation
|
||||
or not element.Representation.Representations
|
||||
or getattr(element, "HasOpenings", None)
|
||||
):
|
||||
return False
|
||||
return
|
||||
|
||||
representation = None
|
||||
representation_priority = None
|
||||
@@ -505,7 +508,7 @@ class IfcImporter:
|
||||
context = rep.ContextOfItems
|
||||
|
||||
if not representation:
|
||||
return False
|
||||
return
|
||||
|
||||
matrix = np.eye(4)
|
||||
representation_id = None
|
||||
@@ -563,11 +566,8 @@ class IfcImporter:
|
||||
"type": "IfcFaceBasedSurfaceModel",
|
||||
}
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_native_swept_disk_solid(
|
||||
self, element: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
|
||||
) -> bool:
|
||||
def is_native_swept_disk_solid(self, element, representation):
|
||||
items = [i["item"] for i in ifcopenshell.util.representation.resolve_items(representation)]
|
||||
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
|
||||
if tool.Blender.Modifier.is_railing(element):
|
||||
@@ -583,20 +583,20 @@ class IfcImporter:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_native_faceted_brep(self, representation: ifcopenshell.entity_instance) -> bool:
|
||||
def is_native_faceted_brep(self, representation):
|
||||
# TODO handle mapped items
|
||||
for i in representation.Items:
|
||||
if i.is_a() != "IfcFacetedBrep":
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_native_face_based_surface_model(self, representation: ifcopenshell.entity_instance) -> bool:
|
||||
def is_native_face_based_surface_model(self, representation):
|
||||
for i in representation.Items:
|
||||
if i.is_a() != "IfcFaceBasedSurfaceModel":
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_products_from_shape_representation(self, element: ifcopenshell.entity_instance) -> None:
|
||||
def get_products_from_shape_representation(self, element):
|
||||
products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation]
|
||||
for rep_map in element.RepresentationMap:
|
||||
for usage in rep_map.MapUsage:
|
||||
@@ -605,7 +605,7 @@ class IfcImporter:
|
||||
products.extend(self.get_products_from_shape_representation(inverse_element))
|
||||
return products
|
||||
|
||||
def predict_dense_mesh(self) -> None:
|
||||
def predict_dense_mesh(self):
|
||||
if self.ifc_import_settings.should_use_native_meshes:
|
||||
return
|
||||
|
||||
@@ -632,7 +632,7 @@ class IfcImporter:
|
||||
if faces and max(faces) > threshold:
|
||||
self.ifc_import_settings.should_use_native_meshes = True
|
||||
|
||||
def calculate_model_offset(self) -> None:
|
||||
def calculate_model_offset(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
return
|
||||
@@ -650,14 +650,14 @@ class IfcImporter:
|
||||
return self.guess_false_origin_and_project_north(building)
|
||||
return self.guess_false_origin()
|
||||
|
||||
def set_manual_blender_offset(self) -> None:
|
||||
def set_manual_blender_offset(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props.blender_eastings = str(self.ifc_import_settings.false_origin[0])
|
||||
props.blender_northings = str(self.ifc_import_settings.false_origin[1])
|
||||
props.blender_orthogonal_height = str(self.ifc_import_settings.false_origin[2])
|
||||
props.has_blender_offset = True
|
||||
|
||||
def guess_false_origin_and_project_north(self, element: ifcopenshell.entity_instance) -> None:
|
||||
def guess_false_origin_and_project_north(self, element):
|
||||
if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"):
|
||||
return
|
||||
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
@@ -672,7 +672,7 @@ class IfcImporter:
|
||||
props.blender_x_axis_ordinate = str(placement[1][0])
|
||||
props.has_blender_offset = True
|
||||
|
||||
def guess_false_origin(self) -> None:
|
||||
def guess_false_origin(self):
|
||||
# Civil BIM applications like to work in absolute coordinates, where the
|
||||
# ObjectPlacement is usually 0,0,0 (but not always, so we'll need to
|
||||
# check for the actual transformation) but each individual coordinate of
|
||||
@@ -689,7 +689,7 @@ class IfcImporter:
|
||||
def get_offset_point(self) -> Union[npt.NDArray[np.float64], None]:
|
||||
elements_checked = 0
|
||||
# If more than these elements aren't far away, the file probably isn't absolutely positioned
|
||||
element_checking_threshold = 3
|
||||
element_checking_threshold = 10
|
||||
for element in self.file.by_type("IfcElement"):
|
||||
if not element.Representation:
|
||||
continue
|
||||
@@ -705,7 +705,10 @@ class IfcImporter:
|
||||
shape = self.create_generic_shape(element)
|
||||
if not shape:
|
||||
continue
|
||||
mat = ifcopenshell.util.shape.get_shape_matrix(shape)
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
point = mat @ np.array(
|
||||
(
|
||||
shape.geometry.verts[0],
|
||||
@@ -740,36 +743,18 @@ class IfcImporter:
|
||||
def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
if (
|
||||
not obj.data
|
||||
and tool.Cad.is_x(matrix[0][3], 0)
|
||||
and tool.Cad.is_x(matrix[1][3], 0)
|
||||
and tool.Cad.is_x(matrix[2][3], 0)
|
||||
):
|
||||
# We assume any non-geometric matrix at 0,0,0 is not
|
||||
# positionally significant and is left alone. This handles
|
||||
# scenarios where often spatial elements are left at 0,0,0 and
|
||||
# everything else is at map coordinates.
|
||||
obj.BIMObjectProperties.blender_offset_type = "NOT_APPLICABLE"
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
elif obj.data and obj.data.get("has_cartesian_point_offset", None):
|
||||
if obj.data and obj.data.get("has_cartesian_point_offset", None):
|
||||
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
|
||||
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
|
||||
obj.BIMObjectProperties.cartesian_point_offset = cartesian_point_offset
|
||||
offset_x, offset_y, offset_z = map(float, cartesian_point_offset.split(","))
|
||||
matrix[0][3] += offset_x
|
||||
matrix[1][3] += offset_y
|
||||
matrix[2][3] += offset_z
|
||||
else:
|
||||
elif self.is_point_far_away((matrix[:3, 3])):
|
||||
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
matrix,
|
||||
float(props.blender_eastings) * self.unit_scale,
|
||||
float(props.blender_northings) * self.unit_scale,
|
||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
matrix,
|
||||
float(props.blender_eastings) * self.unit_scale,
|
||||
float(props.blender_northings) * self.unit_scale,
|
||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
|
||||
@@ -798,16 +783,22 @@ class IfcImporter:
|
||||
if grid.Representation:
|
||||
shape = self.create_generic_shape(grid)
|
||||
grid_obj = self.create_product(grid, shape)
|
||||
grid_placement = self.get_element_matrix(grid)
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.lock_grids_on_import:
|
||||
grid_obj.lock_location = (True, True, True)
|
||||
grid_obj.lock_rotation = (True, True, True)
|
||||
self.create_grid_axes(grid.UAxes, grid_obj, grid_placement)
|
||||
self.create_grid_axes(grid.VAxes, grid_obj, grid_placement)
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(grid))
|
||||
u_axes = bpy.data.collections.new("UAxes")
|
||||
collection.children.link(u_axes)
|
||||
v_axes = bpy.data.collections.new("VAxes")
|
||||
collection.children.link(v_axes)
|
||||
self.create_grid_axes(grid.UAxes, u_axes, grid_obj)
|
||||
self.create_grid_axes(grid.VAxes, v_axes, grid_obj)
|
||||
if grid.WAxes:
|
||||
self.create_grid_axes(grid.WAxes, grid_obj)
|
||||
w_axes = bpy.data.collections.new("WAxes")
|
||||
collection.children.link(w_axes)
|
||||
self.create_grid_axes(grid.WAxes, w_axes, grid_obj)
|
||||
|
||||
def create_grid_axes(self, axes, grid_obj, grid_placement):
|
||||
def create_grid_axes(self, axes, grid_collection, grid_obj):
|
||||
for axis in axes:
|
||||
shape = self.create_generic_shape(axis.AxisCurve)
|
||||
mesh = self.create_mesh(axis, shape)
|
||||
@@ -816,7 +807,8 @@ class IfcImporter:
|
||||
obj.lock_location = (True, True, True)
|
||||
obj.lock_rotation = (True, True, True)
|
||||
self.link_element(axis, obj)
|
||||
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, grid_placement.copy()))
|
||||
self.set_matrix_world(obj, grid_obj.matrix_world)
|
||||
grid_collection.objects.link(obj)
|
||||
|
||||
def create_element_types(self):
|
||||
for element_type in self.element_types:
|
||||
@@ -896,7 +888,7 @@ class IfcImporter:
|
||||
|
||||
def create_generic_shape(
|
||||
self, element: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.geom.ShapeElementType, None]:
|
||||
) -> Union[ifcopenshell_wrapper.TriangulationElement, None]:
|
||||
for settings in self.context_settings:
|
||||
try:
|
||||
result = ifcopenshell.geom.create_shape(settings, element)
|
||||
@@ -1038,6 +1030,14 @@ class IfcImporter:
|
||||
self.profile_code("Merging by material")
|
||||
|
||||
def create_structural_items(self):
|
||||
# Create structural collections
|
||||
self.structural_member_collection = bpy.data.collections.new("Members")
|
||||
self.structural_connection_collection = bpy.data.collections.new("Connections")
|
||||
self.structural_collection = bpy.data.collections.new("StructuralItems")
|
||||
self.structural_collection.children.link(self.structural_member_collection)
|
||||
self.structural_collection.children.link(self.structural_connection_collection)
|
||||
self.project["blender"].children.link(self.structural_collection)
|
||||
|
||||
self.create_generic_elements(set(self.file.by_type("IfcStructuralCurveMember")))
|
||||
self.create_generic_elements(set(self.file.by_type("IfcStructuralCurveConnection")))
|
||||
self.create_generic_elements(set(self.file.by_type("IfcStructuralSurfaceMember")))
|
||||
@@ -1171,8 +1171,11 @@ class IfcImporter:
|
||||
self.link_element(element, obj)
|
||||
|
||||
if shape:
|
||||
m = shape.transformation.matrix.data
|
||||
# We use numpy here because Blender mathutils.Matrix is not accurate enough
|
||||
mat = np.array(shape.transformation.matrix).reshape((4, 4), order="F")
|
||||
mat = np.array(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, mat))
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
elif mesh:
|
||||
@@ -1198,6 +1201,7 @@ class IfcImporter:
|
||||
styles.extend(style.Styles)
|
||||
|
||||
def create_native_faceted_brep(self, element, mesh_name, native_data):
|
||||
# TODO: georeferencing?
|
||||
# co [x y z x y z x y z ...]
|
||||
# vertex_index [i i i i i ...]
|
||||
# loop_start [0 3 6 9 ...] (for tris)
|
||||
@@ -1222,27 +1226,45 @@ class IfcImporter:
|
||||
mesh = bpy.data.meshes.new("Native")
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
mat = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
if props.has_blender_offset and self.is_point_far_away(self.mesh_data["co"][0:3], is_meters=False):
|
||||
verts_array = np.array(self.mesh_data["co"])
|
||||
verts_array *= self.unit_scale
|
||||
offset_x, offset_y, offset_z = verts_array[0:3]
|
||||
offset = np.array([-offset_x, -offset_y, -offset_z])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
|
||||
if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8):
|
||||
verts = offset_verts.tolist()
|
||||
else:
|
||||
verts = self.apply_matrix_to_flat_coords(offset_verts, native_data["matrix"])
|
||||
|
||||
offset_point = np.linalg.inv(mat) @ np.array(
|
||||
(
|
||||
float(props.blender_eastings),
|
||||
float(props.blender_northings),
|
||||
float(props.blender_orthogonal_height),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
verts = [None] * len(self.mesh_data["co"])
|
||||
for i in range(0, len(self.mesh_data["co"]), 3):
|
||||
verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector(
|
||||
(
|
||||
*ifcopenshell.util.geolocation.enh2xyz(
|
||||
self.mesh_data["co"][i] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 1] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 2] * self.unit_scale,
|
||||
offset_point[0] * self.unit_scale,
|
||||
offset_point[1] * self.unit_scale,
|
||||
offset_point[2] * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
),
|
||||
1,
|
||||
)
|
||||
)
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = f"{offset_x},{offset_y},{offset_z}"
|
||||
else:
|
||||
verts_array = np.array(self.mesh_data["co"])
|
||||
verts_array *= self.unit_scale
|
||||
if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8):
|
||||
verts = verts_array.tolist()
|
||||
else:
|
||||
verts = self.apply_matrix_to_flat_coords(verts_array, native_data["matrix"])
|
||||
verts = [None] * len(self.mesh_data["co"])
|
||||
for i in range(0, len(self.mesh_data["co"]), 3):
|
||||
verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector(
|
||||
(
|
||||
self.mesh_data["co"][i] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 1] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 2] * self.unit_scale,
|
||||
1,
|
||||
)
|
||||
)
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
mesh.vertices.add(self.mesh_data["total_verts"])
|
||||
@@ -1259,13 +1281,6 @@ class IfcImporter:
|
||||
mesh["ios_material_ids"] = self.mesh_data["material_ids"]
|
||||
return mesh
|
||||
|
||||
def apply_matrix_to_flat_coords(self, coords, matrix):
|
||||
coords_array = np.array(coords).reshape(-1, 3)
|
||||
ones = np.ones((coords_array.shape[0], 1))
|
||||
homogeneous_coords = np.hstack([coords_array, ones])
|
||||
transformed_coords = homogeneous_coords @ matrix.T
|
||||
return transformed_coords[:, :3].flatten().tolist()
|
||||
|
||||
def convert_representation_item_face_based_surface_model(self, item):
|
||||
mesh = item.get_info_2(recursive=True)
|
||||
for face_set in mesh["FbsmFaces"]:
|
||||
@@ -1495,10 +1510,10 @@ class IfcImporter:
|
||||
# Occurs when reloading a project
|
||||
pass
|
||||
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
|
||||
if types_collection := project_collection.children.get("IfcTypeProduct"):
|
||||
types_collection.hide_viewport = False
|
||||
for obj in types_collection.collection.objects: # turn off all objects inside Types collection.
|
||||
obj.hide_set(True)
|
||||
types_collection = project_collection.children[self.type_collection.name]
|
||||
types_collection.hide_viewport = False
|
||||
for obj in types_collection.collection.objects: # turn off all objects inside Types collection.
|
||||
obj.hide_set(True)
|
||||
|
||||
def clean_mesh(self):
|
||||
obj = None
|
||||
@@ -1512,19 +1527,18 @@ class IfcImporter:
|
||||
if not last_obj:
|
||||
return
|
||||
|
||||
# Temporarily unhide types collection to make sure all objects will be cleaned
|
||||
# temporarily unhide types collection to make sure all objects will be cleaned
|
||||
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
|
||||
if types_collection := project_collection.children.get("IfcTypeProduct"):
|
||||
types_collection.hide_viewport = False
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
types_collection = project_collection.children[self.type_collection.name]
|
||||
types_collection.hide_viewport = False
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.tris_convert_to_quads()
|
||||
bpy.ops.mesh.normals_make_consistent()
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
if types_collection:
|
||||
types_collection.hide_viewport = True
|
||||
types_collection.hide_viewport = True
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
IfcStore.edited_objs.clear()
|
||||
|
||||
@@ -1589,6 +1603,109 @@ class IfcImporter:
|
||||
self.project["blender"].BIMCollectionProperties.obj = obj
|
||||
obj.BIMObjectProperties.collection = self.collections[project.GlobalId] = self.project["blender"]
|
||||
|
||||
def create_collections(self) -> None:
|
||||
self.create_spatial_decomposition_collections()
|
||||
if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
|
||||
self.create_aggregate_and_nest_collections()
|
||||
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
|
||||
pass
|
||||
|
||||
def create_spatial_decomposition_collections(self) -> None:
|
||||
for rel_aggregate in self.project["ifc"].IsDecomposedBy or []:
|
||||
self.create_spatial_decomposition_collection(self.project["blender"], rel_aggregate.RelatedObjects)
|
||||
|
||||
# Invalid IFCs may have orphaned spatial structure elements.
|
||||
orphaned_spaces = [e for e in self.spatial_elements if e.GlobalId not in self.collections]
|
||||
while orphaned_spaces:
|
||||
self.create_spatial_decomposition_collection(self.project["blender"], orphaned_spaces)
|
||||
orphaned_spaces = [e for e in self.spatial_elements if e.GlobalId not in self.collections]
|
||||
|
||||
tool.Loader.create_project_collection("Views")
|
||||
self.type_collection = tool.Loader.create_project_collection("Types")
|
||||
|
||||
def create_spatial_decomposition_collection(
|
||||
self, parent: Union[bpy.types.Collection, bpy.types.Object], related_objects: list[ifcopenshell.entity_instance]
|
||||
) -> None:
|
||||
for element in related_objects:
|
||||
if element not in self.spatial_elements:
|
||||
continue
|
||||
is_existing = False
|
||||
if self.has_existing_project:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
is_existing = True
|
||||
collection = obj.BIMObjectProperties.collection
|
||||
self.collections[element.GlobalId] = collection
|
||||
if not is_existing:
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(element))
|
||||
self.collections[element.GlobalId] = collection
|
||||
parent.children.link(collection)
|
||||
if element.IsDecomposedBy:
|
||||
for rel_aggregate in element.IsDecomposedBy:
|
||||
self.create_spatial_decomposition_collection(collection, rel_aggregate.RelatedObjects)
|
||||
|
||||
def create_aggregate_and_nest_collections(self):
|
||||
if self.ifc_import_settings.has_filter:
|
||||
rel_aggregates = set()
|
||||
for element in self.elements:
|
||||
if decomposed_by := element.IsDecomposedBy:
|
||||
rel_aggregates.add(decomposed_by[0])
|
||||
elif decomposes := element.Decomposes:
|
||||
rel_aggregates.add(decomposes[0])
|
||||
elif nested_by := getattr(element, "IsNestedBy", []): # IFC2X3 does not have IsNestedBy
|
||||
if next((e for e in nested_by[0].RelatedObjects if not e.is_a("IfcPort")), None):
|
||||
rel_aggregates.add(nested_by[0])
|
||||
elif nests := getattr(element, "Nests", []):
|
||||
rel_aggregates.add(nests[0])
|
||||
elif element.is_a("IfcSurfaceFeature") and self.file.schema == "IFC4X3":
|
||||
rel_aggregates.add(element.AdheresToElement[0])
|
||||
else:
|
||||
rel_aggregates = [
|
||||
r
|
||||
for r in self.file.by_type("IfcRelAggregates")
|
||||
if (relating_obj := r.RelatingObject).is_a("IfcElement") or relating_obj.is_a("IfcElementType")
|
||||
] + [
|
||||
r
|
||||
for r in self.file.by_type("IfcRelNests")
|
||||
if (
|
||||
(relating_obj := r.RelatingObject).is_a("IfcElement")
|
||||
or relating_obj.is_a("IfcElementType")
|
||||
or (relating_obj.is_a("IfcPositioningElement") and not relating_obj.is_a("IfcGrid"))
|
||||
)
|
||||
and [e for e in r.RelatedObjects if not e.is_a("IfcPort")]
|
||||
]
|
||||
if self.file.schema == "IFC4X3":
|
||||
rel_aggregates += [r for r in self.file.by_type("IfcRelAdheresToElement")]
|
||||
|
||||
if len(rel_aggregates) > 10000:
|
||||
# More than 10,000 collections makes Blender unhappy
|
||||
print("Skipping aggregate collections for performance.")
|
||||
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION"
|
||||
return
|
||||
|
||||
aggregates: dict[str, dict] = {}
|
||||
for rel_aggregate in rel_aggregates:
|
||||
element: ifcopenshell.entity_instance = getattr(rel_aggregate, "RelatingObject", None) or getattr(
|
||||
rel_aggregate, "RelatingElement"
|
||||
)
|
||||
collection = bpy.data.collections.new(tool.Loader.get_name(element))
|
||||
aggregates[element.GlobalId] = {"element": element, "collection": collection}
|
||||
self.collections[element.GlobalId] = collection
|
||||
|
||||
for global_id, aggregate in aggregates.items():
|
||||
parent = ifcopenshell.util.element.get_aggregate(aggregate["element"])
|
||||
if parent:
|
||||
self.collections[parent.GlobalId].children.link(aggregate["collection"])
|
||||
continue
|
||||
parent = ifcopenshell.util.element.get_container(aggregate["element"])
|
||||
if parent:
|
||||
self.collections[parent.GlobalId].children.link(aggregate["collection"])
|
||||
continue
|
||||
if aggregate["element"].is_a("IfcElementType"):
|
||||
self.type_collection.children.link(aggregate["collection"])
|
||||
continue
|
||||
self.project["blender"].children.link(aggregate["collection"])
|
||||
|
||||
def create_materials(self) -> None:
|
||||
for material in self.file.by_type("IfcMaterial"):
|
||||
self.create_material(material)
|
||||
@@ -1645,7 +1762,75 @@ class IfcImporter:
|
||||
def place_objects_in_collections(self) -> None:
|
||||
for ifc_definition_id, obj in self.added_data.items():
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
tool.Collector.assign(obj)
|
||||
self.place_object_in_collection(self.file.by_id(ifc_definition_id), obj)
|
||||
|
||||
def place_object_in_collection(self, element: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None:
|
||||
if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
|
||||
self.place_object_in_decomposition_collection(element, obj)
|
||||
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
|
||||
self.place_object_in_spatial_decomposition_collection(element, obj)
|
||||
|
||||
def place_object_in_decomposition_collection(
|
||||
self, element: ifcopenshell.entity_instance, obj: bpy.types.Object
|
||||
) -> None:
|
||||
if element.is_a("IfcProject"):
|
||||
return
|
||||
elif element.is_a("IfcGridAxis"):
|
||||
return
|
||||
elif element.GlobalId in self.collections:
|
||||
collection = self.collections[element.GlobalId]
|
||||
collection.BIMCollectionProperties.obj = obj
|
||||
obj.BIMObjectProperties.collection = collection
|
||||
collection.name = obj.name
|
||||
return collection.objects.link(obj)
|
||||
elif getattr(element, "Decomposes", None):
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
return self.collections[aggregate.GlobalId].objects.link(obj)
|
||||
elif getattr(element, "Nests", None) and not element.is_a("IfcPort"):
|
||||
nest = ifcopenshell.util.element.get_nest(element)
|
||||
return self.collections[nest.GlobalId].objects.link(obj)
|
||||
elif element.is_a("IfcSurfaceFeature") and self.file.schema == "IFC4X3":
|
||||
adherend = element.AdheresToElement[0].RelatingElement
|
||||
return self.collections[adherend.GlobalId].objects.link(obj)
|
||||
|
||||
return self.place_object_in_spatial_decomposition_collection(element, obj)
|
||||
|
||||
def place_object_in_spatial_decomposition_collection(
|
||||
self, element: ifcopenshell.entity_instance, obj: bpy.types.Object
|
||||
) -> None:
|
||||
if element.is_a("IfcProject"):
|
||||
return
|
||||
elif element.is_a("IfcGridAxis"):
|
||||
return
|
||||
elif element.GlobalId in self.collections:
|
||||
collection = self.collections[element.GlobalId]
|
||||
collection.BIMCollectionProperties.obj = obj
|
||||
obj.BIMObjectProperties.collection = collection
|
||||
return collection.objects.link(obj)
|
||||
elif element.is_a("IfcTypeObject"):
|
||||
return self.type_collection.objects.link(obj)
|
||||
elif element.is_a("IfcStructuralMember"):
|
||||
return self.structural_member_collection.objects.link(obj)
|
||||
elif element.is_a("IfcStructuralConnection"):
|
||||
return self.structural_connection_collection.objects.link(obj)
|
||||
elif element.is_a("IfcAnnotation"):
|
||||
group = self.get_drawing_group(element)
|
||||
if group:
|
||||
return self.collections[group.GlobalId].objects.link(obj)
|
||||
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if container:
|
||||
if element.is_a("IfcGrid"): # TODO: refactor into a more holistic collection mode feature
|
||||
grid_collection = bpy.data.collections.get(obj.name)
|
||||
if grid_collection: # Just in case we run into invalid grids from Revit
|
||||
self.collections[container.GlobalId].children.link(grid_collection)
|
||||
grid_collection.objects.link(obj)
|
||||
else:
|
||||
self.collections[container.GlobalId].objects.link(obj)
|
||||
|
||||
else:
|
||||
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
def is_curve_annotation(self, element):
|
||||
object_type = element.ObjectType
|
||||
@@ -1723,17 +1908,13 @@ class IfcImporter:
|
||||
polyline.points[-1].co = mathutils.Vector(v2)
|
||||
return curve
|
||||
|
||||
def create_mesh(
|
||||
self,
|
||||
element: ifcopenshell.entity_instance,
|
||||
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
|
||||
) -> bpy.types.Mesh:
|
||||
def create_mesh(self, element: ifcopenshell.entity_instance, shape) -> bpy.types.Mesh:
|
||||
try:
|
||||
if hasattr(shape, "geometry"):
|
||||
# shape is ShapeElementType
|
||||
geometry = shape.geometry
|
||||
# shape is ifcopenshell_wrapper.TriangulationElement
|
||||
geometry: ifcopenshell_wrapper.Triangulation = shape.geometry
|
||||
else:
|
||||
geometry = shape
|
||||
geometry: ifcopenshell_wrapper.Triangulation = shape
|
||||
|
||||
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry))
|
||||
|
||||
@@ -1743,14 +1924,34 @@ class IfcImporter:
|
||||
and geometry.verts
|
||||
and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2]))
|
||||
):
|
||||
# Shift geometry close to the origin based off that first vert it found
|
||||
verts_array = np.array(geometry.verts)
|
||||
offset = np.array([-geometry.verts[0], -geometry.verts[1], -geometry.verts[2]])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
verts = offset_verts.tolist()
|
||||
|
||||
offset_point = np.array(
|
||||
(
|
||||
float(props.blender_eastings),
|
||||
float(props.blender_northings),
|
||||
float(props.blender_orthogonal_height),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
if geometry != shape:
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
offset_point = np.linalg.inv(mat) @ offset_point
|
||||
verts = [None] * len(geometry.verts)
|
||||
for i in range(0, len(geometry.verts), 3):
|
||||
# Note: this enh2xyz call is crazy slow.
|
||||
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
|
||||
geometry.verts[i],
|
||||
geometry.verts[i + 1],
|
||||
geometry.verts[i + 2],
|
||||
offset_point[0] * self.unit_scale,
|
||||
offset_point[1] * self.unit_scale,
|
||||
offset_point[2] * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = f"{geometry.verts[0]},{geometry.verts[1]},{geometry.verts[2]}"
|
||||
else:
|
||||
verts = geometry.verts
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
@@ -1785,7 +1986,7 @@ class IfcImporter:
|
||||
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
|
||||
mesh.from_pydata(vertices, edges, [])
|
||||
|
||||
mesh["ios_materials"] = [m.instance_id() for m in geometry.materials]
|
||||
mesh["ios_materials"] = [int(m.name.split("-")[2]) for m in geometry.materials]
|
||||
mesh["ios_material_ids"] = geometry.material_ids
|
||||
return mesh
|
||||
except:
|
||||
@@ -1885,6 +2086,7 @@ class IfcImportSettings:
|
||||
self.should_filter_spatial_elements = True
|
||||
self.should_setup_viewport_camera = True
|
||||
self.elements: set[ifcopenshell.entity_instance] = set()
|
||||
self.collection_mode = "DECOMPOSITION"
|
||||
|
||||
@staticmethod
|
||||
def factory(context=None, input_file=None, logger=None):
|
||||
@@ -1896,6 +2098,7 @@ class IfcImportSettings:
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
settings.logger = logger
|
||||
settings.diff_file = scene_diff.diff_json_file
|
||||
settings.collection_mode = props.collection_mode
|
||||
settings.should_use_cpu_multiprocessing = props.should_use_cpu_multiprocessing
|
||||
settings.merge_mode = props.merge_mode
|
||||
settings.should_merge_materials_by_colour = props.should_merge_materials_by_colour
|
||||
|
||||
@@ -159,6 +159,7 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not element:
|
||||
continue
|
||||
|
||||
tool.Collector.sync(obj)
|
||||
current_aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
current_container = ifcopenshell.util.element.get_container(element)
|
||||
if current_aggregate:
|
||||
@@ -290,7 +291,7 @@ class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
|
||||
part_class=self.part_class,
|
||||
part_name=self.part_name,
|
||||
)
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
tool.Spatial.load_container_manager()
|
||||
|
||||
|
||||
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -59,20 +59,6 @@ class LoadBcfProject(bpy.types.Operator):
|
||||
if self.filepath:
|
||||
bcfstore.BcfStore.set_by_filepath(self.filepath)
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
# a BCFv2.1 does not need to have a project, but BBIM likes to have one
|
||||
# https://github.com/buildingSMART/BCF-XML/tree/release_2_1/Documentation#bcf-file-structure
|
||||
nameless = "Unknown"
|
||||
if bcfxml.project is None:
|
||||
if bcfxml.version.version_id.startswith("2"):
|
||||
print("No project, we will create one for BBIM.")
|
||||
bcfxml.project_info = bcf.v2.model.ProjectExtension(
|
||||
project=bcf.v2.model.Project(
|
||||
name=nameless,
|
||||
project_id=str(uuid.uuid4())
|
||||
), extension_schema=""
|
||||
)
|
||||
if bcfxml.project.name is None:
|
||||
bcfxml.project.name = nameless
|
||||
context.scene.BCFProperties.name = bcfxml.project.name
|
||||
bpy.ops.bim.load_bcf_topics()
|
||||
return {"FINISHED"}
|
||||
@@ -100,19 +86,7 @@ class LoadBcfTopics(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
context.scene.BCFProperties.topics.clear()
|
||||
# workaround, one non standard topic would break reading entire bcf
|
||||
# ignored these topics ATM
|
||||
# happens on non standard nodes or on missing nodes in markup
|
||||
topics2use = []
|
||||
for topic_guid in bcfxml.topics.keys():
|
||||
# print("topic guid: {}".format(topic_guid))
|
||||
try:
|
||||
topic_titel = bcfxml.topics[topic_guid].topic.title
|
||||
topics2use.append(topic_guid)
|
||||
except:
|
||||
print("Problems on reading topic, thus ignored: {}".format(topic_guid))
|
||||
continue
|
||||
for index, topic_guid in enumerate(topics2use):
|
||||
for index, topic_guid in enumerate(bcfxml.topics.keys()):
|
||||
new = context.scene.BCFProperties.topics.add()
|
||||
bpy.ops.bim.load_bcf_topic(topic_guid=topic_guid, topic_index=index)
|
||||
return {"FINISHED"}
|
||||
@@ -964,42 +938,22 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
# Operators with context overrides are used because they are
|
||||
# significantly faster than looping through all objects
|
||||
|
||||
self.set_exceptions(viewpoint, context)
|
||||
self.set_view_setup_hints(viewpoint, context)
|
||||
# set selection at the end not to conflict with .hide_spaces
|
||||
self.set_selection(viewpoint)
|
||||
self.set_colours(viewpoint)
|
||||
|
||||
def set_exceptions(self, viewpoint, context):
|
||||
if (
|
||||
not hasattr(viewpoint.visualization_info.components, "visibility")
|
||||
or not hasattr(viewpoint.visualization_info.components.visibility.exceptions, "component")
|
||||
):
|
||||
return
|
||||
|
||||
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions.component or []}
|
||||
|
||||
# print("default_visibility: {}".format(viewpoint.visualization_info.components.visibility.default_visibility))
|
||||
if viewpoint.visualization_info.components.visibility.default_visibility:
|
||||
# default_visibility is True: show all objs, hide the exceptions
|
||||
old = context.area.type
|
||||
context.area.type = "VIEW_3D"
|
||||
bpy.ops.object.hide_view_clear()
|
||||
context.area.type = old
|
||||
for global_id in exception_global_ids:
|
||||
# print("{}: hide".format(global_id))
|
||||
obj = IfcStore.get_element(global_id)
|
||||
if obj and bpy.context.view_layer.objects.get(obj.name):
|
||||
# print(" obj found")
|
||||
obj.hide_set(True)
|
||||
else:
|
||||
# default_visibility is False: hide all objs, show the exceptions
|
||||
objs = []
|
||||
for global_id in exception_global_ids:
|
||||
# print("{}: show".format(global_id))
|
||||
obj = IfcStore.get_element(global_id)
|
||||
if obj:
|
||||
# print(" obj found")
|
||||
objs.append(obj)
|
||||
if objs:
|
||||
old = context.area.type
|
||||
@@ -1013,7 +967,6 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
bpy.data.objects["Viewpoint"].hide_set(False)
|
||||
context.area.type = old
|
||||
|
||||
def set_view_setup_hints(self, viewpoint, context):
|
||||
if viewpoint.visualization_info.components.view_setup_hints:
|
||||
if not viewpoint.visualization_info.components.view_setup_hints.spaces_visible:
|
||||
self.hide_spaces(context)
|
||||
@@ -1025,6 +978,10 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
self.hide_spaces(context)
|
||||
self.set_openings_visibility(False, context)
|
||||
|
||||
# set selection at the end not to conflict with .hide_spaces
|
||||
self.set_selection(viewpoint)
|
||||
self.set_colours(viewpoint)
|
||||
|
||||
def hide_spaces(self, context):
|
||||
old = context.area.type
|
||||
context.area.type = "VIEW_3D"
|
||||
@@ -1042,25 +999,19 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
selected_global_ids = [s.ifc_guid for s in viewpoint.visualization_info.components.selection.component or []]
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for global_id in selected_global_ids:
|
||||
# print("{}: selected".format(global_id))
|
||||
obj = IfcStore.get_element(global_id)
|
||||
if obj:
|
||||
# print(" obj found")
|
||||
obj.select_set(True)
|
||||
obj.hide_set(False)
|
||||
|
||||
def set_colours(self, viewpoint):
|
||||
if not viewpoint.visualization_info.components or not viewpoint.visualization_info.components.coloring:
|
||||
return
|
||||
global_id_colours = {}
|
||||
for acoloring in viewpoint.visualization_info.components.coloring.color:
|
||||
for acomponent in acoloring.component:
|
||||
global_id_colours.setdefault(acomponent.ifc_guid, acoloring.color)
|
||||
for coloring in viewpoint.visualization_info.components.coloring or []:
|
||||
for component in coloring.components:
|
||||
global_id_colours.setdefault(component.ifc_guid, coloring.color)
|
||||
for global_id, color in global_id_colours.items():
|
||||
# print("{}: color: {}: {}".format(global_id, color, self.hex_to_rgb(color)))
|
||||
obj = IfcStore.get_element(global_id)
|
||||
if obj:
|
||||
# print(" obj found ")
|
||||
obj.color = self.hex_to_rgb(color)
|
||||
|
||||
def draw_lines(self, viewpoint, context):
|
||||
@@ -1144,14 +1095,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
def hex_to_rgb(self, value):
|
||||
value = value.lstrip("#")
|
||||
lv = len(value)
|
||||
# https://github.com/buildingSMART/BCF-XML/tree/release_3_0/Documentation#coloring
|
||||
if lv == 8:
|
||||
t = tuple(int(value[i : i + lv // 4], 16) for i in range(0, lv, lv // 4))
|
||||
col = [t[1] / 255.0, t[2] / 255.0, t[3] / 255.0, t[0] / 255.0]
|
||||
else:
|
||||
t = tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||
col = [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
|
||||
return col
|
||||
t = tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||
return [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
|
||||
|
||||
|
||||
class OpenBcfReferenceLink(bpy.types.Operator):
|
||||
|
||||
@@ -133,11 +133,14 @@ class Loader:
|
||||
return mesh
|
||||
|
||||
def load_settings(self):
|
||||
return ifcopenshell.geom.settings()
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.EXCLUDE_SOLIDS_AND_SURFACES, False)
|
||||
settings.set(settings.USE_BREP_DATA, False)
|
||||
return settings
|
||||
|
||||
def load_fallback_settings(self):
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
return settings
|
||||
|
||||
def load_importer(self):
|
||||
@@ -629,18 +632,24 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# TODO : refactor to be able to generate all boundaries for selected space automatically or with an option
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="Please set an active container to detect space boundaries from.")
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
|
||||
element = tool.Ifc.get_entity(objs[0])
|
||||
if element.is_a("IfcSpace"):
|
||||
relating_space = element
|
||||
relating_space_obj = objs[0]
|
||||
|
||||
if not (container := tool.Root.get_default_container()):
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = bpy.data.objects.get(collection.name)
|
||||
if not collection_obj:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||
if not spatial_element:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
for subelement in ifcopenshell.util.element.get_decomposition(container):
|
||||
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
|
||||
if not (
|
||||
subelement.is_a("IfcWall") or subelement.is_a("IfcSlab") or subelement.is_a("IfcVirtualElement")
|
||||
):
|
||||
@@ -683,7 +692,8 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tree = ifcopenshell.geom.tree()
|
||||
shapes = {}
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("disable-opening-subtractions", True)
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=include)
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
@@ -821,10 +831,17 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
# Create shape of opening as a dissolved BMesh
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, opening)
|
||||
m = shape.transformation.matrix
|
||||
mat = Matrix(ifcopenshell.util.shape.get_shape_matrix(shape))
|
||||
mat.translation = (0, 0, 0)
|
||||
m = shape.transformation.matrix.data
|
||||
mat = Matrix(
|
||||
(
|
||||
[m[0], m[3], m[6], m[9]],
|
||||
[m[1], m[4], m[7], m[10]],
|
||||
[m[2], m[5], m[8], m[11]],
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
opening_bm = bmesh.new()
|
||||
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
|
||||
for vert in verts:
|
||||
@@ -1015,10 +1032,12 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
if not element.is_a("IfcOpeningElement"):
|
||||
settings.set("disable-opening-subtractions", True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
# geometry = ifcopenshell.geom.create_shape(settings, body)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
mat = Matrix(ifcopenshell.util.shape.get_shape_matrix(shape))
|
||||
m = shape.transformation.matrix.data
|
||||
mat = Matrix(([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1]))
|
||||
verts = [space_matrix_i @ mat @ Vector(v) for v in ifcopenshell.util.shape.get_vertices(shape.geometry)]
|
||||
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
|
||||
polygons = []
|
||||
|
||||
@@ -35,7 +35,6 @@ class Operator:
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CoveringTool(WorkSpaceTool):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_context_mode = "OBJECT"
|
||||
@@ -54,19 +53,18 @@ class CoveringTool(WorkSpaceTool):
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
def add_layout_hotkey(layout, text, hotkey, description):
|
||||
args = ["covering", layout, text, hotkey, description]
|
||||
tool.Blender.add_layout_hotkey_operator(*args)
|
||||
|
||||
|
||||
class CoveringToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout, ifc_element_type=None):
|
||||
def draw(cls, context, layout, ifc_element_type = None):
|
||||
cls.layout = layout
|
||||
cls.props = context.scene.BIMModelProperties
|
||||
cls.covering_props = context.scene.BIMCoveringProperties
|
||||
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
if not tool.Ifc.get():
|
||||
row.label(text="No IFC Project", icon="ERROR")
|
||||
@@ -84,6 +82,8 @@ class CoveringToolUI:
|
||||
|
||||
cls.draw_default_interface()
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def draw_header_interface(cls):
|
||||
cls.draw_type_selection_interface()
|
||||
@@ -95,35 +95,122 @@ class CoveringToolUI:
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(data=cls.covering_props, property="ceiling_height", text="Ceiling Height")
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.operator("bim.add_constr_type_instance", text="Add")
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
relating_type_id = int(cls.props.relating_type_id)
|
||||
type_material_usage = ifcopenshell.util.element.get_material(tool.Ifc.get().by_id(relating_type_id)).is_a()
|
||||
|
||||
# PLEASE KEEP COMMENTS AS A REMINDER
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
|
||||
# if (type_material_usage == "IfcMaterialLayerSet" and
|
||||
# not bpy.context.selected_objects):
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.label(text="", icon="EVENT_SHIFT")
|
||||
# row.label(text="", icon="EVENT_A")
|
||||
# if tool.Ifc.get_entity(collection_obj):
|
||||
# if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
# op = row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
# elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
# op = row.operator("bim.add_instance_ceiling_covering_from_cursor")
|
||||
# else:
|
||||
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
# op.from_invoke = True
|
||||
# if cls.props.relating_type_id.isnumeric():
|
||||
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||
#
|
||||
# else:
|
||||
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
# op.from_invoke = True
|
||||
# if cls.props.relating_type_id.isnumeric():
|
||||
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||
#
|
||||
# elif (AuthoringData.data["predefined_type"] == "FLOORING" and
|
||||
# type_material_usage == "IfcMaterialLayerSet" and
|
||||
# element and
|
||||
# bpy.context.selected_objects and
|
||||
# element.is_a("IfcWall")):
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.label(text="", icon="EVENT_SHIFT")
|
||||
# row.label(text="", icon="EVENT_A")
|
||||
# op = row.operator("bim.add_instance_flooring_coverings_from_walls")
|
||||
#
|
||||
# elif (AuthoringData.data["predefined_type"] == "CEILING" and
|
||||
# type_material_usage == "IfcMaterialLayerSet" and
|
||||
# element and
|
||||
# bpy.context.selected_objects and
|
||||
# element.is_a("IfcWall")):
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.label(text="", icon="EVENT_SHIFT")
|
||||
# row.label(text="", icon="EVENT_A")
|
||||
# op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
#
|
||||
# elif (element and
|
||||
# bpy.context.selected_objects and
|
||||
# element.is_a("IfcCovering") and
|
||||
## AuthoringData.data["predefined_type"] == "FLOORING" and
|
||||
# AuthoringData.data["active_material_usage"] == "LAYER3"):
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.label(text="", icon="EVENT_SHIFT")
|
||||
# row.label(text="", icon="EVENT_G")
|
||||
# op = row.operator("bim.regen_selected_covering_object")
|
||||
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.operator("bim.add_instance_ceiling_covering_from_cursor")
|
||||
op = row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.operator("bim.add_instance_flooring_coverings_from_walls")
|
||||
op = row.operator("bim.add_instance_ceiling_covering_from_cursor")
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
op = row.operator("bim.add_instance_flooring_coverings_from_walls")
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_G")
|
||||
row.operator("bim.regen_selected_covering_object")
|
||||
op = row.operator("bim.regen_selected_covering_object")
|
||||
|
||||
# elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.prop(data=cls.props, property="ceiling_height", text="ceiling height")
|
||||
# if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
# op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
# else:
|
||||
# op = row.operator("bim.add_instance_ceiling_from_cursor")
|
||||
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
# op.from_invoke = True
|
||||
# if cls.props.relating_type_id.isnumeric():
|
||||
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||
# else:
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.label(text="", icon="EVENT_SHIFT")
|
||||
# row.label(text="", icon="EVENT_A")
|
||||
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
# op.from_invoke = True
|
||||
# if cls.props.relating_type_id.isnumeric():
|
||||
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||
|
||||
@classmethod
|
||||
def draw_type_selection_interface(cls):
|
||||
@@ -169,7 +256,7 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMCoveringProperties
|
||||
# self.props = context.scene.BIMCoveringProperties
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -183,19 +270,20 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
def hotkey_S_A(self):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
container = tool.Root.get_default_container()
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
|
||||
elif container:
|
||||
elif tool.Ifc.get_entity(collection_obj):
|
||||
bpy.ops.bim.add_instance_flooring_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_ceiling_coverings_from_walls()
|
||||
elif container:
|
||||
elif tool.Ifc.get_entity(collection_obj):
|
||||
bpy.ops.bim.add_instance_ceiling_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
@@ -205,10 +293,9 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
def hotkey_S_G(self):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if (
|
||||
element
|
||||
and bpy.context.selected_objects
|
||||
and element.is_a("IfcCovering")
|
||||
and AuthoringData.data["active_material_usage"] == "LAYER3"
|
||||
):
|
||||
bpy.ops.bim.regen_selected_covering_object()
|
||||
if (element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcCovering") and
|
||||
AuthoringData.data["active_material_usage"] == "LAYER3"):
|
||||
bpy.ops.bim.regen_selected_covering_object()
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import platform
|
||||
import subprocess
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -173,7 +172,7 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
total = len(elements)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings_2d = ifcopenshell.geom.settings()
|
||||
settings_2d.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
|
||||
failures = []
|
||||
excludes = () # For the developer to debug with
|
||||
for i, element in enumerate(elements, 1):
|
||||
@@ -226,7 +225,7 @@ class CreateShapeFromStepId(bpy.types.Operator):
|
||||
element = self.file.by_id(self.step_id or int(context.scene.BIMDebugProperties.step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
if self.should_include_curves:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
if shape:
|
||||
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
|
||||
@@ -476,7 +476,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
if not context or not drawing_elements:
|
||||
continue
|
||||
geom_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
if ifc.by_id(context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
|
||||
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
|
||||
@@ -561,7 +561,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
if tool.Ifc.get() == ifc and self.camera_element not in drawing_elements:
|
||||
with profile("Camera element"):
|
||||
# The camera must always be included, regardless of any include/exclude filters.
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True)
|
||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||
for elem in it:
|
||||
self.serialiser.write(elem)
|
||||
@@ -875,7 +875,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
def setup_serialiser(self, target_view):
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
||||
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
|
||||
@@ -1519,40 +1519,48 @@ class ActivateDrawing(bpy.types.Operator):
|
||||
bl_idname = "bim.activate_drawing"
|
||||
bl_label = "Activate Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Activates the selected drawing view.\n\n" + "ALT+CLICK to keep the viewport position"
|
||||
bl_description = "Activates the selected drawing view.\n\n" + "ALT+CLICK to keep the viewport position.\n\n" + "SHIFT+CLICK activiate drawing without turning objects on/off."
|
||||
|
||||
drawing: bpy.props.IntProperty()
|
||||
camera_view_point: bpy.props.BoolProperty(name="Camera View Point", default=True, options={"SKIP_SAVE"})
|
||||
switch_camera_only: bpy.props.BoolProperty(name="Only Changes Camera View", default=False, options={"SKIP_SAVE"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# keep the viewport position on alt+click
|
||||
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
|
||||
if event.type == "LEFTMOUSE" and event.alt:
|
||||
self.camera_view_point = False
|
||||
# Only activates the camera view on alt+shift. Does not turn on/off objects in scene
|
||||
if event.type == "LEFTMOUSE" and event.shift:
|
||||
self.switch_camera_only = True
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
drawing = tool.Ifc.get().by_id(self.drawing)
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
camera = tool.Drawing.import_drawing(drawing)
|
||||
|
||||
if not self.camera_view_point:
|
||||
viewport_position = tool.Blender.get_viewport_position()
|
||||
if self.switch_camera_only:
|
||||
tool.Blender.activate_camera(camera)
|
||||
else:
|
||||
if not self.camera_view_point:
|
||||
viewport_position = tool.Blender.get_viewport_position()
|
||||
|
||||
core.activate_drawing_view(tool.Ifc, tool.Blender, tool.Drawing, drawing=drawing)
|
||||
|
||||
if not self.camera_view_point:
|
||||
tool.Blender.set_viewport_position(viewport_position)
|
||||
|
||||
dprops.active_drawing_id = self.drawing
|
||||
# reset DrawingsData to reload_drawing_styles work correctly
|
||||
DrawingsData.is_loaded = False
|
||||
dprops.drawing_styles.clear()
|
||||
if ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing", "HasUnderlay"):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
CutDecorator.install(context)
|
||||
tool.Drawing.show_decorations()
|
||||
if not self.camera_view_point:
|
||||
tool.Blender.set_viewport_position(viewport_position)
|
||||
|
||||
dprops.active_drawing_id = self.drawing
|
||||
# reset DrawingsData to reload_drawing_styles work correctly
|
||||
DrawingsData.is_loaded = False
|
||||
dprops.drawing_styles.clear()
|
||||
if ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing", "HasUnderlay"):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
CutDecorator.install(context)
|
||||
tool.Drawing.show_decorations()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1884,10 +1892,10 @@ class ActivateDrawingStyle(bpy.types.Operator, Operator):
|
||||
except:
|
||||
continue
|
||||
if self.drawing_style.include_query:
|
||||
results = ifcopenshell.util.selector.filter_elements(ifc, self.drawing_style.include_query)
|
||||
results = ifcopenshell.util.selector.Selector.parse(ifc, self.drawing_style.include_query)
|
||||
self.include_global_ids.extend([e.GlobalId for e in results])
|
||||
if self.drawing_style.exclude_query:
|
||||
results = ifcopenshell.util.selector.filter_elements(ifc, self.drawing_style.exclude_query)
|
||||
results = ifcopenshell.util.selector.Selector.parse(ifc, self.drawing_style.exclude_query)
|
||||
self.exclude_global_ids.extend([e.GlobalId for e in results])
|
||||
if self.drawing_style.include_query:
|
||||
self.parse_filter_query("INCLUDE", context)
|
||||
|
||||
@@ -328,17 +328,12 @@ class PlacementData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"has_placement": cls.has_placement()}
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
obj = bpy.context.active_object
|
||||
if obj and props.has_blender_offset:
|
||||
xyz = cls.original_xyz(obj)
|
||||
cls.data.update({
|
||||
"original_x": str(xyz[0]),
|
||||
"original_y": str(xyz[1]),
|
||||
"original_z": str(xyz[2]),
|
||||
})
|
||||
cls.data = {
|
||||
"has_placement": cls.has_placement(),
|
||||
"original_x": cls.original_x(),
|
||||
"original_y": cls.original_y(),
|
||||
"original_z": cls.original_z(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -349,18 +344,40 @@ class PlacementData:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def original_xyz(cls, obj):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
def original_x(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
xyz = ifcopenshell.util.geolocation.xyz2enh(
|
||||
obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][3],
|
||||
obj.matrix_world[2][3],
|
||||
float(props.blender_eastings) * unit_scale,
|
||||
float(props.blender_northings) * unit_scale,
|
||||
float(props.blender_orthogonal_height) / unit_scale,
|
||||
obj = bpy.context.active_object
|
||||
if not obj or not props.has_blender_offset:
|
||||
return
|
||||
return str(round(cls.original_xyz(obj.location)[0], 3))
|
||||
|
||||
@classmethod
|
||||
def original_y(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
obj = bpy.context.active_object
|
||||
if not obj or not props.has_blender_offset:
|
||||
return
|
||||
return str(round(cls.original_xyz(obj.location)[1], 3))
|
||||
|
||||
@classmethod
|
||||
def original_z(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
obj = bpy.context.active_object
|
||||
if not obj or not props.has_blender_offset:
|
||||
return
|
||||
return str(round(cls.original_xyz(obj.location)[2], 3))
|
||||
|
||||
@classmethod
|
||||
def original_xyz(cls, location):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
return ifcopenshell.util.geolocation.xyz2enh(
|
||||
location[0],
|
||||
location[1],
|
||||
location[2],
|
||||
float(props.blender_eastings),
|
||||
float(props.blender_northings),
|
||||
float(props.blender_orthogonal_height),
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
1.0,
|
||||
)
|
||||
return [round(o, 3) / unit_scale for o in xyz] # To nearest mm of precision
|
||||
|
||||
@@ -374,12 +374,14 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
|
||||
gprop = context.scene.BIMGeoreferenceProperties
|
||||
coordinate_offset = None
|
||||
if (
|
||||
gprop.has_blender_offset
|
||||
and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT"
|
||||
and obj.BIMObjectProperties.cartesian_point_offset
|
||||
):
|
||||
coordinate_offset = Vector(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(",")))
|
||||
if gprop.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT":
|
||||
coordinate_offset = Vector(
|
||||
(
|
||||
float(gprop.blender_eastings),
|
||||
float(gprop.blender_northings),
|
||||
float(gprop.blender_orthogonal_height),
|
||||
)
|
||||
)
|
||||
|
||||
representation_data = {
|
||||
"context": context_of_items,
|
||||
@@ -946,7 +948,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Linked_Aggregate")
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new[0], pset=pset)
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new[0],pset=pset)
|
||||
|
||||
if new[0].is_a("IfcElementAssembly"):
|
||||
linked_aggregate_group = [
|
||||
@@ -1174,10 +1176,7 @@ class DuplicateLinkedAggregateTo3dCursor(bpy.types.Operator):
|
||||
return OverrideDuplicateMove.execute_duplicate_operator(self, context, linked=False)
|
||||
|
||||
def _execute(self, context):
|
||||
return DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(
|
||||
self, context, location_from_3d_cursor=True
|
||||
)
|
||||
|
||||
return DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context, location_from_3d_cursor=True)
|
||||
|
||||
class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
bl_idname = "bim.refresh_linked_aggregate"
|
||||
|
||||
@@ -31,6 +31,7 @@ classes = (
|
||||
operator.SelectGroupProducts,
|
||||
operator.ToggleGroup,
|
||||
operator.UnassignGroup,
|
||||
operator.UpdateGroup,
|
||||
operator.SelectGroupElements,
|
||||
prop.ExpandedGroups,
|
||||
prop.Group,
|
||||
|
||||
@@ -22,6 +22,7 @@ import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import json
|
||||
|
||||
|
||||
@@ -47,6 +48,7 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new = self.props.groups.add()
|
||||
new.ifc_definition_id = group.id()
|
||||
new.name = group.Name or "Unnamed"
|
||||
new.selection_query = ""
|
||||
new.tree_depth = tree_depth
|
||||
new.has_children = False
|
||||
new.is_expanded = group.id() in self.expanded_groups
|
||||
@@ -111,6 +113,7 @@ class EditGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_group"
|
||||
bl_label = "Edit Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
copy_from_selector: bpy.props.BoolProperty(name="Copy from Selector", default=False)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMGroupProperties
|
||||
@@ -121,6 +124,9 @@ class EditGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
attributes[attribute.name] = attribute.string_value
|
||||
|
||||
if self.copy_from_selector:
|
||||
attributes["Description"] = context.scene.IFCSelector.selection_query
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"group.edit_group", self.file, **{"group": self.file.by_id(props.active_group_id), "attributes": attributes}
|
||||
@@ -236,6 +242,31 @@ class SelectGroupProducts(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UpdateGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.update_group"
|
||||
bl_label = "Update Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
query: bpy.props.StringProperty()
|
||||
group_id: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
group = self.file.by_id(self.group_id)
|
||||
query = self.query
|
||||
|
||||
new_products = Selector.parse(self.file, query)
|
||||
ifcopenshell.api.run(
|
||||
"group.update_group_products",
|
||||
self.file,
|
||||
**{
|
||||
"group": group,
|
||||
"products": new_products,
|
||||
}
|
||||
)
|
||||
bpy.ops.bim.load_groups()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectGroupElements(bpy.types.Operator):
|
||||
bl_idname = "bim.select_group_elements"
|
||||
bl_label = "Select Group elements"
|
||||
|
||||
@@ -44,6 +44,7 @@ class ExpandedGroups(StrProperty):
|
||||
class Group(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
selection_query: StringProperty(name="Selection Query")
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
has_children: BoolProperty(name="Has Children", default=False)
|
||||
tree_depth: IntProperty(name="Tree Depth")
|
||||
|
||||
@@ -131,7 +131,7 @@ class BIM_UL_groups(UIList):
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
row.label(text=item.name)
|
||||
row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name)
|
||||
group_id = item.ifc_definition_id
|
||||
if context.scene.BIMGroupProperties.active_group_id == group_id:
|
||||
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
@@ -145,6 +145,10 @@ class BIM_UL_groups(UIList):
|
||||
op.group = group_id
|
||||
op = row.operator("bim.remove_group", text="", icon="X")
|
||||
op.group = group_id
|
||||
if item.selection_query != "":
|
||||
op = row.operator("bim.update_group", text="", icon="FILE_REFRESH")
|
||||
op.group_id = item.ifc_definition_id
|
||||
op.query = item.selection_query
|
||||
else:
|
||||
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.group = group_id
|
||||
@@ -154,6 +158,10 @@ class BIM_UL_groups(UIList):
|
||||
op.group = group_id
|
||||
op = row.operator("bim.remove_group", text="", icon="X")
|
||||
op.group = group_id
|
||||
if item.selection_query != "":
|
||||
op = row.operator("bim.update_group", text="", icon="FILE_REFRESH")
|
||||
op.group_id = item.ifc_definition_id
|
||||
op.query = item.selection_query
|
||||
|
||||
|
||||
class BIM_UL_object_groups(UIList):
|
||||
|
||||
@@ -22,7 +22,6 @@ import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.covering as core
|
||||
|
||||
|
||||
class AddInstanceFlooringCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_instance_flooring_covering_from_cursor"
|
||||
bl_label = "Add Flooring From Cursor"
|
||||
@@ -31,16 +30,28 @@ class AddInstanceFlooringCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "FLOORING"
|
||||
return tool.Ifc.get_entity(collection_obj) and relating_type == "FLOORING"
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
core.add_instance_flooring_covering_from_cursor(tool.Ifc, tool.Root, tool.Spatial)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the covering in.")
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
if not collection_obj:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||
if not spatial_element:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.add_instance_flooring_covering_from_cursor(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry)
|
||||
|
||||
class AddInstanceCeilingCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_instance_ceiling_covering_from_cursor"
|
||||
@@ -50,16 +61,28 @@ class AddInstanceCeilingCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "CEILING"
|
||||
return tool.Ifc.get_entity(collection_obj) and relating_type == "CEILING"
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
core.add_instance_ceiling_covering_from_cursor(tool.Ifc, tool.Root, tool.Covering, tool.Spatial)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the covering in.")
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
if not collection_obj:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||
if not spatial_element:
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.add_instance_ceiling_covering_from_cursor(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry, tool.Covering)
|
||||
|
||||
class RegenSelectedCoveringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.regen_selected_covering_object"
|
||||
@@ -69,31 +92,39 @@ class RegenSelectedCoveringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
return element and element.is_a("IfcCovering")
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
core.regen_selected_covering_object(tool.Root, tool.Spatial)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the covering in.")
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if not element.is_a("IfcCovering"):
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.regen_selected_covering_object(tool.Ifc, tool.Spatial, tool.Model, tool.Type, tool.Geometry)
|
||||
|
||||
|
||||
class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_instance_flooring_coverings_from_walls"
|
||||
bl_label = "Add Flooring From Walls"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Add instance flooring coverings from selected walls. The active object must be a wall and layered vertically"
|
||||
)
|
||||
bl_description = "Add instance flooring coverings from selected walls. The active object must be a wall and layered vertically"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "FLOORING"
|
||||
if element:
|
||||
if element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "FLOORING"
|
||||
|
||||
def _execute(self, context):
|
||||
# This only works based on a 2D plan only considering the standard
|
||||
@@ -101,27 +132,40 @@ class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operato
|
||||
# In order to run, the active object must be a wall and
|
||||
# there must be selected walls
|
||||
|
||||
try:
|
||||
core.add_instance_flooring_coverings_from_walls(tool.Root, tool.Spatial)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the covering in.")
|
||||
active_obj = bpy.context.active_object
|
||||
if not active_obj:
|
||||
self.report({"ERROR"}, "No active object. Please select a wall")
|
||||
return
|
||||
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if element and not element.is_a("IfcWall"):
|
||||
return self.report({"ERROR"}, "The active object is not a wall. Please select a wall.")
|
||||
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if not container:
|
||||
self.report({"ERROR"}, "The wall is not contained.")
|
||||
|
||||
if not bpy.context.selected_objects:
|
||||
self.report({"ERROR"}, "No selected objects found. Please select walls.")
|
||||
return
|
||||
|
||||
core.add_instance_flooring_coverings_from_walls(tool.Ifc, tool.Spatial, tool.Collector, tool.Geometry)
|
||||
|
||||
class AddInstanceCeilingCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_instance_ceiling_coverings_from_walls"
|
||||
bl_label = "Add Ceiling From Walls"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Add instance ceiling coverings from selected walls. The active object must be a wall and layered vertically"
|
||||
)
|
||||
bl_description = "Add instance ceiling coverings from selected walls. The active object must be a wall and layered vertically"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "CEILING"
|
||||
if element:
|
||||
if element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "CEILING"
|
||||
|
||||
def _execute(self, context):
|
||||
# This only works based on a 2D plan only considering the standard
|
||||
@@ -129,7 +173,22 @@ class AddInstanceCeilingCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator
|
||||
# In order to run, the active object must be a wall and
|
||||
# there must be selected walls
|
||||
|
||||
try:
|
||||
core.add_instance_ceiling_coverings_from_walls(tool.Root, tool.Spatial, tool.Covering)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the covering in.")
|
||||
active_obj = bpy.context.active_object
|
||||
if not active_obj:
|
||||
self.report({"ERROR"}, "No active object. Please select a wall")
|
||||
return
|
||||
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if element and not element.is_a("IfcWall"):
|
||||
return self.report({"ERROR"}, "The active object is not a wall. Please select a wall.")
|
||||
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if not container:
|
||||
self.report({"ERROR"}, "The wall is not contained.")
|
||||
|
||||
if not bpy.context.selected_objects:
|
||||
self.report({"ERROR"}, "No selected objects found. Please select walls.")
|
||||
return
|
||||
|
||||
core.add_instance_ceiling_coverings_from_walls(tool.Ifc, tool.Spatial, tool.Collector, tool.Geometry, tool.Covering)
|
||||
|
||||
|
||||
@@ -508,6 +508,8 @@ class BIM_OT_add_door(bpy.types.Operator, tool.Ifc.Operator):
|
||||
mesh = bpy.data.meshes.new("IfcDoor")
|
||||
obj = bpy.data.objects.new("IfcDoor", mesh)
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcDoor", should_add_representation=False
|
||||
|
||||
@@ -28,9 +28,23 @@ def add_object(self, context):
|
||||
obj = bpy.data.objects.new("Grid", None)
|
||||
obj.name = "Grid"
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
site = tool.Ifc.get().by_type("IfcSite")
|
||||
if site:
|
||||
site = site[0]
|
||||
site_obj = tool.Ifc.get_object(site)
|
||||
if site_obj:
|
||||
collection = site_obj.BIMObjectProperties.collection or collection
|
||||
|
||||
collection.objects.link(obj)
|
||||
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcGrid")
|
||||
grid = tool.Ifc.get_entity(obj)
|
||||
|
||||
collection = obj.BIMObjectProperties.collection or collection
|
||||
|
||||
axes_collection = bpy.data.collections.new("UAxes")
|
||||
collection.children.link(axes_collection)
|
||||
for i in range(0, self.total_u):
|
||||
verts = [
|
||||
Vector((-2, i * self.u_spacing, 0)),
|
||||
@@ -43,13 +57,19 @@ def add_object(self, context):
|
||||
tag = chr(ord("A") + i)
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
|
||||
|
||||
axes_collection.objects.link(obj)
|
||||
|
||||
result = ifcopenshell.api.run(
|
||||
"grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="UAxes", grid=grid
|
||||
"grid.create_grid_axis",
|
||||
tool.Ifc.get(),
|
||||
**{"axis_tag": tag, "uvw_axes": "UAxes", "grid": grid},
|
||||
)
|
||||
tool.Ifc.link(result, obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result)
|
||||
tool.Collector.assign(obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), **{"axis_curve": obj, "grid_axis": result})
|
||||
obj.BIMObjectProperties.ifc_definition_id = result.id()
|
||||
|
||||
axes_collection = bpy.data.collections.new("VAxes")
|
||||
collection.children.link(axes_collection)
|
||||
for i in range(0, self.total_v):
|
||||
verts = [
|
||||
Vector((i * self.v_spacing, -2, 0)),
|
||||
@@ -62,12 +82,16 @@ def add_object(self, context):
|
||||
tag = str(i + 1).zfill(2)
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
|
||||
|
||||
axes_collection.objects.link(obj)
|
||||
|
||||
result = ifcopenshell.api.run(
|
||||
"grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="VAxes", grid=grid
|
||||
"grid.create_grid_axis",
|
||||
tool.Ifc.get(),
|
||||
**{"axis_tag": tag, "uvw_axes": "VAxes", "grid": grid},
|
||||
)
|
||||
tool.Ifc.link(result, obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), axis_curve=obj, grid_axis=result)
|
||||
tool.Collector.assign(obj)
|
||||
ifcopenshell.api.run("grid.create_axis_curve", tool.Ifc.get(), **{"axis_curve": obj, "grid_axis": result})
|
||||
obj.BIMObjectProperties.ifc_definition_id = result.id()
|
||||
|
||||
|
||||
class BIM_OT_add_object(Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -243,6 +243,7 @@ class MEPGenerator:
|
||||
|
||||
def setup_ports(self, obj):
|
||||
self.file = tool.Ifc.get()
|
||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
|
||||
segment = tool.Ifc.get_entity(obj)
|
||||
representation = ifcopenshell.util.representation.get_representation(segment, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@@ -291,7 +291,7 @@ class FilledOpeningGenerator:
|
||||
return tool.Ifc.get().createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
geometry = ifcopenshell.geom.create_shape(settings, profile)
|
||||
verts = ifcopenshell.util.shape.get_vertices(geometry)
|
||||
# [0, 2] represents X and Z ordinates
|
||||
|
||||
@@ -192,9 +192,9 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
|
||||
obj.location = context.scene.cursor.location
|
||||
|
||||
self.container_obj = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
|
||||
tool.Blender.remove_data_block(mesh) # Remove "Instance" mesh
|
||||
@@ -248,11 +248,11 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
# TODO For now we are hardcoding windows and doors as a prototype
|
||||
bpy.ops.bim.add_filled_opening(voided_obj=building_obj.name, filling_obj=obj.name)
|
||||
else:
|
||||
if self.container_obj:
|
||||
if collection_obj and tool.Ifc.get_entity(collection_obj):
|
||||
if props.rl_mode == "BOTTOM":
|
||||
obj.location.z = self.container_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
|
||||
obj.location.z = collection_obj.location.z - tool.Blender.get_object_bounding_box(obj)["min_z"]
|
||||
elif props.rl_mode == "CONTAINER":
|
||||
obj.location.z = self.container_obj.location.z
|
||||
obj.location.z = collection_obj.location.z
|
||||
elif props.rl_mode == "CURSOR":
|
||||
pass
|
||||
|
||||
@@ -274,8 +274,11 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def set_flow_segment_rl(self, obj):
|
||||
if self.container_obj:
|
||||
obj.location[2] = self.container_obj.location[2] + bpy.context.scene.BIMModelProperties.rl2
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
if collection_obj and tool.Ifc.get_entity(collection_obj):
|
||||
obj.location[2] = collection_obj.location[2] + bpy.context.scene.BIMModelProperties.rl2
|
||||
|
||||
@staticmethod
|
||||
def generate_layered_element(ifc_class: str, relating_type: ifcopenshell.entity_instance) -> bool:
|
||||
|
||||
@@ -57,10 +57,8 @@ class DumbProfileGenerator:
|
||||
self.body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Axis", "GRAPH_VIEW")
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
self.container = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container = container
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
self.collection_obj = self.collection.BIMCollectionProperties.obj
|
||||
self.depth = props.extrusion_depth
|
||||
self.rotation = 0
|
||||
self.location = Vector((0, 0, 0))
|
||||
@@ -85,8 +83,9 @@ class DumbProfileGenerator:
|
||||
):
|
||||
matrix_world = Matrix.Rotation(pi / 2, 4, "Z") @ Matrix.Rotation(pi / 2, 4, "X") @ matrix_world
|
||||
matrix_world.translation = self.location
|
||||
if self.container_obj:
|
||||
matrix_world.translation.z = self.container_obj.location.z
|
||||
if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id:
|
||||
matrix_world.translation.z = self.collection_obj.location.z
|
||||
self.collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -299,6 +299,8 @@ class BIM_OT_add_railing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
mesh = bpy.data.meshes.new("IfcRailing")
|
||||
obj = bpy.data.objects.new("IfcRailing", mesh)
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
blenderbim.core.root.assign_class(
|
||||
|
||||
@@ -515,6 +515,8 @@ class BIM_OT_add_roof(bpy.types.Operator, tool.Ifc.Operator):
|
||||
mesh = bpy.data.meshes.new("IfcRoof")
|
||||
obj = bpy.data.objects.new("IfcRoof", mesh)
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
blenderbim.core.root.assign_class(
|
||||
|
||||
@@ -31,6 +31,7 @@ import blenderbim.core.type
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
from math import radians
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
@@ -136,10 +137,8 @@ class DumbSlabGenerator:
|
||||
)
|
||||
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
self.container = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container = container
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
self.collection_obj = self.collection.BIMCollectionProperties.obj
|
||||
self.depth = sum(thicknesses) * unit_scale
|
||||
self.width = 3
|
||||
self.length = 3
|
||||
@@ -162,12 +161,13 @@ class DumbSlabGenerator:
|
||||
|
||||
matrix_world = Matrix()
|
||||
matrix_world.translation = self.location
|
||||
if self.container_obj:
|
||||
matrix_world.translation.z = self.container_obj.location.z - self.depth
|
||||
if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id:
|
||||
matrix_world.translation.z = self.collection_obj.location.z - self.depth
|
||||
else:
|
||||
matrix_world.translation.z -= self.depth
|
||||
obj.matrix_world = Matrix.Rotation(self.x_angle, 4, "X") @ matrix_world
|
||||
bpy.context.view_layer.update()
|
||||
self.collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.spatial as core
|
||||
import blenderbim.core.type
|
||||
|
||||
|
||||
class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -32,15 +35,37 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"select the right space collection and run the operator"
|
||||
)
|
||||
|
||||
# @classmethod
|
||||
# def poll(cls, context):
|
||||
# print(context)
|
||||
# collection = context.view_layer.active_layer_collection.collection
|
||||
# collection_obj = collection.BIMCollectionProperties.obj
|
||||
# active_obj = context.active_object
|
||||
# element = tool.Ifc.get_entity(active_obj)
|
||||
# return tool.Ifc.get_entity(collection_obj) and not element.is_a("IfcWall")
|
||||
|
||||
def _execute(self, context):
|
||||
# This works as a 2.5 extruded polygon based on a cutting plane. Note
|
||||
# that rooms exclude walls (i.e. not to wall midpoint or exterior /
|
||||
# exterior edge.
|
||||
|
||||
try:
|
||||
core.generate_space(tool.Ifc, tool.Model, tool.Root, tool.Spatial, tool.Type)
|
||||
except core.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the space in.")
|
||||
def msg_no_collection(self, context):
|
||||
self.layout.label(text="NO ACTIVE COLLECTION. PLEASE SELECT A SPATIAL COLLECTION OBJECT OR A WALL")
|
||||
|
||||
def msg_no_active_storey(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY. PLEASE SELECT A SPATIAL COLLECTION OBJECT")
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
if not collection_obj:
|
||||
context.window_manager.popup_menu(msg_no_collection, title="Error", icon="ERROR")
|
||||
return
|
||||
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||
if not spatial_element or not spatial_element.is_a("IfcBuildingStorey"):
|
||||
context.window_manager.popup_menu(msg_no_active_storey, title="Error", icon="ERROR")
|
||||
return
|
||||
|
||||
core.generate_space(tool.Ifc, tool.Spatial, tool.Model, tool.Type)
|
||||
|
||||
|
||||
class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -49,6 +74,13 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Generate spaces from selected walls. The active object must be a wall"
|
||||
|
||||
# @classmethod
|
||||
# def poll(cls, context):
|
||||
# active_obj = context.active_object
|
||||
# element = tool.Ifc.get_entity(active_obj)
|
||||
# if element:
|
||||
# return context.selected_objects and element.is_a("IfcWall")
|
||||
|
||||
def _execute(self, context):
|
||||
# This only works based on a 2D plan only considering the standard
|
||||
# walls (i.e. prismatic) in the active object storey.
|
||||
|
||||
@@ -218,6 +218,8 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator):
|
||||
mesh = bpy.data.meshes.new("IfcStairFlight")
|
||||
obj = bpy.data.objects.new("StairFlight", mesh)
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -34,7 +34,7 @@ import blenderbim.core.root
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import pi, sin, cos, degrees
|
||||
from math import pi, sin, cos, degrees, radians
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Optional
|
||||
@@ -449,10 +449,8 @@ class DumbWallGenerator:
|
||||
self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Plan", "Axis", "GRAPH_VIEW")
|
||||
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
self.container = None
|
||||
if container := tool.Root.get_default_container():
|
||||
self.container = container
|
||||
self.container_obj = tool.Ifc.get_object(container)
|
||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
self.collection_obj = self.collection.BIMCollectionProperties.obj
|
||||
self.width = self.layers["thickness"]
|
||||
self.height = props.extrusion_depth
|
||||
self.length = props.length
|
||||
@@ -542,12 +540,11 @@ class DumbWallGenerator:
|
||||
def derive_from_cursor(self):
|
||||
RAYCAST_PRECISION = 0.01
|
||||
self.location = bpy.context.scene.cursor.location
|
||||
if self.container:
|
||||
for subelement in ifcopenshell.util.element.get_decomposition(self.container):
|
||||
if not subelement.is_a("IfcWall"):
|
||||
if self.collection:
|
||||
for sibling_obj in self.collection.objects:
|
||||
if not isinstance(sibling_obj.data, bpy.types.Mesh):
|
||||
continue
|
||||
sibling_obj = tool.Ifc.get_object(subelement)
|
||||
if not sibling_obj or not isinstance(sibling_obj.data, bpy.types.Mesh):
|
||||
if "IfcWall" not in sibling_obj.name:
|
||||
continue
|
||||
inv_obj_matrix = sibling_obj.matrix_world.inverted()
|
||||
local_location = inv_obj_matrix @ self.location
|
||||
@@ -597,10 +594,11 @@ class DumbWallGenerator:
|
||||
|
||||
matrix_world = Matrix.Rotation(self.rotation, 4, "Z")
|
||||
matrix_world.translation = self.location
|
||||
if self.container_obj:
|
||||
matrix_world.translation.z = self.container_obj.location.z + props.rl1
|
||||
if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id:
|
||||
matrix_world.translation.z = self.collection_obj.location.z + props.rl1
|
||||
obj.matrix_world = matrix_world
|
||||
bpy.context.view_layer.update()
|
||||
self.collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -402,6 +402,8 @@ class BIM_OT_add_window(bpy.types.Operator, tool.Ifc.Operator):
|
||||
mesh = bpy.data.meshes.new("IfcWindow")
|
||||
obj = bpy.data.objects.new("IfcWindow", mesh)
|
||||
obj.location = spawn_location
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcWindow", should_add_representation=False
|
||||
|
||||
@@ -30,7 +30,6 @@ import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.shape
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
@@ -48,7 +47,6 @@ from math import radians
|
||||
from pathlib import Path
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy.app.handlers import persistent
|
||||
from ifcopenshell.geom import ShapeElementType, ShapeType
|
||||
from blenderbim.bim.module.project.data import LinksData
|
||||
from blenderbim.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator
|
||||
from typing import Union
|
||||
@@ -124,7 +122,8 @@ class CreateProject(bpy.types.Operator):
|
||||
bpy.data.meshes.remove(mesh)
|
||||
for mat in bpy.data.materials:
|
||||
bpy.data.materials.remove(mat)
|
||||
core.create_project(tool.Ifc, tool.Project, tool.Spatial, schema=props.export_schema, template=template)
|
||||
core.create_project(tool.Ifc, tool.Project, schema=props.export_schema, template=template)
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
tool.Blender.register_toolbar()
|
||||
|
||||
def rollback(self, data):
|
||||
@@ -745,7 +744,7 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMProjectProperties
|
||||
self.file = IfcStore.get_file()
|
||||
blenderbim.bim.schema.reload(self.file.schema_identifier)
|
||||
blenderbim.bim.schema.reload(self.file.schema)
|
||||
start = time.time()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log")
|
||||
@@ -1455,22 +1454,24 @@ class LoadLinkedProject(bpy.types.Operator):
|
||||
break
|
||||
return {"FINISHED"}
|
||||
|
||||
def is_local(self, shape: ShapeElementType) -> bool:
|
||||
m = shape.transformation.matrix
|
||||
def is_local(self, shape):
|
||||
m = shape.transformation.matrix.data
|
||||
if max([abs(co) for co in (m[9], m[10], m[11])]) > 1000:
|
||||
return False
|
||||
elif max([abs(co) for co in shape.geometry.verts[0:3]]) > 1000:
|
||||
return False
|
||||
return True
|
||||
|
||||
def process_occurrence(self, shape: ShapeElementType) -> None:
|
||||
def process_occurrence(self, shape):
|
||||
element = self.file.by_id(shape.id)
|
||||
matrix = shape.transformation.matrix.data
|
||||
faces = shape.geometry.faces
|
||||
verts = shape.geometry.verts
|
||||
materials = shape.geometry.materials
|
||||
material_ids = shape.geometry.material_ids
|
||||
|
||||
mat = ifcopenshell.util.shape.get_shape_matrix(shape)
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1]))
|
||||
|
||||
mesh = self.meshes.get(shape.geometry.id, None)
|
||||
if not mesh:
|
||||
|
||||
@@ -125,6 +125,15 @@ class BIMProjectProperties(PropertyGroup):
|
||||
library_breadcrumb: CollectionProperty(name="Library Breadcrumb", type=StrProperty)
|
||||
library_elements: CollectionProperty(name="Library Elements", type=LibraryElement)
|
||||
active_library_element_index: IntProperty(name="Active Library Element Index")
|
||||
collection_mode: bpy.props.EnumProperty(
|
||||
items=[
|
||||
("DECOMPOSITION", "Decomposition", "Collections represent aggregates and spatial containers"),
|
||||
("SPATIAL_DECOMPOSITION", "Spatial Decomposition", "Collections represent spatial containers"),
|
||||
("IFC_CLASS", "IFC Class", "Collections represent IFC class"),
|
||||
("NONE", "None", "No collections are created"),
|
||||
],
|
||||
name="Collection Mode",
|
||||
)
|
||||
filter_mode: bpy.props.EnumProperty(
|
||||
items=[
|
||||
("NONE", "None", "No filtering is performed"),
|
||||
@@ -163,16 +172,8 @@ class BIMProjectProperties(PropertyGroup):
|
||||
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.001)
|
||||
angular_tolerance: FloatProperty(name="Angular Tolerance", default=0.5)
|
||||
void_limit: IntProperty(name="Void Limit", default=30)
|
||||
distance_limit: FloatProperty(name="Distance Limit", default=1000, subtype="DISTANCE")
|
||||
false_origin: StringProperty(
|
||||
name="False Origin",
|
||||
description=(
|
||||
"False origin that will be used to offset the entire model.\n"
|
||||
"(0,0,0) value is interpreted as an unset false origin - false origin will be guessed based on Distance Limit.\n"
|
||||
"False origin is defined in project units"
|
||||
),
|
||||
default="0,0,0",
|
||||
)
|
||||
distance_limit: FloatProperty(name="Distance Limit", default=1000)
|
||||
false_origin: StringProperty(name="False Origin", default="0,0,0")
|
||||
element_offset: IntProperty(name="Element Offset", default=0)
|
||||
element_limit: IntProperty(name="Element Offset", default=30000)
|
||||
should_disable_undo_on_save: BoolProperty(
|
||||
|
||||
@@ -104,6 +104,7 @@ class BIM_PT_project(Panel):
|
||||
|
||||
def draw_load_ui(self, context):
|
||||
pprops = context.scene.BIMProjectProperties
|
||||
prop_with_search(self.layout, pprops, "collection_mode")
|
||||
prop_with_search(self.layout, pprops, "filter_mode")
|
||||
if pprops.filter_mode in ["DECOMPOSITION", "IFC_CLASS", "IFC_TYPE"]:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -1162,7 +1162,7 @@ def create_shapely_polygon(obj: bpy.types.Object, polygon: bpy.types.MeshPolygon
|
||||
|
||||
def get_gross_element_mesh(element: ifcopenshell.entity_instance) -> bpy.types.Mesh:
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("disable-opening-subtractions", True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
return create_mesh_from_shape(element, settings)
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
@@ -23,8 +24,10 @@ import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.group import ui
|
||||
import blenderbim.core.search as core
|
||||
from itertools import cycle
|
||||
from bpy.types import PropertyGroup, Operator
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
from ifcopenshell import util
|
||||
from blenderbim.bim.prop import ObjProperty, BIMFilterGroup
|
||||
from ifcopenshell.util.selector import Selector
|
||||
from blenderbim.bim.prop import ObjProperty, StrProperty, BIMFilterGroup
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.tool.ifc import Ifc
|
||||
from . import ui, prop, operator
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
|
||||
@@ -37,7 +37,6 @@ classes = (
|
||||
operator.SelectDecomposedElements,
|
||||
operator.SelectProduct,
|
||||
operator.SelectSimilarContainer,
|
||||
operator.SetDefaultContainer,
|
||||
prop.SpatialElement,
|
||||
prop.Element,
|
||||
prop.BIMSpatialProperties,
|
||||
|
||||
@@ -92,19 +92,9 @@ class SpatialDecompositionData:
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {
|
||||
"default_container": cls.default_container(),
|
||||
"subelement_class": cls.subelement_class(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def default_container(cls) -> str:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
if props.default_container:
|
||||
try:
|
||||
return tool.Ifc.get().by_id(props.default_container).Name
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def subelement_class(cls):
|
||||
results = []
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.spatial as core
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.root
|
||||
import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.spatial.data import SpatialData
|
||||
|
||||
|
||||
class ReferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -231,13 +235,4 @@ class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_decomposed_elements(tool.Spatial)
|
||||
|
||||
|
||||
class SetDefaultContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_default_container"
|
||||
bl_label = "Set Default Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
container: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_default_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -131,7 +131,6 @@ class BIMSpatialDecompositionProperties(PropertyGroup):
|
||||
active_element_index: IntProperty(name="Active Element Index")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
|
||||
default_container: IntProperty(name="Default Container", default=0)
|
||||
|
||||
@property
|
||||
def active_container(self):
|
||||
|
||||
@@ -109,34 +109,30 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
SpatialDecompositionData.load()
|
||||
self.props = context.scene.BIMSpatialDecompositionProperties
|
||||
|
||||
if SpatialDecompositionData.data['default_container']:
|
||||
if self.props.active_container:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
text=f"Default: {SpatialDecompositionData.data['default_container']}",
|
||||
text=f"Active: {self.props.active_container.name}",
|
||||
icon="OUTLINER_COLLECTION",
|
||||
)
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Warning: No Default Container", icon="ERROR")
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
|
||||
if self.props.active_container:
|
||||
ifc_definition_id = self.props.active_container.ifc_definition_id if self.props.active_container else 0
|
||||
if self.props.active_container.ifc_class != "IfcProject":
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text=f"Isolate {self.props.active_container.ifc_class}")
|
||||
row.operator("bim.delete_container", icon="X", text="").container = (
|
||||
self.props.active_container.ifc_definition_id
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "subelement_class", text="")
|
||||
op = row.operator("bim.add_part_to_object", icon="ADD", text="")
|
||||
op.element = ifc_definition_id
|
||||
op.element = self.props.active_container.ifc_definition_id
|
||||
op.part_class = self.props.subelement_class
|
||||
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
if self.props.active_container.ifc_class == "IfcProject":
|
||||
row.enabled = False
|
||||
op = row.operator("bim.set_default_container", icon="OUTLINER_COLLECTION", text="Set Default")
|
||||
op.container = ifc_definition_id
|
||||
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text=f"Isolate {self.props.active_container.ifc_class}")
|
||||
op = row.operator("bim.delete_container", icon="X", text="")
|
||||
op.container = ifc_definition_id
|
||||
row.label(text="Warning: No Active Container", icon="ERROR")
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_containers_manager",
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
import os
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.core.spatial
|
||||
|
||||
|
||||
# declaring it here to avoid circular import problems
|
||||
@@ -160,10 +160,7 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.generate_spaces_from_walls()
|
||||
else:
|
||||
try:
|
||||
blenderbim.core.spatial.generate_space(tool.Ifc, tool.Model, tool.Root, tool.Spatial, tool.Type)
|
||||
except blenderbim.core.spatial.NoDefaultContainer:
|
||||
return self.report({"ERROR"}, "Please set a default container to create the space in.")
|
||||
bpy.ops.bim.generate_space()
|
||||
|
||||
def hotkey_S_B(self):
|
||||
bpy.ops.bim.add_boundary()
|
||||
|
||||
@@ -441,11 +441,10 @@ class BIMObjectProperties(PropertyGroup):
|
||||
collection: PointerProperty(type=bpy.types.Collection)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
blender_offset_type: EnumProperty(
|
||||
items=[(o, o, "") for o in ["NONE", "OBJECT_PLACEMENT", "CARTESIAN_POINT", "NOT_APPLICABLE"]],
|
||||
items=[(o, o, "") for o in ["NONE", "OBJECT_PLACEMENT", "CARTESIAN_POINT"]],
|
||||
name="Blender Offset",
|
||||
default="NONE",
|
||||
)
|
||||
cartesian_point_offset: StringProperty(name="Cartesian Point Offset")
|
||||
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
|
||||
is_renaming: BoolProperty(name="Is Renaming", default=False)
|
||||
location_checksum: StringProperty(name="Location Checksum")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
schema_version = "1.0.0"
|
||||
id = "blenderbim"
|
||||
version = "0.0.0"
|
||||
version = "0.0.999999"
|
||||
name = "BlenderBIM"
|
||||
tagline = "Transforms Blender into a native Building Information Model authoring platform using IFC."
|
||||
maintainer = "Dion Moult <dion@thinkmoult.com>"
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
def add_instance_flooring_covering_from_cursor(ifc, spatial, model, Type, geometry):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
element = None
|
||||
relating_type_id = spatial.get_relating_type_id()
|
||||
|
||||
relating_type = None
|
||||
@@ -33,6 +30,8 @@ def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
element = ifc.get_entity(active_obj)
|
||||
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
|
||||
@@ -43,28 +42,25 @@ def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0)
|
||||
name = "Covering"
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name = name, bmesh = bm)
|
||||
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
|
||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
points = spatial.get_scaled_2d_vertices(points)
|
||||
spatial.assign_type_to_obj(obj)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points)
|
||||
body = spatial.get_body_representation(obj)
|
||||
print(obj, body)
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
def add_instance_ceiling_covering_from_cursor(ifc, spatial, model, Type, geometry, covering):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
element = None
|
||||
relating_type_id = spatial.get_relating_type_id()
|
||||
|
||||
relating_type = None
|
||||
@@ -75,6 +71,8 @@ def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
element = ifc.get_entity(active_obj)
|
||||
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
ceiling_height = covering.get_z_from_ceiling_height()
|
||||
@@ -86,30 +84,29 @@ def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0)
|
||||
name = "Covering"
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name = name, bmesh = bm)
|
||||
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
|
||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z + ceiling_height)
|
||||
spatial.traslate_obj_to_z_location(obj, z+ceiling_height)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
points = spatial.get_scaled_2d_vertices(points)
|
||||
spatial.assign_type_to_obj(obj)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points)
|
||||
body = spatial.get_body_representation(obj)
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
|
||||
def regen_selected_covering_object(root, spatial):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
def regen_selected_covering_object(ifc, spatial, model, Type, geometry):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
element = ifc.get_entity(active_obj)
|
||||
|
||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||
|
||||
@@ -119,23 +116,19 @@ def regen_selected_covering_object(root, spatial):
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0)
|
||||
|
||||
name = "Aux"
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name = name, bmesh = bm)
|
||||
mesh = spatial.get_transformed_mesh_from_local_to_global(mesh)
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
points = spatial.get_scaled_2d_vertices(points)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(active_obj, vertices=points)
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(active_obj, vertices = points)
|
||||
body = spatial.get_body_representation(active_obj)
|
||||
spatial.regen_obj_representation(active_obj, body)
|
||||
|
||||
|
||||
# TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS
|
||||
def add_instance_flooring_coverings_from_walls(root, spatial):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
spatial.regen_obj_representation(ifc, geometry, active_obj, body)
|
||||
|
||||
#TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS
|
||||
def add_instance_flooring_coverings_from_walls(ifc, spatial, collector, geometry):
|
||||
z = spatial.get_active_obj_z()
|
||||
union = spatial.get_union_shape_from_selected_objects()
|
||||
for i, linear_ring in enumerate(union.interiors):
|
||||
@@ -143,25 +136,23 @@ def add_instance_flooring_coverings_from_walls(root, spatial):
|
||||
bm = spatial.get_bmesh_from_polygon(poly, h=0)
|
||||
|
||||
name = "Covering" + str(i)
|
||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh=bm)
|
||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh = bm)
|
||||
|
||||
spatial.set_obj_origin_to_bboxcenter(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
points = spatial.get_scaled_2d_vertices(points)
|
||||
|
||||
spatial.assign_type_to_obj(obj)
|
||||
spatial.assign_container_to_obj(obj)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points)
|
||||
body = spatial.get_body_representation(obj)
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
def add_instance_ceiling_coverings_from_walls(root, spatial, covering):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
def add_instance_ceiling_coverings_from_walls(ifc, spatial, collector, geometry, covering):
|
||||
z = covering.get_z_from_ceiling_height() + spatial.get_active_obj_z()
|
||||
union = spatial.get_union_shape_from_selected_objects()
|
||||
for i, linear_ring in enumerate(union.interiors):
|
||||
@@ -169,20 +160,18 @@ def add_instance_ceiling_coverings_from_walls(root, spatial, covering):
|
||||
bm = spatial.get_bmesh_from_polygon(poly, h=0)
|
||||
|
||||
name = "Covering" + str(i)
|
||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh=bm)
|
||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh = bm)
|
||||
|
||||
spatial.set_obj_origin_to_bboxcenter(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
|
||||
points = spatial.get_2d_vertices_from_obj(obj)
|
||||
points = spatial.get_scaled_2d_vertices(points)
|
||||
|
||||
spatial.assign_type_to_obj(obj)
|
||||
spatial.assign_container_to_obj(obj)
|
||||
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points)
|
||||
body = spatial.get_body_representation(obj)
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
class NoDefaultContainer(Exception):
|
||||
pass
|
||||
spatial.regen_obj_representation(ifc, geometry, obj, body)
|
||||
|
||||
@@ -33,7 +33,6 @@ def edit_object_placement(
|
||||
return
|
||||
geometry.clear_cache(element)
|
||||
geometry.clear_scale(obj)
|
||||
geometry.get_blender_offset_type(obj)
|
||||
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
|
||||
geometry.record_object_position(obj)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def create_project(ifc: tool.Ifc, project: tool.Project, spatial: tool.Spatial, schema: str, template: Optional[str] = None) -> None:
|
||||
def create_project(ifc: tool.Ifc, project: tool.Project, schema: str, template: Optional[str] = None) -> None:
|
||||
if ifc.get():
|
||||
return
|
||||
|
||||
@@ -90,9 +90,8 @@ def create_project(ifc: tool.Ifc, project: tool.Project, spatial: tool.Spatial,
|
||||
project.run_aggregate_assign_object(relating_obj=building, related_obj=storey)
|
||||
|
||||
project.set_context(body)
|
||||
spatial.run_spatial_import_spatial_decomposition()
|
||||
if default_container := spatial.guess_default_container():
|
||||
spatial.set_default_container(default_container)
|
||||
project.set_active_spatial_element(storey)
|
||||
project.create_project_collections()
|
||||
|
||||
if template:
|
||||
project.append_all_types_from_template(template)
|
||||
|
||||
@@ -67,7 +67,7 @@ def assign_class(
|
||||
predefined_type: Optional[str] = None,
|
||||
should_add_representation: bool = True,
|
||||
ifc_representation_class: Optional[str] = None,
|
||||
) -> Optional[ifcopenshell.entity_instance]:
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""
|
||||
Args:
|
||||
context: is not optional if `should_add_representation` is True
|
||||
@@ -86,10 +86,6 @@ def assign_class(
|
||||
obj=obj, context=context, ifc_representation_class=ifc_representation_class, profile_set_usage=None
|
||||
)
|
||||
|
||||
if default_container := root.get_default_container():
|
||||
if root.is_spatial_element(element):
|
||||
ifc.run("aggregate.assign_object", products=[element], relating_object=default_container)
|
||||
elif root.is_containable(element):
|
||||
ifc.run("spatial.assign_container", products=[element], relating_structure=default_container)
|
||||
collector.sync(obj)
|
||||
collector.assign(obj)
|
||||
return element
|
||||
|
||||
@@ -1,2 +1,10 @@
|
||||
def show_scene_elements(spatial):
|
||||
spatial.show_scene_objects()
|
||||
|
||||
def search(search, spatial, query, action):
|
||||
products = search.from_selector_query(query)
|
||||
spatial.deselect_objects()
|
||||
try:
|
||||
spatial.filter_products(products, action)
|
||||
except:
|
||||
return "One or More Products could not be found because they are hidden in the ViewLayer"
|
||||
@@ -86,6 +86,7 @@ def copy_to_container(ifc, collector, spatial, obj=None, containers=None):
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
collector.sync(obj)
|
||||
from_container = spatial.get_container(element)
|
||||
if from_container:
|
||||
matrix = spatial.get_relative_object_matrix(obj, ifc.get_object(from_container))
|
||||
@@ -149,10 +150,8 @@ def select_decomposed_elements(spatial):
|
||||
spatial.select_products(spatial.get_decomposed_elements(container))
|
||||
|
||||
|
||||
def generate_space(ifc, model, root, spatial, type):
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
# HERE STARTS SPATIAL TOOL
|
||||
def generate_space(ifc, spatial, model, Type):
|
||||
active_obj = spatial.get_active_obj()
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
element = None
|
||||
@@ -165,17 +164,18 @@ def generate_space(ifc, model, root, spatial, type):
|
||||
relating_type = None
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj) ##mat
|
||||
element = ifc.get_entity(active_obj)
|
||||
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor() ##mat
|
||||
|
||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||
|
||||
if not space_polygon:
|
||||
return
|
||||
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=h)
|
||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=h) ##mat
|
||||
|
||||
mesh = spatial.get_named_mesh_from_bmesh(name="Space", bmesh=bm)
|
||||
|
||||
@@ -190,15 +190,14 @@ def generate_space(ifc, model, root, spatial, type):
|
||||
|
||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
spatial.assign_ifcspace_class_to_obj(obj)
|
||||
|
||||
element = ifc.get_entity(obj)
|
||||
|
||||
if relating_type:
|
||||
spatial.assign_relating_type_to_element(ifc, type, element, relating_type)
|
||||
|
||||
spatial.import_spatial_decomposition()
|
||||
spatial.assign_relating_type_to_element(ifc, Type, element, relating_type)
|
||||
|
||||
|
||||
def generate_spaces_from_walls(ifc, spatial, collector):
|
||||
@@ -217,9 +216,13 @@ def generate_spaces_from_walls(ifc, spatial, collector):
|
||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh=bm)
|
||||
|
||||
spatial.set_obj_origin_to_bboxcenter_and_zero_elevation(obj)
|
||||
spatial.translate_obj_to_z_location(obj, z)
|
||||
spatial.traslate_obj_to_z_location(obj, z)
|
||||
|
||||
spatial.link_obj_to_active_collection(obj)
|
||||
spatial.assign_ifcspace_class_to_obj(obj)
|
||||
|
||||
spatial.assign_container_to_obj(obj)
|
||||
|
||||
|
||||
def toggle_space_visibility(ifc, spatial):
|
||||
model = ifc.get()
|
||||
@@ -235,11 +238,3 @@ def toggle_hide_spaces(ifc, spatial):
|
||||
if not spaces:
|
||||
return
|
||||
spatial.toggle_hide_spaces(spaces)
|
||||
|
||||
|
||||
def set_default_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance):
|
||||
spatial.set_default_container(container)
|
||||
|
||||
|
||||
class NoDefaultContainer(Exception):
|
||||
pass
|
||||
|
||||
@@ -593,6 +593,7 @@ class Owner:
|
||||
class Project:
|
||||
def append_all_types_from_template(cls, template): pass
|
||||
def create_empty(cls, name): pass
|
||||
def create_project_collections(cls): pass
|
||||
def load_default_thumbnails(cls): pass
|
||||
def run_aggregate_assign_object(cls, relating_obj=None, related_obj=None): pass
|
||||
def run_context_add_context(cls, context_type=None, context_identifier=None, target_view=None, parent=None): pass
|
||||
@@ -602,6 +603,7 @@ class Project:
|
||||
def run_owner_set_user(cls, user=None): pass
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def run_unit_assign_scene_units(cls): pass
|
||||
def set_active_spatial_element(cls, obj): pass
|
||||
def set_context(cls, context): pass
|
||||
def set_default_context(cls): pass
|
||||
def set_default_modeling_dimensions(cls): pass
|
||||
@@ -687,15 +689,12 @@ class Root:
|
||||
def copy_representation(cls, source, dest): pass
|
||||
def does_type_have_representations(cls, element): pass
|
||||
def get_decomposition_relationships(cls, objs): pass
|
||||
def get_default_container(cls): pass
|
||||
def get_element_representation(cls, element, context): pass
|
||||
def get_element_type(cls, element): pass
|
||||
def get_object_name(cls, obj): pass
|
||||
def get_object_representation(cls, obj): pass
|
||||
def get_representation_context(cls, representation): pass
|
||||
def is_containable(cls, element): pass
|
||||
def is_element_a(cls, element, ifc_class): pass
|
||||
def is_spatial_element(cls, element): pass
|
||||
def link_object_data(cls, source_obj, destination_obj): pass
|
||||
def recreate_decompositions(cls, relationships, old_to_new): pass
|
||||
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
||||
@@ -708,7 +707,7 @@ class Selector:
|
||||
|
||||
@interface
|
||||
class Search:
|
||||
pass
|
||||
def from_selector_query(cls, query): pass
|
||||
|
||||
@interface
|
||||
class Sequence:
|
||||
@@ -832,7 +831,7 @@ class Spatial:
|
||||
def can_reference(cls, structure, element): pass
|
||||
def contract_container(cls, container): pass
|
||||
def copy_xy(cls, src_obj, destination_obj): pass
|
||||
def import_spatial_element(cls, element, level_index): pass
|
||||
def import_spatial_structure(cls, element, level_index): pass
|
||||
def deselect_objects(cls): pass
|
||||
def disable_editing(cls, obj): pass
|
||||
def duplicate_object_and_data(cls, obj): pass
|
||||
@@ -852,12 +851,10 @@ class Spatial:
|
||||
def import_spatial_decomposition(cls): pass
|
||||
def run_root_copy_class(cls, obj=None): pass
|
||||
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass
|
||||
def run_spatial_import_spatial_decomposition(cls): pass
|
||||
def select_object(cls, obj): pass
|
||||
def select_products(cls, products, unhide=False): pass
|
||||
def set_active_object(cls, obj): pass
|
||||
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
|
||||
def set_default_container(cls, container): pass
|
||||
def show_scene_objects(cls): pass
|
||||
#HERE STARTS SPATIAL TOOL
|
||||
def is_bounding_class(cls, visible_element): pass
|
||||
@@ -889,7 +886,8 @@ class Spatial:
|
||||
def get_active_obj_z(cls): pass
|
||||
def get_active_obj_height(cls): pass
|
||||
def get_relating_type_id(cls): pass
|
||||
def translate_obj_to_z_location(cls, obj): pass
|
||||
def traslate_obj_to_z_location(cls, obj): pass
|
||||
def link_obj_to_active_collection(cls, obj): pass
|
||||
def get_2d_vertices_from_obj(cls, obj): pass
|
||||
def get_scaled_2d_vertices(cls, points): pass
|
||||
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): pass
|
||||
|
||||
@@ -17,66 +17,255 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.tool as tool
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Collector(blenderbim.core.tool.Collector):
|
||||
@classmethod
|
||||
def assign(cls, obj: bpy.types.Object) -> None:
|
||||
"""Links an object to an appropriate Blender collection."""
|
||||
def sync(cls, obj: bpy.types.Object, skip_unlinking=False) -> None:
|
||||
"""Sync object IFC state (assigned containter / aggregate) with the collection it's currently in.
|
||||
|
||||
Then subsequently run `Collector.assign` (if state has changed)
|
||||
to link them to collections / unlink from anything unrelated collections.
|
||||
|
||||
If `skip_unlinking` is `True` then method won't try to assign parent object
|
||||
if it's already assigned in IFC saving some time.
|
||||
But it has a downside not unlinking object from unrelated collections.
|
||||
"""
|
||||
# This is the reverse of assign. It reads the Blender collection and figures out its IFC hierarchy
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
if (
|
||||
not element
|
||||
or element.is_a("IfcProject")
|
||||
or element.is_a("IfcGridAxis")
|
||||
or element.is_a("IfcOpeningElement")
|
||||
):
|
||||
return
|
||||
|
||||
if not obj.users_collection:
|
||||
return
|
||||
|
||||
# create related collections
|
||||
cls._get_own_collection(element, obj)
|
||||
cls._get_collection(element, obj)
|
||||
|
||||
parent_collection = None
|
||||
|
||||
for collection in obj.users_collection:
|
||||
if parent_collection:
|
||||
break
|
||||
# skip Types and non-BIM collections
|
||||
if not collection.BIMCollectionProperties.obj:
|
||||
continue
|
||||
# for objects that own collections we search for the first parent collection
|
||||
if collection.BIMCollectionProperties.obj == obj:
|
||||
collection_name = collection.name
|
||||
for bpy_collection in bpy.data.collections:
|
||||
if bpy_collection.children.get(collection_name) and bpy_collection.BIMCollectionProperties.obj:
|
||||
parent_collection = bpy_collection
|
||||
parent_obj = bpy_collection.BIMCollectionProperties.obj
|
||||
break
|
||||
else:
|
||||
parent_collection = collection
|
||||
parent_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
if not parent_collection:
|
||||
return
|
||||
|
||||
parent = tool.Ifc.get_entity(parent_obj)
|
||||
if skip_unlinking:
|
||||
previous_parent = ifcopenshell.util.element.get_container(
|
||||
element, should_get_direct=True
|
||||
) or ifcopenshell.util.element.get_aggregate(element)
|
||||
if parent == previous_parent:
|
||||
return
|
||||
|
||||
if parent:
|
||||
# This is lazy, but works. One of these will succeed, the other will fail silently.
|
||||
blenderbim.core.spatial.assign_container(
|
||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
|
||||
)
|
||||
# NOTE: won't allow assigning IfcElements to the IfcProject directly
|
||||
# and some elements might get missing in other viewers if they're don't support displaying
|
||||
# elements without hierarchy
|
||||
try:
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj
|
||||
)
|
||||
except blenderbim.core.aggregate.IncompatibleAggregateError:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def assign(cls, obj: bpy.types.Object) -> None:
|
||||
"""link object and it's owned collection to the proper collection
|
||||
and unlink them from any other"""
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
object_collection = None
|
||||
collection_collection = None
|
||||
|
||||
object_collection = cls._get_own_collection(element, obj)
|
||||
if object_collection:
|
||||
collection_collection = cls._get_collection(element, obj)
|
||||
else:
|
||||
object_collection = cls._get_collection(element, obj)
|
||||
|
||||
# NOTE: calling `obj.users_collection` is expensive in large projects
|
||||
# since it's iterating over all collections under the hood
|
||||
|
||||
# ensure object is linked only to object_collection
|
||||
if obj.users_collection != (object_collection,):
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.unlink(obj)
|
||||
if object_collection is not None:
|
||||
object_collection.objects.link(obj)
|
||||
|
||||
# ensure object_collection is linked only to collection_collection
|
||||
if collection_collection and collection_collection.children.find(object_collection.name) == -1:
|
||||
if bpy.context.scene.collection.children.find(object_collection.name) != -1:
|
||||
bpy.context.scene.collection.children.unlink(object_collection)
|
||||
for collection in bpy.data.collections:
|
||||
if collection.children.find(object_collection.name) != -1:
|
||||
collection.children.unlink(object_collection)
|
||||
collection_collection.children.link(object_collection)
|
||||
|
||||
# If an aggregate or nested host loses all its children, it no longer needs its own collection
|
||||
if obj.BIMObjectProperties.collection and obj.BIMObjectProperties.collection != object_collection:
|
||||
bpy.data.collections.remove(obj.BIMObjectProperties.collection)
|
||||
|
||||
@classmethod
|
||||
def _get_own_collection(
|
||||
cls, element: ifcopenshell.entity_instance, obj: bpy.types.Object
|
||||
) -> Union[bpy.types.Collection, None]:
|
||||
"""get or create own collection for the element if it's neccessary for it's type"""
|
||||
if element.is_a("IfcProject"):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
if element.is_a("IfcSpatialStructureElement"):
|
||||
return cls._create_own_collection(obj)
|
||||
else:
|
||||
if element.is_a("IfcSpatialStructureElement") or element.is_a("IfcExternalSpatialStructureElement"):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if element.is_a("IfcGrid"):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if element.is_a("IfcGridAxis"):
|
||||
element = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
||||
if element.PartOfU:
|
||||
grid = element.PartOfU[0]
|
||||
axes = "UAxes"
|
||||
elif element.PartOfV:
|
||||
grid = element.PartOfV[0]
|
||||
axes = "VAxes"
|
||||
elif element.PartOfW:
|
||||
grid = element.PartOfW[0]
|
||||
axes = "WAxes"
|
||||
grid_obj = tool.Ifc.get_object(grid)
|
||||
if grid_obj:
|
||||
grid_col = cls._get_own_collection(grid, grid_obj)
|
||||
axes_col = next((c for c in grid_col.children if axes in c.name), None)
|
||||
return axes_col or bpy.data.collections.new(axes)
|
||||
|
||||
if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if element.is_a("IfcStructuralMember"):
|
||||
return bpy.data.collections.get("Members") or bpy.data.collections.new("Members")
|
||||
|
||||
if element.is_a("IfcStructuralConnection"):
|
||||
return bpy.data.collections.get("Connections") or bpy.data.collections.new("Connections")
|
||||
|
||||
if getattr(element, "IsDecomposedBy", None):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if getattr(element, "IsNestedBy", None):
|
||||
if any(e for e in element.IsNestedBy[0].RelatedObjects if not e.is_a("IfcPort")):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if getattr(element, "HasSurfaceFeatures", None):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
@classmethod
|
||||
def _get_collection(cls, element: ifcopenshell.entity_instance, obj: bpy.types.Object) -> bpy.types.Collection:
|
||||
"""get or create collection for the element based on it's type"""
|
||||
if element.is_a("IfcTypeObject"):
|
||||
return cls._create_project_child_collection("Types")
|
||||
|
||||
if element.is_a("IfcOpeningElement"):
|
||||
return cls._create_project_child_collection("IfcOpeningElements")
|
||||
|
||||
if element.is_a("IfcGridAxis"):
|
||||
if element.PartOfU:
|
||||
grid = element.PartOfU[0]
|
||||
axes = "UAxes"
|
||||
elif element.PartOfV:
|
||||
grid = element.PartOfV[0]
|
||||
axes = "VAxes"
|
||||
elif element.PartOfW:
|
||||
grid = element.PartOfW[0]
|
||||
axes = "WAxes"
|
||||
grid_obj = tool.Ifc.get_object(grid)
|
||||
if grid_obj:
|
||||
return grid_obj.BIMObjectProperties.collection
|
||||
|
||||
if element.is_a("IfcAnnotation"):
|
||||
if element.ObjectType == "DRAWING":
|
||||
return cls._create_project_child_collection("Views")
|
||||
for rel in element.HasAssignments or []:
|
||||
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
|
||||
for related_object in rel.RelatedObjects:
|
||||
if related_object.is_a("IfcAnnotation") and related_object.ObjectType == "DRAWING":
|
||||
drawing_obj = tool.Ifc.get_object(related_object)
|
||||
if drawing_obj:
|
||||
return drawing_obj.BIMObjectProperties.collection
|
||||
|
||||
if element.is_a("IfcStructuralItem"):
|
||||
return cls._create_project_child_collection("StructuralItems")
|
||||
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
if aggregate:
|
||||
aggregate_obj = tool.Ifc.get_object(aggregate)
|
||||
if aggregate_obj:
|
||||
collection = aggregate_obj.BIMObjectProperties.collection
|
||||
if collection:
|
||||
return collection
|
||||
|
||||
nest = ifcopenshell.util.element.get_nest(element)
|
||||
if nest:
|
||||
nest_obj = tool.Ifc.get_object(nest)
|
||||
if nest_obj:
|
||||
collection = nest_obj.BIMObjectProperties.collection
|
||||
if collection:
|
||||
return collection
|
||||
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if container:
|
||||
container_obj = tool.Ifc.get_object(container)
|
||||
collection = container_obj.BIMObjectProperties.collection
|
||||
if collection:
|
||||
return collection
|
||||
|
||||
if element.is_a("IfcSurfaceFeature") and element.file.schema == "IFC4X3":
|
||||
adherend = element.AdheresToElement[0].RelatingElement
|
||||
adherend_obj = tool.Ifc.get_object(adherend)
|
||||
collection = adherend_obj.BIMObjectProperties.collection
|
||||
if collection:
|
||||
return collection
|
||||
|
||||
if element.is_a("IfcProject"):
|
||||
if collection := cls._create_own_collection(obj):
|
||||
collection.objects.link(obj)
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
elif element.is_a("IfcTypeProduct"):
|
||||
collection = cls._create_project_child_collection("IfcTypeProduct")
|
||||
collection.objects.link(obj)
|
||||
elif element.is_a("IfcOpeningElement"):
|
||||
collection = cls._create_project_child_collection("IfcOpeningElement")
|
||||
collection.objects.link(obj)
|
||||
elif element.is_a("IfcStructuralItem"):
|
||||
collection = cls._create_project_child_collection("IfcStructuralItem")
|
||||
collection.objects.link(obj)
|
||||
elif tool.Ifc.get_schema() == "IFC2X3" and element.is_a("IfcSpatialStructureElement"):
|
||||
if collection := cls._create_own_collection(obj):
|
||||
collection.objects.link(obj)
|
||||
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
|
||||
project_obj.BIMObjectProperties.collection.children.link(collection)
|
||||
elif tool.Ifc.get_schema() != "IFC2X3" and element.is_a("IfcSpatialElement"):
|
||||
if collection := cls._create_own_collection(obj):
|
||||
collection.objects.link(obj)
|
||||
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
|
||||
project_obj.BIMObjectProperties.collection.children.link(collection)
|
||||
elif container := ifcopenshell.util.element.get_container(element):
|
||||
container_obj = tool.Ifc.get_object(container)
|
||||
if not (collection := container_obj.BIMObjectProperties.collection):
|
||||
cls.assign(container_obj)
|
||||
collection = container_obj.BIMObjectProperties.collection
|
||||
collection.objects.link(obj)
|
||||
elif element.is_a("IfcAnnotation"):
|
||||
if element.ObjectType == "DRAWING":
|
||||
if collection := cls._create_own_collection(obj):
|
||||
collection.objects.link(obj)
|
||||
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
|
||||
project_obj.BIMObjectProperties.collection.children.link(collection)
|
||||
else:
|
||||
for rel in element.HasAssignments or []:
|
||||
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
|
||||
for related_object in rel.RelatedObjects:
|
||||
if related_object.is_a("IfcAnnotation") and related_object.ObjectType == "DRAWING":
|
||||
drawing_obj = tool.Ifc.get_object(related_object)
|
||||
if drawing_obj:
|
||||
drawing_obj.BIMObjectProperties.collection.objects.link(obj)
|
||||
else:
|
||||
collection = cls._create_project_child_collection("Unsorted")
|
||||
collection.objects.link(obj)
|
||||
return bpy.context.scene.collection
|
||||
|
||||
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
|
||||
if project_obj:
|
||||
return project_obj.BIMObjectProperties.collection
|
||||
|
||||
@classmethod
|
||||
def _create_project_child_collection(cls, name: str) -> bpy.types.Collection:
|
||||
@@ -93,7 +282,7 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
def _create_own_collection(cls, obj: bpy.types.Object) -> bpy.types.Collection:
|
||||
"""get or create own collection for the element"""
|
||||
if obj.BIMObjectProperties.collection:
|
||||
return
|
||||
return obj.BIMObjectProperties.collection
|
||||
collection = bpy.data.collections.new(obj.name)
|
||||
obj.BIMObjectProperties.collection = collection
|
||||
collection.BIMCollectionProperties.obj = obj
|
||||
|
||||
@@ -39,7 +39,6 @@ import ifcopenshell.geom
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.shape
|
||||
import blenderbim.bim.import_ifc
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
@@ -645,6 +644,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
@classmethod
|
||||
def import_drawing(cls, drawing: ifcopenshell.entity_instance) -> bpy.types.Object:
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
|
||||
camera_type = "ORTHO"
|
||||
body = ifcopenshell.util.representation.get_representation(drawing, "Model", "Body", "MODEL_VIEW")
|
||||
@@ -695,7 +695,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
cls.import_camera_props(drawing, obj)
|
||||
tool.Ifc.link(drawing, obj)
|
||||
|
||||
mat = Matrix(ifcopenshell.util.shape.get_shape_matrix(shape))
|
||||
m = shape.transformation.matrix.data
|
||||
mat = mathutils.Matrix(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
obj.matrix_world = mat
|
||||
|
||||
if cls.get_drawing_target_view(drawing) == "REFLECTED_PLAN_VIEW":
|
||||
|
||||
@@ -40,7 +40,7 @@ import blenderbim.bim.import_ifc
|
||||
from math import radians, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from typing import Union, Iterable, Optional
|
||||
from typing import Union, Iterable
|
||||
|
||||
|
||||
class Geometry(blenderbim.core.tool.Geometry):
|
||||
@@ -150,7 +150,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
tool.Blender.remove_data_block(obj.data)
|
||||
|
||||
if is_spatial:
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
blenderbim.core.spatial.load_container_manager(tool.Spatial)
|
||||
try:
|
||||
obj.name
|
||||
bpy.data.objects.remove(obj)
|
||||
@@ -514,7 +514,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("weld-vertices", True)
|
||||
settings.set(settings.WELD_VERTICES, True)
|
||||
context = representation.ContextOfItems
|
||||
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
@@ -522,20 +522,20 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
except:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, representation)
|
||||
else:
|
||||
if not apply_openings:
|
||||
settings.set("disable-opening-subtractions", True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
|
||||
if context.ContextIdentifier == "Body" and context.TargetView == "MODEL_VIEW":
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(settings, element, representation)
|
||||
except:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element, representation)
|
||||
else:
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element, representation)
|
||||
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(ifc_import_settings)
|
||||
@@ -779,7 +779,7 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
# Filter out unique meshes to avoid
|
||||
# reloading the same representation multiple times.
|
||||
meshes_to_objects: dict[bpy.types.Mesh, bpy.types.Object]
|
||||
meshes_to_objects = {(obj := tool.Ifc.get_object(element)).data: obj for element in elements}
|
||||
meshes_to_objects = {(obj:=tool.Ifc.get_object(element)).data: obj for element in elements}
|
||||
|
||||
for obj in meshes_to_objects.values():
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
@@ -982,11 +982,3 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
def delete_opening_object_placement(cls, placement):
|
||||
model = tool.Ifc.get()
|
||||
ifcopenshell.util.element.remove_deep2(model, placement)
|
||||
|
||||
@classmethod
|
||||
def get_blender_offset_type(cls, obj: bpy.types.Object) -> Optional[str]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
if (result := obj.BIMObjectProperties.blender_offset_type) == "NONE":
|
||||
result = obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
return result
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import re
|
||||
import bpy
|
||||
import bmesh
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
@@ -56,12 +55,9 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
return collection
|
||||
|
||||
@classmethod
|
||||
def get_mesh_name(cls, geometry: ifcopenshell.geom.ShapeType) -> str:
|
||||
def get_mesh_name(cls, geometry) -> str:
|
||||
representation_id = geometry.id
|
||||
if "-" in representation_id:
|
||||
# Example: 2432-openings-2468, where
|
||||
# 2432 is mapped representation id
|
||||
# and 2468 is IFCRELVOIDSELEMENT
|
||||
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
|
||||
else:
|
||||
representation_id = int(re.sub(r"\D", "", representation_id))
|
||||
|
||||
@@ -36,7 +36,7 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
) -> None:
|
||||
"""generates image based on `profile` using `PIL.ImageDraw`"""
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile)
|
||||
verts = shape.verts
|
||||
edges = shape.edges
|
||||
|
||||
@@ -49,6 +49,11 @@ class Project(blenderbim.core.tool.Project):
|
||||
def create_empty(cls, name):
|
||||
return bpy.data.objects.new(name, None)
|
||||
|
||||
@classmethod
|
||||
def create_project_collections(cls):
|
||||
tool.Loader.create_project_collection("Views")
|
||||
tool.Loader.create_project_collection("Types")
|
||||
|
||||
@classmethod
|
||||
def load_default_thumbnails(cls):
|
||||
if tool.Ifc.get().by_type("IfcElementType"):
|
||||
@@ -120,6 +125,22 @@ class Project(blenderbim.core.tool.Project):
|
||||
def run_unit_assign_scene_units(cls):
|
||||
return blenderbim.core.unit.assign_scene_units(tool.Ifc, tool.Unit)
|
||||
|
||||
@classmethod
|
||||
def set_active_spatial_element(cls, obj):
|
||||
collection = obj.BIMObjectProperties.collection
|
||||
queue = [bpy.context.view_layer.layer_collection]
|
||||
layer_collection = None
|
||||
|
||||
while queue:
|
||||
layer = queue.pop()
|
||||
if layer.collection == collection:
|
||||
layer_collection = layer
|
||||
break
|
||||
queue.extend(list(layer.children))
|
||||
|
||||
if layer_collection:
|
||||
bpy.context.view_layer.active_layer_collection = layer_collection
|
||||
|
||||
@classmethod
|
||||
def set_context(cls, context):
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
@@ -23,12 +23,13 @@ import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.material
|
||||
import blenderbim.core.style
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
@@ -97,16 +98,6 @@ class Root(blenderbim.core.tool.Root):
|
||||
relationships[element] = {"type": "fill", "element": building}
|
||||
return relationships
|
||||
|
||||
@classmethod
|
||||
def get_default_container(cls) -> Optional[ifcopenshell.entity_instance]:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
if container := props.default_container:
|
||||
try:
|
||||
return tool.Ifc.get().by_id(container)
|
||||
except:
|
||||
props.default_container = 0
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_connection_relationships(
|
||||
cls, objs: list[bpy.types.Object]
|
||||
@@ -171,20 +162,10 @@ class Root(blenderbim.core.tool.Root):
|
||||
def get_representation_context(cls, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return representation.ContextOfItems
|
||||
|
||||
@classmethod
|
||||
def is_containable(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return element.is_a("IfcElement") or element.is_a("IfcGrid")
|
||||
|
||||
@classmethod
|
||||
def is_element_a(cls, element: ifcopenshell.entity_instance, ifc_class: str) -> bool:
|
||||
return element.is_a(ifc_class)
|
||||
|
||||
@classmethod
|
||||
def is_spatial_element(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
if tool.Ifc.get().schema == "IFC2X3":
|
||||
return element.is_a("IfcSpatialStructureElement")
|
||||
return element.is_a("IfcSpatialElement")
|
||||
|
||||
@classmethod
|
||||
def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
|
||||
destination_obj.data = source_obj.data
|
||||
|
||||
@@ -2,8 +2,10 @@ import bpy
|
||||
import json
|
||||
import lark
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
from blenderbim.bim.prop import BIMFacet
|
||||
from typing import Union, Literal
|
||||
|
||||
@@ -113,6 +115,11 @@ class Search(blenderbim.core.tool.Search):
|
||||
return value
|
||||
return '"' + value.replace('"', '\\"') + '"'
|
||||
|
||||
@classmethod
|
||||
def from_selector_query(cls, query: str) -> list[ifcopenshell.entity_instance]:
|
||||
"""Returns a list of products from a selector query"""
|
||||
return Selector().parse(tool.Ifc.get(), query)
|
||||
|
||||
|
||||
class ImportFilterQueryTransformer(lark.Transformer):
|
||||
def __init__(self, filter_groups):
|
||||
|
||||
@@ -30,8 +30,8 @@ import blenderbim.tool as tool
|
||||
import json
|
||||
from math import pi
|
||||
from mathutils import Vector, Matrix
|
||||
from shapely import Polygon
|
||||
from typing import Generator, Optional
|
||||
from shapely import Polygon, MultiPolygon
|
||||
from typing import Generator
|
||||
|
||||
|
||||
class Spatial(blenderbim.core.tool.Spatial):
|
||||
@@ -134,10 +134,6 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=structure_obj, element_obj=element_obj
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_spatial_import_spatial_decomposition(cls):
|
||||
return blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
|
||||
@classmethod
|
||||
def select_object(cls, obj):
|
||||
obj.select_set(True)
|
||||
@@ -359,9 +355,9 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
@classmethod
|
||||
def get_boundary_lines_from_context_visible_objects(cls):
|
||||
calculation_rl = bpy.context.scene.BIMModelProperties.rl3
|
||||
container = tool.Root.get_default_container()
|
||||
container_obj = tool.Ifc.get_object(container)
|
||||
cut_point = container_obj.matrix_world.translation.copy() + Vector((0, 0, calculation_rl))
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
cut_point = collection_obj.matrix_world.translation.copy() + Vector((0, 0, calculation_rl))
|
||||
cut_normal = Vector((0, 0, 1))
|
||||
boundary_lines = []
|
||||
|
||||
@@ -419,7 +415,7 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
@classmethod
|
||||
def get_gross_mesh_from_element(cls, visible_element):
|
||||
gross_settings = ifcopenshell.geom.settings()
|
||||
gross_settings.set("disable-opening-subtractions", True)
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
new_mesh = cls.create_mesh_from_shape(ifcopenshell.geom.create_shape(gross_settings, visible_element))
|
||||
return new_mesh
|
||||
|
||||
@@ -449,6 +445,7 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
|
||||
@classmethod
|
||||
def get_x_y_z_h_mat_from_active_obj(cls, active_obj):
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
mat = active_obj.matrix_world
|
||||
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
|
||||
global_bbox_center = mat @ local_bbox_center
|
||||
@@ -461,10 +458,10 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
|
||||
@classmethod
|
||||
def get_x_y_z_h_mat_from_cursor(cls):
|
||||
x, y, z = bpy.context.scene.cursor.location.xyz
|
||||
if container := tool.Root.get_default_container():
|
||||
if container_obj := tool.Ifc.get_object(container):
|
||||
z = container_obj.matrix_world.translation.z
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
x, y = bpy.context.scene.cursor.location.xy
|
||||
z = collection_obj.matrix_world.translation.z
|
||||
mat = Matrix()
|
||||
h = 3
|
||||
return x, y, z, h, mat
|
||||
@@ -521,13 +518,14 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT")
|
||||
prefix = getattr(project_unit, "Prefix", None)
|
||||
|
||||
return ifcopenshell.util.unit.convert(
|
||||
converted_tolerance = ifcopenshell.util.unit.convert(
|
||||
value=tolerance,
|
||||
from_prefix=None,
|
||||
from_unit="METRE",
|
||||
to_prefix=prefix,
|
||||
to_unit=project_unit.Name,
|
||||
)
|
||||
return tolerance
|
||||
|
||||
@classmethod
|
||||
def get_purged_inner_holes_poly(cls, union_geom, min_area):
|
||||
@@ -614,6 +612,7 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
@classmethod
|
||||
def get_transformed_mesh_from_local_to_global(cls, mesh):
|
||||
active_obj = cls.get_active_obj()
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
mat = active_obj.matrix_world
|
||||
mesh.transform(mat.inverted())
|
||||
mesh.update()
|
||||
@@ -666,6 +665,8 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
mat = obj.matrix_world
|
||||
inverted = mat.inverted()
|
||||
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
x, y = bpy.context.scene.cursor.location.xy
|
||||
z = 0
|
||||
|
||||
@@ -703,10 +704,14 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
return relating_type_id
|
||||
|
||||
@classmethod
|
||||
def translate_obj_to_z_location(cls, obj, z):
|
||||
def traslate_obj_to_z_location(cls, obj, z):
|
||||
if z != 0:
|
||||
obj.location = obj.location + Vector((0, 0, z))
|
||||
|
||||
@classmethod
|
||||
def link_obj_to_active_collection(cls, obj):
|
||||
bpy.context.view_layer.active_layer_collection.collection.objects.link(obj)
|
||||
|
||||
@classmethod
|
||||
def get_2d_vertices_from_obj(cls, obj):
|
||||
points = []
|
||||
@@ -747,7 +752,9 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
@classmethod
|
||||
def get_body_representation(cls, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
return ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
model = tool.Ifc.get()
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
return body
|
||||
|
||||
@classmethod
|
||||
def assign_ifcspace_class_to_obj(cls, obj):
|
||||
@@ -768,10 +775,20 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
blenderbim.core.type.assign_type(ifc, Type, element=element, type=relating_type)
|
||||
|
||||
@classmethod
|
||||
def regen_obj_representation(cls, obj, body):
|
||||
def assign_container_to_obj(cls, obj):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
container_obj = tool.Ifc.get_object(container)
|
||||
blenderbim.core.spatial.assign_container(
|
||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=container_obj, element_obj=obj
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def regen_obj_representation(cls, ifc, geometry, obj, body):
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
ifc,
|
||||
geometry,
|
||||
obj=obj,
|
||||
representation=body,
|
||||
should_reload=True,
|
||||
@@ -809,23 +826,3 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
for space in spaces:
|
||||
obj = tool.Ifc.get_object(space)
|
||||
obj.hide_set(False)
|
||||
|
||||
@classmethod
|
||||
def set_default_container(cls, container):
|
||||
bpy.context.scene.BIMSpatialDecompositionProperties.default_container = container.id()
|
||||
|
||||
@classmethod
|
||||
def guess_default_container(cls) -> Optional[ifcopenshell.entity_instance]:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
subelement = None
|
||||
# We try to priorise the first Site > Building > Storey as a convention for vertical projects
|
||||
for subelement in ifcopenshell.util.element.get_parts(project):
|
||||
if subelement.is_a("IfcSite"):
|
||||
for subelement2 in ifcopenshell.util.element.get_parts(subelement):
|
||||
if subelement2.is_a("IfcBuilding"):
|
||||
for subelement3 in ifcopenshell.util.element.get_parts(subelement2):
|
||||
if subelement3.is_a("IfcBuildingStorey"):
|
||||
return subelement3
|
||||
if subelement:
|
||||
return subelement
|
||||
return None
|
||||
|
||||
@@ -28,16 +28,8 @@ class Surveyor(blenderbim.core.tool.Surveyor):
|
||||
def get_absolute_matrix(cls, obj):
|
||||
matrix = np.array(obj.matrix_world)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE":
|
||||
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
if (
|
||||
obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT"
|
||||
and obj.BIMObjectProperties.cartesian_point_offset
|
||||
):
|
||||
offset_x, offset_y, offset_z = map(float, obj.BIMObjectProperties.cartesian_point_offset.split(","))
|
||||
matrix[0][3] -= offset_x
|
||||
matrix[1][3] -= offset_y
|
||||
matrix[2][3] -= offset_z
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.local2global(
|
||||
matrix,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
@@ -40,9 +40,7 @@ copyright = "2020-2024 IfcOpenShell Contributors"
|
||||
author = "IfcOpenShell Contributors"
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(os.path.join(cwd, "..", "..", "..", "VERSION"), "r") as f:
|
||||
release = f.read().strip()
|
||||
release = "0.0.240402"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
@@ -148,7 +148,7 @@ changes don't directly conflict.
|
||||
When two branches have diverged, merging an IFC model requires *conflict
|
||||
resolution* (because added entities may inadvertently reuse the same Step-IDs),
|
||||
this means that data on one side or the other may be rewritten by BlenderBIM in
|
||||
order to accommodate both sets of changes. ie. the merge process is
|
||||
order to accomodate both sets of changes. ie. the merge process is
|
||||
*asymmetrical*. BlenderBIM privileges data in the remote `origin/main` branch
|
||||
over the local working branch, similarly it privileges data in the local `main`
|
||||
branch over any other local working branch. The practical result of this is
|
||||
|
||||
@@ -16,10 +16,12 @@ class BlenderImporter:
|
||||
self.created_guids = set()
|
||||
|
||||
self.settings = ifcopenshell.geom.settings()
|
||||
self.settings.set("mesher-linear-deflection", self.deflection_tolerance)
|
||||
self.settings.set("mesher-angular-deflection", self.angular_tolerance)
|
||||
self.settings.set_deflection_tolerance(self.deflection_tolerance)
|
||||
self.settings.set_angular_tolerance(self.angular_tolerance)
|
||||
self.settings.set(self.settings.STRICT_TOLERANCE, True)
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True)
|
||||
self.settings_2d.set(self.settings.STRICT_TOLERANCE, True)
|
||||
|
||||
self.process_element_filter()
|
||||
self.process_context_filter()
|
||||
@@ -47,7 +49,7 @@ class BlenderImporter:
|
||||
]
|
||||
)
|
||||
if self.body_contexts:
|
||||
self.settings.set("context-ids", self.body_contexts)
|
||||
self.settings.set_context_ids(self.body_contexts)
|
||||
# Annotation is to accommodate broken Revit files
|
||||
# See https://github.com/Autodesk/revit-ifc/issues/187
|
||||
self.plan_contexts = [
|
||||
@@ -114,6 +116,7 @@ class BlenderImporter:
|
||||
|
||||
def get_cache(self):
|
||||
cache_settings = ifcopenshell.geom.settings()
|
||||
cache_settings.set(cache_settings.STRICT_TOLERANCE, True)
|
||||
try:
|
||||
return ifcopenshell.geom.serializers.hdf5(self.cache_path, cache_settings)
|
||||
except:
|
||||
|
||||
@@ -92,6 +92,8 @@ class TestCreateProject:
|
||||
project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called()
|
||||
|
||||
project.set_context("body").should_be_called()
|
||||
project.set_active_spatial_element("storey").should_be_called()
|
||||
project.create_project_collections().should_be_called()
|
||||
|
||||
project.load_default_thumbnails().should_be_called()
|
||||
project.set_default_context().should_be_called()
|
||||
@@ -124,6 +126,8 @@ class TestCreateProject:
|
||||
project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called()
|
||||
|
||||
project.set_context("body").should_be_called()
|
||||
project.set_active_spatial_element("storey").should_be_called()
|
||||
project.create_project_collections().should_be_called()
|
||||
|
||||
project.append_all_types_from_template("template").should_be_called()
|
||||
|
||||
@@ -165,6 +169,8 @@ class TestCreateProject:
|
||||
project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called()
|
||||
|
||||
project.set_context("body").should_be_called()
|
||||
project.set_active_spatial_element("storey").should_be_called()
|
||||
project.create_project_collections().should_be_called()
|
||||
|
||||
project.load_default_thumbnails().should_be_called()
|
||||
project.set_default_context().should_be_called()
|
||||
|
||||
@@ -80,6 +80,7 @@ class TestRemoveContainer:
|
||||
class TestCopyToContainer:
|
||||
def test_run(self, ifc, collector, spatial):
|
||||
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||
collector.sync("obj").should_be_called()
|
||||
spatial.get_container("element").should_be_called().will_return("container")
|
||||
ifc.get_object("container").should_be_called().will_return("container_obj")
|
||||
spatial.get_relative_object_matrix("obj", "container_obj").should_be_called().will_return("matrix")
|
||||
@@ -96,6 +97,7 @@ class TestCopyToContainer:
|
||||
|
||||
def test_using_an_absolute_matrix_if_there_is_no_from_container(self, ifc, collector, spatial):
|
||||
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||
collector.sync("obj").should_be_called()
|
||||
spatial.get_container("element").should_be_called().will_return(None)
|
||||
spatial.get_object_matrix("obj").should_be_called().will_return("matrix")
|
||||
|
||||
|
||||
@@ -87,6 +87,21 @@ class TestRunUnitAssignSceneUnits(NewFile):
|
||||
pass
|
||||
|
||||
|
||||
class TestSetActiveSpatialElement(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Foo", None)
|
||||
collection = bpy.data.collections.new("Foo")
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
collection.objects.link(obj)
|
||||
obj.BIMObjectProperties.collection = collection
|
||||
collection.BIMCollectionProperties.obj = obj
|
||||
|
||||
layer = bpy.context.view_layer.layer_collection.children["Foo"]
|
||||
assert bpy.context.view_layer.active_layer_collection != layer
|
||||
subject.set_active_spatial_element(obj)
|
||||
assert bpy.context.view_layer.active_layer_collection == layer
|
||||
|
||||
|
||||
class TestSetContext(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
|
||||
@@ -22,8 +22,6 @@ TARGET_LINK_LIBRARIES(IfcParseExamples IfcParse)
|
||||
set_target_properties(IfcParseExamples PROPERTIES FOLDER Examples)
|
||||
target_compile_features(IfcParseExamples PUBLIC cxx_std_17)
|
||||
|
||||
if (WITH_OPENCASCADE)
|
||||
|
||||
ADD_EXECUTABLE(IfcOpenHouse IfcOpenHouse.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcOpenHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES})
|
||||
set_target_properties(IfcOpenHouse PROPERTIES FOLDER Examples)
|
||||
@@ -31,15 +29,3 @@ set_target_properties(IfcOpenHouse PROPERTIES FOLDER Examples)
|
||||
ADD_EXECUTABLE(IfcAdvancedHouse IfcAdvancedHouse.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcAdvancedHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES})
|
||||
set_target_properties(IfcAdvancedHouse PROPERTIES FOLDER Examples)
|
||||
|
||||
endif()
|
||||
|
||||
if(SCHEMA_VERSIONS MATCHES "4x3")
|
||||
ADD_EXECUTABLE(IfcAlignment IfcAlignment.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcAlignment ${IFCOPENSHELL_LIBRARIES})
|
||||
set_target_properties(IfcAlignment PROPERTIES FOLDER Examples)
|
||||
|
||||
ADD_EXECUTABLE(IfcSimplifiedAlignment IfcSimplifiedAlignment.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcSimplifiedAlignment ${IFCOPENSHELL_LIBRARIES})
|
||||
set_target_properties(IfcSimplifiedAlignment PROPERTIES FOLDER Examples)
|
||||
endif()
|
||||
|
||||
@@ -38,13 +38,18 @@
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "../ifcparse/macros.h"
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#define IfcSchema Ifc4
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#define IfcSchema Ifc2x3
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
#include "../ifcgeom/Serialization/Serialization.h"
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
#include "../ifcgeom_schema_agnostic/Serialization.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
// This example illustrates the basic of building an alignment model.
|
||||
// The alignment is based on "Bridge Geometry Manual", April 2022
|
||||
// US Department of Transportation, Federal Highway Administration (FHWA)
|
||||
// https://www.fhwa.dot.gov/bridge/pubs/hif22034.pdf
|
||||
//
|
||||
// Sections and page number for this document are cited in the code comments.
|
||||
//
|
||||
// This examples differs from IfcSimplifiedAlignment because it builds the
|
||||
// alignment explicitly
|
||||
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
|
||||
#include "../ifcparse/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <fstream>
|
||||
|
||||
const double PI = boost::math::constants::pi<double>();
|
||||
double to_radian(double deg) { return PI * deg / 180; }
|
||||
|
||||
#define Schema Ifc4x3_add2
|
||||
|
||||
// performs basic project setup including created the IfcProject object
|
||||
// and initializing the project units to FEET
|
||||
Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
std::vector<std::string> file_description;
|
||||
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
|
||||
file.header().file_description().description(file_description);
|
||||
|
||||
auto project = file.addProject();
|
||||
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
|
||||
project->setDescription(std::string("C++ Example"));
|
||||
|
||||
// set up project units for feet
|
||||
// the call to file.addProject() sets up length units as millimeter.
|
||||
auto units_in_context = project->UnitsInContext();
|
||||
auto units = units_in_context->Units();
|
||||
auto begin = units->begin();
|
||||
auto iter = begin;
|
||||
auto end = units->end();
|
||||
for (; iter != end; iter++) {
|
||||
auto unit = *iter;
|
||||
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
|
||||
file.addEntity(dimensions);
|
||||
|
||||
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
|
||||
file.addEntity(conversion_factor);
|
||||
|
||||
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
|
||||
file.addEntity(conversion_based_unit);
|
||||
|
||||
units->remove(unit); // remove the millimeter unit
|
||||
units->push(conversion_based_unit); // add the feet unit
|
||||
units_in_context->setUnits(units); // update the UnitsInContext
|
||||
|
||||
break; // Done!, the length unit was found, so break out of the loop
|
||||
}
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for horizontal alignment tangent runs
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_tangent(typename Schema::IfcCartesianPoint* p, double dir, double length) {
|
||||
// geometry
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({0, 0})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{1.0, 0.0}), 1.0));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{cos(dir), sin(dir)})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(
|
||||
boost::none, boost::none, p, dir, 0.0, 0.0, length, boost::none, Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE);
|
||||
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(
|
||||
IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for horizontal alignment horizonal curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_hcurve(typename Schema::IfcCartesianPoint* pc, double dir, double radius, double lc) {
|
||||
// geometry
|
||||
double sign = radius / fabs(radius);
|
||||
auto parent_curve = new Schema::IfcCircle(
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>({0, 0})), new Schema::IfcDirection(std::vector<double>{1, 0})),
|
||||
fabs(radius));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(pc, new Schema::IfcDirection(std::vector<double>{cos(dir), sin(dir)})),
|
||||
new Schema::IfcLengthMeasure(0.0),
|
||||
new Schema::IfcLengthMeasure(sign * lc),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(boost::none, boost::none, pc, dir, radius, radius, lc, boost::none, Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile gradient runs
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_gradient(typename Schema::IfcCartesianPoint* p, double slope, double length) {
|
||||
// geometry
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({0, 0})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{1, 0}), 1.0));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{sqrt(1 - slope * slope), slope})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], slope, slope, boost::none, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile parabolic vertical curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_vcurve(typename Schema::IfcCartesianPoint* p, double start_slope, double end_slope, double length) {
|
||||
// geometry
|
||||
double A = p->Coordinates()[1];
|
||||
double B = start_slope;
|
||||
double C = (end_slope - start_slope) / (2 * length);
|
||||
|
||||
auto parent_curve = new Schema::IfcPolynomialCurve(
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>{0.0, 0.0}), new Schema::IfcDirection(std::vector<double>{1.0, 0.0})),
|
||||
std::vector<double>{0.0, 1.0},
|
||||
std::vector<double>{A, B, C},
|
||||
boost::none);
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{sqrt(1 - start_slope * start_slope), start_slope})),
|
||||
new Schema::IfcLengthMeasure(0.0),
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
|
||||
// business logic
|
||||
double k = (end_slope - start_slope) / length;
|
||||
auto design_parameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], start_slope, end_slope, 1 / k, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates representations for each IfcAlignmentSegment per CT 4.1.7.1.1.4
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Segments/content.html
|
||||
void create_segment_representations(IfcHierarchyHelper<Schema>& file, Schema::IfcLocalPlacement* global_placement, Schema::IfcGeometricRepresentationSubContext* segment_axis_subcontext, typename aggregate_of<typename Schema::IfcSegment>::ptr curve_segments, typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr segments) {
|
||||
auto cs_iter = curve_segments->begin();
|
||||
auto s_iter = segments->begin();
|
||||
for (; cs_iter != curve_segments->end(); cs_iter++, s_iter++) {
|
||||
auto curve_segment = *cs_iter;
|
||||
auto alignment_segment = (*s_iter)->as<Schema::IfcAlignmentSegment>();
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
representation_items->push(curve_segment);
|
||||
|
||||
auto axis_representation = new Schema::IfcShapeRepresentation(segment_axis_subcontext, std::string("Axis"), std::string("Segment"), representation_items);
|
||||
file.addEntity(axis_representation);
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentation>::ptr representations(new aggregate_of<typename Schema::IfcRepresentation>());
|
||||
representations->push(axis_representation);
|
||||
|
||||
auto product = new Schema::IfcProductDefinitionShape(boost::none, boost::none, representations);
|
||||
file.addEntity(product);
|
||||
|
||||
alignment_segment->setObjectPlacement(global_placement);
|
||||
alignment_segment->setRepresentation(product);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
IfcHierarchyHelper<Schema> file;
|
||||
|
||||
auto project = setup_project(file);
|
||||
|
||||
auto geometric_representation_context = file.getRepresentationContext(std::string("Model")); // creates the representation context if it doesn't already exist
|
||||
|
||||
auto axis_model_representation_subcontext = new Schema::IfcGeometricRepresentationSubContext(std::string("Axis"), std::string("Model"), geometric_representation_context, boost::none, Schema::IfcGeometricProjectionEnum::IfcGeometricProjection_MODEL_VIEW, boost::none);
|
||||
file.addEntity(axis_model_representation_subcontext);
|
||||
|
||||
auto global_placement = file.addLocalPlacement();
|
||||
|
||||
//
|
||||
// Define horizontal alignment
|
||||
//
|
||||
|
||||
// define key points
|
||||
// B.1.4 pg 212
|
||||
auto pob = file.addDoublet<Schema::IfcCartesianPoint>(500, 2500); // beginning
|
||||
auto pc1 = file.addDoublet<Schema::IfcCartesianPoint>(2142.237995, 1436.014820); // Point of curve (PC), Curve #1
|
||||
auto pt1 = file.addDoublet<Schema::IfcCartesianPoint>(3660.446123, 2050.736173); // Point of tangent (PT), Curve #1
|
||||
auto pc2 = file.addDoublet<Schema::IfcCartesianPoint>(4084.115884, 3889.462938); // Point of curve (PC), Curve #2
|
||||
auto pt2 = file.addDoublet<Schema::IfcCartesianPoint>(5469.395067, 4847.566310); // Point of tangent (PT), Curve #2
|
||||
auto pc3 = file.addDoublet<Schema::IfcCartesianPoint>(7019.971367, 4638.286073); // Point of curve (PC), Curve #3
|
||||
auto pt3 = file.addDoublet<Schema::IfcCartesianPoint>(7790.932128, 4006.730765); // Point of tangent (PT), Curve #3
|
||||
auto poe = file.addDoublet<Schema::IfcCartesianPoint>(8480, 2010); // ending
|
||||
|
||||
// define tangent runs and curve lengths
|
||||
double run_1 = 1956.785654;
|
||||
double lc_1 = 1919.222667;
|
||||
double run_2 = 1886.905454;
|
||||
double lc_2 = 1848.115835;
|
||||
double run_3 = 1564.635765;
|
||||
double lc_3 = 1049.119737;
|
||||
double run_4 = 2112.285084;
|
||||
|
||||
// define curve radii
|
||||
double rc_1 = 1000;
|
||||
double rc_2 = -1250; // negative radius for curves to the right
|
||||
double rc_3 = -950;
|
||||
|
||||
// bearing of tangents
|
||||
double angle_1 = to_radian(327.0613);
|
||||
double angle_2 = to_radian(77.0247);
|
||||
double angle_3 = to_radian(352.3133);
|
||||
double angle_4 = to_radian(289.0395);
|
||||
|
||||
// create containers to store the curve segments
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr horizontal_curve_segments(new aggregate_of<typename Schema::IfcSegment>()); // geometry
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr horizontal_segments(new aggregate_of<typename Schema::IfcObjectDefinition>()); // business logic
|
||||
|
||||
//
|
||||
// Build the horizontal alignment segments
|
||||
//
|
||||
|
||||
// POB to PC1
|
||||
auto curve_segment_1 = create_tangent(pob, angle_1, run_1);
|
||||
horizontal_curve_segments->push(curve_segment_1.first);
|
||||
horizontal_segments->push(curve_segment_1.second);
|
||||
|
||||
// Curve 1
|
||||
auto curve_segment_2 = create_hcurve(pc1, angle_1, rc_1, lc_1);
|
||||
horizontal_curve_segments->push(curve_segment_2.first);
|
||||
horizontal_segments->push(curve_segment_2.second);
|
||||
|
||||
// PT1 to PC2
|
||||
auto curve_segment_3 = create_tangent(pt1, angle_2, run_2);
|
||||
horizontal_curve_segments->push(curve_segment_3.first);
|
||||
horizontal_segments->push(curve_segment_3.second);
|
||||
|
||||
// Curve 2
|
||||
auto curve_segment_4 = create_hcurve(pc2, angle_2, rc_2, lc_2);
|
||||
horizontal_curve_segments->push(curve_segment_4.first);
|
||||
horizontal_segments->push(curve_segment_4.second);
|
||||
|
||||
// PT2 to PC3
|
||||
auto curve_segment_5 = create_tangent(pt2, angle_3, run_3);
|
||||
horizontal_curve_segments->push(curve_segment_5.first);
|
||||
horizontal_segments->push(curve_segment_5.second);
|
||||
|
||||
// Curve 3
|
||||
auto curve_segment_6 = create_hcurve(pc3, angle_3, rc_3, lc_3);
|
||||
horizontal_curve_segments->push(curve_segment_6.first);
|
||||
horizontal_segments->push(curve_segment_6.second);
|
||||
|
||||
// PT3 to POE
|
||||
auto curve_segment_7 = create_tangent(pt3, angle_4, run_4);
|
||||
horizontal_curve_segments->push(curve_segment_7.first);
|
||||
horizontal_segments->push(curve_segment_7.second);
|
||||
|
||||
// Zero-length terminator segment
|
||||
auto terminator_segment = create_tangent(poe, angle_4, 0.0);
|
||||
terminator_segment.first->setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
horizontal_curve_segments->push(terminator_segment.first);
|
||||
horizontal_segments->push(terminator_segment.second);
|
||||
|
||||
//
|
||||
// Create the horizontal alignment (IfcAlignmentHorizontal) and nest alignment segments
|
||||
//
|
||||
auto horizontal_alignment = new Schema::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, std::string("Horizontal Alignment"), boost::none, boost::none, nullptr, nullptr);
|
||||
file.addEntity(horizontal_alignment);
|
||||
|
||||
auto nests_horizontal_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments);
|
||||
file.addEntity(nests_horizontal_segments);
|
||||
|
||||
//
|
||||
// Create plan view footprint model representation for the horizontal alignment
|
||||
//
|
||||
|
||||
// start by defining a composite curve composed of the horizonal curve segments
|
||||
auto composite_curve = new Schema::IfcCompositeCurve(horizontal_curve_segments, false /*not self-intersecting*/);
|
||||
file.addEntity(composite_curve);
|
||||
|
||||
// the composite curve is a representation item
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr alignment_representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
alignment_representation_items->push(composite_curve);
|
||||
|
||||
// create the footprint representation
|
||||
auto footprint_shape_representation = new Schema::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items);
|
||||
file.addEntity(footprint_shape_representation);
|
||||
|
||||
//
|
||||
// Define vertical profile segments
|
||||
//
|
||||
|
||||
// create containers to store the curve segments
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr vertical_curve_segments(new aggregate_of<typename Schema::IfcSegment>()); // geometry
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr vertical_segments(new aggregate_of<typename Schema::IfcObjectDefinition>()); // business logic
|
||||
|
||||
// define key profile points
|
||||
auto vpob = file.addDoublet<Schema::IfcCartesianPoint>(0.0, 100.0); // beginning
|
||||
auto vpc1 = file.addDoublet<Schema::IfcCartesianPoint>(1200.0, 121.0); // Vertical Curve Point (VPC), Vertical Curve #1
|
||||
auto vpt1 = file.addDoublet<Schema::IfcCartesianPoint>(2800.0, 127.0); // Vertical Curve Tangent (VPT), Vertical Curve #1
|
||||
auto vpc2 = file.addDoublet<Schema::IfcCartesianPoint>(4400.0, 111.0); // Vertical Curve Point (VPC), Vertical Curve #2
|
||||
auto vpt2 = file.addDoublet<Schema::IfcCartesianPoint>(5600.0, 117.0); // Vertical Curve Tangent (VPT), Vertical Curve #2
|
||||
auto vpc3 = file.addDoublet<Schema::IfcCartesianPoint>(6400.0, 133.0); // Vertical Curve Point (VPC), Vertical Curve #3
|
||||
auto vpt3 = file.addDoublet<Schema::IfcCartesianPoint>(8400.0, 133.0); // Vertical Curve Tangent (VPT), Vertical Curve #3
|
||||
auto vpc4 = file.addDoublet<Schema::IfcCartesianPoint>(9400.0, 113.0); // Vertical Curve Point (VPC), Vertical Curve #4
|
||||
auto vpt4 = file.addDoublet<Schema::IfcCartesianPoint>(10200.0, 103.0); // Vertical Curve Tangent (VPT), Vertical Curve #4
|
||||
auto vpoe = file.addDoublet<Schema::IfcCartesianPoint>(12800.0, 90.0); // ending
|
||||
|
||||
//
|
||||
// Build the vertical alignment segments
|
||||
//
|
||||
|
||||
// Grade start to VPC1
|
||||
auto vertical_profile_segment_1 = create_gradient(vpob, 1.75 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_1.first);
|
||||
vertical_segments->push(vertical_profile_segment_1.second);
|
||||
|
||||
// Vertical Curve 1
|
||||
auto vertical_profile_segment_2 = create_vcurve(vpc1, 1.75 / 100, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_2.first);
|
||||
vertical_segments->push(vertical_profile_segment_2.second);
|
||||
|
||||
// Grade VPT1 to VPC2
|
||||
auto vertical_profile_segment_3 = create_gradient(vpt1, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_3.first);
|
||||
vertical_segments->push(vertical_profile_segment_3.second);
|
||||
|
||||
// Vertical Curve 2
|
||||
auto vertical_profile_segment_4 = create_vcurve(vpc2, -1.0 / 100, 2.0 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_4.first);
|
||||
vertical_segments->push(vertical_profile_segment_4.second);
|
||||
|
||||
// Grade PVT2 to VPC3
|
||||
auto vertical_profile_segment_5 = create_gradient(vpt2, 2.0 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_5.first);
|
||||
vertical_segments->push(vertical_profile_segment_5.second);
|
||||
|
||||
// Vertical Curve 3
|
||||
auto vertical_profile_segment_6 = create_vcurve(vpc3, 2.0 / 100, -2.0 / 100, 2000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_6.first);
|
||||
vertical_segments->push(vertical_profile_segment_6.second);
|
||||
|
||||
// Grade PVT3 to VPC4
|
||||
auto vertical_profile_segment_7 = create_gradient(vpt3, -2.0 / 100, 1000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_7.first);
|
||||
vertical_segments->push(vertical_profile_segment_7.second);
|
||||
|
||||
// Vertical Curve 4
|
||||
auto vertical_profile_segment_8 = create_vcurve(vpc4, -2.0 / 100, -0.5 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_8.first);
|
||||
vertical_segments->push(vertical_profile_segment_8.second);
|
||||
|
||||
// Grade VPT4 to End
|
||||
auto vertical_profile_segment_9 = create_gradient(vpt4, -0.5 / 100, 2600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_9.first);
|
||||
vertical_segments->push(vertical_profile_segment_9.second);
|
||||
|
||||
// Zero-length terminator
|
||||
auto vertical_terminator_segment = create_gradient(vpoe, -0.5 / 100, 0.0);
|
||||
vertical_terminator_segment.first->setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
vertical_curve_segments->push(vertical_terminator_segment.first);
|
||||
vertical_segments->push(vertical_terminator_segment.second);
|
||||
|
||||
//
|
||||
// Create the vertical alignment (IfcAlignmentVertical) and nest alignment segments
|
||||
//
|
||||
auto vertical_profile = new Schema::IfcAlignmentVertical(IfcParse::IfcGlobalId(), nullptr, std::string("Vertical Alignment"), boost::none, boost::none, nullptr, nullptr);
|
||||
file.addEntity(vertical_profile);
|
||||
|
||||
auto nests_vertical_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments);
|
||||
file.addEntity(nests_vertical_segments);
|
||||
|
||||
//
|
||||
// Create profile view axis model representation for the vertical profile
|
||||
//
|
||||
|
||||
// start by defining a gradient curve composed of the vertical curve segments and associated with the horizontal composite curve
|
||||
auto gradient_curve = new Schema::IfcGradientCurve(vertical_curve_segments, false, composite_curve, nullptr);
|
||||
file.addEntity(gradient_curve);
|
||||
|
||||
// the gradient curve is a representation item
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr profile_representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
profile_representation_items->push(gradient_curve);
|
||||
|
||||
// create the axis representation
|
||||
auto axis3d_shape_representation = new Schema::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("Axis"), std::string("Curve3D"), profile_representation_items);
|
||||
file.addEntity(axis3d_shape_representation);
|
||||
|
||||
// create axis representations for each segment
|
||||
create_segment_representations(file, global_placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments);
|
||||
create_segment_representations(file, global_placement, axis_model_representation_subcontext, vertical_curve_segments, vertical_segments);
|
||||
|
||||
//
|
||||
// Create the IfcAlignment
|
||||
//
|
||||
|
||||
// the alignment has two representations, a plan view footprint and a 3d curve
|
||||
typename aggregate_of<typename Schema::IfcRepresentation>::ptr alignment_representations(new aggregate_of<typename Schema::IfcRepresentation>());
|
||||
alignment_representations->push(footprint_shape_representation); // 2D footprint
|
||||
alignment_representations->push(axis3d_shape_representation); // 3D curve
|
||||
|
||||
// create the alignment product definition
|
||||
auto alignment_product = new Schema::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations);
|
||||
|
||||
// create the alignment
|
||||
auto alignment = new Schema::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, std::string("Example Alignment"), boost::none, boost::none, global_placement, alignment_product, boost::none);
|
||||
file.addEntity(alignment);
|
||||
|
||||
// Nest the IfcAlignmentHorizontal and IfcAlignmentVertical with the IfcAlignment to complete the business logic
|
||||
// 4.1.4.4.1 Alignments nest horizontal and vertical layouts
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Nesting/Alignment_Layouts/content.html
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr alignment_layout_list(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
alignment_layout_list->push(horizontal_alignment);
|
||||
alignment_layout_list->push(vertical_profile);
|
||||
|
||||
auto nests_alignment_layouts = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list);
|
||||
file.addEntity(nests_alignment_layouts);
|
||||
|
||||
// Define the relationship with the project
|
||||
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Aggregation/Alignment_Aggregation_To_Project/content.html
|
||||
// IfcProject <-> IfcRelAggregates <-> IfcAlignment
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr list_of_alignments_in_project(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
list_of_alignments_in_project->push(alignment);
|
||||
auto aggregate_alignments_with_project = new Schema::IfcRelAggregates(IfcParse::IfcGlobalId(), nullptr, std::string("Alignments in project"), boost::none, project, list_of_alignments_in_project);
|
||||
file.addEntity(aggregate_alignments_with_project);
|
||||
|
||||
// Define the spatial structure of the alignment with respect to the site
|
||||
|
||||
// IFC 4.1.5.1 alignment is referenced in spatial structure of an IfcSpatialElement. In this case IfcSite is the highest level IfcSpatialElement
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Connectivity/Alignment_Spatial_Reference/content.html
|
||||
// IfcSite <-> IfcRelReferencedInSpatialStructure <-> IfcAlignment
|
||||
// This means IfcAlignment is not part of the IfcSite (it is not an aggregate component) but instead IfcAlignment is used within
|
||||
// the IfcSite by reference. This implies an IfcAlignment can traverse many IfcSite instances within an IfcProject
|
||||
typename Schema::IfcSpatialReferenceSelect::list::ptr list_alignments_referenced_in_site(new Schema::IfcSpatialReferenceSelect::list);
|
||||
list_alignments_referenced_in_site->push(alignment);
|
||||
|
||||
// this alignment traverse 3 bridge sites.
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
std::ostringstream os;
|
||||
os << "Site of Bridge " << i;
|
||||
auto site = file.addSite(project, nullptr);
|
||||
site->setName(os.str());
|
||||
|
||||
std::ostringstream description;
|
||||
description << "Alignments referenced into the spatial structure of Bridge Site " << i;
|
||||
|
||||
auto rel_referenced_in_spatial_structure = new Schema::IfcRelReferencedInSpatialStructure(IfcParse::IfcGlobalId(), nullptr, boost::none, description.str(), list_alignments_referenced_in_site, site);
|
||||
file.addEntity(rel_referenced_in_spatial_structure);
|
||||
}
|
||||
|
||||
// That's it - save the model to a file
|
||||
std::ofstream ofs("FHWA_Bridge_Geometry_Alignment_Example.ifc");
|
||||
ofs << file;
|
||||
}
|
||||
@@ -33,15 +33,18 @@
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
|
||||
#include <Precision.hxx>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "../ifcparse/macros.h"
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#define IfcSchema Ifc4
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#define IfcSchema Ifc2x3
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
#include "../ifcgeom/Serialization/Serialization.h"
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
#include "../ifcgeom_schema_agnostic/Serialization.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#define IfcSchema Ifc2x3
|
||||
|
||||
#include "../ifcparse/IfcFile.h"
|
||||
#include "../ifcparse/IfcLogger.h"
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
@@ -46,7 +45,7 @@ static_assert(false, "A boost preprocessor sequence of schema identifiers is nee
|
||||
// for and then overflow into an existing empty include file.
|
||||
|
||||
#define INCLUDE_SCHEMA(n) \
|
||||
BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE(SCHEMA_SEQ), n), BOOST_PP_STRINGIZE(../ifcparse/BOOST_PP_CAT(Ifc,BOOST_PP_SEQ_ELEM(BOOST_PP_MIN(n, BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_POP_BACK(SCHEMA_SEQ))),SCHEMA_SEQ)).h), "../ifcgeom/empty.h")
|
||||
BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE(SCHEMA_SEQ), n), BOOST_PP_STRINGIZE(../ifcparse/BOOST_PP_CAT(Ifc,BOOST_PP_SEQ_ELEM(BOOST_PP_MIN(n, BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_POP_BACK(SCHEMA_SEQ))),SCHEMA_SEQ)).h), "../ifcgeom_schema_agnostic/empty.h")
|
||||
|
||||
#include INCLUDE_SCHEMA(0)
|
||||
#include INCLUDE_SCHEMA(1)
|
||||
@@ -155,7 +154,7 @@ void process_pset(element_properties& props, const T* inst) {
|
||||
}
|
||||
}
|
||||
if constexpr (is_ifc4_or_higher<Schema>::value) {
|
||||
if (auto extprops = inst->template as<typename Schema::IfcExtendedProperties>()) {
|
||||
if (auto extprops = inst->template as<Schema::IfcExtendedProperties>()) {
|
||||
// @todo
|
||||
}
|
||||
}
|
||||
@@ -227,52 +226,53 @@ void get_psets(element_properties& props, const IfcUtil::IfcBaseClass* inst) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
std::cout << "usage: IfcParseExamples <filename.ifc>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Redirect the output (both progress and log) to stdout
|
||||
Logger::SetOutput(&std::cout, &std::cout);
|
||||
if ( argc != 2 ) {
|
||||
std::cout << "usage: IfcParseExamples <filename.ifc>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse the IFC file provided in argv[1]
|
||||
IfcParse::IfcFile file(argv[1]);
|
||||
if (!file.good()) {
|
||||
std::cout << "Unable to parse .ifc file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// Redirect the output (both progress and log) to stdout
|
||||
Logger::SetOutput(&std::cout,&std::cout);
|
||||
|
||||
// Lets get a list of IfcBuildingElements, this is the parent
|
||||
// type of things like walls, windows and doors.
|
||||
// entitiesByType is a templated function and returns a
|
||||
// templated class that behaves like a std::vector.
|
||||
// Note that the return types are all typedef'ed as members of
|
||||
// the generated classes, ::list for the templated vector class,
|
||||
// ::ptr for a shared pointer and ::it for an iterator.
|
||||
// We will simply iterate over the vector and print a string
|
||||
// representation of the entity to stdout.
|
||||
//
|
||||
// Secondly, lets find out which of them are IfcWindows.
|
||||
// In order to access the additional properties that windows
|
||||
// have on top af the properties of building elements,
|
||||
// we need to cast them to IfcWindows. Since these properties
|
||||
// are optional we need to make sure the properties are
|
||||
// defined for the window in question before accessing them.
|
||||
// Parse the IFC file provided in argv[1]
|
||||
IfcParse::IfcFile file(argv[1]);
|
||||
if (!file.good()) {
|
||||
std::cout << "Unable to parse .ifc file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lets get a list of IfcBuildingElements, this is the parent
|
||||
// type of things like walls, windows and doors.
|
||||
// entitiesByType is a templated function and returns a
|
||||
// templated class that behaves like a std::vector.
|
||||
// Note that the return types are all typedef'ed as members of
|
||||
// the generated classes, ::list for the templated vector class,
|
||||
// ::ptr for a shared pointer and ::it for an iterator.
|
||||
// We will simply iterate over the vector and print a string
|
||||
// representation of the entity to stdout.
|
||||
//
|
||||
// Secondly, lets find out which of them are IfcWindows.
|
||||
// In order to access the additional properties that windows
|
||||
// have on top af the properties of building elements,
|
||||
// we need to cast them to IfcWindows. Since these properties
|
||||
// are optional we need to make sure the properties are
|
||||
// defined for the window in question before accessing them.
|
||||
IfcSchema::IfcBuildingElement::list::ptr elements = file.instances_by_type<IfcSchema::IfcBuildingElement>();
|
||||
|
||||
std::cout << "Found " << elements->size() << " elements in " << argv[1] << ":" << std::endl;
|
||||
|
||||
for (auto it = elements->begin(); it != elements->end(); ++it) {
|
||||
const auto* element = *it;
|
||||
std::cout << element->data().toString() << std::endl;
|
||||
|
||||
const IfcSchema::IfcWindow* window;
|
||||
if ((window = element->as<IfcSchema::IfcWindow>()) != 0) {
|
||||
if (window->OverallWidth() && window->OverallHeight()) {
|
||||
const double area = *window->OverallWidth() * *window->OverallHeight();
|
||||
std::cout << "The area of this window is " << area << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "Found " << elements->size() << " elements in " << argv[1] << ":" << std::endl;
|
||||
|
||||
for (IfcSchema::IfcBuildingElement::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
|
||||
const IfcSchema::IfcBuildingElement* element = *it;
|
||||
std::cout << element->data().toString() << std::endl;
|
||||
|
||||
const IfcSchema::IfcWindow* window;
|
||||
if ((window = element->as<IfcSchema::IfcWindow>()) != 0) {
|
||||
if (window->OverallWidth() && window->OverallHeight()) {
|
||||
const double area = *window->OverallWidth() * *window->OverallHeight();
|
||||
std::cout << "The area of this window is " << area << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
element_properties props;
|
||||
get_psets(props, element);
|
||||
@@ -291,5 +291,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
// This example illustrates the basic of building an alignment model.
|
||||
// The alignment is based on "Bridge Geometry Manual", April 2022
|
||||
// US Department of Transportation, Federal Highway Administration (FHWA)
|
||||
// https://www.fhwa.dot.gov/bridge/pubs/hif22034.pdf
|
||||
//
|
||||
// Sections and page number for this document are cited in the code comments.
|
||||
//
|
||||
// This examples differs from IfcAlignment because it uses utility methods
|
||||
// to simplify alignment construction
|
||||
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
|
||||
#include "../ifcparse/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/IfcAlignmentHelper.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#define Schema Ifc4x3_add2
|
||||
|
||||
// performs basic project setup including created the IfcProject object
|
||||
// and initializing the project units to FEET
|
||||
Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
std::vector<std::string> file_description;
|
||||
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
|
||||
file.header().file_description().description(file_description);
|
||||
|
||||
auto project = file.addProject();
|
||||
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
|
||||
project->setDescription(std::string("C++ Example - Simplified"));
|
||||
|
||||
// set up project units for feet
|
||||
// the call to file.addProject() sets up length units as millimeter.
|
||||
auto units_in_context = project->UnitsInContext();
|
||||
auto units = units_in_context->Units();
|
||||
auto begin = units->begin();
|
||||
auto iter = begin;
|
||||
auto end = units->end();
|
||||
for (; iter != end; iter++) {
|
||||
auto unit = *iter;
|
||||
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
|
||||
file.addEntity(dimensions);
|
||||
|
||||
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
|
||||
file.addEntity(conversion_factor);
|
||||
|
||||
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
|
||||
file.addEntity(conversion_based_unit);
|
||||
|
||||
units->remove(unit); // remove the millimeter unit
|
||||
units->push(conversion_based_unit); // add the feet unit
|
||||
units_in_context->setUnits(units); // update the UnitsInContext
|
||||
|
||||
break; // Done!, the length unit was found, so break out of the loop
|
||||
}
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
int main() {
|
||||
IfcHierarchyHelper<Schema> file;
|
||||
|
||||
auto project = setup_project(file);
|
||||
|
||||
//
|
||||
// Define horizontal alignment
|
||||
//
|
||||
|
||||
// PI Data
|
||||
// B.1.1 pg 211
|
||||
std::vector<std::pair<double, double>> points{
|
||||
{ 500.0, 2500.0}, // beginning
|
||||
{3340.0, 660.0}, // Point of intersection (PI), Curve #1
|
||||
{4340.0, 5000.0}, // Point of intersection (PI), Curve #2
|
||||
{7600.0, 4560.0}, // Point of intersection (PI), Curve #3
|
||||
{8480.0, 2010.0} // ending
|
||||
};
|
||||
|
||||
// Curve Data
|
||||
// B.1.3 pg 212
|
||||
std::vector<double> radii{ 1000,1250,950 };
|
||||
|
||||
//
|
||||
// Define vertical profile segments
|
||||
//
|
||||
|
||||
// Vertical Profile Data
|
||||
// Figure B.2 pg 213
|
||||
double xStart = 10000.0; // subtract xStart so the points are distance along, not station
|
||||
std::vector<std::pair<double,double>> vpoints {
|
||||
{10000.0-xStart,100.0}, // Vertical point of beginning (VPOB)
|
||||
{12000.0-xStart,135.0}, // Point of vertical intersection (VPI), Curve #1
|
||||
{15000.0-xStart,105.0}, // Point of vertical intersection (VPI), Curve #2
|
||||
{17400.0-xStart,153.0}, // Point of vertical intersection (VPI), Curve #3
|
||||
{19800.0-xStart,105.0}, // Point of vertical intersection (VPI), Curve #4
|
||||
{22800.0-xStart, 90.0} // Vertical poitn of ending (VPOE)
|
||||
};
|
||||
|
||||
// Vertical Curve Lengths
|
||||
// Figure B.2 pg 213
|
||||
std::vector<double> vclengths{1600.0, 1200.0, 2000.0, 800.0};
|
||||
|
||||
// Use utility function to create alignment
|
||||
auto alignment = addAlignment(file, "Example Alignment", points, radii, vpoints, vclengths);
|
||||
|
||||
// Define the alignment's relationship with the project
|
||||
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Aggregation/Alignment_Aggregation_To_Project/content.html
|
||||
// IfcProject <-> IfcRelAggregates <-> IfcAlignment
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr list_of_alignments_in_project(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
list_of_alignments_in_project->push(alignment);
|
||||
auto aggregate_alignments_with_project = new Schema::IfcRelAggregates(IfcParse::IfcGlobalId(), nullptr, std::string("Alignments in project"), boost::none, project, list_of_alignments_in_project);
|
||||
file.addEntity(aggregate_alignments_with_project);
|
||||
|
||||
// Define the spatial structure of the alignment with respect to the site
|
||||
|
||||
// IFC 4.1.5.1 alignment is referenced in spatial structure of an IfcSpatialElement. In this case IfcSite is the highest level IfcSpatialElement
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Connectivity/Alignment_Spatial_Reference/content.html
|
||||
// IfcSite <-> IfcRelReferencedInSpatialStructure <-> IfcAlignment
|
||||
// This means IfcAlignment is not part of the IfcSite (it is not an aggregate component) but instead IfcAlignment is used within
|
||||
// the IfcSite by reference. This implies an IfcAlignment can traverse many IfcSite instances within an IfcProject
|
||||
typename Schema::IfcSpatialReferenceSelect::list::ptr list_alignments_referenced_in_site(new Schema::IfcSpatialReferenceSelect::list);
|
||||
list_alignments_referenced_in_site->push(alignment);
|
||||
|
||||
// Complete the model by creating sites for the 3 bridges
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
std::ostringstream os;
|
||||
os << "Site of Bridge " << i;
|
||||
auto site = file.addSite(project, nullptr);
|
||||
site->setName(os.str());
|
||||
|
||||
std::ostringstream description;
|
||||
description << "Alignments referenced into the spatial structure of Bridge Site " << i;
|
||||
|
||||
auto rel_referenced_in_spatial_structure = new Schema::IfcRelReferencedInSpatialStructure(IfcParse::IfcGlobalId(), nullptr, boost::none, description.str(), list_alignments_referenced_in_site, site);
|
||||
file.addEntity(rel_referenced_in_spatial_structure);
|
||||
}
|
||||
|
||||
// That's it - save the model to a file
|
||||
std::ofstream ofs("FHWA_Bridge_Geometry_Alignment_Example_Simplified.ifc");
|
||||
ofs << file;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Exterior shell extractor
|
||||
|
||||
Extract a near-optimal manifold shell of a complete building model exterior in IFC.
|
||||
|
||||

|
||||
|
||||
Original triangulated geometry. **14 166** unique vertex positions.
|
||||
|
||||

|
||||
|
||||
Post-processed geometry using this tool. **627** unique vertex positions. Note that it's not only a subset of the elements, but also the thickness of the elements which is eliminated, yielding an exact and optimal representation of the building volume.
|
||||
|
||||
# Background
|
||||
|
||||
Contemporary challenges in our built-environment require multi-disciplinary and multi-scale knowledge. Interoperability is at the hard of a good functioning industry. Even though BIM is all about collaboration, the model data often stays in file silos. This is because of formats, technical challenges, but also due to inconsistent modelling approaches and fragmented applications.
|
||||
|
||||
BIM and GIS information are fundamentally different. In BIM (Building Information Modeling) and its main open source exchange standard (IFC), elements are modeled as individual solid volumes that form a database; with modeling constructs such as decomposition and planning and costing information for the execution of the built work.
|
||||
|
||||
For geospatial usage, people represent information as captured features with attributes and use post-hoc spatial queries for relationships.
|
||||
|
||||
A manifold representation of the entire outer shell enables geospatial queries, is much lighter to visualize and can be better geometrically simplified.
|
||||
|
||||
# Solution
|
||||
|
||||
This tool provides an approach to unify BIM and GIS data by means of an IFC conversion pipeline with extensive geometric post-processing in order to provide up to date and consistent multi-scale data environments. The tool uses IfcOpenShell and CGAL to turn the individual solid volumes into a continuous representation of the outer shell.
|
||||
|
||||
It relies on CGAL's exact computation paradigm and arbitrarily precise coordinates to decompose every element into convex parts, which are then described as a set of halfspace plane equations. These halfspace plane equations are aligned using an angular and linear tolerance, by clustering the plane equations and rewriting them with their cluster's average.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user