Compare commits

..

76 Commits

Author SHA1 Message Date
Thomas Krijnen ba00526e1b Update documentation for filename change 2014-11-05 18:15:27 +01:00
Yorik van Havre e744ba404e Renamed ifc.py to ifcopenshell.py and made it installable 2014-07-29 11:26:49 -03:00
Yorik van Havre eb39eeb30b Added a function to add a new entity without inserting it into the ifc file 2014-07-25 18:03:40 -03:00
Yorik van Havre 286e9d183a Made ifc.py compatible with python2 2014-07-20 12:43:10 -03:00
Thomas Krijnen d4e4ca3a2a Add id() and is_a() functions to entity_instance class Make filename argument to open() function optional 2014-07-19 13:57:47 +02:00
Thomas Krijnen f7adeb9cc1 parse units and precision when a file is opened from the python interface 2014-07-10 16:51:26 +02:00
Thomas Krijnen 607f7bd015 Update CMakeLists for new SWIG module name 2014-07-07 13:52:39 +02:00
Thomas Krijnen bd00d3287e Add missing #include 2014-07-07 13:42:00 +02:00
Thomas Krijnen f674493843 Fixed retrieving attributes derived in subtype Convert argument type enumeration to python string Add ability to iterate over all instances from python 2014-07-04 16:37:21 +02:00
Thomas Krijnen dbc60bedd6 Update install directives in cmake script for wrapper 2014-07-04 14:10:35 +02:00
Thomas Krijnen f7f949b37b Qualify typename rather than 'auto' keyword 2014-07-02 11:53:44 +02:00
Thomas Krijnen 31ca1ac1b5 IfcConvert segfaults #12 2014-07-02 11:53:03 +02:00
Ivano Ras 99d83fce42 Update README.md 2014-06-27 15:11:27 +02:00
Thomas Krijnen 4158424c25 Wrap values of attributes that return entity instances 2014-06-26 15:13:11 +02:00
Thomas Krijnen 1d8616883c Missing file for more idiomatic wrapper wrapper 2014-06-21 19:02:51 +02:00
Thomas Krijnen 8851641d94 Add more idiomatic wrapper wrapper 2014-06-20 14:18:29 +02:00
Thomas Krijnen 0a7b1dd7f4 Merge pull request #13 from iras/patch-1 2014-06-26 16:52:13 +02:00
Thomas Krijnen bb2a983a00 Expose the option to sew shells from connected face sets 2014-06-08 19:32:25 +02:00
Thomas Krijnen 604358b48e Increase precision for writing floats 2014-06-07 14:28:30 +02:00
Thomas Krijnen 68e60f0586 Fix serialization of simple type attributes 2014-06-07 11:47:26 +02:00
Thomas Krijnen e4f141b107 Fixes for inverse attributes 2014-06-03 18:50:29 +02:00
Thomas Krijnen fe172672e9 Add function to retrieve inverse attributes from Python 2014-06-03 16:33:38 +02:00
aothms 69a27e3591 More fixes for parsing files with comments 2014-04-19 09:35:08 +00:00
Thomas Krijnen d91e5d31f6 DISABLE_OPENING_SUBTRACTIONS and DISABLE_OBJECT_PLACEMENT settings for create_shape() / create_brep_data() 2014-04-17 10:50:42 +02:00
aothms a4c3fdfd18 Add support for IfcSweptDiskSolid 2014-04-16 16:51:03 +00:00
aothms b235320d8c Process IfcStyledItems directly applied to an IfcFacetedBrep 2014-04-09 10:57:45 +00:00
aothms e9a3464c26 Account for unit magnitude in matrix translation part when CONVERT_BACK_UNITS is enabled 2014-04-08 07:28:07 +00:00
aothms e08db74706 Fix product's 4x4 matrix on the IfcGeomServer 2014-04-07 17:59:20 +00:00
aothms 3a971d63f8 When writing collada files, postfix geometry instance nodes to make them distinct from their geometry library references 2014-04-07 14:01:16 +00:00
aothms 9a75a1f12d IfcGeomServer, small fixes: refrain from using auto type and restore std::cout rdbuf on exit so implicit flush doesn't segfault 2014-04-06 12:50:51 +00:00
aothms 64f88cbf25 Revamp the 'IfcGeomServer' idea to have an executable rather than a library to interface with. This way IFC processing runs in a separate process from the client application, so that crashes or out of memory issues do not bring down the client application. 2014-04-06 11:16:20 +00:00
aothms ca66aac1c1 Increased verbosity in unsupported entity instances 2014-04-02 15:28:04 +00:00
aothms 9262ea1890 Fixes for parsing files with comments 2014-04-02 15:04:09 +00:00
aothms 81537bee95 Measure conversion time of IfcConvert more faithfully 2014-03-31 14:03:06 +00:00
aothms f9e94f5e01 IfcSurfaceCurveSweptAreaSolid improvements 2014-03-31 14:00:02 +00:00
aothms 691e5766c4 Take into account face location when serializing shape as tesselated faceset. 2014-03-22 21:13:02 +00:00
aothms 0770d91a3c IfcConvert: Handle invalid command line options gracefully and notify user 2014-03-22 13:12:22 +00:00
aothms f3531276c8 No longer accept and silently convert invalid REAL tokens to zero 2014-03-22 13:02:21 +00:00
aothms ffe1eddb00 Handle missing or invalid unit and precision information more gracefully during IfcGeomObject initialization 2014-03-22 12:56:08 +00:00
aothms ba0ba1973a For .obj conversion terminate lines with "\n" rather than std::endl causing a significant speed increase due to longer intermittently flushing the stream 2014-03-22 11:12:50 +00:00
aothms b4e1b15da5 Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface. Add CSG example. 2014-03-22 10:31:51 +00:00
aothms a2b2add58f Add missing header to installation files. Thanks Fred. 2014-03-22 09:22:27 +00:00
aothms c70517f3ba Update build scripts to include TKOffset 2014-03-19 09:47:24 +00:00
aothms d79e28157f Add example file to generate file with IfcCompositeProfileDef and IfcDerivedProfileDef 2014-03-16 13:35:14 +00:00
aothms 4a36816f8f Support for IfcCompositeProfileDef and IfcDerivedProfileDef 2014-03-16 13:34:01 +00:00
aothms 83ac741b59 Add support for IfcGeometricSet IfcRevolvedAreaSolid IfcSurfaceOfLinearExtrusion IfcSurfaceOfRevolution IfcCenterLineProfileDef IfcArbitraryOpenProfileDef 2014-03-15 11:51:23 +00:00
aothms 2fb4be2edc Implementations for more parameterized profile definitions 2014-03-08 17:21:39 +00:00
aothms 868bd6efc8 Add support for ellipses with SemiAxis2 > SemiAxis1 2014-03-03 19:07:39 +00:00
aothms 27e18386d2 Add parameterized profile test files and script to generate them 2014-02-23 11:25:35 +00:00
Thomas Krijnen 562210b862 Fix for products with openings 2014-04-12 23:20:21 +02:00
Yorik van Havre 39b3e03fc5 Added a clean() function to the python wrapper 2014-04-11 15:24:41 -03:00
Thomas Krijnen ba7eec2c0b Fix for empty shape reps 2014-02-26 10:56:22 +01:00
Thomas Krijnen 357357ad78 Initialise string to type map 2014-02-26 10:12:25 +01:00
Yorik van Havre b3bf86e697 Fixes includes for building on Linux 2014-02-24 20:54:48 -03:00
Thomas Krijnen 6b65402611 Merge developments from master into the python_wrapper branch 2014-02-22 11:38:03 +01:00
aothms c7355f03e7 Add Boost as a required dependency in the CMake build file 2012-11-04 11:04:55 +00:00
aothms 5274d73fd0 Add an option to disable the subtraction of IfcOpeningElements from relating building elements to the IfcGeomObject settings 2012-10-19 08:20:43 +00:00
Yorik van Havre 88f6242804 Allowed to enter booleans as arguments, and removed unused line that cause compilation error 2013-12-13 16:22:37 -02:00
Yorik van Havre e936ba8391 Fix for building with gcc 2013-08-02 11:48:26 -03:00
Thomas Krijnen 9136b83223 Accept list of entity instances from Python 2013-07-27 15:55:23 -07:00
Thomas Krijnen 4987753a47 Allow setting enumeration values from python based on string 2013-07-23 23:17:37 -07:00
Thomas Krijnen 871ee13e05 Merge branch 'master' of https://github.com/aothms/IfcOpenShell.git 2013-07-22 20:09:38 -07:00
Yorik van Havre 9c4040345a Merge branch 'master' of https://github.com/aothms/IfcOpenShell 2013-07-22 15:41:10 -03:00
Thomas Krijnen aa4f6c0a7b Merge pull request #1 from yorikvanhavre/master 2013-07-21 14:10:52 -07:00
Yorik van Havre 5471f345d8 Fixed character encoding bug 2013-07-22 15:38:37 -03:00
Yorik van Havre 9679d450fc Fixed string conversion error 2013-07-21 13:41:27 -03:00
Yorik van Havre d8d221dce3 Merge branch 'master' of https://github.com/aothms/IfcOpenShell 2013-07-21 13:31:24 -03:00
Yorik van Havre ed5f879b9f Merge pull request #1 from aothms/master 2012-10-30 04:47:28 -07:00
Thomas Krijnen 3bc3331983 Increase reference count when returning Py_None 2013-07-22 20:06:52 -07:00
Thomas Krijnen 10657416ea Allow optional arguments to be set from Python with None, e.g: ifc_person.set_argument(0, None) 2013-07-20 13:51:06 -07:00
Thomas Krijnen e5abdb7de8 Fix for inline entity instance regression 2013-07-09 09:04:33 -07:00
Thomas Krijnen 7b57c8b5b3 Merge branch 'master' of https://github.com/aothms/IfcOpenShell 2013-07-09 09:01:06 -07:00
Thomas Krijnen 22563f7283 Use a char* instead of a std::string for the std::ofstream constructor 2013-06-21 10:25:05 -07:00
Thomas Krijnen d35e775407 Return empty list when for ifc datatypes not encountered in file 2013-07-09 08:57:09 -07:00
Thomas Krijnen a5684f0996 Correctly take inline entity instances for select types into account in the Python wrapper 2013-06-21 08:49:08 -07:00
Thomas Krijnen 509c34d66a Initial attempt at new python wrapper 2012-10-21 17:54:09 +02:00
867 changed files with 196978 additions and 696412 deletions
-8
View File
@@ -1,8 +0,0 @@
c14f5eeca042232025797990396232e4feab53e7
d6881e833da0b6278313870d37d42ca6ece8686a
892be5444c5bd01a601c99243ac8d3e7b2f0e779
d169a964dd7e9ab5c98b35a82bcbc76dc6229a7a
4a6ec11f6f84a3d1fed2ee9b2f85d783ac3daa9d
2c9d6a47f4d24a923069775206bf734feeb14830
8f1743ed64a2a52c201a1ef5e312b4d3a7a922cf
ad7f030344c405e10cfa7d5cf06d82c121eeb825
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: [aothms]
@@ -1,66 +0,0 @@
name: Publish-blenderbim-multiplatform
on:
push:
paths:
- 'src/blenderbim/**'
- '.github/workflows/ci-blenderbim-matrix.yml'
env:
major: 0
minor: 0
name: blenderbim
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell'
steps:
- name: Set env
run: echo ok go
build:
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
pyver: [py37, py39]
config:
- {
name: "Windows Build",
short_name: win,
}
- {
name: "Linux Build",
short_name: linux
}
- {
name: "MacOS Build",
short_name: macos
}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
with:
python-version: '3.7.7' # 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 date
id: date
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
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blender28-bim-${{steps.date.outputs.date}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
asset_name: blender28-bim-${{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}}"
-107
View File
@@ -1,107 +0,0 @@
name: CI
on:
push:
jobs:
activate:
runs-on: ubuntu-latest
if: |
github.repository == 'IfcOpenShell/IfcOpenShell' &&
!startsWith(github.event.head_commit.message, 'Release ') &&
!contains(github.event.head_commit.message, 'ci skip')
steps:
- run: echo ok go
build:
runs-on: ubuntu-20.04
needs: activate
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install --no-install-recommends \
git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
-
name: ccache
uses: hendrikmuhs/ccache-action@v1
-
name: Build ifcopenshell
run: |
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=$PWD/install/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-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 \
../cmake
make -j $(nproc)
make install
-
name: Package
run: |
make package
working-directory: build
- name: Upload
uses: actions/upload-artifact@v2
with:
# Artifact name
name: ifcos-artifacts
# Directory containing files to upload
path: build/assets/Ifc*
deliver:
runs-on: ubuntu-20.04
needs: build
name: Docker Build, Tag, Push
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: Download
uses: actions/download-artifact@v2
with:
# Artifact name
name: ifcos-artifacts
path: artifacts/
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: aecgeeks
password: ${{ secrets.DOCKER_HUB_TOKEN }}
-
name: Build container image
uses: docker/build-push-action@v2
with:
context: artifacts
repository: aecgeeks/ifcopenshell
tags: aecgeeks/ifcopenshell:latest
file: ./Dockerfile
push: true
-96
View File
@@ -1,96 +0,0 @@
name: Release
on:
push:
tags:
- 'v[0-9].[0-9].[0-9]*'
jobs:
activate:
if: github.repository == 'IfcOpenShell/IfcOpenShell'
runs-on: ubuntu-latest
steps:
- run: echo ok go
build:
needs: activate
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
-
name: Update ubuntu
run: |
sudo apt update
sudo apt-get install git cmake gcc g++ libboost1.67-all-dev python3-all-dev swig libpcre3-dev libxml2-dev
sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir build && cd build
cmake \
-DCMAKE_INSTALL_PREFIX=$PWD/install/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/usr \
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
-DBUILD_PACKAGE=On \
-DOCC_INCLUDE_DIR=/usr/include/oce \
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
-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 \
../cmake
make -j $(nproc)
make install
- name: Package
id: package
run: |
make package
echo ::set-output name=tgz::$( ls assets/*.tar.gz | head -n 1 | xargs basename )
echo ::set-output name=deb::$( ls assets/*.deb | head -n 1 | xargs basename )
working-directory: build
env:
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release
id: release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: true
body: |
Changes to this release
- add github actions support #1306
- add boost depends to ifcopenshell deb packaging
- <add more changes here>
See more details in the documentation
# body_path: build/CHANGELOG.md
- name: Upload TGZ
id: upload-tgz
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/assets/${{ steps.package.outputs.tgz }}
asset_name: ${{ steps.package.outputs.tgz }}
asset_content_type: application/x-gzip
- name: Upload DEB
id: upload-deb
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/assets/${{ steps.package.outputs.deb }}
asset_name: ${{ steps.package.outputs.deb }}
asset_content_type: application/vnd.debian.binary-package
-30
View File
@@ -1,30 +0,0 @@
# Dependency and build folders created by the build scripts
/_build-vs2017-x64/
/_deps-vs2017-x64-installed/
/_deps/
/deps*/
/build*/
/install*/
/win/BuildDepsCache*.txt
# IfcExpressParser residue
/src/ifcexpressparser/express_parser.py
# General Python residue
__pycache__
*.py.bak
# Visual Studio Code files
.vscode
# PyCharm files
.idea
# Docs
/docs/output
/docs/rst_files
/docs/doxygen
/src/ifcblenderexport/docs/_build
# gettext binary translation files
*.mo
# Vim
*.swp
-7
View File
@@ -1,7 +0,0 @@
[submodule "test/input"]
path = test/input
url = https://github.com/IfcOpenShell/files
ignore = dirty
[submodule "src/ifcopenshell-python/ifcopenshell/mvd"]
path = src/ifcopenshell-python/ifcopenshell/mvd
url = https://github.com/opensourceBIM/python-mvdxml/
-73
View File
@@ -1,73 +0,0 @@
language: cpp
compiler: gcc
cache: ccache
os: linux
dist: focal
addons:
apt:
update: true
packages:
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-iostreams-dev
- libboost-program-options-dev
- libboost-regex-dev
- libboost-system-dev
- libboost-thread-dev
- libocct-data-exchange-dev
- libocct-foundation-dev
- libocct-modeling-algorithms-dev
- libocct-modeling-data-dev
- libxml2-dev
- nlohmann-json3-dev
- opencollada-dev
- python3-all-dev
- swig
before_script:
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -z; fi
script:
- pwd
- mkdir build && cd build
- |
cmake \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-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.8 \
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
../cmake
# TODO: Drop sudo
- sudo make -j $(nproc) install
- cd ../test
- /usr/bin/python tests.py
- cd input
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
# - gltf_validator acad2010_walls.glb
- /usr/bin/python -c "from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
- |
(for i in *.ifc; do \
echo $i | tee -a log; \
timeout 1m /usr/local/bin/IfcConvert -yv "$i" "$i.dae" --validate >> log 2>&1; \
echo $i $? >> statuses; \
done) || true
- echo Failed
- grep -v 0$ statuses
- grep -v 0$ statuses | wc -l
- echo Succeeded
- grep 0$ statuses
- grep 0$ statuses | wc -l
after_script:
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -s; fi
-165
View File
@@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
-37
View File
@@ -1,37 +0,0 @@
# -*- mode: Dockerfile -*-
FROM ubuntu:focal
ARG CHANNEL
ENV CHANNEL=${CHANNEL:-latest}
RUN apt-get update \
&& apt-get install -y \
vim \
ssh \
sudo \
wget \
software-properties-common ;\
rm -rf /var/lib/apt/lists/*
RUN useradd --user-group --create-home --shell /bin/bash IfcOpenShell ;\
echo "IfcOpenShell ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
ENV DEBIAN_FRONTEND=noninteractive
RUN echo "deb http://archive.ubuntu.com/ubuntu focal-proposed main restricted" | tee -a /etc/apt/sources.list; \
echo "deb http://archive.ubuntu.com/ubuntu focal-proposed universe" | tee -a /etc/apt/sources.list; \
echo "deb http://archive.ubuntu.com/ubuntu focal-proposed multiverse" | tee -a /etc/apt/sources.list; \
apt-get -qq update; \
apt-get -y install tzdata dos2unix rsync; \
apt-get -y install python3 libxml2 liboce-foundation11 liboce-modeling11 liboce-ocaf11 liboce-visualization11 \
liboce-ocaf-lite11 libpython3.8 libboost-system1.67.0 libboost-program-options1.67.0 \
libboost-regex1.67.0 libboost-thread1.67.0 libboost-date-time1.67.0; \
rm -rf /var/lib/apt/lists/* ;
COPY . /home/IfcOpenShell/
RUN ls -lrtR /home/IfcOpenShell
RUN dpkg -i /home/IfcOpenShell/*.deb
USER IfcOpenShell
+80 -176
View File
@@ -1,178 +1,26 @@
IfcOpenShell
============
IfcOpenShell is an open source ([LGPL]) software library for working with the Industry Foundation Classes ([IFC])
file format. Currently supported IFC releases are [IFC2x3 TC1] and [IFC4 Add2 TC1].
open source (LGPL) software library for working with the IFC file format
For more information, see
* [http://ifcopenshell.org](http://ifcopenshell.org)
* [http://academy.ifcopenshell.org](http://academy.ifcopenshell.org)
[![Build Status](https://travis-ci.org/IfcOpenShell/IfcOpenShell.svg?branch=v0.6.0)](https://travis-ci.org/IfcOpenShell/IfcOpenShell)
http://IfcOpenShell.org
Prerequisites
-------------
* Git
* CMake (2.6 or newer)
* Windows: [Visual Studio] 2008 or newer with C++ toolset (or [Visual C++ Build Tools]) or [MSYS2] + MinGW
* *nix: GCC 4.7 or newer, or Clang (any version)
About this repository
=====================
Dependencies
-------------
* [Boost](http://www.boost.org/)
* [Open Cascade](http://opencascade.org) - *optional*, but required for building IfcGeom
([official](http://www.opencascade.org/getocc/download/loadocc/), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
For converting IFC representation items into BRep solids and tesselated meshes
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
For IfcConvert to be able to write tessellated Collada (.dae) files
* [SWIG](http://www.swig.org/) and [Python](https://www.python.org/) - *optional*
For building the IfcOpenShell Python interface and the Blender add-on
* [3ds Max SDK](http://www.autodesk.com/products/3ds-max/free-trial) - *optional*
For building the 3ds Max plug-in.
All recent versions of 3ds Max (2014 and newer) are 64-bit only, so a 64-bit installation is assumed.
This is an initiative to expose the functionality of IfcOpenShell for parsing and writing IFC files to a Python interface. The creation of a topological representation for building elements using Open Cascade is also wrapped by returning a string serialization of the Open Cascade TopoDS_Shape. Using SWIG, a wrapper is obtained for a C++ Class which evaluates its Express attribute names at runtime. The functions created by the SWIG wrapper closely resemble the C++ structure. An additional Python wrapper around these functions is created that is more idiomatic to Python.
Building IfcOpenShell
---------------------
The following interactive session illustrates its use:
**Note:** The path where the source code is cloned to can contain spaces but non-ASCII characters are very likely to cause problems with the build.
### Compiling on Windows
The preferred way to fetch and build this project's dependencies is to use the build scripts
in win/ folder. **See [win/readme.md] for more information**.
#### Using Visual Studio
Instructions in a nutshell (**assuming Visual Studio 2015 x64 environment variables set**):
> cd IfcOpenShell\win
> build-deps.cmd
> run-cmake.bat
NB: `build-deps.cmd` need to be ran from the directory containing it, i.e. the `./win` folder.
You can now open and build the solution file in Visual Studio:
> ..\build-vs2015-x64\IfcOpenShell.sln
As the scripts default to using the `RelWithDebInfo` configuration, and a freshly created solution by CMake defaults
to `Debug`, make sure to switch the used build configuration. Build the `INSTALL` project (right-click -> Project
Only) to deploy the headers and binaries into a single location if wanted/needed.
Alternatively, one can use the utility batch file(s) to build and install the project easily from the command-line
(installing a project will build it also, if required):
> install-ifcopenshell.bat
#### Using MSYS2 + MinGW
Start the MSYS2 Shell and then:
$ cd IfcOpenShell/win
$ ./build-deps.sh
$ ./run-cmake.sh
$ ./install-ifcopenshell.sh
#### Using Bash on Ubuntu on Windows
Start Bash on Ubuntu on Windows and follow the instructions below. Compiling on Ubuntu 14.04.4 LTS using GCC 4.8.4
or Clang 3.5 has been confirmed to work.
### Compiling on *nix
The following instructions are for Ubuntu, modify as required for other operating systems. [nix/build-all.py] script
can be experimented with and studied for pointers for other operating systems, but note that this script is not currently
meant to be used for a typical IfcOpenShell workspace setup.
Note: where `make -j` is written, add a number roughly equal to the amount of CPU cores + 1.
**1)** Install most of the prerequisites and dependencies:
$ sudo apt-get install git cmake gcc g++ libboost-all-dev
**2a)** Either use an OCE package from your operating system's software repository
$ sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
**2b)** or (if not available, or the latest code is wanted) compile OCE yourself (note that the build takes a long time):
$ sudo apt-get install libftgl-dev libtbb2 libtbb-dev libgl1-mesa-dev libfreetype6-dev
$ git clone https://github.com/tpaviot/oce.git
$ cd oce
$ mkdir build && cd build
$ cmake ..
$ make -j
$ sudo make install
**2c)** or obtain and compile OCCT from http://www.opencascade.org/getocc/download/loadocc/
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
$ sudo apt-get install libpcre3-dev libxml2-dev
$ git clone https://github.com/KhronosGroup/OpenCOLLADA.git
$ cd OpenCOLLADA
Using a known good revision, but HEAD should work too:
$ git checkout 064a60b65c2c31b94f013820856bc84fb1937cc6
$ mkdir build && cd build
$ cmake ..
$ make -j
$ sudo make install
**4)** For building the IfcPython wrapper (on by default), SWIG and Python development are needed, if not already available:
$ sudo apt-get install python-all-dev swig
**5)** To build IfcOpenShell please take the following steps. Alternatively use environment variables for setting the
dependencies' paths. `OCC_INCLUDE_DIR` might be needed to set also. `OPENCOLLADA_INCLUDE_DIR` and `OPENCOLLADA_LIBRARY_DIR`
(and potentially `PCRE_LIBRARY_DIR`) are needed if building with COLLADA support. (`-DCOLLADA_SUPPORT=0` disables it).
$ cd /path/to/IfcOpenShell
$ mkdir build && cd build
$ cmake ../cmake -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
-DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/
$ make -j
If all worked out correctly you can now use IfcOpenShell. See the examples below.
**6)** Install the project if wanted:
$ sudo make install
Installing IfcOpenShell with Conda
----------------------------------
Another option for building and installing IfcOpenShell is to use the popular
[Anaconda Python Distribution](https://www.anaconda.com/download).
The requirements are spread across a number of channels.
You can add these channels to your configuration, or specify them all on the command line:
$ conda install -c conda-forge -c oce -c dlr-sc -c ifcopenshell ifcopenshell
Usage examples
--------------
**Invoking IfcConvert from the command line**
$ wget ftp://ftp.dds.no/pub/ifc/Munkerud/Munkerud_hus6_BE.zip
$ unzip Munkerud_hus6_BE.zip
$ ./IfcConvert Munkerud_hus6_BE.ifc
$ less Munkerud_hus6_BE.obj
**Using the IfcOpenShell Python interface**
$ wget -O duplex.zip https://portal.nibs.org/files/wl/?id=4DsTgHFQAcOXzFetxbpRCECPbbfUqpgo
$ unzip duplex.zip
$ python
>>> import ifcopenshell
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>>
>>> # Accessing entity instances by type:
>>> f.by_type("ifcwall")[:2]
[#91=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FL9r',#1,'Basic Wall:Interior - Partition (92mm Stud):144586',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5198,#18806,'144586'), #92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')]
>>> wall = _[0]
>>> brep_data = ifcopenshell.create_shape(wall, ifcopenshell.SEW_SHELLS)
>>> len(wall) # number of EXPRESS attributes
8
>>>
>>> # Accessing EXPRESS attributes by name:
>>> wall.GlobalId
>>> wall.GlobalId
'2O2Fr$t4X7Zf8NOew3FL9r'
>>> wall.Name = "My wall"
>>> wall.NonExistingAttr
@@ -194,11 +42,8 @@ Usage examples
File ".\ifc_wrapper.py", line 114, in _set_argument
def _set_argument(self, *args): return _ifc_wrapper.entity_instance__set_argument(self, *args)
RuntimeError: INT is not a valid type for 'GlobalId'
>>> # Creating new entity instances
>>> f.createIfcCartesianPoint(Coordinates=(1.0,1.5,2.0))
#27530=IfcCartesianPoint((1.,1.5,2.))
>>>
>>> # Working with GlobalId attributes:
>>> import uuid
>>> ifcopenshell.guid.compress(uuid.uuid1().hex)
'3x4C8Q_6qHuv$P$FYkANRX'
@@ -207,22 +52,81 @@ Usage examples
>>> new_wall = f.createIfcWallStandardCase(new_guid, owner_hist, None, None, Tag='my_tag')
>>> new_wall.ObjectType = ''
>>> new_wall.ObjectPlacement = new_wall.Representation = None
>>>
>>> # Accessing entity instances by instance id or GlobalId:
>>> f[92]
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
>>> f['2O2Fr$t4X7Zf8NOew3FLIE']
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
>>>
>>> # Writing IFC-SPF files to disk:
>>> f.write("out.ifc")
[LGPL]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/COPYING "LGPL"
[IFC]: https://technical.buildingsmart.org/standards/ifc/ "IFC"
[IFC2x3 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ "IFC2x3 TC1"
[IFC4 Add2 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/ "IFC4 Add2 TC1"
[Visual Studio]: https://www.visualstudio.com/ "Visual Studio"
[Visual C++ Build Tools]: http://landinghub.visualstudio.com/visual-cpp-build-tools "Visual C++ Build Tools"
[MSYS2]: https://msys2.github.io/ "MSYS2"
[win/readme.md]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/win/readme.md "win/readme.md"
[nix/build-all.py]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/nix/build-all.py "nix/build-all.py"
Compiling on Windows
====================
Users are advised to use the Visual Studio .sln file in the win/ folder.
For Windows users a prebuilt Open CASCADE version is available from the
http://opencascade.org website. Download and install this version and
provide the paths to the Open CASCADE header and library files to MS
Visual Studio C++.
For building the Autodesk 3ds Max plugin, the 3ds Max SDK needs to be
installed as well as 3ds Max itself. Please provide the include and
library paths to Visual Studio.
For building the IfcPython wrapper, SWIG needs to be installed. Please
download the latest swigwin version from http://www.swig.org/download.html.
After extracting the .zip file, please add the extracted folder to the PATH
environment variable. Python needs to be installed, please provide the
include and library paths to Visual Studio.
Compiling on *nix
====================
Users are advised to build IfcOpenShell using the cmake file provided in
the cmake/ folder. There might be an Open CASCADE package in your operating
system's software repository. If not, you will need to compile Open
CASCADE yourself. See http://opencascade.org.
For building the IfcPython wrapper, SWIG and Python development are
required.
To build IfcOpenShell please take the following steps:
$ cd /path/to/IfcOpenShell/cmake
$ mkdir build
$ cd build
Optionally:
$ OCC_INCLUDE_PATH="/path/to/OpenCASCADE/include"
$ OCC_LIBRARY_PATH="/path/to/OpenCASCADE/lib"
$ export OCC_INCLUDE_PATH
$ export OCC_LIBRARY_PATH
$ cmake ../
$ make
Ubuntu Notes
============
The following sequence of commands has been tested successfully on Ubuntu 14.04.
OCE installation. This step worked OK with cmake 2.8.12.2, gcc 4.8.2, g++ 4.8.2. Also, it might take up to an hour to complete.
apt-get install git cmake gcc g++ libftgl-dev libtbb2 libtbb-dev libboost-all-dev
cd /
mkdir git && cd git
git clone https://github.com/tpaviot/oce.git
mkdir oceBuild && cd oceBuild
cmake ../oce
make -j4
sudo make install
IfcOpenShell installation. This step worked OK with cmake 2.8.12.2, gcc 4.8.2, g++ 4.8.2.
apt-get install swig
cd /git/
git clone https://github.com/aothms/IfcOpenShell.git
cd IfcOpenShell/cmake
mkdir build && cd build
cmake ../
make -j4
Once done, IfcConvert is to be found in the build folder.
+191 -740
View File
@@ -1,779 +1,230 @@
################################################################################
# #
# 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/>. #
# #
################################################################################
cmake_minimum_required (VERSION 2.6)
project (IfcOpenShell)
cmake_minimum_required(VERSION 3.1.3)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
project (IfcOpenShell VERSION 0.6.0)
# use extra version to make pre-release using eg semver
set( EXTRA_VERSION "-alpha.3")
foreach(max_year RANGE 2014 2030)
set(max_sdk "$ENV{ADSK_3DSMAX_SDK_${max_year}}")
if (NOT "${max_sdk}" STREQUAL "")
MESSAGE(STATUS "Autodesk 3ds Max SDK found at ${max_sdk}")
set(HAS_MAX TRUE)
endif()
endforeach()
OPTION(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON)
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
OPTION(BUILD_PACKAGE "" OFF)
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
OPTION(BUILD_EXAMPLES "Build example applications." ON)
OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
OPTION(BUILD_CONVERT "Build IfcConvert executable." ON)
OPTION(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF)
OPTION(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF)
OPTION(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." OFF)
if (${HAS_MAX})
OPTION(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." ON)
endif()
if (${BUILD_CONVERT})
OPTION(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
endif()
OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
# OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer (requires Qt 4 framework)." OFF)
include(GNUInstallDirs)
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
message(STATUS "'IfcGeom' is required with current outputs")
set(BUILD_IFCGEOM ON)
endif()
# Specify where to install files
IF(NOT BINDIR)
set(BINDIR bin)
ENDIF()
IF(NOT IS_ABSOLUTE ${BINDIR})
set(BINDIR ${CMAKE_INSTALL_BINDIR})
ENDIF()
MESSAGE(STATUS "BINDIR: ${BINDIR}")
IF(NOT INCLUDEDIR)
set(INCLUDEDIR include)
ENDIF()
IF(NOT IS_ABSOLUTE ${INCLUDEDIR})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
ENDIF()
MESSAGE(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
IF(NOT LIBDIR)
set(LIBDIR lib)
ENDIF()
IF(NOT IS_ABSOLUTE ${LIBDIR})
set(LIBDIR ${CMAKE_INSTALL_LIBDIR})
ENDIF()
MESSAGE(STATUS "LIBDIR: ${LIBDIR}")
set(IFCOPENSHELL_LIBRARY_DIR "") # for *nix rpaths
if (BUILD_SHARED_LIBS)
add_definitions(-DIFC_SHARED_BUILD)
if (MSVC)
message(WARNING "Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed.")
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
add_definitions(-wd4251)
endif()
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(OPENCOLLADA_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(OPENCOLLADA_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(LIBXML2_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(LIBXML2_LIBRARIES)
UNIFY_ENVVARS_AND_CACHE(PCRE_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(PYTHON_EXECUTABLE)
if (GLTF_SUPPORT AND BUILD_CONVERT)
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
FIND_FILE(json_hpp "json.hpp" ${JSON_INCLUDE_DIR}/nlohmann)
IF(json_hpp)
MESSAGE(STATUS "JSON for Modern C++ header file found")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting")
ENDIF()
add_definitions(-DWITH_GLTF)
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.
IF(WIN32 AND ("$ENV{CONDA_BUILD}" STREQUAL ""))
SET(Boost_USE_STATIC_LIBS ON)
SET(Boost_USE_STATIC_RUNTIME ON)
SET(Boost_USE_MULTITHREADED ON)
# Disable Boost's autolinking as the libraries to be linked to are supplied
# already by CMake, and wrong libraries would be asked for when code is
# compiled with a toolset different from default.
if(MSVC)
ADD_DEFINITIONS(-DBOOST_ALL_NO_LIB)
# Necessary for boost version >= 1.67
SET(BCRYPT_LIBRARIES "bcrypt.lib")
ENDIF()
ELSE()
# Disable Boost's autolinking as the libraries to be linked to are supplied
# already by CMake, and it's going to conflict if there are multiple, as is
# the case in conda-forge's libboost feedstock.
ADD_DEFINITIONS(-DBOOST_ALL_NO_LIB)
IF(WIN32)
# Necessary for boost version >= 1.67
SET(BCRYPT_LIBRARIES "bcrypt.lib")
ENDIF()
ENDIF()
set(BOOST_COMPONENTS system program_options regex thread date_time)
if(USE_MMAP)
if(MSVC)
# filesystem is necessary for the utf-16 wpath
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams filesystem)
else()
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams)
endif()
add_definitions(-DUSE_MMAP)
endif()
if (IFCXML_SUPPORT)
find_package(LibXml2 REQUIRED)
add_definitions(-DWITH_IFCXML)
endif()
FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options)
MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
# 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("${OCC_INCLUDE_DIR}" STREQUAL "")
FIND_PATH(OCC_INCLUDE_DIR Standard_Version.hxx
[PATHS
/usr/include/occt
/usr/include/oce
/usr/include/opencascade
]
REQUIRED
)
IF(OCC_INCLUDE_DIR)
MESSAGE(STATUS "Found Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find Open CASCADE include directory, specify OCC_INCLUDE_DIR manually.")
ENDIF()
# Find Open CASCADE header files
IF("$ENV{OCC_INCLUDE_DIR}" STREQUAL "")
SET(OCC_INCLUDE_DIR "/usr/include/opencascade/" CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for opencascade include files in: ${OCC_INCLUDE_DIR}")
MESSAGE(STATUS "Use OCC_INCLUDE_DIR to specify another directory")
ELSE()
SET(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
SET(OCC_INCLUDE_DIR $ENV{OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
MESSAGE(STATUS "Looking for opencascade include files in: ${OCC_INCLUDE_DIR}")
ENDIF()
SET(OPENCASCADE_LIBRARY_NAMES
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
)
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
find_library(OCC_LIBRARY TKernel
[PATHS
/usr/lib
]
REQUIRED
)
IF(OCC_LIBRARY)
GET_FILENAME_COMPONENT(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH)
MESSAGE(STATUS "Found Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
ELSE()
MESSAGE(FATAL_ERROR "Unable find Open CASCADE library directory, specify OCC_LIBRARY_DIR manually.")
ENDIF()
FIND_FILE(gp_Pnt_hxx "gp_Pnt.hxx" ${OCC_INCLUDE_DIR} /usr/inc /usr/local/inc /usr/local/include/oce)
IF(gp_Pnt_hxx)
MESSAGE(STATUS "Header files found")
ELSE()
SET(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
MESSAGE(FATAL_ERROR "Unable to find header files, aborting")
ENDIF()
FIND_LIBRARY(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH)
# Find Open CASCADE library files
IF("$ENV{OCC_LIBRARY_DIR}" STREQUAL "")
SET(OCC_LIBRARY_DIR "/usr/lib/" CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for opencascade library files in: ${OCC_LIBRARY_DIR}")
MESSAGE(STATUS "Use OCC_LIBRARY_DIR to specify another directory")
ELSE()
SET(OCC_LIBRARY_DIR $ENV{OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
MESSAGE(STATUS "Looking for opencascade library files in: ${OCC_LIBRARY_DIR}")
ENDIF()
FIND_LIBRARY(libTKernel "TKernel" ${OCC_LIBRARY_DIR} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64)
IF(libTKernel)
MESSAGE(STATUS "Required Open Cascade Library files found")
MESSAGE(STATUS "Library files found")
ELSE()
MESSAGE(FATAL_ERROR "Unable to find Open Cascade library files, aborting")
MESSAGE(FATAL_ERROR "Unable to find library files, aborting")
ENDIF()
# Use the found libTKernel as a template for all other OCC libraries
# TODO Extract this into macro/function
foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
string(REPLACE TKerneld "${lib}" lib_path "${libTKernel}")
string(REPLACE TKernel "${lib}" lib_path "${lib_path}")
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
endforeach()
if(MSVC)
add_definitions(-DHAVE_NO_DLL)
add_debug_variants(OPENCASCADE_LIBRARIES "${OPENCASCADE_LIBRARIES}" d)
endif()
if (WIN32)
# OCC might require linking to Winsock depending on the version and build configuration
list(APPEND OPENCASCADE_LIBRARIES ws2_32.lib)
endif()
# Make sure cross-referenced symbols between static OCC libraries get
# resolved. Also add thread and rt libraries.
get_filename_component(libTKernelExt ${libTKernel} EXT)
if("${libTKernelExt}" STREQUAL ".a")
set(OCCT_STATIC ON)
endif()
if(OCCT_STATIC)
find_package(Threads)
# OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
if (NOT APPLE AND NOT WIN32)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
endif()
if (NOT WIN32)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "dl")
endif()
endif()
endif(BUILD_IFCGEOM)
IF(COLLADA_SUPPORT AND BUILD_CONVERT)
# Find OpenCOLLADA
IF("${OPENCOLLADA_INCLUDE_DIR}" STREQUAL "")
MESSAGE(STATUS "No OpenCOLLADA include directory specified")
SET(OPENCOLLADA_INCLUDE_DIR "/usr/include/opencollada" CACHE FILEPATH "OpenCOLLADA header files")
ELSE()
SET(OPENCOLLADA_INCLUDE_DIR "${OPENCOLLADA_INCLUDE_DIR}" CACHE FILEPATH "OpenCOLLADA header files")
ENDIF()
IF("${OPENCOLLADA_LIBRARY_DIR}" STREQUAL "")
MESSAGE(STATUS "No OpenCOLLADA library directory specified")
FIND_LIBRARY(OPENCOLLADA_FRAMEWORK_LIB NAMES OpenCOLLADAFramework
PATHS /usr/lib64/opencollada /usr/lib/opencollada /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib)
GET_FILENAME_COMPONENT(OPENCOLLADA_LIBRARY_DIR ${OPENCOLLADA_FRAMEWORK_LIB} PATH)
ENDIF()
FIND_LIBRARY(OpenCOLLADAFramework NAMES OpenCOLLADAFramework OpenCOLLADAFrameworkd PATHS ${OPENCOLLADA_LIBRARY_DIR} NO_DEFAULT_PATH)
if (OpenCOLLADAFramework)
message(STATUS "OpenCOLLADA library files found")
else()
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find OpenCOLLADA libraries. "
"Disable COLLADA_SUPPORT or fix OpenCOLLADA paths to proceed.")
endif()
SET(OPENCOLLADA_LIBRARY_DIR "${OPENCOLLADA_LIBRARY_DIR}" CACHE FILEPATH "OpenCOLLADA library files")
SET(OPENCOLLADA_INCLUDE_DIRS "${OPENCOLLADA_INCLUDE_DIR}/COLLADABaseUtils" "${OPENCOLLADA_INCLUDE_DIR}/COLLADAStreamWriter")
FIND_FILE(COLLADASWStreamWriter_h "COLLADASWStreamWriter.h" ${OPENCOLLADA_INCLUDE_DIRS})
IF(COLLADASWStreamWriter_h)
MESSAGE(STATUS "OpenCOLLADA header files found")
ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
SET(OPENCOLLADA_LIBRARY_NAMES
GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
OpenCOLLADAStreamWriter UTF buffer ftoa
)
# Use the found OpenCOLLADAFramework as a template for all other OpenCOLLADA libraries
foreach(lib ${OPENCOLLADA_LIBRARY_NAMES})
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
string(REPLACE OpenCOLLADAFrameworkd "${lib}" lib_path "${OpenCOLLADAFramework}")
string(REPLACE OpenCOLLADAFramework "${lib}" lib_path "${lib_path}")
list(APPEND OPENCOLLADA_LIBRARIES "${lib_path}")
endforeach()
if("${PCRE_LIBRARY_DIR}" STREQUAL "")
if(WIN32)
find_library(pcre_library NAMES pcre pcred PATHS ${OPENCOLLADA_LIBRARY_DIR} NO_DEFAULT_PATH)
else()
find_library(pcre_library NAMES pcre PATHS ${OPENCOLLADA_LIBRARY_DIR})
endif()
GET_FILENAME_COMPONENT(PCRE_LIBRARY_DIR ${pcre_library} PATH)
else()
find_library(pcre_library NAMES pcre pcred PATHS ${PCRE_LIBRARY_DIR} NO_DEFAULT_PATH)
endif()
if (pcre_library)
SET(OPENCOLLADA_LIBRARY_DIR ${OPENCOLLADA_LIBRARY_DIR} ${PCRE_LIBRARY_DIR})
if (MSVC)
# Add release lib regardless whether release or debug found. Debug version will be appended below.
list(APPEND OPENCOLLADA_LIBRARIES "${PCRE_LIBRARY_DIR}/pcre.lib")
else()
list(APPEND OPENCOLLADA_LIBRARIES "${pcre_library}")
endif()
else()
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find PCRE. "
"Disable COLLADA_SUPPORT or fix PCRE_LIBRARY_DIR path to proceed.")
endif()
IF(MSVC)
add_debug_variants(OPENCOLLADA_LIBRARIES "${OPENCOLLADA_LIBRARIES}" d)
ENDIF()
ELSE()
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find OpenCOLLADA headers. "
"Disable COLLADA_SUPPORT or fix OpenCOLLADA paths to proceed.")
ENDIF()
IF("$ENV{ICU_INCLUDE_DIR}" STREQUAL "")
MESSAGE(STATUS "No ICU include directory specified")
ElSE()
SET(ICU_INCLUDE_DIR CACHE FILEPATH "ICU header files")
ENDIF()
IF("$ENV{ICU_LIBRARY_DIR}" STREQUAL "")
MESSAGE(STATUS "No ICU library directory specified")
ElSE()
SET(ICU_LIBRARY_DIR CACHE FILEPATH "ICU library files")
ENDIF()
FIND_LIBRARY(icu "icuuc" /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR})
IF(icu)
MESSAGE(STATUS "ICU libraries found")
ADD_DEFINITIONS(-DHAVE_ICU)
ELSE()
MESSAGE(STATUS "Unable to find ICU library files, continuing")
ENDIF()
IF("$ENV{OPENCOLLADA_INCLUDE_DIR}" STREQUAL "")
MESSAGE(STATUS "No OpenCOLLADA include directory specified")
SET(OPENCOLLADA_INCLUDE_DIR "/usr/local/include/opencollada" CACHE FILEPATH "OpenCOLLADA header files")
ElSE()
SET(OPENCOLLADA_INCLUDE_DIR "$ENV{OPENCOLLADA_INCLUDE_DIR}" CACHE FILEPATH "OpenCOLLADA header files")
ENDIF()
IF("$ENV{OPENCOLLADA_LIBRARY_DIR}" STREQUAL "")
MESSAGE(STATUS "No OpenCOLLADA library directory specified")
SET(OPENCOLLADA_LIBRARY_DIR "/usr/local/lib/opencollada" CACHE FILEPATH "OpenCOLLADA library files")
ElSE()
SET(OPENCOLLADA_LIBRARY_DIR "$ENV{OPENCOLLADA_LIBRARY_DIR}" CACHE FILEPATH "OpenCOLLADA library files")
ENDIF()
SET(OPENCOLLADA_INCLUDE_DIRS "${OPENCOLLADA_INCLUDE_DIR}/COLLADABaseUtils" "${OPENCOLLADA_INCLUDE_DIR}/COLLADAStreamWriter")
FIND_FILE(COLLADASWStreamWriter_h "COLLADASWStreamWriter.h" ${OPENCOLLADA_INCLUDE_DIRS})
IF(COLLADASWStreamWriter_h)
MESSAGE(STATUS "OpenCOLLADA header files found")
ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
SET(OPENCOLLADA_LIBRARIES
GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils
OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
OpenCOLLADAStreamWriter UTF buffer ftoa pcre
)
ELSE()
MESSAGE(STATUS "OpenCOLLADA header files not found, continuing without COLLADA support")
ENDIF()
INCLUDE(CheckIncludeFileCXX)
MACRO(CHECK_ADD_OCE_OCC_DEF INCLUDE)
STRING(REPLACE . _ STR ${INCLUDE})
STRING(TOUPPER ${STR} STR)
CHECK_INCLUDE_FILE_CXX("${INCLUDE}" FOUND_${STR})
IF(FOUND_${STR})
ADD_DEFINITIONS(-DOCE_HAVE_${STR})
ADD_DEFINITIONS(-DHAVE_${STR})
ENDIF(FOUND_${STR})
ENDMACRO(CHECK_ADD_OCE_OCC_DEF)
CHECK_ADD_OCE_OCC_DEF(limits)
CHECK_ADD_OCE_OCC_DEF(climits)
CHECK_ADD_OCE_OCC_DEF(limits.h)
CHECK_ADD_OCE_OCC_DEF(fstream)
CHECK_ADD_OCE_OCC_DEF(fstream.h)
CHECK_ADD_OCE_OCC_DEF(iomanip)
CHECK_ADD_OCE_OCC_DEF(iomanip.h)
CHECK_ADD_OCE_OCC_DEF(iostream)
CHECK_ADD_OCE_OCC_DEF(iostream.h)
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release")
ENDIF()
if(ENABLE_BUILD_OPTIMIZATIONS)
if(MSVC)
# NOTE: RelWithDebInfo and Release use O2 (= /Ox /Gl /Gy/ = Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) by default,
# with the exception with RelWithDebInfo has /Ob1 instead. /Ob2 has been observed to improve the performance
# of IfcConvert significantly.
# TODO Setting of /GL and /LTCG don't seem to apply for static libraries (IfcGeom, IfcParse)
# C++
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ob2 /GL")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
# Linker
# /OPT:REF enables also /OPT:ICF and disables INCREMENTAL
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
else()
# GCC-like: Release should use O3 but RelWithDebInfo 02 so enforce 03. Anything other useful that could be added here?
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
endif()
endif()
SET(CMAKE_BUILD_TYPE "Release")
ENDIF(NOT CMAKE_BUILD_TYPE)
IF(MSVC)
# Enable solution folders (free VS versions prior to 2012 don't support solution folders)
if (MSVC_VERSION GREATER 1600)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
ADD_DEFINITIONS(-D_UNICODE)
ElSE(MSVC)
ADD_DEFINITIONS(-fPIC -Wno-non-virtual-dtor)
ENDIF(MSVC)
IF(USE_VLD)
ADD_DEFINITIONS(-DUSE_VLD)
ENDIF()
# Enforce Unicode for CRT and Win32 API calls
ADD_DEFINITIONS(-D_UNICODE -DUNICODE)
# Disable warnings about unsafe C functions; we could use the safe C99 & C11 versions if we have no need for supporting old compilers.
ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
ADD_DEFINITIONS(-bigobj) # required for building the big ifcXXX.objs, https://msdn.microsoft.com/en-us/library/ms173499.aspx
# Bump up the warning level from the default 3 to 4.
ADD_DEFINITIONS(-W4)
IF(MSVC_VERSION GREATER 1800) # > 2013
# Disable overeager and false positives causing C4458 ("declaration of 'indentifier' hides class member"), at least for now.
ADD_DEFINITIONS(-wd4458)
ENDIF()
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
if (MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
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()
ElSE()
add_definitions(-Wall -Wextra)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_definitions(-Wno-tautological-constant-out-of-range-compare)
else()
add_definitions(-Wno-maybe-uninitialized)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0))
# OpenCascade spews a lot of deprecated-copy warnings
add_definitions(-Wno-deprecated-copy)
endif()
# -fPIC is not relevant on Windows and creates pointless warnings
if (UNIX)
add_definitions(-fPIC)
endif()
ENDIF()
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} /usr/inc /usr/local/inc /usr/local/include/oce ${ICU_INCLUDE_DIR} ${Boost_INCLUDE_DIRS})
if (IFCCONVERT_DOUBLE_PRECISION)
SET(CONVERT_PRECISION "-DIFCCONVERT_DOUBLE_PRECISION")
endif()
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR}
ADD_LIBRARY(IfcParse STATIC
../src/ifcparse/Ifc2x3.cpp
../src/ifcparse/Ifc4.cpp
../src/ifcparse/IfcUtil.cpp
../src/ifcparse/IfcParse.cpp
../src/ifcparse/IfcCharacterDecoder.cpp
../src/ifcparse/IfcWrite.cpp
../src/ifcparse/IfcGuidHelper.cpp
../src/ifcparse/IfcHierarchyHelper.cpp
../src/ifcparse/Ifc2x3-rt.cpp
../src/ifcparse/Ifc4-rt.cpp
../src/ifcparse/IfcUntypedEntity.cpp
)
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()
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2")
if(COMPILE_SCHEMA)
# @todo, this appears to be untested at the moment
find_package(PythonInterp)
IF(NOT PYTHONINTERP_FOUND)
MESSAGE(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
ENDIF()
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
# Install pyparsing if necessary
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
execute_process(COMMAND pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
message(WARNING "Failed to find pip. Pip is required to automatically install pyparsing")
endif()
endif()
string(FIND "${PYTHON_PACKAGE_LIST}" pyparsing PYPARSING_FOUND)
if ("${PYPARSING_FOUND}" STREQUAL "-1")
message(STATUS "Installing pyparsing")
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
if (NOT "${SUCCESS}" STREQUAL "0")
execute_process(COMMAND pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
if (NOT "${SUCCESS}" STREQUAL "0")
message(WARNING "Failed to automatically install pyparsing. Please install manually")
endif()
endif()
else()
message(STATUS "Python interpreter with pyparsing found")
endif()
# Bootstrap the parser
message(STATUS "Compiling schema, this will take a while...")
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
WORKING_DIRECTORY ../src/ifcexpressparser
OUTPUT_FILE express_parser.py
RESULT_VARIABLE SUCCESS)
if (NOT "${SUCCESS}" STREQUAL "0")
MESSAGE(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
endif()
# Generate code
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
WORKING_DIRECTORY ../src/ifcparse
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
# Prevent the schema that had just been compiled from being excluded
foreach(s ${SCHEMA_VERSIONS})
if("${COMPILED_SCHEMA_NAME}" STREQUAL "${s}")
list(REMOVE_ITEM IFC_RELEASE_NOT_USED "${s}")
endif()
endforeach()
endif()
# Boost >= 1.58 requires BOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE to build on some Linux distros.
if(NOT Boost_VERSION LESS 105800)
add_definitions(-DBOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE)
endif()
set(IFCOPENSHELL_LIBRARIES IfcParse)
if (BUILD_IFCGEOM)
foreach(s ${SCHEMA_VERSIONS})
set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom_ifc${s})
endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
endif()
if (BUILD_CONVERT)
foreach(s ${SCHEMA_VERSIONS})
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s})
endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
endif()
# IfcParse
file(GLOB IFCPARSE_H_FILES ../src/ifcparse/*.h)
file(GLOB IFCPARSE_CPP_FILES ../src/ifcparse/*.cpp)
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 "0.6.0" SOVERSION "0.6")
TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES})
if (BUILD_IFCGEOM)
# IfcGeom
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h)
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/*.cpp)
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
foreach(s ${SCHEMA_VERSIONS})
add_library(IfcGeom_ifc${s} STATIC ${IFCGEOM_FILES})
set_target_properties(IfcGeom_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s}")
TARGET_LINK_LIBRARIES(IfcGeom_ifc${s} IfcParse ${OPENCASCADE_LIBRARIES})
endforeach()
# IfcGeom (schema agnostic)
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})
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS VERSION "0.6.0" SOVERSION "0.6")
if (UNIX)
find_package(Threads)
endif()
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif(BUILD_IFCGEOM)
if (BUILD_CONVERT)
# Serializers
file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h)
file(GLOB SERIALIZERS_CPP_FILES ../src/serializers/*.cpp)
set(SERIALIZERS_FILES ${SERIALIZERS_H_FILES} ${SERIALIZERS_CPP_FILES})
file(GLOB SERIALIZERS_S_H_FILES ../src/serializers/schema_dependent/*.h)
file(GLOB SERIALIZERS_S_CPP_FILES ../src/serializers/schema_dependent/*.cpp)
set(SERIALIZERS_S_FILES ${SERIALIZERS_S_H_FILES} ${SERIALIZERS_S_CPP_FILES})
foreach(s ${SCHEMA_VERSIONS})
add_library(Serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES})
set_target_properties(Serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}")
TARGET_LINK_LIBRARIES(Serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES})
endforeach()
add_library(Serializers ${SERIALIZERS_FILES})
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}" VERSION "0.6.0" SOVERSION "0.6")
TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
# IfcConvert
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})
set_target_properties(IfcConvert PROPERTIES COMPILE_FLAGS "${CONVERT_PRECISION}")
TARGET_LINK_LIBRARIES(IfcConvert ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
# Only set RPATHs when building shared libraries (i.e. IfcParse and
# IfcGeom are dynamically linked). Not necessarily a perfect solution
# but probably a good indication of whether RPATHs are necessary.
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR}")
endif()
INSTALL(TARGETS IfcConvert
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
ADD_LIBRARY(IfcGeom STATIC
../src/ifcgeom/IfcGeomCurves.cpp
../src/ifcgeom/IfcGeomFaces.cpp
../src/ifcgeom/IfcGeomFunctions.cpp
../src/ifcgeom/IfcGeomHelpers.cpp
../src/ifcgeom/IfcGeomObjects.cpp
../src/ifcgeom/IfcGeomRenderStyles.cpp
../src/ifcgeom/IfcGeomShapes.cpp
../src/ifcgeom/IfcGeomWires.cpp
../src/ifcgeom/IfcRegister.cpp
)
endif(BUILD_CONVERT)
IF(icu)
TARGET_LINK_LIBRARIES(IfcParse icuuc)
ENDIF()
# IfcGeomServer
if(BUILD_GEOMSERVER)
TARGET_LINK_LIBRARIES(IfcGeom IfcParse)
file(GLOB CPP_FILES ../src/ifcgeomserver/*.cpp)
file(GLOB H_FILES ../src/ifcgeomserver/*.h)
set(SOURCE_FILES ${CPP_FILES} ${H_FILES})
ADD_EXECUTABLE(IfcGeomServer ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES})
LINK_DIRECTORIES (${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} ${OPENCOLLADA_LIBRARY_DIR} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR} ${Boost_LIBRARY_DIRS})
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
endif()
INSTALL(TARGETS IfcGeomServer
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
ADD_EXECUTABLE(IfcConvert
../src/ifcconvert/ColladaSerializer.cpp
../src/ifcconvert/IfcConvert.cpp
../src/ifcconvert/OpenCascadeBasedSerializer.cpp
../src/ifcconvert/WavefrontObjSerializer.cpp
)
endif()
TARGET_LINK_LIBRARIES (IfcConvert IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset ${Boost_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
# Documentation
IF(BUILD_DOCUMENTATION)
set(CMAKE_MODULE_PATH "../docs/cmake")
ADD_SUBDIRECTORY(../docs docs)
ENDIF()
ADD_EXECUTABLE(IfcGeomServer
../src/ifcgeomserver/IfcGeomServer.cpp
)
IF(BUILD_IFCPYTHON)
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
ENDIF()
TARGET_LINK_LIBRARIES (IfcGeomServer IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset)
IF(BUILD_EXAMPLES)
ADD_SUBDIRECTORY(../src/examples examples)
ENDIF()
# Build python wrapper using separate CMakeLists.txt
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
IF(BUILD_IFCMAX)
ADD_SUBDIRECTORY(../src/ifcmax ifcmax)
ENDIF()
# Build IfcParseExamples using separate CMakeLists.txt
ADD_SUBDIRECTORY(../src/examples examples)
# ADD_SUBDIRECTORY(../src/qtviewer qtviewer)
# CMake installation targets
INSTALL(FILES ${IFCPARSE_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcparse
SET(include_files_geom
../src/ifcgeom/IfcGeom.h
../src/ifcgeom/IfcGeomObjects.h
../src/ifcgeom/IfcGeomRenderStyles.h
../src/ifcgeom/IfcRegister.h
../src/ifcgeom/IfcRegisterConvertCurve.h
../src/ifcgeom/IfcRegisterConvertFace.h
../src/ifcgeom/IfcRegisterConvertShape.h
../src/ifcgeom/IfcRegisterConvertShapes.h
../src/ifcgeom/IfcRegisterConvertWire.h
../src/ifcgeom/IfcRegisterCreateCache.h
../src/ifcgeom/IfcRegisterDef.h
../src/ifcgeom/IfcRegisterGeomHeader.h
../src/ifcgeom/IfcRegisterIsShapeCollection.h
../src/ifcgeom/IfcRegisterPurgeCache.h
../src/ifcgeom/IfcRegisterUndef.h
../src/ifcgeom/IfcGeomRenderStyles.h
../src/ifcgeom/IfcRepresentationShapeItem.h
)
SET(include_files_parse
../src/ifcparse/Ifc2x3.h
../src/ifcparse/Ifc2x3enum.h
../src/ifcparse/Ifc4.h
../src/ifcparse/Ifc4enum.h
../src/ifcparse/IfcCharacterDecoder.h
../src/ifcparse/IfcException.h
../src/ifcparse/IfcFile.h
../src/ifcparse/IfcHierarchyHelper.h
../src/ifcparse/IfcParse.h
../src/ifcparse/IfcUtil.h
../src/ifcparse/SharedPointer.h
../src/ifcparse/IfcWrite.h
../src/ifcparse/IfcWritableEntity.h
INSTALL(TARGETS IfcParse
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
../src/ifcparse/Ifc2x3-rt.h
../src/ifcparse/Ifc4-rt.h
../src/ifcparse/IfcUntypedEntity.h
../src/ifcparse/IfcEntityDescriptor.h
)
if(BUILD_IFCGEOM)
INSTALL(FILES ${IFCGEOM_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom
)
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
)
INSTALL(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
)
endif()
if(BUILD_CONVERT)
INSTALL(TARGETS Serializers ${SERIALIZER_SCHEMA_LIBRARIES}
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
)
INSTALL(FILES ${SERIALIZERS_FILES}
DESTINATION ${INCLUDEDIR}/serializers/
)
INSTALL(FILES ${SERIALIZERS_S_FILES}
DESTINATION ${INCLUDEDIR}/serializers/schema_dependent
)
endif()
list(APPEND CPACK_SOURCE_IGNORE_FILES
.git
.gitignore
)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/assets")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IfcOpenShell")
set(CPACK_PACKAGE_DESCRIPTION "IfcOpenShell.")
set(CPACK_PACKAGE_VENDOR "Cemosis")
set(CPACK_PACKAGE_CONTACT "Christophe Prud'homme <christophe.prudhomme@cemosis.fr>")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_GENERATOR "TGZ;DEB")
set(CPACK_SOURCE_GENERATOR "TGZ")
FOREACH(COMPONENT IN ITEMS ${BOOST_COMPONENTS})
string(REPLACE "_" "-" COMP ${COMPONENT})
set(BOOST_DEPS "${BOOST_DEPS}, libboost-${COMP}1.67.0")
endforeach(COMPONENT)
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, liboce-foundation11, liboce-modeling11, liboce-ocaf11, liboce-visualization11, liboce-ocaf-lite11, libpython3.8 ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
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")
include(CPack)
INSTALL(FILES ${include_files_geom} DESTINATION include/ifcgeom)
INSTALL(FILES ${include_files_parse} DESTINATION include/ifcparse)
INSTALL(TARGETS IfcConvert DESTINATION bin)
INSTALL(TARGETS IfcParse IfcGeom DESTINATION lib)
-29
View File
@@ -1,29 +0,0 @@
mkdir build && cd build
REM Remove dot from PY_VER for use in library name
REM From https://github.com/tpaviot/pythonocc-core/blob/master/ci/conda/bld.bat
set MY_PY_VER=%PY_VER:.=%
cmake -G "NMake Makefiles" ^
-DCMAKE_INSTALL_PREFIX="%LIBRARY_PREFIX%" ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_PREFIX_PATH="%LIBRARY_PREFIX%" ^
-DCMAKE_SYSTEM_PREFIX_PATH="%LIBRARY_PREFIX%" ^
-DPYTHON_EXECUTABLE="%PYTHON%" ^
-DPYTHON_INCLUDE_DIR="%PREFIX%"/include ^
-DPYTHON_LIBRARY="%PREFIX%"/libs/python%MY_PY_VER%.lib ^
-DBOOST_LIBRARYDIR="%LIBRARY_PREFIX%\lib" ^
-DBOOST_INCLUDEDIR="%LIBRARY_PREFIX%\include" ^
-DOCC_INCLUDE_DIR="%LIBRARY_PREFIX%\include\oce" ^
-DOCC_LIBRARY_DIR="%LIBRARY_PREFIX%\lib" ^
-DCOLLADA_SUPPORT=Off ^
-DBUILD_EXAMPLES=Off ^
-DBUILD_GEOMSERVER=Off ^
-DBUILD_CONVERT=Off ^
../cmake
if errorlevel 1 exit 1
cmake --build . --target INSTALL --config Release
if errorlevel 1 exit 1
-34
View File
@@ -1,34 +0,0 @@
# From https://github.com/tpaviot/pythonocc-core/blob/master/ci/conda/build.sh
if [ "$PY3K" == "1" ]; then
MY_PY_VER="${PY_VER}m"
else
MY_PY_VER="${PY_VER}"
fi
if [ `uname` == Darwin ]; then
PY_LIB="libpython${MY_PY_VER}.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"
else
PY_LIB="libpython${MY_PY_VER}.so"
fi
mkdir build && cd build
cmake \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=$PREFIX \
-DCMAKE_SYSTEM_PREFIX_PATH=$PREFIX \
-DOCC_INCLUDE_DIR=$PREFIX/include/oce \
-DOCC_LIBRARY_DIR=$PREFIX/lib \
-DPYTHON_EXECUTABLE:FILEPATH=$PYTHON \
-DPYTHON_INCLUDE_DIR:PATH=$PREFIX/include/python$MY_PY_VER \
-DPYTHON_LIBRARY:FILEPATH=$PREFIX/lib/${PY_LIB} \
-DCOLLADA_SUPPORT=Off \
../cmake
make -j$CPU_COUNT _ifcopenshell_wrapper
cd ifcwrap
make install/local
-36
View File
@@ -1,36 +0,0 @@
package:
name: ifcopenshell
version: "0.6.0a1"
source:
git_rev: "v0.6.0a1"
git_url: https://github.com/IfcOpenShell/IfcOpenShell
build:
number: 0
features:
- vc9 # [win and py27]
- vc10 # [win and py34]
- vc14 # [win and py35]
- vc14 # [win and py36]
requirements:
build:
- gcc # [osx]
- make
- python
- oce ==0.18.3
- cmake
- swig >=3.0.9
- libboost
- icu
run:
- libgcc # [osx]
- python
- oce ==0.18.3
- libboost
- icu
about:
home: http://ifcopenshell.org
license: LGPL
-40
View File
@@ -1,40 +0,0 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
find_package(Doxygen REQUIRED)
#find_package(Sphinx REQUIRED)
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
MESSAGE(STATUS "SPHINX BUILD ${CMAKE_CURRENT_BINARY_DIR}")
file(MAKE_DIRECTORY ./output/doxygen)
if (DOXYGEN_FOUND)
add_custom_target(Sphinx ALL
COMMAND
${SPHINX_EXECUTABLE} -v -T -b html
${SPHINX_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/output
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output
COMMENT "Generating documentation with Sphinx")
# add_custom_target(ifcopenshell_python_docs ALL
# COMMAND make html
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# COMMENT "Generating documentation with Sphinx")
else (DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)
-2531
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -1,11 +0,0 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
-11
View File
@@ -1,11 +0,0 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
-37
View File
@@ -1,37 +0,0 @@
import textwrap
# The `extensions` list should already be in here from `sphinx-quickstart`
extensions = [
# there may be others here already, e.g. 'sphinx.ext.mathjax'
'breathe',
'exhale',
'sphinx.ext.intersphinx'
]
project = 'IfcOpenShell'
copyright = '2020, IfcOpenShell'
author = ''
# Setup the breathe extension
breathe_projects = {
"My Project": "./output/doxygen/xml"
}
breathe_default_project = "My Project"
# Setup the exhale extension
exhale_args = {
# These arguments are required
"containmentFolder": "./rst_files",
"rootFileName": "library_root.rst",
"rootFileTitle": "IfcOpenShell C++",
"doxygenStripFromPath": "..",
# Suggested optional arguments
"createTreeView": True,
# TIP: if using the sphinx-bootstrap-theme, you need
# "treeViewIsBootstrap": True,
"exhaleExecutesDoxygen": True,
# "exhaleDoxygenStdin": "INPUT = ../src/serializers/ ../src/ifcgeom",
"exhaleUseDoxyfile": True
}
cpp_id_attributes = ['IFC_PARSE_API', 'IFC_GEOM_API']
-10
View File
@@ -1,10 +0,0 @@
IfcOpenShell
============
.. toctree::
:maxdepth: 3
rst_files/library_root
* `ifcopenshell-python documentation <python/html/index.html>`_
-39
View File
@@ -1,39 +0,0 @@
# This program requires doxygen, sphinx, breathe and exhale.
import os
import sys
import shutil
import subprocess
import multiprocessing
# some extra check to see if we can find sphinx in pypy bin dir
sphinx_build = os.path.join(os.path.dirname(sys.executable), 'sphinx-build')
if not os.path.exists(sphinx_build):
sphinx_build = "sphinx-build"
# else:
# os.environ['PATH'] = os.pathsep + os.path.dirname(sys.executable)
# not used, -j caused issues in my sphinx install
j = str(multiprocessing.cpu_count())
if not os.path.isdir("./output/doxygen/xml"):
os.makedirs("output/doxygen/xml")
subprocess.check_call([sphinx_build, "-b", "html", ".", "output"])
if sys.platform == "linux" or sys.platform == "linux2":
if os.path.isdir("output/python"):
shutil.rmtree("output/python")
if not os.path.exists(os.path.join("../src/ifcblenderexport/docs", "contents.rst")):
subprocess.check_call(["ln", "index.rst", "contents.rst"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["sed", "-i", "s/html_theme/# html_theme/g", "conf.py"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["make", "html"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["git", "checkout", "conf.py"], cwd="../src/ifcblenderexport/docs")
shutil.move("../src/ifcblenderexport/docs/_build", "./output")
os.rename("./output/_build", "./output/python")
elif sys.platform == "win32" or sys.platform == "win64":
subprocess.check_call(["make.bat", "html"])
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

-240
View File
@@ -1,240 +0,0 @@
APPLY_DEFAULT_MATERIALS
-----------------------
Given the command invocation:
Duplex_A_20110907_optimized.ifc d.dae -yv --include attribute GlobalId 3bXiCStxP6Fgxdej$yc50U
You will find log messages along the lines of
[Warning] {3bXiCStxP6Fgxdej$yc50U} No material and surface styles for:
#333=IfcCovering('3bXiCStxP6Fgxdej$yc50U',#1,'Compound Ceiling:Gypsum Board:187483',$,'Compound Ceiling:Gypsum Board',#17840,#17052,'187483',.CEILING.)
This means that there is no IfcStyledItem associated to the representation items and that the element does not have an IfcMaterial association with IfcMaterialRepresentation from which we can derive a style (colour) for the element.
The interactive session below shows how with this setting enabled you will get a default generated material from the IFC element entity type and material indices of 0 pointing to that. With this setting disabled the material index would be -1 to indicate a missing style. Note that there is one material index for every triangle in the list of `shp.geometry.faces`.
>>> import ifcopenshell, ifcopenshell.geom
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> s = ifcopenshell.geom.settings()
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>>
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[]
>>>
>>> s.set(s.APPLY_DEFAULT_MATERIALS, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.material_ids
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
[('IfcCovering', (0.7, 0.7, 0.7))]
This is enabled by default for the IfcConvert serializers as they will not gracefully handle -1 material indices and allows users to quickly assign colours based on entity types in their modelling applications.
APPLY_LAYERSETS
---------------
This setting is available in IfcConvert as `--enable-layerset-slicing`.
For IfcWall and IfcSlab elements, takes the associated IfcMaterialLayerSet and builds a set of surfaces to segment the building element geometry.
Note that enabling this settings is computationally intensive as it involves 3D Boolean operations.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d2.dae --enable-layerset-slicing -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d2.dae](images/settings-2.png)
BUILDING_LOCAL_PLACEMENT
------------------------
This setting is available in IfcConvert using `--building-local-placement`.
In the typical IfcSite > IfcBuilding > IfcBuildingStorey > ... hierarchy of elements, don't incorporate the ObjectPlacement of the IfcBuilding and above in the placement of elements in the output. This is useful when there is a large offset in this placement that reduces precision in further processing.
CONVERT_BACK_UNITS
------------------
This setting is available in IfcConvert using `--convert-back-units`.
Internally IfcOpenShell uses meters as the global length unit to do calculations. This setting restores the coordinate positions after conversion by multiplying the factor of the IfcUnit with UnitType=LENGTHUNIT into the output geometry coordinate values.
DISABLE_OPENING_SUBTRACTIONS
----------------------------
This setting is available in IfcConvert using `--disable-opening-subtraction`.
As in most viewer applications, IfcOpeningElement geometry is subtracted from their host elements. This setting disables this behavior.
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d1.dae](images/settings-1.png)
Duplex_A_20110907_optimized.ifc d3.dae --disable-opening-subtraction -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d3.dae](images/settings-3.png)
Note that disabling this settings will reduce processing time and improve robustness as it involves 3D Boolean operations.
DISABLE_TRIANGULATION
---------------------
To be used in conjunction with `USE_BREP_DATA`. Do not apply the triangulation and - when `USE_BREP_DATA` is set - return a OpenCASCADE serialized TopoDS_Shape from `create_shape()` and `iterator`.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.DISABLE_TRIANGULATION, True)
>>> s.set(s.USE_BREP_DATA, True)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> print(shp.geometry.brep_data)
CASCADE Topology V1, (c) Matra-Datavision
Locations 0
Curve2ds 0
Curves 12
1 4.6750000000000034 -8.0749999999999904 2.657 -2.0455514041918775e-15 -1 0
1 4.6750000000000034 -8.0749999999999904 2.657 1 -3.435893306383461e-15 0
1 6.2260000000000044 -8.0749999999999957 2.657 -2.0455514041918724e-15 -1 0
1 6.226 -10.246000000000031 2.657 -1 6.8717866127669219e-15 0
...
EDGE_ARROWS
-----------
When `INCLUDE_CURVES` is true and geometric elements include curves (such as the wall axis), add arrow heads to the edges to indicate direction of the curve.
Duplex_A_20110907_optimized.ifc d4.dae --model --plan --edge-arrows -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d4.dae](images/settings-4.png)
EXCLUDE_SOLIDS_AND_SURFACES
---------------------------
Exclude faces, shells and solids from geometrical output. Implied when using `--plan` without `--model` in IfcConvert.
FASTER_BOOLEANS
---------------
NOTE: Only applicable when using OCCT 6.9 and earlier.
This setting is available in IfcConvert using `--merge-boolean-operands`.
Fuse the collection of all boolean operands into a single union before applying the boolean subtraction, as opposed to doing individual subtractions. This likely improves performance. From OCCT 7.0 onwards the boolean operations with multiple arguments is used.
GENERATE_UVS
------------
This setting is available in IfcConvert using `--generate-uvs`.
Applies a box projection on the generated geometry for the element to obtain UV coordinates. This is purely generated, it does not involve texture coordinates stored in the IFC model.
Duplex_A_20110907_optimized.ifc d5.dae --generate-uvs -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
![Image of d5.dae with a UV grid applied in Blender](images/settings-5.png)
INCLUDE_CURVES
--------------
This setting is available in IfcConvert using `--plan`.
Include edge and wire geometries in the geometric output.
LAYERSET_FIRST
--------------
This setting is available in IfcConvert using `--layerset-first`.
When not using APPLY_LAYERSETS, take the first material layer from the set to use as the material for the overall element.
NO_NORMALS
----------
This setting is available in IfcConvert using `--no-normals`.
Do not emit normals on geometric output
SEARCH_FLOOR
------------
Note: Only applicable to Collada .DAE output when used from IfcConvert.
This setting is available in IfcConvert using `--use-element-hierarchy`.
Include the spatial hierarchy in the elements.
SEW_SHELLS
----------
This setting is available in IfcConvert using `--orient-shells`.
Re-orient or sew connected face sets to have a consistent outwards orientation.
SITE_LOCAL_PLACEMENT
--------------------
This setting is available in IfcConvert using `--site-local-placement`.
See `BUILDING_LOCAL_PLACEMENT`, but exclude also the ObjectPlacement of the IfcSite.
USE_BREP_DATA
-------------
See `DISABLE_TRIANGULATION`.
USE_PYTHON_OPENCASCADE
----------------------
Note: Only available when an import of `OCC.Core.BRepTools` or `OCC.BRepTools` succeeds.
This implies `USE_WORLD_COORDS` `USE_BREP_DATA` and `DISABLE_TRIANGULATION`. The serialized TopoDS_Shape of `USE_BREP_DATA` is deserialized by Python OpenCASCADE.
USE_WORLD_COORDS
----------------
Apply the ObjectPlacement of the building elements to the geometric output. This is implied when using the Wavefront .OBJ output in IfcConvert. Note that this also eliminates the possibility for geometric elements to point to the same interpreted geometry result.
VALIDATE_QUANTITIES
-------------------
This setting is available in IfcConvert using `--validate`.
Running IfcConvert with `--validate` will set a non-zero exit code when ever a log message with severity equal or greater than ERROR has been emitted.
Currently for internal use only. For every building element geometry converted, looks for an associated quantity set where the OwnerHistory's organization name is IfcOpenShell. And looks for the quantities "Total Surface Area", "Volume", "Shape Validation Properties.Surface Genus" and validates these according to the interpreted geometry definition. Emit Logger::Error when calculated values are outside of the tolerance range for the value stored in the model.
WELD_VERTICES
-------------
Note: In Python, this setting is *on* by default.
Note: This setting only affects triangulated output.
This setting is available in IfcConvert using `--weld-vertices`.
Discards normals and joins vertices solely based on position. This is useful when output is to be modified in a modeling application.
>>> import ifcopenshell, ifcopenshell.geom
>>> s = ifcopenshell.geom.settings()
>>> s.set(s.WELD_VERTICES, False)
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.657, 4.674999999999999, -10.24600000000002, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226, -10.24600000000003, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226000000000004, -8.074999999999996, 2.6)
>>> shp.geometry.normals
(3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0)
>>>
>>> s.set(s.WELD_VERTICES, True)
>>> shp = ifcopenshell.geom.create_shape(s, c)
>>> shp.geometry.verts
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6)
>>> shp.geometry.normals
()
-10
View File
@@ -1,10 +0,0 @@
IfcOpenShell
============
.. toctree::
:maxdepth: 3
rst_files/library_root
* `ifcopenshell-python documentation <python/html/index.html>`_
-784
View File
@@ -1,784 +0,0 @@
#!/usr/bin/python
###############################################################################
# #
# 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 script builds IfcOpenShell and its dependencies #
# #
# Prerequisites for this script to function correctly: #
# * cmake * git * bzip2 * tar * c(++) compilers * yacc * autoconf #
# #
# if building with USE_OCCT additionally: #
# * freetype * glx.h #
# #
# if building with OCCT 7.4.0 additionally: #
# * libfontconfig1-dev #
# #
# if building with -shared #
# * libgl1-mesa-dev libxext-dev libxmu-dev libxmu-headers libxi-dev #
# #
# for python37 to install correctly additionally: #
# * libffi(-dev[el]) #
# #
# on debian 7.8 these can be obtained with: #
# $ apt-get install git gcc g++ autoconf bison bzip2 cmake #
# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev #
# #
# on ubuntu 14.04: #
# $ apt-get install git gcc g++ autoconf bison make cmake #
# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev #
# #
# on OS X El Capitan with homebrew: #
# $ brew install git bison autoconf automake freetype libffi cmake #
# #
###############################################################################
from __future__ import print_function
import logging
import os
import sys
import subprocess as sp
import shutil
import time
import tarfile
import multiprocessing
PYTHON_MAJOR = sys.version_info[0]
if PYTHON_MAJOR >= 3:
from urllib.request import urlretrieve
else:
# Not Python 3 - today, it is most likely to be Python 2
# But note that this might need an update when Python 4
# might be around one day
from urllib import urlretrieve
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
logger.addHandler(ch)
PROJECT_NAME="IfcOpenShell"
PYTHON_VERSIONS=["2.7.16", "3.2.6", "3.3.6", "3.4.6", "3.5.3", "3.6.2", "3.7.3", "3.8.6", "3.9.1"]
JSON_VERSION="v3.6.1"
OCE_VERSION="0.18"
# OCCT_VERSION="7.1.0"
# OCCT_HASH="89aebde"
# OCCT_VERSION="7.2.0"
# OCCT_HASH="88af392"
OCCT_VERSION="7.3.0p3"
BOOST_VERSION="1.71.0"
#PCRE_VERSION="8.39"
PCRE_VERSION="8.41"
#LIBXML2_VERSION="2.9.3"
LIBXML2_VERSION="2.9.9"
SWIG_VERSION="3.0.12"
#SWIG_VERSION="4.0.0"
#OPENCOLLADA_VERSION="v1.6.63"
OPENCOLLADA_VERSION="v1.6.68"
# binaries
cp="cp"
bash="bash"
uname="uname"
git="git"
bunzip2="bunzip2"
tar="tar"
cc="cc"
cplusplus="c++"
autoconf="autoconf"
automake="automake"
yacc="yacc"
make="make"
date = "date"
curl="curl"
wget="wget"
strip="strip"
# Helper function for coloured printing
NO_COLOR="\033[0m" # <ref>http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux</ref>
BLACK_ON_WHITE="\033[0;30;107m"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
MAGENTA="\033[35m"
def cecho(message, color=NO_COLOR):
"""Logs message `message` in color `color`."""
logger.info("%s%s\033[0m" % (color, message))
def which(cmd):
for path in os.environ["PATH"].split(":"):
if os.path.exists(path) and cmd in os.listdir(path):
return cmd
return None
def get_os():
ret_value = sp.check_output([uname, "-s"]).strip()
return ret_value
def to_pystring(x):
""" Python 2 & 3 compatibility function for strings handling
(to solve TypeError "Can't mix strings and bytes in path components" for Python 3).
Reference https://github.com/hugsy/gef/issues/382 """
res = str(x, encoding="utf-8") if PYTHON_MAJOR == 3 else x
substs = [("\n","\\n"), ("\r","\\r"), ("\t","\\t"), ("\v","\\v"), ("\b","\\b"), ]
for x,y in substs: res = res.replace(x,y)
return res
# Set defaults for missing empty environment variables
USE_OCCT = os.environ.get("USE_OCCT", "true").lower() == "true"
TOOLSET = None
if get_os() == "Darwin":
# C++11 features used in OCCT 7+ need a more recent stdlib
TOOLSET = "10.9" if USE_OCCT else "10.6"
# python 3.4 doesn't seem to build anymore on recent versions of clang
if get_os() == "Darwin":
try:
PYTHON_VERSIONS.remove("3.4.6")
except ValueError as e:
pass
try:
IFCOS_NUM_BUILD_PROCS = os.environ["IFCOS_NUM_BUILD_PROCS"]
except KeyError:
IFCOS_NUM_BUILD_PROCS=multiprocessing.cpu_count() + 1
try:
TARGET_ARCH = os.environ["TARGET_ARCH"]
except KeyError:
TARGET_ARCH = sp.check_output([uname, "-m"]).strip()
CMAKE_DIR=os.path.realpath(os.path.join("..", "cmake"))
try:
DEPS_DIR = os.environ["DEPS_DIR"]
except KeyError:
path = [b"..", b"build", sp.check_output(uname).strip(), TARGET_ARCH]
if TOOLSET:
path.append(TOOLSET)
DEPS_DIR = to_pystring(os.path.realpath(os.path.join(*path)))
if not os.path.exists(DEPS_DIR):
os.makedirs(DEPS_DIR)
try:
BUILD_CFG=os.environ["BUILD_CFG"]
except KeyError:
BUILD_CFG="RelWithDebInfo"
# Print build configuration information
cecho ("""This script fetches and builds %s and its dependencies
""" % (PROJECT_NAME,), BLACK_ON_WHITE)
cecho("""Script configuration:
""", GREEN)
cecho("""* Target Architecture = %s""" % (TARGET_ARCH,), MAGENTA)
cecho(" - Whether 32-bit (i686) or 64-bit (x86_64) will be built.")
cecho("""* USE_OCCT = %r""" % (USE_OCCT,), MAGENTA)
if USE_OCCT:
cecho(" - Compiling against official Open Cascade")
else:
cecho(" - Compiling against Open Cascade Community Edition")
cecho("* Dependency Directory = %s" % (DEPS_DIR,), MAGENTA)
cecho(" - The directory where %s dependencies are installed." % (PROJECT_NAME,))
cecho("* Build Config Type = %s" % (BUILD_CFG,), MAGENTA)
cecho(""" - The used build configuration type for the dependencies.
Defaults to RelWithDebInfo if not specified.""")
if BUILD_CFG == "MinSizeRel":
cecho(" WARNING: MinSizeRel build can suffer from a significant performance loss.", RED)
cecho("* IFCOS_NUM_BUILD_PROCS = %s" % (IFCOS_NUM_BUILD_PROCS,), MAGENTA)
cecho(""" - How many compiler processes may be run in parallel.
""")
dependency_tree = {
'IfcParse': ('boost', 'libxml2'),
'IfcGeom': ('IfcParse', 'occ'),
'IfcConvert': ('IfcGeom', 'OpenCOLLADA', 'json'),
'OpenCOLLADA': ('libxml2', 'pcre'),
'IfcGeomServer': ('IfcGeom',),
'IfcOpenShell-Python': ('python', 'swig', 'IfcGeom'),
'swig': ('pcre',),
'boost': (),
'libxml2': (),
'python': (),
'occ': (),
'pcre': (),
'json': ()
}
def v(dep):
yield dep
for d in dependency_tree[dep]:
for x in v(d):
yield x
tgts = [s for s in sys.argv[1:] if not s.startswith("-")]
flags = set(s for s in sys.argv[1:] if s.startswith("-"))
BUILD_STATIC = not "-shared" in flags
ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared"
DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static"
LINK_TYPE = "static" if BUILD_STATIC else "shared"
LINK_TYPE_UCFIRST = LINK_TYPE[0].upper() + LINK_TYPE[1:]
LIBRARY_EXT = "a" if BUILD_STATIC else "so"
PIC = "-fPIC" if BUILD_STATIC else ""
if len(tgts):
targets = set(sum((list(v(target)) for target in tgts), []))
else:
targets = set(dependency_tree.keys())
print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
# Check that required tools are in PATH
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "cmake"]:
if which(cmd) is None:
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
# identifiers for the download tool (could be less memory consuming as ints, but are more verbose as strings)
download_tool_default = download_tool_py = "py"
download_tool_git = "git"
# Create log directory and file
log_dir = os.path.join(DEPS_DIR, "logs")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
LOG_FILE="%s.log" % (os.path.join(log_dir, to_pystring(sp.check_output([date, "+%Y%m%d"]).strip())),)
if not os.path.exists(LOG_FILE):
open(LOG_FILE, "w").close()
logger.info("using command log file '%s'" % (LOG_FILE,))
def run(cmds, cwd=None):
"""
Wraps `subprocess.Popen.communicate()` and logs the command being executed,
sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout
with leading and trailing whitespace removed.
"""
logger.debug("running command %r in directory %r" % (" ".join(cmds), cwd))
log_file_handle = open(LOG_FILE, "ab")
proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE)
stdout, stderr = proc.communicate()
log_file_handle.write(stdout)
log_file_handle.write(stderr)
log_file_handle.close()
logger.debug("command returned %r" % proc.returncode)
if proc.returncode != 0:
print("-" * 70)
print(stderr)
print("-" * 70)
raise Exception("Command `%s` returned exit code %d" % (" ".join(cmds), proc.returncode))
return stdout.strip()
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
OCE_LOCATION="https://github.com/tpaviot/oce/archive/OCE-%s.tar.gz" % (OCE_VERSION,)
BOOST_LOCATION="https://dl.bintray.com/boostorg/release/%s/source/" % (BOOST_VERSION,)
# Helper functions
def run_autoconf(arg1, configure_args, cwd):
configure_path = os.path.realpath(os.path.join(cwd, "..", "configure"))
if not os.path.exists(configure_path):
run([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
# Using `sh` over `bash` fixes issues with building swig
run(["/bin/sh", "../configure"]+configure_args+["--prefix=%s" % (os.path.realpath("%s/install/%s" % (DEPS_DIR, arg1)),)], cwd=cwd)
def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
if cmake_dir is None:
P=".."
else:
P=cmake_dir
run(["cmake", P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
"""Lazily clones the `git` repository denoted by `clone_url` into
the `target_dir` or pulls latest changes if the `target_dir` exists (naively assumes
that a working clone exists there) and optionally checks out a revision
`revision` after cloning or in the existing clone if `revision` is not
`None`."""
if not os.path.exists(target_dir):
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
run([git, "clone", "--recursive", clone_url, target_dir])
else:
logger.info("directory '%s' already cloned. Pulling latest changes." % (target_dir,))
# detect whether we are on a branch and pull
if run([git, "rev-parse", "--abbrev-ref", "HEAD"], cwd=target_dir) != "HEAD":
run([git, "pull", clone_url], cwd=target_dir)
if revision != None:
run([git, "checkout", revision], cwd=target_dir)
def build_dependency(name, mode, build_tool_args, download_url, download_name, download_tool=download_tool_default, revision=None, patch=None, additional_files={}, no_append_name=False):
"""Handles building of dependencies with different tools (which are
distinguished with the `mode` argument. `build_tool_args` is expected to be
a list which is necessary in order to not mess up quoting of compiler and
linker flags."""
check_dir = os.path.join(DEPS_DIR, "install", name)
if os.path.exists(check_dir):
logger.info( "Found existing %s, skipping" % (name,))
return
build_dir = os.path.join(DEPS_DIR, "build")
if not os.path.exists(build_dir):
os.makedirs(build_dir)
logger.info("\rFetching %s... " % (name,))
if download_tool == download_tool_py:
if no_append_name:
url = download_url
else:
url = os.path.join(download_url, download_name)
download_path = os.path.join(build_dir, download_name)
if not os.path.exists(download_path):
urlretrieve(url, os.path.join(build_dir, download_path))
else:
logger.info("Download '%s' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping" % (download_path,))
elif download_tool == download_tool_git:
logger.info("\rChecking %s... " % (name,))
git_clone_or_pull_repository(download_url, target_dir=os.path.join(build_dir, download_name), revision=revision)
else:
raise ValueError("download tool '%s' is not supported" % (download_tool,))
download_dir = os.path.join(build_dir, download_name)
if os.path.isdir(download_dir):
extract_dir_name=download_name
extract_dir = os.path.join(build_dir, extract_dir_name)
else:
download_tarfile_path = os.path.join(build_dir, download_name)
if download_name.endswith(".tar.gz") or download_name.endswith(".tgz"):
compr = "gz"
elif download_name.endswith(".tar.bz2"):
compr = "bz2"
else:
raise RuntimeError("fix source for new download type")
download_tarfile = tarfile.open(name=download_tarfile_path, mode="r:%s" % (compr,))
extract_dir_name= os.path.commonprefix(download_tarfile.getnames()) # tarfile seriously doesn't have a function to retrieve the root directory more easily
#run([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works
if extract_dir_name is None:
extract_dir_name= run([bash, "-c", "tar -tf %s 2> /dev/null | head -n 1 | cut -f1 -d /" % (download_name,)], cwd=build_dir)
extract_dir = os.path.join(build_dir, extract_dir_name)
if not os.path.exists(extract_dir):
run([tar, "-xf", download_name], cwd=build_dir)
for path, url in additional_files.items():
if not os.path.exists(path):
urlretrieve(url, os.path.join(extract_dir, path))
if patch is not None:
patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), patch))
if os.path.exists(patch_abs):
try: run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir)
except Exception as e:
# Assert that the patch has already been applied
run(["patch", "-p1", "--batch", "--reverse", "--dry-run", "-i", patch_abs], cwd=extract_dir)
if mode != "bjam":
extract_build_dir = os.path.join(extract_dir, "build")
if os.path.exists(extract_build_dir):
shutil.rmtree(extract_build_dir)
os.makedirs(extract_build_dir)
logger.info("\rConfiguring %s..." % (name,))
if mode == "autoconf":
run_autoconf(name, build_tool_args, cwd=extract_build_dir)
elif mode == "cmake":
run_cmake(name, build_tool_args, cwd=extract_build_dir)
else:
raise ValueError()
logger.info("\rBuilding %s... " % (name,))
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "VERBOSE=1"], cwd=extract_build_dir)
logger.info( "\rInstalling %s... " % (name,))
run([make, "install"], cwd=extract_build_dir)
logger.info( "\rInstalled %s \n" % (name,))
else:
logger.info( "\rConfiguring %s..." % (name,))
run([bash, "./bootstrap.sh"], cwd=extract_dir)
logger.info("\rBuilding %s... " % (name,))
run(["./b2", "-j%s" % (IFCOS_NUM_BUILD_PROCS,)]+build_tool_args, cwd=extract_dir)
logger.info("\rInstalling %s... " % (name,))
shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", "boost-%s" % BOOST_VERSION, "boost"))
logger.info("\rInstalled %s \n" % (name,))
cecho("Collecting dependencies:", GREEN)
# Set compiler flags for 32bit builds on 64bit system
# TODO: This is untested
ADDITIONAL_ARGS=[]
BOOST_ADDRESS_MODEL=[]
if TARGET_ARCH == "i686" and run([uname, "-m"]).strip() == "x86_64":
if get_os() == "Darwin":
ADDITIONAL_ARGS=["-m32", "-arch i386"]
else:
ADDITIONAL_ARGS=["-m32"]
BOOST_ADDRESS_MODEL=["architecture=x86", "address-model=32"]
if get_os() == "Darwin":
ADDITIONAL_ARGS=["-mmacosx-version-min=%s" % TOOLSET]+ADDITIONAL_ARGS
# If the linker supports GC sections, set it up to reduce binary file size
# -fPIC is required for the shared libraries to work
CXXFLAGS=os.environ.get("CXXFLAGS", "")
CFLAGS=os.environ.get("CFLAGS", "")
LDFLAGS=os.environ.get("LDFLAGS", "")
if sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0:
CXXFLAGS_MINIMAL="%s %s %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS_MINIMAL="%s %s %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
if BUILD_STATIC:
CXXFLAGS="%s %s -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS="%s %s -fdata-sections -ffunction-sections -fvisibility=hidden %s"% (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
else:
CXXFLAGS=CXXFLAGS_MINIMAL
CFLAGS=CFLAGS_MINIMAL
LDFLAGS="%s -Wl,--gc-sections %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
else:
CXXFLAGS_MINIMAL="%s %s %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS_MINIMAL="%s %s %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
if BUILD_STATIC:
CXXFLAGS="%s %s -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
CFLAGS="%s %s -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CFLAGS, PIC, str.join(" ", ADDITIONAL_ARGS))
else:
CXXFLAGS=CXXFLAGS_MINIMAL
CFLAGS=CFLAGS_MINIMAL
LDFLAGS="%s %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
os.environ["CXXFLAGS"] = CXXFLAGS
os.environ["CFLAGS"] = CFLAGS
os.environ["LDFLAGS"] = LDFLAGS
# Some dependencies need a more recent CMake version than most distros provide
# @tfk: this is no longer needed
# 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 "json" in targets:
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
json_install_path = "{DEPS_DIR}/install/json/nlohmann/json.hpp".format(**locals())
if not os.path.exists(os.path.dirname(json_install_path)):
os.makedirs(os.path.dirname(json_install_path))
if not os.path.exists(json_install_path):
urlretrieve(json_url, json_install_path)
if "pcre" in targets:
build_dependency(
name="pcre-{PCRE_VERSION}".format(**locals()),
mode="autoconf",
build_tool_args=[DISABLE_FLAG],
download_url="https://downloads.sourceforge.net/project/pcre/pcre/{PCRE_VERSION}/".format(**locals()),
download_name="pcre-{PCRE_VERSION}.tar.bz2".format(**locals())
)
# An issue exists with swig-1.3 and python >= 3.2
# Therefore, build a recent copy from source
if "swig" in targets:
build_dependency(
name="swig",
mode="autoconf",
build_tool_args=["--disable-ccache", "--with-pcre-prefix={DEPS_DIR}/install/pcre-{PCRE_VERSION}".format(**locals())],
download_url="https://github.com/swig/swig.git",
download_name="swig",
download_tool=download_tool_git,
revision="rel-{SWIG_VERSION}".format(**locals())
)
if USE_OCCT and "occ" in targets:
build_dependency(
name="occt-{OCCT_VERSION}".format(**locals()),
mode="cmake",
build_tool_args=[
"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}".format(**locals()),
"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}".format(**locals()),
"-DBUILD_MODULE_Draw=0",
"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off"
],
download_url = "https://git.dev.opencascade.org/repos/occt.git",
download_name = "occt",
download_tool=download_tool_git,
patch=None if OCCT_VERSION >= "7.4" else "./patches/occt/enable-exception-handling.patch",
revision="V" + OCCT_VERSION.replace('.', '_')
)
elif "occ" in targets:
build_dependency(
name="oce-{OCE_VERSION}".format(**locals()),
mode="cmake",
build_tool_args=[
"-DOCE_DISABLE_TKSERVICE_FONT=ON",
"-DOCE_TESTING=OFF",
"-DOCE_BUILD_SHARED_LIB=OFF",
"-DOCE_DISABLE_X11=ON",
"-DOCE_VISUALISATION=OFF",
"-DOCE_OCAF=OFF",
"-DOCE_INSTALL_PREFIX={DEPS_DIR}/install/oce-{OCE_VERSION}".format(**locals())
],
download_url="https://github.com/tpaviot/oce/archive/",
download_name="OCE-{OCE_VERSION}.tar.gz".format(**locals())
)
if "libxml2" in targets:
build_dependency(
"libxml2-{LIBXML2_VERSION}".format(**locals()),
"autoconf",
build_tool_args=[
"--without-python",
ENABLE_FLAG,
DISABLE_FLAG,
"--without-zlib",
"--without-iconv",
"--without-lzma"
],
download_url="ftp://xmlsoft.org/libxml2/",
download_name="libxml2-{LIBXML2_VERSION}.tar.gz".format(**locals())
)
if "OpenCOLLADA" in targets:
build_dependency(
"OpenCOLLADA",
"cmake",
build_tool_args=[
"-DLIBXML2_INCLUDE_DIR={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
"-DLIBXML2_LIBRARIES={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}".format(**locals()),
"-DPCRE_INCLUDE_DIR={DEPS_DIR}/install/pcre-{PCRE_VERSION}/include".format(**locals()),
"-DPCRE_PCREPOSIX_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcreposix.{LIBRARY_EXT}".format(**locals()),
"-DPCRE_PCRE_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcre.{LIBRARY_EXT}".format(**locals()),
"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/OpenCOLLADA/".format(**locals())
],
download_url="https://github.com/KhronosGroup/OpenCOLLADA.git",
download_name="OpenCOLLADA",
download_tool=download_tool_git,
patch="./patches/opencollada/pr622_and_disable_subdirs.patch",
revision=OPENCOLLADA_VERSION
)
if "python" in targets:
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
OLD_CXX_FLAGS=os.environ["CXXFLAGS"]
OLD_C_FLAGS=os.environ["CFLAGS"]
os.environ["CXXFLAGS"]=CXXFLAGS_MINIMAL
os.environ["CFLAGS"]=CFLAGS_MINIMAL
# On OSX a dynamic python library is built or it would not be compatible
# with the system python because of some threading initialization
PYTHON_CONFIGURE_ARGS=[]
if get_os() == "Darwin":
PYTHON_CONFIGURE_ARGS=["--disable-static", "--enable-shared"]
def get_python_unicode_confs(py_ver):
if py_ver < "3.3":
return [("--enable-unicode=ucs2",""), ("--enable-unicode=ucs4","u")]
else: return [("","")]
def PYTHON_VERSION_CONFS():
for v in PYTHON_VERSIONS:
for unicode_conf, abi_tag in get_python_unicode_confs(v):
yield v, unicode_conf, abi_tag
for PYTHON_VERSION, unicode_conf, abi_tag in PYTHON_VERSION_CONFS():
try:
build_dependency(
"python-{PYTHON_VERSION}{abi_tag}".format(**locals()),
"autoconf",
PYTHON_CONFIGURE_ARGS + [unicode_conf],
"http://www.python.org/ftp/python/{PYTHON_VERSION}/".format(**locals()),
"Python-{PYTHON_VERSION}.tgz".format(**locals())
)
except Exception as e:
pyver2 = PYTHON_VERSION[:PYTHON_VERSION.rfind('.')]
if os.path.exists("{DEPS_DIR}/install/python-{PYTHON_VERSION}{abi_tag}/bin/python{pyver2}".format(**locals())):
print("Installation partially failed")
else: raise e
os.environ["CXXFLAGS"]=OLD_CXX_FLAGS
os.environ["CFLAGS"]=OLD_C_FLAGS
if "boost" in targets:
str_concat = lambda prefix: lambda postfix: "" if postfix.strip() == "" else "=".join((prefix, postfix.strip()))
build_dependency(
"boost-{BOOST_VERSION}".format(**locals()),
mode="bjam",
build_tool_args=[
"--stagedir={DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
"--with-system",
"--with-program_options",
"--with-regex",
"--with-thread",
"--with-date_time",
"--with-iostreams",
"link={LINK_TYPE}".format(**locals())
] + \
BOOST_ADDRESS_MODEL + \
list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \
list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \
["stage", "-s", "NO_BZIP2=1"],
download_url=BOOST_LOCATION,
download_name="boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2".format(**locals())
)
cecho("Building IfcOpenShell:", GREEN)
IFCOS_DIR=os.path.join(DEPS_DIR, "build", "ifcopenshell")
if os.path.exists(IFCOS_DIR):
shutil.rmtree(IFCOS_DIR)
os.makedirs(IFCOS_DIR)
executables_dir = os.path.join(IFCOS_DIR, "executables")
if not os.path.exists(executables_dir):
os.makedirs(executables_dir)
logger.info("\rConfiguring executables...")
OFF_ON = ["OFF", "ON"]
cmake_args=[
"-DUSE_MMAP=" "OFF",
"-DBUILD_EXAMPLES=" "OFF",
"-DBUILD_IFCPYTHON=" "OFF",
"-DBUILD_SHARED_LIBS=" +OFF_ON[not BUILD_STATIC],
"-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets],
"-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets],
"-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets],
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell".format(**locals()),
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
"-DGLTF_SUPPORT=" "ON",
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
"-DBoost_NO_BOOST_CMAKE=" "On"
]
if "occ" in targets and USE_OCCT:
occ_include_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib".format(**locals())
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
])
elif "occ" in targets:
occ_include_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
])
if "OpenCOLLADA" in targets:
cmake_args.extend([
"-DOPENCOLLADA_INCLUDE_DIR=" "{DEPS_DIR}/install/OpenCOLLADA/include/opencollada".format(**locals()),
"-DOPENCOLLADA_LIBRARY_DIR=" "{DEPS_DIR}/install/OpenCOLLADA/lib/opencollada".format(**locals())
])
if "pcre" in targets:
cmake_args.append(
"-DPCRE_LIBRARY_DIR=" "{DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib".format(**locals())
)
if "libxml2" in targets:
cmake_args.extend([
"-DLIBXML2_INCLUDE_DIR=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
"-DLIBXML2_LIBRARIES=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}".format(**locals())
])
run_cmake("", cmake_args, cmake_dir=CMAKE_DIR, cwd=executables_dir)
logger.info("\rBuilding executables... ")
run([make, "-j{IFCOS_NUM_BUILD_PROCS}".format(**locals())], cwd=executables_dir)
run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir)
if "IfcOpenShell-Python" in targets:
# On OSX the actual Python library is not linked against.
ADDITIONAL_ARGS=""
if get_os() == "Darwin":
ADDITIONAL_ARGS="-Wl,-flat_namespace,-undefined,suppress"
os.environ["CXXFLAGS"]="%s %s" % (CXXFLAGS_MINIMAL, ADDITIONAL_ARGS)
os.environ["CFLAGS"]="%s %s" % (CFLAGS_MINIMAL, ADDITIONAL_ARGS)
os.environ["LDFLAGS"]="%s %s" % (LDFLAGS, ADDITIONAL_ARGS)
for PYTHON_VERSION, _, TAG in PYTHON_VERSION_CONFS():
logger.info("\rConfiguring python {PYTHON_VERSION}{TAG} wrapper...".format(**locals()))
# python_dir = os.path.join(IFCOS_DIR, "python-{PYTHON_VERSION}{TAG}".format(**locals()))
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
if not os.path.exists(python_dir):
os.makedirs(python_dir)
cache_path = os.path.join(python_dir, "CMakeCache.txt")
if os.path.exists(cache_path):
os.unlink(cache_path)
PYTHON_LIBRARY=run([bash, "-c", "ls {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/lib/libpython*.*".format(**locals())])
PYTHON_INCLUDE=run([bash, "-c", "ls -d {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/include/python*".format(**locals())])
PYTHON_EXECUTABLE=os.path.join(DEPS_DIR, "install", "python-{PYTHON_VERSION}{TAG}".format(**locals()), "bin", "python{PYTHON_VERSION[0]}".format(**locals()))
os.environ["PYTHON_LIBRARY_BASENAME"]=os.path.basename(PYTHON_LIBRARY)
run_cmake("",
cmake_args=[
"-DBUILD_SHARED_LIBS=" "OFF" if BUILD_STATIC else "ON",
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir,
"-DPYTHON_LIBRARY=" +PYTHON_LIBRARY,
"-DPYTHON_EXECUTABLE=" +PYTHON_EXECUTABLE,
"-DPYTHON_INCLUDE_DIR=" +PYTHON_INCLUDE,
"-DSWIG_EXECUTABLE=" "{DEPS_DIR}/install/swig/bin/swig".format(**locals()),
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell/tmp".format(**locals()),
"-DLIBXML2_INCLUDE_DIR=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
"-DLIBXML2_LIBRARIES=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}".format(**locals()),
"-DCOLLADA_SUPPORT=OFF"
], cmake_dir=CMAKE_DIR, cwd=python_dir)
logger.info("\rBuilding python %s%s wrapper... " % (PYTHON_VERSION, TAG))
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "_ifcopenshell_wrapper"], cwd=python_dir)
run([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap"))
module_dir = os.path.dirname(run([PYTHON_EXECUTABLE, "-c", "from __future__ import print_function; import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
if get_os() != "Darwin":
# TODO: This symbol name depends on the Python version?
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", "python-%s%s" % (PYTHON_VERSION, TAG))])
logger.info("\rBuilt IfcOpenShell...\n\n")
-32
View File
@@ -1,32 +0,0 @@
http://git.dev.opencascade.org/gitweb/?p=occt.git;a=commitdiff;h=0ab4e621833f4eae945a3762c9a29ee12e2eec53#patch1
diff --git a/src/HLRBRep/HLRBRep_InternalAlgo.cxx b/src/HLRBRep/HLRBRep_InternalAlgo.cxx
index ca885ca..c13cb06 100644 (file)
--- a/src/HLRBRep/HLRBRep_InternalAlgo.cxx
+++ b/src/HLRBRep/HLRBRep_InternalAlgo.cxx
@@ -165,7 +165,7 @@ void HLRBRep_InternalAlgo::Update ()
SB.Bounds(v1,v2,e1,e2,f1,f2);
for (Standard_Integer e = e1; e <= e2; e++) {
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
HLRAlgo::DecodeMinMax(ed.MinMax(), TheMin, TheMax);
if (FirstTime) {
FirstTime = Standard_False;
@@ -307,7 +307,7 @@ void HLRBRep_InternalAlgo::InitEdgeStatus ()
Standard_Integer nf = myDS->NbFaces();
for (Standard_Integer e = 1; e <= ne; e++) {
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
if (ed.Selected()) ed.Status().ShowAll();
}
// for (Standard_Integer f = 1; f <= nf; f++) {
@@ -368,7 +368,7 @@ void HLRBRep_InternalAlgo::Select ()
Standard_Integer nf = myDS->NbFaces();
for (Standard_Integer e = 1; e <= ne; e++) {
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
ed.Selected(Standard_True);
}
@@ -1,17 +0,0 @@
Description: Enable exception handling
Upstream defaults to no exception handling for performance reasons,
but in OCCT's role as a shared library it's better for Debian to
enable it.
Author: Kurt Kremitzki <kkremitzki@gmail.com>
Last-Update: 2018-06-10
--- a/adm/cmake/occt_defs_flags.cmake
+++ b/adm/cmake/occt_defs_flags.cmake
@@ -138,5 +138,5 @@
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
endif()
-set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNo_Exception")
-set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNo_Exception")
+#set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNo_Exception")
+#set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNo_Exception")
@@ -1,29 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95abbe21..293c951c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -284,10 +284,10 @@ add_subdirectory(COLLADASaxFrameworkLoader)
add_subdirectory(COLLADAStreamWriter)
# building COLLADAValidator app
-add_subdirectory(COLLADAValidator)
+# add_subdirectory(COLLADAValidator)
# DAE validator app
-add_subdirectory(DAEValidator)
+# add_subdirectory(DAEValidator)
# Library export
install(EXPORT LibraryExport DESTINATION ${OPENCOLLADA_INST_CMAKECONFIG} FILE OpenCOLLADATargets.cmake)
diff --git a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
index 1f9a3eef..dd6f5c59 100644
--- a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
+++ b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
@@ -10,6 +10,7 @@
#include "GeneratedSaxParserUtils.h"
#include <math.h>
+#include <cmath>
#include <memory>
#include <string.h>
#include <limits>
-22
View File
@@ -1,22 +0,0 @@
From a0deb4ce8b43cf3c8b8c0a4225c6be5296446dbd Mon Sep 17 00:00:00 2001
From: Adam Eri <adam.eri@blackmirror.media>
Date: Tue, 3 Sep 2019 23:30:20 +0200
Subject: [PATCH] Resolves compile error on macOS
Resolves "no member named 'isnan' in namespace 'std'" on macOS
---
GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
index 1f9a3eef..dd6f5c59 100644
--- a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
+++ b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
@@ -10,6 +10,7 @@
#include "GeneratedSaxParserUtils.h"
#include <math.h>
+#include <cmath>
#include <memory>
#include <string.h>
#include <limits>
-2
View File
@@ -1,2 +0,0 @@
[tool.black]
line-length = 120
-64
View File
@@ -1,64 +0,0 @@
# ****************************************************************************
# line endings
# to suppress line ending changes in github, add ?w=1 at link end of a diff
# for more information see
# FreeCAD source code src/Mod/.gitattributes
# FreeCAD forum topic https://forum.freecadweb.org/viewtopic.php?f=17&t=41117
# FreeCAD pull request https://github.com/FreeCAD/FreeCAD/pull/2752
# get all used file types
# in a directory in a bash use
# find . -type f -name '*.*' | sed 's|.*\.||' | sort -u
# search for a specific file ending
# find . -type f -name '*.ico'
# normalize the line endings of the following files
*.cpp text
*.css text
*.csv text
*.feature text
*.gitattributes text
*.gitignore text
*.gitkeep
*.h text
*.html text
*.ifc text
*.json text
*.md text
*.po text
*.pot text
*.py text
*.txt text
# files not normalized ATM
# bat
# bnf
# blend
# i
# ico
# mo
# mpass
# png
# pth
# pyc
# rst
# svg
# ttf
# xsd
# line endings of all directories will be normalized
# to exclude a directory from being normalized add it here
# example: to exclude dirctory ifcbimtester use the following line without #
# ifcbimtester/** -text
# use git to manually correct the file endings
# git add --renormalize .
-59
View File
@@ -1,59 +0,0 @@
# bcf
A simple Python implementation of BCF. The data model is described in `data.py`.
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
is available via `bcfapi.py`.
Currently supports BCF version 2.1.
## bcfxml
The `bcfxml` module lets you interact with the BCF-XML standard.
```
from bcf.bcfxml import BcfXml
bcfxml = BcfXml()
# Load a project
project = bcfxml.get_project("/path/to/file.bcf")
# The project is also stored in the module
# project == bcfxml.project
print(project.name)
# To edit a project, just modify the object directly
bcfxml.project.name = "New name"
bcfxml.edit_project()
# The BCF file is extracted to this temporary directory
print(bcfxml.filepath)
# Get a dictionary of topics
topics = bcfxml.get_topics()
# Note: topics == bcfxml.topics
for guid, topic in bcfxml.topics.items():
print("Topic guid is", guid)
print("Topic guid is", topic.guid)
print("Topic title is", topic.title)
# Fetch extra data about a topic
header = bcfxml.get_header(guid)
comments = bcfxml.get_comments(guid)
viewpoints = bcfxml.get_viewpoints(guid)
# Note: comments == topic.comments, and so on
for comment_guid, comment in comments.items():
print(comment_guid)
print(comment.comment)
print(comment.author)
# Get a particular topic
topic = bcfxml.get_topic(guid)
# Modify a topic
topic.title = "New title"
bcfxml.edit_topic(topic)
```
-765
View File
@@ -1,765 +0,0 @@
import os
import uuid
import shutil
import zipfile
import logging
import tempfile
import bcf.data
from datetime import datetime
from xml.dom import minidom
from xmlschema import XMLSchema
from contextlib import contextmanager
from shutil import copyfile
cwd = os.path.dirname(os.path.realpath(__file__))
@contextmanager
def cd(newdir):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)
class BcfXml:
def __init__(self):
self.filepath = None
self.logger = logging.getLogger("bcfxml")
self.author = "john@doe.com"
self.project = bcf.data.Project()
self.version = "2.1"
self.topics = {}
def new_project(self):
self.project.project_id = str(uuid.uuid4())
self.project.name = "New Project"
self.topics = {}
if self.filepath:
self.close_project()
self.filepath = tempfile.mkdtemp()
self.edit_project()
self.edit_version()
def get_project(self, filepath=None):
if not filepath:
return self.project
zip_file = zipfile.ZipFile(filepath)
self.filepath = tempfile.mkdtemp()
zip_file.extractall(self.filepath)
if os.path.isfile(os.path.join(self.filepath, "project.bcfp")):
data = self._read_xml("project.bcfp", "project.xsd")
self.project.extension_schema = data["ExtensionSchema"]
if "Project" in data:
self.project.project_id = data["Project"]["@ProjectId"]
self.project.name = data["Project"].get("Name")
return self.project
def edit_project(self):
self.document = minidom.Document()
root = self._create_element(self.document, "ProjectExtension")
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
self._create_element(project, "Name", text=self.project.name)
self._create_element(root, "ExtensionSchema", text="extensions.xsd")
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def save_project(self, filepath):
with cd(self.filepath):
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./"):
for file in files:
zip_file.write(os.path.join(root, file))
zip_file.close()
def get_version(self):
data = self._read_xml("bcf.version", "version.xsd")
self.version = data["@VersionId"]
return self.version
def edit_version(self):
self.document = minidom.Document()
root = self._create_element(self.document, "Version", {"VersionId": self.version})
version = self._create_element(root, "DetailedVersion", text=self.version)
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def get_topics(self):
self.topics = {}
topics = []
subdirs = []
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
subdirs = dirnames
break
for subdir in subdirs:
self.topics[subdir] = self.get_topic(subdir)
return self.topics
def get_header(self, guid):
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Header" not in data:
return
header = bcf.data.Header()
for item in data["Header"]["File"]:
header_file = bcf.data.HeaderFile()
optional_keys = {
"filename": "Filename",
"date": "Date",
"reference": "Reference",
"ifc_project": "@IfcProject",
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
"is_external": "@isExternal",
}
for key, value in optional_keys.items():
if value in item:
setattr(header_file, key, item[value])
header.files.append(header_file)
self.topics[guid].header = header
return header
def get_topic(self, guid):
if guid in self.topics:
return self.topics[guid]
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
topic = bcf.data.Topic()
self.topics[guid] = topic
mandatory_keys = {
"guid": "@Guid",
"title": "Title",
"creation_date": "CreationDate",
"creation_author": "CreationAuthor",
}
for key, value in mandatory_keys.items():
setattr(topic, key, data["Topic"][value])
optional_keys = {
"priority": "Priority",
"index": "Index",
"labels": "Labels",
"reference_links": "ReferenceLink",
"modified_date": "ModifiedDate",
"modified_author": "ModifiedAuthor",
"due_date": "DueDate",
"assigned_to": "AssignedTo",
"stage": "Stage",
"description": "Description",
"topic_status": "@TopicStatus",
"topic_type": "@TopicType",
}
for key, value in optional_keys.items():
if value in data["Topic"]:
setattr(topic, key, data["Topic"][value])
if "BimSnippet" in data["Topic"]:
bim_snippet = bcf.data.BimSnippet()
keys = {
"snippet_type": "@SnippetType",
"is_external": "@IsExternal",
"reference": "Reference",
"reference_schema": "ReferenceSchema",
}
for key, value in keys.items():
if value in data["Topic"]["BimSnippet"]:
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
topic.bim_snippet = bim_snippet
if "DocumentReference" in data["Topic"]:
for item in data["Topic"]["DocumentReference"]:
document_reference = bcf.data.DocumentReference()
keys = {
"referenced_document": "ReferencedDocument",
"is_external": "@IsExternal",
"guid": "@Guid",
"description": "Description",
}
for key, value in keys.items():
if value in item:
setattr(document_reference, key, item[value])
topic.document_references.append(document_reference)
if "RelatedTopic" in data["Topic"]:
for item in data["Topic"]["RelatedTopic"]:
related_topic = bcf.data.RelatedTopic()
related_topic.guid = item["@Guid"]
topic.related_topics.append(related_topic)
return topic
def add_topic(self, topic=None):
if topic is None:
topic = bcf.data.Topic()
if not topic.guid:
topic.guid = str(uuid.uuid4())
if not topic.title:
topic.title = "New Topic"
os.mkdir(os.path.join(self.filepath, topic.guid))
self.edit_topic(topic)
return topic
def edit_topic(self, topic):
if not topic.creation_date:
topic.creation_date = datetime.utcnow().isoformat()
topic.creation_author = self.author
else:
topic.modified_date = datetime.utcnow().isoformat()
topic.modified_author = self.author
self.document = minidom.Document()
root = self._create_element(self.document, "Markup")
self.write_header(topic.header, root)
topic_el = self._create_element(
root,
"Topic",
{
"Guid": topic.guid,
"TopicType": topic.topic_type,
"TopicStatus": topic.topic_status,
},
)
for reference_link in topic.reference_links:
self._create_element(topic_el, "ReferenceLink", text=reference_link)
text_map = {
"Title": topic.title,
"Priority": topic.priority,
"Index": topic.index,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
for label in topic.labels:
self._create_element(topic_el, "Labels", text=label)
text_map = {
"CreationDate": topic.creation_date,
"CreationAuthor": topic.creation_author,
"ModifiedDate": topic.modified_date,
"ModifiedAuthor": topic.modified_author,
"DueDate": topic.due_date,
"AssignedTo": topic.assigned_to,
"Stage": topic.stage,
"Description": topic.description,
}
for key, value in text_map.items():
if value:
self._create_element(topic_el, key, text=value)
if topic.bim_snippet:
bim_snippet = self._create_element(
topic_el,
"BimSnippet",
{"SnippetType": topic.bim_snippet.snippet_type, "isExternal": topic.bim_snippet.is_external},
)
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
self._create_element(bim_snippet, "ReferenceSchema", text=topic.bim_snippet.reference_schema)
for reference in topic.document_references:
reference_el = self._create_element(
topic_el, "DocumentReference", {"Guid": reference.guid, "isExternal": reference.is_external}
)
self._create_element(reference_el, "ReferencedDocument", text=reference.referenced_document)
self._create_element(reference_el, "Description", text=reference.description)
for related_topic in topic.related_topics:
self._create_element(topic_el, "RelatedTopic", {"Guid": related_topic.guid})
self.write_comments(topic.comments, root)
self.write_viewpoints(topic.viewpoints, root, topic)
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
f.write(self.document.toprettyxml(encoding="utf-8"))
def write_header(self, header, root):
if not header or not header.files:
return
header_el = self._create_element(root, "Header")
for f in header.files:
file_el = self._create_element(
header_el,
"File",
{
"IfcProject": f.ifc_project,
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
"isExternal": f.is_external,
},
)
self._create_element(file_el, "Filename", text=f.filename)
self._create_element(file_el, "Date", text=f.date)
self._create_element(file_el, "Reference", text=f.reference)
def write_comments(self, comments, root):
for comment in comments.values():
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
text_map = {
"Date": comment.date,
"Author": comment.author,
"Comment": comment.comment,
"ModifiedDate": comment.modified_date,
"ModifiedAuthor": comment.modified_author,
}
for key, value in text_map.items():
if value:
self._create_element(comment_el, key, text=value)
if comment.viewpoint:
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
def add_comment(self, topic, comment=None):
if comment is None:
comment = bcf.data.Comment()
if not comment.guid:
comment.guid = str(uuid.uuid4())
if not comment.comment:
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
topic.comments[comment.guid] = comment
self.edit_comment(comment, topic)
def edit_comment(self, comment, topic):
if not comment.date:
comment.date = datetime.utcnow().isoformat()
comment.author = self.author
else:
comment.modified_date = datetime.utcnow().isoformat()
comment.modified_author = self.author
self.edit_topic(topic)
def delete_comment(self, guid, topic):
if guid in topic.comments:
del topic.comments[guid]
self.edit_topic(topic)
def delete_topic(self, guid):
if guid in self.topics:
del self.topics[guid]
shutil.rmtree(os.path.join(self.filepath, guid))
def write_viewpoints(self, viewpoints, root, topic):
for viewpoint in viewpoints.values():
viewpoint_el = self._create_element(root, "Viewpoints", {"Guid": viewpoint.guid})
text_map = {"Viewpoint": viewpoint.viewpoint, "Snapshot": viewpoint.snapshot, "Index": viewpoint.index}
for key, value in text_map.items():
if value:
self._create_element(viewpoint_el, key, text=value)
self.write_viewpoint(viewpoint, topic)
def write_viewpoint(self, viewpoint, topic):
document = minidom.Document()
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
self.write_viewpoint_components(viewpoint, root)
self.write_viewpoint_orthogonal_camera(viewpoint, root)
self.write_viewpoint_perspective_camera(viewpoint, root)
self.write_viewpoint_lines(viewpoint, root)
self.write_viewpoint_clipping_planes(viewpoint, root)
self.write_viewpoint_bitmaps(viewpoint, root)
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
f.write(document.toprettyxml(encoding="utf-8"))
def write_viewpoint_components(self, viewpoint, parent):
if not viewpoint.components:
return
components_el = self._create_element(parent, "Components")
if viewpoint.components.view_setup_hints:
view_setup_hints = self._create_element(
components_el,
"ViewSetupHints",
{
"SpacesVisible": viewpoint.components.view_setup_hints.spaces_visible,
"SpaceBoundariesVisible": viewpoint.components.view_setup_hints.space_boundaries_visible,
"OpeningsVisible": viewpoint.components.view_setup_hints.openings_visible,
},
)
if viewpoint.components.selection:
selection_el = self._create_element(components_el, "Selection")
for selection in viewpoint.components.selection:
self.write_component(selection, selection_el)
visibility = self._create_element(
components_el, "Visibility", {"DefaultVisibility": viewpoint.components.visibility.default_visibility}
)
if viewpoint.components.visibility.exceptions:
exceptions_el = self._create_element(visibility, "Exceptions")
for exception in viewpoint.components.visibility.exceptions:
self.write_component(exception, exceptions_el)
if viewpoint.components.coloring:
coloring_el = self._create_element(components_el, "Coloring")
for color in viewpoint.components.coloring:
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
for component in color.components:
self.write_component(component, color_el)
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
if not viewpoint.orthogonal_camera:
return
camera = viewpoint.orthogonal_camera
camera_el = self._create_element(parent, "OrthogonalCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
def write_viewpoint_perspective_camera(self, viewpoint, parent):
if not viewpoint.perspective_camera:
return
camera = viewpoint.perspective_camera
camera_el = self._create_element(parent, "PerspectiveCamera")
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
self.write_vector(camera_view_point, camera.camera_view_point)
camera_direction = self._create_element(camera_el, "CameraDirection")
self.write_vector(camera_direction, camera.camera_direction)
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
self.write_vector(camera_up_vector, camera.camera_up_vector)
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
def write_viewpoint_lines(self, viewpoint, parent):
if not viewpoint.lines:
return
lines_el = self._create_element(parent, "Lines")
for line in viewpoint.lines:
line_el = self._create_element(lines_el, "Line")
start_point_el = self._create_element(line_el, "StartPoint")
self.write_vector(start_point_el, line.start_point)
end_point_el = self._create_element(line_el, "EndPoint")
self.write_vector(end_point_el, line.end_point)
def write_viewpoint_clipping_planes(self, viewpoint, parent):
if not viewpoint.clipping_planes:
return
planes_el = self._create_element(parent, "ClippingPlanes")
for plane in viewpoint.clipping_planes:
plane_el = self._create_element(planes_el, "ClippingPlane")
location_el = self._create_element(plane_el, "Location")
self.write_vector(location_el, plane.location)
direction_el = self._create_element(plane_el, "Direction")
self.write_vector(direction_el, plane.direction)
def write_viewpoint_bitmaps(self, viewpoint, parent):
if not viewpoint.bitmaps:
return
for bitmap in viewpoint.bitmaps:
bitmap_el = self._create_element(parent, "Bitmap")
text_map = {"Bitmap": bitmap.bitmap_type, "Reference": bitmap.reference}
for key, value in text_map.items():
self._create_element(bitmap_el, key, text=value)
location_el = self._create_element(bitmap_el, "Location")
self.write_vector(location_el, bitmap.location)
normal_el = self._create_element(bitmap_el, "Normal")
self.write_vector(normal_el, bitmap.normal)
up_el = self._create_element(bitmap_el, "Up")
self.write_vector(up_el, bitmap.up)
self._create_element(bitmap_el, "Height", text=bitmap.height)
def write_vector(self, parent, from_obj):
self._create_element(parent, "X", text=from_obj.x)
self._create_element(parent, "Y", text=from_obj.y)
self._create_element(parent, "Z", text=from_obj.z)
def write_component(self, data, parent):
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
text_map = {"OriginatingSystem": data.originating_system, "AuthoringToolId": data.authoring_tool_id}
for key, value in text_map.items():
if value:
self._create_element(component_el, key, text=value)
def add_viewpoint(self, topic, viewpoint=None):
if not viewpoint:
viewpoint = bcf.data.Viewpoint()
if not viewpoint.guid:
viewpoint.guid = str(uuid.uuid4())
if not viewpoint.viewpoint:
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
if viewpoint.snapshot:
topic_filepath = os.path.join(self.filepath, topic.guid)
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
if not os.path.exists(filepath):
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
viewpoint.snapshot = filename
topic.viewpoints[viewpoint.guid] = viewpoint
self.edit_topic(topic)
def delete_viewpoint(self, guid, topic):
if guid not in topic.viewpoints:
return
viewpoint = topic.viewpoints[guid]
if viewpoint.snapshot:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
if os.path.exists(filepath):
os.remove(filepath)
if viewpoint.viewpoint:
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
if os.path.exists(filepath):
os.remove(filepath)
for bitmap in viewpoint.bitmaps:
if not bitmap.reference:
continue
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
if os.path.exists(filepath):
os.remove(filepath)
del topic.viewpoints[guid]
self.edit_topic(topic)
def delete_file(self, topic, index):
if not topic.header:
return
f = topic.header.files.pop(index)
filepath = os.path.join(self.filepath, topic.guid, f.reference)
if not f.is_external and os.path.exists(filepath):
os.remove(filepath)
self.edit_topic(topic)
def delete_bim_snippet(self, topic):
if not topic.bim_snippet:
return
if topic.bim_snippet.reference and not topic.bim_snippet.is_external:
filepath = os.path.join(self.filepath, topic.guid, topic.bim_snippet.reference)
if os.path.exists(filepath):
os.remove(filepath)
topic.bim_snippet = None
self.edit_topic(topic)
def delete_document_reference(self, topic, index):
document_reference = topic.document_references[index]
if document_reference.referenced_document and not document_reference.is_external:
filepath = os.path.join(self.filepath, topic.guid, document_reference.referenced_document)
if os.path.exists(filepath):
os.remove(filepath)
del topic.document_references[index]
self.edit_topic(topic)
def add_document_reference(self, topic, document_reference):
if os.path.exists(document_reference.referenced_document):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(document_reference.referenced_document)
copyfile(document_reference.referenced_document, os.path.join(topic_filepath, filename))
document_reference.referenced_document = filename
document_reference.is_external = False
else:
document_reference.is_external = True
if not document_reference.guid:
document_reference.guid = str(uuid.uuid4())
topic.document_references.append(document_reference)
self.edit_topic(topic)
def add_bim_snippet(self, topic, bim_snippet):
if topic.bim_snippet:
self.delete_bim_snippet(topic)
if os.path.exists(bim_snippet.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
filename = os.path.basename(bim_snippet.reference)
copyfile(bim_snippet.reference, os.path.join(topic_filepath, filename))
bim_snippet.reference = filename
bim_snippet.is_external = False
else:
bim_snippet.is_external = True
topic.bim_snippet = bim_snippet
self.edit_topic(topic)
def add_file(self, topic, header_file):
if os.path.exists(header_file.reference):
topic_filepath = os.path.join(self.filepath, topic.guid)
header_file.filename = os.path.basename(header_file.reference)
copyfile(header_file.reference, os.path.join(topic_filepath, header_file.filename))
header_file.reference = header_file.filename
header_file.is_external = False
header_file.date = datetime.utcnow().isoformat()
if not topic.header:
topic.header = bcf.data.Header()
topic.header.files.append(header_file)
self.edit_topic(topic)
def get_comments(self, guid):
comments = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Comment" not in data:
return comments
for item in data["Comment"]:
comment = bcf.data.Comment()
mandatory_keys = {"guid": "@Guid", "date": "Date", "author": "Author", "comment": "Comment"}
for key, value in mandatory_keys.items():
setattr(comment, key, item[value])
optional_keys = {"modified_date": "ModifiedDate", "modified_author": "ModifiedAuthor"}
for key, value in optional_keys.items():
if value in item:
setattr(comment, key, item[value])
if "Viewpoint" in item:
viewpoint = bcf.data.Viewpoint()
viewpoint.guid = item["Viewpoint"]["@Guid"]
comment.viewpoint = viewpoint
comments[comment.guid] = comment
self.topics[guid].comments = comments
return comments
def get_viewpoints(self, guid):
viewpoints = {}
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
if "Viewpoints" not in data:
return viewpoints
for item in data["Viewpoints"]:
viewpoint = self.get_viewpoint(item, guid)
viewpoints[viewpoint.guid] = viewpoint
self.topics[guid].viewpoints = viewpoints
return viewpoints
def get_viewpoint(self, data, topic_guid):
viewpoint = bcf.data.Viewpoint()
viewpoint.guid = data["@Guid"]
optional_keys = {"viewpoint": "Viewpoint", "snapshot": "Snapshot", "index": "Index"}
for key, value in optional_keys.items():
if value in data:
setattr(viewpoint, key, data[value])
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
viewpoint.components = self.get_viewpoint_components(visinfo)
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
viewpoint.lines = self.get_viewpoint_lines(visinfo)
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
return viewpoint
def get_viewpoint_components(self, visinfo):
if "Components" not in visinfo:
return None
components = bcf.data.Components()
data = visinfo["Components"]
if "ViewSetupHints" in data:
view_setup_hints = bcf.data.ViewSetupHints()
optional_keys = {
"spaces_visible": "@SpacesVisible",
"space_boundaries_visible": "@SpaceBoundariesVisible",
"openings_visible": "@OpeningsVisible",
}
for key, value in optional_keys.items():
if value in data["ViewSetupHints"]:
setattr(view_setup_hints, key, data["ViewSetupHints"][value])
components.view_setup_hints = view_setup_hints
if "Selection" in data and "Component" in data["Selection"]:
for item in data["Selection"]["Component"]:
components.selection.append(self.get_component(item))
if "Visibility" in data:
component_visibility = bcf.data.ComponentVisibility()
if "@DefaultVisibility" in data["Visibility"]:
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
for item in data["Visibility"]["Exceptions"]["Component"]:
component_visibility.exceptions.append(self.get_component(item))
components.visibility = component_visibility
if "Coloring" in data and "Color" in data["Coloring"]:
for item in data["Coloring"]["Color"]:
color = bcf.data.Color()
color.color = item["@Color"]
for item2 in item["Component"]:
color.components.append(self.get_component(item2))
components.coloring.append(color)
return components
def get_viewpoint_orthogonal_camera(self, visinfo):
if "OrthogonalCamera" not in visinfo:
return None
camera = bcf.data.OrthogonalCamera()
data = visinfo["OrthogonalCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.view_to_world_scale = data["ViewToWorldScale"]
return camera
def get_viewpoint_perspective_camera(self, visinfo):
if "PerspectiveCamera" not in visinfo:
return None
camera = bcf.data.PerspectiveCamera()
data = visinfo["PerspectiveCamera"]
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
self.set_vector(camera.camera_direction, data["CameraDirection"])
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
camera.field_of_view = data["FieldOfView"]
return camera
def get_viewpoint_lines(self, visinfo):
if "Lines" not in visinfo:
return []
lines = []
for item in visinfo["Lines"]["Line"]:
line = bcf.data.Line()
self.set_vector(line.start_point, item["StartPoint"])
self.set_vector(line.end_point, item["EndPoint"])
lines.append(line)
return lines
def get_viewpoint_clipping_planes(self, visinfo):
if "ClippingPlanes" not in visinfo:
return []
planes = []
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
plane = bcf.data.ClippingPlane()
self.set_vector(plane.location, item["Location"])
self.set_vector(plane.direction, item["Direction"])
planes.append(plane)
return planes
def get_viewpoint_bitmaps(self, visinfo):
if "Bitmap" not in visinfo:
return []
bitmaps = []
for item in visinfo["Bitmap"]:
bitmap = bcf.data.Bitmap()
bitmap.reference = item["Reference"]
bitmap.bitmap_type = item["Bitmap"].upper()
self.set_vector(bitmap.location, item["Location"])
self.set_vector(bitmap.normal, item["Normal"])
self.set_vector(bitmap.up, item["Up"])
bitmap.height = item["Height"]
bitmaps.append(bitmap)
return bitmaps
def set_vector(self, to_obj, from_xml):
to_obj.x = from_xml["X"]
to_obj.y = from_xml["Y"]
to_obj.z = from_xml["Z"]
def get_component(self, data):
component = bcf.data.Component()
optional_keys = {
"originating_system": "OriginatingSystem",
"authoring_tool_id": "AuthoringToolId",
"ifc_guid": "@IfcGuid",
}
for key, value in optional_keys.items():
if value in data:
setattr(component, key, data[value])
return component
def close_project(self):
shutil.rmtree(self.filepath)
def _read_xml(self, filename, xsd):
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
filepath = os.path.join(self.filepath, filename)
(data, errors) = schema.to_dict(filepath, validation="lax")
for error in errors:
self.logger.error(error)
return data
def _create_element(self, parent, name, attributes={}, text=None):
element = self.document.createElement(name)
for key, value in attributes.items():
if isinstance(value, bool):
element.setAttribute(key, str(value).lower())
elif value:
element.setAttribute(key, value)
if text is not None:
text = self.document.createTextNode(str(text))
element.appendChild(text)
parent.appendChild(element)
return element
def __del__(self):
self.close_project()
-179
View File
@@ -1,179 +0,0 @@
class Project:
def __init__(self):
self.project_id = ""
self.name = ""
self.extension_schema = ""
class BimSnippet:
def __init__(self):
self.snippet_type = None
self.is_external = False
self.reference = None
self.reference_schema = None
class DocumentReference:
def __init__(self):
self.referenced_document = None
self.description = None
self.guid = None
self.is_external = False
class RelatedTopic:
def __init__(self):
self.guid = None
class HeaderFile:
def __init__(self):
self.filename = None
self.date = None
self.reference = None
self.ifc_project = None
self.ifc_spatial_structure_element = None
self.is_external = True
class Header:
def __init__(self):
self.files = []
class Topic:
def __init__(self):
self.reference_links = []
self.title = ""
self.priority = None
self.index = None # Deprecated, stored, but ignored
self.labels = []
self.creation_date = None
self.creation_author = None
self.modified_date = None
self.modified_author = None
self.due_date = None
self.assigned_to = None
self.stage = None
self.description = None
self.bim_snippet = None
self.document_references = []
self.related_topics = []
self.topic_status = None
self.topic_type = None
self.guid = None
self.header = None
self.comments = {}
self.viewpoints = {}
class Comment:
def __init__(self):
self.guid = None
self.date = None
self.author = None
self.comment = None
self.viewpoint = None
self.modified_date = None
self.modified_author = None
self.topic_guid = None # Part of BCF-API
class ViewSetupHints:
def __init__(self):
self.spaces_visible = False
self.space_boundaries_visible = False
self.openings_visible = False
class Component:
def __init__(self):
self.originating_system = None
self.authoring_tool_id = None
self.ifc_guid = None
class ComponentVisibility:
def __init__(self):
self.exceptions = []
self.default_visibility = False
class Color:
def __init__(self):
self.color = None
self.components = []
class Components:
def __init__(self):
self.view_setup_hints = None
self.selection = []
self.visibility = None
self.coloring = []
class Point:
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
class Direction(Point):
pass
class OrthogonalCamera:
def __init__(self):
self.camera_view_point = Point()
self.camera_direction = Direction()
self.camera_up_vector = Direction()
self.view_to_world_scale = 1.0
class PerspectiveCamera:
def __init__(self):
self.camera_view_point = Point()
self.camera_direction = Direction()
self.camera_up_vector = Direction()
self.field_of_view = 60.0
class Line:
def __init__(self):
self.start_point = Point()
self.end_point = Point()
class ClippingPlane:
def __init__(self):
self.location = Point()
self.direction = Direction()
class Bitmap:
def __init__(self):
self.reference = "" # Only in BCF-XML
self.bitmap_data = None # Only in BCF-API
self.bitmap_type = "PNG" # Enum of png or jpg
self.location = Point()
self.normal = Direction()
self.up = Direction()
self.height = 1.0
class Viewpoint:
def __init__(self):
self.guid = None
self.viewpoint = None
self.snapshot = None
self.index = None
self.components = None # It's not a list, despite the plural name
self.orthogonal_camera = None
self.perspective_camera = None
self.lines = []
self.clipping_planes = []
self.bitmaps = []
-154
View File
@@ -1,154 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Markup">
<xs:complexType>
<xs:sequence>
<xs:element name="Header" type="Header" minOccurs="0"/>
<xs:element name="Topic" type="Topic"/>
<xs:element name="Comment" type="Comment" minOccurs="0" maxOccurs="unbounded"/>
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
<xs:element name="Viewpoints" type="ViewPoint" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Header">
<xs:sequence>
<xs:element name="File" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Filename" type="xs:string" minOccurs="0"/>
<xs:element name="Date" type="xs:dateTime" minOccurs="0"/>
<!-- Reference (URL) of the file -->
<xs:element name="Reference" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attributeGroup ref="FileAttributes"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
<xs:complexType name="ViewPoint">
<xs:sequence>
<!-- viewpoint file (xml) -->
<xs:element name="Viewpoint" type="xs:string" minOccurs="0"/>
<!-- the snapshot png -->
<xs:element name="Snapshot" type="xs:string" minOccurs="0"/>
<!-- the viewpoint index (sort order) -->
<xs:element name="Index" type="xs:int" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
<!-- Guid of the viewpoint -->
</xs:complexType>
<!-- BimSnippet -->
<xs:complexType name="BimSnippet">
<xs:sequence>
<!--
Name of the file in the topic folder containing the snippet or a URL.
E.G.- Expresscode containing p.e Issue, Request
// Maybe some header infos ?? // IfcEntites // Geometry
-->
<!-- Reference (name) to the snippet file -->
<xs:element name="Reference" type="xs:string"/>
<xs:element name="ReferenceSchema" type="xs:string"/>
</xs:sequence>
<xs:attribute name="SnippetType" type="xs:string" use="required"/>
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
<!-- This flag is true when the reference is a URL pointing outside of the BCF file-->
</xs:complexType>
<xs:complexType name="Topic">
<xs:sequence>
<xs:element name="ReferenceLink" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Title" type="xs:string"/>
<xs:element name="Priority" type="Priority" minOccurs="0"/>
<!-- ISG Jira issue BCF-8 Add a way save order the topics -->
<xs:element name="Index" type="xs:int" minOccurs="0"/>
<xs:element name="Labels" type="TopicLabel" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="CreationDate" type="xs:dateTime" minOccurs="1"/>
<xs:element name="CreationAuthor" type="UserIdType" minOccurs="1"/>
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
<xs:element name="DueDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="AssignedTo" type="UserIdType" minOccurs="0"/>
<xs:element name="Stage" type="Stage" minOccurs="0"/>
<xs:element name="Description" type="xs:string" minOccurs="0"/>
<xs:element name="BimSnippet" type="BimSnippet" minOccurs="0"/>
<!-- Name of the file in the topic folder or url -->
<xs:element name="DocumentReference" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<!-- Name of the file in the topic folder or url -->
<xs:element name="ReferencedDocument" type="xs:string" minOccurs="0"/>
<!-- Human readable name of the document -->
<xs:element name="Description" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attributeGroup ref="DocumentReference"/>
</xs:complexType>
</xs:element>
<xs:element name="RelatedTopic" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
<xs:attribute name="TopicType" type="TopicType"/>
<xs:attribute name="TopicStatus" type="TopicStatus"/>
</xs:complexType>
<!-- Reference to a document inside of the topic folder or a url pointing to the web -->
<xs:attributeGroup name="DocumentReference">
<!-- Guid of the DocumentReference -->
<xs:attribute name="Guid" type="Guid"/>
<!-- A flag that is true when the ReferencedDocument points outside of the BCF file (a URL) -->
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
</xs:attributeGroup>
<xs:complexType name="Comment">
<xs:sequence>
<xs:element name="Date" type="xs:dateTime"/>
<xs:element name="Author" type="UserIdType"/>
<xs:element name="Comment" type="xs:string"/>
<xs:element name="Viewpoint" minOccurs="0">
<xs:complexType>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
<xs:simpleType name="TopicStatus">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="TopicType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="TopicLabel">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="Priority">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="UserIdType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="Stage">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="Guid">
<xs:restriction base="xs:string">
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="IfcGuid">
<xs:restriction base="xs:string">
<xs:length value="22"/>
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
</xs:restriction>
</xs:simpleType>
<xs:attributeGroup name="FileAttributes">
<xs:attribute name="IfcProject" type="IfcGuid"/>
<xs:attribute name="IfcSpatialStructureElement" type="IfcGuid"/>
<xs:attribute name="isExternal" type="xs:boolean" default="true"/>
</xs:attributeGroup>
</xs:schema>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ProjectExtension">
<xs:complexType>
<xs:sequence>
<xs:element name="Project" type="Project" minOccurs="0"/>
<xs:element name="ExtensionSchema" type="xs:anyURI"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Project">
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="ProjectId" type="xs:string" use="required"/>
</xs:complexType>
</xs:schema>
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mit XMLSpy v2011 rel. 3 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Version">
<xs:complexType>
<xs:sequence>
<xs:element name="DetailedVersion" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="VersionId" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
-191
View File
@@ -1,191 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="VisualizationInfo">
<xs:annotation>
<xs:documentation>VisualizationInfo documentation</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="Components" type="Components" minOccurs="0"/>
<xs:element name="OrthogonalCamera" type="OrthogonalCamera" minOccurs="0"/>
<xs:element name="PerspectiveCamera" type="PerspectiveCamera" minOccurs="0"/>
<xs:element name="Lines" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Line" type="Line" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ClippingPlanes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="ClippingPlane" type="ClippingPlane" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
<xs:element name="Bitmap" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Bitmap" type="BitmapFormat"/>
<!-- Name of the bitmap file in the topic folder -->
<xs:element name="Reference" type="xs:string"/>
<!-- Location of the center of the bitmap -->
<xs:element name="Location" type="Point"/>
<!-- Normal of the bitmap -->
<xs:element name="Normal" type="Direction"/>
<!-- Upvector of the bitmap -->
<xs:element name="Up" type="Direction"/>
<!-- Height of the bitmap -->
<xs:element name="Height" type="xs:double"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<!-- Guid of the viewpoint -->
<xs:attribute name="Guid" type="Guid" use="required"/>
</xs:complexType>
</xs:element>
<xs:complexType name="OrthogonalCamera">
<xs:sequence>
<xs:element name="CameraViewPoint" type="Point"/>
<xs:element name="CameraDirection" type="Direction"/>
<xs:element name="CameraUpVector" type="Direction"/>
<xs:element name="ViewToWorldScale" type="xs:double">
<xs:annotation>
<xs:documentation>view's visible size in meters</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PerspectiveCamera">
<xs:sequence>
<xs:element name="CameraViewPoint" type="Point"/>
<xs:element name="CameraDirection" type="Direction"/>
<xs:element name="CameraUpVector" type="Direction"/>
<xs:element name="FieldOfView" type="FieldOfView">
<xs:annotation>
<xs:documentation>
It is currently limited to a value between 45 and 60 degrees.
This limitation will be dropped in the next release and viewers
should be expect values outside this range in current implementations.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Point">
<xs:sequence>
<xs:element name="X" type="xs:double"/>
<xs:element name="Y" type="xs:double"/>
<xs:element name="Z" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Direction">
<xs:sequence>
<xs:element name="X" type="xs:double"/>
<xs:element name="Y" type="xs:double"/>
<xs:element name="Z" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="FieldOfView">
<xs:restriction base="xs:double">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="170"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Components">
<xs:sequence>
<xs:element name="ViewSetupHints" type="ViewSetupHints" minOccurs="0" />
<!-- Components with relevance to the viewpoint. They should be displayed highlighted or selected in a viewer -->
<xs:element name="Selection" type="ComponentSelection" minOccurs="0" />
<xs:element name="Visibility" type="ComponentVisibility" minOccurs="1" />
<xs:element name="Coloring" type="ComponentColoring" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="ViewSetupHints">
<xs:attribute name="SpacesVisible" type="xs:boolean"/>
<xs:attribute name="SpaceBoundariesVisible" type="xs:boolean"/>
<xs:attribute name="OpeningsVisible" type="xs:boolean"/>
</xs:complexType>
<xs:complexType name="ComponentSelection">
<xs:sequence>
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComponentVisibility">
<xs:sequence>
<xs:element name="Exceptions" minOccurs="0">
<!-- List Components that are different than the DefaultVisibility. E.g. if DefaultVisibility = false then list
Components that should be visible -->
<xs:complexType>
<xs:sequence>
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="DefaultVisibility" type="xs:boolean"/>
</xs:complexType>
<xs:complexType name="ComponentColoring">
<xs:sequence>
<xs:element name="Color" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute ref="Color"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Component">
<xs:sequence>
<xs:element name="OriginatingSystem" type="xs:string" minOccurs="0"/>
<xs:element name="AuthoringToolId" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute ref="IfcGuid"/>
<!-- ISG Jira Issue BCF-14 -->
</xs:complexType>
<xs:attribute name="Color">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<!-- Should either match 3 or 4 hex bytes , e.g. "FF00FF" or "FF00FF99" -->
<xs:pattern value="[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="IfcGuid">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:length value="22"/>
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:complexType name="Line">
<xs:sequence>
<xs:element name="StartPoint" type="Point"/>
<xs:element name="EndPoint" type="Point"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ClippingPlane">
<xs:sequence>
<xs:element name="Location" type="Point"/>
<xs:element name="Direction" type="Direction"/>
</xs:sequence>
</xs:complexType>
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
<xs:simpleType name="BitmapFormat">
<xs:restriction base="xs:string">
<xs:enumeration value="PNG"/>
<xs:enumeration value="JPG"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Guid">
<xs:restriction base="xs:string">
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
-1
View File
@@ -1 +0,0 @@
xmlschema
-340
View File
@@ -1,340 +0,0 @@
VERSION:=`date '+%y%m%d'`
PYVERSION:=py37
ifeq ($(PLATFORM), win)
ifeq ($(PYVERSION), py37)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/win-64/pythonocc-core-0.17.3-py37he980bc4_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/win-64/oce-0.17.2-he980bc4_14.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/2019.5/download/win-64/tbb-2019.5-he980bc4_0.tar.bz2
endif
ifeq ($(PYVERSION), py39)
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/win-64/pythonocc-core-7.4.1-py39h3d1c7c5_0.tar.bz2
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/win-64/occt-7.4.0-h823b557_3.tar.bz2
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/win-64/tbb-2020.2-h2d74725_4.tar.bz2
endif
endif
ifeq ($(PLATFORM), macos)
ifeq ($(PYVERSION), py37)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/osx-64/pythonocc-core-0.17.3-py37h04f5b5a_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/osx-64/oce-0.17.2-h04f5b5a_12.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/osx-64/tbb-4.3.6-0.tar.bz2
endif
ifeq ($(PYVERSION), py39)
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/osx-64/pythonocc-core-7.4.1-py39h4d29fe3_0.tar.bz2
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/osx-64/occt-7.4.0-hb9b6dc7_3.tar.bz2
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/osx-64/tbb-2020.2-h940c156_4.tar.bz2
endif
endif
ifeq ($(PLATFORM), linux)
ifeq ($(PYVERSION), py37)
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/linux-64/pythonocc-core-0.17.3-py37h6bb024c_10.tar.bz2
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/linux-64/oce-0.17.2-h6bb024c_14.tar.bz2
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/linux-64/tbb-4.3.6-0.tar.bz2
endif
ifeq ($(PYVERSION), py39)
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/linux-64/pythonocc-core-7.4.1-py39h465cb30_0.tar.bz2
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/linux-64/occt-7.4.0-h9121d39_3.tar.bz2
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/linux-64/tbb-2020.2-h4bd325d_4.tar.bz2
endif
endif
.PHONY: dist
dist:
ifndef PLATFORM
$(error PLATFORM is not set)
endif
rm -rf dist
mkdir -p dist/working
mkdir -p dist/blenderbim
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
ifeq ($(PYVERSION), py37)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
ifeq ($(PYVERSION), py39)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
cd dist/working && unzip ifcblender*
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
# See bug #812
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.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 IfcConvert for construction documentation
mkdir dist/working
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v0.6.0-517b819-$(PLATFORM)64.zip
cd dist/working && unzip IfcConvert*
ifeq ($(PLATFORM), win)
cp -r dist/working/IfcConvert.exe dist/blenderbim/libs/
else
cp -r dist/working/IfcConvert dist/blenderbim/libs/
endif
rm -rf dist/working
# Provides Python OCC functionality for cutting IFC geometry for construction documentation
mkdir dist/working
cd dist/working && wget $(PYTHONOCC_URL)
cd dist/working && tar -xjvf pythonocc-core*
ifeq ($(PLATFORM), win)
cd dist/working && cp -r Lib/site-packages/OCC ../blenderbim/libs/site/packages/
else
ifeq ($(PYVERSION), py37)
cd dist/working && cp -r lib/python3.7/site-packages/OCC ../blenderbim/libs/site/packages/
endif
ifeq ($(PYVERSION), py39)
cd dist/working && cp -r lib/python3.9/site-packages/OCC ../blenderbim/libs/site/packages/
endif
endif
rm -rf dist/working
# Required by Python OCC
mkdir dist/working
cd dist/working && wget $(OCE_URL)
cd dist/working && tar -xjvf oc*
ifeq ($(PLATFORM), win)
ifeq ($(PYVERSION), py37)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
endif
ifeq ($(PYVERSION), py39)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/Core/
endif
else
# Unix Conda builds of PythonOCC expect OCE libs to have a RPATH of ../../../
cd dist/working && cp -r lib/* ../blenderbim/libs/
endif
ifeq ($(PLATFORM), linux)
rm -rf dist/blenderbim/libs/oce-0.17
endif
rm -rf dist/working
# Required by OpenCascade
mkdir dist/working
cd dist/working && wget $(TBB_URL)
cd dist/working && tar -xjvf tbb*
ifeq ($(PLATFORM), win)
ifeq ($(PYVERSION), py37)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
endif
ifeq ($(PYVERSION), py39)
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/Core/
endif
else
cd dist/working && cp -r lib/* ../blenderbim/libs/
endif
rm -rf dist/working
ifeq ($(PLATFORM), macos)
mkdir dist/working
cd dist/working && wget https://blenderbim.org/builds/patch-blender28-bim-macos.zip
cd dist/working && unzip patch*
cd dist/working && cp -r *.dylib ../blenderbim/libs/
rm -rf dist/working
endif
# Provides dependencies that are part of IfcOpenShell
mkdir dist/working
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
cd dist/working && unzip v0.6.0.zip
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
# Provides bcf functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
# Provides IFCClash functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/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.6.0/src/ifcbimtester/bimtester/locale/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
# Provides IFCCOBie functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
# Provides IFCDiff functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
# Provides IFCCSV functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
# Provides IFCPatch functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides Mustache templating in construction documentation
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/d6/fd/eb8c212053addd941cc90baac307c00ac246ac3fce7166b86434c6eae963/pystache-0.5.4.tar.gz
cd dist/working && tar -xzvf pystache*
cd dist/working/pystache-0.5.4/ && python setup.py build && cp -r build/lib/pystache ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides SVG export in construction documentation
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/79/e8/7eb2ba188eda14a4b47e33b51f3b4978985f4116655c699bcd18c79279b5/svgwrite-1.3.1.zip
cd dist/working && unzip svgwrite*
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCDiff
mkdir dist/working
cd dist/working && wget https://github.com/Moult/deepdiff/archive/master.zip
cd dist/working && unzip master.zip
cp -r dist/working/deepdiff-master/deepdiff dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by deepdiff
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/00/55/ce2cbc6d64034b30cad81a29ba61bdba456f190f5e83c09831304bf68d6b/jsonpickle-1.2.tar.gz
cd dist/working && tar -xzvf jsonpickle*
cp -r dist/working/jsonpickle-1.2/jsonpickle dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by deepdiff
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/a3/b7/d4d69641cbe707a45c23b190f2d717466ba5accc4c70b5f7a8a450387895/ordered-set-3.1.1.tar.gz
cd dist/working && tar -xzvf ordered-set*
cp -r dist/working/ordered-set-3.1.1/ordered_set.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by lark
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/00/32/8076fa13e832bb4dcff379f18f228e5a53412be0631808b9ca2610c0f566/pyparsing-2.4.5.tar.gz
cd dist/working && tar -xzvf pyparsing*
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/bb/41/ad9ce53bb978b68af8ae415293cafc89b165b8ad55a593725299dca76729/xmlschema-1.1.1.tar.gz
cd dist/working && tar -xzvf xmlschema*
cp -r dist/working/xmlschema-1.1.1/xmlschema dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/12/f9/f9960222d5274944b01391749e55e4dcdf28d8f0c108b64ac931ceff6fdb/elementpath-1.4.3.tar.gz
cd dist/working && tar -xzvf elementpath*
cp -r dist/working/elementpath-1.4.3/elementpath dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by bcf
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/21/9f/b251f7f8a76dec1d6651be194dfba8fb8d7781d10ab3987190de8391d08e/six-1.14.0.tar.gz
cd dist/working && tar -xzvf six*
cp -r dist/working/six-1.14.0/six.py dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCSV and ifcopenshell.util.selector
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/18/4d/8d522136c37d9e1ea74062b41b8d5e1318ebf45063ae46ce72ed60af223b/lark-parser-0.8.5.tar.gz
cd dist/working && tar -xzvf lark-parser*
cp -r dist/working/lark-parser-0.8.5/lark dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCClash
ifeq ($(PLATFORM), linux)
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/0c/fa/00d85f893b02289e2942849d97f8818dde8e2111182e825fb3a735677791/python_fcl-0.0.12-cp37-cp37m-manylinux1_x86_64.whl
cd dist/working && unzip python_fcl*
cp -r dist/working/fcl dist/blenderbim/libs/site/packages/
rm -rf dist/working
endif
ifeq ($(PLATFORM), win)
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/19/e6/6e9f33fb59e8f27c0e1592bd26e644bc92b85c942b072b2d3854105d5887/python_fcl_win32-0.0.12.post3-py3-none-win_amd64.whl
cd dist/working && unzip python_fcl*
cp -r dist/working/fcl dist/blenderbim/libs/site/packages/
rm -rf dist/working
endif
# Required by BIMTester
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
cd dist/working && tar -xzvf behave*
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
# See bug #1294
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/patch/model_core.patch
cd dist/working/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.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
rm -rf dist/working
# Required by behave
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
cd dist/working && tar -xzvf parse*
cd dist/working/parse-1.15.0/ && cp parse.py ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by behave
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/2e/79/81bebd1b0446d46733db99d74543b4bb80646ef4c988584bae0862e706bc/parse_type-0.5.2.tar.gz
cd dist/working && tar -xzvf parse_type*
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCOBie for XLSX support
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz
cd dist/working && tar -xzvf XlsxWriter*
cd dist/working/XlsxWriter-1.2.9/ && cp -r xlsxwriter ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by IFCCOBie for ODS support
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz
cd dist/working && tar -xzvf odfpy*
cd dist/working/odfpy-1.4.1/ && cp -r odf ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by odfpy
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz
cd dist/working && tar -xzvf defusedxml*
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by augin
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
cd dist/working && tar -xzvf boto3*
cd dist/working/boto3-1.17.17/ && cp -r boto3 ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/7f/2e/e02fdfd0f0377c4e44e61de27b05d5cfe93575770661ef9ded80ed90fa88/botocore-1.20.17.tar.gz
cd dist/working && tar -xzvf botocore*
cd dist/working/botocore-1.20.17/ && cp -r botocore ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/3c/56/3f325b1eef9791759784aa5046a8f6a1aff8f7c898a2e34506771d3b99d8/jmespath-0.10.0.tar.gz
cd dist/working && tar -xzvf jmespath*
cd dist/working/jmespath-0.10.0/ && cp -r jmespath ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Required by boto3
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/08/e1/3ee2096ebaeeb8c186d20ed16c8faf4a503913e5c9a0e14cd6b8ffc405a3/s3transfer-0.3.4.tar.gz
cd dist/working && tar -xzvf s3transfer*
cd dist/working/s3transfer-0.3.4/ && cp -r s3transfer ../../blenderbim/libs/site/packages/
rm -rf dist/working
# Provides IFCJSON functionality
mkdir dist/working
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
cd dist/working && unzip master.zip
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
rm -rf dist/working
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
cd dist && zip -r blender28-bim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
.PHONY: clean
clean:
rm -rf dist
-127
View File
@@ -1,127 +0,0 @@
# Blender BIM IFC
_Under heavy development!_
Let's use Blender to create BIM models, because why not?
## Features
- IFC4 is a first-class citizen. IFC4 has been an ISO standard since 2013, with
the latest published in 2018. It contains many more features than IFC2X3,
published back in 2005.
- Available for Windows, Mac, and Linux!
- Use open-source Blender 2.8 to do digital modeling! Used by NASA, Hollywood,
and Ubisoft, Blender allows real-time modeling in rendered view, animations,
file linking, proxy objects, Blender Cloud collaboration, lighting analysis,
CFD, energy modeling, and a huge ecosystem of add-ons with a thriving
community. No licensing, and with a strong Python API to integrate into your
custom workflow.
- Create complex spatial structures with multiple sites, buildings, facilities,
bridges, and building parts. Sites can be broken down into sub-sites and
multiple buildings per site. You can categorise different floors to different
buildings.
- Classify any geometry as any IFC type. Just because it is a wall doesn't
mean you should be limited to modeling it in a certain way. Easily change
from one type to another, switch relationships and aggregations, reuse or
even remove geometry altogether, without damaging your geometry or breaking
your BIM model.
- Use collections to create element compositions and decompositions with full
control over the classifications of their parts. Most BIM programs don't give
this flexibility. Create collection instances to efficiently duplicate
composite objects.
- Allow BIM objects to instance a shared geometric representations. This allows
you to place the same shape in multiple objects without drastically
increasing your filesize.
- Provide a custom value to any simple IFC attribute of an IFC product.
- Create product types that regular IFC products can inherit attributes from.
- Define your own property sets with control over exactly how the data in the
property sets are stored using property set template definitions which you
can download or create yourself. Property sets can apply to both types and
objects, and object property sets can selectively override type property
sets.
- Applies array modifiers to create instances.
- Basic quantity take-off support for costing.
- Settings to toggle export of quantities or geometric representations.
Exporting IFC data without geometric representations is much faster and a
fraction of the filesize.
- Maintain `GlobalId` between exports, so that you can manipulate the IFC
reliably in other software while making changes.
- Not all objects need full solid geometry. Create representations as 2D or 3D
curves / wireframes for rapid and lightweight BIM development.
- Create map conversions and coordinate reference system definitions to link
BIM to GIS.
- Export basic materials with surface and diffuse colours for representation.
You can also link materials to an externally defined file, such as a `.blend`
Cycles material, V-Ray material, or even Radiance material for lighting
simulation.
- Switch between different types of metric units when modeling.
- Model multiple representations for different purposes for a single BIM
object or type, such as footprints, axis, reference, or clearance geometry.
You can export just the type of representation required for your usecase, or
export multiple for the user to choose from.
- Record qualitative objectives such as design intentions, health and safety
strategies, or code compliance, performance solutions, and more, in the BIM
model and directly associate them to objects or types.
- Classify objects and types to different classification systems such as
Uniclass, Omniclass, or your custom system.
- Associate external document files such as plans, brochures, specifications,
warranties, or anything to objects and types.
- Specify swept solid geometries, allowing you to round-trip geometry
manipulation with other BIM software which have more limited geometric tools,
like Revit.
- Create material layer sets, for objects which have materials in predefined
layers with thicknesses.
- Create material constituent sets, for objects which are made up of
arbitrarily mixed ingredients.
- Specify nested and hosted element relationships.
- Specify predefined door attributes as defined in the IFC4 specification.
- Specify predefined window attributes as defined in the IFC4 specification.
- Specify surface styles either defined as part of a material or assigned
directly to an object's representation.
- Colour coding by IFC class to visually spot mistakes
Some more experimental features:
- Cut sections for documentation with OpenCascade
- Export gbXML files that are linked to your IFC file to do energy analysis
- QA module to generate Gherkin unit tests to validate your BIM models
- HTML formatter to generate reports from QA results
- COBie spreadsheet extraction to import into CAFM systems
## Demo
[See demo video](https://www.youtube.com/watch?v=iD3v3eu2AjY)
It's only just started, so everything can and will change. But if you want to
check it out:
1. Install [Blender](https://www.blender.org/) 64-bit version.
2. Download the latest packaged version of Blender BIM from
[here](https://blenderbim.org/builds/).
3. Launch Blender. Go to `Edit->Preferences->Add-ons->Install...`. Browse to
the downloaded `.zip` file from step 2.
4. Enable the checkbox next to the Import-Export IFC Blender plugin which will
show up.
5. All done! You will find an import and export IFC option in the `File` menu.
By default, Blender BIM will export whatever you have selected. The objects you
have selected _must_ belong in an `IfcProject` and spatially contained, such as
witihn an `IfcBuilding`. It must also follow the naming convention. Please see
the screenshot below for an example scene.
A naming convention of `IfcClass/IfcName` is enforced. If you name your object
`IfcWall/Foo`, it will turn into an `IfcWall` with the name attribute set to
`Foo`. You can override object attributes using Blender's `Custom Properties`.
Your objects must live in a top level `Collection` defined as a context class,
usually something like `IfcProject/Foo`. You can then nest spatial structures
underneath to create a hierarchy.
## Pictures
![UI](https://aws1.discourse-cdn.com/business6/uploads/buildingsmart1/original/1X/ce820c3ee22adcffae59b40f98ff23379f7e3547.png)
![Blender screenshot](blender-screenshot.png)
... this Blender files creates this IFC file ...
![IfcOpenShell screenshot](ifcopenshell-screenshot.png)
-119
View File
@@ -1,119 +0,0 @@
import bpy
from bpy.types import Operator
class Object_OT_RenameObjects(Operator):
bl_idname = "object.renameobjects"
bl_label = "Rename Object(s)"
# Multi Object rename UI
BNameCB: bpy.props.BoolProperty(name="Base Name:")
BaseName: bpy.props.StringProperty(name="")
PreFixCB: bpy.props.BoolProperty(name="Prefix:")
PreFix: bpy.props.StringProperty(name="")
RemFrst: bpy.props.BoolProperty(name="Remove First")
DgtFrst: bpy.props.IntProperty(name="Digits")
SuffixCB: bpy.props.BoolProperty(name="Suffix")
Suffix: bpy.props.StringProperty(name="")
RemLast: bpy.props.BoolProperty(name="Remove Last")
DgtLast: bpy.props.IntProperty(name="Digits")
NumbredCB: bpy.props.BoolProperty(name="Numbred")
BaseNum: bpy.props.IntProperty(name="Base Number")
Step: bpy.props.IntProperty(name="Step", default=1)
findCB: bpy.props.BoolProperty(name="Replace")
find: bpy.props.StringProperty(name="")
replace: bpy.props.StringProperty(name="")
# Single rename UI
Name: bpy.props.StringProperty(name="Name")
def draw(self, ctx):
SelCount = len(bpy.context.selected_objects)
if SelCount > 1:
box = self.layout.box()
row = box.row()
row.prop(self, "BNameCB")
row.prop(self, "BaseName")
row = box.row()
row.prop(self, "PreFixCB")
row.prop(self, "PreFix")
row = box.row()
row.prop(self, "RemFrst")
row.prop(self, "DgtFrst")
row = box.row()
row.prop(self, "SuffixCB")
row.prop(self, "Suffix")
row = box.row()
row.prop(self, "RemLast")
row.prop(self, "DgtLast")
row = box.row(align=True)
row.prop(self, "NumbredCB")
row.prop(self, "BaseNum")
row.prop(self, "Step")
row = box.row(align=True)
row.prop(self, "findCB")
row.prop(self, "find")
row.prop(self, "replace")
if SelCount == 1:
box = self.layout.box()
row = box.row()
row.prop(self, "Name")
if SelCount == 0:
box = self.layout.box()
row = box.row()
row.label("No Selected Object")
def __init__(self):
if len(bpy.context.selected_objects) == 1:
self.Name = bpy.context.selected_objects[0].name
def execute(self, context):
SelCount = len(bpy.context.selected_objects)
if SelCount > 1:
SelObj = bpy.context.selected_objects
Index = self.BaseNum
for i in range(0, SelCount):
# Get Object Original Name #
NewName = SelObj[i].name
# Set the Base name #
if self.BNameCB:
NewName = self.BaseName
# Remove First characters #
if self.RemFrst:
NewName = NewName[self.DgtFrst : self.DgtFrst + len(NewName)]
# Remove Last Characters #
if self.RemLast:
NewName = NewName[1 : len(NewName) - self.DgtLast]
# Add Prefix to the new name #
if self.PreFixCB:
NewName = self.PreFix + NewName
# Add Suffix to the new name #
if self.SuffixCB:
NewName = NewName + self.Suffix
# Add Digits to end of new name #
if self.NumbredCB:
NewName += str(Index)
Index += self.Step
# Find and Replace #
if self.findCB:
NewName = NewName.replace(self.find, self.replace)
# Set the new name to the object #
SelObj[i].name = NewName
elif SelCount == 1:
bpy.context.selected_objects[0].name = self.Name
return {"FINISHED"}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def register():
bpy.utils.register_class(Object_OT_RenameObjects)
def unregister():
bpy.utils.unregister_class(Object_OT_RenameObjects)
if __name__ == "__main__":
register()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

-32
View File
@@ -1,32 +0,0 @@
bl_info = {
"name": "BlenderBIM",
"description": "Author, import, and export files in the " "Industry Foundation Classes (.ifc) file format",
"author": "Dion Moult, IfcOpenShell",
"blender": (2, 80, 0),
"version": (0, 0, 999999),
"location": "File > Export, File > Import, Scene / Object / Material / Mesh Properties",
"tracker_url": "https://github.com/IfcOpenShell/IfcOpenShell/issues",
"category": "Import-Export",
}
import os
import site
# process *.pth in /libs/site/packages to setup globally importable modules
# 3 levels deep required by occ static ../../ path
cwd = os.path.dirname(os.path.realpath(__file__))
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
# main import
from .bim import *
# Explicitely expose bim.xx when imported with from blenderbim import *
# Other bim still are importable using explicit from blenderbim.bim import xxx
__all__ = ["export_ifc", "import_ifc"]
if __name__ == "__main__":
register()
-2
View File
@@ -1,2 +0,0 @@
# addon writes tmp stuff directly to its dir
/data/
-224
View File
@@ -1,224 +0,0 @@
# Check if we are running in Blender before loading, to allow for multiprocessing
import sys
bpy = sys.modules.get("bpy")
if bpy is not None:
import bpy
import importlib
from . import handler, ui, prop, operator, gizmos
modules = {
"project": None,
"search": None,
"bcf": None,
"root": None,
"unit": None,
"georeference": None,
"context": None,
"drawing": None,
"attribute": None,
"type": None,
"spatial": None,
"void": None,
"aggregate": None,
"geometry": None,
"cobie": None,
"sequence": None,
"cost": None,
"group": None,
"structural": None,
"material": None,
"style": None,
"layer": None,
"model": None,
"owner": None,
"pset": None,
"qto": None,
"classification": None,
"constraint": None,
"document": None,
"pset_template": None,
"clash": None,
"csv": None,
"bimtester": None,
"diff": None,
"patch": None,
"covetool": None,
"augin": None,
"debug": None,
}
for name in modules.keys():
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
classes = [
operator.OpenUri,
operator.SelectDataDir,
operator.SelectSchemaDir,
operator.SelectIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.SelectExternalMaterialDir,
operator.AddSweptSolid,
operator.RemoveSweptSolid,
operator.AssignSweptSolidOuterCurve,
operator.SelectSweptSolidOuterCurve,
operator.AddSweptSolidInnerCurve,
operator.SelectSweptSolidInnerCurves,
operator.AssignSweptSolidExtrusion,
operator.SelectSweptSolidExtrusion,
operator.FetchExternalMaterial,
operator.FetchObjectPassport,
operator.CutSection,
operator.AddSheet,
operator.OpenSheet,
operator.AddDrawingToSheet,
operator.CreateSheets,
operator.OpenView,
operator.OpenViewCamera,
operator.ActivateView,
operator.OpenUpstream,
operator.AddSectionPlane,
operator.RemoveSectionPlane,
operator.ReloadIfcFile,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SelectDocIfcFile,
operator.AddAnnotation,
operator.GenerateReferences,
operator.ResizeText,
operator.AddVariable,
operator.RemoveVariable,
operator.PropagateTextData,
operator.SetOverrideColour,
operator.RemoveDrawing,
operator.AddDrawingStyle,
operator.RemoveDrawingStyle,
operator.SaveDrawingStyle,
operator.ActivateDrawingStyle,
operator.EditVectorStyle,
operator.RemoveSheet,
operator.AddSchedule,
operator.RemoveSchedule,
operator.SelectScheduleFile,
operator.BuildSchedule,
operator.AddScheduleToSheet,
operator.SetViewportShadowFromSun,
operator.AddDrawingStyleAttribute,
operator.RemoveDrawingStyleAttribute,
operator.CopyPropertyToSelection,
operator.CopyAttributeToSelection,
operator.RefreshDrawingList,
operator.CleanWireframes,
operator.LinkIfc,
operator.SnapSpacesTogether,
operator.CopyGrid,
operator.AddSectionsAnnotations,
prop.StrProperty,
prop.Attribute,
prop.Variable,
prop.Drawing,
prop.Schedule,
prop.DrawingStyle,
prop.Sheet,
prop.BIMProperties,
prop.DocProperties,
prop.IfcParameter,
prop.PsetQto,
prop.GlobalId,
prop.RepresentationItem,
prop.BIMObjectProperties,
prop.BIMMaterialProperties,
prop.SweptSolid,
prop.ItemSlotMap,
prop.BIMMeshProperties,
prop.BIMCameraProperties,
prop.BIMTextProperties,
ui.BIM_PT_section_plane,
ui.BIM_PT_drawings,
ui.BIM_PT_schedules,
ui.BIM_PT_sheets,
ui.BIM_PT_text,
ui.BIM_PT_annotation_utilities,
ui.BIM_PT_misc_utilities,
ui.BIM_UL_generic,
ui.BIM_UL_drawinglist,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
gizmos.UglyDotGizmo,
gizmos.DotGizmo,
gizmos.DimensionLabelGizmo,
gizmos.ExtrusionGuidesGizmo,
gizmos.ExtrusionWidget
]
for module in modules.values():
classes.extend(module.classes)
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
def menu_func_import(self, context):
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def on_register(scene):
handler.setDefaultProperties(scene)
bpy.app.handlers.depsgraph_update_post.remove(on_register)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.app.handlers.depsgraph_update_post.append(on_register)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.app.handlers.save_pre.append(handler.storeIdMap)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
for module in modules.values():
module.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.storeIdMap)
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Scene.DocProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
del bpy.types.Collection.BIMObjectProperties # Check if we need this
del bpy.types.Material.BIMMaterialProperties
del bpy.types.Mesh.BIMMeshProperties
del bpy.types.Curve.BIMMeshProperties
del bpy.types.Camera.BIMMeshProperties
del bpy.types.Camera.BIMCameraProperties
del bpy.types.TextCurve.BIMTextProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
for module in reversed(list(modules.values())):
module.unregister()
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
-167
View File
@@ -1,167 +0,0 @@
import bpy
import os
from mathutils import Vector
class Annotator:
@staticmethod
def get_svg_text_size(size):
sizes = {
"1.8": "2.97",
"2.5": "4.13",
"3.5": "5.78",
"5.0": "8.25",
"7.0": "11.55",
}
return float(sizes[str(size)])
@staticmethod
def add_text(related_element=None):
curve = bpy.data.curves.new(type="FONT", name="Plan/Annotation/PLAN_VIEW/Text")
curve.body = "TEXT"
obj = bpy.data.objects.new("IfcAnnotation/Text", curve)
obj.matrix_world = bpy.context.scene.camera.matrix_world
if related_element is None:
location, _, _, _ = Annotator.get_placeholder_coords()
else:
obj.data.BIMTextProperties.related_element = related_element
location = related_element.location
obj.location = location
obj.hide_render = True
font = bpy.data.fonts.get("OpenGost TypeB TT")
if not font:
font = bpy.data.fonts.load(
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
)
font.name = "OpenGost Type B TT"
obj.data.font = font
obj.data.BIMTextProperties.font_size = "2.5"
collection = bpy.context.scene.camera.users_collection[0]
collection.objects.link(obj)
Annotator.resize_text(obj)
return obj
@staticmethod
def resize_text(text_obj):
camera = None
for obj in text_obj.users_collection[0].objects:
if isinstance(obj.data, bpy.types.Camera):
camera = obj
break
if not camera:
return
# This is a magic number for OpenGost
font_size = 1.6 / 1000
font_size *= float(text_obj.data.BIMTextProperties.font_size)
if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
else:
human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
font_size /= float(numerator) / float(denominator)
text_obj.data.size = font_size
@staticmethod
def add_line_to_annotation(obj, co1=None, co2=None):
if co1 is None:
co1, co2, _, _ = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1
co2 = obj.matrix_world.inverted() @ co2
if isinstance(obj.data, bpy.types.Mesh):
obj.data.vertices.add(2)
obj.data.vertices[-2].co = co1
obj.data.vertices[-1].co = co2
obj.data.edges.add(1)
obj.data.edges[-1].vertices = (obj.data.vertices[-2].index, obj.data.vertices[-1].index)
if isinstance(obj.data, bpy.types.Curve):
polyline = obj.data.splines.new("POLY")
polyline.points.add(1)
polyline.points[-2].co = list(co1) + [1]
polyline.points[-1].co = list(co2) + [1]
return obj
@staticmethod
def add_plane_to_annotation(obj):
co1, co2, co3, co4 = Annotator.get_placeholder_coords()
co1 = obj.matrix_world.inverted() @ co1 # bot left
co2 = obj.matrix_world.inverted() @ co2 # top left
co3 = obj.matrix_world.inverted() @ co3 # bot right
co4 = obj.matrix_world.inverted() @ co4 # top right
obj.data.vertices.add(4)
v1 = obj.data.vertices[-4]
v2 = obj.data.vertices[-3]
v3 = obj.data.vertices[-2]
v4 = obj.data.vertices[-1]
v1.co = co4
v2.co = co2
v3.co = co1
v4.co = co3
obj.data.edges.add(4)
e1 = obj.data.edges[-4]
e2 = obj.data.edges[-3]
e3 = obj.data.edges[-2]
e4 = obj.data.edges[-1]
e1.vertices = (v1.index, v2.index)
e2.vertices = (v2.index, v3.index)
e3.vertices = (v3.index, v4.index)
e4.vertices = (v4.index, v1.index)
obj.data.loops.add(4)
l1 = obj.data.loops[-4]
l2 = obj.data.loops[-3]
l3 = obj.data.loops[-2]
l4 = obj.data.loops[-1]
l1.vertex_index = v1.index
l1.edge_index = e1.index
l2.vertex_index = v2.index
l2.edge_index = e2.index
l3.vertex_index = v3.index
l3.edge_index = e3.index
l4.vertex_index = v4.index
l4.edge_index = e4.index
obj.data.polygons.add(1)
p1 = obj.data.polygons[-1]
p1.vertices = (v1.index, v2.index, v3.index, v4.index)
p1.loop_start = l1.index
p1.loop_total = 4
return obj
@staticmethod
def get_annotation_obj(name, data_type):
collection = bpy.context.scene.camera.users_collection[0]
for obj in collection.objects:
if name in obj.name:
return obj
if data_type == "mesh":
data = bpy.data.meshes.new("Plan/Annotation/PLAN_VIEW/" + name)
elif data_type == "curve":
data = bpy.data.curves.new("Plan/Annotation/PLAN_VIEW/" + name, type="CURVE")
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new("IfcAnnotation/" + name, data)
collection.objects.link(obj)
return obj
@staticmethod
def get_placeholder_coords():
camera = bpy.context.scene.camera
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
if bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y:
y = (
camera.data.ortho_scale
* (bpy.context.scene.render.resolution_y / bpy.context.scene.render.resolution_x)
/ 4
)
else:
y = camera.data.ortho_scale / 4
y_offset = camera.matrix_world.to_quaternion() @ Vector((0, y, 0))
x_offset = camera.matrix_world.to_quaternion() @ Vector((y/2, 0, 0))
return (camera.location + z_offset,
camera.location + z_offset + y_offset,
camera.location + z_offset + x_offset,
camera.location + z_offset + x_offset + y_offset)
-552
View File
@@ -1,552 +0,0 @@
import os
import re
import math
import time
import numpy
import pickle
import multiprocessing
def load_occ():
# Don't import until we really need to, as a temporary step before we can purge OCC
try:
from OCC.Core import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.Core.TopoDS import topods
except ImportError:
from OCC import (
gp,
Geom,
Bnd,
BRepBndLib,
BRep,
BRepPrimAPI,
BRepAlgoAPI,
BRepBuilderAPI,
TopOpeBRepTool,
TopOpeBRepBuild,
ShapeExtend,
GProp,
BRepGProp,
GC,
ShapeAnalysis,
TopTools,
TopExp,
TopAbs,
HLRAlgo,
HLRBRep,
TopLoc,
Bnd,
BRepBndLib,
BRepTools,
TopoDS,
GeomLProp,
IntCurvesFace,
)
from OCC.TopoDS import topods
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.selector
import ifcopenshell.util.element
cwd = os.path.dirname(os.path.realpath(__file__))
this_file = os.path.join(cwd, "cut_ifc.py")
def get_booleaned_edges(shape):
load_occ()
edges = []
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
while exp.More():
edges.append(topods.Edge(exp.Current()))
exp.Next()
return edges
def connect_edges_into_wires(unconnected_edges):
load_occ()
edges = TopTools.TopTools_HSequenceOfShape()
edges_handle = TopTools.Handle_TopTools_HSequenceOfShape(edges)
wires = TopTools.TopTools_HSequenceOfShape()
wires_handle = TopTools.Handle_TopTools_HSequenceOfShape(wires)
for edge in unconnected_edges:
edges.Append(edge)
ShapeAnalysis.ShapeAnalysis_FreeBounds.ConnectEdgesToWires(edges_handle, 1e-5, True, wires_handle)
return wires_handle.GetObject()
def do_cut(process_data):
load_occ()
global_id, shape, section, trsf_data = process_data
axis = gp.gp_Ax2(
gp.gp_Pnt(trsf_data["top_left_corner"][0], trsf_data["top_left_corner"][1], trsf_data["top_left_corner"][2]),
gp.gp_Dir(trsf_data["projection"][0], trsf_data["projection"][1], trsf_data["projection"][2]),
gp.gp_Dir(trsf_data["x_axis"][0], trsf_data["x_axis"][1], trsf_data["x_axis"][2]),
)
source = gp.gp_Ax3(axis)
destination = gp.gp_Ax3(gp.gp_Pnt(0, 0, 0), gp.gp_Dir(0, 0, -1), gp.gp_Dir(1, 0, 0))
transformation = gp.gp_Trsf()
transformation.SetDisplacement(source, destination)
cut_polygons = []
section = BRepAlgoAPI.BRepAlgoAPI_Section(section, shape).Shape()
section_edges = get_booleaned_edges(section)
if len(section_edges) <= 0:
return cut_polygons
wires = connect_edges_into_wires(section_edges)
for i in range(wires.Length()):
wire_shape = wires.Value(i + 1)
transformed_wire = BRepBuilderAPI.BRepBuilderAPI_Transform(wire_shape, transformation)
wire_shape = transformed_wire.Shape()
wire = topods.Wire(wire_shape)
face = BRepBuilderAPI.BRepBuilderAPI_MakeFace(wire).Face()
points = []
exp = BRepTools.BRepTools_WireExplorer(wire)
while exp.More():
point = BRep.BRep_Tool.Pnt(exp.CurrentVertex())
points.append((point.X(), -point.Y()))
exp.Next()
cut_polygons.append({"global_id": global_id, "metadata": {}, "points": points})
return cut_polygons
class IfcCutter:
def __init__(self):
self.time = None
self.selector = ifcopenshell.util.selector.Selector()
self.product_shapes = []
self.background_elements = []
self.cut_polygons = []
self.template_variables = {}
self.metadata = {}
self.data_dir = ""
self.vector_style = ""
self.ifc_filenames = []
self.ifc_files = {}
self.resolved_pixels = set()
self.text_pickle_file = "text.pickle"
self.metadata_pickle_file = "metadata.pickle"
self.cut_pickle_file = "cut.pickle"
self.should_recut = True
self.should_recut_selected = True
self.cut_objects = ""
self.selected_global_ids = []
self.should_extract = True
self.diagram_name = None
self.background_image = None
self.section_box = {
"projection": (0, 1, 0),
"x_axis": (1, 0, 0),
"y_axis": (0, 0, -1),
"top_left_corner": (-2, 2, 8),
"x": 14,
"y": 9,
"z": 2,
"shape": None,
"face": None,
}
def cut(self):
self.profile_code("Starting cut process")
self.load_ifc_files()
self.profile_code("Load IFC files")
self.get_template_variables()
self.profile_code("Get template variables")
self.get_product_shapes()
self.profile_code("Get product shapes")
self.create_section_box()
self.profile_code("Create section box")
self.get_cut_polygons()
self.profile_code("Get cut polygons")
self.get_annotation()
self.profile_code("Get annotation")
self.get_cut_polygon_metadata()
self.profile_code("Get cut polygon metadata")
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()
def load_ifc_files(self):
if not self.should_recut and not self.should_extract:
return
loaded_files = []
for filename in self.ifc_filenames:
print("Loading file {} ...".format(filename))
if filename:
self.ifc_files[filename] = ifcopenshell.open(filename)
def get_template_variables(self):
if not self.should_extract:
if os.path.isfile(self.text_pickle_file):
with open(self.text_pickle_file, "rb") as text_file:
self.template_variables = pickle.load(text_file)
return
data = {}
for text_obj in self.text_objs:
text_obj_data = self.get_text_variables(text_obj)
if text_obj_data:
data[text_obj.name] = text_obj_data
with open(self.text_pickle_file, "wb") as text_file:
pickle.dump(data, text_file, protocol=pickle.HIGHEST_PROTOCOL)
self.template_variables = data
def get_text_variables(self, text_obj):
text_obj_data = {}
text_body = text_obj.data.body
related_element = text_obj.data.BIMTextProperties.related_element
if not related_element:
return
global_id = related_element.BIMObjectProperties.attributes.get("GlobalId")
if not global_id:
return
element = self.get_ifc_element(global_id.string_value)
for variable in text_obj.data.BIMTextProperties.variables:
if element:
if "{{" in variable.prop_key:
prop_key = variable.prop_key.split("{{")[1].split("}}")[0]
prop_value = self.selector.get_element_value(element, prop_key)
variable_value = eval(variable.prop_key.replace("{{" + prop_key + "}}", str(prop_value)))
else:
variable_value = self.selector.get_element_value(element, variable.prop_key)
text_obj_data[variable.name] = variable_value
return text_obj_data
def get_product_shapes(self):
if not self.should_recut:
return
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
products = []
for filename, ifc_file in self.ifc_files.items():
shape_pickle = os.path.join(
self.data_dir, "cache", "shapes", "{}.pickle".format(os.path.basename(filename))
)
shape_map = {}
if self.should_recut_selected and os.path.isfile(shape_pickle):
with open(shape_pickle, "rb") as shape_file:
shape_map = pickle.load(shape_file)
products.extend(self.selector.parse(ifc_file, self.cut_objects))
selected_elements = []
for i, product in enumerate(products):
if (
product.is_a("IfcOpeningElement")
or product.is_a("IfcSite")
or product.Representation is None
or self.has_annotation(product)
):
continue
try:
if self.should_recut_selected and product.GlobalId in self.selected_global_ids:
selected_elements.append(product)
elif product.GlobalId in shape_map:
shape = shape_map[product.GlobalId]
self.add_product_shape(product, shape)
else:
selected_elements.append(product)
except:
print("Failed to create shape for {}".format(product))
if selected_elements:
total = 0
checkpoint = time.time()
iterator = ifcopenshell.geom.iterator(
settings, ifc_file, multiprocessing.cpu_count(), include=selected_elements
)
valid_file = iterator.initialize()
if valid_file:
while True:
total += 1
if total % 250 == 0:
print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint))
checkpoint = time.time()
shape = iterator.get()
shape_map[shape.data.guid] = shape.geometry
self.add_product_shape(ifc_file.by_guid(shape.data.guid), shape.geometry)
if not iterator.next():
break
with open(shape_pickle, "wb") as shape_file:
pickle.dump(shape_map, shape_file, protocol=pickle.HIGHEST_PROTOCOL)
def add_product_shape(self, product, shape):
self.product_shapes.append((product, shape))
def has_annotation(self, element):
for representation in element.Representation.Representations:
if (
representation.ContextOfItems.ContextType == "Plan"
and representation.ContextOfItems.ContextIdentifier == "Annotation"
):
return True
return False
def create_section_box(self):
load_occ()
top_left_corner = gp.gp_Pnt(
self.section_box["top_left_corner"][0],
self.section_box["top_left_corner"][1],
self.section_box["top_left_corner"][2],
)
axis = gp.gp_Ax2(
top_left_corner,
gp.gp_Dir(
self.section_box["projection"][0], self.section_box["projection"][1], self.section_box["projection"][2]
),
gp.gp_Dir(self.section_box["x_axis"][0], self.section_box["x_axis"][1], self.section_box["x_axis"][2]),
)
section_box = BRepPrimAPI.BRepPrimAPI_MakeBox(
axis, self.section_box["x"], self.section_box["y"], self.section_box["z"]
)
self.section_box["shape"] = section_box.Shape()
self.section_box["face"] = section_box.BottomFace()
source = gp.gp_Ax3(axis)
self.transformation_data = {
"top_left_corner": self.section_box["top_left_corner"],
"projection": self.section_box["projection"],
"x_axis": self.section_box["x_axis"],
}
destination = gp.gp_Ax3(gp.gp_Pnt(0, 0, 0), gp.gp_Dir(0, 0, -1), gp.gp_Dir(1, 0, 0))
self.transformation_dest = destination
self.transformation = gp.gp_Trsf()
self.transformation.SetDisplacement(source, destination)
def get_bbox(self, shape):
load_occ()
bbox = Bnd.Bnd_Box()
BRepBndLib.brepbndlib_Add(shape, bbox)
return bbox
def calculate_face_zpos(self, face):
bbox = self.get_bbox(face)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
zpos = zmin + ((zmax - zmin) / 2)
return zpos, zmax
def get_booleaned_edges(self, shape):
load_occ()
edges = []
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
while exp.More():
edges.append(topods.Edge(exp.Current()))
exp.Next()
return edges
def get_cut_polygons(self):
if self.should_recut:
self.get_fresh_cut_polygons()
self.pickle_cut_polygons()
else:
self.get_pickled_cut_polygons()
def get_annotation(self):
import mathutils
load_occ()
self.annotation_objs = []
settings_2d = ifcopenshell.geom.settings()
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
settings_py = ifcopenshell.geom.settings()
settings_py.set(settings_py.USE_PYTHON_OPENCASCADE, True)
for ifc_file in self.ifc_files.values():
for element in ifc_file.by_type("IfcElement"):
annotation_representation = None
box_representation = None
if not element.Representation:
continue # This can occur for aggregates
for representation in element.Representation.Representations:
if (
representation.ContextOfItems.ContextType == "Plan"
and representation.ContextOfItems.ContextIdentifier == "Annotation"
):
annotation_representation = representation
elif (
representation.ContextOfItems.ContextType == "Model"
and representation.ContextOfItems.ContextIdentifier == "Box"
):
box_representation = representation
if not annotation_representation or not box_representation:
continue
# This is bad code. See bug #85 to make it slightly less bad.
# Effectively if the bbox does not intersect with the camera
# plane, then we should "continue" and not process the 2D
# wireframe. This approach works but is not very smart.
for subelement in ifc_file.traverse(box_representation):
if subelement.is_a("IfcBoundingBox"):
block = ifc_file.createIfcBlock(
ifc_file.createIfcAxis2Placement3D(subelement.Corner, None, None),
subelement.XDim,
subelement.YDim,
subelement.ZDim,
)
for inverse in ifc_file.get_inverse(subelement):
ifcopenshell.util.element.replace_attribute(inverse, subelement, block)
element.Representation.Representations = [box_representation]
shape = ifcopenshell.geom.create_shape(settings_py, element)
section = BRepAlgoAPI.BRepAlgoAPI_Section(self.section_box["face"], shape.geometry).Shape()
section_edges = get_booleaned_edges(section)
if len(section_edges) <= 0:
# The bounding box of the annotation object does not
# intersect with the camera plane, so don't bother drawing
# the annotation
continue
# Monkey patch - see bug #771.
element.Representation.Representations = [annotation_representation]
shape = ifcopenshell.geom.create_shape(settings_2d, element)
if hasattr(shape, "geometry"):
geometry = shape.geometry
else:
geometry = shape
e = geometry.edges
v = geometry.verts
m = shape.transformation.matrix.data
mat = mathutils.Matrix(
([m[0], m[1], m[2], 0], [m[3], m[4], m[5], 0], [m[6], m[7], m[8], 0], [m[9], m[10], m[11], 1])
)
mat.transpose()
self.annotation_objs.append(
{
"raw": element,
"classes": self.get_classes(element, "annotation"),
"edges": [[e[i], e[i + 1]] for i in range(0, len(e), 2)],
"vertices": [mat @ mathutils.Vector((v[i], v[i + 1], v[i + 2])) for i in range(0, len(v), 3)],
}
)
def get_cut_polygon_metadata(self):
if not self.should_extract:
if os.path.isfile(self.metadata_pickle_file):
with open(self.metadata_pickle_file, "rb") as metadata_file:
self.metadata = pickle.load(metadata_file)
for polygon in self.cut_polygons:
if polygon["global_id"] in self.metadata:
polygon["metadata"] = self.metadata[polygon["global_id"]]
return
for polygon in self.cut_polygons:
metadata = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), "cut")}
self.metadata[polygon["global_id"]] = metadata
polygon["metadata"] = metadata
with open(self.metadata_pickle_file, "wb") as metadata_file:
pickle.dump(self.metadata, metadata_file, protocol=pickle.HIGHEST_PROTOCOL)
def pickle_cut_polygons(self):
with open(self.cut_pickle_file, "wb") as pickle_file:
pickle.dump(self.cut_polygons, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
def get_fresh_cut_polygons(self):
process_data = [
(p.GlobalId, s, self.section_box["face"], self.transformation_data) for p, s in self.product_shapes
]
import bpy
if bpy.app.version > (2, 90, 0) and os.name == 'nt':
# See bug #1148
for data in process_data:
results = do_cut(data)
polygons = [r for r in results if r["points"]]
self.cut_polygons.extend(polygons)
else:
multiprocessing.set_executable(bpy.app.binary_path_python)
with multiprocessing.Pool(9) as p:
results = p.map(do_cut, process_data)
for result in results:
polygons = [p for p in result if p["points"]]
self.cut_polygons.extend(polygons)
def get_polygon_metadata(self, polygon, position):
polygon["metadata"] = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), position)}
return polygon
def get_ifc_element(self, global_id):
# TODO: make this less bad
element = None
for ifc_file in self.ifc_files.values():
try:
element = ifc_file.by_id(global_id)
return element
except:
pass
def get_classes(self, element, position):
classes = [position, element.is_a()]
material = ifcopenshell.util.element.get_material(element)
if material:
classes.append(
"material-{}".format(
re.sub("[^0-9a-zA-Z]+", "", self.get_material_name(material))
)
)
classes.append("globalid-{}".format(element.GlobalId))
for attribute in self.attributes:
result = self.selector.get_element_value(element, attribute)
if result:
classes.append(
"{}-{}".format(re.sub("[^0-9a-zA-Z]+", "", attribute), re.sub("[^0-9a-zA-Z]+", "", result))
)
return classes
def get_material_name(self, element):
if hasattr(element, "Name") and element.Name:
return element.Name
elif hasattr(element, "LayerSetName") and element.LayerSetName:
return element.LayerSetName
return "mat-" + str(element.id())
def get_pickled_cut_polygons(self):
if os.path.isfile(self.cut_pickle_file):
with open(self.cut_pickle_file, "rb") as pickle_file:
self.cut_polygons = pickle.load(pickle_file)
@@ -1,2 +0,0 @@
*
!.gitignore
@@ -1,8 +0,0 @@
{
"eevee": {
"uri": "mpass/shader.blend"
},
"cycles": {
"uri": "mpass/shader.blend"
}
}
@@ -1,14 +0,0 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('Pset_Custom.ifc','2020-01-01T00:00:00',(),(),'Sample','Sample',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1= IFCPROPERTYSETTEMPLATE('05N2Dhepb9ouTCSmWBh22D',$,'Custom_Pset','An example custom pset.',.PSET_TYPEDRIVENOVERRIDE.,'IfcObject',(#2,#3));
#2= IFCSIMPLEPROPERTYTEMPLATE('37EpTDq4v96gTZh196BBXQ',$,'NumericProperty','An example numeric property',.P_SINGLEVALUE.,'IfcCountMeasure','',$,$,$,$,.READWRITE.);
#3= IFCSIMPLEPROPERTYTEMPLATE('22H7CzDvz1XfVHKkGWoYUK',$,'StringProperty','An example string property',.P_SINGLEVALUE.,'IfcLabel','',$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -1,27 +0,0 @@
* { stroke-linecap: round; }
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.hidden { stroke-dasharray: 3, 2; }
.solid {}
.leader { marker-end: url(#leader-marker); }
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.break { fill: white; }
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.material-blank { fill: white; }
.material-diagonal1 { fill: url(#diagonal1); }
.material-diagonal2 { fill: url(#diagonal2); }
.material-diagonal3 { fill: url(#diagonal3); }
.material-crosshatch1 { fill: url(#crosshatch1); }
.material-crosshatch2 { fill: url(#crosshatch2); }
.material-crosshatch3 { fill: url(#crosshatch3); }
.material-brick { fill: url(#brick); }
.material-earth { fill: url(#earth); }
.material-glass { fill: url(#glass); }
.material-liquid { fill: url(#liquid); }
.material-grass { fill: url(#grass); }
.material-honeycomb { fill: url(#honeycomb); }
.material-sand { fill: url(#sand); }
.material-concrete { fill: url(#concrete); stroke-width: 0.5; }
.material-boundary { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12,4,3,4,3,4; }
.IfcSpace { fill: none; stroke: none; }
@@ -1,10 +0,0 @@
.cut { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.hidden { stroke-dasharray: 3, 2; }
.solid {}
.leader { marker-end: url(#leader-marker); }
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.break { fill: white; }
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.IfcSpace { fill: none; stroke: none; }
@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<marker id="eng-dimension-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
<path d="M 1 0 L 1 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="eng-dimension-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
<path d="M 10 0 L 10 14" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="dimension-marker-start" markerHeight="30" markerWidth="20" refX="10" refY="15" orient="auto">
<g>
<path d="M 0 15 L 20 15" class="annotation" style="stroke-width:1;" />
<path d="M 10 0 L 10 30" class="annotation" style="stroke-width:1;" />
<path d="M 5 20 L 15 10" class="annotation" style="stroke-width:3;" />
</g>
</marker>
<marker id="dimension-marker-end" markerHeight="30" markerWidth="20" refX="10" refY="15" orient="auto">
<g>
<path d="M 0 15 L 20 15" class="annotation" style="stroke-width:1;" />
<path d="M 10 0 L 10 30" class="annotation" style="stroke-width:1;" />
<path d="M 5 20 L 15 10" class="annotation" style="stroke-width:3;" />
</g>
</marker>
<marker id="grid-marker" markerHeight="40" markerWidth="40" orient="auto" refX="20" refY="20">
<circle cx="20" cy="20" fill="white" stroke="black" r="17" style="stroke-width: 2"/>
</marker>
<marker id="leader-marker" markerHeight="7" markerWidth="10" orient="auto" refX="10" refY="3.5">
<path d="M 0 0 L 0 7 L 10 3.5" class="annotation" style="fill:black;" />
</marker>
<marker id="stair-marker-start" markerHeight="10" markerWidth="10" orient="auto" refX="5" refY="5">
<circle cx="5" cy="5" fill="black" stroke="none" r="5"/>
</marker>
<marker id="stair-marker-end" markerHeight="42" markerWidth="21" orient="auto" refX="21" refY="21">
<path d="M 0 0 L 21 21 L 0 42" class="annotation stair" style="stroke-width:2; marker-start: none; marker-end: none;" />
</marker>
<marker id="plan-level-marker" markerHeight="30" markerWidth="30" refX="15" refY="15">
<g>
<path d="M 15 0 L 15 30" class="annotation" style="stroke-width:1;" />
<path d="M 0 15 L 30 15" class="annotation" style="stroke-width:1;" />
<circle cx="15" cy="15" fill="white" stroke="black" r="7.5" style="stroke-width: 2"/>
<path d="M 15 15 L 7.5 15 A 7.5 7.5 0 0 1 15 7.5 Z" fill="black"/>
<path d="M 15 15 L 22.5 15 A 7.5 7.5 0 0 1 15 22.5 Z" fill="black"/>
</g>
</marker>
<marker id="section-level-marker" markerHeight="11" markerWidth="90" refX="20" refY="10">
<g>
<path d="M 0 1 L 20 1 L 10 11 Z" fill="black" />
<path d="M 0 1 L 90 1" class="annotation" style="stroke-width:1;" />
</g>
</marker>
<marker id="breakline-marker" markerHeight="20" markerWidth="15" refX="7.5" refY="10">
<g>
<path d="M 0 10 L 5 20 L 7.5 10" class="annotation" style="stroke-width:1; fill: white;" />
<path d="M 7.5 10 L 10 0 L 15 10" class="annotation" style="stroke-width:1; fill: white;" />
</g>
</marker>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 65 KiB

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="rectangle-tag">
<rect x="-6" y="-2.5" width="12" height="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
</g>
<g id="door-tag">
<circle cy="1.75" r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
<line x1="-5" y1="1.75" x2="5" y2="1.75" style="stroke:black; stroke-width: 0.25;" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 572 B

@@ -1,208 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 841 594"
id="root"
version="1.1"
height="594mm"
width="841mm">
<defs
id="defs901" />
<metadata
id="metadata90683">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect1036"
width="841"
height="594"
x="-4.3253262e-05"
y="3.1719057e-05" />
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90667"
style="stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">
<rect
y="-1046.7792"
x="75.740562"
height="2093.5583"
width="3027.1016"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.88976377;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="909.15491"
x="113.70713"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="909.15491"
x="113.70713"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="M 75.590564,895.24802 H 3102.9921"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 151.6811,895.24802 V 1046.9291"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 454.04331,895.24802 V 1046.9291"
id="path116534" />
<path
id="path116536"
d="M 529.63386,895.24802 V 1046.4302"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 605.22441,895.24802 V 1046.9291"
id="path116538" />
<path
id="path116540"
d="M 75.590564,914.14565 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,933.04329 H 605.22441"
id="path116542" />
<path
id="path116544"
d="M 75.590564,951.94093 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,970.83857 H 605.22441"
id="path116546" />
<path
id="path116548"
d="M 75.590564,989.73621 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.590564,1008.6338 H 605.22441"
id="path116550" />
<path
id="path116552"
d="M 75.590564,1027.5315 H 605.22441"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116576"
d="M 2800.5,895.24802 V 1046.9291"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116578"
d="m 2800.5,1008.6338 h 302.4921"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116598"
d="m 3027.4027,1008.6338 v 38.2953"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="302.86591"
y="909.15491"
id="text1395"><tspan
id="tspan1393"
x="302.86591"
y="909.15491"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="909.15491"
x="491.97513"
style="font-style:normal;font-weight:normal;font-size:15.1536px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15.1536px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="909.15491"
x="491.97513"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:16.0591px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="568.05585"
y="909.15485"
id="text1403"><tspan
id="tspan1401"
x="568.05585"
y="909.15485"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.0591px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:11.2271px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
x="2839.5081"
y="1020.4988"
id="text1421"><tspan
id="tspan1419"
x="2839.5081"
y="1020.4988"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.2271px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none">DRAWING NUMBER</tspan></text>
<text
id="text1429"
y="1020.4988"
x="3038.5586"
style="font-style:normal;font-weight:normal;font-size:11.2271px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.94488188;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.2271px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.94488188;stroke-miterlimit:4;stroke-dasharray:none"
y="1020.4988"
x="3038.5586"
id="tspan1427">REV</tspan></text>
<text
id="text2529"
y="1036.1166"
x="3030.9653"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.999999px"
y="1036.1166"
x="3030.9653"
id="tspan2527">{{revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="2804.7021"
y="1036.1166"
id="text2533"><tspan
id="tspan2531"
x="2804.7021"
y="1036.1166"
style="stroke-width:0.999999px">{{number}}</tspan></text>
</g>
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90669"
style="stroke-width:3.77953" />
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90671"
style="stroke-width:3.77953" />
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90673"
style="stroke-width:3.77953" />
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90675"
style="stroke-width:3.77953" />
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
id="g90677"
style="stroke-width:3.77953" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,203 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="594mm"
height="420mm"
version="1.1"
id="root"
viewBox="0 0 594 420">
<defs
id="defs901" />
<metadata
id="metadata90683">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
y="3.1753578e-05"
x="-4.3300333e-05"
height="420"
width="594.64642"
id="rect1036"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000" />
<rect
y="20.25"
x="20.25"
height="379.5"
width="553.5"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="363.54721"
x="30.085009"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="363.54721"
x="30.085009"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="m 20.000001,359.86771 553.749999,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 40.132289,359.86771 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 120.13229,359.86771 V 400"
id="path116534" />
<path
id="path116536"
d="M 140.13229,359.86771 V 399.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 160.13229,359.86771 V 400"
id="path116538" />
<path
id="path116540"
d="M 20.000001,364.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,369.86771 H 160.13229"
id="path116542" />
<path
id="path116544"
d="M 20.000001,374.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,379.86771 H 160.13229"
id="path116546" />
<path
id="path116548"
d="M 20.000001,384.86771 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 20.000001,389.8677 H 160.13229"
id="path116550" />
<path
id="path116552"
d="M 20.000001,394.86772 H 160.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="80.13327"
y="363.54721"
id="text1395"><tspan
id="tspan1393"
x="80.13327"
y="363.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="363.54721"
x="130.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="363.54721"
x="130.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="150.29811"
y="363.54721"
id="text1403"><tspan
id="tspan1401"
x="150.29811"
y="363.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<path
id="path116576"
d="M 493.96563,359.86771 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116578"
d="M 493.96563,389.8677 H 574"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116598"
d="M 554.00031,389.8677 V 400"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="504.28653"
y="393.00699"
id="text1421"><tspan
id="tspan1419"
x="504.28653"
y="393.00699"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DRAWING NUMBER</tspan></text>
<text
id="text1429"
y="393.00699"
x="556.9519"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="393.00699"
x="556.9519"
id="tspan1427">REV</tspan></text>
<text
id="text2529"
y="397.13919"
x="554.94287"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.264583px"
y="397.13919"
x="554.94287"
id="tspan2527">{{revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="495.07742"
y="397.13919"
id="text2533"><tspan
id="tspan2531"
x="495.07742"
y="397.13919"
style="stroke-width:0.264583px">{{number}}</tspan></text>
<g
style="stroke-width:3.77953"
id="g90669"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90671"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90673"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90675"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90677"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,207 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="420mm"
height="297mm"
version="1.1"
id="root"
viewBox="0 0 420 297">
<defs
id="defs901" />
<metadata
id="metadata90683">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<desc
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
<rect
y="3.1753578e-05"
x="-4.3300333e-05"
height="297"
width="420"
id="rect1036"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000" />
<rect
y="10.249999"
x="10.249999"
height="276.5"
width="399.5"
id="rect116616"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.49999897;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text116526"
y="250.54721"
x="20.085007"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="20.085007"
id="tspan116524">REV. NO.</tspan></text>
<path
id="path116530"
d="m 10,246.86771 399.75,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path116532"
d="M 30.132288,246.86771 V 287"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 110.13229,246.86771 V 287"
id="path116534" />
<path
id="path116536"
d="M 130.13229,246.86771 V 286.868"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 150.13229,246.86771 V 287"
id="path116538" />
<path
id="path116540"
d="M 10,251.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,256.86771 H 150.13229"
id="path116542" />
<path
id="path116544"
d="M 10,261.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,266.86771 H 150.13229"
id="path116546" />
<path
id="path116548"
d="M 10,271.86771 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,276.8677 H 150.13229"
id="path116550" />
<path
id="path116552"
d="M 10,281.86772 H 150.13229"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="70.13327"
y="250.54721"
id="text1395"><tspan
id="tspan1393"
x="70.13327"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DESCRIPTION</tspan></text>
<text
id="text1399"
y="250.54721"
x="120.16841"
style="font-style:normal;font-weight:normal;font-size:4.00939px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.00939px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="250.54721"
x="120.16841"
id="tspan1397">AUTHOR</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.24897px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="140.29811"
y="250.54721"
id="text1403"><tspan
id="tspan1401"
x="140.29811"
y="250.54721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.24897px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">DATE</tspan></text>
<g
transform="translate(-154)"
id="g3735">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 483.96563,246.86771 V 287"
id="path116576" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 483.96563,276.8677 H 564"
id="path116578" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 544.00031,276.8677 V 287"
id="path116598" />
<text
id="text1421"
y="280.00699"
x="494.28653"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none"
y="280.00699"
x="494.28653"
id="tspan1419">DRAWING NUMBER</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:2.9705px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="546.9519"
y="280.00699"
id="text1429"><tspan
id="tspan1427"
x="546.9519"
y="280.00699"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.9705px;line-height:100%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.25;stroke-miterlimit:4;stroke-dasharray:none">REV</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="544.94287"
y="284.13919"
id="text2529"><tspan
id="tspan2527"
x="544.94287"
y="284.13919"
style="stroke-width:0.264583px">{{revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
x="485.07742"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xml:space="preserve"><tspan
style="stroke-width:0.264583px"
y="284.13919"
x="485.07742"
id="tspan2531">{{number}}</tspan></text>
</g>
<g
style="stroke-width:3.77953"
id="g90669"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90671"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90673"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90675"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
<g
style="stroke-width:3.77953"
id="g90677"
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,95 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="root"
version="1.1"
viewBox="0 0 50.222462 10"
height="10mm"
width="50.222462mm"
sodipodi:docname="view-title.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview14"
showgrid="false"
inkscape:zoom="4.2318262"
inkscape:cx="61.584312"
inkscape:cy="26.339719"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-27.82024,-96.649986)"
id="layer1">
<circle
r="4.8499999"
cy="101.64999"
cx="32.82024"
id="path853"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.30000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path855"
d="m 37.67024,101.64999 40.37246,-1e-5"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
id="text859"
y="103.10933"
x="32.822662"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24.80276871px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055365px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.26458332px"
y="103.10933"
x="32.822662"
id="tspan857">{{no}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.71626091px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458329px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="39.12896"
y="100.19173"
id="text863"><tspan
id="tspan861"
x="39.12896"
y="100.19173"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458329px">{{name}}</tspan></text>
<text
id="text867"
y="105.0553"
x="39.285267"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.71626282px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458332px"
y="105.0553"
x="39.285267"
id="tspan865">{{scale}}</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

@@ -1,2 +0,0 @@
Name,foo
TransomThickness,500
1 Name foo
2 TransomThickness 500
File diff suppressed because it is too large Load Diff
-162
View File
@@ -1,162 +0,0 @@
import os
import bpy
import json
import numpy as np
import datetime
import zipfile
import tempfile
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
import addon_utils
class IfcExporter:
def __init__(self, ifc_export_settings):
self.ifc_export_settings = ifc_export_settings
def export(self):
self.file = IfcStore.get_file()
self.set_header()
if bpy.context.scene.BIMProjectProperties.is_authoring:
self.sync_object_placements_and_deletions()
self.sync_edited_objects()
extension = self.ifc_export_settings.output_file.split(".")[-1]
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
filename, ext = os.path.splitext(os.path.basename(self.ifc_export_settings.output_file))
tmp_name = "{}.ifc".format(filename)
tmp_file = os.path.join(unzipped_path, tmp_name)
self.file.write(tmp_file)
with zipfile.ZipFile(
self.ifc_export_settings.output_file, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
) as zf:
zf.write(tmp_file)
elif extension == "ifc":
self.file.write(self.ifc_export_settings.output_file)
elif extension == "ifcjson":
import ifcjson
if self.ifc_export_settings.json_version == "4":
jsonData = ifcjson.IFC2JSON4(self.file, self.ifc_export_settings.json_compact).spf2Json()
with open(self.ifc_export_settings.output_file, "w") as outfile:
json.dump(jsonData, outfile, indent=None if self.ifc_export_settings.json_compact else 4)
elif self.ifc_export_settings.json_version == "5a":
jsonData = ifcjson.IFC2JSON5a(self.file, self.ifc_export_settings.json_compact).spf2Json()
with open(self.ifc_export_settings.output_file, "w") as outfile:
json.dump(jsonData, outfile, indent=None if self.ifc_export_settings.json_compact else 4)
if bpy.context.scene.BIMProjectProperties.is_authoring:
if bpy.data.filepath:
bpy.ops.wm.save_mainfile()
def set_header(self):
# TODO: add all metadata, pending bug #747
self.file.wrapped_data.header.file_name.name = os.path.basename(self.ifc_export_settings.output_file)
self.file.wrapped_data.header.file_name.time_stamp = (
datetime.datetime.utcnow()
.replace(tzinfo=datetime.timezone.utc)
.astimezone()
.replace(microsecond=0)
.isoformat()
)
self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
self.get_application_name(), self.get_application_version()
)
# TODO: reimplement. See #1222.
# if self.owner_history:
# if self.schema_version == "IFC2X3":
# self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
# else:
# self.file.wrapped_data.header.file_name.authorization = (
# self.owner_history.OwningUser.ThePerson.Identification
# )
# else:
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
def sync_object_placements_and_deletions(self):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
to_delete = []
for guid, obj in IfcStore.guid_map.items():
try:
self.sync_object_placement(obj)
except:
pass
if self.should_delete(guid, obj):
to_delete.append(guid)
for guid in to_delete:
product = self.file.by_id(guid)
IfcStore.unlink_element(product)
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
def sync_edited_objects(self):
for obj_name in IfcStore.edited_objs.copy():
obj = bpy.data.objects.get(obj_name)
if not obj:
continue
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
bpy.ops.bim.update_mesh_representation(obj=obj.name)
IfcStore.edited_objs.clear()
def sync_object_placement(self, obj):
blender_matrix = np.matrix(obj.matrix_world)
ifc_matrix = ifcopenshell.util.placement.get_local_placement(
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).ObjectPlacement
)
ifc_matrix[0][3] *= self.unit_scale
ifc_matrix[1][3] *= self.unit_scale
ifc_matrix[2][3] *= self.unit_scale
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
ifc_matrix = ifcopenshell.util.geolocation.global2local(
ifc_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),
)
if not np.allclose(ifc_matrix, blender_matrix, atol=0.0001):
bpy.ops.bim.edit_object_placement(obj=obj.name)
def should_delete(self, guid, obj):
try:
# This will throw an exception if the Blender object no longer exists
foo = obj.name
return False
except:
return True
def get_application_name(self):
return "BlenderBIM"
def get_application_version(self):
return ".".join(
[
str(x)
for x in [
addon.bl_info.get("version", (-1, -1, -1))
for addon in addon_utils.modules()
if addon.bl_info["name"] == "BlenderBIM"
][0]
]
)
class IfcExportSettings:
def __init__(self):
self.logger = None
self.output_file = None
@staticmethod
def factory(context, output_file, logger):
scene_bim = context.scene.BIMProperties
settings = IfcExportSettings()
settings.output_file = output_file
settings.logger = logger
return settings
-400
View File
@@ -1,400 +0,0 @@
import bpy
import blf
from bpy import types
import math
from mathutils import Vector, Matrix
from mathutils import geometry
import gpu, bgl
from bpy_extras import view3d_utils
from .shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader
"""Gizmos under the hood
## Transforms:
source/blender/windowmanager/gizmo/WM_gizmo_types.h
matrix_basis -- "Transformation of this gizmo." = placement in scene
matrix_offset -- "Custom offset from origin." = local transforms according to state/value
matrix_space -- "The space this gizmo is being modified in." used by some gizmos for undefined purposes
matrix_world -- final matrix, scaled according to viewport zoom and custom scale_basis
source/blender/windowmanager/gizmo/intern/wm_gizmo.c:WM_gizmo_calc_matrix_final_params
final = space @ (autoscale * (basis @ offset))
final = space @ (basis @ offset) -- if gizmo.use_draw_scale == False
final = space @ ((autoscale * basis) @ offset) -- if gizmo.use_draw_offset_scale
source/blender/windowmanager/gizmo/intern/wm_gizmo.c:wm_gizmo_calculate_scale
autoscale = gizmo.scale_basis * magic(preferences, matrix_space, matrix_basis, context.region_data)
magic -- making 1.0 to match preferences.view.gizmo_size pixels (75 by default)
## Selection
select_id -- apparently, id of a selectable part
test_select -- expected to return id of selection, doesn't seem to work
draw_select -- fake-draw of selection geometry for gpu-side cursor tracking
"""
# some geometries for Gizmo.custom_shape shaders
CUBE = (
(+1, +1, +1), (-1, +1, +1), (+1, -1, +1), # top
(+1, -1, +1), (-1, +1, +1), (-1, -1, +1),
(+1, +1, +1), (+1, -1, +1), (+1, +1, -1), # right
(+1, +1, -1), (+1, -1, +1), (+1, -1, -1),
(+1, +1, +1), (+1, +1, -1), (-1, +1, +1), # back
(-1, +1, +1), (+1, +1, -1), (-1, +1, -1),
(-1, -1, -1), (-1, +1, -1), (+1, -1, -1), # bot
(+1, -1, -1), (-1, +1, -1), (+1, +1, -1),
(-1, -1, -1), (-1, -1, +1), (-1, +1, -1), # left
(-1, +1, -1), (-1, -1, +1), (-1, +1, +1),
(-1, -1, -1), (+1, -1, -1), (-1, -1, +1), # front
(-1, -1, +1), (+1, -1, -1), (+1, -1, +1)
)
DISC = (
(0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0),
(0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0),
(0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0),
(0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0),
(0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0),
(0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0),
(0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0),
(0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0),
(0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0),
(0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0),
(0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0),
(0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0),
)
X3DISC = (
(0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0),
(0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0),
(0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0),
(0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0),
(0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0),
(0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0),
(0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0),
(0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0),
(0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0),
(0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0),
(0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0),
(0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0),
(0.0, 0.0, 0.0), (0, 1.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994),
(0.0, 0.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994), (0, 0.5000000000000001, 0.8660254037844386),
(0.0, 0.0, 0.0), (0, 0.5000000000000001, 0.8660254037844386), (0, 6.123233995736766e-17, 1.0),
(0.0, 0.0, 0.0), (0, 6.123233995736766e-17, 1.0), (0, -0.4999999999999998, 0.8660254037844387),
(0.0, 0.0, 0.0), (0, -0.4999999999999998, 0.8660254037844387), (0, -0.8660254037844385, 0.5000000000000003),
(0.0, 0.0, 0.0), (0, -0.8660254037844385, 0.5000000000000003), (0, -1.0, 1.2246467991473532e-16),
(0.0, 0.0, 0.0), (0, -1.0, 1.2246467991473532e-16), (0, -0.8660254037844388, -0.4999999999999997),
(0.0, 0.0, 0.0), (0, -0.8660254037844388, -0.4999999999999997), (0, -0.5000000000000004, -0.8660254037844384),
(0.0, 0.0, 0.0), (0, -0.5000000000000004, -0.8660254037844384), (0, -1.8369701987210297e-16, -1.0),
(0.0, 0.0, 0.0), (0, -1.8369701987210297e-16, -1.0), (0, 0.49999999999999933, -0.866025403784439),
(0.0, 0.0, 0.0), (0, 0.49999999999999933, -0.866025403784439), (0, 0.8660254037844384, -0.5000000000000004),
(0.0, 0.0, 0.0), (0, 0.8660254037844384, -0.5000000000000004), (0, 1.0, 0.0),
(0.0, 0.0, 0.0), (0.0, 0, 1.0), (0.49999999999999994, 0, 0.8660254037844387),
(0.0, 0.0, 0.0), (0.49999999999999994, 0, 0.8660254037844387), (0.8660254037844386, 0, 0.5000000000000001),
(0.0, 0.0, 0.0), (0.8660254037844386, 0, 0.5000000000000001), (1.0, 0, 6.123233995736766e-17),
(0.0, 0.0, 0.0), (1.0, 0, 6.123233995736766e-17), (0.8660254037844387, 0, -0.4999999999999998),
(0.0, 0.0, 0.0), (0.8660254037844387, 0, -0.4999999999999998), (0.5000000000000003, 0, -0.8660254037844385),
(0.0, 0.0, 0.0), (0.5000000000000003, 0, -0.8660254037844385), (1.2246467991473532e-16, 0, -1.0),
(0.0, 0.0, 0.0), (1.2246467991473532e-16, 0, -1.0), (-0.4999999999999997, 0, -0.8660254037844388),
(0.0, 0.0, 0.0), (-0.4999999999999997, 0, -0.8660254037844388), (-0.8660254037844384, 0, -0.5000000000000004),
(0.0, 0.0, 0.0), (-0.8660254037844384, 0, -0.5000000000000004), (-1.0, 0, -1.8369701987210297e-16),
(0.0, 0.0, 0.0), (-1.0, 0, -1.8369701987210297e-16), (-0.866025403784439, 0, 0.49999999999999933),
(0.0, 0.0, 0.0), (-0.866025403784439, 0, 0.49999999999999933), (-0.5000000000000004, 0, 0.8660254037844384),
(0.0, 0.0, 0.0), (-0.5000000000000004, 0, 0.8660254037844384), (0.0, 0, 1.0),
)
class CustomGizmo():
# FIXME: highliting/selection doesnt work
def draw_very_custom_shape(self, ctx, custom_shape, select_id=None):
# similar to draw_custom_shape
shape, batch, shader = custom_shape
shader.bind()
if select_id is not None:
gpu.select.load_id(select_id)
else:
if self.is_highlight:
color = (*self.color_highlight, self.alpha_highlight)
else:
color = (*self.color, self.alpha)
shader.uniform_float('color', color)
shape.glenable()
shape.uniform_region(ctx)
# shader.uniform_float('modelMatrix', self.matrix_world)
with gpu.matrix.push_pop():
gpu.matrix.multiply_matrix(self.matrix_world)
batch.draw()
bgl.glDisable(bgl.GL_BLEND)
class OffsetHandle():
"""Handling mouse to offset gizmo from base along Z axis"""
# FIXME: works a bit weird for rotated objects
def invoke(self, ctx, event):
self.init_value = self.target_get_value('offset') / self.scale_value
coordz = self.project_mouse(ctx, event)
if coordz is None:
return {'CANCELLED'}
self.init_coordz = coordz
return {'RUNNING_MODAL'}
def modal(self, ctx, event, tweak):
coordz = self.project_mouse(ctx, event)
if coordz is None:
return {'CANCELLED'}
delta = coordz - self.init_coordz
if 'PRECISE' in tweak:
delta /= 10.0
value = max(0, self.init_value + delta)
value *= self.scale_value
# ctx.area.header_text_set(f"coords: {self.init_coordz} - {coordz}, delta: {delta}, value: {value}")
ctx.area.header_text_set(f"Depth: {value}")
self.target_set_value('offset', value)
return {'RUNNING_MODAL'}
def project_mouse(self, ctx, event):
"""Projecting mouse coords to local axis Z"""
# logic from source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c:gizmo_arrow_modal
mouse = Vector((event.mouse_region_x, event.mouse_region_y))
region = ctx.region
region3d = ctx.region_data
ray_orig = view3d_utils.region_2d_to_origin_3d(region, region3d, mouse)
ray_norm = view3d_utils.region_2d_to_vector_3d(region, region3d, mouse)
# 'arrow' origin and direction
base = Vector((0, 0, 0))
axis = Vector((0, 0, 1))
# projecttion of the arrow to a plane, perpendicular to view ray
axis_proj = axis - ray_norm * axis.dot(ray_norm)
# intersection of the axis with the plane through view origin perpendicular to the arrow projection
coords = geometry.intersect_line_plane(base, axis, ray_orig, axis_proj)
return coords.z
def exit(self, ctx, cancel):
if cancel:
self.target_set_value('offset', self.init_value)
else:
self.group.update(ctx)
class UglyDotGizmo(OffsetHandle, types.Gizmo):
"""three orthogonal circles"""
bl_idname = "BIM_GT_uglydot_3d"
bl_target_properties = (
{'id': 'offset', 'type': 'FLOAT', 'array_length': 1},
)
__slots__ = (
'scale_value',
'custom_shape',
'init_value',
'init_coordz',
)
def setup(self):
self.custom_shape = self.new_custom_shape(type='TRIS', verts=X3DISC)
def refresh(self):
offset = self.target_get_value('offset') / self.scale_value
self.matrix_offset.col[3][2] = offset # z-shift
def draw(self, ctx):
self.refresh()
self.draw_custom_shape(self.custom_shape)
def draw_select(self, ctx, select_id):
self.refresh()
self.draw_custom_shape(self.custom_shape, select_id=select_id)
class DotGizmo(CustomGizmo, OffsetHandle, types.Gizmo):
"""Single dot viewport-aligned"""
# FIXME: make it selectable
bl_idname = "BIM_GT_dot_2d"
bl_target_properties = (
{'id': 'offset', 'type': 'FLOAT', 'array_length': 1},
)
__slots__ = (
'scale_value',
'custom_shape',
)
def setup(self):
shader = DotsGizmoShader()
self.custom_shape = shader, shader.batch(pos=((0, 0, 0),)), shader.prog
self.use_draw_scale = False
def refresh(self):
offset = self.target_get_value('offset') / self.scale_value
self.matrix_offset.col[3][2] = offset # z-shifted
def draw(self, ctx):
self.refresh()
self.draw_very_custom_shape(ctx, self.custom_shape)
def draw_select(self, ctx, select_id):
self.refresh()
self.draw_very_custom_shape(ctx, self.custom_shape, select_id=select_id)
# doesn't get called
# def test_select(self, ctx, location):
# pass
class ExtrusionGuidesGizmo(CustomGizmo, types.Gizmo):
"""Extrusion guides
Noninteractive gizmo to indicate extrusion depth and planes.
Draws main segment and orthogonal cross at endpoints.
"""
bl_idname = "BIM_GT_extrusion_guides"
bl_target_properties = (
{'id': 'depth', 'type': 'FLOAT', 'array_length': 1},
)
__slots__ = (
'scale_value',
'custom_shape'
)
def setup(self):
shader = ExtrusionGuidesShader()
self.custom_shape = shader, shader.batch(pos=((0, 0, 0), (0, 0, 1))), shader.prog
self.use_draw_scale = False
def refresh(self):
depth = self.target_get_value('depth') / self.scale_value
self.matrix_offset.col[2][2] = depth # z-scaled
def draw(self, ctx):
self.refresh()
self.draw_very_custom_shape(ctx, self.custom_shape)
class DimensionLabelGizmo(types.Gizmo):
"""Text label for a dimension"""
# does not work properly, fonts are totally screwed up
bl_idname = "BIM_GT_dimension_label"
bl_target_properties = (
{'id': 'value', 'type': 'FLOAT', 'array_length': 1},
)
__slots__ = (
'text_label'
)
def setup(self):
pass
def refresh(self, ctx):
value = self.target_get_value('value')
self.matrix_offset.col[3][2] = value * .5
unit_system = ctx.scene.unit_settings.system
self.text_label = bpy.utils.units.to_string(unit_system, 'LENGTH', value, 3, split_unit=False)
def draw(self, ctx):
self.refresh(ctx)
self.draw_text(ctx)
def draw_text(self, ctx):
font_id = 0
font_size = 16
dpi = ctx.preferences.system.dpi
# pos = self.matrix_world @ Vector((0, 0, 0, 1))
# pos = Vector((0, 0, 0.5))
# region = ctx.region
# region3d = ctx.region_data
# pos = view3d_utils.location_3d_to_region_2d(region, region3d, pos)
# text = self.text_label
blf.size(font_id, font_size, dpi)
blf.position(font_id, 0, 0, 0)
blf.color(font_id, *self.color, self.alpha)
blf.draw(font_id, "ABC")
class ExtrusionWidget(types.GizmoGroup):
bl_idname = "bim.extrusion_widget"
bl_label = "Extrusion Gizmos"
bl_space_type = 'VIEW_3D'
bl_region_type = 'WINDOW'
bl_options = {'3D', 'PERSISTENT', 'SHOW_MODAL_ALL'}
# FIXME: use proper scale from ifc value to blender units
@classmethod
def poll(cls, ctx):
obj = ctx.object
return (obj and obj.type == 'MESH'
and obj.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth') is not None)
def setup(self, ctx):
target = ctx.object
prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth')
basis = target.matrix_world.normalized()
theme = ctx.preferences.themes[0].user_interface
gz = self.handle = self.gizmos.new('BIM_GT_uglydot_3d')
gz.matrix_basis = basis
gz.scale_basis = 0.1
gz.color = gz.color_highlight = tuple(theme.gizmo_primary)
gz.alpha = 0.5
gz.alpha_highlight = 1.0
gz.use_draw_modal = True
gz.target_set_prop('offset', prop, 'value')
gz.scale_value = 1000
gz = self.guides = self.gizmos.new('BIM_GT_extrusion_guides')
gz.matrix_basis = basis
gz.color = gz.color_highlight = tuple(theme.gizmo_secondary)
gz.alpha = gz.alpha_highlight = 0.5
gz.use_draw_modal = True
gz.target_set_prop('depth', prop, 'value')
gz.scale_value = 1000
# gz = self.label = self.gizmos.new('GIZMO_GT_dimension_label')
# gz.matrix_basis = basis
# gz.color = tuple(theme.gizmo_secondary)
# gz.alpha = 0.5
# gz.use_draw_modal = True
# gz.target_set_prop('value', target.demo, 'depth')
def refresh(self, ctx):
"""updating gizmos"""
target = ctx.object
basis = target.matrix_world.normalized()
self.handle.matrix_basis = basis
self.guides.matrix_basis = basis
def update(self, ctx):
"""updating object"""
bpy.ops.bim.update_parametric_representation()
# parameters disappear after update
# need to retrieve and rebind them again
bpy.ops.bim.get_representation_ifc_parameters()
target = ctx.object
prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth')
self.handle.target_set_prop('offset', prop, 'value')
self.guides.target_set_prop('depth', prop, 'value')
-260
View File
@@ -1,260 +0,0 @@
import bpy
import json
import addon_utils
import blenderbim.bim.decoration as decoration
import ifcopenshell.api.owner.settings
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data as AttributeData
def mode_callback(obj, data):
if (
obj.mode != "OBJECT"
or not obj.data
or not isinstance(obj.data, bpy.types.Mesh)
or not obj.data.BIMMeshProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring
):
return
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep":
IfcStore.edited_objs.add(obj.name)
def name_callback(obj, data):
# Blender material names are up to 63 UTF-8 bytes
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name or len(bytes(obj.name, "utf-8")) >= 63:
return
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
if not element.is_a("IfcRoot"):
return
element.Name = "/".join(obj.name.split("/")[1:])
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
def subscribe_to(object, data_path, callback):
subscribe_to = object.path_resolve(data_path, False)
bpy.msgbus.subscribe_rna(
key=subscribe_to,
owner=object,
args=(
object,
data_path,
),
notify=callback,
options={
"PERSISTENT",
},
)
def purge_module_data():
from blenderbim.bim import modules
for name in modules.keys():
try:
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
except AttributeError:
pass
@persistent
def loadIfcStore(scene):
IfcStore.file = None
IfcStore.schema = None
props = bpy.context.scene.BIMProperties
IfcStore.id_map = (
{int(k): bpy.data.objects.get(v) for k, v in json.loads(props.id_map).items()} if props.id_map else {}
)
IfcStore.guid_map = (
{k: bpy.data.objects.get(v) for k, v in json.loads(props.guid_map).items()} if props.id_map else {}
)
purge_module_data()
@persistent
def ensureIfcExported(scene):
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
# The invocation pops up a file select window.
# This is non-blocking, therefore the Blend file is saved before we export.
bpy.ops.export_ifc.bim("INVOKE_DEFAULT", should_force_resave=True)
@persistent
def storeIdMap(scene):
try:
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
except:
# Regenerate maps. Is there a better solution for this? It seems fragile.
file = IfcStore.get_file()
IfcStore.id_map = {
o.ifc_definition_id: o.name for o in bpy.data.objects if o.BIMObjectProperties.ifc_definition_id
}
IfcStore.guid_map = {
file.by_id(i).GlobalId: n for i, n in IfcStore.id_map.items() if file.by_id(i).is_a("IfcRoot")
}
# Then attempt to store it again
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
def get_application(ifc):
version = get_application_version()
for element in ifc.by_type("IfcApplication"):
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
return element
return ifc.create_entity(
"IfcApplication",
**{
"ApplicationDeveloper": create_application_organisation(ifc),
"Version": get_application_version(),
"ApplicationFullName": "BlenderBIM Add-on",
"ApplicationIdentifier": "BlenderBIM",
},
)
def get_application_version():
return ".".join(
[
str(x)
for x in [
addon.bl_info.get("version", (-1, -1, -1))
for addon in addon_utils.modules()
if addon.bl_info["name"] == "BlenderBIM"
][0]
]
)
def create_application_organisation(ifc):
return ifc.create_entity(
"IfcOrganization",
**{
"Name": "IfcOpenShell",
"Description": "IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.",
"Roles": [ifc.create_entity("IfcActorRole", **{"Role": "USERDEFINED", "UserDefinedRole": "CONTRIBUTOR"})],
"Addresses": [
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "WEBPAGE",
"Description": "The main webpage of the software collection.",
"WWWHomePageURL": "https://ifcopenshell.org",
},
),
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "WEBPAGE",
"Description": "The BlenderBIM Add-on webpage of the software collection.",
"WWWHomePageURL": "https://blenderbim.org",
},
),
ifc.create_entity(
"IfcTelecomAddress",
**{
"Purpose": "USERDEFINED",
"UserDefinedPurpose": "REPOSITORY",
"Description": "The source code repository of the software collection.",
"WWWHomePageURL": "https://github.com/IfcOpenShell/IfcOpenShell.git",
},
),
],
},
)
@persistent
def setDefaultProperties(scene):
ifcopenshell.api.owner.settings.get_person = (
lambda ifc : ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_person))
if bpy.context.scene.BIMOwnerProperties.user_person
else None
)
ifcopenshell.api.owner.settings.get_organisation = (
lambda ifc : ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_organisation))
if bpy.context.scene.BIMOwnerProperties.user_organisation
else None
)
ifcopenshell.api.owner.settings.get_application = get_application
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": False,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "FLAT",
"bpy.context.scene.display.shading.color_type": "SINGLE",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": False,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Shaded"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": True,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "STUDIO",
"bpy.context.scene.display.shading.color_type": "MATERIAL",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": True,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="2")
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
-411
View File
@@ -1,411 +0,0 @@
import math
from mathutils import geometry
from mathutils import Vector
import bpy
# TODO: Deprecate this in favour of ifcopenshell.util.unit
class SIUnitHelper:
prefixes = {
"EXA": 1e18,
"PETA": 1e15,
"TERA": 1e12,
"GIGA": 1e9,
"MEGA": 1e6,
"KILO": 1e3,
"HECTO": 1e2,
"DECA": 1e1,
"DECI": 1e-1,
"CENTI": 1e-2,
"MILLI": 1e-3,
"MICRO": 1e-6,
"NANO": 1e-9,
"PICO": 1e-12,
"FEMTO": 1e-15,
"ATTO": 1e-18,
}
unit_names = [
"AMPERE",
"BECQUEREL",
"CANDELA",
"COULOMB",
"CUBIC_METRE",
"DEGREE CELSIUS",
"FARAD",
"GRAM",
"GRAY",
"HENRY",
"HERTZ",
"JOULE",
"KELVIN",
"LUMEN",
"LUX",
"MOLE",
"NEWTON",
"OHM",
"PASCAL",
"RADIAN",
"SECOND",
"SIEMENS",
"SIEVERT",
"SQUARE METRE",
"METRE",
"STERADIAN",
"TESLA",
"VOLT",
"WATT",
"WEBER",
]
si_conversions = {
"inch": 0.0254,
"foot": 0.3048,
"yard": 0.914,
"mile": 1609,
"square inch": 0.0006452,
"square foot": 0.09290304,
"square yard": 0.83612736,
"acre": 4046.86,
"square mile": 2588881,
"cubic inch": 0.00001639,
"cubic foot": 0.02831684671168849,
"cubic yard": 0.7636,
"litre": 0.001,
"fluid ounce UK": 0.0000284130625,
"fluid ounce US": 0.00002957353,
"pint UK": 0.000568,
"pint US": 0.000473,
"gallon UK": 0.004546,
"gallon US": 0.003785,
"degree": math.pi / 180,
"ounce": 0.02835,
"pound": 0.454,
"ton UK": 1016.0469088,
"ton US": 907.18474,
"lbf": 4.4482216153,
"kip": 4448.2216153,
"psi": 6894.7572932,
"ksi": 6894757.2932,
"minute": 60,
"hour": 3600,
"day": 86400,
"btu": 1055.056,
}
@staticmethod
def get_prefix(text):
for prefix in SIUnitHelper.prefixes.keys():
if prefix in text.upper():
return prefix
@staticmethod
def get_prefix_multiplier(text):
if not text:
return 1
prefix = SIUnitHelper.get_prefix(text)
if prefix:
return SIUnitHelper.prefixes[prefix]
return 1
@staticmethod
def get_unit_name(text):
for name in SIUnitHelper.unit_names:
if name in text.upper().replace("METER", "METRE"):
return name
@staticmethod
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
"""Converts between length, area, and volume units
:param value: The numeric value you want to convert
:type value: float
:param from_prefix: A prefix from IfcSIPrefix. Can be None.
:type from_prefix: string
:param from_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type from_unit: string
:param to_prefix: A prefix from IfcSIPrefix. Can be None.
:type to_prefix: string
:param to_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type to_unit: string
"""
if from_unit in SIUnitHelper.si_conversions:
value *= SIUnitHelper.si_conversions[from_unit]
elif from_prefix:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if "SQUARE" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
elif "CUBIC" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if to_unit in SIUnitHelper.si_conversions:
return value * (1 / SIUnitHelper.si_conversions[to_unit])
elif to_prefix:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
if "SQUARE" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
elif "CUBIC" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
return value
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
# MeasureIt-ARCH is GPL-v3
# In the future I will need to rewrite this to allow the user to have custom
# settings for each annotation object, not read from Blender.
def format_distance(value, isArea=False, hide_units=True):
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
# Get Scene Unit Settings
scaleFactor = bpy.context.scene.unit_settings.scale_length
unit_system = bpy.context.scene.unit_settings.system
unit_length = bpy.context.scene.unit_settings.length_unit
toInches = 39.3700787401574887
inPerFoot = 11.999
if isArea:
toInches = 1550
inPerFoot = 143.999
value *= scaleFactor
# Imperial Formating
if unit_system == "IMPERIAL":
precision = bpy.context.scene.BIMProperties.imperial_precision
if precision == "NONE":
precision = 256
elif precision == "1":
precision = 1
elif "/" in precision:
precision = int(precision.split("/")[1])
base = int(precision)
decInches = value * toInches
# Seperate ft and inches
# Unless Inches are the specified Length Unit
if unit_length != "INCHES":
feet = math.floor(decInches / inPerFoot)
decInches -= feet * inPerFoot
else:
feet = 0
# Seperate Fractional Inches
inches = math.floor(decInches)
if inches != 0:
frac = round(base * (decInches - inches))
else:
frac = round(base * (decInches))
# Set proper numerator and denominator
if frac != base:
numcycles = int(math.log2(base))
for i in range(numcycles):
if frac % 2 == 0:
frac = int(frac / 2)
base = int(base / 2)
else:
break
else:
frac = 0
inches += 1
# Check values and compose string
if inches == 12:
feet += 1
inches = 0
if not isArea:
tx_dist = ""
if feet:
tx_dist += str(feet) + "'"
if feet and inches:
tx_dist += " - "
if inches:
tx_dist += str(inches)
if inches and frac:
tx_dist += " "
if frac:
tx_dist += str(frac) + "/" + str(base)
if inches or frac:
tx_dist += '"'
else:
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
# METRIC FORMATING
elif unit_system == "METRIC":
precision = bpy.context.scene.BIMProperties.metric_precision
if precision != 0:
value = precision * round(float(value) / precision)
# Meters
if unit_length == "METERS":
fmt = "%1.3f"
if hide_units is False:
fmt += " m"
tx_dist = fmt % value
# Centimeters
elif unit_length == "CENTIMETERS":
fmt = "%1.1f"
if hide_units is False:
fmt += " cm"
d_cm = value * (100)
tx_dist = fmt % d_cm
# Millimeters
elif unit_length == "MILLIMETERS":
fmt = "%1.0f"
if hide_units is False:
fmt += " mm"
d_mm = value * (1000)
tx_dist = fmt % d_mm
# Otherwise Use Adaptive Units
else:
if round(value, 2) >= 1.0:
fmt = "%1.3f"
if hide_units is False:
fmt += " m"
tx_dist = fmt % value
else:
if round(value, 2) >= 0.01:
fmt = "%1.1f"
if hide_units is False:
fmt += " cm"
d_cm = value * (100)
tx_dist = fmt % d_cm
else:
fmt = "%1.0f"
if hide_units is False:
fmt += " mm"
d_mm = value * (1000)
tx_dist = fmt % d_mm
if isArea:
tx_dist += s_code
else:
tx_dist = fmt % value
return tx_dist
def parse_diagram_scale(camera):
"""Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
else:
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
return float(numerator) / float(denominator)
def get_project_collection(scene):
"""Get main project collection"""
colls = [c for c in scene.collection.children if c.name.startswith('IfcProject')]
if len(colls) != 1:
raise RuntimeError("project collection missing or not unique")
return colls[0]
def get_active_drawing(scene):
"""Get active drawing collection and camera"""
props = scene.DocProperties
if props.active_drawing_index is None or len(props.drawings) == 0:
return None, None
try:
drawing = props.drawings[props.active_drawing_index]
return scene.collection.children['Views'].children[f"IfcGroup/{drawing.name}"], drawing.camera
except (KeyError, IndexError):
raise RuntimeError("missing drawing collection")
def ortho_view_frame(camera, margin=0.015):
"""Calculates 2d bounding box of camera view area.
Similar to `bpy.types.Camera.view_frame`
:arg camera: camera of drawing
:type camera: bpy.types.Camera + BIMCameraProperties
:arg margin: margins, in scene units
:type margin: float
:return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates
"""
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale
hwidth = size * .5
hheight = size * .5 * aspect
scale = parse_diagram_scale(camera)
xmarg = margin * scale
ymarg = margin * scale * aspect
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg, -camera.clip_start, -camera.clip_end)
def almost_zero(v):
return abs(v) < 1e-5
def clip_segment(bounds, segm):
"""Clipping line segment to bounds
:arg bounds: (xmin, xmax, ymin, ymax)
:arg segm: 2 vertices of the segment
:return: 2 new vertices of segment or None if segment outside the bounding box
"""
# LiangBarsky algorithm
xmin, xmax, ymin, ymax, _, _ = bounds
p1, p2 = segm
def clip_side(p, q):
if almost_zero(p): # ~= 0, parallel to the side
if q < 0:
return None # outside
else:
return 0, 1 # inside
t = q / p # the intersection point
if p < 0: # entering
return t, 1
else: # leaving
return 0, t
dlt = p2 - p1
tt = (
clip_side(-dlt.x, p1.x - xmin), # left
clip_side(+dlt.x, xmax - p1.x), # right
clip_side(-dlt.y, p1.y - ymin), # bottom
clip_side(+dlt.y, ymax - p1.y), # top
)
if None in tt:
return None
t1 = max(0, max(t[0] for t in tt))
t2 = min(1, min(t[1] for t in tt))
if t1 >= t2:
return None
p1c = p1 + dlt * t1
p2c = p1 + dlt * t2
return p1c, p2c
def elevate_segment(bounds, segm):
"""Elevate line xy-perpendicular segment vertically
:arg bounds: (xmin, xmax, ymin, ymax)
:arg segm: 2 vertices of the segment
:return: 2 new vertices of segment or None if segment outside the bounding box
"""
_, _, ymin, ymax, zmin, _ = bounds
p1, p2 = segm
dlt = p2 - p1
if not (almost_zero(dlt.x) and almost_zero(dlt.y)):
return None
x = p1.x
return [Vector((x, ymin, zmin)), Vector((x, ymax, zmin))]
-50
View File
@@ -1,50 +0,0 @@
import bpy
import ifcopenshell
import blenderbim.bim.handler
class IfcStore:
path = ""
file = None
schema = None
id_map = {}
guid_map = {}
edited_objs = set()
pset_template_path = ""
pset_template_file = None
@staticmethod
def get_file():
if IfcStore.file is None:
IfcStore.path = bpy.context.scene.BIMProperties.ifc_file
if IfcStore.path:
try:
IfcStore.file = ifcopenshell.open(IfcStore.path)
except:
IfcStore.file
return IfcStore.file
@staticmethod
def get_schema():
if IfcStore.file is None:
return
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
@staticmethod
def link_element(element, obj):
IfcStore.id_map[element.id()] = obj
if hasattr(element, "GlobalId"):
IfcStore.guid_map[element.GlobalId] = obj
obj.BIMObjectProperties.ifc_definition_id = element.id()
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
@staticmethod
def unlink_element(element, obj=None):
del IfcStore.id_map[element.id()]
if hasattr(element, "GlobalId"):
del IfcStore.guid_map[element.GlobalId]
if obj:
obj.BIMObjectProperties.ifc_definition_id = 0
File diff suppressed because it is too large Load Diff
@@ -1,18 +0,0 @@
import bpy
from . import ui, operator
classes = (
operator.AssignObject,
operator.EnableEditingAggregate,
operator.DisableEditingAggregate,
operator.AddAggregate,
ui.BIM_PT_aggregate,
)
def register():
pass
def unregister():
pass
@@ -1,92 +0,0 @@
import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.aggregate.data import Data
class AssignObject(bpy.types.Operator):
bl_idname = "bim.assign_object"
bl_label = "Assign Object"
relating_object: bpy.props.StringProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
relating_object = bpy.data.objects.get(self.relating_object)
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
return {"FINISHED"}
for related_object in related_objects:
oprops = related_object.BIMObjectProperties
product = self.file.by_id(oprops.ifc_definition_id)
ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
**{
"product": product,
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
},
)
bpy.ops.bim.edit_object_placement(obj=related_object.name)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
spatial_collection = bpy.data.collections.get(related_object.name)
relating_collection = bpy.data.collections.get(relating_object.name)
if spatial_collection:
self.remove_collection(bpy.context.scene.collection, spatial_collection)
for collection in bpy.data.collections:
if collection == relating_collection:
collection.children.link(spatial_collection)
continue
self.remove_collection(collection, spatial_collection)
else:
for collection in related_object.users_collection:
collection.objects.unlink(related_object)
relating_collection.objects.link(related_object)
return {"FINISHED"}
def remove_collection(self, parent, child):
try:
parent.children.unlink(child)
except:
pass
class EnableEditingAggregate(bpy.types.Operator):
bl_idname = "bim.enable_editing_aggregate"
bl_label = "Enable Editing Aggregate"
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.relating_object = None
bpy.context.active_object.BIMObjectProperties.is_editing_aggregate = True
return {"FINISHED"}
class DisableEditingAggregate(bpy.types.Operator):
bl_idname = "bim.disable_editing_aggregate"
bl_label = "Disable Editing Aggregate"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
obj.BIMObjectProperties.is_editing_aggregate = False
return {"FINISHED"}
class AddAggregate(bpy.types.Operator):
bl_idname = "bim.add_aggregate"
bl_label = "Add Aggregate"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
bpy.context.scene.collection.children.link(aggregate_collection)
aggregate = bpy.data.objects.new("Assembly", None)
aggregate_collection.objects.link(aggregate)
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class="IfcElementAssembly")
bpy.ops.bim.assign_object(related_object=obj.name, relating_object=aggregate.name)
return {"FINISHED"}
@@ -1,49 +0,0 @@
from bpy.types import Panel
from ifcopenshell.api.aggregate.data import Data
from blenderbim.bim.ifc import IfcStore
class BIM_PT_aggregate(Panel):
bl_label = "IFC Aggregates"
bl_idname = "BIM_PT_aggregate"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
return False
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if props.is_editing_aggregate:
row = self.layout.row(align=True)
row.prop(props, "relating_object", text="")
if props.relating_object:
row.operator("bim.assign_object", icon="CHECKMARK", text="").relating_object = props.relating_object.name
row.operator("bim.disable_editing_aggregate", icon="X", text="")
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
)
if name == "None/None":
name = "No Aggregate Found"
row.label(text=name)
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
row.operator("bim.add_aggregate", icon="ADD", text="")
@@ -1,22 +0,0 @@
import bpy
from . import ui, prop, operator
classes = (
operator.EnableEditingAttributes,
operator.DisableEditingAttributes,
operator.EditAttributes,
operator.GenerateGlobalId,
prop.BIMAttributeProperties,
ui.BIM_PT_object_attributes,
ui.BIM_PT_material_attributes,
)
def register():
bpy.types.Object.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
bpy.types.Material.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties)
def unregister():
del bpy.types.Object.BIMAttributeProperties
del bpy.types.Material.BIMAttributeProperties
@@ -1,125 +0,0 @@
import bpy
import json
import ifcopenshell
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
class EnableEditingAttributes(bpy.types.Operator):
bl_idname = "bim.enable_editing_attributes"
bl_label = "Enable Editing Attributes"
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
while len(props.attributes) > 0:
props.attributes.remove(0)
for attribute in Data.products[oprops.ifc_definition_id]:
new = props.attributes.add()
if attribute["type"] == "entity":
continue
new.name = attribute["name"]
new.is_null = attribute["is_null"]
if attribute["type"] == "string" or attribute["type"] == "list":
new.string_value = attribute["value"] or ""
elif attribute["type"] == "integer":
new.int_value = attribute["value"] or 0
elif attribute["type"] == "float":
new.float_value = attribute["value"] or 0.0
elif attribute["type"] == "enum":
new.enum_items = json.dumps(attribute["enum_items"])
if attribute["value"]:
new.enum_value = attribute["value"]
props.is_editing_attributes = True
return {"FINISHED"}
class DisableEditingAttributes(bpy.types.Operator):
bl_idname = "bim.disable_editing_attributes"
bl_label = "Disable Editing Attributes"
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
props = obj.BIMAttributeProperties
props.is_editing_attributes = False
return {"FINISHED"}
class EditAttributes(bpy.types.Operator):
bl_idname = "bim.edit_attributes"
bl_label = "Edit Attributes"
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
if self.obj_type == "Object":
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
attributes = {}
for attribute in Data.products[oprops.ifc_definition_id]:
blender_attribute = props.attributes.get(attribute["name"])
if not blender_attribute:
continue
if attribute["is_optional"] and blender_attribute.is_null:
attributes[attribute["name"]] = None
elif attribute["type"] == "string":
attributes[attribute["name"]] = blender_attribute.string_value
elif attribute["type"] == "list":
values = blender_attribute.string_value[1:-1].split(", ")
if attribute["list_type"] == "float":
values = [float(v) for v in values]
elif attribute["list_type"] == "integer":
values = [int(v) for v in values]
attributes[attribute["name"]] = values
elif attribute["type"] == "integer":
attributes[attribute["name"]] = blender_attribute.int_value
elif attribute["type"] == "float":
attributes[attribute["name"]] = blender_attribute.float_value
elif attribute["type"] == "enum":
attributes[attribute["name"]] = blender_attribute.enum_value
product = self.file.by_id(oprops.ifc_definition_id)
ifcopenshell.api.run(
"attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes}
)
if "Name" in attributes:
new_name = "{}/{}".format(product.is_a(), product.Name or "Unnamed")
collection = bpy.data.collections.get(obj.name)
if collection:
collection.name = new_name
obj.name = new_name
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
return {"FINISHED"}
class GenerateGlobalId(bpy.types.Operator):
bl_idname = "bim.generate_global_id"
bl_label = "Regenerate GlobalId"
def execute(self, context):
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
if index >= 0:
global_id = bpy.context.active_object.BIMAttributeProperties.attributes[index]
else:
global_id = bpy.context.active_object.BIMAttributeProperties.attributes.add()
global_id.name = "GlobalId"
global_id.data_type = "string"
global_id.string_value = ifcopenshell.guid.new()
return {"FINISHED"}
@@ -1,19 +0,0 @@
import bpy
import blenderbim.bim.schema # refactor
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
class BIMAttributeProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
@@ -1,102 +0,0 @@
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
def draw_ui(context, layout, obj_type):
obj = context.active_object if obj_type == "Object" else context.active_object.active_material
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
if props.is_editing_attributes:
row = layout.row(align=True)
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
op.obj_type = obj_type
op.obj = obj.name
op = row.operator("bim.disable_editing_attributes", icon="X", text="")
op.obj_type = obj_type
op.obj = obj.name
for attribute in Data.products[oprops.ifc_definition_id]:
if attribute["type"] == "entity":
continue
row = layout.row(align=True)
blender_attribute = props.attributes.get(attribute["name"])
if attribute["type"] == "string" or attribute["type"] == "list":
row.prop(blender_attribute, "string_value", text=attribute["name"])
elif attribute["type"] == "integer":
row.prop(blender_attribute, "int_value", text=attribute["name"])
elif attribute["type"] == "float":
row.prop(blender_attribute, "float_value", text=attribute["name"])
elif attribute["type"] == "enum":
row.prop(blender_attribute, "enum_value", text=attribute["name"])
if attribute["name"] == "GlobalId":
row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
if attribute["is_optional"]:
row.prop(
blender_attribute,
"is_null",
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
text="",
)
# TODO: reimplement, see #1222
# op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="")
# op.attribute_name = attribute.name
# op.attribute_value = attribute.string_value
else:
row = layout.row()
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
op.obj_type = obj_type
op.obj = obj.name
if "GlobalId" not in [a["name"] for a in Data.products[oprops.ifc_definition_id]]:
row = layout.row(align=True)
row.label(text="STEP ID")
row.label(text=str(oprops.ifc_definition_id))
for attribute in Data.products[oprops.ifc_definition_id]:
if attribute["value"] is None or attribute["type"] == "entity":
continue
row = layout.row(align=True)
row.label(text=attribute["name"])
row.label(text=str(attribute["value"]))
# TODO: reimplement, see #1222
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
# self.draw_addresses_ui()
class BIM_PT_object_attributes(Panel):
bl_label = "IFC Attributes"
bl_idname = "BIM_PT_object_attributes"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context):
draw_ui(context, self.layout, "Object")
class BIM_PT_material_attributes(Panel):
bl_label = "IFC Attributes"
bl_idname = "BIM_PT_material_attributes"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
@classmethod
def poll(cls, context):
try:
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
except:
return False
def draw(self, context):
draw_ui(context, self.layout, "Material")
@@ -1,18 +0,0 @@
import bpy
from . import ui, prop, operator
classes = (
prop.AuginProperties,
ui.BIM_PT_augin,
operator.AuginLogin,
operator.AuginCreateNewModel,
operator.AuginReset,
)
def register():
bpy.types.Scene.AuginProperties = bpy.props.PointerProperty(type=prop.AuginProperties)
def unregister():
del bpy.types.Scene.AuginProperties
@@ -1,133 +0,0 @@
import os
import bpy
import json
import string
import random
import shutil
import requests
import tempfile
import datetime
import addon_utils
class AuginLogin(bpy.types.Operator):
bl_idname = "bim.augin_login"
bl_label = "Login to Augin"
def execute(self, context):
props = bpy.context.scene.AuginProperties
url = "https://server.auge.pro.br/API/v3/augin_rest.php/user_login"
payload = {"email": props.username, "password": props.password}
response = requests.request("POST", url, data=payload)
result = response.json()
if result["success_feedback"]["success"]:
props.token = result["token"]
else:
self.report({"ERROR"}, "Login failed :(")
return {"FINISHED"}
class AuginReset(bpy.types.Operator):
bl_idname = "bim.augin_reset"
bl_label = "Upload Another Project"
def execute(self, context):
props = bpy.context.scene.AuginProperties
props.is_success = False
return {"FINISHED"}
class AuginCreateNewModel(bpy.types.Operator):
bl_idname = "bim.augin_create_new_model"
bl_label = "Create New Augin Model"
def execute(self, context):
import boto3
from botocore.config import Config
props = bpy.context.scene.AuginProperties
# Create project
url = "https://server.auge.pro.br/API/v3/augin_rest.php/new_model"
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
props.project_filename = "BBM{}{}".format(
datetime.datetime.now().strftime("%Y%m%d%H%M%S"), "".join(random.choice(chars) for _ in range(6))
)
addon_version = ".".join(
[
str(x)
for x in [
addon.bl_info.get("version", (-1, -1, -1))
for addon in addon_utils.modules()
if addon.bl_info["name"] == "BlenderBIM"
][0]
]
)
payload = {
"user_token": props.token,
"partner_token": "Mmf2zpxdgrkHaUNRm6Cv7GLJdBAeb7Rn",
"project_name": props.project_name,
"model_filename": props.project_filename + ".ifc",
"thumb_filename": props.project_filename + ".png",
"app_name": "BlenderBIM",
"sw_version": bpy.app.version_string,
"plugin_version": addon_version,
}
response = requests.request("POST", url, data=payload)
result = response.json()
# Upload to S3
my_config = Config(
region_name=result["s3_region"],
)
client = boto3.client(
"s3",
aws_access_key_id=result["s3_key"],
aws_secret_access_key=result["s3_secret"],
aws_session_token=result["s3_token"],
config=my_config,
)
# Generate thumb
old_file_format = context.scene.render.image_settings.file_format
old_filepath = context.scene.render.filepath
cam = bpy.data.cameras.new("Camera")
cam_obj = bpy.data.objects.new("Camera", cam)
context.scene.collection.objects.link(cam_obj)
context.scene.camera = cam_obj
tmpdir = tempfile.mkdtemp()
thumb_path = os.path.join(tmpdir, "thumb.png")
context.scene.render.image_settings.file_format = "PNG"
context.scene.render.filepath = thumb_path
bpy.ops.render.opengl(write_still=True)
context.scene.render.image_settings.file_format = old_file_format
context.scene.render.filepath = old_filepath
client.upload_file(bpy.context.scene.BIMProperties.ifc_file, result["s3_bucket"], result["model_path"])
client.upload_file(thumb_path, result["s3_bucket"], result["thumb_path"])
# Notify done
url = "https://server.auge.pro.br/API/v3/augin_rest.php/files_uploaded"
payload = {
"user_token": props.token,
"ifc_filesize": os.path.getsize(bpy.context.scene.BIMProperties.ifc_file),
"model_filesize": os.path.getsize(bpy.context.scene.BIMProperties.ifc_file),
"thumb_filesize": os.path.getsize(thumb_path),
"model_upload_path": result["model_path"],
"thumb_upload_path": result["thumb_path"],
"model_id": result["model_id"],
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.request("POST", url, headers=headers, data=payload)
shutil.rmtree(tmpdir)
props.is_success = True
return {"FINISHED"}
@@ -1,11 +0,0 @@
import bpy.props
import bpy.types
class AuginProperties(bpy.types.PropertyGroup):
username: bpy.props.StringProperty(name="Username")
password: bpy.props.StringProperty(name="Password")
token: bpy.props.StringProperty(name="Token")
project_name: bpy.props.StringProperty(name="Project Name")
project_filename: bpy.props.StringProperty(name="IFC Filename")
is_success: bpy.props.BoolProperty(name="Is Successful Upload", default=False)
@@ -1,45 +0,0 @@
import bpy.types
class BIM_PT_augin(bpy.types.Panel):
bl_label = "Augin"
bl_idname = "BIM_PT_augin"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
props = scene.AuginProperties
if not props.token:
row = layout.row()
row.prop(props, "username")
row = layout.row()
row.prop(props, "password")
row = layout.row()
row.operator("bim.augin_login")
return
row = layout.row()
row.label(text="Logged in as " + props.username)
if not bpy.context.scene.BIMProperties.ifc_file:
row = layout.row()
row.label(text="No IFC Found")
return
if props.is_success:
row = layout.row()
row.label(text="Upload Successful")
row = layout.row()
row.operator("bim.augin_reset")
else:
row = layout.row()
row.prop(props, "project_name")
row = layout.row()
row.operator("bim.augin_create_new_model")
@@ -1,59 +0,0 @@
import bpy
from . import ui, prop, operator
classes = (
operator.NewBcfProject,
operator.LoadBcfProject,
operator.LoadBcfTopics,
operator.LoadBcfTopic,
operator.LoadBcfComments,
operator.EditBcfProjectName,
operator.EditBcfAuthor,
operator.EditBcfTopicName,
operator.EditBcfTopic,
operator.SaveBcfProject,
operator.AddBcfTopic,
operator.AddBcfHeaderFile,
operator.AddBcfBimSnippet,
operator.AddBcfReferenceLink,
operator.AddBcfDocumentReference,
operator.AddBcfLabel,
operator.AddBcfRelatedTopic,
operator.ViewBcfTopic,
operator.RemoveBcfComment,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfReferenceLink,
operator.RemoveBcfDocumentReference,
operator.RemoveBcfRelatedTopic,
operator.EditBcfReferenceLinks,
operator.EditBcfLabels,
operator.RemoveBcfLabel,
operator.EditBcfComment,
operator.AddBcfComment,
operator.ActivateBcfViewpoint,
operator.AddBcfViewpoint,
operator.RemoveBcfViewpoint,
operator.RemoveBcfFile,
operator.OpenBcfReferenceLink,
operator.SelectBcfHeaderFile,
operator.SelectBcfBimSnippetReference,
operator.SelectBcfDocumentReference,
prop.BcfReferenceLink,
prop.BcfLabel,
prop.BcfBimSnippet,
prop.BcfDocumentReference,
prop.BcfComment,
prop.BcfTopic,
prop.BCFProperties,
ui.BIM_PT_bcf,
ui.BIM_PT_bcf_metadata,
ui.BIM_PT_bcf_comments,
)
def register():
bpy.types.Scene.BCFProperties = bpy.props.PointerProperty(type=prop.BCFProperties)
def unregister():
del bpy.types.Scene.BCFProperties
@@ -1,11 +0,0 @@
import bcf
import bcf.bcfxml
class BcfStore:
bcfxml = None
@staticmethod
def get_bcfxml():
if not BcfStore.bcfxml:
BcfStore.bcfxml = bcf.bcfxml.BcfXml()
return BcfStore.bcfxml
@@ -1,880 +0,0 @@
import os
import bpy
import bcf
from . import bcfstore
from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry
class NewBcfProject(bpy.types.Operator):
bl_idname = "bim.new_bcf_project"
bl_label = "New BCF Project"
def execute(self, context):
bpy.context.scene.BCFProperties.is_loaded = False
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.new_project()
bpy.ops.bim.load_bcf_project()
bpy.context.scene.BCFProperties.is_loaded = True
return {"FINISHED"}
class LoadBcfProject(bpy.types.Operator):
bl_idname = "bim.load_bcf_project"
bl_label = "Load BCF Project"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BCFProperties.is_loaded = False
bcfxml = bcfstore.BcfStore.get_bcfxml()
if self.filepath:
bcfxml.get_project(self.filepath)
bpy.context.scene.BCFProperties.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
bpy.context.scene.BCFProperties.is_loaded = True
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class LoadBcfTopics(bpy.types.Operator):
bl_idname = "bim.load_bcf_topics"
bl_label = "Load BCF Topics"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_topics()
while len(bpy.context.scene.BCFProperties.topics) > 0:
bpy.context.scene.BCFProperties.topics.remove(0)
index = 0
for topic_guid in bcfxml.topics.keys():
new = bpy.context.scene.BCFProperties.topics.add()
bpy.ops.bim.load_bcf_topic(topic_guid = topic_guid, topic_index = index)
index += 1
return {"FINISHED"}
class LoadBcfTopic(bpy.types.Operator):
bl_idname = "bim.load_bcf_topic"
bl_label = "Load BCF Topics"
topic_guid: bpy.props.StringProperty()
topic_index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.get_topic(self.topic_guid)
new = bpy.context.scene.BCFProperties.topics[self.topic_index]
data_map = {
"name": topic.guid,
"title": topic.title,
"type": topic.topic_type,
"status": topic.topic_status,
"priority": topic.priority,
"stage": topic.stage,
"creation_date": topic.creation_date,
"creation_author": topic.creation_author,
"modified_date": topic.modified_date,
"modified_author": topic.modified_author,
"assigned_to": topic.assigned_to,
"due_date": topic.due_date,
"description": topic.description
}
for key, value in data_map.items():
if value is not None:
setattr(new, key, str(value))
while len(new.reference_links) > 0:
new.reference_links.remove(0)
for reference_link in topic.reference_links:
new2 = new.reference_links.add()
new2.name = reference_link
while len(new.labels) > 0:
new.labels.remove(0)
for label in topic.labels:
new2 = new.labels.add()
new2.name = label
if topic.bim_snippet:
data_map = {
"type": topic.bim_snippet.snippet_type,
"is_external": topic.bim_snippet.is_external,
"reference": topic.bim_snippet.reference,
"schema": topic.bim_snippet.reference_schema
}
for key, value in data_map.items():
if value is not None:
setattr(new.bim_snippet, key, value)
while len(new.document_references) > 0:
new.document_references.remove(0)
for doc in topic.document_references:
new2 = new.document_references.add()
data_map = {
"reference": doc.referenced_document,
"description": doc.description,
"guid": doc.guid,
"is_external": doc.is_external
}
for key, value in data_map.items():
if value is not None:
setattr(new2, key, value)
while len(new.related_topics) > 0:
new.related_topics.remove(0)
for related_topic in topic.related_topics:
new2 = new.related_topics.add()
new2.name = related_topic.guid
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
return {"FINISHED"}
class LoadBcfComments(bpy.types.Operator):
bl_idname = "bim.load_bcf_comments"
bl_label = "Load BCF Comments"
topic_guid: bpy.props.StringProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_comments(self.topic_guid)
blender_topic = bpy.context.scene.BCFProperties.topics.get(self.topic_guid)
while len(blender_topic.comments) > 0:
blender_topic.comments.remove(0)
for comment in bcfxml.topics[self.topic_guid].comments.values():
new = blender_topic.comments.add()
data_map = {
"name": comment.guid,
"comment": comment.comment,
"viewpoint": comment.viewpoint.guid if comment.viewpoint else None,
"date": comment.date,
"author": comment.author,
"modified_date": comment.modified_date,
"modified_author": comment.modified_author,
}
for key, value in data_map.items():
if value is not None:
setattr(new, key, str(value))
return {"FINISHED"}
class EditBcfProjectName(bpy.types.Operator):
bl_idname = "bim.edit_bcf_project_name"
bl_label = "Edit BCF Project Name"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.project.name = bpy.context.scene.BCFProperties.name
bcfxml.edit_project()
return {"FINISHED"}
class EditBcfAuthor(bpy.types.Operator):
bl_idname = "bim.edit_bcf_author"
bl_label = "Edit BCF Author"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.author = bpy.context.scene.BCFProperties.author
return {"FINISHED"}
class EditBcfTopicName(bpy.types.Operator):
bl_idname = "bim.edit_bcf_topic_name"
bl_label = "Edit BCF Topic Name"
def execute(self, context):
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic.title = blender_topic.title
bcfxml.edit_topic(topic)
return {"FINISHED"}
class EditBcfTopic(bpy.types.Operator):
bl_idname = "bim.edit_bcf_topic"
bl_label = "Edit BCF Topic"
def execute(self, context):
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic.title = blender_topic.title or None
topic.priority = blender_topic.priority or None
topic.due_date = blender_topic.due_date or None
topic.assigned_to = blender_topic.assigned_to or None
topic.stage = blender_topic.stage or None
topic.description = blender_topic.description or None
topic.topic_status = blender_topic.status or None
topic.topic_type = blender_topic.type or None
bcfxml.edit_topic(topic)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
return {"FINISHED"}
class SaveBcfProject(bpy.types.Operator):
bl_idname = "bim.save_bcf_project"
bl_label = "Save BCF Project"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.save_project(self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class AddBcfTopic(bpy.types.Operator):
bl_idname = "bim.add_bcf_topic"
bl_label = "Add BCF Topic"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.add_topic()
new = bpy.context.scene.BCFProperties.topics.add()
new.name = "New Topic"
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
class AddBcfBimSnippet(bpy.types.Operator):
bl_idname = "bim.add_bcf_bim_snippet"
bl_label = "Add BCF BIM Snippet"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bim_snippet = bcf.data.BimSnippet()
bim_snippet.reference = blender_topic.bim_snippet_reference
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
bim_snippet.snippet_type = blender_topic.bim_snippet_type
bcfxml.add_bim_snippet(topic, bim_snippet)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
return {"FINISHED"}
class AddBcfRelatedTopic(bpy.types.Operator):
bl_idname = "bim.add_bcf_related_topic"
bl_label = "Add BCF Related Topic"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
related_topic = None
for topic in bcfxml.topics.values():
if topic.title == blender_topic.related_topic:
related_topic = bcf.data.RelatedTopic()
related_topic.guid = topic.guid
break
if not related_topic:
return {"FINISHED"}
topic = bcfxml.topics[blender_topic.name]
topic.related_topics.append(related_topic)
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
return {"FINISHED"}
class AddBcfHeaderFile(bpy.types.Operator):
bl_idname = "bim.add_bcf_header_file"
bl_label = "Add BCF Header File"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
header_file = bcf.data.HeaderFile()
header_file.reference = blender_topic.file_reference
if not os.path.exists(header_file.reference):
header_file.filename = header_file.reference
if len(blender_topic.file_ifc_project) == 22:
header_file.ifc_project = blender_topic.file_ifc_project
if len(blender_topic.file_ifc_spatial_structure_element) == 22:
header_file.ifc_spatial_structure_element = blender_topic.file_ifc_spatial_structure_element
bcfxml.add_file(topic, header_file)
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
return {"FINISHED"}
class ViewBcfTopic(bpy.types.Operator):
bl_idname = "bim.view_bcf_topic"
bl_label = "Get BCF Topic"
topic_guid: bpy.props.StringProperty()
def execute(self, context):
for index, topic in enumerate(bpy.context.scene.BCFProperties.topics):
if topic.guid.lower() == self.topic_guid.lower():
bpy.context.scene.BCFProperties.active_topic_index = index
return {"FINISHED"}
class AddBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.add_bcf_viewpoint"
bl_label = "Add BCF Viewpoint"
def execute(self, context):
if not bpy.context.scene.camera:
return {"FINISHED"}
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.data.Viewpoint()
if bpy.context.scene.camera.data.type == "ORTHO":
camera = bcf.data.OrthogonalCamera()
camera.view_to_world_scale = bpy.context.scene.camera.data.ortho_scale
viewpoint.orthogonal_camera = camera
elif bpy.context.scene.camera.data.type == "PERSP":
camera = bcf.data.PerspectiveCamera()
camera.field_of_view = degrees(bpy.context.scene.camera.data.angle)
viewpoint.perspective_camera = camera
camera.camera_view_point.x = bpy.context.scene.camera.location.x
camera.camera_view_point.y = bpy.context.scene.camera.location.y
camera.camera_view_point.z = bpy.context.scene.camera.location.z
direction = bpy.context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
camera.camera_direction.x = direction.x
camera.camera_direction.y = direction.y
camera.camera_direction.z = direction.z
up = bpy.context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
camera.camera_up_vector.x = up.x
camera.camera_up_vector.y = up.y
camera.camera_up_vector.z = up.z
old_file_format = bpy.context.scene.render.image_settings.file_format
bpy.context.scene.render.image_settings.file_format = "PNG"
old_filepath = bpy.context.scene.render.filepath
bpy.context.scene.render.filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True)
viewpoint.snapshot = bpy.context.scene.render.filepath
bcfxml.add_viewpoint(topic, viewpoint)
bpy.context.scene.render.filepath = old_filepath
bpy.context.scene.render.image_settings.file_format = old_file_format
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
return {"FINISHED"}
class RemoveBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.remove_bcf_viewpoint"
bl_label = "Remove BCF Viewpoint"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
viewpoint_guid = blender_topic.viewpoints
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_viewpoint(viewpoint_guid, topic)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
return {"FINISHED"}
class RemoveBcfFile(bpy.types.Operator):
bl_idname = "bim.remove_bcf_file"
bl_label = "Remove BCF File"
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_file(topic, self.index)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
return {"FINISHED"}
class AddBcfReferenceLink(bpy.types.Operator):
bl_idname = "bim.add_bcf_reference_link"
bl_label = "Add BCF Reference Link"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.reference_link:
return {"FINISHED"}
topic.reference_links.append(blender_topic.reference_link)
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
blender_topic.reference_link = ""
return {"FINISHED"}
class AddBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.add_bcf_document_reference"
bl_label = "Add BCF Document Reference"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.document_reference:
return {"FINISHED"}
document_reference = bcf.data.DocumentReference()
document_reference.referenced_document = blender_topic.document_reference
document_reference.description = blender_topic.document_reference_description or None
bcfxml.add_document_reference(topic, document_reference)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
blender_topic.document_reference = ""
blender_topic.document_reference_description = ""
return {"FINISHED"}
class AddBcfLabel(bpy.types.Operator):
bl_idname = "bim.add_bcf_label"
bl_label = "Add BCF Label"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.label:
return {"FINISHED"}
new = blender_topic.labels.add()
new.name = blender_topic.label
topic.labels.append(blender_topic.label)
bcfxml.edit_topic(topic)
blender_topic.label = ""
return {"FINISHED"}
class EditBcfReferenceLinks(bpy.types.Operator):
bl_idname = "bim.edit_bcf_reference_links"
bl_label = "Edit BCF Reference Link"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
for index, reference_link in enumerate(topic.reference_links):
if reference_link == blender_topic.reference_links[index].name:
continue
topic.reference_links[index] = blender_topic.reference_links[index].name
bcfxml.edit_topic(topic)
return {"FINISHED"}
class EditBcfLabels(bpy.types.Operator):
bl_idname = "bim.edit_bcf_labels"
bl_label = "Edit BCF Labels"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
for index, label in enumerate(blender_topic.labels):
if label.name == topic.labels[index]:
continue
topic.labels[index] = blender_topic.labels[index].name
bcfxml.edit_topic(topic)
return {"FINISHED"}
class RemoveBcfReferenceLink(bpy.types.Operator):
bl_idname = "bim.remove_bcf_reference_link"
bl_label = "Remove BCF Reference Link"
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
del topic.reference_links[self.index]
bcfxml.edit_topic(topic)
blender_topic.reference_links.remove(self.index)
return {"FINISHED"}
class RemoveBcfLabel(bpy.types.Operator):
bl_idname = "bim.remove_bcf_label"
bl_label = "Remove BCF Label"
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
del topic.labels[self.index]
bcfxml.edit_topic(topic)
blender_topic.labels.remove(self.index)
return {"FINISHED"}
class RemoveBcfBimSnippet(bpy.types.Operator):
bl_idname = "bim.remove_bcf_bim_snippet"
bl_label = "Remove BCF BIM Snippet"
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_bim_snippet(topic)
blender_topic.bim_snippet.schema = ""
blender_topic.bim_snippet.reference = ""
blender_topic.bim_snippet.type = ""
return {"FINISHED"}
class RemoveBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.remove_bcf_document_reference"
bl_label = "Remove BCF Document Reference"
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_document_reference(topic, self.index)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
return {"FINISHED"}
class RemoveBcfRelatedTopic(bpy.types.Operator):
bl_idname = "bim.remove_bcf_related_topic"
bl_label = "Remove BCF Related Topic"
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
del topic.related_topics[self.index]
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
return {"FINISHED"}
class RemoveBcfComment(bpy.types.Operator):
bl_idname = "bim.remove_bcf_comment"
bl_label = "Remove BCF Comment"
comment_guid: bpy.props.StringProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_comment(self.comment_guid, topic)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
return {"FINISHED"}
class EditBcfComment(bpy.types.Operator):
bl_idname = "bim.edit_bcf_comment"
bl_label = "Edit BCF Comment"
comment_guid: bpy.props.StringProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_comment = blender_topic.comments.get(self.comment_guid)
topic = bcfxml.topics[blender_topic.name]
comment = topic.comments[self.comment_guid]
comment.comment = blender_comment.comment
bcfxml.edit_comment(comment, topic)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
return {"FINISHED"}
class AddBcfComment(bpy.types.Operator):
bl_idname = "bim.add_bcf_comment"
bl_label = "Add BCF Comment"
comment_guid: bpy.props.StringProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.comment:
return {"FINISHED"}
comment = bcf.data.Comment()
comment.comment = blender_topic.comment
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
comment.viewpoint = bcf.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
print(bcfxml.filepath)
return {"FINISHED"}
class ActivateBcfViewpoint(bpy.types.Operator):
bl_idname = "bim.activate_bcf_viewpoint"
bl_label = "Activate BCF Viewpoint"
def execute(self, context):
self.file = IfcStore.get_file()
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = bpy.context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
if not topic.viewpoints:
return {"FINISHED"}
viewpoint_guid = blender_topic.viewpoints
viewpoint = topic.viewpoints[viewpoint_guid]
obj = bpy.data.objects.get("Viewpoint")
if not obj:
obj = bpy.data.objects.new("Viewpoint", bpy.data.cameras.new("Viewpoint"))
bpy.context.scene.collection.objects.link(obj)
bpy.context.scene.camera = obj
cam_width = bpy.context.scene.render.resolution_x
cam_height = bpy.context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
if viewpoint.snapshot:
obj.data.show_background_images = True
while len(obj.data.background_images) > 0:
obj.data.background_images.remove(obj.data.background_images[0])
background = obj.data.background_images.new()
background.image = bpy.data.images.load(
os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot)
)
src_width = background.image.size[0]
src_height = background.image.size[1]
src_aspect = src_width / src_height
if src_aspect > cam_aspect:
background.frame_method = "FIT"
else:
background.frame_method = "CROP"
background.display_depth = "FRONT"
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
if viewpoint.orthogonal_camera:
camera = viewpoint.orthogonal_camera
obj.data.type = "ORTHO"
obj.data.ortho_scale = viewpoint.orthogonal_camera.view_to_world_scale
elif viewpoint.perspective_camera:
camera = viewpoint.perspective_camera
obj.data.type = "PERSP"
if cam_aspect >= 1:
obj.data.angle = radians(camera.field_of_view)
else:
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2)))
self.set_viewpoint_components(viewpoint)
gp = bpy.data.grease_pencils.get("BCF")
if gp:
bpy.data.grease_pencils.remove(gp)
if viewpoint.lines:
self.draw_lines(viewpoint)
self.delete_clipping_planes()
if viewpoint.clipping_planes:
self.create_clipping_planes(viewpoint)
self.delete_bitmaps()
if viewpoint.bitmaps:
self.create_bitmaps(bcfxml, viewpoint, topic)
z_axis = Vector((-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)).normalized()
y_axis = Vector((camera.camera_up_vector.x, camera.camera_up_vector.y, camera.camera_up_vector.z)).normalized()
x_axis = y_axis.cross(z_axis).normalized()
rotation = Matrix((x_axis, y_axis, z_axis))
rotation.invert()
location = Vector((camera.camera_view_point.x, camera.camera_view_point.y, camera.camera_view_point.z))
obj.matrix_world = rotation.to_4x4()
obj.location = location
return {"FINISHED"}
def set_viewpoint_components(self, viewpoint):
if not viewpoint.components:
return
selected_global_ids = [s.ifc_guid for s in viewpoint.components.selection]
exception_global_ids = [v.ifc_guid for v in viewpoint.components.visibility.exceptions]
global_id_colours = {}
for coloring in viewpoint.components.coloring:
for component in coloring.components:
global_id_colours.setdefault(component.ifc_guid, coloring.color)
for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
global_id = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
is_visible = viewpoint.components.visibility.default_visibility
if global_id in exception_global_ids:
is_visible = not is_visible
if not is_visible:
obj.hide_set(True)
continue
if "IfcSpace" in obj.name:
if viewpoint.components.view_setup_hints:
is_visible = viewpoint.components.view_setup_hints.spaces_visible
else:
is_visible = False
elif "IfcOpeningElement" in obj.name:
if viewpoint.components.view_setup_hints:
is_visible = viewpoint.components.view_setup_hints.openings_visible
else:
is_visible = False
obj.hide_set(not is_visible)
if not is_visible:
continue
obj.select_set(global_id in selected_global_ids)
if global_id in global_id_colours:
obj.color = self.hex_to_rgb(global_id_colours[global_id])
def draw_lines(self, viewpoint):
gp = bpy.data.grease_pencils.new("BCF")
scene = bpy.context.scene
scene.grease_pencil = gp
scene.frame_set(1)
layer = gp.layers.new("BCF Annotation", set_active=True)
layer.thickness = 3
layer.color = (1, 0, 0)
frame = layer.frames.new(1)
stroke = frame.strokes.new()
stroke.display_mode = "3DSPACE"
stroke.points.add(len(viewpoint.lines) * 2)
coords = []
for l in viewpoint.lines:
coords.extend([l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z])
stroke.points.foreach_set("co", coords)
def create_clipping_planes(self, viewpoint):
n = 0
for plane in viewpoint.clipping_planes:
bpy.ops.bim.add_section_plane()
if n == 0:
obj = bpy.data.objects["Section"]
else:
obj = bpy.data.objects["Section.{:03d}".format(n)]
obj.location = (plane.location.x, plane.location.y, plane.location.z)
obj.rotation_mode = "QUATERNION"
obj.rotation_quaternion = Vector((plane.direction.x, plane.direction.y, plane.direction.z)).to_track_quat(
"Z", "Y"
)
n += 1
def delete_clipping_planes(self):
collection = bpy.data.collections.get("Sections")
if not collection:
return
for section in collection.objects:
bpy.context.view_layer.objects.active = section
bpy.ops.bim.remove_section_plane()
def delete_bitmaps(self):
collection = bpy.data.collections.get("Bitmaps")
if not collection:
collection = bpy.data.collections.new("Bitmaps")
bpy.context.scene.collection.children.link(collection)
for bitmap in collection.objects:
bpy.data.objects.remove(bitmap)
def create_bitmaps(self, bcfxml, viewpoint, topic):
collection = bpy.data.collections.get("Bitmaps")
if not collection:
collection = bpy.data.collections.new("Bitmaps")
for bitmap in viewpoint.bitmaps:
obj = bpy.data.objects.new("Bitmap", None)
obj.empty_display_type = "IMAGE"
image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
src_width = image.size[0]
src_height = image.size[1]
if src_height > src_width:
obj.empty_display_size = bitmap.height
else:
obj.empty_display_size = bitmap.height * (src_width / src_height)
obj.data = image
y = Vector((bitmap.up.x, bitmap.up.y, bitmap.up.z))
z = Vector((bitmap.normal.x, bitmap.normal.y, bitmap.normal.z))
x = y.cross(z)
obj.matrix_world = Matrix(
[[x[0], y[0], z[0], 0], [x[1], y[1], z[1], 0], [x[2], y[2], z[2], 0], [0, 0, 0, 1]]
)
obj.location = (bitmap.location.x, bitmap.location.y, bitmap.location.z)
collection.objects.link(obj)
def hex_to_rgb(self, value):
value = value.lstrip("#")
lv = len(value)
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):
bl_idname = "bim.open_bcf_reference_link"
bl_label = "Open BCF Reference Link"
index: bpy.props.IntProperty()
def execute(self, context):
webbrowser.open(bpy.context.scene.BCFProperties.topic_links[self.index].name)
return {"FINISHED"}
class SelectBcfHeaderFile(bpy.types.Operator):
bl_idname = "bim.select_bcf_header_file"
bl_label = "Select BCF Header File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
if self.filepath:
props = bpy.context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.file_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectBcfBimSnippetReference(bpy.types.Operator):
bl_idname = "bim.select_bcf_bim_snippet_reference"
bl_label = "Select BCF BIM Snippet Reference"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
if self.filepath:
props = bpy.context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.bim_snippet_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectBcfDocumentReference(bpy.types.Operator):
bl_idname = "bim.select_bcf_document_reference"
bl_label = "Select BCF Document Reference"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
if self.filepath:
props = bpy.context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.document_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
@@ -1,155 +0,0 @@
import bpy
from . import bcfstore
from blenderbim.bim.prop import StrProperty
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
bcfviewpoints_enum = None
def updateBcfReferenceLink(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_reference_links()
def updateBcfLabel(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_labels()
def updateBcfProjectName(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_project_name()
def updateBcfAuthor(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_author()
def updateBcfTopicName(self, context):
if bpy.context.scene.BCFProperties.is_loaded:
bpy.ops.bim.edit_bcf_topic_name()
def updateBcfTopicIsEditable(self, context):
if bpy.context.scene.BCFProperties.is_loaded and not self.is_editable:
bpy.ops.bim.edit_bcf_topic()
def updateBcfCommentIsEditable(self, context):
if bpy.context.scene.BCFProperties.is_loaded and not self.is_editable:
bpy.ops.bim.edit_bcf_comment(comment_guid = self.name)
def refreshBcfTopic(self, context):
global bcfviewpoints_enum
bcfviewpoints_enum = None
props = bpy.context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = props.topics[props.active_topic_index]
header = bcfxml.get_header(topic.name)
getBcfViewpoints(self, context)
class BcfReferenceLink(PropertyGroup):
name: StringProperty(name="Name", update=updateBcfReferenceLink)
class BcfLabel(PropertyGroup):
name: StringProperty(name="Name", update=updateBcfLabel)
def getBcfViewpoints(self, context):
global bcfviewpoints_enum
if bcfviewpoints_enum is None:
bcfviewpoints_enum = []
props = bpy.context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = props.topics[props.active_topic_index]
viewpoints = bcfxml.get_viewpoints(topic.name)
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints.keys())])
return bcfviewpoints_enum
class BcfBimSnippet(PropertyGroup):
schema: StringProperty(name="Schema")
reference: StringProperty(name="Reference")
type: StringProperty(name="Type")
is_external: BoolProperty(name="Is External")
class BcfDocumentReference(PropertyGroup):
reference: StringProperty(name="Reference")
description: StringProperty(name="Description")
guid: StringProperty(name="GUID")
is_external: BoolProperty(name="Is External")
class BcfComment(PropertyGroup):
name: StringProperty(name="GUID")
date: StringProperty(name="Date")
author: StringProperty(name="Author")
comment: StringProperty(name="Comment")
viewpoint: StringProperty(name="Viewpoint")
modified_date: StringProperty(name="Modified Date")
modified_author: StringProperty(name="Modified Author")
is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfCommentIsEditable)
class BcfTopic(PropertyGroup):
name: StringProperty(name="GUID")
title: StringProperty(default="", name="Title", update=updateBcfTopicName)
type: StringProperty(default="", name="Type")
status: StringProperty(default="", name="Status")
priority: StringProperty(default="", name="Priority")
stage: StringProperty(default="", name="Stage")
creation_date: StringProperty(default="", name="Date")
creation_author: StringProperty(default="", name="Author")
modified_date: StringProperty(default="", name="Modified Date")
modified_author: StringProperty(default="", name="Modified By")
assigned_to: StringProperty(default="", name="Assigned To")
due_date: StringProperty(default="", name="Due Date")
description: StringProperty(default="", name="Description")
viewpoints: EnumProperty(items=getBcfViewpoints, name="BCF Viewpoints")
files: CollectionProperty(name="Files", type=StrProperty)
file_reference: StringProperty(default="", name="Reference")
file_ifc_project: StringProperty(default="", name="IFC Project")
file_ifc_spatial_structure_element: StringProperty(default="", name="IFC Spatial Structure Element")
reference_links: CollectionProperty(name="Reference Links", type=BcfReferenceLink)
reference_link: StringProperty(default="", name="Reference Link")
labels: CollectionProperty(name="Labels", type=BcfLabel)
label: StringProperty(default="", name="Label")
bim_snippet: PointerProperty(type=BcfBimSnippet)
bim_snippet_type: StringProperty(default="", name="Type")
bim_snippet_reference: StringProperty(default="", name="Reference")
bim_snippet_schema: StringProperty(default="", name="Schema")
document_references: CollectionProperty(name="Document References", type=BcfDocumentReference)
document_reference: StringProperty(default="", name="Referenced Document")
document_reference_description: StringProperty(default="", name="Description")
related_topics: CollectionProperty(name="Related Topics", type=StrProperty)
related_topic: StringProperty(default="", name="Related Topic")
comments: CollectionProperty(name="Comments", type=BcfComment)
is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfTopicIsEditable)
comment: StringProperty(default="", name="Comment")
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
class BCFProperties(PropertyGroup):
is_loaded: BoolProperty(name="Is Loaded", default=False)
comment_text_width: IntProperty(name="Comment Text Width", default=40)
name: StringProperty(default="", name="Project Name", update=updateBcfProjectName)
author: StringProperty(default="john@doe.com", name="Author Email", update=updateBcfAuthor)
topics: CollectionProperty(name="BCF Topics", type=BcfTopic)
active_topic_index: IntProperty(name="Active BCF Topic Index", update=refreshBcfTopic)
@@ -1,283 +0,0 @@
import os
import bpy
from . import bcfstore
from bpy.types import Panel
class BIM_PT_bcf(Panel):
bl_label = "BCF Project"
bl_idname = "BIM_PT_bcf"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = bpy.context.scene.BCFProperties
row = layout.row(align=True)
row.operator("bim.new_bcf_project", text="New Project")
row.operator("bim.load_bcf_project", text="Load Project")
if not props.is_loaded:
return
row.operator("bim.save_bcf_project", text="Save Project")
row = layout.row()
row.prop(props, "name")
row = layout.row()
row.prop(props, "author")
props = bpy.context.scene.BCFProperties
row = layout.row()
row.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
col = row.column(align=True)
col.operator("bim.add_bcf_topic", icon="ADD", text="")
if props.active_topic_index < len(props.topics):
topic = props.topics[props.active_topic_index]
col.prop(topic, "is_editable", icon="CHECKMARK" if topic.is_editable else "GREASEPENCIL", icon_only=True)
if props.active_topic_index < len(props.topics):
topic = props.topics[props.active_topic_index]
row = layout.row()
row.enabled = topic.is_editable
row.prop(topic, "description", text="")
row = layout.row()
row.prop(topic, "viewpoints")
row.operator("bim.activate_bcf_viewpoint", icon="SCENE", text="")
row.operator("bim.add_bcf_viewpoint", icon="ADD", text="")
row.operator("bim.remove_bcf_viewpoint", icon="X", text="")
col = layout.column(align=True)
if topic.type:
col.prop(topic, "type", emboss=topic.is_editable)
if topic.status:
col.prop(topic, "status", emboss=topic.is_editable)
if topic.priority:
col.prop(topic, "priority", emboss=topic.is_editable)
if topic.stage:
col.prop(topic, "stage", emboss=topic.is_editable)
if topic.assigned_to:
col.prop(topic, "assigned_to", emboss=topic.is_editable)
if topic.due_date:
col.prop(topic, "due_date", emboss=topic.is_editable)
col = layout.column(align=True)
if topic.modified_date:
col.prop(topic, "modified_date", emboss=False)
col.prop(topic, "modified_author", emboss=False)
else:
col.prop(topic, "creation_date", emboss=False)
col.prop(topic, "creation_author", emboss=False)
class BIM_PT_bcf_metadata(Panel):
bl_label = "BCF Metadata"
bl_idname = "BIM_PT_bcf_metadata"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_bcf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = bpy.context.scene.BCFProperties
if props.active_topic_index >= len(props.topics):
layout.label(text="No BCF project is loaded")
return
topic = props.topics[props.active_topic_index]
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcf_topic = bcfxml.topics[topic.name]
layout.label(text="Header Files:")
if bcf_topic.header:
for index, f in enumerate(bcf_topic.header.files):
box = self.layout.box()
row = box.row(align=True)
row.label(text=f.filename, icon="FILE_BLANK")
if f.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = f.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, f.reference)
row.operator("bim.remove_bcf_file", icon="X", text="").index = index
row = box.row()
row.label(text="Date")
row.label(text=f.date)
if f.ifc_project:
row = box.row()
row.label(text="IFC Project")
row.label(text=f.ifc_project)
if f.ifc_spatial_structure_element:
row = box.row()
row.label(text="IFC Spatial Structure Element")
row.label(text=f.ifc_spatial_structure_element)
row = layout.row(align=True)
row.prop(topic, "file_reference")
row.operator("bim.select_bcf_header_file", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "file_ifc_project")
row = layout.row()
row.prop(topic, "file_ifc_spatial_structure_element")
row = layout.row()
row.operator("bim.add_bcf_header_file")
layout.label(text="Reference Links:")
for index, link in enumerate(topic.reference_links):
row = layout.row(align=True)
row.prop(link, "name")
row.operator("bim.open_uri", icon="URL", text="").uri = link.name
row.operator("bim.remove_bcf_reference_link", icon="X", text="").index = index
row = layout.row()
row.prop(topic, "reference_link")
row = layout.row()
row.operator("bim.add_bcf_reference_link")
layout.label(text="Labels:")
for index, label in enumerate(topic.labels):
row = layout.row(align=True)
row.prop(label, "name", text="")
row.operator("bim.remove_bcf_label", icon="X", text="").index = index
row = layout.row()
row.prop(topic, "label")
row = layout.row()
row.operator("bim.add_bcf_label")
layout.label(text="BIM Snippet:")
if topic.bim_snippet.schema:
row = layout.row(align=True)
row.prop(topic.bim_snippet, "type", emboss=False)
if topic.bim_snippet.schema:
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.schema
row = layout.row(align=True)
row.prop(topic.bim_snippet, "reference", emboss=False)
if topic.bim_snippet.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, topic.bim_snippet.reference)
row.operator("bim.remove_bcf_bim_snippet", icon="X", text="")
else:
row = layout.row(align=True)
row.prop(topic, "bim_snippet_reference")
row.operator("bim.select_bcf_bim_snippet_reference", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "bim_snippet_type")
row = layout.row()
row.prop(topic, "bim_snippet_schema")
row = layout.row()
row.operator("bim.add_bcf_bim_snippet")
layout.label(text="Document References:")
for index, doc in enumerate(topic.document_references):
box = self.layout.box()
row = box.row(align=True)
row.prop(doc, "reference")
if doc.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = doc.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, doc.reference)
row.operator("bim.remove_bcf_document_reference", icon="X", text="").index = index
row = box.row(align=True)
row.prop(doc, "description")
row = layout.row(align=True)
row.prop(topic, "document_reference")
row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "document_reference_description")
row = layout.row()
row.operator("bim.add_bcf_document_reference")
layout.label(text="Related Topics:")
for index, related_topic in enumerate(topic.related_topics):
row = layout.row(align=True)
row.operator("bim.view_bcf_topic", text=bcfxml.topics[related_topic.name.lower()].title).topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index
row = layout.row()
row.prop(topic, "related_topic")
row = layout.row()
row.operator("bim.add_bcf_related_topic")
class BIM_PT_bcf_comments(Panel):
bl_label = "BCF Comments"
bl_idname = "BIM_PT_bcf_comments"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_bcf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = bpy.context.scene.BCFProperties
if props.active_topic_index >= len(props.topics):
layout.label(text="No BCF project is loaded")
return
row = layout.row()
row.prop(props, "comment_text_width")
topic = props.topics[props.active_topic_index]
for comment in topic.comments:
box = self.layout.box()
author_text = "{} ({})".format(comment.author, comment.date)
if comment.modified_author:
author_text = "*{} ({})".format(comment.modified_author, comment.modified_date)
row = box.row(align=True)
row.label(text=author_text, icon="WORDWRAP_ON")
row.prop(
comment, "is_editable", icon="CHECKMARK" if comment.is_editable else "GREASEPENCIL", icon_only=True
)
row.operator("bim.remove_bcf_comment", icon="X", text="").comment_guid = comment.name
if comment.is_editable:
row = box.row()
row.prop(comment, "comment", text="")
else:
col = box.column(align=True)
col.scale_y = 0.8
words = comment.comment.split()
while words:
total_line_chars = 0
line_words = []
while words and total_line_chars < props.comment_text_width:
word = words.pop(0)
line_words.append(word)
total_line_chars += len(word) + 1 # 1 is for the space
col.label(text=" ".join(line_words))
row = layout.row()
row.prop(topic, "comment")
row = layout.row()
row.prop(topic, "has_related_viewpoint")
row = layout.row()
row.operator("bim.add_bcf_comment")
@@ -1,24 +0,0 @@
import bpy
from . import ui, prop, operator
classes = (
operator.ExecuteBIMTester,
operator.BIMTesterPurge,
operator.SelectFeature,
operator.SelectSteps,
operator.SelectBIMTesterIfcFile,
operator.RejectElement,
operator.ApproveClass,
operator.RejectClass,
operator.SelectAudited,
prop.BimTesterProperties,
ui.BIM_PT_qa,
)
def register():
bpy.types.Scene.BimTesterProperties = bpy.props.PointerProperty(type=prop.BimTesterProperties)
def unregister():
del bpy.types.Scene.BimTesterProperties
@@ -1,233 +0,0 @@
import os
import bpy
import tempfile
import webbrowser
import ifcopenshell
try:
import bimtester
import bimtester.run
import bimtester.reports
except:
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
from pathlib import Path
from itertools import cycle
from blenderbim.bim.ifc import IfcStore
class ExecuteBIMTester(bpy.types.Operator):
bl_idname = "bim.execute_bim_tester"
bl_label = "Execute BIMTester"
def execute(self, context):
props = context.scene.BimTesterProperties
with tempfile.TemporaryDirectory() as dirpath:
report = os.path.join(dirpath, "{}.html".format(props.feature))
args = {
"action": "run",
"advanced_arguments": "",
"console": False,
"feature": props.feature,
"ifc": props.ifc_file,
"path": "",
"report": report,
"schema_file": "",
"schema_name": "",
"lang": "en",
"steps": props.steps,
}
use_stored_ifc = props.should_load_from_memory and IfcStore.get_file()
runner = bimtester.run.TestRunner(args["ifc"], ifc=IfcStore.get_file() if use_stored_ifc else None)
report_json = runner.run(args)
bimtester.reports.ReportGenerator().generate(report_json, args["report"])
webbrowser.open("file://" + report)
return {"FINISHED"}
class BIMTesterPurge(bpy.types.Operator):
bl_idname = "bim.bim_tester_purge"
bl_label = "Purge Tests"
def execute(self, context):
filename = os.path.join(
bpy.context.scene.BimTesterProperties.features_dir,
bpy.context.scene.BimTesterProperties.features_file + ".feature",
)
cwd = os.getcwd()
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
bimtester.clean.TestPurger().purge()
os.chdir(cwd)
return {"FINISHED"}
class SelectFeature(bpy.types.Operator):
bl_idname = "bim.select_feature"
bl_label = "Select Feature / IDS"
filename_ext = ".feature"
filter_glob: bpy.props.StringProperty(default="*.feature;*.xml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BimTesterProperties.feature = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectSteps(bpy.types.Operator):
bl_idname = "bim.select_steps"
bl_label = "Select Steps"
filename_ext = ".py"
filter_glob: bpy.props.StringProperty(default="*.py", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BimTesterProperties.steps = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectBIMTesterIfcFile(bpy.types.Operator):
bl_idname = "bim.select_bimtester_ifc_file"
bl_label = "Select BIMTester IFC File"
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
context.scene.BimTesterProperties.ifc_file = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class RejectElement(bpy.types.Operator):
bl_idname = "bim.reject_element"
bl_label = "Reject Element"
def execute(self, context):
lines = []
self.file = IfcStore.get_file()
for obj in bpy.context.selected_objects:
lines.append(
" * The element {} should not exist because {}".format(
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
bpy.context.scene.BimTesterProperties.qa_reject_element_reason,
)
)
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class ApproveClass(bpy.types.Operator):
bl_idname = "bim.approve_class"
bl_label = "Approve Class"
def execute(self, context):
lines = []
self.file = IfcStore.get_file()
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
lines.append(" * The element {} is an {}".format(element.GlobalId, element.is_a()))
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class RejectClass(bpy.types.Operator):
bl_idname = "bim.reject_class"
bl_label = "Reject Class"
def execute(self, context):
lines = []
self.file = IfcStore.get_file()
for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
lines.append(
" * The element {} is an {}".format(
self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId,
bpy.context.scene.BimTesterProperties.audit_ifc_class,
)
)
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class SelectAudited(bpy.types.Operator):
bl_idname = "bim.select_audited"
bl_label = "Select Audited"
def execute(self, context):
audited_global_ids = []
self.file = IfcStore.get_file()
for filename in Path(bpy.context.scene.BimTesterProperties.features_dir).glob("*.feature"):
with open(filename, "r") as feature_file:
lines = feature_file.readlines()
for line in lines:
words = line.strip().split()
for word in words:
if self.is_a_global_id(word):
audited_global_ids.append(word)
for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
if self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId in audited_global_ids:
obj.select_set(True)
return {"FINISHED"}
def is_a_global_id(self, word):
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
class QAHelper:
@classmethod
def append_to_scenario(cls, lines):
filename = os.path.join(
bpy.context.scene.BimTesterProperties.features_dir,
bpy.context.scene.BimTesterProperties.features_file + ".feature",
)
if os.path.exists(filename + "~"):
os.remove(filename + "~")
os.rename(filename, filename + "~")
with open(filename, "w") as destination:
with open(filename + "~", "r") as source:
is_in_scenario = False
for source_line in source:
if (
"Scenario: " in source_line
and bpy.context.scene.BimTesterProperties.scenario == source_line.strip()[len("Scenario: ") :]
):
is_in_scenario = True
elif is_in_scenario:
for line in lines:
destination.write(line + "\n")
is_in_scenario = False
destination.write(source_line)
os.remove(filename + "~")
colour_list = [
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
(0.699, 0.876, 0.54, 1),
(0.199, 0.629, 0.174, 1),
(0.983, 0.605, 0.602, 1),
(0.89, 0.101, 0.112, 1),
(0.989, 0.751, 0.427, 1),
(0.986, 0.497, 0.1, 1),
(0.792, 0.699, 0.839, 1),
(0.414, 0.239, 0.603, 1),
(0.993, 0.999, 0.6, 1),
(0.693, 0.349, 0.157, 1),
]
@@ -1,49 +0,0 @@
import os
from pathlib import Path
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.module.root.prop import getIfcClasses
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
scenarios_enum = []
classes_enum = []
def getScenarios(self, context):
global scenarios_enum
if len(scenarios_enum) < 1:
scenarios_enum.clear()
if context.scene.BimTesterProperties.feature != "":
with open(context.scene.BimTesterProperties.feature, "r") as feature_file:
lines = feature_file.readlines()
for line in lines:
if "Scenario:" in line:
s = line.strip()[len("Scenario: ") :]
scenarios_enum.append((s, s, ""))
return scenarios_enum
def refreshScenarios(self, context):
global scenarios_enum
scenarios_enum.clear()
getScenarios(self, context)
class BimTesterProperties(PropertyGroup):
feature: StringProperty(default="", name="Feature / IDS", update=refreshScenarios)
steps: StringProperty(default="", name="Custom Steps")
ifc_file: StringProperty(default="", name="IFC File")
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
scenario: EnumProperty(items=getScenarios, name="Scenario")
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
@@ -1,62 +0,0 @@
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
class BIM_PT_qa(Panel):
bl_label = "BIMTester"
bl_idname = "BIM_PT_qa"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
self.layout.use_property_split = True
props = context.scene.BimTesterProperties
if IfcStore.get_file():
row = self.layout.row()
row.prop(props, "should_load_from_memory")
if not IfcStore.get_file() or not props.should_load_from_memory:
row = self.layout.row(align=True)
row.prop(props, "ifc_file")
row.operator("bim.select_bimtester_ifc_file", icon="FILE_FOLDER", text="")
row = self.layout.row(align=True)
row.prop(props, "feature")
row.operator("bim.select_feature", icon="FILE_FOLDER", text="")
row = self.layout.row(align=True)
row.prop(props, "steps")
row.operator("bim.select_steps", icon="FILE_FOLDER", text="")
has_ifc_file = (IfcStore.get_file() and props.should_load_from_memory) or (
props.ifc_file and not props.should_load_from_memory
)
row = self.layout.row(align=True)
row.operator("bim.execute_bim_tester")
row.operator("bim.bim_tester_purge")
if props.feature and has_ifc_file:
self.layout.label(text="Scenario Authoring:")
row = self.layout.row(align=True)
row.prop(props, "scenario")
row = self.layout.row()
row.prop(props, "qa_reject_element_reason")
row = self.layout.row()
row.operator("bim.reject_element")
row = self.layout.row()
row.prop(props, "audit_ifc_class")
row = self.layout.row(align=True)
row.operator("bim.approve_class")
row.operator("bim.reject_class")
row = self.layout.row()
row.operator("bim.select_audited")
@@ -1,38 +0,0 @@
import bpy
from . import ui, prop, operator
classes = (
operator.ExportClashSets,
operator.ImportClashSets,
operator.AddClashSet,
operator.RemoveClashSet,
operator.AddClashSource,
operator.RemoveClashSource,
operator.SelectClashSource,
operator.ExecuteIfcClash,
operator.SelectIfcClashResults,
operator.SelectClashResults,
operator.SelectSmartGroupedClashesPath,
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
operator.SetBlenderClashSetA,
operator.SetBlenderClashSetB,
operator.ExecuteBlenderClash,
prop.ClashSource,
prop.ClashSet,
prop.SmartClashGroup,
prop.BIMClashProperties,
ui.BIM_PT_ifcclash,
ui.BIM_PT_clash_manager,
ui.BIM_UL_clash_sets,
ui.BIM_UL_smart_groups,
)
def register():
bpy.types.Scene.BIMClashProperties = bpy.props.PointerProperty(type=prop.BIMClashProperties)
def unregister():
del bpy.types.Scene.BIMClashProperties
@@ -1,456 +0,0 @@
import os
import bpy
import json
import bmesh
import logging
import numpy as np
from mathutils import Matrix
from math import radians
class ExportClashSets(bpy.types.Operator):
bl_idname = "bim.export_clash_sets"
bl_label = "Export Clash Sets"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
clash_sets = []
for clash_set in bpy.context.scene.BIMClashProperties.clash_sets:
self.a = []
self.b = []
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
clash_source = {"file": data.name}
if data.selector:
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b})
with open(self.filepath, "w") as destination:
destination.write(json.dumps(clash_sets, indent=4))
return {"FINISHED"}
class ImportClashSets(bpy.types.Operator):
bl_idname = "bim.import_clash_sets"
bl_label = "Import Clash Sets"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
with open(self.filepath) as f:
clash_sets = json.load(f)
for clash_set in clash_sets:
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
new.name = clash_set["name"]
new.tolerance = clash_set["tolerance"]
for clash_source in clash_set["a"]:
new_source = new.a.add()
new_source.name = clash_source["file"]
if "selector" in clash_source:
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
if clash_set["b"]:
for clash_source in clash_set["b"]:
new_source = new.b.add()
new_source.name = clash_source["file"]
if "selector" in clash_source:
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
return {"FINISHED"}
class AddClashSet(bpy.types.Operator):
bl_idname = "bim.add_clash_set"
bl_label = "Add Clash Set"
def execute(self, context):
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
new.name = "New Clash Set"
new.tolerance = 0.01
return {"FINISHED"}
class RemoveClashSet(bpy.types.Operator):
bl_idname = "bim.remove_clash_set"
bl_label = "Remove Clash Set"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMClashProperties.clash_sets.remove(self.index)
return {"FINISHED"}
class AddClashSource(bpy.types.Operator):
bl_idname = "bim.add_clash_source"
bl_label = "Add Clash Source"
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
source = getattr(clash_set, self.group).add()
return {"FINISHED"}
class RemoveClashSource(bpy.types.Operator):
bl_idname = "bim.remove_clash_source"
bl_label = "Remove Clash Source"
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
getattr(clash_set, self.group).remove(self.index)
return {"FINISHED"}
class SelectClashSource(bpy.types.Operator):
bl_idname = "bim.select_clash_source"
bl_label = "Select Clash Source"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMClashProperties.clash_sets[bpy.context.scene.BIMClashProperties.active_clash_set_index]
getattr(clash_set, self.group)[self.index].name = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectClashResults(bpy.types.Operator):
bl_idname = "bim.select_clash_results"
bl_label = "Select Clash Results"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMClashProperties.clash_results_path = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class SelectSmartGroupedClashesPath(bpy.types.Operator):
bl_idname = "bim.select_smart_grouped_clashes_path"
bl_label = "Select Smart-Grouped Clashes Path"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ExecuteIfcClash(bpy.types.Operator):
bl_idname = "bim.execute_ifc_clash"
bl_label = "Execute IFC Clash"
filename_ext = ".bcf"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
if ".json" not in bpy.data.filepath:
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
import ifcclash
settings = ifcclash.IfcClashSettings()
if ".json" not in self.filepath:
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
settings.output = self.filepath
settings.logger = logging.getLogger("Clash")
settings.logger.setLevel(logging.DEBUG)
ifc_clasher = ifcclash.IfcClasher(settings)
if bpy.context.scene.BIMClashProperties.should_create_clash_snapshots:
def get_viewpoint_snapshot(self, viewpoint, mat):
camera = bpy.data.objects.get("IFC Clash Camera")
if not camera:
camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera"))
bpy.context.scene.collection.objects.link(camera)
camera.matrix_world = Matrix(mat)
bpy.context.scene.camera = camera
camera.data.angle = radians(60)
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
area.spaces[0].shading.show_xray = True
bpy.context.scene.render.resolution_x = 480
bpy.context.scene.render.resolution_y = 270
bpy.context.scene.render.image_settings.file_format = "PNG"
bpy.context.scene.render.filepath = os.path.join(
bpy.context.scene.BIMProperties.data_dir, "snapshot.png"
)
bpy.ops.render.opengl(write_still=True)
return bpy.context.scene.render.filepath
ifcclash.IfcClasher.get_viewpoint_snapshot = get_viewpoint_snapshot
ifc_clasher.clash_sets = []
for clash_set in bpy.context.scene.BIMClashProperties.clash_sets:
self.a = []
self.b = []
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
clash_source = {"file": data.name}
if data.selector:
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
ifc_clasher.clash_sets.append(
{"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b}
)
ifc_clasher.clash()
ifc_clasher.export()
return {"FINISHED"}
class SelectIfcClashResults(bpy.types.Operator):
bl_idname = "bim.select_ifc_clash_results"
bl_label = "Select IFC Clash Results"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
def execute(self, context):
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
with open(self.filepath) as f:
clash_sets = json.load(f)
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
bpy.context.scene.BIMClashProperties.active_clash_set_index
].name
global_ids = []
for clash_set in clash_sets:
if clash_set["name"] != clash_set_name:
continue
if not "clashes" in clash_set.keys():
self.report({"WARNING"}, "No clashes found for the selected Clash Set.")
return {"CANCELLED"}
for clash in clash_set["clashes"].values():
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if element.GlobalId in global_ids:
obj.select_set(True)
return {"FINISHED"}
class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
import ifcclash
settings = ifcclash.IfcClashSettings()
self.filepath = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.clash_results_path, ".json")
settings.output = self.filepath
settings.logger = logging.getLogger("Clash")
settings.logger.setLevel(logging.DEBUG)
ifc_clasher = ifcclash.IfcClasher(settings)
with open(self.filepath) as f:
clash_sets = json.load(f)
# execute the smart grouping
save_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
smart_grouped_clashes = ifc_clasher.smart_group_clashes(
clash_sets, bpy.context.scene.BIMClashProperties.smart_clash_grouping_max_distance
)
# save smart_groups to json
with open(save_path, "w") as f:
f.write(json.dumps(smart_grouped_clashes))
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
bpy.context.scene.BIMClashProperties.active_clash_set_index
].name
# Reset the list of smart_clash_groups for the UI
bpy.context.scene.BIMClashProperties.smart_clash_groups.clear()
for clash_set, smart_groups in smart_grouped_clashes.items():
# Only select the clashes that correspond to the actively selected IFC Clash Set
if clash_set != clash_set_name:
continue
else:
for smart_group, global_id_pairs in smart_groups[0].items():
new_group = bpy.context.scene.BIMClashProperties.smart_clash_groups.add()
new_group.number = f"{smart_group}"
for pair in global_id_pairs:
for id in pair:
new_global_id = new_group.global_ids.add()
new_global_id.name = id
return {"FINISHED"}
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
bl_idname = "bim.load_smart_groups_for_active_clash_set"
bl_label = "Load Smart Groups for Active Clash Set"
def execute(self, context):
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
clash_set_name = bpy.context.scene.BIMClashProperties.clash_sets[
bpy.context.scene.BIMClashProperties.active_clash_set_index
].name
with open(smart_groups_path) as f:
smart_grouped_clashes = json.load(f)
# Reset the list of smart_clash_groups for the UI
bpy.context.scene.BIMClashProperties.smart_clash_groups.clear()
for clash_set, smart_groups in smart_grouped_clashes.items():
# Only select the clashes that correspond to the actively selected IFC Clash Set
if clash_set != clash_set_name:
continue
else:
for smart_group, global_id_pairs in smart_groups[0].items():
new_group = bpy.context.scene.BIMClashProperties.smart_clash_groups.add()
new_group.number = f"{smart_group}"
for pair in global_id_pairs:
for id in pair:
new_global_id = new_group.global_ids.add()
new_global_id.name = id
return {"FINISHED"}
class SelectSmartGroup(bpy.types.Operator):
bl_idname = "bim.select_smart_group"
bl_label = "Select Smart Group"
def execute(self, context):
# Select smart group in view
selected_smart_group = bpy.context.scene.BIMClashProperties.smart_clash_groups[
bpy.context.scene.BIMCLashProperties.active_smart_group_index
]
# print(selected_smart_group.number)
for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
for id in selected_smart_group.global_ids:
# print("Id: ", id)
# print("Global id: ", element.GlobalId)
if element.GlobalId in id.name:
# print("object match: ", global_id)
obj.select_set(True)
return {"FINISHED"}
class BlenderClasher:
def process_clash_set(self):
import collision
a_cm = collision.CollisionManager()
b_cm = collision.CollisionManager()
self.add_to_cm(a_cm, bpy.context.scene.BIMClashProperties.blender_clash_set_a)
self.add_to_cm(b_cm, bpy.context.scene.BIMClashProperties.blender_clash_set_b)
results = a_cm.in_collision_other(b_cm, return_data=True)
if not results[0]:
print("No clashes")
return
for contact in results[1]:
if contact.raw.penetration_depth < 0.01:
continue
print("-----")
print(contact.names)
print(contact.raw.normal)
print(contact.raw.pos)
def add_to_cm(self, cm, object_names):
import ifcclash
for object_name in object_names:
name = object_name.name
obj = bpy.data.objects[name]
triangulated_mesh = self.triangulate_mesh(obj)
mesh = ifcclash.Mesh()
mesh.vertices = np.array([tuple(obj.matrix_world @ v.co) for v in triangulated_mesh.vertices])
mesh.faces = np.array([tuple(p.vertices) for p in triangulated_mesh.polygons])
cm.add_object(name, mesh)
def triangulate_mesh(self, obj):
mesh = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
del bm
return mesh
class SetBlenderClashSetA(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_a"
bl_label = "Set Blender Clash Set A"
def execute(self, context):
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_a) > 0:
bpy.context.scene.BIMClashProperties.blender_clash_set_a.remove(0)
for obj in bpy.context.selected_objects:
new = bpy.context.scene.BIMClashProperties.blender_clash_set_a.add()
new.name = obj.name
return {"FINISHED"}
class SetBlenderClashSetB(bpy.types.Operator):
bl_idname = "bim.set_blender_clash_set_b"
bl_label = "Set Blender Clash Set B"
def execute(self, context):
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_b) > 0:
bpy.context.scene.BIMClashProperties.blender_clash_set_b.remove(0)
for obj in bpy.context.selected_objects:
new = bpy.context.scene.BIMClashProperties.blender_clash_set_b.add()
new.name = obj.name
return {"FINISHED"}
class ExecuteBlenderClash(bpy.types.Operator):
bl_idname = "bim.execute_blender_clash"
bl_label = "Execute Blender Clash"
def execute(self, context):
blender_clasher = BlenderClasher()
blender_clasher.process_clash_set()
return {"FINISHED"}

Some files were not shown because too many files have changed in this diff Show More