Merge branch 'v0.6.0' into v0.7.0
@@ -0,0 +1,66 @@
|
|||||||
|
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}}"
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
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,4 +1,11 @@
|
|||||||
# Dependency and build folders created by the build scripts
|
# Dependency and build folders created by the build scripts
|
||||||
|
/_build-vs2015-x64/
|
||||||
|
/_deps-vs2015-x64-installed/
|
||||||
|
/_build-vs2017-x64/
|
||||||
|
/_deps-vs2017-x64-installed/
|
||||||
|
/_build-vs2019-x64/
|
||||||
|
/_deps-vs2019-x64-installed/
|
||||||
|
/_deps/
|
||||||
/deps*/
|
/deps*/
|
||||||
/build*/
|
/build*/
|
||||||
/install*/
|
/install*/
|
||||||
@@ -16,6 +23,10 @@ __pycache__
|
|||||||
# PyCharm files
|
# PyCharm files
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
#Virtual Env Files
|
||||||
|
Pipfile
|
||||||
|
Pipfile.lock
|
||||||
|
|
||||||
# Docs
|
# Docs
|
||||||
/docs/output
|
/docs/output
|
||||||
/docs/rst_files
|
/docs/rst_files
|
||||||
@@ -26,3 +37,5 @@ __pycache__
|
|||||||
*.mo
|
*.mo
|
||||||
# Vim
|
# Vim
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +1,61 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
|
cache: ccache
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: focal
|
||||||
sudo: required
|
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_install:
|
before_script:
|
||||||
- "sudo add-apt-repository -y ppa:freecad-community/ppa"
|
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -z; fi
|
||||||
- sudo apt-get update -qq
|
|
||||||
|
|
||||||
|
|
||||||
install:
|
|
||||||
- |
|
|
||||||
sudo apt-get install -y \
|
|
||||||
libocct-data-exchange-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
|
|
||||||
libboost-system-dev libboost-program-options-dev libboost-regex-dev libboost-thread-dev libboost-date-time-dev libboost-iostreams-dev libboost-filesystem-dev \
|
|
||||||
build-essential cmake python2.7 libpython2.7-dev swig zlib1g liblzma5 opencollada-dev wget apt-transport-https
|
|
||||||
- sudo mkdir -p /usr/include/json/nlohmann/
|
|
||||||
- sudo wget https://github.com/nlohmann/json/releases/download/v3.6.1/json.hpp -O /usr/include/json/nlohmann/json.hpp
|
|
||||||
# - sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
|
||||||
# - sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
|
||||||
# - sudo apt-get update -qq && sudo apt-get install -y dart
|
|
||||||
# - export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
|
||||||
# - git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- pwd
|
- pwd
|
||||||
- cd cmake
|
- mkdir build && cd build
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- |
|
- |
|
||||||
cmake \
|
cmake \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||||
-DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so \
|
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||||
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
|
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||||
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
|
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
|
||||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.60;/usr/lib/x86_64-linux-gnu/libicudata.so.60" \
|
|
||||||
-DGLTF_SUPPORT=On \
|
-DGLTF_SUPPORT=On \
|
||||||
-DJSON_INCLUDE_DIR=/usr/include/json \
|
-DJSON_INCLUDE_DIR=/usr/include \
|
||||||
..
|
../cmake
|
||||||
- sudo make -j2 install
|
|
||||||
|
# TODO: Drop sudo
|
||||||
- cd ../../test
|
- sudo make -j $(nproc) install
|
||||||
- /usr/bin/python2.7 tests.py
|
|
||||||
|
- cd ../test
|
||||||
|
- /usr/bin/python tests.py
|
||||||
|
- /usr/bin/python ../src/ifcopenshell-python/ifcopenshell/test_ids.py
|
||||||
|
|
||||||
- cd input
|
- cd input
|
||||||
|
|
||||||
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
|
- /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]"
|
||||||
- /usr/bin/python2.7 -c "from __future__ import print_function; 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 \
|
(for i in *.ifc; do \
|
||||||
echo $i | tee -a log; \
|
echo $i | tee -a log; \
|
||||||
@@ -64,3 +68,6 @@ script:
|
|||||||
- echo Succeeded
|
- echo Succeeded
|
||||||
- grep 0$ statuses
|
- grep 0$ statuses
|
||||||
- grep 0$ statuses | wc -l
|
- grep 0$ statuses | wc -l
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -s; fi
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- 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
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ Prerequisites
|
|||||||
Dependencies
|
Dependencies
|
||||||
-------------
|
-------------
|
||||||
* [Boost](http://www.boost.org/)
|
* [Boost](http://www.boost.org/)
|
||||||
* [Open Cascade](http://opencascade.org) - *optional*, but required for building IfcGeom
|
* [Open Cascade](https://dev.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")
|
([official](https://dev.opencascade.org/release), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
|
||||||
For converting IFC representation items into BRep solids and tesselated meshes
|
For converting IFC representation items into BRep solids and tesselated meshes
|
||||||
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
|
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
|
||||||
For IfcConvert to be able to write tessellated Collada (.dae) files
|
For IfcConvert to be able to write tessellated Collada (.dae) files
|
||||||
@@ -101,7 +101,7 @@ Note: where `make -j` is written, add a number roughly equal to the amount of CP
|
|||||||
$ make -j
|
$ make -j
|
||||||
$ sudo make install
|
$ sudo make install
|
||||||
|
|
||||||
**2c)** or obtain and compile OCCT from http://www.opencascade.org/getocc/download/loadocc/
|
**2c)** or obtain and compile OCCT from https://dev.opencascade.org/release
|
||||||
|
|
||||||
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
|
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ cmake_minimum_required(VERSION 3.1.3)
|
|||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
|
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
|
||||||
|
|
||||||
project (IfcOpenShell)
|
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)
|
foreach(max_year RANGE 2014 2030)
|
||||||
set(max_sdk "$ENV{ADSK_3DSMAX_SDK_${max_year}}")
|
set(max_sdk "$ENV{ADSK_3DSMAX_SDK_${max_year}}")
|
||||||
@@ -35,6 +38,7 @@ OPTION(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLL
|
|||||||
OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." 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(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(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
|
||||||
|
OPTION(BUILD_PACKAGE "" OFF)
|
||||||
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
|
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
|
||||||
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
|
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
|
||||||
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
|
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
|
||||||
@@ -55,7 +59,7 @@ OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only ins
|
|||||||
|
|
||||||
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
|
# 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)
|
# 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))
|
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
|
||||||
message(STATUS "'IfcGeom' is required with current outputs")
|
message(STATUS "'IfcGeom' is required with current outputs")
|
||||||
set(BUILD_IFCGEOM ON)
|
set(BUILD_IFCGEOM ON)
|
||||||
@@ -66,7 +70,7 @@ IF(NOT BINDIR)
|
|||||||
set(BINDIR bin)
|
set(BINDIR bin)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
IF(NOT IS_ABSOLUTE ${BINDIR})
|
IF(NOT IS_ABSOLUTE ${BINDIR})
|
||||||
set(BINDIR ${CMAKE_INSTALL_PREFIX}/${BINDIR})
|
set(BINDIR ${CMAKE_INSTALL_BINDIR})
|
||||||
ENDIF()
|
ENDIF()
|
||||||
MESSAGE(STATUS "BINDIR: ${BINDIR}")
|
MESSAGE(STATUS "BINDIR: ${BINDIR}")
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ IF(NOT INCLUDEDIR)
|
|||||||
set(INCLUDEDIR include)
|
set(INCLUDEDIR include)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
IF(NOT IS_ABSOLUTE ${INCLUDEDIR})
|
IF(NOT IS_ABSOLUTE ${INCLUDEDIR})
|
||||||
set(INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${INCLUDEDIR})
|
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
ENDIF()
|
ENDIF()
|
||||||
MESSAGE(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
|
MESSAGE(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
|
||||||
|
|
||||||
@@ -82,11 +86,11 @@ IF(NOT LIBDIR)
|
|||||||
set(LIBDIR lib)
|
set(LIBDIR lib)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
IF(NOT IS_ABSOLUTE ${LIBDIR})
|
IF(NOT IS_ABSOLUTE ${LIBDIR})
|
||||||
set(LIBDIR ${CMAKE_INSTALL_PREFIX}/${LIBDIR})
|
set(LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||||
ENDIF()
|
ENDIF()
|
||||||
MESSAGE(STATUS "LIBDIR: ${LIBDIR}")
|
MESSAGE(STATUS "LIBDIR: ${LIBDIR}")
|
||||||
|
|
||||||
set(IFCOPENSHELL_LIBARY_DIR "") # for *nix rpaths
|
set(IFCOPENSHELL_LIBRARY_DIR "") # for *nix rpaths
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
if (BUILD_SHARED_LIBS)
|
||||||
add_definitions(-DIFC_SHARED_BUILD)
|
add_definitions(-DIFC_SHARED_BUILD)
|
||||||
@@ -96,7 +100,7 @@ if (BUILD_SHARED_LIBS)
|
|||||||
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
|
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
|
||||||
add_definitions(-wd4251)
|
add_definitions(-wd4251)
|
||||||
endif()
|
endif()
|
||||||
set(IFCOPENSHELL_LIBARY_DIR "${LIBDIR}")
|
set(IFCOPENSHELL_LIBRARY_DIR "${LIBDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Create cache entries if absent for environment variables
|
# Create cache entries if absent for environment variables
|
||||||
@@ -180,6 +184,7 @@ if(USE_MMAP)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (IFCXML_SUPPORT)
|
if (IFCXML_SUPPORT)
|
||||||
|
find_package(LibXml2 REQUIRED)
|
||||||
add_definitions(-DWITH_IFCXML)
|
add_definitions(-DWITH_IFCXML)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -226,51 +231,63 @@ endfunction()
|
|||||||
if(BUILD_IFCGEOM)
|
if(BUILD_IFCGEOM)
|
||||||
|
|
||||||
IF(MSVC)
|
IF(MSVC)
|
||||||
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
|
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# Find Open CASCADE
|
# Open CASCADE
|
||||||
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
|
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
|
||||||
SET(OCC_INCLUDE_DIR "/usr/include/oce/" CACHE FILEPATH "Open CASCADE header files")
|
FIND_PATH(OCC_INCLUDE_DIR Standard_Version.hxx
|
||||||
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
|
[PATHS
|
||||||
MESSAGE(STATUS "Use OCC_INCLUDE_DIR to specify another directory")
|
/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()
|
||||||
ELSE()
|
ELSE()
|
||||||
SET(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
|
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}")
|
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
FIND_FILE(gp_Pnt_hxx "gp_Pnt.hxx" ${OCC_INCLUDE_DIR})
|
|
||||||
IF(gp_Pnt_hxx)
|
|
||||||
MESSAGE(STATUS "Header files found")
|
|
||||||
ELSE()
|
|
||||||
MESSAGE(FATAL_ERROR "Unable to find header files, aborting")
|
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
SET(OPENCASCADE_LIBRARY_NAMES
|
SET(OPENCASCADE_LIBRARY_NAMES
|
||||||
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
|
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
|
||||||
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
|
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
|
||||||
)
|
)
|
||||||
|
|
||||||
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
|
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
|
||||||
SET(OCC_LIBRARY_DIR "/usr/lib/" CACHE FILEPATH "Open CASCADE library files")
|
find_library(OCC_LIBRARY TKernel
|
||||||
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
|
[PATHS
|
||||||
MESSAGE(STATUS "Use OCC_LIBRARY_DIR to specify another directory")
|
/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()
|
||||||
ELSE()
|
ELSE()
|
||||||
SET(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
|
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(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
FIND_LIBRARY(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH)
|
FIND_LIBRARY(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||||
IF(libTKernel)
|
IF(libTKernel)
|
||||||
MESSAGE(STATUS "Library files found")
|
MESSAGE(STATUS "Required Open Cascade Library files found")
|
||||||
ELSE()
|
ELSE()
|
||||||
MESSAGE(FATAL_ERROR "Unable to find library files, aborting")
|
MESSAGE(FATAL_ERROR "Unable to find Open Cascade library files, aborting")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# Use the found libTKernel as a template for all other OCC libraries
|
# Use the found libTKernel as a template for all other OCC libraries
|
||||||
# TODO Extract this into macro/function
|
# TODO Extract this into macro/function
|
||||||
foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
|
foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
|
||||||
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
|
# Make sure we'll handle the Windows/MSVC debug postfix convention too.
|
||||||
string(REPLACE TKerneld "${lib}" lib_path "${libTKernel}")
|
string(REPLACE TKerneld "${lib}" lib_path "${libTKernel}")
|
||||||
string(REPLACE TKernel "${lib}" lib_path "${lib_path}")
|
string(REPLACE TKernel "${lib}" lib_path "${lib_path}")
|
||||||
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
|
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
|
||||||
@@ -345,7 +362,7 @@ IF(COLLADA_SUPPORT AND BUILD_CONVERT)
|
|||||||
|
|
||||||
# Use the found OpenCOLLADAFramework as a template for all other OpenCOLLADA libraries
|
# Use the found OpenCOLLADAFramework as a template for all other OpenCOLLADA libraries
|
||||||
foreach(lib ${OPENCOLLADA_LIBRARY_NAMES})
|
foreach(lib ${OPENCOLLADA_LIBRARY_NAMES})
|
||||||
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
|
# Make sure we'll handle the Windows/MSVC debug postfix convention too.
|
||||||
string(REPLACE OpenCOLLADAFrameworkd "${lib}" lib_path "${OpenCOLLADAFramework}")
|
string(REPLACE OpenCOLLADAFrameworkd "${lib}" lib_path "${OpenCOLLADAFramework}")
|
||||||
string(REPLACE OpenCOLLADAFramework "${lib}" lib_path "${lib_path}")
|
string(REPLACE OpenCOLLADAFramework "${lib}" lib_path "${lib_path}")
|
||||||
list(APPEND OPENCOLLADA_LIBRARIES "${lib_path}")
|
list(APPEND OPENCOLLADA_LIBRARIES "${lib_path}")
|
||||||
@@ -465,7 +482,7 @@ IF(MSVC)
|
|||||||
# Disable overeager and false positives causing C4458 ("declaration of 'indentifier' hides class member"), at least for now.
|
# Disable overeager and false positives causing C4458 ("declaration of 'indentifier' hides class member"), at least for now.
|
||||||
ADD_DEFINITIONS(-wd4458)
|
ADD_DEFINITIONS(-wd4458)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
|
# 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))
|
if (MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
|
||||||
add_definitions(-permissive-)
|
add_definitions(-permissive-)
|
||||||
endif()
|
endif()
|
||||||
@@ -490,6 +507,10 @@ ElSE()
|
|||||||
else()
|
else()
|
||||||
add_definitions(-Wno-maybe-uninitialized)
|
add_definitions(-Wno-maybe-uninitialized)
|
||||||
endif()
|
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
|
# -fPIC is not relevant on Windows and creates pointless warnings
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
add_definitions(-fPIC)
|
add_definitions(-fPIC)
|
||||||
@@ -506,27 +527,31 @@ INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCL
|
|||||||
|
|
||||||
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||||
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
|
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
|
||||||
set(${RESULT_NAME}
|
set(${RESULT_NAME}
|
||||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
|
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
|
||||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
|
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
|
||||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
|
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1")
|
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4")
|
||||||
|
|
||||||
|
foreach(s ${SCHEMA_VERSIONS})
|
||||||
|
add_definitions(-DHAS_SCHEMA_${s})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
if(COMPILE_SCHEMA)
|
if(COMPILE_SCHEMA)
|
||||||
# @todo, this appears to be untested at the moment
|
# @todo, this appears to be untested at the moment
|
||||||
|
|
||||||
find_package(PythonInterp)
|
find_package(PythonInterp)
|
||||||
|
|
||||||
IF(NOT PYTHONINTERP_FOUND)
|
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.")
|
MESSAGE(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
|
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
|
||||||
|
|
||||||
# Install pyparsing if necessary
|
# Install pyparsing if necessary
|
||||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
|
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
|
||||||
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
|
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
|
||||||
@@ -548,23 +573,23 @@ if(COMPILE_SCHEMA)
|
|||||||
else()
|
else()
|
||||||
message(STATUS "Python interpreter with pyparsing found")
|
message(STATUS "Python interpreter with pyparsing found")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Bootstrap the parser
|
# Bootstrap the parser
|
||||||
message(STATUS "Compiling schema, this will take a while...")
|
message(STATUS "Compiling schema, this will take a while...")
|
||||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
|
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
|
||||||
WORKING_DIRECTORY ../src/ifcexpressparser
|
WORKING_DIRECTORY ../src/ifcexpressparser
|
||||||
OUTPUT_FILE express_parser.py
|
OUTPUT_FILE express_parser.py
|
||||||
RESULT_VARIABLE SUCCESS)
|
RESULT_VARIABLE SUCCESS)
|
||||||
|
|
||||||
if (NOT "${SUCCESS}" STREQUAL "0")
|
if (NOT "${SUCCESS}" STREQUAL "0")
|
||||||
MESSAGE(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
|
MESSAGE(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Generate code
|
# Generate code
|
||||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
|
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
|
||||||
WORKING_DIRECTORY ../src/ifcparse
|
WORKING_DIRECTORY ../src/ifcparse
|
||||||
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
|
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
|
||||||
|
|
||||||
# Prevent the schema that had just been compiled from being excluded
|
# Prevent the schema that had just been compiled from being excluded
|
||||||
foreach(s ${SCHEMA_VERSIONS})
|
foreach(s ${SCHEMA_VERSIONS})
|
||||||
if("${COMPILED_SCHEMA_NAME}" STREQUAL "${s}")
|
if("${COMPILED_SCHEMA_NAME}" STREQUAL "${s}")
|
||||||
@@ -593,8 +618,35 @@ if (BUILD_CONVERT)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# IfcParse
|
# IfcParse
|
||||||
file(GLOB IFCPARSE_H_FILES ../src/ifcparse/*.h)
|
file(GLOB IFCPARSE_H_FILES_ALL ../src/ifcparse/*.h)
|
||||||
file(GLOB IFCPARSE_CPP_FILES ../src/ifcparse/*.cpp)
|
file(GLOB IFCPARSE_CPP_FILES_ALL ../src/ifcparse/*.cpp)
|
||||||
|
|
||||||
|
foreach(s ${IFCPARSE_H_FILES_ALL})
|
||||||
|
get_filename_component(p "${s}" NAME)
|
||||||
|
if (NOT "${p}" MATCHES "[0-9]")
|
||||||
|
list(APPEND IFCPARSE_H_FILES "${s}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(s ${IFCPARSE_CPP_FILES_ALL})
|
||||||
|
get_filename_component(p "${s}" NAME)
|
||||||
|
if (NOT "${p}" MATCHES "[0-9]")
|
||||||
|
list(APPEND IFCPARSE_CPP_FILES "${s}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(s ${SCHEMA_VERSIONS})
|
||||||
|
list(APPEND IFCPARSE_H_FILES
|
||||||
|
../src/ifcparse/Ifc${s}.h
|
||||||
|
../src/ifcparse/Ifc${s}-definitions.h
|
||||||
|
)
|
||||||
|
list(APPEND IFCPARSE_CPP_FILES
|
||||||
|
../src/ifcparse/Ifc${s}.cpp
|
||||||
|
../src/ifcparse/Ifc${s}-schema.cpp
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
|
set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
|
||||||
|
|
||||||
add_library(IfcParse ${IFCPARSE_FILES})
|
add_library(IfcParse ${IFCPARSE_FILES})
|
||||||
@@ -627,7 +679,7 @@ if (UNIX)
|
|||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
endif(BUILD_IFCGEOM)
|
endif(BUILD_IFCGEOM)
|
||||||
|
|
||||||
@@ -665,7 +717,7 @@ if ((NOT WIN32) AND BUILD_SHARED_LIBS)
|
|||||||
# Only set RPATHs when building shared libraries (i.e. IfcParse and
|
# Only set RPATHs when building shared libraries (i.e. IfcParse and
|
||||||
# IfcGeom are dynamically linked). Not necessarily a perfect solution
|
# IfcGeom are dynamically linked). Not necessarily a perfect solution
|
||||||
# but probably a good indication of whether RPATHs are necessary.
|
# but probably a good indication of whether RPATHs are necessary.
|
||||||
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR}")
|
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
INSTALL(TARGETS IfcConvert
|
INSTALL(TARGETS IfcConvert
|
||||||
@@ -686,7 +738,7 @@ ADD_EXECUTABLE(IfcGeomServer ${SOURCE_FILES})
|
|||||||
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES})
|
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES})
|
||||||
|
|
||||||
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
|
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
|
||||||
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
|
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBRARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
INSTALL(TARGETS IfcGeomServer
|
INSTALL(TARGETS IfcGeomServer
|
||||||
@@ -697,7 +749,7 @@ INSTALL(TARGETS IfcGeomServer
|
|||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
IF(BUILD_DOCUMENTATION)
|
IF(BUILD_DOCUMENTATION)
|
||||||
set(CMAKE_MODULE_PATH "../docs/cmake")
|
set(CMAKE_MODULE_PATH "../docs/cmake")
|
||||||
ADD_SUBDIRECTORY(../docs docs)
|
ADD_SUBDIRECTORY(../docs docs)
|
||||||
@@ -716,7 +768,7 @@ IF(BUILD_IFCMAX)
|
|||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# CMake installation targets
|
# CMake installation targets
|
||||||
INSTALL(FILES ${IFCPARSE_H_FILES}
|
INSTALL(FILES ${IFCPARSE_H_FILES}
|
||||||
DESTINATION ${INCLUDEDIR}/ifcparse
|
DESTINATION ${INCLUDEDIR}/ifcparse
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -727,11 +779,11 @@ INSTALL(TARGETS IfcParse
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(BUILD_IFCGEOM)
|
if(BUILD_IFCGEOM)
|
||||||
INSTALL(FILES ${IFCGEOM_H_FILES}
|
INSTALL(FILES ${IFCGEOM_H_FILES}
|
||||||
DESTINATION ${INCLUDEDIR}/ifcgeom
|
DESTINATION ${INCLUDEDIR}/ifcgeom
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
|
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
|
||||||
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
|
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -757,3 +809,43 @@ INSTALL(FILES ${SERIALIZERS_S_FILES}
|
|||||||
DESTINATION ${INCLUDEDIR}/serializers/schema_dependent
|
DESTINATION ${INCLUDEDIR}/serializers/schema_dependent
|
||||||
)
|
)
|
||||||
endif()
|
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)
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ def run(cmds, cwd=None):
|
|||||||
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
|
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
|
||||||
|
|
||||||
OCE_LOCATION="https://github.com/tpaviot/oce/archive/OCE-%s.tar.gz" % (OCE_VERSION,)
|
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,)
|
BOOST_LOCATION="https://boostorg.jfrog.io/artifactory/main/release/%s/source/" % (BOOST_VERSION,)
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
@@ -336,7 +336,7 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
|||||||
`None`."""
|
`None`."""
|
||||||
if not os.path.exists(target_dir):
|
if not os.path.exists(target_dir):
|
||||||
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
|
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
|
||||||
run([git, "clone", clone_url, target_dir])
|
run([git, "clone", "--recursive", clone_url, target_dir])
|
||||||
else:
|
else:
|
||||||
logger.info("directory '%s' already cloned. Pulling latest changes." % (target_dir,))
|
logger.info("directory '%s' already cloned. Pulling latest changes." % (target_dir,))
|
||||||
|
|
||||||
|
|||||||
@@ -4,23 +4,26 @@ 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
|
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
||||||
is available via `bcfapi.py`.
|
is available via `bcfapi.py`.
|
||||||
|
|
||||||
Currently supports BCF version 2.1.
|
- BCF-XML version 2.1: Fully supported
|
||||||
|
- BCF-API version 2.1: Not supported, will probably tackle this after BCF-API v3.0
|
||||||
|
- BCF-XML version 3.0: Almost fully supported, except for the documents module
|
||||||
|
- BCF-API version 3.0: Not supported, but work underway to support it
|
||||||
|
|
||||||
## bcfxml
|
## bcfxml
|
||||||
|
|
||||||
The `bcfxml` module lets you interact with the BCF-XML standard.
|
The `bcfxml` module lets you interact with the BCF-XML standard.
|
||||||
|
|
||||||
```
|
```
|
||||||
from bcf.bcfxml import BcfXml
|
from bcf import bcfxml
|
||||||
|
|
||||||
bcfxml = BcfXml()
|
|
||||||
|
|
||||||
# Load a project
|
# Load a project
|
||||||
project = bcfxml.get_project("/path/to/file.bcf")
|
bcfxml = bcfxml.load("/path/to/file.bcf")
|
||||||
|
|
||||||
|
|
||||||
# The project is also stored in the module
|
# The project is also stored in the module
|
||||||
# project == bcfxml.project
|
# project == bcfxml.project
|
||||||
|
project=bcfxml.get_project()
|
||||||
print(project.name)
|
print(project.name)
|
||||||
|
|
||||||
# To edit a project, just modify the object directly
|
# To edit a project, just modify the object directly
|
||||||
|
|||||||
@@ -1,762 +1,42 @@
|
|||||||
import os
|
import os.path
|
||||||
import uuid
|
|
||||||
import shutil
|
|
||||||
import zipfile
|
import zipfile
|
||||||
import logging
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import bcf.data
|
|
||||||
from datetime import datetime
|
|
||||||
from xml.dom import minidom
|
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__))
|
def load(filepath):
|
||||||
|
filepath = extract_project(filepath)
|
||||||
|
if os.path.isfile(os.path.join(filepath, "bcf.version")):
|
||||||
|
version_path = os.path.join(filepath, "bcf.version")
|
||||||
|
version_id = get_version(version_path)
|
||||||
|
# TODO: we actually coded it for 2.1, let's check the difference between 2.0 and 2.1
|
||||||
|
if version_id == "2.1" or version_id == "2.0":
|
||||||
|
from bcf.v2.bcfxml import BcfXml
|
||||||
|
|
||||||
|
bcfxml = BcfXml()
|
||||||
|
bcfxml.filepath = filepath
|
||||||
|
return bcfxml
|
||||||
|
elif version_id == "3.0":
|
||||||
|
from bcf.v3.bcfxml import BcfXml
|
||||||
|
|
||||||
@contextmanager
|
bcfxml = BcfXml()
|
||||||
def cd(newdir):
|
bcfxml.filepath = filepath
|
||||||
prevdir = os.getcwd()
|
return bcfxml
|
||||||
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)
|
|
||||||
data = self._read_xml("project.bcfp", "project.xsd")
|
|
||||||
self.project.project_id = data["Project"]["@ProjectId"]
|
|
||||||
self.project.name = data["Project"]["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:
|
else:
|
||||||
topic.modified_date = datetime.utcnow().isoformat()
|
raise Exception(f"Version {version_id} not supported.")
|
||||||
topic.modified_author = self.author
|
|
||||||
|
|
||||||
self.document = minidom.Document()
|
|
||||||
root = self._create_element(self.document, "Markup")
|
|
||||||
|
|
||||||
self.write_header(topic.header, root)
|
def get_version(version_path):
|
||||||
|
xmlparse = minidom.parse(version_path)
|
||||||
|
version_el = xmlparse.getElementsByTagName("Version")[0]
|
||||||
|
version = version_el.getAttribute("VersionId")
|
||||||
|
return version
|
||||||
|
|
||||||
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:
|
def extract_project(filepath):
|
||||||
self._create_element(topic_el, "ReferenceLink", text=reference_link)
|
if not filepath:
|
||||||
|
return
|
||||||
text_map = {
|
zip_file = zipfile.ZipFile(filepath)
|
||||||
"Title": topic.title,
|
filepath = tempfile.mkdtemp()
|
||||||
"Priority": topic.priority,
|
zip_file.extractall(filepath)
|
||||||
"Index": topic.index,
|
return filepath
|
||||||
}
|
|
||||||
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()
|
|
||||||
|
|||||||
@@ -0,0 +1,771 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import logging
|
||||||
|
import tempfile
|
||||||
|
import bcf.v2.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.v2.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
|
||||||
|
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:
|
||||||
|
try:
|
||||||
|
uuid.UUID(subdir)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if not os.path.exists(os.path.join(self.filepath, subdir, "markup.bcf")):
|
||||||
|
continue
|
||||||
|
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.v2.data.Header()
|
||||||
|
for item in data["Header"]["File"]:
|
||||||
|
header_file = bcf.v2.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.v2.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.v2.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.v2.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.v2.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.v2.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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if f.filename:
|
||||||
|
self._create_element(file_el, "Filename", text=f.filename)
|
||||||
|
if f.date:
|
||||||
|
self._create_element(file_el, "Date", text=f.date)
|
||||||
|
if f.reference:
|
||||||
|
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.v2.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_format, "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.v2.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.v2.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.v2.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.v2.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.v2.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.v2.data.Components()
|
||||||
|
data = visinfo["Components"]
|
||||||
|
if "ViewSetupHints" in data:
|
||||||
|
view_setup_hints = bcf.v2.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.v2.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.v2.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.v2.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.v2.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.v2.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.v2.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.v2.data.Bitmap()
|
||||||
|
bitmap.reference = item["Reference"]
|
||||||
|
bitmap.bitmap_format = 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.v2.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()
|
||||||
@@ -2,6 +2,7 @@ class Project:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.project_id = ""
|
self.project_id = ""
|
||||||
self.name = ""
|
self.name = ""
|
||||||
|
self.extension_schema = ""
|
||||||
|
|
||||||
|
|
||||||
class BimSnippet:
|
class BimSnippet:
|
||||||
@@ -156,7 +157,7 @@ class Bitmap:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reference = "" # Only in BCF-XML
|
self.reference = "" # Only in BCF-XML
|
||||||
self.bitmap_data = None # Only in BCF-API
|
self.bitmap_data = None # Only in BCF-API
|
||||||
self.bitmap_type = "PNG" # Enum of png or jpg
|
self.bitmap_format = "PNG" # Enum of png or jpg
|
||||||
self.location = Point()
|
self.location = Point()
|
||||||
self.normal = Direction()
|
self.normal = Direction()
|
||||||
self.up = Direction()
|
self.up = Direction()
|
||||||
@@ -0,0 +1,827 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import logging
|
||||||
|
import tempfile
|
||||||
|
import bcf.v3.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.v3.data.Project()
|
||||||
|
self.version = "3.0"
|
||||||
|
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 os.path.isfile(os.path.join(self.filepath, "project.bcfp")):
|
||||||
|
data = self._read_xml("project.bcfp", "project.xsd")
|
||||||
|
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, "ProjectInfo")
|
||||||
|
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
|
||||||
|
if self.project.name:
|
||||||
|
self._create_element(project, "Name", text=self.project.name)
|
||||||
|
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})
|
||||||
|
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:
|
||||||
|
try:
|
||||||
|
uuid.UUID(subdir)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if not os.path.exists(os.path.join(self.filepath, subdir, "markup.bcf")):
|
||||||
|
continue
|
||||||
|
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.v3.data.Header()
|
||||||
|
if data["Header"].get("Files"):
|
||||||
|
for item in data["Header"]["Files"].get("File", []):
|
||||||
|
header_file = bcf.v3.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.v3.data.Topic()
|
||||||
|
self.topics[guid] = topic
|
||||||
|
|
||||||
|
mandatory_keys = {
|
||||||
|
"guid": "@Guid",
|
||||||
|
"title": "Title",
|
||||||
|
"creation_date": "CreationDate",
|
||||||
|
"creation_author": "CreationAuthor",
|
||||||
|
"topic_status": "@TopicStatus",
|
||||||
|
"topic_type": "@TopicType",
|
||||||
|
}
|
||||||
|
for key, value in mandatory_keys.items():
|
||||||
|
setattr(topic, key, data["Topic"][value])
|
||||||
|
|
||||||
|
optional_keys = {
|
||||||
|
"priority": "Priority",
|
||||||
|
"index": "Index",
|
||||||
|
"modified_date": "ModifiedDate",
|
||||||
|
"modified_author": "ModifiedAuthor",
|
||||||
|
"due_date": "DueDate",
|
||||||
|
"assigned_to": "AssignedTo",
|
||||||
|
"stage": "Stage",
|
||||||
|
"description": "Description",
|
||||||
|
"server_assigned_id": "@ServerAssignedId",
|
||||||
|
}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in data["Topic"]:
|
||||||
|
setattr(topic, key, data["Topic"][value])
|
||||||
|
|
||||||
|
if data["Topic"].get("ReferenceLinks"):
|
||||||
|
topic.reference_links.extend(data["Topic"]["ReferenceLinks"].get("ReferenceLink", []))
|
||||||
|
|
||||||
|
if data["Topic"].get("Labels"):
|
||||||
|
topic.labels.extend(data["Topic"]["Labels"].get("Label", []))
|
||||||
|
|
||||||
|
if "BimSnippet" in data["Topic"]:
|
||||||
|
bim_snippet = bcf.v3.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 data["Topic"].get("DocumentReferences"):
|
||||||
|
for item in data["Topic"]["DocumentReferences"].get("DocumentReference", []):
|
||||||
|
document_reference = bcf.v3.data.DocumentReference()
|
||||||
|
keys = {
|
||||||
|
"document_guid": "DocumentGuid",
|
||||||
|
"url": "Url",
|
||||||
|
"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 data["Topic"].get("RelatedTopics"):
|
||||||
|
for item in data["Topic"]["RelatedTopics"].get("RelatedTopic", []):
|
||||||
|
related_topic = bcf.v3.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.v3.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")
|
||||||
|
if topic.header:
|
||||||
|
self.write_header(topic.header, root)
|
||||||
|
|
||||||
|
topic_el = self._create_element(
|
||||||
|
root,
|
||||||
|
"Topic",
|
||||||
|
{
|
||||||
|
"Guid": topic.guid,
|
||||||
|
"ServerAssignedId": topic.server_assigned_id,
|
||||||
|
"TopicType": topic.topic_type,
|
||||||
|
"TopicStatus": topic.topic_status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if topic.reference_links:
|
||||||
|
reference_Links_el = self._create_element(topic_el, "ReferenceLinks")
|
||||||
|
for reference_link in topic.reference_links:
|
||||||
|
self._create_element(reference_Links_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)
|
||||||
|
if topic.labels:
|
||||||
|
label_el = self._create_element(topic_el, "Labels")
|
||||||
|
for label in topic.labels:
|
||||||
|
self._create_element(label_el, "Label", 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,
|
||||||
|
)
|
||||||
|
if topic.document_references:
|
||||||
|
reference_el = self._create_element(topic_el, "DocumentReferences")
|
||||||
|
self.write_document_references(topic.document_references, reference_el)
|
||||||
|
if topic.related_topics:
|
||||||
|
related_topic_el = self._create_element(topic_el, "RelatedTopics")
|
||||||
|
for related_topic in topic.related_topics:
|
||||||
|
self._create_element(related_topic_el, "RelatedTopic", {"Guid": related_topic.guid})
|
||||||
|
if topic.comments:
|
||||||
|
comment_el = self._create_element(topic_el, "Comments")
|
||||||
|
self.write_comments(topic.comments, comment_el)
|
||||||
|
if topic.viewpoints:
|
||||||
|
viewpoint_el = self._create_element(topic_el, "Viewpoints")
|
||||||
|
self.write_viewpoints(topic.viewpoints, viewpoint_el, 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_document_references(self, references, root):
|
||||||
|
for reference in references:
|
||||||
|
document_reference_el = self._create_element(root, "DocumentReference", {"Guid": reference.guid})
|
||||||
|
if reference.document_guid:
|
||||||
|
self._create_element(document_reference_el, "DocumentGuid", text=reference.document_guid)
|
||||||
|
elif reference.url:
|
||||||
|
self._create_element(document_reference_el, "Url", text=reference.url)
|
||||||
|
if reference.description:
|
||||||
|
self._create_element(document_reference_el, "Description", text=reference.description)
|
||||||
|
|
||||||
|
def write_header(self, header, root):
|
||||||
|
if not header or not header.files:
|
||||||
|
return
|
||||||
|
header_el = self._create_element(root, "Header")
|
||||||
|
files_el = self._create_element(header_el, "Files")
|
||||||
|
for f in header.files:
|
||||||
|
file_el = self._create_element(
|
||||||
|
files_el,
|
||||||
|
"File",
|
||||||
|
{
|
||||||
|
"IfcProject": f.ifc_project,
|
||||||
|
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
|
||||||
|
"IsExternal": f.is_external,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if f.filename:
|
||||||
|
self._create_element(file_el, "Filename", text=f.filename)
|
||||||
|
if f.date:
|
||||||
|
self._create_element(file_el, "Date", text=f.date)
|
||||||
|
if f.reference:
|
||||||
|
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.v3.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, "ViewPoint", {"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.selection:
|
||||||
|
selection_el = self._create_element(components_el, "Selection")
|
||||||
|
for selection in viewpoint.components.selection:
|
||||||
|
self.write_component(selection, selection_el)
|
||||||
|
if viewpoint.components.visibility:
|
||||||
|
visibility = self._create_element(
|
||||||
|
components_el,
|
||||||
|
"Visibility",
|
||||||
|
{"DefaultVisibility": viewpoint.components.visibility.default_visibility},
|
||||||
|
)
|
||||||
|
if viewpoint.components.visibility.view_setup_hints:
|
||||||
|
view_setup_hints = self._create_element(
|
||||||
|
visibility,
|
||||||
|
"ViewSetupHints",
|
||||||
|
{
|
||||||
|
"SpacesVisible": viewpoint.components.visibility.view_setup_hints.spaces_visible,
|
||||||
|
"SpaceBoundariesVisible": viewpoint.components.visibility.view_setup_hints.space_boundaries_visible,
|
||||||
|
"OpeningsVisible": viewpoint.components.visibility.view_setup_hints.openings_visible,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
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})
|
||||||
|
component_el = self._create_element(color_el, "Components")
|
||||||
|
for component in color.components:
|
||||||
|
self.write_component(component, component_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)
|
||||||
|
self._create_element(camera_el, "AspectRatio", text=camera.aspect_ratio)
|
||||||
|
|
||||||
|
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)
|
||||||
|
self._create_element(camera_el, "AspectRatio", text=camera.aspect_ratio)
|
||||||
|
|
||||||
|
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
|
||||||
|
bitmaps_el = self._create_element(parent, "Bitmaps")
|
||||||
|
for bitmap in viewpoint.bitmaps:
|
||||||
|
bitmap_el = self._create_element(bitmaps_el, "Bitmap")
|
||||||
|
|
||||||
|
text_map = {"Format": bitmap.bitmap_format, "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.v3.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.v3.data.Header()
|
||||||
|
topic.header.files.append(header_file)
|
||||||
|
self.edit_topic(topic)
|
||||||
|
|
||||||
|
def get_comments(self, guid):
|
||||||
|
comments = {}
|
||||||
|
if "Comments" not in data["Topics"]:
|
||||||
|
return comments
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
for item in data["Topic"]["Comments"].get("Comment", []):
|
||||||
|
comment = bcf.v3.data.Comment()
|
||||||
|
mandatory_keys = {
|
||||||
|
"guid": "@Guid",
|
||||||
|
"date": "Date",
|
||||||
|
"author": "Author",
|
||||||
|
}
|
||||||
|
for key, value in mandatory_keys.items():
|
||||||
|
setattr(comment, key, item[value])
|
||||||
|
optional_keys = {
|
||||||
|
"comment": "Comment",
|
||||||
|
"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.v3.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 = {}
|
||||||
|
if "Viewpoints" not in data["Topic"]:
|
||||||
|
return viewpoints
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
for item in data["Topic"]["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.v3.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.v3.data.Components()
|
||||||
|
data = visinfo["Components"]
|
||||||
|
if "Selection" in data and "Component" in data["Selection"]:
|
||||||
|
for item in data["Selection"].get("Component", []):
|
||||||
|
components.selection.append(self.get_component(item))
|
||||||
|
if "Visibility" in data:
|
||||||
|
component_visibility = bcf.v3.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))
|
||||||
|
if "ViewSetupHints" in data["Visibility"]:
|
||||||
|
view_setup_hints = bcf.v3.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["Visibility"]["ViewSetupHints"]:
|
||||||
|
setattr(view_setup_hints, key, data["Visibility"]["ViewSetupHints"][value])
|
||||||
|
component_visibility.view_setup_hints = view_setup_hints
|
||||||
|
components.visibility = component_visibility
|
||||||
|
if "Coloring" in data and "Color" in data["Coloring"]:
|
||||||
|
for item in data["Coloring"]["Color"]:
|
||||||
|
color = bcf.v3.data.Color()
|
||||||
|
color.color = item["@Color"]
|
||||||
|
for item2 in item["Components"]["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.v3.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"]
|
||||||
|
camera.aspect_ratio = data["AspectRatio"]
|
||||||
|
return camera
|
||||||
|
|
||||||
|
def get_viewpoint_perspective_camera(self, visinfo):
|
||||||
|
if "PerspectiveCamera" not in visinfo:
|
||||||
|
return None
|
||||||
|
camera = bcf.v3.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"]
|
||||||
|
camera.aspect_ratio = data["AspectRatio"]
|
||||||
|
return camera
|
||||||
|
|
||||||
|
def get_viewpoint_lines(self, visinfo):
|
||||||
|
if "Lines" not in visinfo:
|
||||||
|
return []
|
||||||
|
lines = []
|
||||||
|
for item in visinfo["Lines"].get("Line", []):
|
||||||
|
line = bcf.v3.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.v3.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 "Bitmaps" not in visinfo:
|
||||||
|
return []
|
||||||
|
bitmaps = []
|
||||||
|
for item in visinfo["Bitmaps"].get("Bitmap"):
|
||||||
|
bitmap = bcf.v3.data.Bitmap()
|
||||||
|
bitmap.reference = item["Reference"]
|
||||||
|
bitmap.bitmap_format = item["Format"].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.v3.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()
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
class Project:
|
||||||
|
def __init__(self):
|
||||||
|
self.project_id = ""
|
||||||
|
self.name = ""
|
||||||
|
|
||||||
|
|
||||||
|
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.description = None
|
||||||
|
self.document_guid = None
|
||||||
|
self.url = None
|
||||||
|
self.guid = None
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedTopic:
|
||||||
|
def __init__(self):
|
||||||
|
self.guid = None
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderFile:
|
||||||
|
def __init__(self):
|
||||||
|
self.file_name = ""
|
||||||
|
self.date = None
|
||||||
|
self.reference = ""
|
||||||
|
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 = {}
|
||||||
|
self.server_assigned_id = ""
|
||||||
|
|
||||||
|
|
||||||
|
class Comment:
|
||||||
|
def __init__(self):
|
||||||
|
self.guid = None
|
||||||
|
self.date = None
|
||||||
|
self.author = ""
|
||||||
|
self.comment = ""
|
||||||
|
self.viewpoint = None
|
||||||
|
self.modified_date = None
|
||||||
|
self.modified_author = ""
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
self.view_setup_hints = None
|
||||||
|
|
||||||
|
|
||||||
|
class Color:
|
||||||
|
def __init__(self):
|
||||||
|
self.color = None
|
||||||
|
self.components = []
|
||||||
|
|
||||||
|
|
||||||
|
class Components:
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
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
|
||||||
|
self.aspect_ratio = 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
|
||||||
|
self.aspect_ratio = 1.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_format = "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 = []
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:include schemaLocation="shared-types.xsd"/>
|
||||||
|
|
||||||
|
<xs:element name="DocumentInfo">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Documents" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Document" type="Document" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="Document">
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- Filename of the document with the file extension. Not used to store the file in the BCF -->
|
||||||
|
<xs:element name="Filename" type="NonEmptyOrBlankString"/>
|
||||||
|
<!-- Human readable description of the document -->
|
||||||
|
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attributeGroup ref="DocumentAttributes"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:attributeGroup name="DocumentAttributes">
|
||||||
|
<!-- Guid of the document. Must match the filename in the BCF -->
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:include schemaLocation="shared-types.xsd"/>
|
||||||
|
|
||||||
|
<xs:element name="Extensions">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="TopicTypes" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="TopicType" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="TopicStatuses" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="TopicStatus" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="Priorities" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Priority" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="TopicLabels" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="TopicLabel" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="Users" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="User" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="SnippetTypes" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="SnippetType" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="Stages" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Stage" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:include schemaLocation="shared-types.xsd"/>
|
||||||
|
<xs:element name="Markup">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Header" type="Header" minOccurs="0"/>
|
||||||
|
<xs:element name="Topic" type="Topic"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="Header">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Files" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="File" type="File" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</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="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<!-- the snapshot png -->
|
||||||
|
<xs:element name="Snapshot" type="NonEmptyOrBlankString" 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="NonEmptyOrBlankString"/>
|
||||||
|
<xs:element name="ReferenceSchema" type="NonEmptyOrBlankString"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="SnippetType" type="NonEmptyOrBlankString" 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="ReferenceLinks" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ReferenceLink" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="Title" type="NonEmptyOrBlankString"/>
|
||||||
|
<xs:element name="Priority" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<!-- ISG Jira issue BCF-8 Add a way save order the topics -->
|
||||||
|
<!-- This property is deprecated and will be removed in a future release -->
|
||||||
|
<xs:element name="Index" type="xs:int" minOccurs="0"/>
|
||||||
|
<xs:element name="Labels" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Label" type="NonEmptyOrBlankString" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="CreationDate" type="xs:dateTime"/>
|
||||||
|
<xs:element name="CreationAuthor" type="NonEmptyOrBlankString"/>
|
||||||
|
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<xs:element name="ModifiedAuthor" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="DueDate" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<xs:element name="AssignedTo" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="Stage" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="BimSnippet" type="BimSnippet" minOccurs="0"/>
|
||||||
|
<xs:element name="DocumentReferences" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="DocumentReference" type="DocumentReference" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="RelatedTopics" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<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:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="Comments" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Comment" type="Comment" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
|
||||||
|
<xs:element name="Viewpoints" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ViewPoint" type="ViewPoint" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
<xs:attribute name="ServerAssignedId" type="NonEmptyOrBlankString" use="optional"/>
|
||||||
|
<xs:attribute name="TopicType" type="NonEmptyOrBlankString" use="required"/>
|
||||||
|
<xs:attribute name="TopicStatus" type="NonEmptyOrBlankString" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="File">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Filename" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="Date" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<!-- Reference (URL) of the file -->
|
||||||
|
<xs:element name="Reference" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attributeGroup ref="FileAttributes"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:attributeGroup name="FileAttributes">
|
||||||
|
<xs:attribute name="IfcProject" type="IfcGuid" use="optional"/>
|
||||||
|
<xs:attribute name="IfcSpatialStructureElement" type="IfcGuid" use="optional"/>
|
||||||
|
<xs:attribute name="IsExternal" type="xs:boolean" default="true"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
|
<!-- Reference to a document inside of the topic folder or a url pointing to the web -->
|
||||||
|
<xs:complexType name="DocumentReference">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:choice>
|
||||||
|
<!-- Guid of the document: If pointing to an internal document -->
|
||||||
|
<xs:element name="DocumentGuid" type="Guid" minOccurs="0"/>
|
||||||
|
<!-- Url of the reference. If pointing to an external document -->
|
||||||
|
<xs:element name="Url" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:choice>
|
||||||
|
<!-- Human readable description of the document reference -->
|
||||||
|
<xs:element name="Description" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attributeGroup ref="DocumentReferenceAttributes"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:attributeGroup name="DocumentReferenceAttributes">
|
||||||
|
<!-- Guid of the document reference -->
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
|
<xs:complexType name="Comment">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Date" type="xs:dateTime"/>
|
||||||
|
<xs:element name="Author" type="NonEmptyOrBlankString"/>
|
||||||
|
<xs:element name="Comment" minOccurs="0" type="NonEmptyOrBlankString"/>
|
||||||
|
<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="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:simpleType name="IfcGuid">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:length value="22"/>
|
||||||
|
<xs:pattern value="[0-9A-Za-z_$]*"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:include schemaLocation="shared-types.xsd"/>
|
||||||
|
<xs:element name="ProjectInfo">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Project" type="Project"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="Project">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Name" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="ProjectId" type="NonEmptyOrBlankString" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:simpleType name="Guid">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="NonEmptyOrBlankString">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:whiteSpace value="collapse"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="Version">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="VersionId" type="xs:string" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
||||||
|
<xs:include schemaLocation="shared-types.xsd"/>
|
||||||
|
<xs:element name="VisualizationInfo">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>VisualizationInfo documentation</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<!--
|
||||||
|
Although plural, 'Components' is not a collection
|
||||||
|
-->
|
||||||
|
<xs:element name="Components" type="Components" minOccurs="0"/>
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="OrthogonalCamera" type="OrthogonalCamera"/>
|
||||||
|
<xs:element name="PerspectiveCamera" type="PerspectiveCamera"/>
|
||||||
|
</xs:choice>
|
||||||
|
<xs:element name="Lines" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Line" type="Line" minOccurs="0" 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>
|
||||||
|
<xs:element name="Bitmaps" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Bitmap" type="Bitmap" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</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 vertical size in meters</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="AspectRatio" type="PositiveDouble">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
Proportional relationship between the width and the height of the view (w/h).
|
||||||
|
</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>
|
||||||
|
Vertical field of view, in degrees.
|
||||||
|
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:element name="AspectRatio" type="PositiveDouble">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
Proportional relationship between the width and the height of the view (w/h).
|
||||||
|
</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="PositiveDouble">
|
||||||
|
<xs:restriction base="xs:double">
|
||||||
|
<xs:minExclusive value="0"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="FieldOfView">
|
||||||
|
<xs:restriction base="xs:double">
|
||||||
|
<xs:minExclusive value="0"/>
|
||||||
|
<xs:maxExclusive value="180"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:complexType name="Components">
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- 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="0"/>
|
||||||
|
<xs:element name="Coloring" type="ComponentColoring" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentSelection">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Component" type="Component" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentVisibility">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ViewSetupHints" type="ViewSetupHints" minOccurs="0"/>
|
||||||
|
<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" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="DefaultVisibility" type="xs:boolean" default="false"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ViewSetupHints">
|
||||||
|
<xs:attribute name="SpacesVisible" type="xs:boolean" default="false"/>
|
||||||
|
<xs:attribute name="SpaceBoundariesVisible" type="xs:boolean" default="false"/>
|
||||||
|
<xs:attribute name="OpeningsVisible" type="xs:boolean" default="false"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentColoring">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Color" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- At least one component is required for a Color. -->
|
||||||
|
<xs:element name="Components">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute ref="Color" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="Component">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="OriginatingSystem" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
<xs:element name="AuthoringToolId" type="NonEmptyOrBlankString" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute ref="IfcGuid"/>
|
||||||
|
</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-9A-Fa-f]{6}([0-9A-Fa-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-9A-Za-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>
|
||||||
|
<xs:complexType name="Bitmap">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Format" type="BitmapFormat"/>
|
||||||
|
<!-- Name of the bitmap file in the topic folder -->
|
||||||
|
<xs:element name="Reference" type="NonEmptyOrBlankString"/>
|
||||||
|
<!-- 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:simpleType name="BitmapFormat">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:enumeration value="png"/>
|
||||||
|
<xs:enumeration value="jpg"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
xmlschema
|
||||||
@@ -1,19 +1,5 @@
|
|||||||
VERSION:=`date '+%y%m%d'`
|
VERSION:=`date '+%y%m%d'`
|
||||||
ifeq ($(PLATFORM), win)
|
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 ($(PLATFORM), macos)
|
|
||||||
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 ($(PLATFORM), linux)
|
|
||||||
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
|
|
||||||
|
|
||||||
.PHONY: dist
|
.PHONY: dist
|
||||||
dist:
|
dist:
|
||||||
@@ -26,70 +12,47 @@ endif
|
|||||||
cp -r blenderbim/* dist/blenderbim/
|
cp -r blenderbim/* dist/blenderbim/
|
||||||
|
|
||||||
# Provides IfcOpenShell Python functionality
|
# Provides IfcOpenShell Python functionality
|
||||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-3bbdc3f-$(PLATFORM)64.zip
|
ifeq ($(PYVERSION), py37)
|
||||||
|
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-2fd2b49-$(PLATFORM)64.zip
|
||||||
|
endif
|
||||||
|
ifeq ($(PYVERSION), py39)
|
||||||
|
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-2fd2b49-$(PLATFORM)64.zip
|
||||||
|
endif
|
||||||
cd dist/working && unzip ifcblender*
|
cd dist/working && unzip ifcblender*
|
||||||
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||||
|
|
||||||
# See bug #812
|
# See bug #812
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcblenderexport/occ_utils.py
|
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
|
cd dist/working && mv occ_utils.py ../blenderbim/libs/site/packages/ifcopenshell/geom/occ_utils.py
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Provides Python OCC functionality for cutting IFC geometry for construction documentation
|
# Provides IfcConvert for construction documentation
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget $(PYTHONOCC_URL)
|
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v0.6.0-81ad689-$(PLATFORM)64.zip
|
||||||
cd dist/working && tar -xjvf pythonocc-core*
|
cd dist/working && unzip IfcConvert*
|
||||||
ifeq ($(PLATFORM), win)
|
ifeq ($(PLATFORM), win)
|
||||||
cd dist/working && cp -r Lib/site-packages/OCC ../blenderbim/libs/site/packages/
|
cp -r dist/working/IfcConvert.exe dist/blenderbim/libs/
|
||||||
else
|
else
|
||||||
cd dist/working && cp -r lib/python3.7/site-packages/OCC ../blenderbim/libs/site/packages/
|
cp -r dist/working/IfcConvert dist/blenderbim/libs/
|
||||||
endif
|
endif
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by Python OCC
|
|
||||||
mkdir dist/working
|
|
||||||
cd dist/working && wget $(OCE_URL)
|
|
||||||
cd dist/working && tar -xjvf oce*
|
|
||||||
ifeq ($(PLATFORM), win)
|
|
||||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
|
|
||||||
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)
|
|
||||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
|
|
||||||
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
|
# Provides dependencies that are part of IfcOpenShell
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
|
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
|
||||||
cd dist/working && unzip v0.6.0.zip
|
cd dist/working && unzip v0.6.0.zip
|
||||||
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
|
# 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/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
|
# Provides bcf functionality
|
||||||
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
|
||||||
# Provides IFCClash functionality
|
# Provides IFCClash functionality
|
||||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/* dist/blenderbim/libs/site/packages/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/* dist/blenderbim/libs/site/packages/
|
||||||
# Provides BIMTester functionality
|
# 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/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||||
# Provides IFCCOBie functionality
|
# Provides IFCCOBie functionality
|
||||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
||||||
@@ -98,7 +61,9 @@ endif
|
|||||||
# Provides IFCCSV functionality
|
# Provides IFCCSV functionality
|
||||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifccsv/* dist/blenderbim/libs/site/packages/
|
||||||
# Provides IFCPatch functionality
|
# Provides IFCPatch functionality
|
||||||
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch dist/blenderbim/libs/site/packages/
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcpatch/ifcpatch dist/blenderbim/libs/site/packages/
|
||||||
|
# Provides IFCP6 functionality
|
||||||
|
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcp6/ifcp6 dist/blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Provides Mustache templating in construction documentation
|
# Provides Mustache templating in construction documentation
|
||||||
@@ -115,6 +80,41 @@ endif
|
|||||||
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
|
cp -r dist/working/svgwrite-1.3.1/svgwrite dist/blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Provides fuzzy date parsing for construction sequencing
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
|
||||||
|
cd dist/working && tar -xzvf python-dateutil*
|
||||||
|
cp -r dist/working/python-dateutil-2.8.1/dateutil dist/blenderbim/libs/site/packages/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Provides duration parsing for construction sequencing
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz
|
||||||
|
cd dist/working && tar -xzvf isodate*
|
||||||
|
cp -r dist/working/isodate-0.6.0/src/isodate dist/blenderbim/libs/site/packages/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Provides networkx graph analysis for project dependency calculations
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget https://files.pythonhosted.org/packages/b0/21/adfbf6168631e28577e4af9eb9f26d75fe72b2bb1d33762a5f2c425e6c2a/networkx-2.5.1.tar.gz
|
||||||
|
cd dist/working && tar -xzvf networkx*
|
||||||
|
cp -r dist/working/networkx-2.5.1/networkx dist/blenderbim/libs/site/packages/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Required by networkx
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget https://files.pythonhosted.org/packages/4f/51/15a4f6b8154d292e130e5e566c730d8ec6c9802563d58760666f1818ba58/decorator-5.0.9.tar.gz
|
||||||
|
cd dist/working && tar -xzvf decorator*
|
||||||
|
cp -r dist/working/decorator-5.0.9/src/decorator.py dist/blenderbim/libs/site/packages/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Provides jsgantt-improved supports for web-based construction sequencing gantt charts
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||||
|
cd dist/working && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||||
|
cp dist/working/jsgantt* dist/blenderbim/bim/data/gantt/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by IFCDiff
|
# Required by IFCDiff
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget https://github.com/Moult/deepdiff/archive/master.zip
|
cd dist/working && wget https://github.com/Moult/deepdiff/archive/master.zip
|
||||||
@@ -179,8 +179,6 @@ ifeq ($(PLATFORM), linux)
|
|||||||
cp -r dist/working/fcl dist/blenderbim/libs/site/packages/
|
cp -r dist/working/fcl dist/blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Required by IFCClash
|
|
||||||
ifeq ($(PLATFORM), win)
|
ifeq ($(PLATFORM), win)
|
||||||
mkdir dist/working
|
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 && wget https://files.pythonhosted.org/packages/19/e6/6e9f33fb59e8f27c0e1592bd26e644bc92b85c942b072b2d3854105d5887/python_fcl_win32-0.0.12.post3-py3-none-win_amd64.whl
|
||||||
@@ -194,6 +192,11 @@ endif
|
|||||||
cd dist/working && wget https://files.pythonhosted.org/packages/c8/4b/d0a8c23b6c8985e5544ea96d27105a273ea22051317f850c2cdbf2029fe4/behave-1.2.6.tar.gz
|
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 && tar -xzvf behave*
|
||||||
cd dist/working/behave-1.2.6/ && cp -r behave ../../blenderbim/libs/site/packages/
|
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
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by behave
|
# Required by behave
|
||||||
@@ -231,6 +234,34 @@ endif
|
|||||||
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
|
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
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
|
# Provides IFCJSON functionality
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
cd dist/working && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
||||||
@@ -239,7 +270,7 @@ endif
|
|||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
|
||||||
cd dist && zip -r blender28-bim-$(VERSION)-$(PLATFORM).zip ./*
|
cd dist && zip -r blender28-bim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
|
||||||
rm -rf dist/blenderbim
|
rm -rf dist/blenderbim
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 399 KiB |
@@ -15,6 +15,7 @@ import site
|
|||||||
|
|
||||||
# process *.pth in /libs/site/packages to setup globally importable modules
|
# process *.pth in /libs/site/packages to setup globally importable modules
|
||||||
# 3 levels deep required by occ static ../../ path
|
# 3 levels deep required by occ static ../../ path
|
||||||
|
# TODO: 3 levels deep is no longer required as we no longer bundle OCC
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
|
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
|
||||||
|
|
||||||
@@ -14,8 +14,10 @@ if bpy is not None:
|
|||||||
"bcf": None,
|
"bcf": None,
|
||||||
"root": None,
|
"root": None,
|
||||||
"unit": None,
|
"unit": None,
|
||||||
|
"model": None,
|
||||||
"georeference": None,
|
"georeference": None,
|
||||||
"context": None,
|
"context": None,
|
||||||
|
"drawing": None,
|
||||||
"attribute": None,
|
"attribute": None,
|
||||||
"type": None,
|
"type": None,
|
||||||
"spatial": None,
|
"spatial": None,
|
||||||
@@ -23,10 +25,15 @@ if bpy is not None:
|
|||||||
"aggregate": None,
|
"aggregate": None,
|
||||||
"geometry": None,
|
"geometry": None,
|
||||||
"cobie": None,
|
"cobie": None,
|
||||||
|
"resource": None,
|
||||||
|
"cost": None,
|
||||||
|
"sequence": None,
|
||||||
|
"group": None,
|
||||||
|
"structural": None,
|
||||||
|
"boundary": None,
|
||||||
"material": None,
|
"material": None,
|
||||||
"style": None,
|
"style": None,
|
||||||
"layer": None,
|
"layer": None,
|
||||||
"model": None,
|
|
||||||
"owner": None,
|
"owner": None,
|
||||||
"pset": None,
|
"pset": None,
|
||||||
"qto": None,
|
"qto": None,
|
||||||
@@ -40,6 +47,7 @@ if bpy is not None:
|
|||||||
"diff": None,
|
"diff": None,
|
||||||
"patch": None,
|
"patch": None,
|
||||||
"covetool": None,
|
"covetool": None,
|
||||||
|
"augin": None,
|
||||||
"debug": None,
|
"debug": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,94 +61,27 @@ if bpy is not None:
|
|||||||
operator.SelectIfcFile,
|
operator.SelectIfcFile,
|
||||||
operator.ExportIFC,
|
operator.ExportIFC,
|
||||||
operator.ImportIFC,
|
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.OpenUpstream,
|
||||||
operator.AddSectionPlane,
|
operator.AddSectionPlane,
|
||||||
operator.RemoveSectionPlane,
|
operator.RemoveSectionPlane,
|
||||||
operator.ReloadIfcFile,
|
operator.ReloadIfcFile,
|
||||||
operator.AddIfcFile,
|
operator.AddIfcFile,
|
||||||
operator.RemoveIfcFile,
|
operator.RemoveIfcFile,
|
||||||
operator.SelectDocIfcFile,
|
|
||||||
operator.AddAnnotation,
|
|
||||||
operator.GenerateReferences,
|
|
||||||
operator.ResizeText,
|
|
||||||
operator.AddVariable,
|
|
||||||
operator.RemoveVariable,
|
|
||||||
operator.PropagateTextData,
|
|
||||||
operator.SetOverrideColour,
|
operator.SetOverrideColour,
|
||||||
operator.AddDrawing,
|
|
||||||
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.SetViewportShadowFromSun,
|
||||||
operator.AddDrawingStyleAttribute,
|
|
||||||
operator.RemoveDrawingStyleAttribute,
|
|
||||||
operator.CopyPropertyToSelection,
|
|
||||||
operator.CopyAttributeToSelection,
|
|
||||||
operator.RefreshDrawingList,
|
|
||||||
operator.CleanWireframes,
|
|
||||||
operator.LinkIfc,
|
operator.LinkIfc,
|
||||||
operator.SnapSpacesTogether,
|
operator.SnapSpacesTogether,
|
||||||
operator.CopyGrid,
|
|
||||||
operator.AddSectionsAnnotations,
|
|
||||||
prop.StrProperty,
|
prop.StrProperty,
|
||||||
prop.Attribute,
|
prop.Attribute,
|
||||||
prop.Variable,
|
|
||||||
prop.Drawing,
|
|
||||||
prop.Schedule,
|
|
||||||
prop.DrawingStyle,
|
|
||||||
prop.Sheet,
|
|
||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
prop.DocProperties,
|
|
||||||
prop.IfcParameter,
|
prop.IfcParameter,
|
||||||
prop.BoundaryCondition,
|
|
||||||
prop.PsetQto,
|
prop.PsetQto,
|
||||||
prop.GlobalId,
|
prop.GlobalId,
|
||||||
prop.RepresentationItem,
|
|
||||||
prop.BIMObjectProperties,
|
prop.BIMObjectProperties,
|
||||||
prop.BIMMaterialProperties,
|
prop.BIMMaterialProperties,
|
||||||
prop.SweptSolid,
|
|
||||||
prop.ItemSlotMap,
|
|
||||||
prop.BIMMeshProperties,
|
prop.BIMMeshProperties,
|
||||||
prop.BIMCameraProperties,
|
|
||||||
prop.BIMTextProperties,
|
|
||||||
ui.BIM_PT_section_plane,
|
ui.BIM_PT_section_plane,
|
||||||
ui.BIM_PT_drawings,
|
|
||||||
ui.BIM_PT_schedules,
|
|
||||||
ui.BIM_PT_sheets,
|
|
||||||
ui.BIM_PT_camera,
|
|
||||||
ui.BIM_PT_text,
|
|
||||||
ui.BIM_PT_annotation_utilities,
|
|
||||||
ui.BIM_PT_misc_utilities,
|
|
||||||
ui.BIM_UL_generic,
|
ui.BIM_UL_generic,
|
||||||
ui.BIM_UL_drawinglist,
|
|
||||||
ui.BIM_UL_topics,
|
ui.BIM_UL_topics,
|
||||||
ui.BIM_ADDON_preferences,
|
ui.BIM_ADDON_preferences,
|
||||||
]
|
]
|
||||||
@@ -162,49 +103,49 @@ if bpy is not None:
|
|||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
bpy.app.handlers.depsgraph_update_post.append(on_register)
|
bpy.app.handlers.depsgraph_update_post.append(on_register)
|
||||||
|
bpy.app.handlers.undo_pre.append(handler.undo_pre)
|
||||||
|
bpy.app.handlers.undo_post.append(handler.undo_post)
|
||||||
|
bpy.app.handlers.redo_pre.append(handler.redo_pre)
|
||||||
|
bpy.app.handlers.redo_post.append(handler.redo_post)
|
||||||
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
|
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
|
||||||
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
||||||
bpy.app.handlers.save_pre.append(handler.storeIdMap)
|
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
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.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
bpy.types.Material.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.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.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
||||||
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
|
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
|
bpy.types.PointLight.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
||||||
|
|
||||||
for module in modules.values():
|
for module in modules.values():
|
||||||
module.register()
|
module.register()
|
||||||
|
|
||||||
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
|
||||||
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
|
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
|
||||||
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
|
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_export.remove(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||||
del bpy.types.Scene.BIMProperties
|
del bpy.types.Scene.BIMProperties
|
||||||
del bpy.types.Scene.DocProperties
|
|
||||||
del bpy.types.Object.BIMObjectProperties
|
del bpy.types.Object.BIMObjectProperties
|
||||||
del bpy.types.Material.BIMObjectProperties
|
del bpy.types.Material.BIMObjectProperties
|
||||||
del bpy.types.Collection.BIMObjectProperties # Check if we need this
|
del bpy.types.Collection.BIMObjectProperties # Check if we need this
|
||||||
del bpy.types.Material.BIMMaterialProperties
|
del bpy.types.Material.BIMMaterialProperties
|
||||||
del bpy.types.Mesh.BIMMeshProperties
|
del bpy.types.Mesh.BIMMeshProperties
|
||||||
del bpy.types.Camera.BIMCameraProperties
|
del bpy.types.Curve.BIMMeshProperties
|
||||||
del bpy.types.TextCurve.BIMTextProperties
|
del bpy.types.Camera.BIMMeshProperties
|
||||||
|
del bpy.types.PointLight.BIMMeshProperties
|
||||||
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
|
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
|
||||||
|
|
||||||
for module in reversed(list(modules.values())):
|
for module in reversed(list(modules.values())):
|
||||||
module.unregister()
|
module.unregister()
|
||||||
|
|
||||||
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
|
||||||
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<link href="jsgantt.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<script src="jsgantt.js" type="text/javascript"></script>
|
||||||
|
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
|
||||||
|
g.setOptions({
|
||||||
|
vCaptionType: 'Complete', // Set to Show Caption : None,Caption,Resource,Duration,Complete,
|
||||||
|
vQuarterColWidth: 36,
|
||||||
|
vDateTaskDisplayFormat: 'day dd month yyyy', // Shown in tool tip box
|
||||||
|
vDayMajorDateDisplayFormat: 'mon yyyy - Week ww',// Set format to dates in the "Major" header of the "Day" view
|
||||||
|
vWeekMinorDateDisplayFormat: 'dd mon', // Set format to display dates in the "Minor" header of the "Week" view
|
||||||
|
vLang: 'en',
|
||||||
|
vShowTaskInfoLink: 1, // Show link in tool tip (0/1)
|
||||||
|
vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily
|
||||||
|
vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data.
|
||||||
|
vFormatArr: ['Day', 'Week', 'Month', 'Quarter'], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers,
|
||||||
|
vTotalHeight: 1000,
|
||||||
|
});
|
||||||
|
var json_data = `
|
||||||
|
{{{json_data}}}
|
||||||
|
`;
|
||||||
|
JSGantt.parseJSONString(json_data, g);
|
||||||
|
g.Draw();
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
* { stroke-linecap: round; }
|
* { stroke-linecap: round; stroke-linejoin: round; }
|
||||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
*[id] { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
||||||
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
.annotation { fill: none; 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; }
|
.hidden { stroke-dasharray: 3, 2; }
|
||||||
.solid {}
|
.solid {}
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
@@ -25,7 +25,7 @@
|
|||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<desc
|
<desc
|
||||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||||
<rect
|
<rect
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.07874;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
|
||||||
id="rect1036"
|
id="rect1036"
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -25,7 +25,7 @@
|
|||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<desc
|
<desc
|
||||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||||
<rect
|
<rect
|
||||||
y="3.1753578e-05"
|
y="3.1753578e-05"
|
||||||
x="-4.3300333e-05"
|
x="-4.3300333e-05"
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -25,7 +25,7 @@
|
|||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<desc
|
<desc
|
||||||
id="desc89610">/home/dion/Projects/IfcOpenShell/src/ifcblenderexport/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
id="desc89610">/home/dion/Projects/IfcOpenShell/src/blenderbim/titleblock.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual</desc>
|
||||||
<rect
|
<rect
|
||||||
y="3.1753578e-05"
|
y="3.1753578e-05"
|
||||||
x="-4.3300333e-05"
|
x="-4.3300333e-05"
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,187 @@
|
|||||||
|
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 ifcopenshell.api.spatial.data import Data as SpatialData
|
||||||
|
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)
|
||||||
|
|
||||||
|
def set_header(self):
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
|
||||||
|
def sync_object_placements_and_deletions(self):
|
||||||
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||||
|
to_delete = []
|
||||||
|
|
||||||
|
for ifc_definition_id, obj in IfcStore.id_map.items():
|
||||||
|
try:
|
||||||
|
self.sync_object_placement(obj)
|
||||||
|
self.sync_object_container(ifc_definition_id, obj)
|
||||||
|
except ReferenceError:
|
||||||
|
pass # The object is likely deleted
|
||||||
|
if self.should_delete(obj):
|
||||||
|
to_delete.append(ifc_definition_id)
|
||||||
|
|
||||||
|
SpatialData.purge()
|
||||||
|
|
||||||
|
for ifc_definition_id in to_delete:
|
||||||
|
product = self.file.by_id(ifc_definition_id)
|
||||||
|
IfcStore.unlink_element(product)
|
||||||
|
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
|
||||||
|
|
||||||
|
def sync_edited_objects(self):
|
||||||
|
for obj in IfcStore.edited_objs.copy():
|
||||||
|
if not obj:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
bpy.ops.bim.update_representation(obj=obj.name)
|
||||||
|
except ReferenceError:
|
||||||
|
pass # The object is likely deleted
|
||||||
|
IfcStore.edited_objs.clear()
|
||||||
|
|
||||||
|
def sync_object_placement(self, obj):
|
||||||
|
blender_matrix = np.matrix(obj.matrix_world)
|
||||||
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
if not hasattr(element, "ObjectPlacement"):
|
||||||
|
return
|
||||||
|
ifc_matrix = ifcopenshell.util.placement.get_local_placement(element.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 obj.BIMObjectProperties.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 sync_object_container(self, guid, obj):
|
||||||
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
element_collection = bpy.data.collections.get(obj.name)
|
||||||
|
|
||||||
|
if self.file.schema == "IFC2X3":
|
||||||
|
if element.is_a("IfcProject"):
|
||||||
|
return
|
||||||
|
elif element.is_a("IfcContext"):
|
||||||
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
(element.is_a("IfcElement") and element_collection)
|
||||||
|
or element.is_a("IfcSpatialStructureElement")
|
||||||
|
or element.is_a("IfcGrid")
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
parent_collection = [c for c in bpy.data.collections if c.children.get(element_collection.name)][0]
|
||||||
|
except:
|
||||||
|
return # Out of the spatial tree
|
||||||
|
else:
|
||||||
|
parent_collection = obj.users_collection[0]
|
||||||
|
|
||||||
|
|
||||||
|
parent_obj = bpy.data.objects.get(parent_collection.name)
|
||||||
|
if not parent_obj or not parent_obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
return
|
||||||
|
parent = self.file.by_id(parent_obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
|
if parent.is_a("IfcSpatialStructureElement") and not element.is_a("IfcSpatialStructureElement"):
|
||||||
|
if parent != ifcopenshell.util.element.get_container(element):
|
||||||
|
bpy.ops.bim.assign_container(relating_structure=parent.id(), related_element=obj.name)
|
||||||
|
elif parent != ifcopenshell.util.element.get_aggregate(element):
|
||||||
|
bpy.ops.bim.assign_object(relating_object=parent_obj.name, related_object=obj.name)
|
||||||
|
|
||||||
|
def should_delete(self, 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
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
import bpy
|
||||||
|
import json
|
||||||
|
import addon_utils
|
||||||
|
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
|
||||||
|
from ifcopenshell.api.material.data import Data as MaterialData
|
||||||
|
from ifcopenshell.api.type.data import Data as TypeData
|
||||||
|
|
||||||
|
|
||||||
|
global_subscription_owner = object()
|
||||||
|
|
||||||
|
|
||||||
|
def mode_callback(obj, data):
|
||||||
|
objects = bpy.context.selected_objects
|
||||||
|
if bpy.context.active_object:
|
||||||
|
objects += [bpy.context.active_object]
|
||||||
|
for obj in objects:
|
||||||
|
if (
|
||||||
|
obj.mode != "EDIT"
|
||||||
|
or not obj.data
|
||||||
|
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
||||||
|
or not obj.BIMObjectProperties.ifc_definition_id
|
||||||
|
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||||
|
):
|
||||||
|
return
|
||||||
|
if obj.data.BIMMeshProperties.ifc_definition_id:
|
||||||
|
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
|
if representation.RepresentationType in ["Tessellation", "Brep", "Annotation2D"]:
|
||||||
|
IfcStore.edited_objs.add(obj)
|
||||||
|
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
|
||||||
|
IfcStore.edited_objs.add(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def name_callback(obj, data):
|
||||||
|
try:
|
||||||
|
obj.name
|
||||||
|
except:
|
||||||
|
return # In case the object RNA is gone during an undo / redo operation
|
||||||
|
# Blender names are up to 63 UTF-8 bytes
|
||||||
|
if len(bytes(obj.name, "utf-8")) >= 63:
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(obj, bpy.types.Material):
|
||||||
|
if obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).Name = obj.name
|
||||||
|
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
MaterialData.load_materials()
|
||||||
|
if obj.BIMMaterialProperties.ifc_style_id:
|
||||||
|
IfcStore.get_file().by_id(obj.BIMMaterialProperties.ifc_style_id).Name = obj.name
|
||||||
|
return
|
||||||
|
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
||||||
|
return
|
||||||
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
if not element.is_a("IfcRoot"):
|
||||||
|
return
|
||||||
|
if element.is_a("IfcSpatialStructureElement") or (hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy):
|
||||||
|
collection = obj.users_collection[0]
|
||||||
|
collection.name = obj.name
|
||||||
|
if element.is_a("IfcGrid"):
|
||||||
|
axis_obj = IfcStore.get_element(element.UAxes[0].id())
|
||||||
|
axis_collection = axis_obj.users_collection[0]
|
||||||
|
grid_collection = None
|
||||||
|
for collection in bpy.data.collections:
|
||||||
|
if axis_collection.name in collection.children.keys():
|
||||||
|
grid_collection = collection
|
||||||
|
break
|
||||||
|
if grid_collection:
|
||||||
|
grid_collection.name = obj.name
|
||||||
|
if element.is_a("IfcTypeProduct"):
|
||||||
|
TypeData.purge()
|
||||||
|
element.Name = "/".join(obj.name.split("/")[1:])
|
||||||
|
AttributeData.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
|
|
||||||
|
def active_object_callback():
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
continue
|
||||||
|
stored_obj = IfcStore.get_element(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
if stored_obj and stored_obj != obj:
|
||||||
|
bpy.ops.bim.copy_class(obj=obj.name)
|
||||||
|
|
||||||
|
|
||||||
|
def subscribe_to(object, data_path, callback):
|
||||||
|
try:
|
||||||
|
subscribe_to = object.path_resolve(data_path, False)
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
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, value in modules.items():
|
||||||
|
try:
|
||||||
|
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
getattr(value, "prop").purge()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def loadIfcStore(scene):
|
||||||
|
IfcStore.purge()
|
||||||
|
ifc_file = IfcStore.get_file()
|
||||||
|
if not ifc_file:
|
||||||
|
return
|
||||||
|
IfcStore.get_schema()
|
||||||
|
IfcStore.reload_linked_elements()
|
||||||
|
purge_module_data()
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def undo_pre(scene):
|
||||||
|
IfcStore.update_undo_redo_stack_objects()
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def undo_post(scene):
|
||||||
|
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||||
|
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||||
|
IfcStore.undo()
|
||||||
|
purge_module_data()
|
||||||
|
IfcStore.update_undo_redo_stack_objects()
|
||||||
|
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def redo_pre(scene):
|
||||||
|
IfcStore.update_undo_redo_stack_objects()
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def redo_post(scene):
|
||||||
|
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||||
|
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||||
|
IfcStore.redo()
|
||||||
|
purge_module_data()
|
||||||
|
IfcStore.update_undo_redo_stack_objects()
|
||||||
|
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def ensureIfcExported(scene):
|
||||||
|
if IfcStore.get_file() and not bpy.context.scene.BIMProperties.ifc_file:
|
||||||
|
bpy.ops.export_ifc.bim("INVOKE_DEFAULT")
|
||||||
|
|
||||||
|
|
||||||
|
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 ifcopenshell.api.run(
|
||||||
|
"owner.add_application",
|
||||||
|
ifc,
|
||||||
|
version=get_application_version(),
|
||||||
|
application_full_name="BlenderBIM Add-on",
|
||||||
|
application_identifier="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]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def setDefaultProperties(scene):
|
||||||
|
global global_subscription_owner
|
||||||
|
active_object_key = bpy.types.LayerObjects, "active"
|
||||||
|
bpy.msgbus.subscribe_rna(
|
||||||
|
key=active_object_key, owner=global_subscription_owner, args=(), notify=active_object_callback
|
||||||
|
)
|
||||||
|
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")
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import bpy
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.attribute
|
||||||
|
from mathutils import geometry
|
||||||
|
from mathutils import Vector
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
def draw_attributes(props, layout, copy_operator=None):
|
||||||
|
for attribute in props:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
value = None
|
||||||
|
if attribute.data_type == "string":
|
||||||
|
row.prop(attribute, "string_value", text=attribute.name)
|
||||||
|
value = attribute.string_value
|
||||||
|
elif attribute.data_type == "boolean":
|
||||||
|
row.prop(attribute, "bool_value", text=attribute.name)
|
||||||
|
value = attribute.bool_value
|
||||||
|
elif attribute.data_type == "integer":
|
||||||
|
row.prop(attribute, "int_value", text=attribute.name)
|
||||||
|
value = attribute.int_value
|
||||||
|
elif attribute.data_type == "float":
|
||||||
|
row.prop(attribute, "float_value", text=attribute.name)
|
||||||
|
value = attribute.float_value
|
||||||
|
elif attribute.data_type == "enum":
|
||||||
|
row.prop(attribute, "enum_value", text=attribute.name)
|
||||||
|
value = attribute.enum_value
|
||||||
|
if attribute.is_optional:
|
||||||
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
|
if copy_operator:
|
||||||
|
op = row.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
||||||
|
op.data = json.dumps({"name": attribute.name, "value": value, "is_null": attribute.is_null})
|
||||||
|
|
||||||
|
|
||||||
|
def import_attributes(ifc_class, props, data, callback=None):
|
||||||
|
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||||
|
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||||
|
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
|
||||||
|
continue
|
||||||
|
new = props.add()
|
||||||
|
new.name = attribute.name()
|
||||||
|
new.is_null = data[attribute.name()] is None
|
||||||
|
new.is_optional = attribute.optional()
|
||||||
|
new.data_type = data_type if isinstance(data_type, str) else ""
|
||||||
|
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
|
||||||
|
if is_handled_by_callback:
|
||||||
|
pass # Our job is done
|
||||||
|
elif is_handled_by_callback is False:
|
||||||
|
props.remove(len(props) - 1)
|
||||||
|
elif data_type == "string":
|
||||||
|
new.string_value = "" if new.is_null else data[attribute.name()]
|
||||||
|
elif data_type == "boolean":
|
||||||
|
new.bool_value = False if new.is_null else data[attribute.name()]
|
||||||
|
elif data_type == "integer":
|
||||||
|
new.int_value = 0 if new.is_null else data[attribute.name()]
|
||||||
|
elif data_type == "float":
|
||||||
|
new.float_value = 0.0 if new.is_null else data[attribute.name()]
|
||||||
|
elif data_type == "enum":
|
||||||
|
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||||
|
if data[attribute.name()]:
|
||||||
|
new.enum_value = data[attribute.name()]
|
||||||
|
|
||||||
|
|
||||||
|
def export_attributes(props, callback=None):
|
||||||
|
attributes = {}
|
||||||
|
for attribute in props:
|
||||||
|
is_handled_by_callback = callback(attributes, attribute) if callback else False
|
||||||
|
if attribute.is_null:
|
||||||
|
attributes[attribute.name] = None
|
||||||
|
elif is_handled_by_callback:
|
||||||
|
pass # Our job is done
|
||||||
|
elif attribute.data_type == "string":
|
||||||
|
attributes[attribute.name] = attribute.string_value
|
||||||
|
elif attribute.data_type == "boolean":
|
||||||
|
attributes[attribute.name] = attribute.bool_value
|
||||||
|
elif attribute.data_type == "integer":
|
||||||
|
attributes[attribute.name] = attribute.int_value
|
||||||
|
elif attribute.data_type == "float":
|
||||||
|
attributes[attribute.name] = attribute.float_value
|
||||||
|
elif attribute.data_type == "enum":
|
||||||
|
attributes[attribute.name] = attribute.enum_value
|
||||||
|
return attributes
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import bpy
|
||||||
|
import uuid
|
||||||
|
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
|
||||||
|
library_path = ""
|
||||||
|
library_file = None
|
||||||
|
element_listeners = set()
|
||||||
|
undo_redo_stack_objects = set()
|
||||||
|
current_transaction = ""
|
||||||
|
last_transaction = ""
|
||||||
|
history = []
|
||||||
|
future = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def purge():
|
||||||
|
IfcStore.path = ""
|
||||||
|
IfcStore.file = None
|
||||||
|
IfcStore.schema = None
|
||||||
|
IfcStore.id_map = {}
|
||||||
|
IfcStore.guid_map = {}
|
||||||
|
IfcStore.edited_objs = set()
|
||||||
|
IfcStore.pset_template_path = ""
|
||||||
|
IfcStore.pset_template_file = None
|
||||||
|
IfcStore.library_path = ""
|
||||||
|
IfcStore.library_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 get_element(id_or_guid):
|
||||||
|
if isinstance(id_or_guid, int):
|
||||||
|
map_object = IfcStore.id_map
|
||||||
|
else:
|
||||||
|
map_object = IfcStore.guid_map
|
||||||
|
try:
|
||||||
|
obj = map_object[id_or_guid]
|
||||||
|
obj.name # In case the object has been deleted, this triggers an exception
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_element_listener(callback):
|
||||||
|
IfcStore.element_listeners.add(callback)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_undo_redo_stack_objects():
|
||||||
|
"""Keeps track of selected object names, typically during undo and redo
|
||||||
|
|
||||||
|
When any Blender object is stored outside a Blender PointerProperty, such as
|
||||||
|
in a regular Python list, there is the likely probability that the object
|
||||||
|
will be invalidated when undo or redo occurs. Object invalidation seems to
|
||||||
|
only occur for selected objects either pre/post undo/redo event, including
|
||||||
|
selected objects for consecutive undo/redos.
|
||||||
|
|
||||||
|
So if I first select o1, then o2, then o3, then press undo, o3 will be
|
||||||
|
invalidated. If instead I press undo twice, o3 and o2 will be invalidated.
|
||||||
|
"""
|
||||||
|
if bpy.context.active_object:
|
||||||
|
objects = set([o.name for o in bpy.context.selected_objects + [bpy.context.active_object]])
|
||||||
|
else:
|
||||||
|
objects = set([o.name for o in bpy.context.selected_objects])
|
||||||
|
IfcStore.undo_redo_stack_objects |= objects
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reload_linked_elements(objects=None):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
if objects is None:
|
||||||
|
objects = bpy.data.objects
|
||||||
|
|
||||||
|
for obj in objects:
|
||||||
|
if not obj:
|
||||||
|
continue
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
continue
|
||||||
|
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
data = {"id": element.id(), "obj": obj.name}
|
||||||
|
if hasattr(element, "GlobalId"):
|
||||||
|
data["guid"] = element.GlobalId
|
||||||
|
IfcStore.commit_link_element(data)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
for listener in IfcStore.element_listeners:
|
||||||
|
listener(element, obj)
|
||||||
|
|
||||||
|
if IfcStore.history:
|
||||||
|
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
|
||||||
|
IfcStore.history[-1]["operations"].append(
|
||||||
|
{"rollback": IfcStore.rollback_link_element, "commit": IfcStore.commit_link_element, "data": data}
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rollback_link_element(data):
|
||||||
|
del IfcStore.id_map[data["id"]]
|
||||||
|
if data["guid"]:
|
||||||
|
del IfcStore.guid_map[data["guid"]]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def commit_link_element(data):
|
||||||
|
obj = bpy.data.objects.get(data["obj"])
|
||||||
|
IfcStore.id_map[data["id"]] = obj
|
||||||
|
if data["guid"]:
|
||||||
|
IfcStore.guid_map[data["guid"]] = obj
|
||||||
|
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=None, obj=None):
|
||||||
|
if element is None:
|
||||||
|
try:
|
||||||
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if element:
|
||||||
|
del IfcStore.id_map[element.id()]
|
||||||
|
else:
|
||||||
|
del IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if element and hasattr(element, "GlobalId"):
|
||||||
|
del IfcStore.guid_map[element.GlobalId]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if obj:
|
||||||
|
obj.BIMObjectProperties.ifc_definition_id = 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_ifc_operator(operator, context):
|
||||||
|
is_top_level_operator = not bool(IfcStore.current_transaction)
|
||||||
|
|
||||||
|
if is_top_level_operator:
|
||||||
|
IfcStore.begin_transaction(operator)
|
||||||
|
IfcStore.get_file().begin_transaction()
|
||||||
|
# This empty transaction ensures that each operator has at least one transaction
|
||||||
|
IfcStore.add_transaction_operation(operator, rollback=lambda data: True, commit=lambda data: True)
|
||||||
|
else:
|
||||||
|
operator.transaction_key = IfcStore.current_transaction
|
||||||
|
|
||||||
|
result = getattr(operator, "_execute")(context)
|
||||||
|
|
||||||
|
if is_top_level_operator:
|
||||||
|
IfcStore.get_file().end_transaction()
|
||||||
|
IfcStore.add_transaction_operation(
|
||||||
|
operator, rollback=lambda d: IfcStore.get_file().undo(), commit=lambda d: IfcStore.get_file().redo()
|
||||||
|
)
|
||||||
|
IfcStore.end_transaction(operator)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def begin_transaction(operator):
|
||||||
|
IfcStore.undo_redo_stack_objects = set()
|
||||||
|
IfcStore.current_transaction = str(uuid.uuid4())
|
||||||
|
operator.transaction_key = IfcStore.current_transaction
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def end_transaction(operator):
|
||||||
|
IfcStore.current_transaction = ""
|
||||||
|
operator.transaction_key = ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_transaction_operation(operator, rollback=None, commit=None):
|
||||||
|
key = getattr(operator, "transaction_key", None)
|
||||||
|
data = getattr(operator, "transaction_data", None)
|
||||||
|
bpy.context.scene.BIMProperties.last_transaction = key
|
||||||
|
IfcStore.last_transaction = key
|
||||||
|
rollback = rollback or getattr(operator, "rollback", lambda data: True)
|
||||||
|
commit = commit or getattr(operator, "commit", lambda data: True)
|
||||||
|
if IfcStore.history and IfcStore.history[-1]["key"] == key:
|
||||||
|
IfcStore.history[-1]["operations"].append({"rollback": rollback, "commit": commit, "data": data})
|
||||||
|
else:
|
||||||
|
IfcStore.history.append(
|
||||||
|
{"key": key, "operations": [{"rollback": rollback, "commit": commit, "data": data}]}
|
||||||
|
)
|
||||||
|
IfcStore.future = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def undo():
|
||||||
|
if not IfcStore.history:
|
||||||
|
return
|
||||||
|
event = IfcStore.history.pop()
|
||||||
|
for transaction in event["operations"][::-1]:
|
||||||
|
transaction["rollback"](transaction["data"])
|
||||||
|
IfcStore.future.append(event)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def redo():
|
||||||
|
if not IfcStore.future:
|
||||||
|
return
|
||||||
|
event = IfcStore.future.pop()
|
||||||
|
for transaction in event["operations"]:
|
||||||
|
transaction["commit"](transaction["data"])
|
||||||
|
IfcStore.history.append(event)
|
||||||
@@ -1,47 +1,57 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import blenderbim.bim.module.aggregate.assign_object as assign_object
|
import ifcopenshell.api
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.aggregate.data import Data
|
from ifcopenshell.api.aggregate.data import Data
|
||||||
|
|
||||||
|
|
||||||
class AssignObject(bpy.types.Operator):
|
class AssignObject(bpy.types.Operator):
|
||||||
bl_idname = "bim.assign_object"
|
bl_idname = "bim.assign_object"
|
||||||
bl_label = "Assign Object"
|
bl_label = "Assign Object"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
relating_object: bpy.props.StringProperty()
|
relating_object: bpy.props.StringProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
|
related_objects = (
|
||||||
props = related_object.BIMObjectProperties
|
[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 self.relating_object else props.relating_object
|
)
|
||||||
|
relating_object = bpy.data.objects.get(self.relating_object)
|
||||||
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
|
if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
product = self.file.by_id(props.ifc_definition_id)
|
for related_object in related_objects:
|
||||||
assign_object.Usecase(
|
oprops = related_object.BIMObjectProperties
|
||||||
self.file,
|
product = self.file.by_id(oprops.ifc_definition_id)
|
||||||
{
|
ifcopenshell.api.run(
|
||||||
"product": product,
|
"aggregate.assign_object",
|
||||||
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
|
self.file,
|
||||||
},
|
**{
|
||||||
).execute()
|
"product": product,
|
||||||
bpy.ops.bim.edit_object_placement(obj=related_object.name)
|
"relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id),
|
||||||
Data.load(props.ifc_definition_id)
|
},
|
||||||
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
|
)
|
||||||
|
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)
|
spatial_collection = bpy.data.collections.get(related_object.name)
|
||||||
relating_collection = bpy.data.collections.get(relating_object.name)
|
relating_collection = bpy.data.collections.get(relating_object.name)
|
||||||
if spatial_collection:
|
if spatial_collection:
|
||||||
self.remove_collection(bpy.context.scene.collection, spatial_collection)
|
self.remove_collection(bpy.context.scene.collection, spatial_collection)
|
||||||
for collection in bpy.data.collections:
|
for collection in bpy.data.collections:
|
||||||
if collection == relating_collection:
|
if collection == relating_collection:
|
||||||
collection.children.link(spatial_collection)
|
if not collection.children.get(spatial_collection.name):
|
||||||
continue
|
collection.children.link(spatial_collection)
|
||||||
self.remove_collection(collection, spatial_collection)
|
continue
|
||||||
else:
|
self.remove_collection(collection, spatial_collection)
|
||||||
for collection in related_object.users_collection:
|
else:
|
||||||
collection.objects.unlink(related_object)
|
for collection in related_object.users_collection:
|
||||||
relating_collection.objects.link(related_object)
|
if collection.name.startswith("Ifc"):
|
||||||
|
collection.objects.unlink(related_object)
|
||||||
|
relating_collection.objects.link(related_object)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def remove_collection(self, parent, child):
|
def remove_collection(self, parent, child):
|
||||||
@@ -54,6 +64,7 @@ class AssignObject(bpy.types.Operator):
|
|||||||
class EnableEditingAggregate(bpy.types.Operator):
|
class EnableEditingAggregate(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_aggregate"
|
bl_idname = "bim.enable_editing_aggregate"
|
||||||
bl_label = "Enable Editing Aggregate"
|
bl_label = "Enable Editing Aggregate"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.context.active_object.BIMObjectProperties.relating_object = None
|
bpy.context.active_object.BIMObjectProperties.relating_object = None
|
||||||
@@ -65,6 +76,7 @@ class DisableEditingAggregate(bpy.types.Operator):
|
|||||||
bl_idname = "bim.disable_editing_aggregate"
|
bl_idname = "bim.disable_editing_aggregate"
|
||||||
bl_label = "Disable Editing Aggregate"
|
bl_label = "Disable Editing Aggregate"
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
@@ -75,9 +87,13 @@ class DisableEditingAggregate(bpy.types.Operator):
|
|||||||
class AddAggregate(bpy.types.Operator):
|
class AddAggregate(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_aggregate"
|
bl_idname = "bim.add_aggregate"
|
||||||
bl_label = "Add Aggregate"
|
bl_label = "Add Aggregate"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
|
aggregate_collection = bpy.data.collections.new("IfcElementAssembly/Assembly")
|
||||||
bpy.context.scene.collection.children.link(aggregate_collection)
|
bpy.context.scene.collection.children.link(aggregate_collection)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from blenderbim.bim.module.aggregate.data import Data
|
from ifcopenshell.api.aggregate.data import Data
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
@@ -16,10 +16,12 @@ class BIM_PT_aggregate(Panel):
|
|||||||
props = context.active_object.BIMObjectProperties
|
props = context.active_object.BIMObjectProperties
|
||||||
if not props.ifc_definition_id:
|
if not props.ifc_definition_id:
|
||||||
return False
|
return False
|
||||||
|
if not IfcStore.get_element(props.ifc_definition_id):
|
||||||
|
return False
|
||||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||||
return False
|
return False
|
||||||
if props.ifc_definition_id not in Data.products:
|
if props.ifc_definition_id not in Data.products:
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(IfcStore.get_file(), props.ifc_definition_id)
|
||||||
if not Data.products[props.ifc_definition_id]:
|
if not Data.products[props.ifc_definition_id]:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -29,12 +31,13 @@ class BIM_PT_aggregate(Panel):
|
|||||||
if not props.ifc_definition_id:
|
if not props.ifc_definition_id:
|
||||||
return
|
return
|
||||||
if props.ifc_definition_id not in Data.products:
|
if props.ifc_definition_id not in Data.products:
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(IfcStore.get_file(), props.ifc_definition_id)
|
||||||
|
|
||||||
if props.is_editing_aggregate:
|
if props.is_editing_aggregate:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "relating_object", text="")
|
row.prop(props, "relating_object", text="")
|
||||||
row.operator("bim.assign_object", icon="CHECKMARK", 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="")
|
row.operator("bim.disable_editing_aggregate", icon="X", text="")
|
||||||
else:
|
else:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
|
import ifcopenshell.api
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.attribute.data import Data
|
from ifcopenshell.api.attribute.data import Data
|
||||||
|
|
||||||
|
|
||||||
class EnableEditingAttributes(bpy.types.Operator):
|
class EnableEditingAttributes(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_attributes"
|
bl_idname = "bim.enable_editing_attributes"
|
||||||
bl_label = "Enable Editing Attributes"
|
bl_label = "Enable Editing Attributes"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ class EnableEditingAttributes(bpy.types.Operator):
|
|||||||
props.attributes.remove(0)
|
props.attributes.remove(0)
|
||||||
for attribute in Data.products[oprops.ifc_definition_id]:
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
new = props.attributes.add()
|
new = props.attributes.add()
|
||||||
if attribute["type"] == "entity":
|
if attribute["type"] == "entity" or (attribute["type"] == "list" and attribute["list_type"] == "entity"):
|
||||||
continue
|
continue
|
||||||
new.name = attribute["name"]
|
new.name = attribute["name"]
|
||||||
new.is_null = attribute["is_null"]
|
new.is_null = attribute["is_null"]
|
||||||
@@ -33,7 +34,7 @@ class EnableEditingAttributes(bpy.types.Operator):
|
|||||||
elif attribute["type"] == "integer":
|
elif attribute["type"] == "integer":
|
||||||
new.int_value = attribute["value"] or 0
|
new.int_value = attribute["value"] or 0
|
||||||
elif attribute["type"] == "float":
|
elif attribute["type"] == "float":
|
||||||
new.float_value = attribute["value"] or 0.
|
new.float_value = attribute["value"] or 0.0
|
||||||
elif attribute["type"] == "enum":
|
elif attribute["type"] == "enum":
|
||||||
new.enum_items = json.dumps(attribute["enum_items"])
|
new.enum_items = json.dumps(attribute["enum_items"])
|
||||||
if attribute["value"]:
|
if attribute["value"]:
|
||||||
@@ -45,6 +46,7 @@ class EnableEditingAttributes(bpy.types.Operator):
|
|||||||
class DisableEditingAttributes(bpy.types.Operator):
|
class DisableEditingAttributes(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_editing_attributes"
|
bl_idname = "bim.disable_editing_attributes"
|
||||||
bl_label = "Disable Editing Attributes"
|
bl_label = "Disable Editing Attributes"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
@@ -61,10 +63,14 @@ class DisableEditingAttributes(bpy.types.Operator):
|
|||||||
class EditAttributes(bpy.types.Operator):
|
class EditAttributes(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_attributes"
|
bl_idname = "bim.edit_attributes"
|
||||||
bl_label = "Edit Attributes"
|
bl_label = "Edit Attributes"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if self.obj_type == "Object":
|
if self.obj_type == "Object":
|
||||||
obj = bpy.data.objects.get(self.obj)
|
obj = bpy.data.objects.get(self.obj)
|
||||||
@@ -95,17 +101,16 @@ class EditAttributes(bpy.types.Operator):
|
|||||||
elif attribute["type"] == "enum":
|
elif attribute["type"] == "enum":
|
||||||
attributes[attribute["name"]] = blender_attribute.enum_value
|
attributes[attribute["name"]] = blender_attribute.enum_value
|
||||||
product = self.file.by_id(oprops.ifc_definition_id)
|
product = self.file.by_id(oprops.ifc_definition_id)
|
||||||
edit_attributes.Usecase(self.file, {
|
ifcopenshell.api.run(
|
||||||
"product": product,
|
"attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes}
|
||||||
"attributes": attributes
|
)
|
||||||
}).execute()
|
|
||||||
if "Name" in attributes:
|
if "Name" in attributes:
|
||||||
new_name = "{}/{}".format(product.is_a(), product.Name or "Unnamed")
|
new_name = "{}/{}".format(product.is_a(), product.Name or "Unnamed")
|
||||||
collection = bpy.data.collections.get(obj.name)
|
collection = bpy.data.collections.get(obj.name)
|
||||||
if collection:
|
if collection:
|
||||||
collection.name = new_name
|
collection.name = new_name
|
||||||
obj.name = new_name
|
obj.name = new_name
|
||||||
Data.load(oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
|
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -113,6 +118,7 @@ class EditAttributes(bpy.types.Operator):
|
|||||||
class GenerateGlobalId(bpy.types.Operator):
|
class GenerateGlobalId(bpy.types.Operator):
|
||||||
bl_idname = "bim.generate_global_id"
|
bl_idname = "bim.generate_global_id"
|
||||||
bl_label = "Regenerate GlobalId"
|
bl_label = "Regenerate GlobalId"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
|
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import blenderbim.bim.schema # refactor
|
|
||||||
from blenderbim.bim.module.material.data import Data
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from blenderbim.bim.module.attribute.data import Data
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from ifcopenshell.api.attribute.data import Data
|
||||||
|
|
||||||
|
|
||||||
def draw_ui(context, layout, obj_type):
|
def draw_ui(context, layout, obj_type):
|
||||||
@@ -7,14 +8,14 @@ def draw_ui(context, layout, obj_type):
|
|||||||
oprops = obj.BIMObjectProperties
|
oprops = obj.BIMObjectProperties
|
||||||
props = obj.BIMAttributeProperties
|
props = obj.BIMAttributeProperties
|
||||||
if oprops.ifc_definition_id not in Data.products:
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
|
|
||||||
if props.is_editing_attributes:
|
if props.is_editing_attributes:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
|
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
op.obj = obj.name
|
op.obj = obj.name
|
||||||
op = row.operator("bim.disable_editing_attributes", icon="X", text="")
|
op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
op.obj = obj.name
|
op.obj = obj.name
|
||||||
|
|
||||||
@@ -41,15 +42,17 @@ def draw_ui(context, layout, obj_type):
|
|||||||
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
|
icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON",
|
||||||
text="",
|
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:
|
else:
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
|
op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit")
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
op.obj = obj.name
|
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]:
|
for attribute in Data.products[oprops.ifc_definition_id]:
|
||||||
if attribute["value"] is None or attribute["type"] == "entity":
|
if attribute["value"] is None or attribute["type"] == "entity":
|
||||||
continue
|
continue
|
||||||
@@ -71,6 +74,8 @@ class BIM_PT_object_attributes(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||||
|
return False
|
||||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@@ -78,7 +83,7 @@ class BIM_PT_object_attributes(Panel):
|
|||||||
|
|
||||||
|
|
||||||
class BIM_PT_material_attributes(Panel):
|
class BIM_PT_material_attributes(Panel):
|
||||||
bl_label = "IFC Attributes"
|
bl_label = "IFC Material Attributes"
|
||||||
bl_idname = "BIM_PT_material_attributes"
|
bl_idname = "BIM_PT_material_attributes"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
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"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
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"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
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"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
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"}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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,5 +1,5 @@
|
|||||||
import bcf
|
import bcf
|
||||||
import bcf.bcfxml
|
import bcf.v2.bcfxml
|
||||||
|
|
||||||
class BcfStore:
|
class BcfStore:
|
||||||
bcfxml = None
|
bcfxml = None
|
||||||
@@ -7,5 +7,5 @@ class BcfStore:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_bcfxml():
|
def get_bcfxml():
|
||||||
if not BcfStore.bcfxml:
|
if not BcfStore.bcfxml:
|
||||||
BcfStore.bcfxml = bcf.bcfxml.BcfXml()
|
BcfStore.bcfxml = bcf.v2.bcfxml.BcfXml()
|
||||||
return BcfStore.bcfxml
|
return BcfStore.bcfxml
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import bcf
|
import bcf
|
||||||
|
import bcf.bcfxml
|
||||||
|
import bcf.v2.data
|
||||||
from . import bcfstore
|
from . import bcfstore
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from math import radians, degrees, atan, tan, cos, sin
|
from math import radians, degrees, atan, tan, cos, sin
|
||||||
@@ -9,6 +11,7 @@ from mathutils import Vector, Matrix, Euler, geometry
|
|||||||
class NewBcfProject(bpy.types.Operator):
|
class NewBcfProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.new_bcf_project"
|
bl_idname = "bim.new_bcf_project"
|
||||||
bl_label = "New BCF Project"
|
bl_label = "New BCF Project"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.context.scene.BCFProperties.is_loaded = False
|
bpy.context.scene.BCFProperties.is_loaded = False
|
||||||
@@ -22,13 +25,16 @@ class NewBcfProject(bpy.types.Operator):
|
|||||||
class LoadBcfProject(bpy.types.Operator):
|
class LoadBcfProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_bcf_project"
|
bl_idname = "bim.load_bcf_project"
|
||||||
bl_label = "Load BCF Project"
|
bl_label = "Load BCF Project"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.context.scene.BCFProperties.is_loaded = False
|
bpy.context.scene.BCFProperties.is_loaded = False
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
||||||
if self.filepath:
|
if self.filepath:
|
||||||
bcfxml.get_project(self.filepath)
|
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.get_project()
|
||||||
bpy.context.scene.BCFProperties.name = bcfxml.project.name
|
bpy.context.scene.BCFProperties.name = bcfxml.project.name
|
||||||
bpy.ops.bim.load_bcf_topics()
|
bpy.ops.bim.load_bcf_topics()
|
||||||
bpy.context.scene.BCFProperties.is_loaded = True
|
bpy.context.scene.BCFProperties.is_loaded = True
|
||||||
@@ -42,6 +48,7 @@ class LoadBcfProject(bpy.types.Operator):
|
|||||||
class LoadBcfTopics(bpy.types.Operator):
|
class LoadBcfTopics(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_bcf_topics"
|
bl_idname = "bim.load_bcf_topics"
|
||||||
bl_label = "Load BCF Topics"
|
bl_label = "Load BCF Topics"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -59,6 +66,7 @@ class LoadBcfTopics(bpy.types.Operator):
|
|||||||
class LoadBcfTopic(bpy.types.Operator):
|
class LoadBcfTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_bcf_topic"
|
bl_idname = "bim.load_bcf_topic"
|
||||||
bl_label = "Load BCF Topics"
|
bl_label = "Load BCF Topics"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
topic_guid: bpy.props.StringProperty()
|
topic_guid: bpy.props.StringProperty()
|
||||||
topic_index: bpy.props.IntProperty()
|
topic_index: bpy.props.IntProperty()
|
||||||
|
|
||||||
@@ -129,6 +137,7 @@ class LoadBcfTopic(bpy.types.Operator):
|
|||||||
class LoadBcfComments(bpy.types.Operator):
|
class LoadBcfComments(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_bcf_comments"
|
bl_idname = "bim.load_bcf_comments"
|
||||||
bl_label = "Load BCF Comments"
|
bl_label = "Load BCF Comments"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
topic_guid: bpy.props.StringProperty()
|
topic_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -157,6 +166,7 @@ class LoadBcfComments(bpy.types.Operator):
|
|||||||
class EditBcfProjectName(bpy.types.Operator):
|
class EditBcfProjectName(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_project_name"
|
bl_idname = "bim.edit_bcf_project_name"
|
||||||
bl_label = "Edit BCF Project Name"
|
bl_label = "Edit BCF Project Name"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -168,6 +178,7 @@ class EditBcfProjectName(bpy.types.Operator):
|
|||||||
class EditBcfAuthor(bpy.types.Operator):
|
class EditBcfAuthor(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_author"
|
bl_idname = "bim.edit_bcf_author"
|
||||||
bl_label = "Edit BCF Author"
|
bl_label = "Edit BCF Author"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -178,6 +189,7 @@ class EditBcfAuthor(bpy.types.Operator):
|
|||||||
class EditBcfTopicName(bpy.types.Operator):
|
class EditBcfTopicName(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_topic_name"
|
bl_idname = "bim.edit_bcf_topic_name"
|
||||||
bl_label = "Edit BCF Topic Name"
|
bl_label = "Edit BCF Topic Name"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
@@ -192,6 +204,7 @@ class EditBcfTopicName(bpy.types.Operator):
|
|||||||
class EditBcfTopic(bpy.types.Operator):
|
class EditBcfTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_topic"
|
bl_idname = "bim.edit_bcf_topic"
|
||||||
bl_label = "Edit BCF Topic"
|
bl_label = "Edit BCF Topic"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
@@ -216,6 +229,7 @@ class EditBcfTopic(bpy.types.Operator):
|
|||||||
class SaveBcfProject(bpy.types.Operator):
|
class SaveBcfProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.save_bcf_project"
|
bl_idname = "bim.save_bcf_project"
|
||||||
bl_label = "Save BCF Project"
|
bl_label = "Save BCF Project"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -231,6 +245,7 @@ class SaveBcfProject(bpy.types.Operator):
|
|||||||
class AddBcfTopic(bpy.types.Operator):
|
class AddBcfTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_topic"
|
bl_idname = "bim.add_bcf_topic"
|
||||||
bl_label = "Add BCF Topic"
|
bl_label = "Add BCF Topic"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -244,13 +259,14 @@ class AddBcfTopic(bpy.types.Operator):
|
|||||||
class AddBcfBimSnippet(bpy.types.Operator):
|
class AddBcfBimSnippet(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_bim_snippet"
|
bl_idname = "bim.add_bcf_bim_snippet"
|
||||||
bl_label = "Add BCF BIM Snippet"
|
bl_label = "Add BCF BIM Snippet"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
blender_topic = props.topics[props.active_topic_index]
|
blender_topic = props.topics[props.active_topic_index]
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
bim_snippet = bcf.data.BimSnippet()
|
bim_snippet = bcf.v2.data.BimSnippet()
|
||||||
bim_snippet.reference = blender_topic.bim_snippet_reference
|
bim_snippet.reference = blender_topic.bim_snippet_reference
|
||||||
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
|
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
|
||||||
bim_snippet.snippet_type = blender_topic.bim_snippet_type
|
bim_snippet.snippet_type = blender_topic.bim_snippet_type
|
||||||
@@ -262,6 +278,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
|
|||||||
class AddBcfRelatedTopic(bpy.types.Operator):
|
class AddBcfRelatedTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_related_topic"
|
bl_idname = "bim.add_bcf_related_topic"
|
||||||
bl_label = "Add BCF Related Topic"
|
bl_label = "Add BCF Related Topic"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -270,7 +287,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
|
|||||||
related_topic = None
|
related_topic = None
|
||||||
for topic in bcfxml.topics.values():
|
for topic in bcfxml.topics.values():
|
||||||
if topic.title == blender_topic.related_topic:
|
if topic.title == blender_topic.related_topic:
|
||||||
related_topic = bcf.data.RelatedTopic()
|
related_topic = bcf.v2.data.RelatedTopic()
|
||||||
related_topic.guid = topic.guid
|
related_topic.guid = topic.guid
|
||||||
break
|
break
|
||||||
if not related_topic:
|
if not related_topic:
|
||||||
@@ -285,13 +302,14 @@ class AddBcfRelatedTopic(bpy.types.Operator):
|
|||||||
class AddBcfHeaderFile(bpy.types.Operator):
|
class AddBcfHeaderFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_header_file"
|
bl_idname = "bim.add_bcf_header_file"
|
||||||
bl_label = "Add BCF Header File"
|
bl_label = "Add BCF Header File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
blender_topic = props.topics[props.active_topic_index]
|
blender_topic = props.topics[props.active_topic_index]
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
header_file = bcf.data.HeaderFile()
|
header_file = bcf.v2.data.HeaderFile()
|
||||||
header_file.reference = blender_topic.file_reference
|
header_file.reference = blender_topic.file_reference
|
||||||
if not os.path.exists(header_file.reference):
|
if not os.path.exists(header_file.reference):
|
||||||
header_file.filename = header_file.reference
|
header_file.filename = header_file.reference
|
||||||
@@ -307,6 +325,7 @@ class AddBcfHeaderFile(bpy.types.Operator):
|
|||||||
class ViewBcfTopic(bpy.types.Operator):
|
class ViewBcfTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.view_bcf_topic"
|
bl_idname = "bim.view_bcf_topic"
|
||||||
bl_label = "Get BCF Topic"
|
bl_label = "Get BCF Topic"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
topic_guid: bpy.props.StringProperty()
|
topic_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -319,6 +338,7 @@ class ViewBcfTopic(bpy.types.Operator):
|
|||||||
class AddBcfViewpoint(bpy.types.Operator):
|
class AddBcfViewpoint(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_viewpoint"
|
bl_idname = "bim.add_bcf_viewpoint"
|
||||||
bl_label = "Add BCF Viewpoint"
|
bl_label = "Add BCF Viewpoint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if not bpy.context.scene.camera:
|
if not bpy.context.scene.camera:
|
||||||
@@ -327,14 +347,14 @@ class AddBcfViewpoint(bpy.types.Operator):
|
|||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
blender_topic = props.topics[props.active_topic_index]
|
blender_topic = props.topics[props.active_topic_index]
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
viewpoint = bcf.data.Viewpoint()
|
viewpoint = bcf.v2.data.Viewpoint()
|
||||||
|
|
||||||
if bpy.context.scene.camera.data.type == "ORTHO":
|
if bpy.context.scene.camera.data.type == "ORTHO":
|
||||||
camera = bcf.data.OrthogonalCamera()
|
camera = bcf.v2.data.OrthogonalCamera()
|
||||||
camera.view_to_world_scale = bpy.context.scene.camera.data.ortho_scale
|
camera.view_to_world_scale = bpy.context.scene.camera.data.ortho_scale
|
||||||
viewpoint.orthogonal_camera = camera
|
viewpoint.orthogonal_camera = camera
|
||||||
elif bpy.context.scene.camera.data.type == "PERSP":
|
elif bpy.context.scene.camera.data.type == "PERSP":
|
||||||
camera = bcf.data.PerspectiveCamera()
|
camera = bcf.v2.data.PerspectiveCamera()
|
||||||
camera.field_of_view = degrees(bpy.context.scene.camera.data.angle)
|
camera.field_of_view = degrees(bpy.context.scene.camera.data.angle)
|
||||||
viewpoint.perspective_camera = camera
|
viewpoint.perspective_camera = camera
|
||||||
camera.camera_view_point.x = bpy.context.scene.camera.location.x
|
camera.camera_view_point.x = bpy.context.scene.camera.location.x
|
||||||
@@ -366,6 +386,7 @@ class AddBcfViewpoint(bpy.types.Operator):
|
|||||||
class RemoveBcfViewpoint(bpy.types.Operator):
|
class RemoveBcfViewpoint(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_viewpoint"
|
bl_idname = "bim.remove_bcf_viewpoint"
|
||||||
bl_label = "Remove BCF Viewpoint"
|
bl_label = "Remove BCF Viewpoint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -381,6 +402,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
|
|||||||
class RemoveBcfFile(bpy.types.Operator):
|
class RemoveBcfFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_file"
|
bl_idname = "bim.remove_bcf_file"
|
||||||
bl_label = "Remove BCF File"
|
bl_label = "Remove BCF File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -396,6 +418,7 @@ class RemoveBcfFile(bpy.types.Operator):
|
|||||||
class AddBcfReferenceLink(bpy.types.Operator):
|
class AddBcfReferenceLink(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_reference_link"
|
bl_idname = "bim.add_bcf_reference_link"
|
||||||
bl_label = "Add BCF Reference Link"
|
bl_label = "Add BCF Reference Link"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -414,6 +437,7 @@ class AddBcfReferenceLink(bpy.types.Operator):
|
|||||||
class AddBcfDocumentReference(bpy.types.Operator):
|
class AddBcfDocumentReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_document_reference"
|
bl_idname = "bim.add_bcf_document_reference"
|
||||||
bl_label = "Add BCF Document Reference"
|
bl_label = "Add BCF Document Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -422,7 +446,7 @@ class AddBcfDocumentReference(bpy.types.Operator):
|
|||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
if not blender_topic.document_reference:
|
if not blender_topic.document_reference:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
document_reference = bcf.data.DocumentReference()
|
document_reference = bcf.v2.data.DocumentReference()
|
||||||
document_reference.referenced_document = blender_topic.document_reference
|
document_reference.referenced_document = blender_topic.document_reference
|
||||||
document_reference.description = blender_topic.document_reference_description or None
|
document_reference.description = blender_topic.document_reference_description or None
|
||||||
bcfxml.add_document_reference(topic, document_reference)
|
bcfxml.add_document_reference(topic, document_reference)
|
||||||
@@ -435,6 +459,7 @@ class AddBcfDocumentReference(bpy.types.Operator):
|
|||||||
class AddBcfLabel(bpy.types.Operator):
|
class AddBcfLabel(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_label"
|
bl_idname = "bim.add_bcf_label"
|
||||||
bl_label = "Add BCF Label"
|
bl_label = "Add BCF Label"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -454,6 +479,7 @@ class AddBcfLabel(bpy.types.Operator):
|
|||||||
class EditBcfReferenceLinks(bpy.types.Operator):
|
class EditBcfReferenceLinks(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_reference_links"
|
bl_idname = "bim.edit_bcf_reference_links"
|
||||||
bl_label = "Edit BCF Reference Link"
|
bl_label = "Edit BCF Reference Link"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -471,6 +497,7 @@ class EditBcfReferenceLinks(bpy.types.Operator):
|
|||||||
class EditBcfLabels(bpy.types.Operator):
|
class EditBcfLabels(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_labels"
|
bl_idname = "bim.edit_bcf_labels"
|
||||||
bl_label = "Edit BCF Labels"
|
bl_label = "Edit BCF Labels"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -488,6 +515,7 @@ class EditBcfLabels(bpy.types.Operator):
|
|||||||
class RemoveBcfReferenceLink(bpy.types.Operator):
|
class RemoveBcfReferenceLink(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_reference_link"
|
bl_idname = "bim.remove_bcf_reference_link"
|
||||||
bl_label = "Remove BCF Reference Link"
|
bl_label = "Remove BCF Reference Link"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -504,6 +532,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
|
|||||||
class RemoveBcfLabel(bpy.types.Operator):
|
class RemoveBcfLabel(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_label"
|
bl_idname = "bim.remove_bcf_label"
|
||||||
bl_label = "Remove BCF Label"
|
bl_label = "Remove BCF Label"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -520,6 +549,7 @@ class RemoveBcfLabel(bpy.types.Operator):
|
|||||||
class RemoveBcfBimSnippet(bpy.types.Operator):
|
class RemoveBcfBimSnippet(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_bim_snippet"
|
bl_idname = "bim.remove_bcf_bim_snippet"
|
||||||
bl_label = "Remove BCF BIM Snippet"
|
bl_label = "Remove BCF BIM Snippet"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
@@ -536,6 +566,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
|
|||||||
class RemoveBcfDocumentReference(bpy.types.Operator):
|
class RemoveBcfDocumentReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_document_reference"
|
bl_idname = "bim.remove_bcf_document_reference"
|
||||||
bl_label = "Remove BCF Document Reference"
|
bl_label = "Remove BCF Document Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -551,6 +582,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
|
|||||||
class RemoveBcfRelatedTopic(bpy.types.Operator):
|
class RemoveBcfRelatedTopic(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_related_topic"
|
bl_idname = "bim.remove_bcf_related_topic"
|
||||||
bl_label = "Remove BCF Related Topic"
|
bl_label = "Remove BCF Related Topic"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -567,6 +599,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
|
|||||||
class RemoveBcfComment(bpy.types.Operator):
|
class RemoveBcfComment(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_bcf_comment"
|
bl_idname = "bim.remove_bcf_comment"
|
||||||
bl_label = "Remove BCF Comment"
|
bl_label = "Remove BCF Comment"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
comment_guid: bpy.props.StringProperty()
|
comment_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -582,6 +615,7 @@ class RemoveBcfComment(bpy.types.Operator):
|
|||||||
class EditBcfComment(bpy.types.Operator):
|
class EditBcfComment(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_bcf_comment"
|
bl_idname = "bim.edit_bcf_comment"
|
||||||
bl_label = "Edit BCF Comment"
|
bl_label = "Edit BCF Comment"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
comment_guid: bpy.props.StringProperty()
|
comment_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -600,6 +634,7 @@ class EditBcfComment(bpy.types.Operator):
|
|||||||
class AddBcfComment(bpy.types.Operator):
|
class AddBcfComment(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_bcf_comment"
|
bl_idname = "bim.add_bcf_comment"
|
||||||
bl_label = "Add BCF Comment"
|
bl_label = "Add BCF Comment"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
comment_guid: bpy.props.StringProperty()
|
comment_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -609,20 +644,20 @@ class AddBcfComment(bpy.types.Operator):
|
|||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
if not blender_topic.comment:
|
if not blender_topic.comment:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
comment = bcf.data.Comment()
|
comment = bcf.v2.data.Comment()
|
||||||
comment.comment = blender_topic.comment
|
comment.comment = blender_topic.comment
|
||||||
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
|
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
|
||||||
comment.viewpoint = bcf.data.Viewpoint()
|
comment.viewpoint = bcf.v2.data.Viewpoint()
|
||||||
comment.viewpoint.guid = blender_topic.viewpoints
|
comment.viewpoint.guid = blender_topic.viewpoints
|
||||||
bcfxml.add_comment(topic, comment)
|
bcfxml.add_comment(topic, comment)
|
||||||
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
||||||
print(bcfxml.filepath)
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class ActivateBcfViewpoint(bpy.types.Operator):
|
class ActivateBcfViewpoint(bpy.types.Operator):
|
||||||
bl_idname = "bim.activate_bcf_viewpoint"
|
bl_idname = "bim.activate_bcf_viewpoint"
|
||||||
bl_label = "Activate BCF Viewpoint"
|
bl_label = "Activate BCF Viewpoint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
@@ -832,6 +867,7 @@ class OpenBcfReferenceLink(bpy.types.Operator):
|
|||||||
class SelectBcfHeaderFile(bpy.types.Operator):
|
class SelectBcfHeaderFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_bcf_header_file"
|
bl_idname = "bim.select_bcf_header_file"
|
||||||
bl_label = "Select BCF Header File"
|
bl_label = "Select BCF Header File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -849,6 +885,7 @@ class SelectBcfHeaderFile(bpy.types.Operator):
|
|||||||
class SelectBcfBimSnippetReference(bpy.types.Operator):
|
class SelectBcfBimSnippetReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_bcf_bim_snippet_reference"
|
bl_idname = "bim.select_bcf_bim_snippet_reference"
|
||||||
bl_label = "Select BCF BIM Snippet Reference"
|
bl_label = "Select BCF BIM Snippet Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -866,6 +903,7 @@ class SelectBcfBimSnippetReference(bpy.types.Operator):
|
|||||||
class SelectBcfDocumentReference(bpy.types.Operator):
|
class SelectBcfDocumentReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_bcf_document_reference"
|
bl_idname = "bim.select_bcf_document_reference"
|
||||||
bl_label = "Select BCF Document Reference"
|
bl_label = "Select BCF Document Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -17,6 +17,11 @@ from bpy.props import (
|
|||||||
bcfviewpoints_enum = None
|
bcfviewpoints_enum = None
|
||||||
|
|
||||||
|
|
||||||
|
def purge():
|
||||||
|
global bcfviewpoints_enum
|
||||||
|
bcfviewpoints_enum = None
|
||||||
|
|
||||||
|
|
||||||
def updateBcfReferenceLink(self, context):
|
def updateBcfReferenceLink(self, context):
|
||||||
if bpy.context.scene.BCFProperties.is_loaded:
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
bpy.ops.bim.edit_bcf_reference_links()
|
bpy.ops.bim.edit_bcf_reference_links()
|
||||||
@@ -3,9 +3,14 @@ import bpy
|
|||||||
import tempfile
|
import tempfile
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import bimtester
|
|
||||||
import bimtester.run
|
try:
|
||||||
import bimtester.reports
|
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 pathlib import Path
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
@@ -31,7 +36,7 @@ class ExecuteBIMTester(bpy.types.Operator):
|
|||||||
"schema_file": "",
|
"schema_file": "",
|
||||||
"schema_name": "",
|
"schema_name": "",
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
"steps": props.steps
|
"steps": props.steps,
|
||||||
}
|
}
|
||||||
use_stored_ifc = props.should_load_from_memory and IfcStore.get_file()
|
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)
|
runner = bimtester.run.TestRunner(args["ifc"], ifc=IfcStore.get_file() if use_stored_ifc else None)
|
||||||
@@ -59,9 +64,10 @@ class BIMTesterPurge(bpy.types.Operator):
|
|||||||
|
|
||||||
class SelectFeature(bpy.types.Operator):
|
class SelectFeature(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_feature"
|
bl_idname = "bim.select_feature"
|
||||||
bl_label = "Select Feature"
|
bl_label = "Select Feature / IDS"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".feature"
|
filename_ext = ".feature"
|
||||||
filter_glob: bpy.props.StringProperty(default="*.feature", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.feature;*.xml", options={"HIDDEN"})
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -76,6 +82,7 @@ class SelectFeature(bpy.types.Operator):
|
|||||||
class SelectSteps(bpy.types.Operator):
|
class SelectSteps(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_steps"
|
bl_idname = "bim.select_steps"
|
||||||
bl_label = "Select Steps"
|
bl_label = "Select Steps"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".py"
|
filename_ext = ".py"
|
||||||
filter_glob: bpy.props.StringProperty(default="*.py", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.py", options={"HIDDEN"})
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
@@ -92,6 +99,7 @@ class SelectSteps(bpy.types.Operator):
|
|||||||
class SelectBIMTesterIfcFile(bpy.types.Operator):
|
class SelectBIMTesterIfcFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_bimtester_ifc_file"
|
bl_idname = "bim.select_bimtester_ifc_file"
|
||||||
bl_label = "Select BIMTester IFC File"
|
bl_label = "Select BIMTester IFC File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
@@ -162,6 +170,7 @@ class RejectClass(bpy.types.Operator):
|
|||||||
class SelectAudited(bpy.types.Operator):
|
class SelectAudited(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_audited"
|
bl_idname = "bim.select_audited"
|
||||||
bl_label = "Select Audited"
|
bl_label = "Select Audited"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
audited_global_ids = []
|
audited_global_ids = []
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from blenderbim.bim.prop import StrProperty
|
from blenderbim.bim.prop import StrProperty
|
||||||
from blenderbim.bim.module.root.prop import getIfcClasses
|
from blenderbim.bim.module.root.prop import getIfcClasses
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -19,6 +18,13 @@ scenarios_enum = []
|
|||||||
classes_enum = []
|
classes_enum = []
|
||||||
|
|
||||||
|
|
||||||
|
def purge():
|
||||||
|
global scenarios_enum
|
||||||
|
global classes_enum
|
||||||
|
scenarios_enum = []
|
||||||
|
classes_enum = []
|
||||||
|
|
||||||
|
|
||||||
def getScenarios(self, context):
|
def getScenarios(self, context):
|
||||||
global scenarios_enum
|
global scenarios_enum
|
||||||
if len(scenarios_enum) < 1:
|
if len(scenarios_enum) < 1:
|
||||||
@@ -41,7 +47,7 @@ def refreshScenarios(self, context):
|
|||||||
|
|
||||||
|
|
||||||
class BimTesterProperties(PropertyGroup):
|
class BimTesterProperties(PropertyGroup):
|
||||||
feature: StringProperty(default="", name="Feature File", update=refreshScenarios)
|
feature: StringProperty(default="", name="Feature / IDS", update=refreshScenarios)
|
||||||
steps: StringProperty(default="", name="Custom Steps")
|
steps: StringProperty(default="", name="Custom Steps")
|
||||||
ifc_file: StringProperty(default="", name="IFC File")
|
ifc_file: StringProperty(default="", name="IFC File")
|
||||||
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
|
audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class")
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import bpy
|
||||||
|
from . import ui
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
ui.BIM_PT_boundary,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import bpy
|
||||||
|
import blenderbim.bim.helper
|
||||||
|
from bpy.types import Panel, UIList
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from ifcopenshell.api.boundary.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_boundary(Panel):
|
||||||
|
bl_label = "IFC Space Boundaries"
|
||||||
|
bl_idname = "BIM_PT_boundary"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "object"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if not context.active_object:
|
||||||
|
return False
|
||||||
|
props = context.active_object.BIMObjectProperties
|
||||||
|
if not props.ifc_definition_id:
|
||||||
|
return False
|
||||||
|
if not IfcStore.get_element(props.ifc_definition_id):
|
||||||
|
return False
|
||||||
|
if IfcStore.get_file().by_id(props.ifc_definition_id).is_a() not in ["IfcSpace", "IfcExternalSpatialElement"]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
self.oprops = context.active_object.BIMObjectProperties
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
|
for boundary_id in Data.spaces.get(self.oprops.ifc_definition_id, []):
|
||||||
|
boundary = Data.boundaries[boundary_id]
|
||||||
|
row = self.layout.row()
|
||||||
|
row.label(text=f"{boundary_id}", icon="GHOST_ENABLED")
|
||||||
@@ -42,6 +42,7 @@ class ExportClashSets(bpy.types.Operator):
|
|||||||
class ImportClashSets(bpy.types.Operator):
|
class ImportClashSets(bpy.types.Operator):
|
||||||
bl_idname = "bim.import_clash_sets"
|
bl_idname = "bim.import_clash_sets"
|
||||||
bl_label = "Import Clash Sets"
|
bl_label = "Import Clash Sets"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".json"
|
filename_ext = ".json"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ class ImportClashSets(bpy.types.Operator):
|
|||||||
class AddClashSet(bpy.types.Operator):
|
class AddClashSet(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_clash_set"
|
bl_idname = "bim.add_clash_set"
|
||||||
bl_label = "Add Clash Set"
|
bl_label = "Add Clash Set"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
|
new = bpy.context.scene.BIMClashProperties.clash_sets.add()
|
||||||
@@ -88,6 +90,7 @@ class AddClashSet(bpy.types.Operator):
|
|||||||
class RemoveClashSet(bpy.types.Operator):
|
class RemoveClashSet(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_clash_set"
|
bl_idname = "bim.remove_clash_set"
|
||||||
bl_label = "Remove Clash Set"
|
bl_label = "Remove Clash Set"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -98,6 +101,7 @@ class RemoveClashSet(bpy.types.Operator):
|
|||||||
class AddClashSource(bpy.types.Operator):
|
class AddClashSource(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_clash_source"
|
bl_idname = "bim.add_clash_source"
|
||||||
bl_label = "Add Clash Source"
|
bl_label = "Add Clash Source"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -109,6 +113,7 @@ class AddClashSource(bpy.types.Operator):
|
|||||||
class RemoveClashSource(bpy.types.Operator):
|
class RemoveClashSource(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_clash_source"
|
bl_idname = "bim.remove_clash_source"
|
||||||
bl_label = "Remove Clash Source"
|
bl_label = "Remove Clash Source"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
@@ -121,7 +126,9 @@ class RemoveClashSource(bpy.types.Operator):
|
|||||||
class SelectClashSource(bpy.types.Operator):
|
class SelectClashSource(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_clash_source"
|
bl_idname = "bim.select_clash_source"
|
||||||
bl_label = "Select Clash Source"
|
bl_label = "Select Clash Source"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
@@ -138,6 +145,7 @@ class SelectClashSource(bpy.types.Operator):
|
|||||||
class SelectClashResults(bpy.types.Operator):
|
class SelectClashResults(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_clash_results"
|
bl_idname = "bim.select_clash_results"
|
||||||
bl_label = "Select Clash Results"
|
bl_label = "Select Clash Results"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -152,6 +160,7 @@ class SelectClashResults(bpy.types.Operator):
|
|||||||
class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_smart_grouped_clashes_path"
|
bl_idname = "bim.select_smart_grouped_clashes_path"
|
||||||
bl_label = "Select Smart-Grouped Clashes Path"
|
bl_label = "Select Smart-Grouped Clashes Path"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -233,6 +242,7 @@ class ExecuteIfcClash(bpy.types.Operator):
|
|||||||
class SelectIfcClashResults(bpy.types.Operator):
|
class SelectIfcClashResults(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_ifc_clash_results"
|
bl_idname = "bim.select_ifc_clash_results"
|
||||||
bl_label = "Select IFC Clash Results"
|
bl_label = "Select IFC Clash Results"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".json"
|
filename_ext = ".json"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
@@ -270,6 +280,7 @@ class SelectIfcClashResults(bpy.types.Operator):
|
|||||||
class SmartClashGroup(bpy.types.Operator):
|
class SmartClashGroup(bpy.types.Operator):
|
||||||
bl_idname = "bim.smart_clash_group"
|
bl_idname = "bim.smart_clash_group"
|
||||||
bl_label = "Smart Group Clashes"
|
bl_label = "Smart Group Clashes"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -322,6 +333,7 @@ class SmartClashGroup(bpy.types.Operator):
|
|||||||
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_smart_groups_for_active_clash_set"
|
bl_idname = "bim.load_smart_groups_for_active_clash_set"
|
||||||
bl_label = "Load Smart Groups for Active Clash Set"
|
bl_label = "Load Smart Groups for Active Clash Set"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
smart_groups_path = bpy.path.ensure_ext(bpy.context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
||||||
@@ -355,6 +367,7 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
|||||||
class SelectSmartGroup(bpy.types.Operator):
|
class SelectSmartGroup(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_smart_group"
|
bl_idname = "bim.select_smart_group"
|
||||||
bl_label = "Select Smart Group"
|
bl_label = "Select Smart Group"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# Select smart group in view
|
# Select smart group in view
|
||||||
@@ -423,6 +436,7 @@ class BlenderClasher:
|
|||||||
class SetBlenderClashSetA(bpy.types.Operator):
|
class SetBlenderClashSetA(bpy.types.Operator):
|
||||||
bl_idname = "bim.set_blender_clash_set_a"
|
bl_idname = "bim.set_blender_clash_set_a"
|
||||||
bl_label = "Set Blender Clash Set A"
|
bl_label = "Set Blender Clash Set A"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_a) > 0:
|
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_a) > 0:
|
||||||
@@ -436,6 +450,7 @@ class SetBlenderClashSetA(bpy.types.Operator):
|
|||||||
class SetBlenderClashSetB(bpy.types.Operator):
|
class SetBlenderClashSetB(bpy.types.Operator):
|
||||||
bl_idname = "bim.set_blender_clash_set_b"
|
bl_idname = "bim.set_blender_clash_set_b"
|
||||||
bl_label = "Set Blender Clash Set B"
|
bl_label = "Set Blender Clash Set B"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_b) > 0:
|
while len(bpy.context.scene.BIMClashProperties.blender_clash_set_b) > 0:
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from blenderbim.bim.module.owner.data import Data
|
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -93,6 +93,7 @@ class BIM_PT_ifcclash(Panel):
|
|||||||
class BIM_PT_clash_manager(Panel):
|
class BIM_PT_clash_manager(Panel):
|
||||||
bl_idname = "BIM_PT_clash_manager"
|
bl_idname = "BIM_PT_clash_manager"
|
||||||
bl_label = "Clash Manager"
|
bl_label = "Clash Manager"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
bl_space_type = "VIEW_3D"
|
bl_space_type = "VIEW_3D"
|
||||||
bl_region_type = "UI"
|
bl_region_type = "UI"
|
||||||
bl_category = "BlenderBIM"
|
bl_category = "BlenderBIM"
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import blenderbim.bim.module.classification.add_classification as add_classification
|
import ifcopenshell.api
|
||||||
import blenderbim.bim.module.classification.remove_classification as remove_classification
|
|
||||||
import blenderbim.bim.module.classification.edit_classification as edit_classification
|
|
||||||
import blenderbim.bim.module.classification.add_reference as add_reference
|
|
||||||
import blenderbim.bim.module.classification.remove_reference as remove_reference
|
|
||||||
import blenderbim.bim.module.classification.edit_reference as edit_reference
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.classification.data import Data
|
from ifcopenshell.api.classification.data import Data
|
||||||
from blenderbim.bim.module.classification.prop import getClassifications, getReferences
|
from blenderbim.bim.module.classification.prop import getClassifications, getReferences
|
||||||
|
|
||||||
|
|
||||||
@@ -31,19 +26,26 @@ class LoadClassificationLibrary(bpy.types.Operator):
|
|||||||
class AddClassification(bpy.types.Operator):
|
class AddClassification(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_classification"
|
bl_idname = "bim.add_classification"
|
||||||
bl_label = "Add Classification"
|
bl_label = "Add Classification"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
props = context.scene.BIMClassificationProperties
|
props = context.scene.BIMClassificationProperties
|
||||||
add_classification.Usecase(
|
ifcopenshell.api.run(
|
||||||
IfcStore.get_file(), {"classification": Data.library_file.by_id(int(props.available_classifications))}
|
"classification.add_classification",
|
||||||
).execute()
|
IfcStore.get_file(),
|
||||||
Data.load()
|
**{"classification": Data.library_file.by_id(int(props.available_classifications))},
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EnableEditingClassification(bpy.types.Operator):
|
class EnableEditingClassification(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_classification"
|
bl_idname = "bim.enable_editing_classification"
|
||||||
bl_label = "Enable Editing Classification"
|
bl_label = "Enable Editing Classification"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
classification: bpy.props.IntProperty()
|
classification: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -67,6 +69,7 @@ class EnableEditingClassification(bpy.types.Operator):
|
|||||||
class DisableEditingClassification(bpy.types.Operator):
|
class DisableEditingClassification(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_editing_classification"
|
bl_idname = "bim.disable_editing_classification"
|
||||||
bl_label = "Disable Editing Classification"
|
bl_label = "Disable Editing Classification"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMClassificationProperties.active_classification_id = 0
|
context.scene.BIMClassificationProperties.active_classification_id = 0
|
||||||
@@ -76,20 +79,32 @@ class DisableEditingClassification(bpy.types.Operator):
|
|||||||
class RemoveClassification(bpy.types.Operator):
|
class RemoveClassification(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_classification"
|
bl_idname = "bim.remove_classification"
|
||||||
bl_label = "Remove Classification"
|
bl_label = "Remove Classification"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
classification: bpy.props.IntProperty()
|
classification: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
remove_classification.Usecase(self.file, {"classification": self.file.by_id(self.classification)}).execute()
|
ifcopenshell.api.run(
|
||||||
Data.load()
|
"classification.remove_classification",
|
||||||
|
self.file,
|
||||||
|
**{"classification": self.file.by_id(self.classification)},
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EditClassification(bpy.types.Operator):
|
class EditClassification(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_classification"
|
bl_idname = "bim.edit_classification"
|
||||||
bl_label = "Edit Classification"
|
bl_label = "Edit Classification"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
props = context.scene.BIMClassificationProperties
|
props = context.scene.BIMClassificationProperties
|
||||||
attributes = {}
|
attributes = {}
|
||||||
for attribute in props.classification_attributes:
|
for attribute in props.classification_attributes:
|
||||||
@@ -100,10 +115,12 @@ class EditClassification(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
attributes[attribute.name] = attribute.string_value
|
attributes[attribute.name] = attribute.string_value
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
edit_classification.Usecase(
|
ifcopenshell.api.run(
|
||||||
self.file, {"classification": self.file.by_id(props.active_classification_id), "attributes": attributes}
|
"classification.edit_classification",
|
||||||
).execute()
|
self.file,
|
||||||
Data.load()
|
**{"classification": self.file.by_id(props.active_classification_id), "attributes": attributes},
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
bpy.ops.bim.disable_editing_classification()
|
bpy.ops.bim.disable_editing_classification()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -111,6 +128,7 @@ class EditClassification(bpy.types.Operator):
|
|||||||
class EnableEditingClassificationReference(bpy.types.Operator):
|
class EnableEditingClassificationReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_classification_reference"
|
bl_idname = "bim.enable_editing_classification_reference"
|
||||||
bl_label = "Enable Editing Classification Reference"
|
bl_label = "Enable Editing Classification Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
reference: bpy.props.IntProperty()
|
reference: bpy.props.IntProperty()
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
@@ -135,6 +153,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
|
|||||||
class DisableEditingClassificationReference(bpy.types.Operator):
|
class DisableEditingClassificationReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_editing_classification_reference"
|
bl_idname = "bim.disable_editing_classification_reference"
|
||||||
bl_label = "Disable Editing Classification Reference"
|
bl_label = "Disable Editing Classification Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -146,30 +165,39 @@ class DisableEditingClassificationReference(bpy.types.Operator):
|
|||||||
class RemoveClassificationReference(bpy.types.Operator):
|
class RemoveClassificationReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_classification_reference"
|
bl_idname = "bim.remove_classification_reference"
|
||||||
bl_label = "Remove Classification Reference"
|
bl_label = "Remove Classification Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
reference: bpy.props.IntProperty()
|
reference: bpy.props.IntProperty()
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
remove_reference.Usecase(
|
ifcopenshell.api.run(
|
||||||
|
"classification.remove_reference",
|
||||||
self.file,
|
self.file,
|
||||||
{
|
**{
|
||||||
"reference": self.file.by_id(self.reference),
|
"reference": self.file.by_id(self.reference),
|
||||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||||
},
|
},
|
||||||
).execute()
|
)
|
||||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EditClassificationReference(bpy.types.Operator):
|
class EditClassificationReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_classification_reference"
|
bl_idname = "bim.edit_classification_reference"
|
||||||
bl_label = "Edit Classification Reference"
|
bl_label = "Edit Classification Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
props = obj.BIMClassificationReferenceProperties
|
props = obj.BIMClassificationReferenceProperties
|
||||||
attributes = {}
|
attributes = {}
|
||||||
@@ -179,10 +207,12 @@ class EditClassificationReference(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
attributes[attribute.name] = attribute.string_value
|
attributes[attribute.name] = attribute.string_value
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
edit_reference.Usecase(
|
ifcopenshell.api.run(
|
||||||
self.file, {"reference": self.file.by_id(props.active_reference_id), "attributes": attributes}
|
"classification.edit_reference",
|
||||||
).execute()
|
self.file,
|
||||||
Data.load()
|
**{"reference": self.file.by_id(props.active_reference_id), "attributes": attributes},
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
bpy.ops.bim.disable_editing_classification_reference()
|
bpy.ops.bim.disable_editing_classification_reference()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -190,10 +220,14 @@ class EditClassificationReference(bpy.types.Operator):
|
|||||||
class AddClassificationReference(bpy.types.Operator):
|
class AddClassificationReference(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_classification_reference"
|
bl_idname = "bim.add_classification_reference"
|
||||||
bl_label = "Add Classification Reference"
|
bl_label = "Add Classification Reference"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
reference: bpy.props.IntProperty()
|
reference: bpy.props.IntProperty()
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
|
|
||||||
@@ -206,22 +240,24 @@ class AddClassificationReference(bpy.types.Operator):
|
|||||||
classification = self.file.by_id(classification_id)
|
classification = self.file.by_id(classification_id)
|
||||||
break
|
break
|
||||||
|
|
||||||
add_reference.Usecase(
|
ifcopenshell.api.run(
|
||||||
|
"classification.add_reference",
|
||||||
self.file,
|
self.file,
|
||||||
{
|
**{
|
||||||
"reference": Data.library_file.by_id(self.reference),
|
"reference": Data.library_file.by_id(self.reference),
|
||||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||||
"classification": classification
|
"classification": classification,
|
||||||
},
|
},
|
||||||
).execute()
|
)
|
||||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class ChangeClassificationLevel(bpy.types.Operator):
|
class ChangeClassificationLevel(bpy.types.Operator):
|
||||||
bl_idname = "bim.change_classification_level"
|
bl_idname = "bim.change_classification_level"
|
||||||
bl_label = "Change Classification Level"
|
bl_label = "Change Classification Level"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
parent_id: bpy.props.IntProperty()
|
parent_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from blenderbim.bim.module.classification.data import Data
|
from ifcopenshell.api.classification.data import Data
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -16,6 +16,11 @@ from bpy.props import (
|
|||||||
classification_enum = []
|
classification_enum = []
|
||||||
|
|
||||||
|
|
||||||
|
def purge():
|
||||||
|
global classification_enum
|
||||||
|
classification_enum = []
|
||||||
|
|
||||||
|
|
||||||
def getClassifications(self, context):
|
def getClassifications(self, context):
|
||||||
global classification_enum
|
global classification_enum
|
||||||
if len(classification_enum) < 1:
|
if len(classification_enum) < 1:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.classification.data import Data
|
from ifcopenshell.api.classification.data import Data
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_classifications(Panel):
|
class BIM_PT_classifications(Panel):
|
||||||
@@ -17,7 +17,7 @@ class BIM_PT_classifications(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
if not Data.is_loaded:
|
if not Data.is_loaded:
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
|
|
||||||
self.props = context.scene.BIMClassificationProperties
|
self.props = context.scene.BIMClassificationProperties
|
||||||
|
|
||||||
@@ -70,6 +70,8 @@ class BIM_PT_classification_references(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||||
|
return False
|
||||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@@ -79,9 +81,9 @@ class BIM_PT_classification_references(Panel):
|
|||||||
self.props = obj.BIMClassificationReferenceProperties
|
self.props = obj.BIMClassificationReferenceProperties
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if not Data.is_loaded:
|
if not Data.is_loaded:
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
if self.oprops.ifc_definition_id not in Data.products:
|
if self.oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(self.oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
|
||||||
|
|
||||||
self.draw_add_ui()
|
self.draw_add_ui()
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
class SelectCobieIfcFile(bpy.types.Operator):
|
class SelectCobieIfcFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_cobie_ifc_file"
|
bl_idname = "bim.select_cobie_ifc_file"
|
||||||
bl_label = "Select COBie IFC File"
|
bl_label = "Select COBie IFC File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -25,6 +26,7 @@ class SelectCobieIfcFile(bpy.types.Operator):
|
|||||||
class SelectCobieJsonFile(bpy.types.Operator):
|
class SelectCobieJsonFile(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_cobie_json_file"
|
bl_idname = "bim.select_cobie_json_file"
|
||||||
bl_label = "Select COBie JSON File"
|
bl_label = "Select COBie JSON File"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import blenderbim.bim.module.constraint.add_objective as add_objective
|
import ifcopenshell.api
|
||||||
import blenderbim.bim.module.constraint.edit_objective as edit_objective
|
import ifcopenshell.util.attribute
|
||||||
import blenderbim.bim.module.constraint.remove_constraint as remove_constraint
|
import blenderbim.bim.helper
|
||||||
import blenderbim.bim.module.constraint.assign_constraint as assign_constraint
|
|
||||||
import blenderbim.bim.module.constraint.unassign_constraint as unassign_constraint
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.constraint.data import Data
|
from ifcopenshell.api.constraint.data import Data
|
||||||
|
|
||||||
|
|
||||||
class LoadObjectives(bpy.types.Operator):
|
class LoadObjectives(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_objectives"
|
bl_idname = "bim.load_objectives"
|
||||||
bl_label = "Load Objectives"
|
bl_label = "Load Objectives"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.BIMConstraintProperties
|
props = context.scene.BIMConstraintProperties
|
||||||
@@ -29,6 +28,7 @@ class LoadObjectives(bpy.types.Operator):
|
|||||||
class DisableConstraintEditingUI(bpy.types.Operator):
|
class DisableConstraintEditingUI(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_constraint_editing_ui"
|
bl_idname = "bim.disable_constraint_editing_ui"
|
||||||
bl_label = "Disable Constraint Editing UI"
|
bl_label = "Disable Constraint Editing UI"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMConstraintProperties.is_editing = ""
|
context.scene.BIMConstraintProperties.is_editing = ""
|
||||||
@@ -39,6 +39,7 @@ class DisableConstraintEditingUI(bpy.types.Operator):
|
|||||||
class EnableEditingConstraint(bpy.types.Operator):
|
class EnableEditingConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_constraint"
|
bl_idname = "bim.enable_editing_constraint"
|
||||||
bl_label = "Enable Editing Constraint"
|
bl_label = "Enable Editing Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
constraint: bpy.props.IntProperty()
|
constraint: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -49,22 +50,7 @@ class EnableEditingConstraint(bpy.types.Operator):
|
|||||||
if props.is_editing == "IfcObjective":
|
if props.is_editing == "IfcObjective":
|
||||||
data = Data.objectives[self.constraint]
|
data = Data.objectives[self.constraint]
|
||||||
|
|
||||||
for attribute in IfcStore.get_schema().declaration_by_name(props.is_editing).all_attributes():
|
blenderbim.bim.helper.import_attributes(props.is_editing, props.constraint_attributes, data)
|
||||||
data_type = str(attribute.type_of_attribute)
|
|
||||||
if "<entity" in data_type:
|
|
||||||
continue
|
|
||||||
new = props.constraint_attributes.add()
|
|
||||||
new.name = attribute.name()
|
|
||||||
new.is_null = data[attribute.name()] is None
|
|
||||||
new.is_optional = attribute.optional()
|
|
||||||
if "<string>" in data_type:
|
|
||||||
new.string_value = "" if new.is_null else data[attribute.name()]
|
|
||||||
new.data_type = "string"
|
|
||||||
elif "<enumeration" in data_type:
|
|
||||||
new.enum_items = json.dumps(attribute.type_of_attribute().declared_type().enumeration_items())
|
|
||||||
new.data_type = "enum"
|
|
||||||
if data[attribute.name()]:
|
|
||||||
new.enum_value = data[attribute.name()]
|
|
||||||
props.active_constraint_id = self.constraint
|
props.active_constraint_id = self.constraint
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -72,6 +58,7 @@ class EnableEditingConstraint(bpy.types.Operator):
|
|||||||
class DisableEditingConstraint(bpy.types.Operator):
|
class DisableEditingConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_editing_constraint"
|
bl_idname = "bim.disable_editing_constraint"
|
||||||
bl_label = "Disable Editing Constraint"
|
bl_label = "Disable Editing Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMConstraintProperties.active_constraint_id = 0
|
context.scene.BIMConstraintProperties.active_constraint_id = 0
|
||||||
@@ -81,10 +68,14 @@ class DisableEditingConstraint(bpy.types.Operator):
|
|||||||
class AddObjective(bpy.types.Operator):
|
class AddObjective(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_objective"
|
bl_idname = "bim.add_objective"
|
||||||
bl_label = "Add Objective"
|
bl_label = "Add Objective"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
result = add_objective.Usecase(IfcStore.get_file()).execute()
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
Data.load()
|
|
||||||
|
def _execute(self, context):
|
||||||
|
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
bpy.ops.bim.load_objectives()
|
bpy.ops.bim.load_objectives()
|
||||||
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -93,8 +84,12 @@ class AddObjective(bpy.types.Operator):
|
|||||||
class EditObjective(bpy.types.Operator):
|
class EditObjective(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_objective"
|
bl_idname = "bim.edit_objective"
|
||||||
bl_label = "Edit Objective"
|
bl_label = "Edit Objective"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
props = context.scene.BIMConstraintProperties
|
props = context.scene.BIMConstraintProperties
|
||||||
attributes = {}
|
attributes = {}
|
||||||
for attribute in props.constraint_attributes:
|
for attribute in props.constraint_attributes:
|
||||||
@@ -105,10 +100,12 @@ class EditObjective(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
attributes[attribute.name] = attribute.string_value
|
attributes[attribute.name] = attribute.string_value
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
edit_objective.Usecase(
|
ifcopenshell.api.run(
|
||||||
self.file, {"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
"constraint.edit_objective",
|
||||||
).execute()
|
self.file,
|
||||||
Data.load()
|
**{"objective": self.file.by_id(props.active_constraint_id), "attributes": attributes}
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
bpy.ops.bim.load_objectives()
|
bpy.ops.bim.load_objectives()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -116,13 +113,19 @@ class EditObjective(bpy.types.Operator):
|
|||||||
class RemoveConstraint(bpy.types.Operator):
|
class RemoveConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.remove_constraint"
|
bl_idname = "bim.remove_constraint"
|
||||||
bl_label = "Remove Constraint"
|
bl_label = "Remove Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
constraint: bpy.props.IntProperty()
|
constraint: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
props = context.scene.BIMConstraintProperties
|
props = context.scene.BIMConstraintProperties
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
remove_constraint.Usecase(self.file, {"constraint": self.file.by_id(self.constraint)}).execute()
|
ifcopenshell.api.run(
|
||||||
Data.load()
|
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
if props.is_editing == "IfcObjective":
|
if props.is_editing == "IfcObjective":
|
||||||
bpy.ops.bim.load_objectives()
|
bpy.ops.bim.load_objectives()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -131,6 +134,7 @@ class RemoveConstraint(bpy.types.Operator):
|
|||||||
class EnableAssigningConstraint(bpy.types.Operator):
|
class EnableAssigningConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_assigning_constraint"
|
bl_idname = "bim.enable_assigning_constraint"
|
||||||
bl_label = "Enable Assigning Constraint"
|
bl_label = "Enable Assigning Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -145,6 +149,7 @@ class EnableAssigningConstraint(bpy.types.Operator):
|
|||||||
class DisableAssigningConstraint(bpy.types.Operator):
|
class DisableAssigningConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.disable_assigning_constraint"
|
bl_idname = "bim.disable_assigning_constraint"
|
||||||
bl_label = "Disable Assigning Constraint"
|
bl_label = "Disable Assigning Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -157,32 +162,48 @@ class DisableAssigningConstraint(bpy.types.Operator):
|
|||||||
class AssignConstraint(bpy.types.Operator):
|
class AssignConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.assign_constraint"
|
bl_idname = "bim.assign_constraint"
|
||||||
bl_label = "Assign Constraint"
|
bl_label = "Assign Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
constraint: bpy.props.IntProperty()
|
constraint: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
assign_constraint.Usecase(self.file, {
|
ifcopenshell.api.run(
|
||||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
"constraint.assign_constraint",
|
||||||
"constraint": self.file.by_id(self.constraint)
|
self.file,
|
||||||
}).execute()
|
**{
|
||||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||||
|
"constraint": self.file.by_id(self.constraint),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class UnassignConstraint(bpy.types.Operator):
|
class UnassignConstraint(bpy.types.Operator):
|
||||||
bl_idname = "bim.unassign_constraint"
|
bl_idname = "bim.unassign_constraint"
|
||||||
bl_label = "Unassign Constraint"
|
bl_label = "Unassign Constraint"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
constraint: bpy.props.IntProperty()
|
constraint: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
unassign_constraint.Usecase(self.file, {
|
ifcopenshell.api.run(
|
||||||
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
"constraint.unassign_constraint",
|
||||||
"constraint": self.file.by_id(self.constraint)
|
self.file,
|
||||||
}).execute()
|
**{
|
||||||
Data.load(obj.BIMObjectProperties.ifc_definition_id)
|
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||||
|
"constraint": self.file.by_id(self.constraint),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.constraint.data import Data
|
from ifcopenshell.api.constraint.data import Data
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_constraints(Panel):
|
class BIM_PT_constraints(Panel):
|
||||||
@@ -17,7 +17,7 @@ class BIM_PT_constraints(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
if not Data.is_loaded:
|
if not Data.is_loaded:
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
|
|
||||||
self.props = context.scene.BIMConstraintProperties
|
self.props = context.scene.BIMConstraintProperties
|
||||||
|
|
||||||
@@ -42,7 +42,6 @@ class BIM_PT_constraints(Panel):
|
|||||||
|
|
||||||
if self.props.active_constraint_id:
|
if self.props.active_constraint_id:
|
||||||
self.draw_editable_ui(context)
|
self.draw_editable_ui(context)
|
||||||
return
|
|
||||||
|
|
||||||
def draw_editable_ui(self, context):
|
def draw_editable_ui(self, context):
|
||||||
for attribute in self.props.constraint_attributes:
|
for attribute in self.props.constraint_attributes:
|
||||||
@@ -65,6 +64,8 @@ class BIM_PT_object_constraints(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||||
|
return False
|
||||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@@ -74,9 +75,9 @@ class BIM_PT_object_constraints(Panel):
|
|||||||
self.props = obj.BIMObjectConstraintProperties
|
self.props = obj.BIMObjectConstraintProperties
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if not Data.is_loaded:
|
if not Data.is_loaded:
|
||||||
Data.load()
|
Data.load(IfcStore.get_file())
|
||||||
if self.oprops.ifc_definition_id not in Data.products:
|
if self.oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(self.oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
|
||||||
|
|
||||||
self.draw_add_ui()
|
self.draw_add_ui()
|
||||||
|
|
||||||