Compare commits

..

3 Commits

Author SHA1 Message Date
Ryan Schultz 8c9e4bae2f Merge branch 'switch_camera_only' of https://github.com/IfcOpenShell/IfcOpenShell into switch_camera_only
# Conflicts:
#	src/blenderbim/blenderbim/bim/module/drawing/operator.py
2024-06-05 11:12:01 -05:00
Ryan Schultz 777dea3756 shift + click with bim.activate_drawing switches view without turning off/off objects in scene 2024-06-05 11:03:33 -05:00
Ryan Schultz 2cc583b88b shift + click with bim.activate_drawing switches view without turning off/off objects in scene 2024-06-02 09:41:59 -05:00
463 changed files with 316649 additions and 143098 deletions
-24
View File
@@ -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
-5
View File
@@ -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
@@ -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}}"
-62
View File
@@ -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
@@ -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
@@ -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}}"
-42
View File
@@ -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
+2 -3
View File
@@ -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
-3
View File
@@ -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
-1
View File
@@ -1 +0,0 @@
0.7.9
+178 -242
View File
@@ -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)
-124
View File
@@ -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
View File
@@ -1 +0,0 @@
.env
+3 -7
View File
@@ -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
View File
@@ -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
+5 -10
View File
@@ -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
+16
View File
@@ -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)
-58
View File
@@ -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
-17
View File
@@ -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
+22 -18
View File
@@ -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
View File
@@ -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...")
-13
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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
+326 -123
View File
@@ -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()
+1 -2
View File
@@ -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>"
+38 -49
View File
@@ -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)
+3 -4
View File
@@ -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)
+2 -6
View File
@@ -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
+8
View File
@@ -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"
+15 -20
View File
@@ -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
+6 -8
View File
@@ -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
+240 -51
View File
@@ -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
+5 -2
View File
@@ -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":
+8 -16
View File
@@ -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
+1 -5
View File
@@ -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))
+1 -1
View File
@@ -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
+21
View File
@@ -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()
+2 -21
View File
@@ -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
+7
View File
@@ -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):
+37 -40
View File
@@ -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
+1 -9
View File
@@ -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,
+2 -4
View File
@@ -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 ---------------------------------------------------
+1 -1
View File
@@ -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:
+6
View File
@@ -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()
+2
View File
@@ -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")
+15
View File
@@ -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()
-14
View 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()
+9 -4
View File
@@ -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>
-494
View File
@@ -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;
}
+9 -6
View 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>
+49 -47
View File
@@ -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;
}
}
}
}
}
-164
View File
@@ -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;
}
-27
View File
@@ -1,27 +0,0 @@
# Exterior shell extractor
Extract a near-optimal manifold shell of a complete building model exterior in IFC.
![](duplex_a_orig.png)
Original triangulated geometry. **14 166** unique vertex positions.
![](duplex_a_exterior.png)
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