Merge branch 'v0.6.0' into v0.7.0

This commit is contained in:
Thomas Krijnen
2021-07-10 16:55:57 +02:00
728 changed files with 376298 additions and 19422 deletions
@@ -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}}"
+107
View File
@@ -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
+96
View File
@@ -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
+13
View File
@@ -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
+46 -39
View File
@@ -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
+37
View File
@@ -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
+3 -3
View File
@@ -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:
+145 -53
View File
@@ -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)
+2 -2
View File
@@ -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,))
+8 -5
View File
@@ -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
+30 -750
View File
@@ -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()
+771
View File
@@ -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()
+827
View File
@@ -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()
+181
View File
@@ -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 = []
+31
View File
@@ -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>
+60
View File
@@ -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>
+174
View File
@@ -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>
+17
View File
@@ -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>
+14
View File
@@ -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>
+8
View File
@@ -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>
+220
View File
@@ -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>
+1
View File
@@ -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)
Binary file not shown.
@@ -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

+187
View File
@@ -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
+280
View File
@@ -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")
+84
View File
@@ -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
+235
View File
@@ -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()

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