Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba00526e1b | |||
| e744ba404e | |||
| eb39eeb30b | |||
| 286e9d183a | |||
| d4e4ca3a2a | |||
| f7adeb9cc1 | |||
| 607f7bd015 | |||
| bd00d3287e | |||
| f674493843 | |||
| dbc60bedd6 | |||
| f7f949b37b | |||
| 31ca1ac1b5 | |||
| 99d83fce42 | |||
| 4158424c25 | |||
| 1d8616883c | |||
| 8851641d94 | |||
| 0a7b1dd7f4 | |||
| bb2a983a00 | |||
| 604358b48e | |||
| 68e60f0586 | |||
| e4f141b107 | |||
| fe172672e9 | |||
| 69a27e3591 | |||
| d91e5d31f6 | |||
| a4c3fdfd18 | |||
| b235320d8c | |||
| e9a3464c26 | |||
| e08db74706 | |||
| 3a971d63f8 | |||
| 9a75a1f12d | |||
| 64f88cbf25 | |||
| ca66aac1c1 | |||
| 9262ea1890 | |||
| 81537bee95 | |||
| f9e94f5e01 | |||
| 691e5766c4 | |||
| 0770d91a3c | |||
| f3531276c8 | |||
| ffe1eddb00 | |||
| ba0ba1973a | |||
| b4e1b15da5 | |||
| a2b2add58f | |||
| c70517f3ba | |||
| d79e28157f | |||
| 4a36816f8f | |||
| 83ac741b59 | |||
| 2fb4be2edc | |||
| 868bd6efc8 | |||
| 27e18386d2 | |||
| 562210b862 | |||
| 39b3e03fc5 | |||
| ba7eec2c0b | |||
| 357357ad78 | |||
| b3bf86e697 | |||
| 6b65402611 | |||
| c7355f03e7 | |||
| 5274d73fd0 | |||
| 88f6242804 | |||
| e936ba8391 | |||
| 9136b83223 | |||
| 4987753a47 | |||
| 871ee13e05 | |||
| 9c4040345a | |||
| aa4f6c0a7b | |||
| 5471f345d8 | |||
| 9679d450fc | |||
| d8d221dce3 | |||
| ed5f879b9f | |||
| 3bc3331983 | |||
| 10657416ea | |||
| e5abdb7de8 | |||
| 7b57c8b5b3 | |||
| 22563f7283 | |||
| d35e775407 | |||
| a5684f0996 | |||
| 509c34d66a |
@@ -1,8 +0,0 @@
|
||||
c14f5eeca042232025797990396232e4feab53e7
|
||||
d6881e833da0b6278313870d37d42ca6ece8686a
|
||||
892be5444c5bd01a601c99243ac8d3e7b2f0e779
|
||||
d169a964dd7e9ab5c98b35a82bcbc76dc6229a7a
|
||||
4a6ec11f6f84a3d1fed2ee9b2f85d783ac3daa9d
|
||||
2c9d6a47f4d24a923069775206bf734feeb14830
|
||||
8f1743ed64a2a52c201a1ef5e312b4d3a7a922cf
|
||||
ad7f030344c405e10cfa7d5cf06d82c121eeb825
|
||||
@@ -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}}"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
[](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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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']
|
||||
@@ -1,10 +0,0 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
rst_files/library_root
|
||||
|
||||
|
||||
* `ifcopenshell-python documentation <python/html/index.html>`_
|
||||
@@ -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"])
|
||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 120 KiB |
@@ -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
|
||||
|
||||

|
||||
|
||||
Duplex_A_20110907_optimized.ifc d2.dae --enable-layerset-slicing -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
Duplex_A_20110907_optimized.ifc d3.dae --disable-opening-subtraction -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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
|
||||
()
|
||||
@@ -1,10 +0,0 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
rst_files/library_root
|
||||
|
||||
|
||||
* `ifcopenshell-python documentation <python/html/index.html>`_
|
||||
@@ -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")
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,2 +0,0 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
@@ -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 .
|
||||
@@ -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)
|
||||
```
|
||||
@@ -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()
|
||||
@@ -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 = []
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 +0,0 @@
|
||||
xmlschema
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
... this Blender files creates this IFC 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()
|
||||
|
Before Width: | Height: | Size: 399 KiB |
@@ -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()
|
||||
@@ -1,2 +0,0 @@
|
||||
# addon writes tmp stuff directly to its dir
|
||||
/data/
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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 |
|
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,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
|
||||
@@ -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')
|
||||
@@ -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()
|
||||
@@ -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
|
||||
"""
|
||||
# Liang–Barsky 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))]
|
||||
@@ -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
|
||||
@@ -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"}
|
||||