mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
311 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46cfe70181 | |||
| 294935ceed | |||
| f2097b9036 | |||
| e48a3a06c0 | |||
| c07371cb76 | |||
| 8d450aa500 | |||
| 726c792fbe | |||
| 83dda3e73f | |||
| 5c78e29849 | |||
| e6d48cd37f | |||
| a3ecde3ad9 | |||
| 2bb64a6cae | |||
| 764c818d10 | |||
| 2d18185928 | |||
| 43468166d2 | |||
| fbfd6bc485 | |||
| 1578efe366 | |||
| 76948c5156 | |||
| 562ed5908d | |||
| 9caac14014 | |||
| 695f75f3a7 | |||
| 7d7c04ddc0 | |||
| 863bf8ebbe | |||
| 0fcc28f50c | |||
| 04e9a08f3d | |||
| 837c401116 | |||
| b087d5125f | |||
| a6bff09c78 | |||
| 4716c0b7b7 | |||
| 1d040a170e | |||
| 951026a95c | |||
| c6b82bfa56 | |||
| 01b6d90432 | |||
| 504a116ebf | |||
| be785a878c | |||
| 5c2b7404d2 | |||
| 84e9e2396a | |||
| 7687d5e26e | |||
| 25ce3b039e | |||
| 9c4c42e27e | |||
| 26dd850ac1 | |||
| 0380c286a5 | |||
| 10aca77de4 | |||
| b6f2fdef77 | |||
| 9cc1f5f0e3 | |||
| adcf22f281 | |||
| ad0c49ac37 | |||
| 73d77b8382 | |||
| 3e45f39ea2 | |||
| a2bb015c79 | |||
| b231f43fc7 | |||
| 383038fdb4 | |||
| fbd8ea1edb | |||
| 26e292dd34 | |||
| c520e16031 | |||
| 101972fbad | |||
| f23ebc4031 | |||
| 44431db092 | |||
| d1b6c1ffe4 | |||
| 4d98151b62 | |||
| 5e8e18e3b5 | |||
| 808ff548ec | |||
| bc9b482373 | |||
| 4506eb0df9 | |||
| 921d412fc7 | |||
| 6089d3af78 | |||
| b8168ca606 | |||
| 61a7325bc0 | |||
| c483116b32 | |||
| 55a623dc97 | |||
| ad420108b0 | |||
| 9944c7d2de | |||
| 1a8f2be4fd | |||
| 13f64fdfe2 | |||
| 38a9e29102 | |||
| 3c23a57138 | |||
| 7baed872c6 | |||
| 9a7a284561 | |||
| e9c24a2656 | |||
| 5c73ef0a6d | |||
| dadcbe61ea | |||
| 93502a05ea | |||
| 5ebde4c79a | |||
| cf21ae22f1 | |||
| cc500ae833 | |||
| 256c0baa14 | |||
| 66decc604e | |||
| 356bc18078 | |||
| 1344cbc192 | |||
| e00ffb07ad | |||
| dba43590a5 | |||
| a0613cfee6 | |||
| c3de72b71a | |||
| 39fc60a482 | |||
| 0d7e71a269 | |||
| 78a2f65ec3 | |||
| 802bd74c52 | |||
| 33cf40debb | |||
| ff63fd9f6f | |||
| 5b207e6f54 | |||
| 695dc6ec5a | |||
| d3bcb8c6b9 | |||
| 6226aea3c3 | |||
| a30ad9b886 | |||
| 2b1e17681f | |||
| 8f25a31f19 | |||
| 1c7e6cf1b9 | |||
| fc7459c07f | |||
| 8f6932de64 | |||
| b708100c03 | |||
| d4e2d3d2dd | |||
| 13309cb8c4 | |||
| 5a4bc0909b | |||
| 39540e5794 | |||
| 0a0f977b5c | |||
| 13a75e7ad7 | |||
| 99574d6255 | |||
| 968cceb725 | |||
| 002985a446 | |||
| 5adc29b0b3 | |||
| 52ea4ff44d | |||
| 4e31a87c20 | |||
| 5d85390715 | |||
| 3e13eb8942 | |||
| 262c6d1fcb | |||
| 12b5a88861 | |||
| 2ccc9d886e | |||
| a78769c8ff | |||
| 1a142ad5eb | |||
| 2bc22e150c | |||
| aad5d280fd | |||
| 7ab07ed149 | |||
| 3690998dbc | |||
| 1b83fcc1c7 | |||
| 5cf25c45f9 | |||
| efa734c06e | |||
| e242244ec0 | |||
| 6f9da163fb | |||
| a3fd169bb6 | |||
| 899056f929 | |||
| 132fef1b76 | |||
| 422f772c40 | |||
| 66f5081ada | |||
| 839e35f1da | |||
| 40d9713326 | |||
| 17b04ad697 | |||
| 2f99dd8e98 | |||
| 49f825ba82 | |||
| 3f18ad0bc5 | |||
| 991c42e816 | |||
| 20d6eae50a | |||
| 9740582a84 | |||
| a8046a6d69 | |||
| d6655a5c14 | |||
| 0a3a2db4ae | |||
| 499eb5ea14 | |||
| b8ca287883 | |||
| 52f3392ece | |||
| d53641ce5e | |||
| 7a3ad6c3a1 | |||
| 0b2007011e | |||
| eae6983ca5 | |||
| 0ec337748f | |||
| 2699278ba3 | |||
| 0406166d8a | |||
| 2128eda87b | |||
| 21f4fb169f | |||
| 30fc52ebd9 | |||
| 64bc7af4ef | |||
| b3f1fc667a | |||
| fcbb28f9d8 | |||
| 240a31db3a | |||
| c1bde09585 | |||
| 5dd2b5e555 | |||
| 37dcb92220 | |||
| 6c43c60d04 | |||
| b8d191ac1c | |||
| 57173c7740 | |||
| 2d3295ec54 | |||
| 941ede117c | |||
| 4210b8333f | |||
| 1723d1e5a9 | |||
| 3ee06ee7a0 | |||
| d3e8da58dc | |||
| 29e000b939 | |||
| 6686a72d1f | |||
| 7dd20f69d7 | |||
| 0044ef5588 | |||
| ffa5686b22 | |||
| ab8d6bea99 | |||
| e7bb89f582 | |||
| c80b0464e5 | |||
| 7e3547b309 | |||
| 45c7432ff7 | |||
| c98c20d403 | |||
| d26a2580ea | |||
| f84c4ab2e5 | |||
| de6676a516 | |||
| bfbb7bec94 | |||
| ac00aaae4d | |||
| 422222a7cb | |||
| e1c7b37d62 | |||
| dec978743b | |||
| 10fae033c8 | |||
| b23180d287 | |||
| 99ccd81b3f | |||
| e2d3f3a351 | |||
| 5e6a444aa2 | |||
| 72cf5cfc59 | |||
| 75cb68d2f1 | |||
| 7f40a9d723 | |||
| dcc9d0e9cd | |||
| 7228329f5e | |||
| d8786717b6 | |||
| b73bd3506f | |||
| bdb2791b35 | |||
| 549ea11945 | |||
| aed01b5703 | |||
| 13eb8562e0 | |||
| c022a12835 | |||
| 04eb47ff65 | |||
| 10ecd16f0c | |||
| 75852bfa75 | |||
| 750c720c9b | |||
| 4697681fcb | |||
| 9398363cbf | |||
| ae45e6b960 | |||
| 88c002703e | |||
| 51c78a89bd | |||
| e36a958c0d | |||
| a8126a6f90 | |||
| 9b857cdefa | |||
| 90e6400115 | |||
| 2dcfcf9708 | |||
| f4971da42e | |||
| 008f953b6c | |||
| 5481237480 | |||
| 4f7564e5ae | |||
| d214cbaece | |||
| d0c5f1e69f | |||
| 4ba3240fb4 | |||
| 6d8e5e4c83 | |||
| cd1402570b | |||
| 6c0e528dd2 | |||
| 297a513d21 | |||
| 9f0a4ba064 | |||
| 8813ed66e0 | |||
| fafcd5dfca | |||
| f523870b57 | |||
| 2ed24434c8 | |||
| 6878b2c072 | |||
| 0ed8dc970d | |||
| bc85f09109 | |||
| 793b28e43b | |||
| ed4abacd1e | |||
| 8bf55ab46c | |||
| 7edabce657 | |||
| 56187e4da3 | |||
| 31a12faef1 | |||
| eb94c16465 | |||
| e8437a9c54 | |||
| 840039ef1e | |||
| 84a749605e | |||
| c1283d2ab0 | |||
| 07b5c4918b | |||
| 703f0a6b70 | |||
| 5e98dee46f | |||
| b2eddf55d2 | |||
| db7be640d4 | |||
| 04fa597844 | |||
| 694dbeb96c | |||
| 7afd358c3c | |||
| b0a5a570a9 | |||
| c45b84c850 | |||
| d575a43aee | |||
| 0b151ea559 | |||
| 926970b256 | |||
| d58af0bfa5 | |||
| 429da7c6f3 | |||
| 60a7433573 | |||
| 08a5d444d4 | |||
| 1982069b69 | |||
| 7c06c65df3 | |||
| 6263766202 | |||
| 66fbbeff54 | |||
| 3c5ad1328f | |||
| 3345d7e088 | |||
| 2bb25cb874 | |||
| 444a728e7a | |||
| 70bea0c865 | |||
| 6eea1e9035 | |||
| 4deb1836ad | |||
| 4c8007dcfe | |||
| 000116bb3d | |||
| a2b6f21c7a | |||
| a374ee39d3 | |||
| 0f5c82dffd | |||
| 268fed761e | |||
| fe01d252db | |||
| dc36d74337 | |||
| 7a0da7bd8e | |||
| 530d5a4e63 | |||
| 2d847d1704 | |||
| 631d8fcfb1 | |||
| a8888efb88 | |||
| a51ecea9b1 | |||
| 4d49517111 | |||
| d5d1b3567b | |||
| 7827ce3448 | |||
| a05059db01 | |||
| b78490ca56 |
@@ -0,0 +1,57 @@
|
||||
name: ci-bcf-pypi
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
# │ │ ┌───────────── day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# * * * * *
|
||||
- cron: "0 0 18 * *"
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-bcf-pypi.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: ifcopenshell
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2 # https://github.com/actions/checkout
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
python-version: '3.10' # 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: |
|
||||
pip install build
|
||||
cd src/bcf &&
|
||||
make dist
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: ortega2247/pypi-upload-action@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/bcf/dist
|
||||
@@ -1,37 +0,0 @@
|
||||
name: ci-bcf
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell' &&
|
||||
contains(github.event.head_commit.message, '[bcf release]')
|
||||
steps:
|
||||
- run: echo ok go
|
||||
upload:
|
||||
needs: activate
|
||||
name: Upload BCF package to Pypi
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Build package
|
||||
run: |
|
||||
cd src/bcf
|
||||
pip install build
|
||||
python -m build
|
||||
- name: Test
|
||||
run: |
|
||||
cd src/bcf
|
||||
make test
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
packages_dir: src/bcf/dist
|
||||
@@ -88,6 +88,7 @@ jobs:
|
||||
sudo /usr/bin/python -m pip install networkx
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
sudo /usr/bin/python -m pip install mathutils
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
|
||||
@@ -114,6 +114,7 @@ jobs:
|
||||
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
sudo /usr/bin/python -m pip install mathutils
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
|
||||
+4
-1
@@ -96,5 +96,8 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
|
||||
# apple
|
||||
.DS_Store
|
||||
|
||||
# clangd cache
|
||||
.cache
|
||||
|
||||
# Brickschema
|
||||
src/blenderbim/blenderbim/bim/schema/Brick.ttl
|
||||
src/blenderbim/blenderbim/bim/schema/Brick.ttl
|
||||
|
||||
+626
-567
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -4,6 +4,7 @@ set MY_PY_VER=%PY_VER:.=%
|
||||
set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
cmake -G "Ninja" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" ^
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
|
||||
@@ -13,6 +13,7 @@ if [ `uname` == Darwin ]; then
|
||||
fi
|
||||
|
||||
cmake -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
${CMAKE_PLATFORM_FLAGS[@]} \
|
||||
|
||||
+3
-3
@@ -36,10 +36,10 @@ requirements:
|
||||
|
||||
run:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- {{ pin_compatible('occt', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('cgal-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('boost-cpp', max_pin='x.x.x') }}
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- mpfr
|
||||
- gmp # [unix]
|
||||
|
||||
+14
-1
@@ -1,3 +1,9 @@
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
SED:=sed -i
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED:=sed -i '' -e
|
||||
endif
|
||||
|
||||
.PHONY: ci
|
||||
ci:
|
||||
tox
|
||||
@@ -12,6 +18,13 @@ models:
|
||||
cd src && xsdata generate -p bcf.v2.model --unnest-classes --kw-only --slots -ds Google bcf/v2/xsd
|
||||
cd src && xsdata generate -p bcf.v3.model --unnest-classes --kw-only --slots -ds Google bcf/v3/xsd
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -rf dist
|
||||
$(SED) "s/999999/$(VERSION)/" pyproject.toml
|
||||
python -m build
|
||||
$(SED) "s/$(VERSION)/999999/" pyproject.toml
|
||||
|
||||
# .PHONY
|
||||
# api:
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
@@ -17,7 +17,7 @@ dependencies = [
|
||||
"numpy",
|
||||
"ifcopenshell",
|
||||
]
|
||||
version = "0.0.1"
|
||||
version = "0.0.999999"
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: OS Independent",
|
||||
@@ -76,7 +76,7 @@ deps =
|
||||
black
|
||||
isort
|
||||
pylint
|
||||
commands =
|
||||
commands =
|
||||
black {posargs:.}
|
||||
isort {posargs:.}
|
||||
pylint {posargs:.} --output-format=colorized
|
||||
@@ -138,4 +138,4 @@ max-attributes = 10
|
||||
|
||||
[tool.pylint.format]
|
||||
expected-line-ending-format = "LF"
|
||||
max-line-length = 120
|
||||
max-line-length = 120
|
||||
+22
-26
@@ -38,22 +38,25 @@ endif
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
PYVERSION:=py310
|
||||
PYPI_IMP:=cp
|
||||
|
||||
ifeq ($(PYVERSION), py39)
|
||||
PYLIBDIR:=python3.9
|
||||
PYNUMBER:=39
|
||||
PYPI_VERSION:=3.9
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
PYLIBDIR:=python3.10
|
||||
PYNUMBER:=310
|
||||
PYPI_VERSION:=3.10
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py39h40a70d0_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py39hdfdd6bb_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py39h7c9e3ff_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/2d/f2/8ec281d357e8bb7d08dc8d727f0e4c8ef3dae7d3fa75c69c8e452bb82d50/shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/01/61/3ff85fb4bb596ce3d223c8fcf93c8df5c12bc8899dfb4fb3cb1c5b20dd5f/Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl
|
||||
@@ -62,23 +65,23 @@ ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py310h995690b_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py310hf02b7e0_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py310hc4a4660_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/a8/a5/403728b5614b28083f6424dfdefec5fcf58068495fb03bb08532671c642f/shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/f6/51/320986ebd6d46a0e95c2240468ced73153b691ce07617078bcdf30c609ec/Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/linux-64/octomap-1.9.8-h924138e_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/linux-64/boost-cpp-1.78.0-h6582d0a_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
PYPI_PLATFORM:=--platform macosx_10_9_x86_64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py39hd85b194_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py39hc4d6e28_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py39h953a6b8_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/36/a4/7e542a209f862f967d7cb8e939eff155f4294a27d17e16441fb8bdd51a2c/shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/88/7a/ddfe28b485b623361457d4783007c1f9ba83a87f93e7fec32f64793efb6c/Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl
|
||||
@@ -87,23 +90,23 @@ ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py310h1db6f5f_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py310h43da829_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py310h3e792ce_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/1f/2a/dc3353c2431cf53e8d04bb8fba27e584410ca3435c9c85f76d71bf0c0e80/shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/d8/60/b13c00d403f34110e96c1b5c0afa73ce461efe3fe960c3a7e3e7fe190d82/Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-64/octomap-1.9.8-hb8565cd_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-64/boost-cpp-1.78.0-hf5ba120_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-64/zlib-1.2.11-h7795811_1010.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
PYPI_PLATFORM:=--platform macosx_11_0_arm64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py39hc34188a_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py39h13cfc01_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py39h99de9ae_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h9eb174b_0.tar.bz2
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/ea/aa/45fbd031edf3149cb767d8b9f9db45d5faf0324d743c6b8fb0298cc022d0/shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
|
||||
@@ -112,18 +115,19 @@ ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py310h46fc4cd_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py310ha2643af_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py310h629746b_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/ec/41/d59208743e737184e1b403e95a937aebb022b8459e99efbcd5208fc8be46/shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/0c/5f/117b653cad585f3aedfe0de996c292e67d4b020ed77f652e5a6c8c24f908/Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-arm64/assimp-5.0.1-h0f81e16_7.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-arm64/octomap-1.9.8-hffc8910_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-arm64/boost-cpp-1.78.0-h9ed8d21_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), win)
|
||||
PYPI_PLATFORM:=--platform win_amd64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py39h39b25cb_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py39hb6c915b_0.conda
|
||||
@@ -140,7 +144,8 @@ LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe15
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/81/8a/7ac076a86b2632f1872284c5e60ed5f2fc26094875a85b35d9fa17b52504/shapely-2.0.1-cp310-cp310-win_amd64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/02/55/67a3c17b9e7d972ed8c246f104da99ca4f3ea42fba566697e479011b84b6/Pillow-9.2.0-cp310-cp310-win_amd64.whl
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.2.5/download/win-64/assimp-5.2.5-h4dcb625_0.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/win-64/qhull-2020.2-h70d2c02_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/win-64/octomap-1.9.8-h91493d7_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/win-64/boost-cpp-1.78.0-h9f4b32c_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/win-64/zlib-1.2.11-h62dcd97_1010.tar.bz2
|
||||
@@ -164,7 +169,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-fc50bdd-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-9cc1f5f-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
@@ -376,6 +381,10 @@ endif
|
||||
mkdir dist/working
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && mkdir site-packages
|
||||
# Required by IfcCSV
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pandas $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install openpyxl --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install odfpy --target=./site-packages
|
||||
# Required by IFCCityJSON
|
||||
cd dist/working && . env/bin/activate && $(PIP) install cjio --target=./site-packages
|
||||
# Provides express rule validation for ifcopenshell.validate
|
||||
@@ -426,8 +435,7 @@ ifeq ($(PLATFORM), win)
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by hpp-fcl except on Windows
|
||||
ifneq ($(PLATFORM), win)
|
||||
# Required by hpp-fcl
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(QHULL_URL)
|
||||
cd dist/working && tar -xf qhull*
|
||||
@@ -440,8 +448,10 @@ endif
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
ifeq ($(PLATFORM), win)
|
||||
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by hpp-fcl
|
||||
mkdir dist/working
|
||||
@@ -476,7 +486,7 @@ ifeq ($(PLATFORM), macosm1)
|
||||
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
|
||||
endif
|
||||
ifeq ($(PLATFORM), win)
|
||||
# Uh, do nothing, apparently? No DLLs are shipped.
|
||||
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
@@ -590,20 +600,6 @@ endif
|
||||
cd dist/working/XlsxWriter-1.2.9/ && cp -r xlsxwriter ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCOBie for ODS support
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz
|
||||
cd dist/working && tar -xzvf odfpy*
|
||||
cd dist/working/odfpy-1.4.1/ && cp -r odf ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by odfpy
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz
|
||||
cd dist/working && tar -xzvf defusedxml*
|
||||
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by augin
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
|
||||
|
||||
@@ -145,11 +145,17 @@ classes = [
|
||||
ui.BIM_PT_tab_services_object,
|
||||
# Structural analysis
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
ui.BIM_PT_tab_status,
|
||||
ui.BIM_PT_tab_resources,
|
||||
ui.BIM_PT_tab_cost,
|
||||
ui.BIM_PT_tab_sequence,
|
||||
# Facility management
|
||||
ui.BIM_PT_tab_handover,
|
||||
ui.BIM_PT_tab_operations,
|
||||
# Quality and coordination
|
||||
ui.BIM_PT_tab_quality_control,
|
||||
ui.BIM_PT_tab_clash_detection,
|
||||
ui.BIM_PT_tab_collaboration,
|
||||
ui.BIM_PT_tab_sandbox,
|
||||
# TODO: move this somewhere else and clean it up
|
||||
|
||||
@@ -44,17 +44,22 @@
|
||||
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea">
|
||||
<g id="dot">
|
||||
<circle r="1" fill="black" stroke="black" style="stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea-CONTROLPOINT">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.25;" d="M 2.286165,1.4798666 H -1.0000316e-7 -2.2861651 L -1.1430826,-0.5000101 -1.0000316e-7,-2.479888 1.1430827,-0.5000101 Z" />
|
||||
<g id="setout-point">
|
||||
<circle fill="white" stroke="black" r="2.5" style="stroke-width: 0.5"/>
|
||||
<path d="M 0 0 L -2.5 0 A 2.5 2.5 0 0 1 0 -2.5 Z" fill="black"/>
|
||||
<path d="M 0 0 L 2.5 0 A 2.5 2.5 0 0 1 0 2.5 Z" fill="black"/>
|
||||
</g>
|
||||
<g id="control-point">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M 2.286165,1.4798666 H -1.0000316e-7 -2.2861651 L -1.1430826,-0.5000101 -1.0000316e-7,-2.479888 1.1430827,-0.5000101 Z" />
|
||||
<circle r="0.5" fill="black" stroke="black" style="stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea-TRAVERSEPOINT">
|
||||
<g id="traverse-point">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M -2,-2 2,2 M -2,2 2,-2" />
|
||||
</g>
|
||||
<g id="SurveyArea-SPOTELEVATION">
|
||||
<g id="spot-elevation">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M 0,-2 0,2 M -2,0 2,0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -51,5 +51,5 @@ var json_data = `{{{json_data}}}`;
|
||||
<script>
|
||||
var data = `{{{data}}}`;
|
||||
setupPage(data);
|
||||
create_gantt_chart(json_data)
|
||||
</script>
|
||||
create_gantt_chart(atob(json_data))
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('/dev/null','2023-07-30T21:07:33+10:00',(),(),'IfcOpenShell v0.7.0-8f41ae0c1','IfcOpenShell v0.7.0-8f41ae0c1','Nobody');
|
||||
FILE_NAME('/dev/null','2023-09-02T16:09:02+10:00',(),(),'IfcOpenShell v0.7.0-fbd8ea1ed','IfcOpenShell v0.7.0-fbd8ea1ed','Nobody');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('0TszJmTqLCyxcP10DtK_MP',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0Y_CJXSsbCgBlSGFw0mbLa',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('3IxQaOPH5Fx90CGmGm1gtQ',$,$,$,#1,(#2));
|
||||
#1=IFCPROJECT('1UlLTVwDzDvR$QNEAxFx6F',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('3QN02XEh1FQh0fii364VEi',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('0aiEyU5lL7mAdB5xwdXbUN',$,$,$,#1,(#2));
|
||||
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#6=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
@@ -25,82 +25,82 @@ DATA;
|
||||
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
|
||||
#19=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
|
||||
#20=IFCMATERIAL('Unknown',$,$);
|
||||
#21=IFCWALLTYPE('3U0gAUglf9WBlcfpB15EK0',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#21=IFCWALLTYPE('0Ir6HewafB2hnPRigxrUnM',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#22=IFCMATERIALLAYERSET((#24),$,$);
|
||||
#23=IFCRELASSOCIATESMATERIAL('3J36H22Gz1ovVzdiKgApaM',$,$,$,(#21),#22);
|
||||
#23=IFCRELASSOCIATESMATERIAL('3S$YbWQCnDCA2tmoikSnrh',$,$,$,(#21),#22);
|
||||
#24=IFCMATERIALLAYER(#20,0.05,$,$,$,$,$);
|
||||
#25=IFCRELDECLARES('28ApgMJzH10h$Ng$fmCpYf',$,$,$,#2,(#63,#21,#54,#87,#153,#34,#67,#210,#38,#82,#77,#42,#26,#48,#72,#59,#92,#30,#96));
|
||||
#26=IFCWALLTYPE('1wGfdUdC10TuASJo4jnO99',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#25=IFCRELDECLARES('1eLCJqssP2kvQIdmPlRK19',$,$,$,#2,(#38,#82,#77,#42,#26,#48,#72,#59,#92,#30,#96,#63,#54,#87,#153,#34,#67,#21,#210));
|
||||
#26=IFCWALLTYPE('2oUFJayonDlBTcbJtxYFSv',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#27=IFCMATERIALLAYERSET((#29),$,$);
|
||||
#28=IFCRELASSOCIATESMATERIAL('0NRoxSQPzCgQHfsu5ru91z',$,$,$,(#26),#27);
|
||||
#28=IFCRELASSOCIATESMATERIAL('0n3vPKxufDgO48f4yfGtUd',$,$,$,(#26),#27);
|
||||
#29=IFCMATERIALLAYER(#20,0.1,$,$,$,$,$);
|
||||
#30=IFCWALLTYPE('1lt5MuGLLDfwmZd0tNruF7',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#30=IFCWALLTYPE('0UDhWxnIXDMOxZ2yPDCUMP',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#31=IFCMATERIALLAYERSET((#33),$,$);
|
||||
#32=IFCRELASSOCIATESMATERIAL('21rGVxPODC$xmWYFaDy1T6',$,$,$,(#30),#31);
|
||||
#32=IFCRELASSOCIATESMATERIAL('2Boxz3uaP0LOy6luKau2R0',$,$,$,(#30),#31);
|
||||
#33=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#34=IFCWALLTYPE('0NZm1D8h12xAxyW7fL5nc1',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#34=IFCWALLTYPE('20fPutWQrFyeMIiKxc$MkU',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#35=IFCMATERIALLAYERSET((#37),$,$);
|
||||
#36=IFCRELASSOCIATESMATERIAL('2yvmN42crCJevtirp5cMeV',$,$,$,(#34),#35);
|
||||
#36=IFCRELASSOCIATESMATERIAL('2qAjlTTxjBkvdp80A9XdMZ',$,$,$,(#34),#35);
|
||||
#37=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
#38=IFCCOVERINGTYPE('1eb4wZ_5n6xRGrs3rlKMiM',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#38=IFCCOVERINGTYPE('100hdtcxzBdQATomAi0tv5',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#39=IFCMATERIALLAYERSET((#41),$,$);
|
||||
#40=IFCRELASSOCIATESMATERIAL('3HKIKf06T6xRqM6Lp69$OM',$,$,$,(#38),#39);
|
||||
#40=IFCRELASSOCIATESMATERIAL('3tO1xRn9b06utMa1TZC$WW',$,$,$,(#38),#39);
|
||||
#41=IFCMATERIALLAYER(#20,0.01,$,$,$,$,$);
|
||||
#42=IFCCOVERINGTYPE('2E89aYmtrEfxhX_jsNqviN',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
|
||||
#42=IFCCOVERINGTYPE('1sO51oul95ABWezHr7P7Dk',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
|
||||
#43=IFCMATERIALLAYERSET((#45),$,$);
|
||||
#44=IFCRELASSOCIATESMATERIAL('2115lEhhP4Z9G1Yhd_DqSC',$,$,$,(#42),#43);
|
||||
#44=IFCRELASSOCIATESMATERIAL('0w6lexCg19TuiqqTxo91zc',$,$,$,(#42),#43);
|
||||
#45=IFCMATERIALLAYER(#20,0.02,$,$,$,$,$);
|
||||
#46=IFCPROPERTYSET('0vtU63YmjAgg8DeHlTgNx7',$,'EPset_Parametric',$,(#47));
|
||||
#46=IFCPROPERTYSET('3ytgFxH0571uBCG4mBNXI9',$,'EPset_Parametric',$,(#47));
|
||||
#47=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$);
|
||||
#48=IFCCOVERINGTYPE('0XiSBI57L9Uf8f2NaQ_ksi',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
|
||||
#48=IFCCOVERINGTYPE('09GKC1aO56qwEPF9halcxJ',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
|
||||
#49=IFCMATERIALLAYERSET((#51),$,$);
|
||||
#50=IFCRELASSOCIATESMATERIAL('3U3JdGAmbABA4mB$Fgpq1l',$,$,$,(#48),#49);
|
||||
#50=IFCRELASSOCIATESMATERIAL('0rDrnpiX58DgQhcURj_WlB',$,$,$,(#48),#49);
|
||||
#51=IFCMATERIALLAYER(#20,0.03,$,$,$,$,$);
|
||||
#52=IFCPROPERTYSET('17nhQ5I4b9ZvhDu4gRJLKO',$,'EPset_Parametric',$,(#53));
|
||||
#52=IFCPROPERTYSET('14u6OUyPL5y8O$QkEnB6Rr',$,'EPset_Parametric',$,(#53));
|
||||
#53=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$);
|
||||
#54=IFCRAMPTYPE('1BTHjVDP19tgb3MDCSkWCJ',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#54=IFCRAMPTYPE('2G4JrZ99j2B9SkIwWDk0sC',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#55=IFCMATERIALLAYERSET((#57),$,$);
|
||||
#56=IFCRELASSOCIATESMATERIAL('3HN21bHtL16vvHA2GnnxH1',$,$,$,(#54),#55);
|
||||
#56=IFCRELASSOCIATESMATERIAL('2nNlFzNPj0_h6_NwdVJvW$',$,$,$,(#54),#55);
|
||||
#57=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#58=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3);
|
||||
#59=IFCPILETYPE('04_2WZSqH8TgT7ET6DCCpd',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#59=IFCPILETYPE('3uIc5iHjP5Lel3X6KXyN8W',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#60=IFCMATERIALPROFILESET($,$,(#62),$);
|
||||
#61=IFCRELASSOCIATESMATERIAL('1qTZZRXsPF9hmYwT$tgFgp',$,$,$,(#59),#60);
|
||||
#61=IFCRELASSOCIATESMATERIAL('13buLvs1r5vAyhgbabb2g1',$,$,$,(#59),#60);
|
||||
#62=IFCMATERIALPROFILE($,$,#20,#58,$,$);
|
||||
#63=IFCSLABTYPE('3_OV9$Sd52KRhIW2TWIxbW',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#63=IFCSLABTYPE('2CbahfxvnFpPoHyMBpGTQm',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#64=IFCMATERIALLAYERSET((#66),$,$);
|
||||
#65=IFCRELASSOCIATESMATERIAL('1agyf45$X1NhR127pUL0Iw',$,$,$,(#63),#64);
|
||||
#65=IFCRELASSOCIATESMATERIAL('2Irb7$9xL8YOXW99sF0SOp',$,$,$,(#63),#64);
|
||||
#66=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#67=IFCSLABTYPE('0LvmpIGf9AVPcG$Kt06TEk',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#67=IFCSLABTYPE('04dx2Vg4vEp9ZOKGI1ifWb',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#68=IFCMATERIALLAYERSET((#70),$,$);
|
||||
#69=IFCRELASSOCIATESMATERIAL('0EKQPSL656secN86SgeutH',$,$,$,(#67),#68);
|
||||
#69=IFCRELASSOCIATESMATERIAL('04tl3Me6T6xOb$WEvv_dL0',$,$,$,(#67),#68);
|
||||
#70=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
#71=IFCRECTANGLEPROFILEDEF(.AREA.,'500x600',$,0.5,0.6);
|
||||
#72=IFCCOLUMNTYPE('1SI8m9m813tAXfSnM1fCbm',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#72=IFCCOLUMNTYPE('13rk_6wmDApeVQWfrArKbl',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#73=IFCMATERIALPROFILESET($,$,(#75),$);
|
||||
#74=IFCRELASSOCIATESMATERIAL('1EhwSTRtPBQgBt2x6dhuT$',$,$,$,(#72),#73);
|
||||
#74=IFCRELASSOCIATESMATERIAL('0X3OUScT9E1BjOvJWiUrv3',$,$,$,(#72),#73);
|
||||
#75=IFCMATERIALPROFILE($,$,#20,#71,$,$);
|
||||
#76=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,'500.0x5.0 CHS',$,0.25,0.005);
|
||||
#77=IFCCOLUMNTYPE('25GZwy2zLFcgi57r5S8iY3',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#77=IFCCOLUMNTYPE('0IaS5T1Uv6DQu$BJ5I3bks',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#78=IFCMATERIALPROFILESET($,$,(#80),$);
|
||||
#79=IFCRELASSOCIATESMATERIAL('2VURAOhz93ZQqT9YJUA1DO',$,$,$,(#77),#78);
|
||||
#79=IFCRELASSOCIATESMATERIAL('1HWcDe7a17WwPeKCHnckid',$,$,$,(#77),#78);
|
||||
#80=IFCMATERIALPROFILE($,$,#20,#76,$,$);
|
||||
#81=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,'150x75x2.0 RHS',$,0.075,0.15,0.002,0.005,0.005);
|
||||
#82=IFCCOLUMNTYPE('1Wg$I84s9BTh$iDuZnmSkH',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#82=IFCCOLUMNTYPE('13p6$J2Jv84RFI5QAp6FTQ',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#83=IFCMATERIALPROFILESET($,$,(#85),$);
|
||||
#84=IFCRELASSOCIATESMATERIAL('3fIIwEe7DAle7x_zISvMUD',$,$,$,(#82),#83);
|
||||
#84=IFCRELASSOCIATESMATERIAL('1GWCzHY657$9kUJrD0AEir',$,$,$,(#82),#83);
|
||||
#85=IFCMATERIALPROFILE($,$,#20,#81,$,$);
|
||||
#86=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$);
|
||||
#87=IFCBEAMTYPE('2yg2Irs7L26gFsD_AvvFUu',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#87=IFCBEAMTYPE('1Y_PtOyJP9YfZkU59r$vs1',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#88=IFCMATERIALPROFILESET($,$,(#90),$);
|
||||
#89=IFCRELASSOCIATESMATERIAL('1PU_gbkifF9hYpi9DvXTAU',$,$,$,(#87),#88);
|
||||
#89=IFCRELASSOCIATESMATERIAL('2CfmfKJ0X45xijxsDUHZ85',$,$,$,(#87),#88);
|
||||
#90=IFCMATERIALPROFILE($,$,#20,#86,$,$);
|
||||
#91=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005);
|
||||
#92=IFCBEAMTYPE('2Nf2L5qpz8K8KhUNFBg_fT',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#92=IFCBEAMTYPE('2qogXGNrb9EANilnZjnGjq',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#93=IFCMATERIALPROFILESET($,$,(#95),$);
|
||||
#94=IFCRELASSOCIATESMATERIAL('1SgarGNCzEQQpfs29NMq0y',$,$,$,(#92),#93);
|
||||
#94=IFCRELASSOCIATESMATERIAL('3$1pGIrVLA2RTOmJIsbelu',$,$,$,(#92),#93);
|
||||
#95=IFCMATERIALPROFILE($,$,#20,#91,$,$);
|
||||
#96=IFCWINDOWTYPE('2zRUFP8Ef81xDOR2iJx1zK',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#96=IFCWINDOWTYPE('1Dc9pAExL3fxL9jScEo2EF',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#97=IFCINDEXEDPOLYGONALFACE((13,17,18,14));
|
||||
#98=IFCINDEXEDPOLYGONALFACE((5,6,3,4));
|
||||
#99=IFCINDEXEDPOLYGONALFACE((7,8,2,1));
|
||||
@@ -157,7 +157,7 @@ DATA;
|
||||
#150=IFCDIRECTION((0.,0.,1.));
|
||||
#151=IFCAXIS2PLACEMENT3D(#148,#150,#149);
|
||||
#152=IFCREPRESENTATIONMAP(#151,#147);
|
||||
#153=IFCDOORTYPE('0oekOIA$z8lxWiQs9_tFWU',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#153=IFCDOORTYPE('05Z1DhJjj11wczPRkvXSPE',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#154=IFCINDEXEDPOLYGONALFACE((17,16,15,14));
|
||||
#155=IFCINDEXEDPOLYGONALFACE((2,3,29,28));
|
||||
#156=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31));
|
||||
@@ -214,7 +214,7 @@ DATA;
|
||||
#207=IFCDIRECTION((0.,0.,1.));
|
||||
#208=IFCAXIS2PLACEMENT3D(#205,#207,#206);
|
||||
#209=IFCREPRESENTATIONMAP(#208,#204);
|
||||
#210=IFCFURNITURETYPE('0I2mc$m3T60hLXZYg9Y1wu',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
|
||||
#210=IFCFURNITURETYPE('3ZMmSB_rfBnhrBckPTFKvP',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
|
||||
#211=IFCINDEXEDPOLYGONALFACE((187,278,44));
|
||||
#212=IFCINDEXEDPOLYGONALFACE((21,52,60));
|
||||
#213=IFCINDEXEDPOLYGONALFACE((91,100,31));
|
||||
@@ -957,131 +957,140 @@ DATA;
|
||||
#950=IFCDIRECTION((0.,0.,1.));
|
||||
#951=IFCAXIS2PLACEMENT3D(#948,#950,#949);
|
||||
#952=IFCREPRESENTATIONMAP(#951,#947);
|
||||
#953=IFCTYPEPRODUCT('2be705eyD4DPBr6j4K7ZEx',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#954),$,$);
|
||||
#954=IFCPROPERTYSET('0uEfutIxDDIxMIBUJOZpSM',$,'EPset_Annotation',$,(#955));
|
||||
#955=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('dashed'),$);
|
||||
#956=IFCTYPEPRODUCT('1M7mm9wUHFoxdM0MdAMyqM',$,'FINE',$,'IfcAnnotation/LINEWORK',(#957),$,$);
|
||||
#957=IFCPROPERTYSET('3AZ8m1Bdn1gfbvagzKr2c3',$,'EPset_Annotation',$,(#958));
|
||||
#958=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('fine'),$);
|
||||
#959=IFCTYPEPRODUCT('3LdrNIFOvAkBzrNnqWAZfA',$,'THIN',$,'IfcAnnotation/LINEWORK',(#960),$,$);
|
||||
#960=IFCPROPERTYSET('0$9iwgSID7xBul4Tb9r2du',$,'EPset_Annotation',$,(#961));
|
||||
#961=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thin'),$);
|
||||
#962=IFCTYPEPRODUCT('3fFNO0t1TB$wYRaIkKwUCH',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#963),$,$);
|
||||
#963=IFCPROPERTYSET('33Fm$9zgb1cgsCOy859RlR',$,'EPset_Annotation',$,(#964));
|
||||
#964=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('medium'),$);
|
||||
#965=IFCTYPEPRODUCT('1CpMBhemj5VQC43bU9J7zG',$,'THICK',$,'IfcAnnotation/LINEWORK',(#966),$,$);
|
||||
#966=IFCPROPERTYSET('1OLDsFSDz2TPz5ifkQvWo8',$,'EPset_Annotation',$,(#967));
|
||||
#967=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thick'),$);
|
||||
#968=IFCTYPEPRODUCT('0NITtya3b16hoA3sh17SL4',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#969),$,$);
|
||||
#969=IFCPROPERTYSET('1_kfqWVDn0puI29Ah6eitX',$,'EPset_Annotation',$,(#970));
|
||||
#970=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('strong'),$);
|
||||
#971=IFCTYPEPRODUCT('0rRN9EsvL5AQ3PorxrZSKM',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#972),(#991),$);
|
||||
#972=IFCPROPERTYSET('3lRZ$d2VH8cxZYh$mm45iq',$,'EPset_Annotation',$,(#973));
|
||||
#973=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('door-tag'),$);
|
||||
#974=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#975=IFCDIRECTION((0.,0.,1.));
|
||||
#976=IFCDIRECTION((1.,0.,0.));
|
||||
#977=IFCAXIS2PLACEMENT3D(#974,#975,#976);
|
||||
#978=IFCPLANAREXTENT(1000.,1000.);
|
||||
#979=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#977,.RIGHT.,#978,'center');
|
||||
#980=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#981=IFCDIRECTION((0.,0.,1.));
|
||||
#982=IFCDIRECTION((1.,0.,0.));
|
||||
#983=IFCAXIS2PLACEMENT3D(#980,#981,#982);
|
||||
#984=IFCPLANAREXTENT(1000.,1000.);
|
||||
#985=IFCTEXTLITERALWITHEXTENT('{{Name}}',#983,.RIGHT.,#984,'center');
|
||||
#986=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#979,#985));
|
||||
#987=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#988=IFCDIRECTION((1.,0.,0.));
|
||||
#989=IFCDIRECTION((0.,0.,1.));
|
||||
#990=IFCAXIS2PLACEMENT3D(#987,#989,#988);
|
||||
#991=IFCREPRESENTATIONMAP(#990,#986);
|
||||
#992=IFCTYPEPRODUCT('0bFZUWHjbA2e03hRHNinTt',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#993),(#1006),$);
|
||||
#993=IFCPROPERTYSET('3TJOWv5NDBXBNCshYFjsFV',$,'EPset_Annotation',$,(#994));
|
||||
#994=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('window-tag'),$);
|
||||
#995=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#996=IFCDIRECTION((0.,0.,1.));
|
||||
#953=IFCTYPEPRODUCT('1hmWolVQb9buX2GhHwaVFB',$,'SETOUT-POINT',$,'IfcAnnotation/SYMBOL',(#954),$,$);
|
||||
#954=IFCPROPERTYSET('2YNCFr62b8vhuhHGspQl4$',$,'EPset_Annotation',$,(#955));
|
||||
#955=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('setout-point'),$);
|
||||
#956=IFCTYPEPRODUCT('3H4oE1a_9AwQE8OFsCjdfh',$,'CONTROL-POINT',$,'IfcAnnotation/SYMBOL',(#957),$,$);
|
||||
#957=IFCPROPERTYSET('0ARWSOH$z3kQDRkpEPHWCt',$,'EPset_Annotation',$,(#958));
|
||||
#958=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('control-point'),$);
|
||||
#959=IFCTYPEPRODUCT('3YDPnyrWn2wRWgRzb0V7Kx',$,'TRAVERSE-POINT',$,'IfcAnnotation/SYMBOL',(#960),$,$);
|
||||
#960=IFCPROPERTYSET('1v$MzHU1b82hDqHdtz4e5y',$,'EPset_Annotation',$,(#961));
|
||||
#961=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('traverse-point'),$);
|
||||
#962=IFCTYPEPRODUCT('31Em8$VNL0Mh$9EZ3pXjpL',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#963),$,$);
|
||||
#963=IFCPROPERTYSET('0K84$piRb31PmkAwZfP5kd',$,'EPset_Annotation',$,(#964));
|
||||
#964=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('dashed'),$);
|
||||
#965=IFCTYPEPRODUCT('3P0K0gDxP4NgwnuLGqlyW6',$,'FINE',$,'IfcAnnotation/LINEWORK',(#966),$,$);
|
||||
#966=IFCPROPERTYSET('179vwKcVv9uet4_kI4NT9G',$,'EPset_Annotation',$,(#967));
|
||||
#967=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('fine'),$);
|
||||
#968=IFCTYPEPRODUCT('0Rh30iszn41xBCgQJl2w2c',$,'THIN',$,'IfcAnnotation/LINEWORK',(#969),$,$);
|
||||
#969=IFCPROPERTYSET('0bZV_Tby121uj5O3n08l1l',$,'EPset_Annotation',$,(#970));
|
||||
#970=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thin'),$);
|
||||
#971=IFCTYPEPRODUCT('3ti_6xztr7qPp9dASz9vP4',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#972),$,$);
|
||||
#972=IFCPROPERTYSET('19ZhF3bxL07evL8gi27Kei',$,'EPset_Annotation',$,(#973));
|
||||
#973=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('medium'),$);
|
||||
#974=IFCTYPEPRODUCT('2hlcxjzxv8zf0gh1md23xY',$,'THICK',$,'IfcAnnotation/LINEWORK',(#975),$,$);
|
||||
#975=IFCPROPERTYSET('0wRtpbInzE2AkqX8RIreQr',$,'EPset_Annotation',$,(#976));
|
||||
#976=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thick'),$);
|
||||
#977=IFCTYPEPRODUCT('1pefU$PA1ENw7kZG9T3mjr',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#978),$,$);
|
||||
#978=IFCPROPERTYSET('1RpJZBysn8shqevvAquPQ8',$,'EPset_Annotation',$,(#979));
|
||||
#979=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('strong'),$);
|
||||
#980=IFCTYPEPRODUCT('1HKC8zCIv5_ef3dEfa$L5N',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#981),(#1000),$);
|
||||
#981=IFCPROPERTYSET('0aC8JrqFX6HhKUhjdLw4mN',$,'EPset_Annotation',$,(#982));
|
||||
#982=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('door-tag'),$);
|
||||
#983=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#984=IFCDIRECTION((0.,0.,1.));
|
||||
#985=IFCDIRECTION((1.,0.,0.));
|
||||
#986=IFCAXIS2PLACEMENT3D(#983,#984,#985);
|
||||
#987=IFCPLANAREXTENT(1000.,1000.);
|
||||
#988=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#986,.RIGHT.,#987,'center');
|
||||
#989=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#990=IFCDIRECTION((0.,0.,1.));
|
||||
#991=IFCDIRECTION((1.,0.,0.));
|
||||
#992=IFCAXIS2PLACEMENT3D(#989,#990,#991);
|
||||
#993=IFCPLANAREXTENT(1000.,1000.);
|
||||
#994=IFCTEXTLITERALWITHEXTENT('{{Name}}',#992,.RIGHT.,#993,'center');
|
||||
#995=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#988,#994));
|
||||
#996=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#997=IFCDIRECTION((1.,0.,0.));
|
||||
#998=IFCAXIS2PLACEMENT3D(#995,#996,#997);
|
||||
#999=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1000=IFCTEXTLITERALWITHEXTENT('{{Name}}',#998,.RIGHT.,#999,'center');
|
||||
#1001=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1000));
|
||||
#1002=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1003=IFCDIRECTION((1.,0.,0.));
|
||||
#1004=IFCDIRECTION((0.,0.,1.));
|
||||
#1005=IFCAXIS2PLACEMENT3D(#1002,#1004,#1003);
|
||||
#1006=IFCREPRESENTATIONMAP(#1005,#1001);
|
||||
#1007=IFCTYPEPRODUCT('2DlmDdasX05un_uMP4O23U',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1008),(#1033),$);
|
||||
#1008=IFCPROPERTYSET('1$OAHURHr3GON7ZzAJnv72',$,'EPset_Annotation',$,(#1009));
|
||||
#1009=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('space-tag'),$);
|
||||
#1010=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1011=IFCDIRECTION((0.,0.,1.));
|
||||
#998=IFCDIRECTION((0.,0.,1.));
|
||||
#999=IFCAXIS2PLACEMENT3D(#996,#998,#997);
|
||||
#1000=IFCREPRESENTATIONMAP(#999,#995);
|
||||
#1001=IFCTYPEPRODUCT('2Jy_wl$FX8h9mgYT1$Y9oc',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#1002),(#1015),$);
|
||||
#1002=IFCPROPERTYSET('2ncCyO$LPCzv7kqXZK_Omh',$,'EPset_Annotation',$,(#1003));
|
||||
#1003=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('window-tag'),$);
|
||||
#1004=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1005=IFCDIRECTION((0.,0.,1.));
|
||||
#1006=IFCDIRECTION((1.,0.,0.));
|
||||
#1007=IFCAXIS2PLACEMENT3D(#1004,#1005,#1006);
|
||||
#1008=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1009=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1007,.RIGHT.,#1008,'center');
|
||||
#1010=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1009));
|
||||
#1011=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1012=IFCDIRECTION((1.,0.,0.));
|
||||
#1013=IFCAXIS2PLACEMENT3D(#1010,#1011,#1012);
|
||||
#1014=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1015=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1013,.RIGHT.,#1014,'center');
|
||||
#1016=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1017=IFCDIRECTION((0.,0.,1.));
|
||||
#1018=IFCDIRECTION((1.,0.,0.));
|
||||
#1019=IFCAXIS2PLACEMENT3D(#1016,#1017,#1018);
|
||||
#1020=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1021=IFCTEXTLITERALWITHEXTENT('{{Description}}',#1019,.RIGHT.,#1020,'center');
|
||||
#1022=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1023=IFCDIRECTION((0.,0.,1.));
|
||||
#1024=IFCDIRECTION((1.,0.,0.));
|
||||
#1025=IFCAXIS2PLACEMENT3D(#1022,#1023,#1024);
|
||||
#1026=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1027=IFCTEXTLITERALWITHEXTENT('``round({{Qto_SpaceBaseQuantities.NetFloorArea}} or 0., 2)``',#1025,.RIGHT.,#1026,'center');
|
||||
#1028=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1015,#1021,#1027));
|
||||
#1029=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1030=IFCDIRECTION((1.,0.,0.));
|
||||
#1031=IFCDIRECTION((0.,0.,1.));
|
||||
#1032=IFCAXIS2PLACEMENT3D(#1029,#1031,#1030);
|
||||
#1033=IFCREPRESENTATIONMAP(#1032,#1028);
|
||||
#1034=IFCTYPEPRODUCT('2vNx3CIlrFs8bLFlmGvJF3',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1035),(#1048),$);
|
||||
#1035=IFCPROPERTYSET('1l$T4lUqnBb93zcgfbSPvx',$,'EPset_Annotation',$,(#1036));
|
||||
#1036=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('rectangle-tag'),$);
|
||||
#1037=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1038=IFCDIRECTION((0.,0.,1.));
|
||||
#1013=IFCDIRECTION((0.,0.,1.));
|
||||
#1014=IFCAXIS2PLACEMENT3D(#1011,#1013,#1012);
|
||||
#1015=IFCREPRESENTATIONMAP(#1014,#1010);
|
||||
#1016=IFCTYPEPRODUCT('1LTLrGzxH4qhuWGNeTlTRi',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1017),(#1042),$);
|
||||
#1017=IFCPROPERTYSET('2JpTIsSZf3zQUWK48RLIcL',$,'EPset_Annotation',$,(#1018));
|
||||
#1018=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('space-tag'),$);
|
||||
#1019=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1020=IFCDIRECTION((0.,0.,1.));
|
||||
#1021=IFCDIRECTION((1.,0.,0.));
|
||||
#1022=IFCAXIS2PLACEMENT3D(#1019,#1020,#1021);
|
||||
#1023=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1024=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1022,.RIGHT.,#1023,'center');
|
||||
#1025=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1026=IFCDIRECTION((0.,0.,1.));
|
||||
#1027=IFCDIRECTION((1.,0.,0.));
|
||||
#1028=IFCAXIS2PLACEMENT3D(#1025,#1026,#1027);
|
||||
#1029=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1030=IFCTEXTLITERALWITHEXTENT('{{Description}}',#1028,.RIGHT.,#1029,'center');
|
||||
#1031=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1032=IFCDIRECTION((0.,0.,1.));
|
||||
#1033=IFCDIRECTION((1.,0.,0.));
|
||||
#1034=IFCAXIS2PLACEMENT3D(#1031,#1032,#1033);
|
||||
#1035=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1036=IFCTEXTLITERALWITHEXTENT('``round({{Qto_SpaceBaseQuantities.NetFloorArea}} or 0., 2)``',#1034,.RIGHT.,#1035,'center');
|
||||
#1037=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1024,#1030,#1036));
|
||||
#1038=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1039=IFCDIRECTION((1.,0.,0.));
|
||||
#1040=IFCAXIS2PLACEMENT3D(#1037,#1038,#1039);
|
||||
#1041=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1042=IFCTEXTLITERALWITHEXTENT('{{material.Name}}',#1040,.RIGHT.,#1041,'center');
|
||||
#1043=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1042));
|
||||
#1044=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1045=IFCDIRECTION((1.,0.,0.));
|
||||
#1046=IFCDIRECTION((0.,0.,1.));
|
||||
#1047=IFCAXIS2PLACEMENT3D(#1044,#1046,#1045);
|
||||
#1048=IFCREPRESENTATIONMAP(#1047,#1043);
|
||||
#1049=IFCTYPEPRODUCT('3hqzFtbSb56RjhAZNaB4Vh',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1050),(#1063),$);
|
||||
#1050=IFCPROPERTYSET('3U7kaKt_nBUvD$U2ryif_H',$,'EPset_Annotation',$,(#1051));
|
||||
#1051=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1052=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1053=IFCDIRECTION((0.,0.,1.));
|
||||
#1040=IFCDIRECTION((0.,0.,1.));
|
||||
#1041=IFCAXIS2PLACEMENT3D(#1038,#1040,#1039);
|
||||
#1042=IFCREPRESENTATIONMAP(#1041,#1037);
|
||||
#1043=IFCTYPEPRODUCT('3Or$DhaVf6ygGBUpRb2PMs',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1044),(#1057),$);
|
||||
#1044=IFCPROPERTYSET('2YfvIc6dPDdwWjERHZZlof',$,'EPset_Annotation',$,(#1045));
|
||||
#1045=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('rectangle-tag'),$);
|
||||
#1046=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1047=IFCDIRECTION((0.,0.,1.));
|
||||
#1048=IFCDIRECTION((1.,0.,0.));
|
||||
#1049=IFCAXIS2PLACEMENT3D(#1046,#1047,#1048);
|
||||
#1050=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1051=IFCTEXTLITERALWITHEXTENT('{{material.Name}}',#1049,.RIGHT.,#1050,'center');
|
||||
#1052=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1051));
|
||||
#1053=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1054=IFCDIRECTION((1.,0.,0.));
|
||||
#1055=IFCAXIS2PLACEMENT3D(#1052,#1053,#1054);
|
||||
#1056=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1057=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#1055,.RIGHT.,#1056,'center');
|
||||
#1058=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1057));
|
||||
#1059=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1060=IFCDIRECTION((1.,0.,0.));
|
||||
#1061=IFCDIRECTION((0.,0.,1.));
|
||||
#1062=IFCAXIS2PLACEMENT3D(#1059,#1061,#1060);
|
||||
#1063=IFCREPRESENTATIONMAP(#1062,#1058);
|
||||
#1064=IFCTYPEPRODUCT('2SnSxJPiD9sh$NDx92OCSS',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1065),(#1078),$);
|
||||
#1065=IFCPROPERTYSET('3Lu86QVuv5nfjkuPLvdQSH',$,'EPset_Annotation',$,(#1066));
|
||||
#1066=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1067=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1068=IFCDIRECTION((0.,0.,1.));
|
||||
#1055=IFCDIRECTION((0.,0.,1.));
|
||||
#1056=IFCAXIS2PLACEMENT3D(#1053,#1055,#1054);
|
||||
#1057=IFCREPRESENTATIONMAP(#1056,#1052);
|
||||
#1058=IFCTYPEPRODUCT('01Hhk_DbjDROhyr91Hp$aV',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1059),(#1072),$);
|
||||
#1059=IFCPROPERTYSET('0LyRD7lHr17uJo7XNzCvZq',$,'EPset_Annotation',$,(#1060));
|
||||
#1060=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1061=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1062=IFCDIRECTION((0.,0.,1.));
|
||||
#1063=IFCDIRECTION((1.,0.,0.));
|
||||
#1064=IFCAXIS2PLACEMENT3D(#1061,#1062,#1063);
|
||||
#1065=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1066=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#1064,.RIGHT.,#1065,'center');
|
||||
#1067=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1066));
|
||||
#1068=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1069=IFCDIRECTION((1.,0.,0.));
|
||||
#1070=IFCAXIS2PLACEMENT3D(#1067,#1068,#1069);
|
||||
#1071=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1072=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1070,.RIGHT.,#1071,'center');
|
||||
#1073=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1072));
|
||||
#1074=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1075=IFCDIRECTION((1.,0.,0.));
|
||||
#1076=IFCDIRECTION((0.,0.,1.));
|
||||
#1077=IFCAXIS2PLACEMENT3D(#1074,#1076,#1075);
|
||||
#1078=IFCREPRESENTATIONMAP(#1077,#1073);
|
||||
#1070=IFCDIRECTION((0.,0.,1.));
|
||||
#1071=IFCAXIS2PLACEMENT3D(#1068,#1070,#1069);
|
||||
#1072=IFCREPRESENTATIONMAP(#1071,#1067);
|
||||
#1073=IFCTYPEPRODUCT('0a8MHfTUzA5AV8H6p1bqC9',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1074),(#1087),$);
|
||||
#1074=IFCPROPERTYSET('2EboL9u1j68Q_Mp8dBa7ux',$,'EPset_Annotation',$,(#1075));
|
||||
#1075=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1076=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1077=IFCDIRECTION((0.,0.,1.));
|
||||
#1078=IFCDIRECTION((1.,0.,0.));
|
||||
#1079=IFCAXIS2PLACEMENT3D(#1076,#1077,#1078);
|
||||
#1080=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1081=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1079,.RIGHT.,#1080,'center');
|
||||
#1082=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1081));
|
||||
#1083=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1084=IFCDIRECTION((1.,0.,0.));
|
||||
#1085=IFCDIRECTION((0.,0.,1.));
|
||||
#1086=IFCAXIS2PLACEMENT3D(#1083,#1085,#1084);
|
||||
#1087=IFCREPRESENTATIONMAP(#1086,#1082);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
||||
@@ -5,27 +5,27 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21));
|
||||
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('2T$a4OFsv2LeD5JeBKEV4f',$,'IsNTS','Whether or not the scale is intended to be significant',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#8=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#9=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#12=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#13=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#14=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#15=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#16=IFCSIMPLEPROPERTYTEMPLATE('1$xfo9EVb26QLqmPll2_RK',$,'MetricPrecision','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#17=IFCSIMPLEPROPERTYTEMPLATE('38uAtrp9nD_901NO42zd$7',$,'ImperialPrecision','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#18=IFCSIMPLEPROPERTYTEMPLATE('1MX0uffTL6TOvtvEJpxFmk',$,'DecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#19=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#20=IFCSIMPLEPROPERTYTEMPLATE('0nYMT3OSj5gArVniCWZRtv',$,'ShadingStyles','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#21=IFCSIMPLEPROPERTYTEMPLATE('3VWG22eZXBdQwdKlzMeVQH',$,'CurrentShadingStyle','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCSIMPLEPROPERTYTEMPLATE('2T$a4OFsv2LeD5JeBKEV4f',$,'IsNTS','Whether or not the scale is intended to be significant',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#8=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#9=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#10=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#11=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#12=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#13=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#14=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#15=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#16=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#17=IFCSIMPLEPROPERTYTEMPLATE('1$xfo9EVb26QLqmPll2_RK',$,'MetricPrecision','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#18=IFCSIMPLEPROPERTYTEMPLATE('38uAtrp9nD_901NO42zd$7',$,'ImperialPrecision','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#19=IFCSIMPLEPROPERTYTEMPLATE('1MX0uffTL6TOvtvEJpxFmk',$,'DecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#20=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#21=IFCSIMPLEPROPERTYTEMPLATE('0nYMT3OSj5gArVniCWZRtv',$,'ShadingStyles','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#22=IFCSIMPLEPROPERTYTEMPLATE('3VWG22eZXBdQwdKlzMeVQH',$,'CurrentShadingStyle','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION((),'2;1');
|
||||
FILE_NAME('EPset_Status.ifc','2020-01-01T00:00:00',(),(),'EPset_Status','EPset_Status',$);
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('3JoyTt$cz4fhYuQvJeTMhs',$,'EPset_Status','',.PSET_TYPEDRIVENOVERRIDE.,'IfcProduct',(#2,#4));
|
||||
#2= IFCSIMPLEPROPERTYTEMPLATE('2HBOdOfz5E3hZ7IBLCq$$D',$,'Status','Status of the element, predominately used in renovation or retrofitting projects. The status can be assigned to as "New" - element designed as new addition, "Existing" - element exists and remains, "Demolish" - element existed but is to be demolished, "Temporary" - element will exists only temporary (like a temporary support structure).',.P_ENUMERATEDVALUE.,'IfcLabel',$,#3,$,$,$,.READWRITE.);
|
||||
#3= IFCPROPERTYENUMERATION('EPEnum_ElementStatus',(IFCLABEL('NEW'),IFCLABEL('EXISTING'),IFCLABEL('DEMOLISH'),IFCLABEL('TEMPORARY'),IFCLABEL('OTHER'),IFCLABEL('NOTKNOWN'),IFCLABEL('UNSET')),$);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3ttXxqmlz62BlxjzEPpqvH',$,'UserDefinedStatus','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -82,7 +82,7 @@ class IfcExporter:
|
||||
)
|
||||
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()
|
||||
self.get_application_name(), tool.Blender.get_blenderbim_version()
|
||||
)
|
||||
|
||||
def sync_all_objects(self):
|
||||
@@ -93,6 +93,8 @@ class IfcExporter:
|
||||
try:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
continue
|
||||
if obj.library:
|
||||
continue
|
||||
tool.Collector.sync(obj)
|
||||
result = self.sync_object_placement(obj)
|
||||
if result:
|
||||
@@ -162,7 +164,7 @@ class IfcExporter:
|
||||
return "BlenderBIM"
|
||||
|
||||
def get_application_version(self):
|
||||
return ".".join(
|
||||
version = ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
@@ -172,6 +174,9 @@ class IfcExporter:
|
||||
][0]
|
||||
]
|
||||
)
|
||||
if blenderbim.bim.last_commit_hash != "8888888":
|
||||
version += f"-{blenderbim.bim.last_commit_hash[:7]}"
|
||||
return version
|
||||
|
||||
|
||||
class IfcExportSettings:
|
||||
|
||||
@@ -27,6 +27,7 @@ from bpy.app.handlers import persistent
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.model.workspace import LIST_OF_TOOLS, TOOLS_TO_CLASSES_MAP
|
||||
from mathutils import Vector
|
||||
from math import cos, degrees
|
||||
|
||||
@@ -73,17 +74,27 @@ def name_callback(obj, data):
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
return
|
||||
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if "/" in obj.name:
|
||||
object_name = obj.name
|
||||
element_name = obj.name.split("/", 1)[1]
|
||||
else:
|
||||
element_name = obj.name
|
||||
object_name = element.is_a() + f"/{element_name}"
|
||||
obj.name = object_name # NOTE: doesn't trigger infinite recursion
|
||||
|
||||
if element.is_a("IfcGridAxis"):
|
||||
element.AxisTag = obj.name.split("/")[1]
|
||||
element.AxisTag = object_name.split("/")[1]
|
||||
refresh_ui_data()
|
||||
|
||||
if not element.is_a("IfcRoot"):
|
||||
return
|
||||
element.Name = element_name
|
||||
if obj.BIMObjectProperties.collection:
|
||||
obj.BIMObjectProperties.collection.name = obj.name
|
||||
element.Name = "/".join(obj.name.split("/")[1:])
|
||||
obj.BIMObjectProperties.collection.name = object_name
|
||||
refresh_ui_data()
|
||||
|
||||
|
||||
@@ -106,7 +117,7 @@ def update_bim_tool_props():
|
||||
return
|
||||
mode = bpy.context.mode
|
||||
current_tool = bpy.context.workspace.tools.from_space_view3d_mode(mode)
|
||||
if not current_tool or current_tool.idname != "bim.bim_tool":
|
||||
if not current_tool or current_tool.idname not in LIST_OF_TOOLS:
|
||||
return
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
@@ -114,6 +125,16 @@ def update_bim_tool_props():
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if element.is_a("IfcElementType") or element.is_a("IfcElement"):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type:
|
||||
is_bim_tool = current_tool.idname == "bim.bim_tool"
|
||||
if is_bim_tool:
|
||||
props.ifc_class = element_type.is_a()
|
||||
if is_bim_tool or TOOLS_TO_CLASSES_MAP.get(current_tool.idname) == element_type.is_a():
|
||||
props.relating_type_id = str(element_type.id())
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if not extrusion:
|
||||
return
|
||||
@@ -124,7 +145,6 @@ def update_bim_tool_props():
|
||||
return x_angle
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
|
||||
|
||||
@@ -273,6 +273,81 @@ def convert_property_group_from_si(property_group, skip_props=()):
|
||||
setattr(property_group, prop_name, prop_value)
|
||||
|
||||
|
||||
def draw_filter(layout, props, data, module):
|
||||
if not data.is_loaded:
|
||||
data.load()
|
||||
|
||||
sprops = bpy.context.scene.BIMSearchProperties
|
||||
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{len(data.data['saved_searches'])} Saved Searches")
|
||||
|
||||
if data.data["saved_searches"]:
|
||||
row.operator("bim.load_search", text="", icon="IMPORT").module = module
|
||||
row.operator("bim.save_search", text="", icon="EXPORT").module = module
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD").module = module
|
||||
row.operator("bim.edit_filter_query", text="", icon="FILTER").module = module
|
||||
|
||||
for i, filter_group in enumerate(props.filter_groups):
|
||||
box = layout.box()
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "facet", text="")
|
||||
op = row.operator("bim.add_filter", text="Add Filter", icon="ADD")
|
||||
op.type = sprops.facet
|
||||
op.index = i
|
||||
op.module = module
|
||||
|
||||
for j, ifc_filter in enumerate(filter_group.filters):
|
||||
if ifc_filter.type == "entity":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_3D")
|
||||
elif ifc_filter.type == "attribute":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="COPY_ID")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "type":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME")
|
||||
elif ifc_filter.type == "material":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="MATERIAL")
|
||||
elif ifc_filter.type == "property":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
|
||||
row.prop(ifc_filter, "name", text="")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "classification":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER")
|
||||
elif ifc_filter.type == "location":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="PACKAGE")
|
||||
elif ifc_filter.type == "query":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "instance":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="GRIP")
|
||||
op = row.operator("bim.select_filter_elements", text="", icon="EYEDROPPER")
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
op.module = module
|
||||
op = row.operator("bim.remove_filter", text="", icon="X")
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
op.module = module
|
||||
|
||||
row = box.row()
|
||||
op = row.operator("bim.remove_filter_group", icon="X")
|
||||
op.index = i
|
||||
op.module = module
|
||||
|
||||
|
||||
# TODO this should move into ifcopenshell.util
|
||||
|
||||
|
||||
|
||||
@@ -221,7 +221,9 @@ class IfcStore:
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
||||
blenderbim.bim.handler.subscribe_to(obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback)
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
|
||||
if IfcStore.history:
|
||||
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
|
||||
@@ -248,7 +250,9 @@ class IfcStore:
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
||||
blenderbim.bim.handler.subscribe_to(obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback)
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
# TODO Listeners are not re-registered. Does this cause nasty problems to debug later on?
|
||||
# TODO We're handling id_map and guid_map, but what about edited_objs? This might cause big problems.
|
||||
|
||||
@@ -321,7 +325,7 @@ class IfcStore:
|
||||
IfcStore.begin_transaction(operator)
|
||||
if tool.Ifc.get():
|
||||
tool.Ifc.get().begin_transaction()
|
||||
if BrickStore.graph:
|
||||
if BrickStore.graph is not None: # `if BrickStore.graph` by itself takes ages.
|
||||
BrickStore.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)
|
||||
@@ -339,7 +343,7 @@ class IfcStore:
|
||||
IfcStore.add_transaction_operation(
|
||||
operator, rollback=lambda d: tool.Ifc.get().undo(), commit=lambda d: tool.Ifc.get().redo()
|
||||
)
|
||||
if BrickStore.graph:
|
||||
if BrickStore.graph is not None: # `if BrickStore.graph` by itself takes ages.
|
||||
BrickStore.end_transaction()
|
||||
IfcStore.end_transaction(operator)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
@@ -227,16 +227,16 @@ class IfcImporter:
|
||||
self.settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
self.settings.set(self.settings.STRICT_TOLERANCE, True)
|
||||
self.settings_curve = ifcopenshell.geom.settings()
|
||||
self.settings_curve.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
self.settings_curve.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
self.settings_curve.set(self.settings_curve.STRICT_TOLERANCE, True)
|
||||
self.settings_curve.set(self.settings_curve.INCLUDE_CURVES, True)
|
||||
self.settings_body_2d = ifcopenshell.geom.settings()
|
||||
self.settings_body_2d.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
self.settings_body_2d.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
self.settings_body_2d.set(self.settings_body_2d.STRICT_TOLERANCE, True)
|
||||
self.settings_body_2d.set(self.settings_body_2d.INCLUDE_CURVES, True)
|
||||
self.settings_native = ifcopenshell.geom.settings()
|
||||
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True)
|
||||
self.settings_2d.set(self.settings_2d.STRICT_TOLERANCE, True)
|
||||
self.settings_plan_2d = ifcopenshell.geom.settings()
|
||||
self.settings_plan_2d.set(self.settings_plan_2d.INCLUDE_CURVES, True)
|
||||
self.settings_plan_2d.set(self.settings_plan_2d.STRICT_TOLERANCE, True)
|
||||
self.project = None
|
||||
self.has_existing_project = False
|
||||
self.collections = {}
|
||||
@@ -348,13 +348,14 @@ class IfcImporter:
|
||||
if c.ContextIdentifier in ["Body", "Facetation"]
|
||||
]
|
||||
# Ideally, all representations should be in a subcontext, but some BIM programs don't do this correctly
|
||||
self.body_contexts.extend(
|
||||
[
|
||||
c.id()
|
||||
for c in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False)
|
||||
if c.ContextType == "Model"
|
||||
]
|
||||
)
|
||||
if not self.body_contexts:
|
||||
self.body_contexts.extend(
|
||||
[
|
||||
c.id()
|
||||
for c in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False)
|
||||
if c.ContextType == "Model"
|
||||
]
|
||||
)
|
||||
if self.body_contexts:
|
||||
self.settings.set_context_ids(self.body_contexts)
|
||||
# Annotation ContextType is to accommodate broken Revit files
|
||||
@@ -365,7 +366,7 @@ class IfcImporter:
|
||||
if c.ContextType in ["Plan", "Annotation"] or c.ContextIdentifier == "Annotation"
|
||||
]
|
||||
if self.plan_contexts:
|
||||
self.settings_2d.set_context_ids(self.plan_contexts)
|
||||
self.settings_plan_2d.set_context_ids(self.plan_contexts)
|
||||
|
||||
def process_element_filter(self):
|
||||
offset = self.ifc_import_settings.element_offset
|
||||
@@ -573,7 +574,13 @@ class IfcImporter:
|
||||
return
|
||||
if not self.does_element_likely_have_geometry_far_away(element):
|
||||
continue
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, element)
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, element)
|
||||
except:
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_body_2d, element)
|
||||
except:
|
||||
continue
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
@@ -645,7 +652,7 @@ class IfcImporter:
|
||||
self.ifc_import_settings.logger.error("An invalid grid was found %s", grid)
|
||||
continue
|
||||
if grid.Representation:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_2d, grid)
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_plan_2d, grid)
|
||||
grid_obj = self.create_product(grid, shape)
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.lock_grids_on_import:
|
||||
grid_obj.lock_location = (True, True, True)
|
||||
@@ -664,7 +671,7 @@ class IfcImporter:
|
||||
|
||||
def create_grid_axes(self, axes, grid_collection, grid_obj):
|
||||
for axis in axes:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_plan_2d, axis.AxisCurve)
|
||||
mesh = self.create_mesh(axis, shape)
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.lock_grids_on_import:
|
||||
@@ -698,7 +705,7 @@ class IfcImporter:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings, representation)
|
||||
except:
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_2d, representation)
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_plan_2d, representation)
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
|
||||
if shape:
|
||||
@@ -764,9 +771,9 @@ class IfcImporter:
|
||||
if self.ifc_import_settings.should_load_geometry:
|
||||
products = self.create_products(elements)
|
||||
elements -= products
|
||||
products = self.create_products(elements, settings=self.settings_curve)
|
||||
products = self.create_products(elements, settings=self.settings_body_2d)
|
||||
elements -= products
|
||||
products = self.create_products(elements, settings=self.settings_2d)
|
||||
products = self.create_products(elements, settings=self.settings_plan_2d)
|
||||
elements -= products
|
||||
products = self.create_pointclouds(elements)
|
||||
elements -= products
|
||||
@@ -901,10 +908,10 @@ class IfcImporter:
|
||||
self.structural_collection.children.link(self.structural_connection_collection)
|
||||
self.project["blender"].children.link(self.structural_collection)
|
||||
|
||||
self.create_products(self.file.by_type("IfcStructuralCurveMember"), settings=self.settings_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralCurveConnection"), settings=self.settings_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralSurfaceMember"), settings=self.settings_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralSurfaceConnection"), settings=self.settings_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralCurveMember"), settings=self.settings_plan_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralCurveConnection"), settings=self.settings_plan_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralSurfaceMember"), settings=self.settings_plan_2d)
|
||||
self.create_products(self.file.by_type("IfcStructuralSurfaceConnection"), settings=self.settings_plan_2d)
|
||||
self.create_structural_point_connections()
|
||||
|
||||
def create_structural_point_connections(self):
|
||||
@@ -973,10 +980,14 @@ class IfcImporter:
|
||||
placement_matrix = self.get_element_matrix(product)
|
||||
vertex_list = []
|
||||
for item in representation.Items:
|
||||
if item.is_a("IfcCartesianPointList"):
|
||||
if item.is_a("IfcCartesianPointList3D"):
|
||||
vertex_list.extend(
|
||||
mathutils.Vector(list(coordinates)) * self.unit_scale for coordinates in item.CoordList
|
||||
)
|
||||
elif item.is_a("IfcCartesianPointList2D"):
|
||||
vertex_list.extend(
|
||||
mathutils.Vector(list(coordinates)).to_3d() * self.unit_scale for coordinates in item.CoordList
|
||||
)
|
||||
elif item.is_a("IfcCartesianPoint"):
|
||||
vertex_list.append(mathutils.Vector(list(item.Coordinates)) * self.unit_scale)
|
||||
|
||||
|
||||
@@ -35,13 +35,16 @@ classes = (
|
||||
operator.ViewBrickItem,
|
||||
operator.SerializeBrick,
|
||||
operator.AddBrickNamespace,
|
||||
operator.SetBrickListRoot,
|
||||
operator.RemoveBrickRelation,
|
||||
prop.Brick,
|
||||
prop.BIMBrickProperties,
|
||||
ui.BIM_PT_brickschema,
|
||||
ui.BIM_PT_ifc_brickschema_references,
|
||||
ui.BIM_PT_brickschema_project_info,
|
||||
ui.BIM_PT_brickschema_namespaces,
|
||||
ui.BIM_PT_brickschema_create_entity,
|
||||
ui.BIM_PT_brickschema_viewport,
|
||||
ui.BIM_UL_bricks,
|
||||
ui.BIM_PT_ifc_brickschema_references,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class BrickschemaData:
|
||||
|
||||
@classmethod
|
||||
def get_is_loaded(cls):
|
||||
return BrickStore.graph is not None
|
||||
return BrickStore.graph is not None # `if BrickStore.graph` by itself takes ages.
|
||||
|
||||
@classmethod
|
||||
def active_relations(cls):
|
||||
@@ -67,45 +67,63 @@ class BrickschemaData:
|
||||
PREFIX brick: <https://brickschema.org/schema/Brick#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||
SELECT DISTINCT ?predicate ?object ?sp ?sv WHERE {
|
||||
<{uri}> ?predicate ?object .
|
||||
OPTIONAL {
|
||||
{ ?predicate rdfs:range brick:TimeseriesReference . }
|
||||
UNION
|
||||
{ ?predicate a brick:EntityProperty . }
|
||||
?object ?sp ?sv }
|
||||
SELECT DISTINCT ?predicate ?object ?label ?sp ?sv WHERE {
|
||||
<{uri}> ?predicate ?object .
|
||||
OPTIONAL {
|
||||
?object rdfs:label ?label .
|
||||
}
|
||||
OPTIONAL {
|
||||
{ ?predicate rdfs:range brick:TimeseriesReference . }
|
||||
UNION
|
||||
{ ?predicate a brick:EntityProperty . }
|
||||
?object ?sp ?sv .
|
||||
}
|
||||
}
|
||||
GROUP BY ?object
|
||||
""".replace(
|
||||
"{uri}", uri
|
||||
)
|
||||
)
|
||||
for row in query:
|
||||
predicate = row.get("predicate")
|
||||
predicate_name = predicate.toPython().split("#")[-1]
|
||||
object = row.get("object")
|
||||
object_name = object.toPython().split("#")[-1]
|
||||
predicate_uri = row.get("predicate")
|
||||
predicate_name = predicate_uri.toPython().split("#")[-1]
|
||||
object_uri = row.get("object")
|
||||
object_name = row.get("label")
|
||||
if not object_name:
|
||||
if isinstance(object_uri, BNode):
|
||||
object_name = "[]"
|
||||
else:
|
||||
try:
|
||||
object_name = object_uri.toPython().split("#")[-1]
|
||||
except:
|
||||
object_name = str(object_uri)
|
||||
results.append(
|
||||
{
|
||||
"predicate": predicate,
|
||||
"predicate_uri": predicate_uri,
|
||||
"predicate_name": predicate_name,
|
||||
"object": object,
|
||||
"object_uri": object_uri,
|
||||
"object_name": object_name,
|
||||
"is_uri": isinstance(object, URIRef),
|
||||
"object_uri": object.toPython(),
|
||||
"is_globalid": predicate == "globalID",
|
||||
"is_uri": isinstance(object_uri, URIRef),
|
||||
"is_globalid": predicate_name == "ifcGlobalID",
|
||||
}
|
||||
)
|
||||
# if isinstance(row.get("object"), BNode):
|
||||
# for s, p, o in BrickStore.graph.triples((object, None, None)):
|
||||
# results.append(
|
||||
# {
|
||||
# "predicate": predicate + ":" + p.toPython().split("#")[-1],
|
||||
# "object": o.toPython().split("#")[-1],
|
||||
# "is_uri": isinstance(o, URIRef),
|
||||
# "object_uri": o.toPython(),
|
||||
# "is_globalid": p.toPython().split("#")[-1] == "globalID",
|
||||
# }
|
||||
# )
|
||||
if isinstance(object_uri, BNode):
|
||||
for subject2, predicate2, object2 in BrickStore.graph.triples((object_uri, None, None)):
|
||||
predicate2_name = predicate2.toPython().split("#")[-1]
|
||||
try:
|
||||
object2_name = object2.toPython().split("#")[-1]
|
||||
except:
|
||||
object2_name = str(object2)
|
||||
results.append(
|
||||
{
|
||||
"predicate_uri": None,
|
||||
"predicate_name": predicate_name + ":" + predicate2_name,
|
||||
"object_uri": object2,
|
||||
"object_name": object2_name,
|
||||
"is_uri": isinstance(object2, URIRef),
|
||||
"is_globalid": predicate2_name == "ifcGlobalID",
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
@@ -131,7 +149,7 @@ class BrickschemaReferencesData:
|
||||
for library in ifc.by_type("IfcLibraryInformation"):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||
elif ".ttl" in library.Location:
|
||||
elif library.Location and ".ttl" in library.Location:
|
||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||
return results
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class LoadBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.load_brick_project"
|
||||
bl_label = "Load Brickschema Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load in a Brick project from a file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
|
||||
|
||||
@@ -56,6 +57,7 @@ class ViewBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_class"
|
||||
bl_label = "View Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect the subclasses of this class"
|
||||
brick_class: bpy.props.StringProperty(name="Brick Class")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
@@ -67,17 +69,22 @@ class ViewBrickItem(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_item"
|
||||
bl_label = "View Brick Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect this entity in the viewer"
|
||||
item: bpy.props.StringProperty(name="Brick Item")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
core.view_brick_item(tool.Brick, item=self.item, split_screen=self.split_screen)
|
||||
try:
|
||||
core.view_brick_item(tool.Brick, item=self.item, split_screen=self.split_screen)
|
||||
except:
|
||||
self.report({'ERROR'}, f'Could not find {self.item}')
|
||||
|
||||
|
||||
class RewindBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.rewind_brick_class"
|
||||
bl_label = "Rewind Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Go back to the previous list view"
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -88,6 +95,7 @@ class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.close_brick_project"
|
||||
bl_label = "Close Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Close the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.close_brick_project(tool.Brick)
|
||||
@@ -97,6 +105,7 @@ class ConvertBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.convert_brick_project"
|
||||
bl_label = "Convert Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create an Ifc library for this Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_brick_project(tool.Ifc, tool.Brick)
|
||||
@@ -106,9 +115,18 @@ class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.assign_brick_reference"
|
||||
bl_label = "Assign Brick Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Assign the selected Ifc entity to the selected Brick entity"
|
||||
|
||||
def _execute(self, context):
|
||||
if not context.active_object:
|
||||
self.report({'ERROR'}, f'No Ifc selected')
|
||||
return
|
||||
props = context.scene.BIMBrickProperties
|
||||
try:
|
||||
props.bricks[props.active_brick_index]
|
||||
except:
|
||||
self.report({'ERROR'}, f'No Brick selected')
|
||||
return
|
||||
core.assign_brick_reference(
|
||||
tool.Ifc,
|
||||
tool.Brick,
|
||||
@@ -122,19 +140,17 @@ class AddBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick"
|
||||
bl_label = "Add Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
library = None
|
||||
if props.libraries:
|
||||
library = tool.Ifc.get().by_id(int(props.libraries))
|
||||
core.add_brick(
|
||||
tool.Ifc,
|
||||
tool.Brick,
|
||||
element=tool.Ifc.get_entity(context.active_object) if context.selected_objects else None,
|
||||
namespace=props.namespace,
|
||||
brick_class=props.brick_entity_class,
|
||||
library=library,
|
||||
library=tool.Ifc.get().by_id(int(props.libraries)) if props.libraries else None,
|
||||
label=props.new_brick_label,
|
||||
)
|
||||
|
||||
@@ -143,6 +159,7 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_relation"
|
||||
bl_label = "Add Brick Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick relationship"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -152,7 +169,7 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
elif props.split_screen_toggled:
|
||||
object = props.split_screen_bricks[props.split_screen_active_brick_index].uri
|
||||
else:
|
||||
object = props.new_brick_relation_namespace + props.new_brick_relation_object
|
||||
object = props.namespace + props.new_brick_relation_object
|
||||
core.add_brick_relation(
|
||||
tool.Brick,
|
||||
brick_uri=brick.uri,
|
||||
@@ -165,6 +182,7 @@ class ConvertIfcToBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.convert_ifc_to_brick"
|
||||
bl_label = "Convert IFC To Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Convert Ifc entities and relations to Brick entities and relations"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -178,6 +196,7 @@ class NewBrickFile(bpy.types.Operator):
|
||||
bl_idname = "bim.new_brick_file"
|
||||
bl_label = "New Brick File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create a Brick project from scratch"
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
@@ -210,16 +229,17 @@ class RefreshBrickViewer(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.refresh_brick_viewer"
|
||||
bl_label = "Refresh Brick Viewer"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
bl_description = "Refresh the list view"
|
||||
|
||||
def _execute(self, context):
|
||||
core.refresh_brick_viewer(tool.Brick, split_screen=self.split_screen)
|
||||
core.refresh_brick_viewer(tool.Brick)
|
||||
|
||||
|
||||
class RemoveBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick"
|
||||
bl_label = "Remove Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -262,32 +282,20 @@ class SerializeBrick(bpy.types.Operator):
|
||||
class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_namespace"
|
||||
bl_label = "Add Brick Namespace"
|
||||
bl_description = "Bind a new namespace to the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
alias = props.new_brick_namespace_alias
|
||||
uri = props.new_brick_namespace_uri
|
||||
core.add_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class SetBrickListRoot(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.set_brick_list_root"
|
||||
bl_label = "Set Brick View Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
if self.split_screen:
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
else:
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=self.split_screen)
|
||||
core.add_brick_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class RemoveBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick_relation"
|
||||
bl_label = "Remove Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this relationship"
|
||||
predicate: bpy.props.StringProperty(name="Relation")
|
||||
object: bpy.props.StringProperty(name="Object")
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.core.brick as core
|
||||
import blenderbim.tool.brick as tool
|
||||
from blenderbim.tool.brick import BrickStore
|
||||
|
||||
def update_active_brick_index(self, context):
|
||||
@@ -43,12 +45,12 @@ def get_libraries(self, context):
|
||||
|
||||
|
||||
def get_namespaces(self, context):
|
||||
return BrickStore.namespaces
|
||||
return [(uri, f"{alias}: {uri}", "") for alias, uri in BrickStore.namespaces]
|
||||
|
||||
|
||||
def get_brick_entity_classes(self, context):
|
||||
entity = self.brick_entity_create_type
|
||||
return BrickStore.entity_classes[entity]
|
||||
return [(uri, uri.split("#")[-1], "") for uri in BrickStore.entity_classes[entity]]
|
||||
|
||||
|
||||
def get_brick_roots(self, context):
|
||||
@@ -56,13 +58,22 @@ def get_brick_roots(self, context):
|
||||
|
||||
|
||||
def get_brick_relations(self, context):
|
||||
def is_label(relation):
|
||||
return relation["predicate_name"] == "label"
|
||||
if not list(filter(is_label, BrickschemaData.data["active_relations"])):
|
||||
new_relations = BrickStore.relationships.copy()
|
||||
new_relations.append(("http://www.w3.org/2000/01/rdf-schema#label", "label", ""))
|
||||
return new_relations
|
||||
return BrickStore.relationships
|
||||
relations = [(uri, uri.split("#")[-1], "") for uri in BrickStore.relationships]
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
if relation["predicate_name"] == "label":
|
||||
return relations
|
||||
relations.append(("http://www.w3.org/2000/01/rdf-schema#label", "label", ""))
|
||||
return relations
|
||||
|
||||
|
||||
|
||||
def update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=False)
|
||||
|
||||
def split_screen_update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=True)
|
||||
|
||||
|
||||
class Brick(PropertyGroup):
|
||||
@@ -79,10 +90,9 @@ class BIMBrickProperties(PropertyGroup):
|
||||
active_brick_index: IntProperty(name="Active Brick Index", update=update_active_brick_index)
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
set_list_root_toggled: BoolProperty(name="Set List Root Toggled", default=False)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots, update=update_view)
|
||||
# namespace manager
|
||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||
brick_settings_toggled: BoolProperty(name="Brick Settings Toggled", default=False)
|
||||
new_brick_namespace_alias: StringProperty(name="New Brick Namespace Alias")
|
||||
new_brick_namespace_uri: StringProperty(name="New Brick Namespace URI")
|
||||
# create brick entity
|
||||
@@ -93,13 +103,12 @@ class BIMBrickProperties(PropertyGroup):
|
||||
brick_create_relations_toggled: BoolProperty(name="Brick Create Relations Toggled", default=False)
|
||||
brick_edit_relations_toggled: BoolProperty(name="Brick Edit Relations Toggled", default=False)
|
||||
new_brick_relation_type: EnumProperty(name="New Brick Relation Type", items=get_brick_relations)
|
||||
new_brick_relation_namespace: EnumProperty(name="New Brick Relation Namespace", items=get_namespaces)
|
||||
new_brick_relation_object: StringProperty(name="New Brick Relation Object")
|
||||
add_relation_failed: BoolProperty(name="Add Relation Failed", default=False)
|
||||
add_brick_relation_failed: BoolProperty(name="Add Relation Failed", default=False)
|
||||
# create relations split screen
|
||||
split_screen_toggled: BoolProperty(name="Split Screen Toggled", default=False)
|
||||
split_screen_bricks: CollectionProperty(name="Split Screen Bricks", type=Brick)
|
||||
split_screen_active_brick_index: IntProperty(name="Split Screen Active Brick Index", update=update_active_brick_index)
|
||||
split_screen_active_brick_class: StringProperty(name="Split Screen Active Brick Class")
|
||||
split_screen_brick_breadcrumbs: CollectionProperty(name="Split Screen Brick Breadcrumbs", type=StrProperty)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots, update=split_screen_update_view)
|
||||
@@ -34,17 +34,36 @@ class BIM_PT_brickschema(Panel):
|
||||
def draw(self, context):
|
||||
if not BrickschemaData.is_loaded:
|
||||
BrickschemaData.load()
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
|
||||
class BIM_PT_brickschema_project_info(Panel):
|
||||
bl_label = "Project Info"
|
||||
bl_idname = "BIM_PT_brickschema_project_info"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
if not BrickschemaData.data["is_loaded"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.new_brick_file", text="Create Project")
|
||||
row.operator("bim.load_brick_project", text="Load Project")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.path:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=BrickStore.path, icon="FILEBROWSER")
|
||||
else:
|
||||
row.label(text="No file", icon="FILEBROWSER")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.last_saved:
|
||||
row.label(text=BrickStore.last_saved, icon="TIME")
|
||||
else:
|
||||
row.label(text="Not saved", icon="TIME")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.serialize_brick", icon="EXPORT", text="Save")
|
||||
@@ -53,134 +72,173 @@ class BIM_PT_brickschema(Panel):
|
||||
op.should_save_as = True
|
||||
row.operator("bim.close_brick_project", text="", icon="CANCEL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_settings_toggled", text="", icon="PREFERENCES")
|
||||
|
||||
if self.props.brick_settings_toggled:
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.label(text="Active Namespace:")
|
||||
|
||||
row = box.row(align=True)
|
||||
prop_with_search(row, self.props, "namespace", text="")
|
||||
class BIM_PT_brickschema_namespaces(Panel):
|
||||
bl_label = "Namespaces"
|
||||
bl_idname = "BIM_PT_brickschema_namespaces"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
row = box.row(align=True)
|
||||
row.label(text="Bind New Namespace:")
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
|
||||
col = row.column()
|
||||
col.alignment = "CENTER"
|
||||
col.scale_x = 1.1
|
||||
col.label(text=":")
|
||||
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
|
||||
row.operator("bim.add_brick_namespace", text="", icon="ADD")
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Entity:")
|
||||
row.label(text="Active Namespace:")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_entity_create_type", text="")
|
||||
prop_with_search(row, self.props, "namespace", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_label", text="")
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="")
|
||||
row.operator("bim.add_brick", text="", icon="ADD")
|
||||
# row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
row.label(text="Bind New Namespace:")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.alignment = "CENTER"
|
||||
col.scale_x = 1.1
|
||||
col.label(text=":")
|
||||
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
|
||||
row.operator("bim.add_brick_namespace", text="", icon="ADD")
|
||||
|
||||
|
||||
class BIM_PT_brickschema_create_entity(Panel):
|
||||
bl_label = "Create Entity"
|
||||
bl_idname = "BIM_PT_brickschema_create_entity"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
# TO DO: hide this if selected entity already has a reference, or something similar
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_entity_create_type", text="Class")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="Type")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
active = tool.Ifc.get_entity(context.active_object)
|
||||
if active and context.selected_objects:
|
||||
row.label(text="Label:")
|
||||
row.label(text=active.Name if active.Name else "Unnamed")
|
||||
else:
|
||||
row.prop(data=self.props, property="new_brick_label", text="Label")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_brick", text="Create Entity")
|
||||
|
||||
|
||||
class BIM_PT_brickschema_viewport(Panel):
|
||||
bl_label = "Viewport"
|
||||
bl_idname = "BIM_PT_brickschema_viewport"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.column().alignment = "RIGHT"
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.prop(data=self.props, property="split_screen_toggled", text="", icon="WINDOW")
|
||||
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
|
||||
grid = self.layout.grid_flow(even_columns=True)
|
||||
grid1 = grid.column(align=True)
|
||||
row = grid1.row(align=True)
|
||||
grid_left = grid.column(align=True)
|
||||
row = grid_left.row(align=True)
|
||||
if len(self.props.brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = False
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.label(text=self.props.active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid1.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = False
|
||||
row = grid_left.row(align=True)
|
||||
row.prop(data=self.props, property="brick_list_root", text="")
|
||||
|
||||
row = grid1.row()
|
||||
row = grid_left.row()
|
||||
BIM_UL_bricks.split_screen = False
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
|
||||
|
||||
if self.props.split_screen_toggled:
|
||||
grid2 = grid.column(align=True)
|
||||
row = grid2.row(align=True)
|
||||
grid_right = grid.column(align=True)
|
||||
row = grid_right.row(align=True)
|
||||
if len(self.props.split_screen_brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = True
|
||||
row.label(text=self.props.split_screen_active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid2.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = True
|
||||
row = grid_right.row(align=True)
|
||||
row.prop(data=self.props, property="split_screen_brick_list_root", text="")
|
||||
|
||||
row = grid2.row()
|
||||
row = grid_right.row()
|
||||
BIM_UL_bricks.split_screen = True
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "split_screen_bricks", self.props, "split_screen_active_brick_index")
|
||||
|
||||
if BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
row.column().alignment = "RIGHT"
|
||||
row.prop(data=self.props, property="brick_create_relations_toggled", text="", icon="PLUGIN")
|
||||
row.prop(data=self.props, property="brick_edit_relations_toggled", text="", icon="TOOL_SETTINGS")
|
||||
row.operator("bim.remove_brick", text="", icon="X")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Relation:")
|
||||
|
||||
if self.props.brick_create_relations_toggled and self.props.new_brick_relation_type == "http://www.w3.org/2000/01/rdf-schema#label":
|
||||
if self.props.brick_create_relations_toggled and self.props.split_screen_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.prop(data=self.props, property="new_brick_relation_object", text="")
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
elif self.props.brick_create_relations_toggled and self.props.split_screen_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
split_screen_selection = self.props.split_screen_bricks[self.props.split_screen_active_brick_index]
|
||||
if split_screen_selection.total_items:
|
||||
try:
|
||||
split_screen_selection = self.props.split_screen_bricks[self.props.split_screen_active_brick_index]
|
||||
except:
|
||||
split_screen_selection = None
|
||||
if not split_screen_selection or split_screen_selection.total_items:
|
||||
row.label(text="No selection", icon="INFO")
|
||||
else:
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.label(text=split_screen_selection.label if split_screen_selection.label else split_screen_selection.name)
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
elif self.props.brick_create_relations_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_namespace", text="")
|
||||
|
||||
elif self.props.brick_create_relations_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.prop(data=self.props, property="new_brick_relation_object", text="")
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
|
||||
if self.props.brick_create_relations_toggled and self.props.add_relation_failed:
|
||||
if self.props.brick_create_relations_toggled and self.props.add_brick_relation_failed:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Failed to find this entity!", icon="ERROR")
|
||||
|
||||
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
split = self.layout.split(factor=0.85, align=True)
|
||||
row = split.row(align=True)
|
||||
row.label(text=relation["predicate_name"])
|
||||
row.separator()
|
||||
row.label(text=relation["object_name"])
|
||||
if self.props.brick_edit_relations_toggled and relation["predicate_name"] != "type":
|
||||
row = split.row(align=True)
|
||||
row.column().alignment = "RIGHT"
|
||||
if self.props.brick_edit_relations_toggled and relation["predicate_uri"] and relation["predicate_name"] != "type":
|
||||
op = row.operator("bim.remove_brick_relation", text="", icon="UNLINKED")
|
||||
op.predicate = relation["predicate"]
|
||||
op.object = relation["object"]
|
||||
if relation["is_uri"] and relation["predicate_name"] != "type":
|
||||
op.predicate = relation["predicate_uri"]
|
||||
op.object = relation["object_uri"]
|
||||
if relation["is_uri"] and relation["object_uri"].toPython().split("#")[-1] != self.props.active_brick_class:
|
||||
op = row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT")
|
||||
op.item = relation["object_uri"]
|
||||
if relation["is_globalid"]:
|
||||
@@ -188,6 +246,23 @@ class BIM_PT_brickschema(Panel):
|
||||
op.global_id = relation["object_name"]
|
||||
|
||||
|
||||
class BIM_UL_bricks(UIList):
|
||||
split_screen = False
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
split = layout.split(factor=0.85, align=True)
|
||||
row = split.row()
|
||||
label = item.label if item.label else item.name
|
||||
if item.total_items:
|
||||
op = row.operator("bim.view_brick_class", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.brick_class = item.name
|
||||
op.split_screen = self.split_screen
|
||||
row.label(text=label)
|
||||
if item.total_items:
|
||||
row = split.row()
|
||||
row.label(text=str(item.total_items))
|
||||
|
||||
class BIM_PT_ifc_brickschema_references(Panel):
|
||||
bl_label = "Brickschema References"
|
||||
bl_idname = "BIM_PT_ifc_brickschema_references"
|
||||
@@ -211,15 +286,24 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text="No Brickschema Project Loaded")
|
||||
return
|
||||
|
||||
if not BrickschemaReferencesData.data["libraries"]:
|
||||
if not BrickschemaReferencesData.data["libraries"] and BrickStore.path:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
return
|
||||
|
||||
elif not BrickschemaReferencesData.data["libraries"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries: save the Brick project to create a new library", icon="ERROR")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Libraries must have location pointing to a \".ttl\" file", icon="INFO")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
if BrickStore.path:
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.assign_brick_reference", icon="ADD")
|
||||
@@ -234,19 +318,4 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text=reference["identification"], icon="ASSET_MANAGER")
|
||||
row.label(text=reference["name"])
|
||||
row.operator("bim.unassign_library_reference", text="", icon="X").reference = reference["id"]
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
|
||||
|
||||
class BIM_UL_bricks(UIList):
|
||||
split_screen = False
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
label = item.label if item.label else item.name
|
||||
if item.total_items:
|
||||
op = row.operator("bim.view_brick_class", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.brick_class = item.name
|
||||
op.split_screen = self.split_screen
|
||||
label = label + " (" + str(item.total_items) + ")"
|
||||
row.label(text=label)
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
@@ -27,7 +27,7 @@ class BIM_PT_ifcclash(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_quality_control"
|
||||
bl_parent_id = "BIM_PT_tab_clash_detection"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -102,9 +102,10 @@ class BIM_PT_clash_manager(Panel):
|
||||
bl_idname = "BIM_PT_clash_manager"
|
||||
bl_label = "Clash Manager"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_clash_detection"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
@@ -20,64 +20,65 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.AddCostSchedule,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.EditCostSchedule,
|
||||
operator.AddCostValue,
|
||||
operator.AddCurrency,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.AssignCostItemType,
|
||||
operator.AssignCostValue,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.ContractCostItem,
|
||||
operator.ContractCostItemRate,
|
||||
operator.ContractCostItems,
|
||||
operator.CopyCostItem,
|
||||
operator.CopyCostItemValues,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.EditCostItem,
|
||||
operator.EditCostItemQuantity,
|
||||
operator.EditCostItemValue,
|
||||
operator.EditCostItemValueFormula,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EditCostSchedule,
|
||||
operator.EnableEditingCostItem,
|
||||
operator.ExportCostSchedules,
|
||||
operator.ExpandCostItems,
|
||||
operator.EnableEditingCostItemQuantities,
|
||||
operator.EnableEditingCostItemQuantity,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EnableEditingCostItemValue,
|
||||
operator.EnableEditingCostItemValueFormula,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.AddCostColumn,
|
||||
operator.RemoveCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ExpandCostItems,
|
||||
operator.ExportCostSchedules,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.LoadProductCostItems,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.RemoveCostColumn,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignCostItemType,
|
||||
operator.UnassignCostItemType,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostValue,
|
||||
operator.RemoveCostItemValue,
|
||||
operator.CopyCostItemValues,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectCostItemProducts,
|
||||
operator.SelectCostScheduleProducts,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.AssignCostValue,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ContractCostItemRate,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.LoadProductCostItems,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectUnassignedProducts,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.CopyCostItem,
|
||||
operator.AddCurrency,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.UnassignCostItemType,
|
||||
prop.CostItem,
|
||||
prop.CostItemQuantity,
|
||||
prop.CostItemType,
|
||||
|
||||
@@ -117,6 +117,7 @@ class CostSchedulesData:
|
||||
data["TotalAppliedValue"] = 0.0
|
||||
data["TotalCost"] = 0.0
|
||||
has_unit_basis = False
|
||||
is_sum = False
|
||||
if root_element.is_a("IfcCostItem"):
|
||||
values = root_element.CostValues
|
||||
elif root_element.is_a("IfcConstructionResource"):
|
||||
@@ -130,6 +131,11 @@ class CostSchedulesData:
|
||||
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
|
||||
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
|
||||
has_unit_basis = True
|
||||
else:
|
||||
data["UnitBasisValueComponent"] = 1
|
||||
data["UnitBasisUnitSymbol"] = "U"
|
||||
if cost_value.Category == "*":
|
||||
is_sum = True
|
||||
if has_unit_basis:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] / data["UnitBasisValueComponent"]
|
||||
else:
|
||||
@@ -137,7 +143,8 @@ class CostSchedulesData:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * data["TotalCostQuantity"]
|
||||
else:
|
||||
data["TotalCost"] = data["TotalAppliedValue"]
|
||||
data["TotalAppliedValue"] = None
|
||||
if is_sum:
|
||||
data["TotalAppliedValue"] = None
|
||||
|
||||
@classmethod
|
||||
def _load_cost_item_quantities(cls, cost_item, data):
|
||||
@@ -154,7 +161,7 @@ class CostSchedulesData:
|
||||
if unit:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
else:
|
||||
data["UnitSymbol"] = None
|
||||
data["UnitSymbol"] = "U"
|
||||
|
||||
# same_unit_nested_cost_item = set()
|
||||
# data["DerivedTotalCostQuantity"] = None
|
||||
|
||||
@@ -33,9 +33,26 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add Cost Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Add a new cost schedule"
|
||||
name: bpy.props.StringProperty()
|
||||
object_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_cost_schedule(tool.Ifc, predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types)
|
||||
core.add_cost_schedule(
|
||||
tool.Ifc,
|
||||
name=self.name,
|
||||
predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types,
|
||||
object_type=self.object_type,
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "name", text="Name")
|
||||
layout.prop(context.scene.BIMCostProperties, "cost_schedule_predefined_types", text="Type")
|
||||
if context.scene.BIMCostProperties.cost_schedule_predefined_types == "USERDEFINED":
|
||||
layout.prop(self, "object_type", text="Object type")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -156,6 +173,17 @@ class ContractCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.contract_cost_item(tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
||||
|
||||
|
||||
class ContractCostItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.contract_cost_items"
|
||||
bl_label = "Contract Cost Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Collapse cost item tree"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.contract_cost_items(tool.Cost)
|
||||
|
||||
|
||||
class RemoveCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_cost_item"
|
||||
bl_label = "Remove Cost Item"
|
||||
@@ -635,7 +663,9 @@ class ExportCostSchedules(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
cost_schedule = tool.Ifc.get().by_id(self.cost_schedule) if self.cost_schedule else None
|
||||
r = core.export_cost_schedules(tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule)
|
||||
r = core.export_cost_schedules(
|
||||
tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule
|
||||
)
|
||||
if isinstance(r, str):
|
||||
self.report({"ERROR"}, r)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -21,7 +21,6 @@ import blenderbim.bim.module.cost.prop as CostProp
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.cost.data import CostSchedulesData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
@@ -30,30 +29,32 @@ class BIM_PT_cost_schedules(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_cost"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not CostSchedulesData.is_loaded:
|
||||
CostSchedulesData.load()
|
||||
|
||||
self.props = context.scene.BIMCostProperties
|
||||
row = self.layout.row()
|
||||
row = self.layout.row(align=True)
|
||||
if not self.props.active_cost_schedule_id:
|
||||
if CostSchedulesData.data["total_cost_schedules"]:
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules found.", icon="COMMUNITY")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "cost_schedule_predefined_types")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="Add")
|
||||
row.label(text="No Cost Schedules found.", icon="TEXT")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="")
|
||||
|
||||
for schedule in CostSchedulesData.data["schedules"]:
|
||||
self.draw_cost_schedule_ui(schedule)
|
||||
row2 = self.layout.row()
|
||||
row2.alignment = "RIGHT"
|
||||
row2.operator("bim.export_cost_schedules", text="Export all", icon="EXPORT")
|
||||
|
||||
def draw_cost_schedule_ui(self, cost_schedule):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -138,8 +139,8 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_summary_cost_item", text="Add Summary Cost", icon="ADD")
|
||||
row.operator("bim.expand_all_tasks", text="Expand All")
|
||||
row.operator("bim.contract_all_tasks", text="Contract All")
|
||||
row.operator("bim.expand_cost_items", text="Expand All")
|
||||
row.operator("bim.contract_cost_items", text="Contract All")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||
@@ -619,18 +620,22 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
row.label(text="", icon="DOT")
|
||||
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
|
||||
currency = CostSchedulesData.data["currency"]
|
||||
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
|
||||
layout.label(text=text)
|
||||
|
||||
def draw_quantity_column(self, layout, cost_item):
|
||||
if CostSchedulesData.data["is_editing_rates"]:
|
||||
self.draw_uom_column(layout, cost_item)
|
||||
else:
|
||||
self.draw_total_quantity_column(layout, cost_item)
|
||||
|
||||
def draw_uom_column(self, layout, cost_item):
|
||||
layout.label(text=cost_item["UnitBasisUnitSymbol"])
|
||||
|
||||
def draw_total_quantity_column(self, layout, cost_item):
|
||||
if cost_item["TotalCostQuantity"]:
|
||||
label = "{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||
layout.label(text=label)
|
||||
else:
|
||||
layout.label(text="-")
|
||||
|
||||
def draw_value_column(self, layout, cost_item):
|
||||
if cost_item["TotalAppliedValue"]:
|
||||
text = "{0:,.2f}".format(cost_item["TotalAppliedValue"]).replace(",", " ")
|
||||
@@ -642,8 +647,11 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
layout.label(text="-")
|
||||
|
||||
def draw_uom_column(self, layout, cost_item):
|
||||
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "-" if cost_item["UnitBasisValueComponent"] else "-")
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
|
||||
currency = CostSchedulesData.data["currency"]
|
||||
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
|
||||
layout.label(text=text)
|
||||
|
||||
def draw_order_operator(self, row, ifc_definition_id, cost_item):
|
||||
if cost_item["NestingIndex"] is not None:
|
||||
@@ -656,20 +664,6 @@ class BIM_UL_cost_items_trait:
|
||||
op.cost_item = ifc_definition_id
|
||||
op.new_index = cost_item["NestingIndex"] - 1
|
||||
|
||||
def draw_total_quantity_column(self, layout, cost_item):
|
||||
if cost_item["TotalCostQuantity"]:
|
||||
label = "{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||
layout.label(text=label)
|
||||
else:
|
||||
layout.label(text="-")
|
||||
# if cost_item["DerivedTotalCostQuantity"] not in [None, 0]:
|
||||
# layout.label(text="{0:.2f}".format(cost_item["DerivedTotalCostQuantity"]) + f" {cost_item['DerivedUnitSymbol'] or '-'}")
|
||||
# else:
|
||||
# if cost_item["TotalCostQuantity"] == 0:
|
||||
# layout.label(text="-")
|
||||
# else:
|
||||
# layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}")
|
||||
|
||||
|
||||
class BIM_UL_cost_items(BIM_UL_cost_items_trait, UIList):
|
||||
def __init__(self):
|
||||
|
||||
@@ -21,14 +21,15 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCsvAttribute,
|
||||
operator.RemoveCsvAttribute,
|
||||
operator.RemoveAllCsvAttributes,
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportIfcCsv,
|
||||
operator.EyedropIfcCsv,
|
||||
operator.SelectCsvIfcFile,
|
||||
operator.ImportCsvAttributes,
|
||||
operator.ExportCsvAttributes,
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportCsvAttributes,
|
||||
operator.ImportIfcCsv,
|
||||
operator.RemoveAllCsvAttributes,
|
||||
operator.RemoveCsvAttribute,
|
||||
operator.ReorderCsvAttribute,
|
||||
operator.SelectCsvIfcFile,
|
||||
prop.CsvAttribute,
|
||||
prop.CsvProperties,
|
||||
ui.BIM_PT_ifccsv,
|
||||
)
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifccsv
|
||||
import ifcopenshell
|
||||
import os
|
||||
import logging
|
||||
import bpy
|
||||
import json
|
||||
import webbrowser
|
||||
import ifccsv
|
||||
import logging
|
||||
import tempfile
|
||||
import webbrowser
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.handler import purge_module_data
|
||||
|
||||
@@ -61,28 +62,46 @@ class RemoveAllCsvAttributes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ReorderCsvAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.reorder_csv_attribute"
|
||||
bl_label = "Reorder CSV Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
old_index: bpy.props.IntProperty()
|
||||
new_index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
old = context.scene.CsvProperties.csv_attributes[self.old_index]
|
||||
new = context.scene.CsvProperties.csv_attributes[self.new_index]
|
||||
props = ["name", "header", "sort", "group", "varies_value", "summary"]
|
||||
for prop in props:
|
||||
value = getattr(new, prop)
|
||||
setattr(new, prop, getattr(old, prop))
|
||||
setattr(old, prop, value)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportCsvAttributes(bpy.types.Operator):
|
||||
bl_idname = "bim.import_csv_attributes"
|
||||
bl_label = "Import CSV Template"
|
||||
bl_label = "Load CSV Settings"
|
||||
bl_description = "Import a json template for CSV export"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
csv_json = json.load(open(self.filepath))
|
||||
|
||||
expression = csv_json.get("expression", "")
|
||||
if expression:
|
||||
csv_props.ifc_selector = expression
|
||||
|
||||
attributes = csv_json.get("attributes", [])
|
||||
if attributes:
|
||||
csv_props.csv_attributes.clear()
|
||||
for attribute in attributes:
|
||||
csv_props.csv_attributes.add().name = attribute
|
||||
props = context.scene.CsvProperties
|
||||
data = json.load(open(self.filepath))
|
||||
tool.Search.import_filter_query(data["query"], props.filter_groups)
|
||||
|
||||
props.csv_attributes.clear()
|
||||
for attribute in data["attributes"]:
|
||||
new = props.csv_attributes.add()
|
||||
new.name = attribute["name"]
|
||||
new.header = attribute["header"]
|
||||
new.sort = attribute["sort"]
|
||||
new.group = attribute["group"]
|
||||
new.summary = attribute["summary"]
|
||||
new.formatting = attribute["formatting"]
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -92,7 +111,7 @@ class ImportCsvAttributes(bpy.types.Operator):
|
||||
|
||||
class ExportCsvAttributes(bpy.types.Operator):
|
||||
bl_idname = "bim.export_csv_attributes"
|
||||
bl_label = "Export CSV Template"
|
||||
bl_label = "Save CSV Settings"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Save a json template for CSV export"
|
||||
filename_ext = ".json"
|
||||
@@ -100,22 +119,25 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
csv_template = {}
|
||||
expression = csv_props.ifc_selector
|
||||
if expression:
|
||||
csv_template["expression"] = expression
|
||||
|
||||
csv_attributes = []
|
||||
for attribute in csv_props.csv_attributes:
|
||||
attribute_name = attribute.name
|
||||
csv_attributes.append(attribute_name)
|
||||
if csv_attributes:
|
||||
csv_template["attributes"] = csv_attributes
|
||||
data = {
|
||||
"query": tool.Search.export_filter_query(props.filter_groups),
|
||||
"attributes": [
|
||||
{
|
||||
"name": a.name,
|
||||
"header": a.header,
|
||||
"sort": a.sort,
|
||||
"group": a.group,
|
||||
"summary": a.summary,
|
||||
"formatting": a.formatting,
|
||||
}
|
||||
for a in props.csv_attributes
|
||||
],
|
||||
}
|
||||
|
||||
with open(self.filepath, "w") as outfile:
|
||||
json.dump(csv_template, outfile)
|
||||
json.dump(data, outfile)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -129,9 +151,8 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
class ExportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.export_ifccsv"
|
||||
bl_label = "Export IFC"
|
||||
#filename_ext = ".csv"
|
||||
filename_ext = ".csv"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = context.scene.CsvProperties
|
||||
@@ -149,19 +170,55 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
ifc_file = IfcStore.get_file()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
results = selector.parse(ifc_file, props.ifc_selector)
|
||||
results = ifcopenshell.util.selector.filter_elements(
|
||||
ifc_file, tool.Search.export_filter_query(props.filter_groups)
|
||||
)
|
||||
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
headers = [a.header for a in props.csv_attributes]
|
||||
|
||||
sort = []
|
||||
groups = []
|
||||
summaries = []
|
||||
formatting = []
|
||||
for attribute in props.csv_attributes:
|
||||
if attribute.sort != "NONE":
|
||||
sort.append({"name": attribute.name, "order": attribute.sort})
|
||||
if attribute.group != "NONE":
|
||||
groups.append({"name": attribute.name, "type": attribute.group, "varies_value": attribute.varies_value})
|
||||
if attribute.summary != "NONE":
|
||||
summaries.append({"name": attribute.name, "type": attribute.summary})
|
||||
|
||||
if attribute.formatting != "{{value}}" and "{{value}}" in attribute.formatting:
|
||||
formatting.append({"name": attribute.name, "format": attribute.formatting})
|
||||
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
ifc_csv.export(ifc_file, results, attributes, output=self.filepath, format=props.format, delimiter=sep)
|
||||
ifc_csv.export(
|
||||
ifc_file,
|
||||
results,
|
||||
attributes,
|
||||
headers=headers,
|
||||
output=self.filepath,
|
||||
format=props.format,
|
||||
should_preserve_existing=props.should_preserve_existing,
|
||||
delimiter=sep,
|
||||
include_global_id=props.include_global_id,
|
||||
null=props.null_value,
|
||||
bool_true=props.true_value,
|
||||
bool_false=props.false_value,
|
||||
sort=sort,
|
||||
groups=groups,
|
||||
summaries=summaries,
|
||||
formatting=formatting,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.import_ifccsv"
|
||||
bl_label = "Import CSV to IFC"
|
||||
filename_ext = ".csv"
|
||||
bl_label = "Import to IFC"
|
||||
filter_glob: bpy.props.StringProperty(default="*.csv;*.ods;*.xlsx", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -180,28 +237,22 @@ class ImportIfcCsv(bpy.types.Operator):
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
ifc_csv.Import(ifc_file, self.filepath, delimiter=sep)
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
ifc_csv.Import(
|
||||
ifc_file,
|
||||
self.filepath,
|
||||
attributes=attributes,
|
||||
delimiter=sep,
|
||||
null=props.null_value,
|
||||
bool_true=props.true_value,
|
||||
bool_false=props.false_value,
|
||||
)
|
||||
if not props.should_load_from_memory:
|
||||
ifc_file.write(props.csv_ifc_file)
|
||||
purge_module_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EyedropIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.eyedrop_ifccsv"
|
||||
bl_label = "Query Selected Items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
global_ids = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.ifc_definition_id:
|
||||
global_ids.append("#" + self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId)
|
||||
context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectCsvIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_csv_ifc_file"
|
||||
bl_label = "Select CSV IFC File"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.module.search.prop import BIMFilterGroup
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -31,10 +32,45 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
class CsvAttribute(PropertyGroup):
|
||||
name: StringProperty(name="Query", default="class")
|
||||
header: StringProperty(name="Header Value", default="IFC Class")
|
||||
sort: EnumProperty(items=[("NONE", "None", ""), ("ASC", "Ascending", ""), ("DESC", "Descending", "")])
|
||||
group: EnumProperty(
|
||||
items=[
|
||||
("NONE", "None", "Don't group any rows"),
|
||||
("GROUP", "Group", "All rows where this value is identical will be merged"),
|
||||
("CONCAT", "Concatenation", "Concatenate values if values vary within a group"),
|
||||
("VARIES", "Varies", "Show a custom value if values vary within a group"),
|
||||
("SUM", "Sum", "Sums the total value of rows in a group"),
|
||||
("AVERAGE", "Average", "Averages the total value of rows in a group"),
|
||||
("MIN", "Min", "Gets the minimum value of rows in a group"),
|
||||
("MAX", "Max", "Gets the maximum value of rows in a group"),
|
||||
]
|
||||
)
|
||||
varies_value: StringProperty(default="Varies", name="Varies Value")
|
||||
summary: EnumProperty(
|
||||
items=[
|
||||
("NONE", "None", "Don't provide a summary row"),
|
||||
("SUM", "Sum", "Sums the total value of all rows"),
|
||||
("AVERAGE", "Average", "Averages the total value of all rows"),
|
||||
("MIN", "Min", "Gets the minimum value of all rows"),
|
||||
("MAX", "Max", "Gets the maximum value of all rows"),
|
||||
]
|
||||
)
|
||||
formatting: StringProperty(default="{{value}}", name="Formatting")
|
||||
|
||||
|
||||
class CsvProperties(PropertyGroup):
|
||||
csv_ifc_file: StringProperty(default="", name="IFC File")
|
||||
ifc_selector: StringProperty(default="", name="IFC Selector")
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
|
||||
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=CsvAttribute)
|
||||
should_preserve_existing: BoolProperty(default=False, name="Preserve Existing")
|
||||
include_global_id: BoolProperty(default=True, name="Include GlobalId")
|
||||
null_value: StringProperty(default="N/A", name="Null Value")
|
||||
true_value: StringProperty(default="YES", name="True Value")
|
||||
false_value: StringProperty(default="NO", name="False Value")
|
||||
csv_delimiter: EnumProperty(
|
||||
items=[
|
||||
(";", ";", ""),
|
||||
@@ -55,4 +91,9 @@ class CsvProperties(PropertyGroup):
|
||||
default="csv",
|
||||
)
|
||||
csv_custom_delimiter: StringProperty(default="", name="Custom Delimiter")
|
||||
should_show_settings: BoolProperty(default=False, name="Show Settings")
|
||||
should_show_sort: BoolProperty(default=False, name="Show Sorting")
|
||||
should_show_group: BoolProperty(default=False, name="Show Grouping")
|
||||
should_show_summary: BoolProperty(default=False, name="Show Summary")
|
||||
should_show_formatting: BoolProperty(default=False, name="Show Formatting")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.search.data import SearchData
|
||||
|
||||
|
||||
class BIM_PT_ifccsv(Panel):
|
||||
bl_label = "CSV Import/Export"
|
||||
bl_label = "Spreadsheet Import/Export"
|
||||
bl_idname = "BIM_PT_ifccsv"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -36,45 +38,82 @@ class BIM_PT_ifccsv(Panel):
|
||||
props = scene.CsvProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "should_load_from_memory")
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
row.prop(props, "should_show_settings", icon="PREFERENCES", text="")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_ifc_file")
|
||||
row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "ifc_selector")
|
||||
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
|
||||
layout.separator()
|
||||
if props.should_show_settings:
|
||||
layout.use_property_split = True
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "format")
|
||||
|
||||
if props.format == "csv":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_delimiter")
|
||||
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_custom_delimiter")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "should_preserve_existing")
|
||||
row = layout.row()
|
||||
row.prop(props, "include_global_id")
|
||||
row = layout.row()
|
||||
row.prop(props, "null_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "true_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "false_value")
|
||||
layout.use_property_split = False
|
||||
|
||||
blenderbim.bim.helper.draw_filter(self.layout, props, SearchData, "csv")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_csv_attribute", icon="ADD")
|
||||
row.operator("bim.remove_all_csv_attributes", icon="CANCEL")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT")
|
||||
row.prop(props, "should_show_sort", icon="SORTSIZE", text="")
|
||||
row.prop(props, "should_show_group", icon="OUTLINER_COLLECTION", text="")
|
||||
row.prop(props, "should_show_summary", icon="SYNTAX_ON", text="")
|
||||
row.prop(props, "should_show_formatting", icon="CON_TRANSLIKE", text="")
|
||||
|
||||
total = len(props.csv_attributes)
|
||||
for index, attribute in enumerate(props.csv_attributes):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, "name", text="")
|
||||
row.prop(attribute, "header", text="")
|
||||
if props.should_show_sort:
|
||||
row.prop(attribute, "sort", text="")
|
||||
if props.should_show_group:
|
||||
row.prop(attribute, "group", text="")
|
||||
if attribute.group == "VARIES":
|
||||
row.prop(attribute, "varies_value", text="")
|
||||
if props.should_show_summary:
|
||||
row.prop(attribute, "summary", text="")
|
||||
if props.should_show_formatting:
|
||||
row.prop(attribute, "formatting", text="")
|
||||
if total > 1:
|
||||
if index != 0:
|
||||
op = row.operator(f"bim.reorder_csv_attribute", icon="TRIA_UP", text="")
|
||||
op.old_index = index
|
||||
op.new_index = index - 1
|
||||
if index + 1 != total:
|
||||
op = row.operator(f"bim.reorder_csv_attribute", icon="TRIA_DOWN", text="")
|
||||
op.old_index = index
|
||||
op.new_index = index + 1
|
||||
row.operator("bim.remove_csv_attribute", icon="X", text="").index = index
|
||||
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "format")
|
||||
|
||||
if props.format == 'csv':
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_delimiter")
|
||||
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_custom_delimiter")
|
||||
|
||||
row = layout.row()
|
||||
split = row.split(factor=0.5)
|
||||
c = split.column()
|
||||
c.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
c = split.column()
|
||||
c.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
row.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
|
||||
@@ -154,6 +154,8 @@ class ValidateIfcFile(bpy.types.Operator):
|
||||
logger = logging.getLogger("validate")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
ifcopenshell.validate.validate(IfcStore.get_file(), logger, express_rules=True)
|
||||
|
||||
self.report({"INFO"}, "Check validation results in the system console.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import os
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
|
||||
class Annotator:
|
||||
@@ -90,6 +92,15 @@ class Annotator:
|
||||
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_vertex_to_annotation(obj):
|
||||
verts_world_space = Annotator.get_placeholder_coords()
|
||||
vert_local = obj.matrix_world.inverted() @ verts_world_space[0]
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
|
||||
bm.verts.new(vert_local)
|
||||
tool.Blender.apply_bmesh(obj.data, bm, obj)
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_plane_to_annotation(obj, remove_face=False):
|
||||
# default order = bot left, top left, bot right, top right
|
||||
@@ -111,7 +122,7 @@ class Annotator:
|
||||
def get_annotation_obj(drawing, object_type, data_type):
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
|
||||
matrix_world = camera.matrix_world.copy()
|
||||
matrix_world = tool.Drawing.get_camera_matrix(camera)
|
||||
matrix_world.translation = co1
|
||||
collection = camera.BIMObjectProperties.collection
|
||||
|
||||
@@ -142,6 +153,8 @@ class Annotator:
|
||||
data = bpy.data.curves.new(object_type, type="CURVE")
|
||||
data.dimensions = "3D"
|
||||
data.resolution_u = 2
|
||||
elif data_type == "empty":
|
||||
data = None
|
||||
|
||||
obj = bpy.data.objects.new(object_type, data)
|
||||
obj.matrix_world = matrix_world
|
||||
@@ -154,6 +167,13 @@ class Annotator:
|
||||
camera = bpy.context.scene.camera
|
||||
|
||||
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
|
||||
if (
|
||||
ifcopenshell.util.element.get_pset(tool.Ifc.get_entity(camera), "EPset_Drawing", "TargetView")
|
||||
== "REFLECTED_PLAN_VIEW"
|
||||
):
|
||||
z_offset *= -1
|
||||
|
||||
y = camera.data.ortho_scale / 4
|
||||
|
||||
res_x = bpy.context.scene.render.resolution_x
|
||||
@@ -166,6 +186,7 @@ class Annotator:
|
||||
|
||||
center = camera.matrix_world.inverted() @ bpy.context.scene.cursor.location
|
||||
center.z = 0
|
||||
center = camera.matrix_world @ center
|
||||
|
||||
return (
|
||||
center + z_offset,
|
||||
|
||||
@@ -64,11 +64,6 @@ class profile_consequential:
|
||||
cls.lines = []
|
||||
|
||||
|
||||
def ccw(A, B, C):
|
||||
"""whether a-b-c located in counter-clockwise order in 2d space"""
|
||||
return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
|
||||
|
||||
|
||||
def worldspace_to_winspace(verts, context):
|
||||
"""Convert world space verts to window space"""
|
||||
region = context.region
|
||||
@@ -248,8 +243,13 @@ class BaseDecorator:
|
||||
if check_mode and obj.data.is_editmode:
|
||||
return self.get_editmesh_geom(obj)
|
||||
|
||||
vertices = [obj.matrix_world @ v.co for v in obj.data.vertices]
|
||||
indices = [e.vertices for e in obj.data.edges]
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
vertices = [obj.matrix_world @ v.co for v in bm.verts]
|
||||
# In object mode, it's nicer to not show "internal edges". Most will be dissolved anyway.
|
||||
indices = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces) != 2]
|
||||
bm.free()
|
||||
|
||||
return vertices, indices
|
||||
|
||||
def get_editmesh_geom(self, obj):
|
||||
@@ -520,7 +520,10 @@ class BaseDecorator:
|
||||
return matrix.inverted()[i].to_3d().normalized()
|
||||
|
||||
text_dir_world_x_axis = get_basis_vector(obj.matrix_world)
|
||||
text_dir = (camera.matrix_world.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
camera_matrix = tool.Drawing.get_camera_matrix(camera)
|
||||
|
||||
text_dir = (camera_matrix.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
pos = location_3d_to_region_2d(region, region3d, text_world_position)
|
||||
props = obj.BIMTextProperties
|
||||
@@ -742,7 +745,7 @@ class AngleDecorator(BaseDecorator):
|
||||
except ZeroDivisionError:
|
||||
continue
|
||||
circle_angle = acos(cos_a)
|
||||
counter_clockwise = ccw(v2, v1, v0)
|
||||
counter_clockwise = tool.Cad.is_counter_clockwise_order(v2, v1, v0)
|
||||
angle_circle = get_angle_circle(circle_start, circle_angle, counter_clockwise)
|
||||
add_verts_sequence([v1 + v for v in angle_circle], start_i_arcs, **out_kwargs_arcs)
|
||||
|
||||
@@ -774,7 +777,7 @@ class AngleDecorator(BaseDecorator):
|
||||
region3d = context.region_data
|
||||
|
||||
viewportDrawingScale = self.get_viewport_drawing_scale(context)
|
||||
ANGLE_LABEL_OFFSET = 25 * viewportDrawingScale
|
||||
ANGLE_LABEL_OFFSET = 20 * viewportDrawingScale
|
||||
last_segment_i = len(indices) - 1
|
||||
|
||||
for edge_i, edge_vertices in enumerate(indices):
|
||||
@@ -792,24 +795,24 @@ class AngleDecorator(BaseDecorator):
|
||||
edge1_ws = v2 - v1
|
||||
try:
|
||||
cos_a = edge0_ws.dot(edge1_ws) / (edge0_ws.length * edge1_ws.length)
|
||||
angle_rad = acos(cos_a)
|
||||
angle = angle_rad / pi * 180
|
||||
except ZeroDivisionError:
|
||||
continue
|
||||
circle_angle_rad = acos(cos_a)
|
||||
circle_angle = circle_angle_rad / pi * 180
|
||||
angle = 0
|
||||
|
||||
# calculate angle position
|
||||
p0 = location_3d_to_region_2d(region, region3d, v0)
|
||||
p1 = location_3d_to_region_2d(region, region3d, v1)
|
||||
p2 = location_3d_to_region_2d(region, region3d, v2)
|
||||
p0, p1, p2 = [location_3d_to_region_2d(region, region3d, p) for p in vertices[i0 : i1 + 2]]
|
||||
edge0 = p0 - p1
|
||||
edge1 = p2 - p1
|
||||
base_edge = edge0 if ccw(p0, p1, p2) else edge1
|
||||
text_offset = (Matrix.Rotation(-circle_angle_rad / 2, 2) @ base_edge).normalized() * ANGLE_LABEL_OFFSET
|
||||
radius = min(edge0.length_squared, edge1.length_squared) ** 0.5
|
||||
# TODO: can be helpful for svg positioning
|
||||
base_edge = edge0 if tool.Cad.is_counter_clockwise_order(p0, p1, p2) else edge1
|
||||
text_offset = (Matrix.Rotation(-angle_rad / 2, 2) @ base_edge).normalized() * (radius + ANGLE_LABEL_OFFSET)
|
||||
label_position = p1 + text_offset
|
||||
|
||||
text = f"{int(circle_angle)}d"
|
||||
text = f"{int(angle)}deg"
|
||||
label_dir = Vector((1, 0))
|
||||
self.draw_label(context, text, label_position, label_dir)
|
||||
self.draw_label(context, text, label_position, label_dir, box_alignment="center")
|
||||
|
||||
|
||||
class DiameterDecorator(DimensionDecorator):
|
||||
|
||||
@@ -70,15 +70,6 @@ class profile:
|
||||
print(self.task, timer() - self.start)
|
||||
|
||||
|
||||
def open_with_user_command(user_command, path):
|
||||
if user_command:
|
||||
commands = eval(user_command)
|
||||
for command in commands:
|
||||
subprocess.Popen(command)
|
||||
else:
|
||||
webbrowser.open("file://" + path)
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
@@ -269,7 +260,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
with profile("Combine SVG layers"):
|
||||
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
|
||||
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
|
||||
|
||||
if self.print_all:
|
||||
bpy.ops.bim.activate_drawing(drawing=original_drawing_id, camera_view_point=False)
|
||||
@@ -485,7 +476,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||
|
||||
self.setup_serialiser(ifc)
|
||||
self.setup_serialiser(ifc, target_view)
|
||||
cache = IfcStore.get_cache()
|
||||
[cache.remove(guid) for guid in invalidated_guids]
|
||||
tree = ifcopenshell.geom.tree()
|
||||
@@ -555,7 +546,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
if self.camera.data.BIMCameraProperties.calculate_shapely_surfaces:
|
||||
# shapely variant
|
||||
group = root.findall(".//{http://www.w3.org/2000/svg}g")[0]
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
nm = group.attrib["{http://www.ifcopenshell.org/ns}name"]
|
||||
m4 = np.array(json.loads(group.attrib["{http://www.ifcopenshell.org/ns}plane"]))
|
||||
m3 = np.array(json.loads(group.attrib["{http://www.ifcopenshell.org/ns}matrix3"]))
|
||||
@@ -566,10 +557,21 @@ class CreateDrawing(bpy.types.Operator):
|
||||
m44[1][3] = m3[1][2]
|
||||
m44 = np.linalg.inv(m44)
|
||||
|
||||
projections = group.findall('.//{http://www.w3.org/2000/svg}g[@class="projection"]') or []
|
||||
elements_with_faces = set()
|
||||
for element in drawing_elements.copy():
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj and obj.type == "MESH" and len(obj.data.polygons):
|
||||
elements_with_faces.add(element.GlobalId)
|
||||
|
||||
projections = root.xpath(
|
||||
".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"}
|
||||
)
|
||||
|
||||
boundary_lines = []
|
||||
for projection in projections:
|
||||
boundary_lines = []
|
||||
global_id = projection.attrib["{http://www.ifcopenshell.org/ns}guid"]
|
||||
if global_id not in elements_with_faces:
|
||||
continue
|
||||
for path in projection.findall("./{http://www.w3.org/2000/svg}path"):
|
||||
# Rounding is necessary to ensure coincident points are coincident
|
||||
start, end = [[round(float(o), 1) for o in co[1:].split(",")] for co in path.attrib["d"].split()]
|
||||
@@ -578,50 +580,55 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# Extension by 0.5mm is necessary to ensure lines overlap with other diagonal lines
|
||||
start, end = tool.Drawing.extend_line(start, end, 0.5)
|
||||
boundary_lines.append(shapely.LineString([start, end]))
|
||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||
|
||||
for polygon in closed_polygons.geoms:
|
||||
# Less than 1mm2 is not worth styling on sheet
|
||||
if polygon.area < 1:
|
||||
continue
|
||||
centroid = polygon.centroid
|
||||
internal_point = centroid if polygon.contains(centroid) else polygon.representative_point()
|
||||
if internal_point:
|
||||
internal_point = [internal_point.x, internal_point.y]
|
||||
a, b = self.drawing_to_model_co(m44, m4, internal_point, 0.0), self.drawing_to_model_co(
|
||||
m44, m4, internal_point, -100.0
|
||||
)
|
||||
inside_elements = [e for e in tree.select(self.pythonize(a)) if not e.is_a("IfcAnnotation")]
|
||||
if not inside_elements:
|
||||
elements = [
|
||||
e
|
||||
for e in tree.select_ray(self.pythonize(a), self.pythonize(b - a))
|
||||
if not e.instance.is_a("IfcAnnotation")
|
||||
and tool.Cad.is_point_on_edge(
|
||||
Vector(list(e.position)), (Vector(self.pythonize(a)), Vector(self.pythonize(b)))
|
||||
)
|
||||
]
|
||||
if elements:
|
||||
path = etree.Element("path")
|
||||
d = (
|
||||
"M"
|
||||
+ " L".join(
|
||||
[",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]]
|
||||
)
|
||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||
|
||||
for polygon in closed_polygons.geoms:
|
||||
# Less than 1mm2 is not worth styling on sheet
|
||||
if polygon.area < 1:
|
||||
continue
|
||||
centroid = polygon.centroid
|
||||
internal_point = centroid if polygon.contains(centroid) else polygon.representative_point()
|
||||
if internal_point:
|
||||
internal_point = [internal_point.x, internal_point.y]
|
||||
a, b = self.drawing_to_model_co(m44, m4, internal_point, 0.0), self.drawing_to_model_co(
|
||||
m44, m4, internal_point, -100.0
|
||||
)
|
||||
inside_elements = [e for e in tree.select(self.pythonize(a)) if not e.is_a("IfcAnnotation")]
|
||||
if not inside_elements:
|
||||
elements = [
|
||||
e
|
||||
for e in tree.select_ray(self.pythonize(a), self.pythonize(b - a))
|
||||
if not e.instance.is_a("IfcAnnotation")
|
||||
and tool.Cad.is_point_on_edge(
|
||||
Vector(list(e.position)), (Vector(self.pythonize(a)), Vector(self.pythonize(b)))
|
||||
)
|
||||
]
|
||||
if elements:
|
||||
path = etree.Element("path")
|
||||
d = (
|
||||
"M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
for interior in polygon.interiors:
|
||||
d += (
|
||||
" M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in interior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
for interior in polygon.interiors:
|
||||
d += (
|
||||
" M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in interior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
path.attrib["d"] = d
|
||||
classes = self.get_svg_classes(ifc.by_id(elements[0].instance.id()))
|
||||
classes.append("surface")
|
||||
path.set("class", " ".join(list(classes)))
|
||||
group.insert(0, path)
|
||||
path.attrib["d"] = d
|
||||
classes = self.get_svg_classes(ifc.by_id(elements[0].instance.id()))
|
||||
classes.append(f"intpoint-{internal_point}")
|
||||
classes.append(f"ab-{a}, {b}")
|
||||
for i, ray_result in enumerate(elements):
|
||||
classes.append(f"el{i}-{ray_result.instance.id()}")
|
||||
classes.append(f"el{i}-pos-{list(ray_result.position)}")
|
||||
classes.append(f"el{i}-dst-{ray_result.distance}")
|
||||
classes.append("surface")
|
||||
path.set("class", " ".join(list(classes)))
|
||||
group.insert(0, path)
|
||||
|
||||
if self.camera.data.BIMCameraProperties.calculate_svgfill_surfaces:
|
||||
results = etree.tostring(root).decode("utf8")
|
||||
@@ -636,7 +643,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
dom1 = parseString(svg_data_1)
|
||||
svg1 = dom1.childNodes[0]
|
||||
groups1 = [g for g in yield_groups(svg1) if g.getAttribute("class") == "projection"]
|
||||
groups1 = [g for g in yield_groups(svg1) if "projection" in g.getAttribute("class")]
|
||||
|
||||
ls_groups = ifcopenshell.ifcopenshell_wrapper.svg_to_line_segments(results, "projection")
|
||||
|
||||
@@ -829,7 +836,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
return svg_path
|
||||
|
||||
def setup_serialiser(self, ifc):
|
||||
def setup_serialiser(self, ifc, target_view):
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
@@ -840,7 +847,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.serialiser.setPolygonal(True)
|
||||
self.serialiser.setUseHlrPoly(True)
|
||||
# Objects with more than these edges are rendered as wireframe instead of HLR for optimisation
|
||||
self.serialiser.setProfileThreshold(1000)
|
||||
self.serialiser.setProfileThreshold(10000)
|
||||
self.serialiser.setUseNamespace(True)
|
||||
self.serialiser.setAlwaysProject(True)
|
||||
self.serialiser.setAutoElevation(False)
|
||||
@@ -853,6 +860,10 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.serialiser.setScale(self.scale)
|
||||
self.serialiser.setSubtractionSettings(ifcopenshell.ifcopenshell_wrapper.ALWAYS)
|
||||
self.serialiser.setUsePrefiltering(True) # See #3359
|
||||
self.serialiser.setUnifyInputs(True)
|
||||
self.serialiser.setSegmentProjection(True)
|
||||
if target_view == "REFLECTED_PLAN_VIEW":
|
||||
self.serialiser.setMirrorY(True)
|
||||
# tree = ifcopenshell.geom.tree()
|
||||
# This instructs the tree to explode BReps into faces and return
|
||||
# the style of the face when running tree.select_ray()
|
||||
@@ -887,6 +898,21 @@ class CreateDrawing(bpy.types.Operator):
|
||||
)
|
||||
return classes
|
||||
|
||||
def is_manifold(self, obj):
|
||||
result = self.is_manifold_cache.get(obj.data.name, None)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
for edge in bm.edges:
|
||||
if not edge.is_manifold:
|
||||
bm.free()
|
||||
self.is_manifold_cache[obj.data.name] = False
|
||||
return False
|
||||
self.is_manifold_cache[obj.data.name] = True
|
||||
return True
|
||||
|
||||
def merge_linework_and_add_metadata(self, root):
|
||||
join_criteria = ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "JoinCriteria")
|
||||
if join_criteria:
|
||||
@@ -895,16 +921,27 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# Drawing convention states that same objects classes with the same material are merged when cut.
|
||||
join_criteria = ["class", "material.Name", 'r"Pset.*Common"."Status"']
|
||||
|
||||
group = root.findall(".//{http://www.w3.org/2000/svg}g")[0]
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
joined_paths = {}
|
||||
self.is_manifold_cache = {}
|
||||
|
||||
ifc = tool.Ifc.get()
|
||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||
element = ifc.by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("cut")
|
||||
el.set("class", " ".join(classes))
|
||||
if "projection" in el.get("class", "").split():
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("projection")
|
||||
el.set("class", " ".join(classes))
|
||||
continue
|
||||
else:
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("cut")
|
||||
el.set("class", " ".join(classes))
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not self.is_manifold(obj):
|
||||
continue
|
||||
|
||||
# An element group will contain a bunch of paths representing the
|
||||
# cut of that element. However IfcOpenShell may not correctly
|
||||
@@ -1022,9 +1059,12 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# IfcConvert puts the projection afterwards which is not correct since
|
||||
# projection should be drawn underneath the cut.
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
projection = group.find("{http://www.w3.org/2000/svg}g[@class='projection']")
|
||||
projection.getparent().remove(projection)
|
||||
group.insert(0, projection)
|
||||
projections = root.xpath(
|
||||
".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"}
|
||||
)
|
||||
for projection in projections:
|
||||
projection.getparent().remove(projection)
|
||||
group.insert(0, projection)
|
||||
|
||||
def generate_annotation(self, context):
|
||||
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasAnnotation"):
|
||||
@@ -1035,7 +1075,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
elements = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(self.camera_element))
|
||||
filtered_drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||
elements = [e for e in elements if e in filtered_drawing_elements]
|
||||
filtered_drawing_annotations = {e for e in filtered_drawing_elements if e.is_a("IfcAnnotation")}
|
||||
elements = {e for e in elements if e in filtered_drawing_elements}
|
||||
elements = list(elements | filtered_drawing_annotations)
|
||||
|
||||
annotations = sorted(
|
||||
elements, key=lambda a: (tool.Drawing.get_annotation_z_index(a), 1 if a.ObjectType == "TEXT" else 0)
|
||||
@@ -1236,11 +1278,15 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
|
||||
# These variables will be made available to the evaluated commands
|
||||
svg = references["SHEET"]
|
||||
basename = os.path.basename(svg)
|
||||
path = os.path.dirname(svg)
|
||||
pdf = os.path.splitext(svg)[0] + ".pdf"
|
||||
eps = os.path.splitext(svg)[0] + ".eps"
|
||||
dxf = os.path.splitext(svg)[0] + ".dxf"
|
||||
replacements = {
|
||||
"svg": svg,
|
||||
"basename": os.path.basename(svg),
|
||||
"path": os.path.dirname(svg),
|
||||
"pdf": pdf,
|
||||
"eps": os.path.splitext(svg)[0] + ".eps",
|
||||
"dxf": os.path.splitext(svg)[0] + ".dxf",
|
||||
}
|
||||
|
||||
has_sheet_reference = False
|
||||
for reference in tool.Drawing.get_document_references(sheet):
|
||||
@@ -1273,22 +1319,23 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
|
||||
if svg2pdf_command:
|
||||
# With great power comes great responsibility. Example:
|
||||
# [['inkscape', svg, '-o', pdf]]
|
||||
commands = eval(svg2pdf_command)
|
||||
# [["inkscape", "svg", "-o", "pdf"]]
|
||||
commands = json.loads(svg2pdf_command)
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
subprocess.run([replacements.get(c, c) for c in command])
|
||||
|
||||
if svg2dxf_command:
|
||||
# With great power comes great responsibility. Example:
|
||||
# [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]
|
||||
commands = eval(svg2dxf_command)
|
||||
# [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]]
|
||||
commands = json.loads(svg2dxf_command)
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
command[0] = shutil.which(command[0]) or command[0]
|
||||
subprocess.run([replacements.get(c, c) for c in command])
|
||||
|
||||
if svg2pdf_command:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
|
||||
else:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
|
||||
|
||||
|
||||
class SelectAllDrawings(bpy.types.Operator):
|
||||
@@ -1355,7 +1402,9 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
for drawing_uri in drawing_uris:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri)
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1391,6 +1440,7 @@ class ActivateModel(bpy.types.Operator):
|
||||
|
||||
if view3d_context:
|
||||
bpy.ops.object.hide_view_clear(view3d_context)
|
||||
bpy.ops.bim.activate_status_filters()
|
||||
|
||||
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@@ -1512,11 +1562,17 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in context.scene.DocProperties.drawings if d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
self.report({"ERROR"}, "No drawing selected")
|
||||
return {"CANCELLED"}
|
||||
drawings = [tool.Ifc.get().by_id(self.drawing)]
|
||||
|
||||
removed_drawings = [drawing.id() for drawing in drawings]
|
||||
|
||||
for drawing in drawings:
|
||||
sheet_references = tool.Drawing.get_sheet_references(drawing)
|
||||
for reference in sheet_references:
|
||||
bpy.ops.bim.remove_drawing_from_sheet(reference=reference.id())
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
|
||||
|
||||
@@ -1862,7 +1918,7 @@ class RemoveSchedule(bpy.types.Operator, Operator):
|
||||
schedule: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", schedule=tool.Ifc.get().by_id(self.schedule))
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", document=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class OpenSchedule(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -502,6 +502,8 @@ ANNOTATION_TYPES_DATA = {
|
||||
"PLAN_LEVEL": ("Level (Plan)", "", "SORTBYEXT", "curve"),
|
||||
"SECTION_LEVEL": ("Level (Section)", "", "TRIA_DOWN", "curve"),
|
||||
"BREAKLINE": ("Breakline", "", "FCURVE", "mesh"),
|
||||
"SYMBOL": ("Symbol", "", "KEYFRAME", "empty"),
|
||||
"MULTI_SYMBOL": ("Multi-Symbol", "", "OUTLINER_DATA_POINTCLOUD", "mesh"),
|
||||
"LINEWORK": ("Line", "", "SNAP_MIDPOINT", "mesh"),
|
||||
"BATTING": ("Batting", "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set", "FORCE_FORCE", "mesh"),
|
||||
"REVISION_CLOUD":("Revision Cloud", "Add revision cloud", "VOLUME_DATA", "mesh"),
|
||||
@@ -538,7 +540,7 @@ class BIMAnnotationProperties(PropertyGroup):
|
||||
name="Create Representation For Type",
|
||||
default=False,
|
||||
description='Whether "Add type" should define a representation for the type \n'
|
||||
"or allow occurences to have their own",
|
||||
"or allow occurrences to have their own",
|
||||
)
|
||||
is_adding_type: bpy.props.BoolProperty(default=False)
|
||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||
|
||||
@@ -118,6 +118,25 @@ class Scheduler:
|
||||
if cell_style:
|
||||
related_styles.append((style_name, cell_style))
|
||||
|
||||
# sometimes there are no column styles (e.g. in ODS from IfcCSV)
|
||||
# and we just use some constant number for column widths
|
||||
if not column_widths:
|
||||
row_columns = []
|
||||
for tr in table.getElementsByType(TableRow):
|
||||
row_cols = 0
|
||||
for td in tr.getElementsByType(TableCell):
|
||||
column_span = td.getAttribute("numbercolumnsspanned")
|
||||
column_span = int(column_span) if column_span else 1
|
||||
|
||||
col_repeat = td.getAttribute("numbercolumnsrepeated")
|
||||
col_repeat = int(col_repeat) if col_repeat else 1
|
||||
row_cols += column_span * col_repeat
|
||||
row_columns.append(row_cols)
|
||||
|
||||
n_columns = max(row_columns)
|
||||
column_widths = [25] * n_columns # some constant width value 👀
|
||||
column_styles = [None] * n_columns
|
||||
|
||||
# collect rows height
|
||||
row_heights = []
|
||||
# TODO: never used yet because unsure about priority for row styles
|
||||
@@ -351,7 +370,7 @@ class Scheduler:
|
||||
wrap_text: if True, text will be wrapped to fit in cell
|
||||
cell_width: width of cell, used for wrapping text
|
||||
"""
|
||||
text_lines = [str(p).upper() for p in p_tags]
|
||||
text_lines = [str(p) for p in p_tags]
|
||||
box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
text_params = {
|
||||
"font-size": font_size,
|
||||
|
||||
@@ -174,6 +174,8 @@ class SheetBuilder:
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
|
||||
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
|
||||
if not os.path.exists(layout_path):
|
||||
return
|
||||
layout_tree = ET.parse(layout_path)
|
||||
layout_root = layout_tree.getroot()
|
||||
|
||||
@@ -414,7 +416,7 @@ class SheetBuilder:
|
||||
view.remove(image)
|
||||
|
||||
def get_href(self, element):
|
||||
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href"))
|
||||
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href")).replace('\\','/')
|
||||
|
||||
def parse_embedded_svg(self, image, data):
|
||||
group = ET.Element("g")
|
||||
|
||||
@@ -36,7 +36,7 @@ import blenderbim.bim.module.drawing.annotation as annotation
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
from math import pi, ceil, atan, degrees
|
||||
from math import pi, ceil, atan, degrees, acos
|
||||
from mathutils import geometry, Vector
|
||||
from bpy_extras import view3d_utils
|
||||
|
||||
@@ -326,11 +326,12 @@ class SvgWriter:
|
||||
# We have to decide whether this should come from Blender or from IFC.
|
||||
# For the moment, for convenience of experimenting with ideas, it comes
|
||||
# from Blender. In the future, it should probably come from IFC.
|
||||
if not isinstance(obj.data, bpy.types.Mesh):
|
||||
return
|
||||
|
||||
classes = self.get_attribute_classes(obj)
|
||||
if len(obj.data.vertices) and not len(obj.data.edges):
|
||||
if obj.data is None:
|
||||
return self.draw_empty_annotation(obj, classes)
|
||||
elif not isinstance(obj.data, bpy.types.Mesh):
|
||||
return
|
||||
elif len(obj.data.vertices) and not len(obj.data.edges):
|
||||
return self.draw_point_annotation(obj, classes)
|
||||
elif len(obj.data.polygons) == 0:
|
||||
return self.draw_edge_annotation(obj, classes)
|
||||
@@ -774,6 +775,18 @@ class SvgWriter:
|
||||
"text-anchor": text_anchor,
|
||||
}
|
||||
|
||||
def add_fill_bg(self, element, copy=True):
|
||||
if copy:
|
||||
element = element.copy()
|
||||
if hasattr(element, "xml"):
|
||||
attrib = element.xml.attrib
|
||||
elif isinstance(element, ET.Element):
|
||||
attrib = element.attrib
|
||||
else: # assuming it's svgwrite.base.BaseElement
|
||||
attrib = element.attribs
|
||||
attrib["filter"] = "url(#fill-background)"
|
||||
return element
|
||||
|
||||
def draw_text_annotation(self, text_obj, position):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
@@ -791,20 +804,21 @@ class SvgWriter:
|
||||
return matrix.inverted()[i].to_3d().normalized()
|
||||
|
||||
text_dir_world_x_axis = get_basis_vector(text_obj.matrix_world)
|
||||
text_dir = (self.camera.matrix_world.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
# RCP cameras may be scaled, so reset scales.
|
||||
camera_matrix = tool.Drawing.get_camera_matrix(self.camera)
|
||||
text_dir = (camera_matrix.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
angle = math.degrees(-text_dir.angle_signed(Vector((1, 0))))
|
||||
|
||||
classes = self.get_attribute_classes(text_obj)
|
||||
classes_str = " ".join(classes)
|
||||
fill_bg = "fill-bg" in classes
|
||||
|
||||
symbol = tool.Drawing.get_annotation_symbol(element)
|
||||
template_text_fields = []
|
||||
if not symbol:
|
||||
text_transform = f"translate({text_position_svg_str}) rotate({angle})"
|
||||
else:
|
||||
if symbol:
|
||||
# NOTE: for now we assume that scale is uniform
|
||||
symbol_transform = f"translate({text_position_svg_str}) rotate({angle}) scale({text_obj.scale.x})"
|
||||
text_transform = symbol_transform
|
||||
|
||||
symbol_svg = self.find_xml_symbol_by_id(symbol)
|
||||
if symbol_svg:
|
||||
@@ -815,10 +829,18 @@ class SvgWriter:
|
||||
if template_text_fields:
|
||||
symbol_xml.attrib["transform"] = symbol_transform
|
||||
symbol_xml.attrib.pop("id")
|
||||
# note: zip makes sure that we iterate over the shortest list
|
||||
# NOTE: zip makes sure that we iterate over the shortest list
|
||||
for field, text_literal in zip(template_text_fields, text_literals):
|
||||
field.text = tool.Drawing.replace_text_literal_variables(text_literal.Literal, product)
|
||||
field.attrib["class"] = classes_str
|
||||
|
||||
if fill_bg:
|
||||
symbol_copied = symbol_svg.copy()
|
||||
for text_tag in symbol_copied.xml.findall("text"):
|
||||
self.add_fill_bg(text_tag, copy=False)
|
||||
# NOTE: in case we'll later need to add fill-bg for the entire symbol:
|
||||
# self.add_fill_bg(symbol_svg, copy=False)
|
||||
self.svg.add(symbol_copied)
|
||||
self.svg.add(symbol_svg)
|
||||
return None
|
||||
|
||||
@@ -834,13 +856,34 @@ class SvgWriter:
|
||||
angle,
|
||||
text_literal.BoxAlignment,
|
||||
classes_str,
|
||||
fill_bg="fill-bg" in classes,
|
||||
fill_bg=fill_bg,
|
||||
line_number_start=line_number,
|
||||
)
|
||||
for tag in text_tags:
|
||||
self.svg.add(tag)
|
||||
line_number += len(tag.elements)
|
||||
|
||||
def draw_empty_annotation(self, obj, classes):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
|
||||
point = self.project_point_onto_camera(obj.matrix_world.translation)
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
svg_id = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Symbol")
|
||||
if not svg_id:
|
||||
# EPset_AnnotationSurveyArea is not standard! See bSI-4.3 proposal #660.
|
||||
svg_id = ifcopenshell.util.element.get_pset(element, "EPset_AnnotationSurveyArea", "PointType")
|
||||
if not svg_id:
|
||||
svg_id = str(ifcopenshell.util.element.get_predefined_type(element))
|
||||
if not svg_id:
|
||||
return
|
||||
|
||||
point = Vector(((x_offset + point.x), (y_offset - point.y)))
|
||||
symbol_position_svg = point * self.svg_scale
|
||||
self.svg.add(self.svg.use(f"#{svg_id}", insert=symbol_position_svg))
|
||||
|
||||
|
||||
def draw_point_annotation(self, obj, classes):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
@@ -849,12 +892,14 @@ class SvgWriter:
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ v.co) for v in obj.data.vertices]
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
svg_id = str(ifcopenshell.util.element.get_predefined_type(element))
|
||||
|
||||
# EPset_AnnotationSurveyArea is not standard! See bSI-4.3 proposal #660.
|
||||
point_type = ifcopenshell.util.element.get_pset(element, "EPset_AnnotationSurveyArea", "PointType")
|
||||
if point_type:
|
||||
svg_id += f"-{point_type}"
|
||||
svg_id = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Symbol")
|
||||
if not svg_id:
|
||||
# EPset_AnnotationSurveyArea is not standard! See bSI-4.3 proposal #660.
|
||||
svg_id = ifcopenshell.util.element.get_pset(element, "EPset_AnnotationSurveyArea", "PointType")
|
||||
if not svg_id:
|
||||
svg_id = str(ifcopenshell.util.element.get_predefined_type(element))
|
||||
if not svg_id:
|
||||
return
|
||||
|
||||
for symbol_position in projected_points:
|
||||
symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y)))
|
||||
@@ -936,54 +981,54 @@ class SvgWriter:
|
||||
|
||||
def draw_angle_annotations(self, obj):
|
||||
points = obj.data.splines[0].points
|
||||
region = bpy.context.region
|
||||
area = tool.Blender.get_viewport_context()["area"]
|
||||
region_3d = area.spaces.active.region_3d
|
||||
# [1, 2, 3, 4, 5] -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
|
||||
points_chunked = [points[i : i + 3] for i in range(len(points) - 2)]
|
||||
|
||||
for points_chunk in points_chunked:
|
||||
points_2d = [view3d_utils.location_3d_to_region_2d(region, region_3d, p.co.xyz) for p in points_chunk]
|
||||
points_chunk = [obj.matrix_world @ p.co.xyz for p in points_chunk]
|
||||
self.draw_svg_3point_arc(obj, points_chunk)
|
||||
|
||||
edge0 = points_2d[0] - points_2d[1]
|
||||
edge1 = points_2d[2] - points_2d[1]
|
||||
angle_radius = min(edge0.length, edge1.length)
|
||||
dir0 = edge0.normalized()
|
||||
dir1 = edge1.normalized()
|
||||
dir2 = ((dir0 + dir1) / 2).normalized()
|
||||
|
||||
# calculate p3 which is the center of the arc
|
||||
# to use draw_svg_3point_arc()
|
||||
p3 = points_2d[1] + dir2 * angle_radius
|
||||
|
||||
# make all edges the same radius
|
||||
p0 = points_2d[1] + dir0 * angle_radius
|
||||
p2 = points_2d[1] + dir1 * angle_radius
|
||||
points_chunk = [view3d_utils.region_2d_to_origin_3d(region, region_3d, p) for p in [p0, p3, p2]]
|
||||
# points = [p.co.xyz for p in bpy.context.active_object.data.splines[0].points[:3]]
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
new_verts = [bm.verts.new(p) for p in points_chunk]
|
||||
new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in ((0, 1), (1, 2))]
|
||||
self.draw_svg_3point_arc(obj, bm)
|
||||
|
||||
def draw_svg_3point_arc(self, obj, bm):
|
||||
def draw_svg_3point_arc(self, obj, angle_points):
|
||||
"""`angle_points` are expected to be already in world space"""
|
||||
# This implementation uses an SVG arc, which means that it can only draw
|
||||
# arcs that are orthogonal to the view (e.g. not arcs in 3D).
|
||||
# Gosh this is bad code :(
|
||||
|
||||
points = [v.co for v in bm.verts][:3]
|
||||
center = tool.Cad.get_center_of_arc(points, obj)
|
||||
classes = self.get_attribute_classes(obj)
|
||||
matrix_world = obj.matrix_world
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
bm.verts.ensure_lookup_table()
|
||||
arc_end_verts = [v for v in bm.verts if len(v.link_edges) == 1]
|
||||
arc_end_pts = [matrix_world @ v.co for v in arc_end_verts]
|
||||
def position_on_svg(p):
|
||||
p = self.project_point_onto_camera(p)
|
||||
offset = Vector([self.raw_width / 2, self.raw_height / 2])
|
||||
return (offset + p.xy * Vector((1, -1))) * self.svg_scale
|
||||
|
||||
# Probably need this when rewriting to use an SVG polyline instead of an arc
|
||||
def get_angle_value():
|
||||
"""points should be in world space"""
|
||||
# calculate arc angle, need to make sure we do it in world space
|
||||
v0, v1, v2 = angle_points
|
||||
edge0_ws = v0 - v1
|
||||
edge1_ws = v2 - v1
|
||||
try:
|
||||
cos_a = edge0_ws.dot(edge1_ws) / (edge0_ws.length * edge1_ws.length)
|
||||
angle_rad = acos(cos_a)
|
||||
angle = angle_rad / pi * 180
|
||||
except ZeroDivisionError:
|
||||
angle = 0
|
||||
return angle
|
||||
|
||||
angle = get_angle_value()
|
||||
angle_points = [position_on_svg(p) for p in angle_points]
|
||||
|
||||
# creating arc and making sure radius is consistent across the arc
|
||||
edge0 = angle_points[0] - angle_points[1]
|
||||
edge1 = angle_points[2] - angle_points[1]
|
||||
angle_radius = min(edge0.length, edge1.length)
|
||||
dir0 = edge0.normalized()
|
||||
dir1 = edge1.normalized()
|
||||
arc_mid_dir = ((dir0 + dir1) / 2).normalized()
|
||||
arc_points = [angle_points[1] + direction * angle_radius for direction in [dir0, arc_mid_dir, dir1]]
|
||||
|
||||
arc_end_pts = [arc_points[0], arc_points[2]]
|
||||
arc_mid_point = arc_points[1]
|
||||
|
||||
# The commented code below can be useful when we start using SVG polyline instead of an arc
|
||||
# arc_path = [arc_end_verts[0]]
|
||||
# while True:
|
||||
# last_point = arc_end_verts[0]
|
||||
@@ -996,79 +1041,44 @@ class SvgWriter:
|
||||
# if not found_another_point:
|
||||
# break
|
||||
|
||||
distance_between_end_verts = (arc_end_verts[0].co - arc_end_verts[1].co).length
|
||||
arc_mid_vert = arc_end_verts[0].link_edges[0].other_vert(arc_end_verts[0])
|
||||
is_reflex = 0 if (arc_mid_vert.co - arc_end_verts[1].co).length < distance_between_end_verts else 1
|
||||
|
||||
bm.free()
|
||||
|
||||
# Calculate the angle
|
||||
# This is the true normal in 3D, whereas the camera projection we use is the drawing direction.
|
||||
# This assumes (because we use SVG arcs) that the radius is always orthogonal to our view.
|
||||
# When rewriting to use polylines, we should use this normal instead.
|
||||
# center = tool.Cad.get_center_of_arc([p.to_3d() for p in arc_points], None).to_2d()
|
||||
# normal = mathutils.geometry.normal([arc_end_pts[0], arc_end_pts[1], center])
|
||||
normal = Vector(self.camera_projection)
|
||||
dir1 = (arc_end_pts[0] - center).normalized()
|
||||
dir2 = (arc_end_pts[1] - center).normalized()
|
||||
# normal = Vector(self.camera_projection)
|
||||
# dir1 = (arc_end_pts[0] - center).normalized()
|
||||
# arc_mid_dir = (arc_end_pts[1] - center).normalized()
|
||||
|
||||
# Let's get the matrix that represents the coordinate system of the arc.
|
||||
# This matrix allows us to get 2D vectors for calculating the signed arc angle.
|
||||
z = normal
|
||||
x = (arc_end_pts[0] - center).normalized()
|
||||
y = z.cross(x)
|
||||
arc_matrix = mathutils.Matrix([x, y, z]).transposed().to_4x4()
|
||||
# z = normal
|
||||
# x = (arc_end_pts[0] - center).normalized()
|
||||
# y = z.cross(x)
|
||||
# arc_matrix = mathutils.Matrix([x, y, z]).transposed().to_4x4()
|
||||
|
||||
dir1 = ((arc_matrix.inverted() @ arc_end_pts[0]) - (arc_matrix.inverted() @ center)).normalized()
|
||||
dir2 = ((arc_matrix.inverted() @ arc_end_pts[1]) - (arc_matrix.inverted() @ center)).normalized()
|
||||
angle = -dir1.xy.angle_signed(dir2.xy)
|
||||
|
||||
# if is_reflex:
|
||||
# angle = angle % (math.pi * 2)
|
||||
|
||||
# Center of gravity of all vertices, used to help position the text
|
||||
cog = Vector((0, 0, 0))
|
||||
for point in points:
|
||||
cog += point
|
||||
cog = matrix_world @ (cog / len(points))
|
||||
|
||||
radius = ((matrix_world @ points[0]) - center).length
|
||||
|
||||
arc_midpoint = center + ((cog - center).normalized() * radius)
|
||||
|
||||
text_position = self.project_point_onto_camera(arc_midpoint)
|
||||
text_position = Vector(
|
||||
((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)
|
||||
)
|
||||
|
||||
center_projected = self.project_point_onto_camera(center)
|
||||
center_position = Vector(
|
||||
((x_offset + center_projected.x) * self.svg_scale, (y_offset - center_projected.y) * self.svg_scale)
|
||||
)
|
||||
text_offset = (text_position - center_position).xy.normalized() * 5
|
||||
text_position += text_offset
|
||||
# dir1 = ((arc_matrix.inverted() @ arc_end_pts[0]) - (arc_matrix.inverted() @ center)).normalized()
|
||||
# dir2 = ((arc_matrix.inverted() @ arc_end_pts[1]) - (arc_matrix.inverted() @ center)).normalized()
|
||||
# angle = -dir1.xy.angle_signed(dir2.xy)
|
||||
|
||||
# calculating text parameters and adding text
|
||||
text_position = arc_mid_point + arc_mid_dir * 5
|
||||
text_style = SvgWriter.get_box_alignment_parameters("center")
|
||||
angle_text = abs(round(math.degrees(angle), 3))
|
||||
if is_reflex:
|
||||
angle_text = 360 - angle_text
|
||||
self.svg.add(self.svg.text(f"{angle_text}deg", insert=tuple(text_position), class_="ANGLE", **text_style))
|
||||
angle_text = f"{int(angle)}deg"
|
||||
self.svg.add(self.svg.text(angle_text, insert=text_position, class_="ANGLE", **text_style))
|
||||
|
||||
# Draw SVG arc, see for details: http://xahlee.info/js/svg_circle_arc.html
|
||||
arc_proj_end_pts = [self.project_point_onto_camera(v) for v in arc_end_pts]
|
||||
p1 = Vector(
|
||||
((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale)
|
||||
)
|
||||
p2 = Vector(
|
||||
((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale)
|
||||
)
|
||||
r = radius * self.svg_scale
|
||||
# reflex = 1 if angle > math.pi else 0
|
||||
reflex = is_reflex
|
||||
if reflex:
|
||||
sense = 0 if angle > 0 else 1
|
||||
else:
|
||||
sense = 1 if angle > 0 else 0
|
||||
p1, p2 = arc_end_pts
|
||||
r = angle_radius
|
||||
# reflex: 0 => arc < 180 degrees
|
||||
# 1 => arc > 180 degrees
|
||||
reflex = int(angle > 180)
|
||||
# sense: 0 => moving at negative angles
|
||||
# 1 => moving at positive angles
|
||||
sense = int(tool.Cad.is_counter_clockwise_order(*arc_points))
|
||||
d = f"M {p1.x} {p1.y} A {r} {r} 0 {reflex} {sense} {p2.x} {p2.y}"
|
||||
classes = self.get_attribute_classes(obj)
|
||||
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
||||
|
||||
def draw_radius_annotations(self, obj):
|
||||
@@ -1344,18 +1354,10 @@ class SvgWriter:
|
||||
multiline_to_bottom=True,
|
||||
fill_bg=False,
|
||||
line_number_start=0,
|
||||
_draw_fill_bg=False,
|
||||
):
|
||||
"""returns list of created text tags"""
|
||||
text_tags = []
|
||||
if fill_bg:
|
||||
method_kwargs = locals() | {"_draw_fill_bg": True, "fill_bg": False}
|
||||
del method_kwargs["self"]
|
||||
del method_kwargs["text_tags"]
|
||||
text_tags += self.create_text_tag(**method_kwargs)
|
||||
|
||||
base_text_attrs = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
base_text_attrs = base_text_attrs | ({"filter": "url(#fill-background)"} if _draw_fill_bg else {})
|
||||
|
||||
if not multiline:
|
||||
transform_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
|
||||
@@ -1392,6 +1394,11 @@ class SvgWriter:
|
||||
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
|
||||
tspan.update({"dy": f"{line_number if multiline_to_bottom else -line_number}em"})
|
||||
text_tag.add(tspan)
|
||||
|
||||
if fill_bg:
|
||||
fill_bg_tags = [self.add_fill_bg(text_tag) for text_tag in text_tags]
|
||||
text_tags = fill_bg_tags + text_tags
|
||||
|
||||
return text_tags
|
||||
|
||||
def project_point_onto_camera(self, point):
|
||||
|
||||
@@ -314,7 +314,7 @@ class BIM_PT_references(Panel):
|
||||
|
||||
if not self.props.is_editing_references:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{DocumentsData.data['total_references']} References Found", icon="LONGDISPLAY")
|
||||
row.label(text=f"{DocumentsData.data['total_references']} References Found", icon="OBJECT_HIDDEN")
|
||||
row.operator("bim.load_references", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
|
||||
@@ -26,17 +26,19 @@ classes = (
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
operator.OverrideDuplicateMoveAggregate,
|
||||
operator.OverrideDuplicateMoveAggregateMacro,
|
||||
operator.OverrideDuplicateMoveLinked,
|
||||
operator.OverrideDuplicateMoveLinkedMacro,
|
||||
operator.OverrideDuplicateMoveMacro,
|
||||
operator.OverrideDuplicateMoveAggregate,
|
||||
operator.OverrideDuplicateMoveAggregateMacro,
|
||||
operator.RefreshAggregate,
|
||||
operator.OverrideJoin,
|
||||
operator.OverrideModeSetEdit,
|
||||
operator.OverrideModeSetObject,
|
||||
operator.OverrideOriginSet,
|
||||
operator.OverrideOutlinerDelete,
|
||||
operator.OverridePasteBuffer,
|
||||
operator.PurgeUnusedRepresentations,
|
||||
operator.RefreshAggregate,
|
||||
operator.RemoveConnection,
|
||||
operator.RemoveRepresentation,
|
||||
operator.SelectConnection,
|
||||
@@ -50,6 +52,7 @@ classes = (
|
||||
ui.BIM_PT_connections,
|
||||
ui.BIM_PT_mesh,
|
||||
ui.BIM_PT_workarounds,
|
||||
ui.BIM_MT_object_set_origin,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class ConnectionsData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"connections": cls.connections()}
|
||||
cls.data = {"connections": cls.connections(), "is_connection_realization": cls.is_connection_realization()}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -114,7 +114,13 @@ class ConnectionsData:
|
||||
else:
|
||||
related_element = rel.RelatedElement
|
||||
|
||||
if element.is_a("IfcRelConnectsPathElements"):
|
||||
realizing_elements = []
|
||||
realizing_elements_connection_type = ""
|
||||
if rel.is_a("IfcRelConnectsWithRealizingElements"):
|
||||
realizing_elements.extend(rel.RealizingElements)
|
||||
realizing_elements_connection_type = rel.ConnectionType
|
||||
|
||||
if rel.is_a("IfcRelConnectsPathElements"):
|
||||
related_element_connection_type = rel.RelatedConnectionType
|
||||
else:
|
||||
related_element_connection_type = ""
|
||||
@@ -125,6 +131,8 @@ class ConnectionsData:
|
||||
"is_relating": True,
|
||||
"Name": related_element.Name or "Unnamed",
|
||||
"ConnectionType": related_element_connection_type,
|
||||
"realizing_elements": realizing_elements,
|
||||
"realizing_elements_connection_type": realizing_elements_connection_type,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -134,7 +142,13 @@ class ConnectionsData:
|
||||
else:
|
||||
relating_element = rel.RelatingElement
|
||||
|
||||
if element.is_a("IfcRelConnectsPathElements"):
|
||||
realizing_elements = []
|
||||
realizing_elements_connection_type = ""
|
||||
if rel.is_a("IfcRelConnectsWithRealizingElements"):
|
||||
realizing_elements.extend(rel.RealizingElements)
|
||||
realizing_elements_connection_type = rel.ConnectionType
|
||||
|
||||
if rel.is_a("IfcRelConnectsPathElements"):
|
||||
relating_element_connection_type = rel.RelatingConnectionType
|
||||
else:
|
||||
relating_element_connection_type = ""
|
||||
@@ -145,11 +159,30 @@ class ConnectionsData:
|
||||
"is_relating": False,
|
||||
"Name": relating_element.Name or "Unnamed",
|
||||
"ConnectionType": relating_element_connection_type,
|
||||
"realizing_elements": realizing_elements,
|
||||
"realizing_elements_connection_type": realizing_elements_connection_type,
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def is_connection_realization(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
connections = element.IsConnectionRealization
|
||||
if not connections:
|
||||
return
|
||||
|
||||
results = []
|
||||
for rel in connections:
|
||||
data = {
|
||||
"realizing_elements_connection_type": rel.ConnectionType,
|
||||
"connected_from": rel.RelatingElement,
|
||||
"connected_to": rel.RelatedElement,
|
||||
}
|
||||
results.append(data)
|
||||
return results
|
||||
|
||||
|
||||
class DerivedPlacementsData:
|
||||
data = {}
|
||||
|
||||
@@ -57,6 +57,31 @@ class EditObjectPlacement(bpy.types.Operator, Operator):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
|
||||
class OverrideOriginSet(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.override_origin_set"
|
||||
bl_label = "IFC Origin Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
origin_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
if tool.Ifc.is_moved(obj):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
if not tool.Geometry.is_meshlike(representation):
|
||||
continue
|
||||
bpy.ops.object.origin_set(type=self.origin_type)
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
|
||||
class AddRepresentation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_representation"
|
||||
bl_label = "Add Representation"
|
||||
@@ -185,6 +210,15 @@ class RemoveRepresentation(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class PurgeUnusedRepresentations(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.purge_unused_representations"
|
||||
bl_label = "Purge Unused Representations"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.purge_unused_representations(tool.Ifc, tool.Geometry)
|
||||
|
||||
|
||||
class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.update_representation"
|
||||
bl_label = "Update Representation"
|
||||
@@ -214,6 +248,10 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
material = ifcopenshell.util.element.get_material(product, should_skip_usage=True)
|
||||
|
||||
if getattr(product, "HasOpenings", False) and obj.data.BIMMeshProperties.has_openings_applied:
|
||||
# Meshlike things with openings can only be updated without openings applied.
|
||||
return
|
||||
|
||||
if not product.is_a("IfcGridAxis"):
|
||||
tool.Geometry.clear_cache(product)
|
||||
|
||||
@@ -231,8 +269,14 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
if material and material.is_a() in ["IfcMaterialProfileSet", "IfcMaterialLayerSet"]:
|
||||
# These objects are parametrically based on an axis and should not be modified as a mesh
|
||||
return
|
||||
if self.ifc_representation_class == "IfcTessellatedFaceSet":
|
||||
# We are explicitly casting to a tessellation, so remove all parametric materials.
|
||||
element_type = ifcopenshell.util.element.get_type(product)
|
||||
ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), product=element_type)
|
||||
ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), product=product)
|
||||
else:
|
||||
# These objects are parametrically based on an axis and should not be modified as a mesh
|
||||
return
|
||||
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
context_of_items = old_representation.ContextOfItems
|
||||
@@ -285,7 +329,7 @@ class UpdateRepresentation(bpy.types.Operator, Operator):
|
||||
"style.assign_representation_styles",
|
||||
self.file,
|
||||
shape_representation=new_representation,
|
||||
styles=tool.Geometry.get_styles(obj),
|
||||
styles=tool.Geometry.get_styles(obj, only_assigned_to_faces=True),
|
||||
should_use_presentation_style_assignment=context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
)
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
@@ -353,6 +397,8 @@ class GetRepresentationIfcParameters(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=context.active_object)
|
||||
parameters = context.active_object.data.BIMMeshProperties.ifc_parameters
|
||||
self.report({"INFO"}, f"{len(parameters)} parameters found.")
|
||||
|
||||
|
||||
class CopyRepresentation(bpy.types.Operator, Operator):
|
||||
@@ -637,6 +683,11 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element and element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
|
||||
continue # For now, don't copy drawings until we stabilise a bit more. It's tricky.
|
||||
|
||||
# Prior to duplicating, sync the object placement to make decomposition recreation more stable.
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
new_obj = obj.copy()
|
||||
if obj.data:
|
||||
new_obj.data = obj.data.copy()
|
||||
@@ -705,6 +756,10 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
|
||||
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
|
||||
old_to_new = {}
|
||||
for obj in context.selected_objects:
|
||||
# Prior to duplicating, sync the object placement to make decomposition recreation more stable.
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
new_obj = obj.copy()
|
||||
if obj.data:
|
||||
new_obj.data = obj.data.copy()
|
||||
@@ -1317,8 +1372,13 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
continue
|
||||
|
||||
is_profile = True
|
||||
representation_class = tool.Geometry.get_ifc_representation_class(element, representation)
|
||||
if usage_type == "PROFILE":
|
||||
operator = lambda: bpy.ops.bim.hotkey(hotkey="A_E")
|
||||
elif representation_class and "IfcCircleProfileDef" in representation_class:
|
||||
self.report({"INFO"}, "Can't edit Circle Profile Extrusion")
|
||||
obj.select_set(False)
|
||||
continue
|
||||
elif (
|
||||
tool.Geometry.is_profile_based(obj.data)
|
||||
or usage_type == "LAYER3"
|
||||
@@ -1365,8 +1425,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
else:
|
||||
obj.select_set(False)
|
||||
continue
|
||||
|
||||
if not context.selected_objects or len(context.selected_objects) != len(selected_objs):
|
||||
if len(selected_objs) > 1 and (not context.selected_objects or len(context.selected_objects) != len(selected_objs)):
|
||||
# We are trying to edit at least one non-mesh-like object : Display a hint to the user
|
||||
self.report({"INFO"}, "Only mesh-compatible representations may be edited concurrently in edit mode.")
|
||||
|
||||
@@ -1408,9 +1467,11 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
self.reload_representation(obj)
|
||||
else:
|
||||
self.reload_representation(obj)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
|
||||
for obj in self.unchanged_objs_with_openings:
|
||||
self.reload_representation(obj)
|
||||
tool.Ifc.finish_edit(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
def reload_representation(self, obj):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bpy.types import Panel, Menu
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from blenderbim.bim.module.geometry.data import RepresentationsData, ConnectionsData, DerivedPlacementsData
|
||||
@@ -29,6 +29,29 @@ def object_menu(self, context):
|
||||
self.layout.operator("bim.override_object_duplicate_move", icon="PLUGIN")
|
||||
self.layout.operator("bim.override_object_delete", icon="PLUGIN")
|
||||
self.layout.operator("bim.override_paste_buffer", icon="PLUGIN")
|
||||
self.layout.menu("BIM_MT_object_set_origin", icon="PLUGIN")
|
||||
|
||||
|
||||
class BIM_MT_object_set_origin(Menu):
|
||||
bl_idname = "BIM_MT_object_set_origin"
|
||||
bl_label = "IFC Set Origin"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Geometry to Origin"
|
||||
).origin_type = "GEOMETRY_ORIGIN"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Geometry"
|
||||
).origin_type = "ORIGIN_GEOMETRY"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to 3D Cursor"
|
||||
).origin_type = "ORIGIN_CURSOR"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Center of Mass (Surface)"
|
||||
).origin_type = "ORIGIN_CENTER_OF_MASS"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Center of Mass (Volume)"
|
||||
).origin_type = "ORIGIN_CENTER_OF_VOLUME"
|
||||
|
||||
|
||||
def outliner_menu(self, context):
|
||||
@@ -111,7 +134,7 @@ class BIM_PT_connections(Panel):
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMObjectProperties
|
||||
|
||||
if not ConnectionsData.data["connections"]:
|
||||
if not ConnectionsData.data["connections"] and not ConnectionsData.data["is_connection_realization"]:
|
||||
layout.label(text="No connections found")
|
||||
|
||||
for connection in ConnectionsData.data["connections"]:
|
||||
@@ -123,6 +146,44 @@ class BIM_PT_connections(Panel):
|
||||
op = row.operator("bim.remove_connection", icon="X", text="")
|
||||
op.connection = connection["id"]
|
||||
|
||||
if connection["realizing_elements"]:
|
||||
row = self.layout.row(align=True)
|
||||
connection_type = connection["realizing_elements_connection_type"]
|
||||
connection_type = f" ({connection_type})" if connection_type else ""
|
||||
row.label(text=f"Realizing elements{connection_type}:")
|
||||
|
||||
for element in connection["realizing_elements"]:
|
||||
row = self.layout.row(align=True)
|
||||
obj = tool.Ifc.get_object(element)
|
||||
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = element.id()
|
||||
row.label(text=obj.name)
|
||||
|
||||
# display connections where element is connection realization
|
||||
connections = ConnectionsData.data["is_connection_realization"]
|
||||
if not connections:
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Element is connections realization:")
|
||||
for connection in connections:
|
||||
# NOTE: not displayed yet
|
||||
connection_type = connection["realizing_elements_connection_type"]
|
||||
connection_type = f" ({connection_type})" if connection_type else ""
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
connected_from = connection["connected_from"]
|
||||
obj = tool.Ifc.get_object(connected_from)
|
||||
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = connected_from.id()
|
||||
row.label(text=obj.name)
|
||||
|
||||
row.label(text="", icon="FORWARD")
|
||||
|
||||
connected_to = connection["connected_to"]
|
||||
obj = tool.Ifc.get_object(connected_to)
|
||||
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = connected_to.id()
|
||||
row.label(text=obj.name)
|
||||
|
||||
|
||||
class BIM_PT_mesh(Panel):
|
||||
bl_label = "Representation Utilities"
|
||||
|
||||
@@ -34,6 +34,7 @@ classes = (
|
||||
operator.GetCursorLocation,
|
||||
operator.SetCursorLocation,
|
||||
operator.ConvertAngleToCoordinates,
|
||||
operator.ImportPlot,
|
||||
prop.BIMGeoreferenceProperties,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_gis_utilities,
|
||||
|
||||
@@ -76,7 +76,7 @@ class SetIfcGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_ifc_grid_north"
|
||||
bl_label = "Set IFC Grid North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set IFC grid north"
|
||||
bl_description = "Set IFC grid north based on current Blender North Offset"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_ifc_grid_north(tool.Georeference)
|
||||
@@ -86,7 +86,7 @@ class SetBlenderGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_blender_grid_north"
|
||||
bl_label = "Set Blender Grid North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set Blender grid north"
|
||||
bl_description = "Set Blender North Offset based on current IFC grid north"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_blender_grid_north(tool.Georeference)
|
||||
@@ -110,7 +110,7 @@ class SetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_cursor_location"
|
||||
bl_label = "Set Cursor Location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Move curson location to the specified coordinates"
|
||||
bl_description = "Move cursor location to the specified coordinates"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -125,7 +125,7 @@ class SetIfcTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_ifc_true_north"
|
||||
bl_label = "Set IFC True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set IFC True north"
|
||||
bl_description = "Set IFC true north based on current Blender North Offset"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_ifc_true_north(tool.Georeference)
|
||||
@@ -135,7 +135,7 @@ class SetBlenderTrueNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.set_blender_true_north"
|
||||
bl_label = "Set Blender True North"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Set Blender true north"
|
||||
bl_description = "Set Blender North Offset based on current IFC true north"
|
||||
|
||||
def _execute(self, context):
|
||||
core.set_blender_true_north(tool.Georeference)
|
||||
@@ -187,3 +187,20 @@ class ConvertAngleToCoordinates(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_angle_to_coord(tool.Georeference, type=self.type)
|
||||
|
||||
|
||||
class ImportPlot(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.import_plot"
|
||||
bl_label = "Import Plot"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Import plot"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.csv", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
core.import_plot(tool.Georeference, filepath=self.filepath)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@@ -58,6 +58,8 @@ class BIM_PT_materials(Panel):
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.material_type == "IfcMaterial":
|
||||
if not self.props.active_material_id:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
@@ -66,13 +68,10 @@ class BIM_PT_materials(Panel):
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL").material = material.ifc_definition_id
|
||||
self.draw_editable_material_attributes_ui()
|
||||
else:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL").material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
else:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
|
||||
@@ -50,8 +50,9 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
|
||||
row.operator("bim.clean_wireframes")
|
||||
row = layout.row()
|
||||
row.operator("bim.patch_non_parametric_mep_segment")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.enable_editing_sketch_extrusion_profile", text="Start Sketching")
|
||||
row.operator("bim.edit_sketch_extrusion_profile", text="", icon="FILE_REFRESH")
|
||||
row.operator("bim.disable_editing_sketch_extrusion_profile", text="", icon="CANCEL")
|
||||
row = layout.row()
|
||||
row.operator("bim.import_plot", text="Import Plot Coordinates", icon="FILE_FOLDER")
|
||||
|
||||
@@ -78,6 +78,7 @@ classes = (
|
||||
opening.AddPotentialHalfSpaceSolid,
|
||||
opening.AddPotentialOpening,
|
||||
opening.EditOpenings,
|
||||
opening.CloneOpening,
|
||||
opening.FlipFill,
|
||||
opening.HideBooleans,
|
||||
opening.HideOpenings,
|
||||
|
||||
@@ -130,11 +130,11 @@ def update_door_modifier_representation(context, obj):
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
element.OperationType = props.door_type
|
||||
|
||||
# occurences attributes
|
||||
occurences = tool.Ifc.get_all_element_occurences(element)
|
||||
for occurence in occurences:
|
||||
occurence.OverallWidth = props.overall_width / si_conversion
|
||||
occurence.OverallHeight = props.overall_height / si_conversion
|
||||
# occurrences attributes
|
||||
occurrences = tool.Ifc.get_all_element_occurrences(element)
|
||||
for occurrence in occurrences:
|
||||
occurrence.OverallWidth = props.overall_width / si_conversion
|
||||
occurrence.OverallHeight = props.overall_height / si_conversion
|
||||
|
||||
update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import ifcopenshell.util.system
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import mathutils.geometry
|
||||
import numpy as np
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.core.type
|
||||
import blenderbim.core.root
|
||||
@@ -73,29 +74,68 @@ class RegenerateDistributionElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
[e for e in ifcopenshell.util.system.get_connected_from(element) if e not in processed_elements]
|
||||
)
|
||||
|
||||
if len(connected) == 1:
|
||||
extend_branch(list(connected)[0], branch, element)
|
||||
else:
|
||||
for connected_element in connected:
|
||||
branch_element["children"].append(extend_branch(connected_element, [], element))
|
||||
for connected_element in connected:
|
||||
branch_element["children"].append(extend_branch(connected_element, [], element))
|
||||
|
||||
return branch
|
||||
|
||||
queue = extend_branch(current_element, [])[0]["children"]
|
||||
extended_branch = extend_branch(current_element, [])
|
||||
queue = extended_branch[0]["children"]
|
||||
|
||||
# import pprint
|
||||
# pprint.pprint(queue)
|
||||
|
||||
def get_connected_ports_between(element1, element2):
|
||||
ports1 = tool.System.get_ports(element1)
|
||||
ports2 = tool.System.get_ports(element2)
|
||||
|
||||
for p in ports1:
|
||||
connected_port = tool.System.get_connected_port(p)
|
||||
# in IFC2X3 there is no PredefinedType
|
||||
if getattr(p, "PredefinedType", None) == "WIRELESS":
|
||||
continue
|
||||
if connected_port in ports2:
|
||||
return p, connected_port
|
||||
|
||||
return None, None
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
def process_branch(branch):
|
||||
for branch_element in branch:
|
||||
element = branch_element["element"]
|
||||
print("processing", element)
|
||||
predecessor = branch_element["predecessor"]
|
||||
if False: # If the element does not need to be transformed, return early.
|
||||
return
|
||||
|
||||
# Perform the extend, translate, rotate, etc the element as necessary based on the predecessor.
|
||||
# For segments, prioritise extensions instead of translations.
|
||||
# For everything else, only translate. No rotation.
|
||||
# For everything besides segments, only translate. No rotation.
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
obj_pred = tool.Ifc.get_object(predecessor)
|
||||
if tool.Ifc.is_moved(obj):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
if tool.Ifc.is_moved(obj_pred):
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj_pred)
|
||||
|
||||
port, port_pred = get_connected_ports_between(element, predecessor)
|
||||
port_matrix_pred = tool.Model.get_element_matrix(port_pred)
|
||||
|
||||
# Only segments can be extended
|
||||
# extension for them takes priority over translation
|
||||
if element.is_a("IfcFlowSegment"):
|
||||
DumbProfileJoiner().join_E(obj, port_matrix_pred.translation * si_conversion)
|
||||
context.view_layer.update() # update since extrusion might involve changing object's location
|
||||
|
||||
port_martix = tool.Model.get_element_matrix(port)
|
||||
port_location = port_martix.translation
|
||||
port_location_pred = port_matrix_pred.translation
|
||||
if not tool.Cad.are_vectors_equal(port_location, port_location_pred):
|
||||
obj.location += (port_location_pred - port_location) * si_conversion
|
||||
context.view_layer.update() # otherwise tool.Ifc.is_moved won't get triggered
|
||||
else:
|
||||
# If the element does not need to be transformed, return early.
|
||||
return
|
||||
|
||||
for child_branch in branch_element["children"]:
|
||||
process_branch(child_branch)
|
||||
|
||||
@@ -168,6 +208,7 @@ class FitFlowSegments(bpy.types.Operator, tool.Ifc.Operator):
|
||||
elif total_profiles == 2:
|
||||
if is_parallel:
|
||||
fitting_type = "TRANSITION"
|
||||
bpy.ops.bim.mep_add_transition()
|
||||
|
||||
elif total_selected_objs == 3:
|
||||
if total_profiles > 1:
|
||||
@@ -229,6 +270,10 @@ class MEPGenerator:
|
||||
if port_position == "end_port":
|
||||
tool.Model.edit_element_placement(port, end_port_matrix)
|
||||
|
||||
continue
|
||||
|
||||
# NOTE: currently this functionality is moved to bim.regenerate_distribution_element
|
||||
|
||||
connected_port = tool.System.get_connected_port(port)
|
||||
if not connected_port:
|
||||
continue
|
||||
@@ -303,6 +348,8 @@ class MEPGenerator:
|
||||
There lies the problem that it won't be
|
||||
able to identify the fittings that were not yet connected to any segments yet.
|
||||
"""
|
||||
|
||||
# TODO: check angle, start, end and offset for transitions
|
||||
if not isinstance(segment_or_segments, collections.abc.Iterable):
|
||||
segments = [segment_or_segments]
|
||||
ports = [port_or_ports]
|
||||
@@ -313,7 +360,7 @@ class MEPGenerator:
|
||||
segments_data = []
|
||||
for segment, port in zip(segments, ports, strict=True):
|
||||
segment_type = ifcopenshell.util.element.get_type(segment)
|
||||
# if segment doesn't have type we cannot check compatibility by available occurences
|
||||
# if segment doesn't have type we cannot check compatibility by available occurrences
|
||||
if segment_type is None:
|
||||
return
|
||||
segments_data.append((segment_type, port.PredefinedType, port.SystemType))
|
||||
@@ -355,42 +402,60 @@ class MEPGenerator:
|
||||
return len(segments_data) == 0
|
||||
|
||||
def pack_return_data(fitting_type, ports, segments_data):
|
||||
packed_data = {"fitting_type": fitting_type}
|
||||
|
||||
if predefined_type == "OBSTRUCTION":
|
||||
return packed_data
|
||||
|
||||
for port in ports:
|
||||
port_local_position = V(*port.ObjectPlacement.RelativePlacement.Location.Coordinates)
|
||||
if tool.Cad.is_x(port_local_position.length, 0.0):
|
||||
start_port = port
|
||||
break
|
||||
|
||||
connected_port = tool.System.get_connected_port(start_port)
|
||||
connected_element = tool.System.get_port_relating_element(connected_port)
|
||||
element_type = ifcopenshell.util.element.get_type(connected_element)
|
||||
return {"fitting_type": fitting_type, "start_port_match": element_type == segments_data[0][0]}
|
||||
packed_data["start_port_match"] = element_type == segments_data[0][0]
|
||||
|
||||
return packed_data
|
||||
|
||||
fitting_types = tool.Ifc.get().by_type(self.get_mep_element_class_name(segments[0], "FittingType"))
|
||||
for fitting_type in fitting_types:
|
||||
if fitting_type.PredefinedType != predefined_type:
|
||||
continue
|
||||
fittings = tool.Ifc.get_all_element_occurences(fitting_type)
|
||||
fittings = tool.Ifc.get_all_element_occurrences(fitting_type)
|
||||
if not fittings:
|
||||
continue
|
||||
fitting = fittings[0]
|
||||
|
||||
ports = ifcopenshell.util.system.get_ports(fitting)
|
||||
fitting_data = []
|
||||
fitting_connected_to_none_type = False
|
||||
for port in ports:
|
||||
connected_port = tool.System.get_connected_port(port)
|
||||
connected_element = tool.System.get_port_relating_element(connected_port)
|
||||
element_type = ifcopenshell.util.element.get_type(connected_element)
|
||||
if element_type is None:
|
||||
fitting_connected_to_none_type = True
|
||||
break
|
||||
fitting_data.append((element_type, port.PredefinedType, port.SystemType))
|
||||
for fitting in fittings:
|
||||
ports = ifcopenshell.util.system.get_ports(fitting)
|
||||
fitting_data = []
|
||||
skipped_the_occurrence = False
|
||||
for port in ports:
|
||||
connected_port = tool.System.get_connected_port(port)
|
||||
|
||||
if fitting_connected_to_none_type:
|
||||
continue
|
||||
# fitting port is not connected to anything
|
||||
if not connected_port:
|
||||
skipped_the_occurrence = True
|
||||
break
|
||||
|
||||
if are_connected_elements_compatible(segments_data, fitting_data):
|
||||
return pack_return_data(fitting_type, ports, segments_data)
|
||||
connected_element = tool.System.get_port_relating_element(connected_port)
|
||||
element_type = ifcopenshell.util.element.get_type(connected_element)
|
||||
|
||||
# fitting is connected to none type
|
||||
if element_type is None:
|
||||
skipped_the_occurrence = True
|
||||
break
|
||||
|
||||
fitting_data.append((element_type, port.PredefinedType, port.SystemType))
|
||||
|
||||
# if we skipped the occurrence we still can other occurrences
|
||||
# otherwise checking 1 occurrence is enough
|
||||
if not skipped_the_occurrence:
|
||||
if are_connected_elements_compatible(segments_data, fitting_data):
|
||||
return pack_return_data(fitting_type, ports, segments_data)
|
||||
return
|
||||
|
||||
def create_obstruction_type(self, segment):
|
||||
# code is very similar to "bim.add_type"
|
||||
@@ -453,7 +518,7 @@ class MEPGenerator:
|
||||
obstruction_type = self.create_obstruction_type(segment)
|
||||
|
||||
profile_joiner = DumbProfileJoiner()
|
||||
# create obstruction occurence and setup it's length and port
|
||||
# create obstruction occurrence and setup it's length and port
|
||||
# NOTE: at this point we loose current blender objects selection
|
||||
bpy.ops.bim.add_constr_type_instance(relating_type_id=obstruction_type.id())
|
||||
obstruction_obj = bpy.context.active_object
|
||||
@@ -570,10 +635,13 @@ class MEPAddTransition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
start_axis = tool.Model.get_flow_segment_axis(start_object)
|
||||
end_axis = tool.Model.get_flow_segment_axis(end_object)
|
||||
start_object_rotation = start_object.matrix_world.to_quaternion()
|
||||
start_object_z_basis = start_object_rotation.to_matrix().col[2] # z basis vector
|
||||
keep_only_z_axis = lambda p_ws: p_ws.dot(start_object_z_basis) * start_object_z_basis
|
||||
|
||||
# TODO: support cases when segments are partially or completely overlapping each other
|
||||
if not tool.Cad.are_edges_collinear(start_axis, end_axis):
|
||||
self.report({"ERROR"}, f"Failed to add transition - non collinear segments are not yet supported.")
|
||||
if not tool.Cad.are_edges_parallel(start_axis, end_axis):
|
||||
self.report({"ERROR"}, f"Failed to add transition - segments are not parallel.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
start_segment_data = MEPGenerator().get_segment_data(start_element)
|
||||
@@ -588,38 +656,115 @@ class MEPAddTransition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
end_segment_data["end_point"]: end_segment_data["end_port"],
|
||||
}
|
||||
|
||||
# transition points
|
||||
start_point, end_point = tool.Cad.closest_points(
|
||||
(start_segment_data["start_point"], start_segment_data["end_point"]),
|
||||
(end_segment_data["start_point"], end_segment_data["end_point"]),
|
||||
)
|
||||
transition_dir = (end_point - start_point).normalized()
|
||||
|
||||
# figure profile offset
|
||||
base_transition_dir = keep_only_z_axis(end_point - start_point).normalized()
|
||||
flip_profile_offset = base_transition_dir.dot(start_object_z_basis) < 0
|
||||
|
||||
if tool.Cad.are_edges_collinear(start_axis, end_axis):
|
||||
profile_offset = None
|
||||
else:
|
||||
to_start_object_space = start_object_rotation.inverted()
|
||||
profile_offset = (
|
||||
(to_start_object_space @ end_object.location) - (to_start_object_space @ start_object.location)
|
||||
).xy
|
||||
if tool.Cad.is_x(profile_offset.length_squared, 0):
|
||||
profile_offset = None
|
||||
else:
|
||||
profile_offset = profile_offset / si_conversion
|
||||
if flip_profile_offset:
|
||||
profile_offset *= V(1, -1)
|
||||
|
||||
# world space profile offset
|
||||
profile_offset_ws = (
|
||||
start_object_rotation @ (profile_offset * si_conversion).to_3d() if profile_offset else V(0, 0, 0)
|
||||
)
|
||||
|
||||
# will need entire_length to check that transition length fill fit
|
||||
first_segment_start, second_segment_end = [
|
||||
p
|
||||
for p in (
|
||||
start_segment_data["start_point"],
|
||||
start_segment_data["end_point"],
|
||||
end_segment_data["start_point"],
|
||||
end_segment_data["end_point"],
|
||||
)
|
||||
if p not in (start_point, end_point)
|
||||
]
|
||||
|
||||
def get_segments_length():
|
||||
start_dir = (start_point - first_segment_start).normalized()
|
||||
segments_vector = second_segment_end - first_segment_start
|
||||
return segments_vector.dot(start_dir)
|
||||
|
||||
entire_length = get_segments_length()
|
||||
|
||||
# can't rely on (end_point-start_point) here because
|
||||
# transition might change the segments length and therefore direction will be changed
|
||||
segments_dir = (start_point - first_segment_start).normalized()
|
||||
start_port = points_ports_map[start_point]
|
||||
end_port = points_ports_map[end_point]
|
||||
|
||||
# add transition representation
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
rep, transition_data = builder.mep_transition_shape(
|
||||
start_element, end_element, self.start_length / si_conversion, self.end_length / si_conversion
|
||||
start_element,
|
||||
end_element,
|
||||
self.start_length / si_conversion,
|
||||
self.end_length / si_conversion,
|
||||
profile_offset=profile_offset,
|
||||
)
|
||||
|
||||
if not rep:
|
||||
self.report({"ERROR"}, f"Failed to add transition - this kind of profiles is not yet supported.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
middle_point = (start_point + end_point) / 2
|
||||
full_transition_length = transition_data["full_transition_length"] * si_conversion
|
||||
start_segment_extend_point = middle_point - transition_dir * full_transition_length / 2
|
||||
end_segment_extend_point = middle_point + transition_dir * full_transition_length / 2
|
||||
DumbProfileJoiner().join_E(start_object, start_segment_extend_point)
|
||||
DumbProfileJoiner().join_E(end_object, end_segment_extend_point)
|
||||
if full_transition_length >= entire_length:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Failed to add transition - transition length is larger the segments and the distance between them.\n"
|
||||
+ f"Transition length: {full_transition_length:.2f}m, segments length: {entire_length:.2f}m",
|
||||
)
|
||||
ifcopenshell.api.run("geometry.remove_representation", ifc_file, representation=rep)
|
||||
return {"CANCELLED"}
|
||||
|
||||
# calculate bunch of points to for adjustments
|
||||
middle_point = keep_only_z_axis((start_point + end_point) / 2 - start_point) + start_point
|
||||
start_segment_extend_point = middle_point - segments_dir * full_transition_length / 2
|
||||
end_segment_extend_point = middle_point + segments_dir * full_transition_length / 2 + profile_offset_ws
|
||||
transition_dir = keep_only_z_axis(end_segment_extend_point - start_segment_extend_point).normalized()
|
||||
|
||||
# adjust the segments
|
||||
end_object_rotation = end_object.matrix_world.to_quaternion()
|
||||
end_object_z_basis = end_object_rotation.to_matrix().col[2] # z basis vector
|
||||
if tool.Cad.is_x(start_object_z_basis.dot(transition_dir), 1):
|
||||
start_connection = "ATEND"
|
||||
else:
|
||||
start_connection = "ATSTART"
|
||||
if tool.Cad.is_x(end_object_z_basis.dot(transition_dir), 1):
|
||||
end_connection = "ATSTART"
|
||||
else:
|
||||
end_connection = "ATEND"
|
||||
DumbProfileJoiner().join_E(start_object, start_segment_extend_point, start_connection)
|
||||
DumbProfileJoiner().join_E(end_object, end_segment_extend_point, end_connection)
|
||||
|
||||
# find the compatible fitting type
|
||||
fitting_data = MEPGenerator().get_compatible_fitting_type(
|
||||
[start_element, end_element], [start_port, end_port], "TRANSITION"
|
||||
)
|
||||
|
||||
transition_type = fitting_data["fitting_type"] if fitting_data else None
|
||||
if transition_type:
|
||||
# TODO: handle the case without creating a representation in the first place?
|
||||
ifcopenshell.api.run("geometry.remove_representation", ifc_file, representation=rep)
|
||||
start_port_match = fitting_data["start_port_match"] if fitting_data else True
|
||||
|
||||
# create new fitting type if nothing is compatible
|
||||
if not transition_type:
|
||||
mesh = bpy.data.meshes.new("Transition")
|
||||
obj = bpy.data.objects.new("Transition", mesh)
|
||||
@@ -643,22 +788,28 @@ class MEPAddTransition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
# NOTE: at this point we loose current blender objects selection
|
||||
# create transition element
|
||||
bpy.ops.bim.add_constr_type_instance(relating_type_id=transition_type.id())
|
||||
transition_obj = bpy.context.active_object
|
||||
|
||||
# adjust transition segment rotation and location
|
||||
# required since we'll base our `transition_obj_dir` on this
|
||||
transition_obj.matrix_world = start_object.matrix_world
|
||||
context.view_layer.update()
|
||||
transition_obj_dir = tool.Cad.get_edge_direction(tool.Model.get_flow_segment_axis(transition_obj))
|
||||
direction_match = tool.Cad.are_vectors_equal(transition_obj_dir, transition_dir)
|
||||
|
||||
# depending on transition direction we may need to flip it or attach it's origin to end segment
|
||||
# direction can be different depending on:
|
||||
# - order of the current segments
|
||||
# - order of the segments that were used with the same transition type before
|
||||
transition_obj_dir = tool.Cad.get_edge_direction(tool.Model.get_flow_segment_axis(transition_obj))
|
||||
direction_match = tool.Cad.are_vectors_equal(transition_dir, transition_obj_dir)
|
||||
# if there are no mismatches or everything matches up we don't need to flip the transition
|
||||
if start_port_match != direction_match:
|
||||
transition_obj.matrix_world = start_object.matrix_world @ Matrix.Rotation(radians(180), 4, "X")
|
||||
transition_obj.location = start_segment_extend_point if start_port_match else end_segment_extend_point
|
||||
|
||||
# add ports and connect them
|
||||
ports = tool.System.add_ports(transition_obj)
|
||||
ports = tool.System.add_ports(transition_obj, offset_end_port=profile_offset_ws)
|
||||
if not start_port_match:
|
||||
start_port, end_port = end_port, start_port
|
||||
tool.Ifc.run("system.connect_port", port1=ports[0], port2=start_port, direction="NOTDEFINED")
|
||||
|
||||
@@ -70,7 +70,10 @@ class FilledOpeningGenerator:
|
||||
)
|
||||
|
||||
if target is None:
|
||||
target = bpy.context.scene.cursor.location
|
||||
should_set_z_level = True
|
||||
target = bpy.context.scene.cursor.location.copy()
|
||||
else:
|
||||
should_set_z_level = False
|
||||
|
||||
# Sometimes, the voided_obj may be an aggregate, which won't have any representation.
|
||||
if voided_obj.data:
|
||||
@@ -86,12 +89,17 @@ class FilledOpeningGenerator:
|
||||
axis = tool.Model.get_wall_axis(voided_obj, layers=layers)["base"]
|
||||
|
||||
new_matrix = voided_obj.matrix_world.copy()
|
||||
new_matrix.translation = tool.Cad.point_on_edge(target, axis)
|
||||
point_on_axis = tool.Cad.point_on_edge(target, axis)
|
||||
new_matrix.translation.x = point_on_axis.x
|
||||
new_matrix.translation.y = point_on_axis.y
|
||||
|
||||
if filling.is_a("IfcDoor"):
|
||||
new_matrix.translation.z = voided_obj.matrix_world.translation.z
|
||||
if should_set_z_level:
|
||||
if filling.is_a("IfcDoor"):
|
||||
new_matrix.translation.z = voided_obj.matrix_world.translation.z
|
||||
else:
|
||||
new_matrix.translation.z = voided_obj.matrix_world.translation.z + props.rl2
|
||||
else:
|
||||
new_matrix.translation.z = voided_obj.matrix_world.translation.z + props.rl2
|
||||
new_matrix.translation.z = filling_obj.matrix_world.copy().translation.z
|
||||
|
||||
filling_obj.matrix_world = new_matrix
|
||||
bpy.context.view_layer.update()
|
||||
@@ -498,10 +506,14 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_boolean"
|
||||
bl_label = "Add Boolean"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Applies a boolean to the selected IFC object using the other selected blender object as a void"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_objects) == 2
|
||||
if not len(context.selected_objects) == 2:
|
||||
cls.poll_message_set("Exactly 2 objects need to be selected.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
@@ -646,6 +658,7 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
upstream_obj = None
|
||||
for obj in context.selected_objects:
|
||||
if (
|
||||
not obj.data
|
||||
@@ -673,6 +686,8 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
tool.Blender.set_active_object(upstream_obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -735,12 +750,19 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
building_objs = set()
|
||||
model = tool.Ifc.get()
|
||||
all_openings = model.by_type("IfcOpeningElement")
|
||||
similar_openings = []
|
||||
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
openings = [r.RelatedOpeningElement for r in element.HasOpenings]
|
||||
for opening in openings:
|
||||
for all_opening in all_openings:
|
||||
if all_opening.ObjectPlacement == opening.ObjectPlacement:
|
||||
similar_openings.append(all_opening)
|
||||
opening_obj = tool.Ifc.get_object(opening)
|
||||
if opening_obj:
|
||||
if tool.Ifc.is_edited(opening_obj):
|
||||
@@ -749,6 +771,8 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
blenderbim.core.geometry.edit_object_placement(
|
||||
tool.Ifc, tool.Geometry, tool.Surveyor, obj=opening_obj
|
||||
)
|
||||
for similar_opening in similar_openings:
|
||||
similar_opening.ObjectPlacement = opening.ObjectPlacement
|
||||
building_objs.add(obj)
|
||||
building_objs.update(self.get_all_building_objects_of_similar_openings(opening))
|
||||
tool.Ifc.unlink(element=opening, obj=opening_obj)
|
||||
@@ -773,6 +797,34 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
results.add(obj)
|
||||
return results
|
||||
|
||||
class CloneOpening(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.clone_opening"
|
||||
bl_label = "Clone Opening"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create and assign to the selected element the selected opening and assign to it the same representation and object placement"
|
||||
|
||||
def _execute(self, context):
|
||||
objects = bpy.context.selected_objects
|
||||
|
||||
for obj in objects:
|
||||
entity = tool.Ifc.get_entity(obj)
|
||||
if entity.is_a() == "IfcWall":
|
||||
wall = entity
|
||||
continue
|
||||
if entity.is_a() == "IfcOpeningElement":
|
||||
opening = entity
|
||||
continue
|
||||
|
||||
opening_placement = opening.ObjectPlacement
|
||||
opening_representations = opening.Representation.Representations
|
||||
|
||||
new_opening = ifcopenshell.api.run("root.create_entity", tool.Ifc.get(), ifc_class="IfcOpeningElement")
|
||||
for representation in opening_representations:
|
||||
ifcopenshell.api.run("geometry.assign_representation", tool.Ifc.get(), product = new_opening, representation = representation)
|
||||
|
||||
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening = new_opening, element = wall)
|
||||
new_opening.ObjectPlacement = opening_placement
|
||||
return {"FINISHED"}
|
||||
|
||||
# TODO: merge with ProfileDecorator?
|
||||
class DecorationsHandler:
|
||||
|
||||
@@ -213,7 +213,7 @@ class AddConstrTypeInstance(bpy.types.Operator):
|
||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
|
||||
)
|
||||
|
||||
# set occurences properties for the types defined with modifiers
|
||||
# set occurrences properties for the types defined with modifiers
|
||||
if instance_class in ["IfcWindow", "IfcDoor"]:
|
||||
pset_name = f"BBIM_{instance_class[3:]}"
|
||||
bbim_pset = ifcopenshell.util.element.get_psets(element).get(pset_name, None)
|
||||
|
||||
@@ -272,13 +272,21 @@ class DumbProfileJoiner:
|
||||
body = copy.deepcopy(axis1)
|
||||
self.recreate_profile(element1, profile1, axis, body)
|
||||
|
||||
def join_E(self, profile1, target):
|
||||
def join_E(self, profile1, target, connection=None):
|
||||
"""`connection` = `ATEND` / `ATSTART` to explicitly define the reference point for the join.
|
||||
|
||||
For example if profile 1m long and `target` is at (0, 0, 0.1) and `connection` = `None`
|
||||
it will implicitly use `connection` = `ATSTART` resulting in profile object 0.9m long and moved to (0, 0, 0.1).
|
||||
|
||||
But with `connection` = `ATEND` it will result in the profile object 0.1m long, locaiton unchanged.
|
||||
"""
|
||||
element1 = tool.Ifc.get_entity(profile1)
|
||||
if not element1:
|
||||
return
|
||||
axis1 = self.get_profile_axis(profile1)
|
||||
intersect, connection = mathutils.geometry.intersect_point_line(target, *axis1)
|
||||
connection = "ATEND" if connection > 0.5 else "ATSTART"
|
||||
intersect, connection_value = mathutils.geometry.intersect_point_line(target, *axis1)
|
||||
if connection is None:
|
||||
connection = "ATEND" if connection_value > 0.5 else "ATSTART"
|
||||
|
||||
ifcopenshell.api.run("geometry.disconnect_path", tool.Ifc.get(), element=element1, connection_type=connection)
|
||||
|
||||
|
||||
@@ -59,10 +59,10 @@ def update_railing_modifier_ifc_data(context):
|
||||
|
||||
# type attributes
|
||||
element.PredefinedType = "USERDEFINED"
|
||||
# occurences attributes
|
||||
occurences = tool.Ifc.get_all_element_occurences(element)
|
||||
for occurence in occurences:
|
||||
occurence.ObjectType = props.railing_type
|
||||
# occurrences attributes
|
||||
occurrences = tool.Ifc.get_all_element_occurrences(element)
|
||||
for occurrence in occurrences:
|
||||
occurrence.ObjectType = props.railing_type
|
||||
|
||||
# update pset
|
||||
pset_common = tool.Pset.get_element_pset(element, "Pset_RailingCommon")
|
||||
|
||||
@@ -40,10 +40,6 @@ from pprint import pprint
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoofType.htm
|
||||
|
||||
|
||||
def float_is_zero(f):
|
||||
return 0.0001 >= f >= -0.0001
|
||||
|
||||
|
||||
def bm_mesh_clean_up(bm):
|
||||
# remove internal edges and faces
|
||||
# adding missing faces so we could rely on `e.is_boundary` later
|
||||
@@ -99,7 +95,7 @@ def is_valid_roof_footprint(bm):
|
||||
# should be bmesh to support edit mode
|
||||
bm.verts.ensure_lookup_table()
|
||||
base_z = bm.verts[0].co.z
|
||||
all_verts_same_level = all([float_is_zero(v.co.z - base_z) for v in bm.verts[1:]])
|
||||
all_verts_same_level = all([tool.Cad.is_x(v.co.z - base_z, 0) for v in bm.verts[1:]])
|
||||
if not all_verts_same_level:
|
||||
return (
|
||||
{"ERROR"},
|
||||
@@ -202,7 +198,7 @@ def generate_hiped_roof_bmesh(
|
||||
|
||||
def find_identical_new_vert(co):
|
||||
for v in bm.verts:
|
||||
if float_is_zero((co - v.co).length):
|
||||
if tool.Cad.is_x((co - v.co).length, 0):
|
||||
return v
|
||||
|
||||
def find_other_polygon_verts(edge):
|
||||
@@ -234,7 +230,7 @@ def generate_hiped_roof_bmesh(
|
||||
bottom_chords_to_remove = []
|
||||
|
||||
def is_footprint_vert(v):
|
||||
return float_is_zero(v.co.z - footprint_z)
|
||||
return tool.Cad.is_x(v.co.z - footprint_z, 0)
|
||||
|
||||
def is_footprint_edge(edge):
|
||||
return all(is_footprint_vert(v) for v in edge.verts)
|
||||
@@ -326,7 +322,7 @@ def generate_hiped_roof_bmesh(
|
||||
default_offset_dir = Vector([0, 0, 1]) * roof_thickness
|
||||
footprint_verts = set()
|
||||
|
||||
if not float_is_zero(rafter_edge_angle):
|
||||
if not tool.Cad.is_x(rafter_edge_angle, 0):
|
||||
footprint_edges = []
|
||||
for edge in extruded_edges:
|
||||
if is_footprint_edge(edge):
|
||||
@@ -398,7 +394,7 @@ def update_roof_modifier_ifc_data(context):
|
||||
if not angle_layer:
|
||||
return False
|
||||
for edge_angle in angle_layer:
|
||||
if float_is_zero(edge_angle - pi / 2):
|
||||
if tool.Cad.is_x(edge_angle - pi / 2, 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -406,8 +402,8 @@ def update_roof_modifier_ifc_data(context):
|
||||
if props.roof_type == "HIP/GABLE ROOF":
|
||||
element.PredefinedType = "GABLE_ROOF" if roof_is_gabled() else "HIP_ROOF"
|
||||
|
||||
# occurences attributes
|
||||
# occurences = tool.Ifc.get_all_element_occurences(element)
|
||||
# occurrences attributes
|
||||
# occurrences = tool.Ifc.get_all_element_occurrences(element)
|
||||
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
|
||||
@@ -95,19 +95,19 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
|
||||
for obj in bpy.context.visible_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
visible_element = tool.Ifc.get_entity(obj)
|
||||
|
||||
if (
|
||||
not element
|
||||
not visible_element
|
||||
or obj.type != "MESH"
|
||||
or not self.is_bounding_class(element)
|
||||
or not self.is_bounding_class(visible_element)
|
||||
or not tool.Drawing.is_intersecting_plane(obj, self.cut_point, self.cut_normal)
|
||||
):
|
||||
continue
|
||||
|
||||
old_mesh = None
|
||||
if element.HasOpenings:
|
||||
new_mesh = self.create_mesh(ifcopenshell.geom.create_shape(gross_settings, element))
|
||||
if visible_element.HasOpenings:
|
||||
new_mesh = self.create_mesh(ifcopenshell.geom.create_shape(gross_settings, visible_element))
|
||||
old_mesh = obj.data
|
||||
obj.data = new_mesh
|
||||
|
||||
|
||||
@@ -156,15 +156,14 @@ class BIM_PT_authoring(Panel):
|
||||
class BIM_PT_Grids(Panel):
|
||||
bl_label = "Grids"
|
||||
bl_idname = "BIM_PT_Grids"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row()
|
||||
row.operator("mesh.add_grid", icon="ADD", text="Add Grids")
|
||||
self.layout.row().operator("mesh.add_grid", icon="ADD", text="Add Grids")
|
||||
|
||||
|
||||
class BIM_PT_array(bpy.types.Panel):
|
||||
|
||||
@@ -34,7 +34,7 @@ from mathutils import Vector
|
||||
# TODO: move to some utils helpers/tool module
|
||||
def update_simple_openings(element, opening_width, opening_height):
|
||||
ifc_file = tool.Ifc.get()
|
||||
fillings = tool.Ifc.get_all_element_occurences(element)
|
||||
fillings = tool.Ifc.get_all_element_occurrences(element)
|
||||
|
||||
voided_objs = set()
|
||||
has_replaced_opening_representation = False
|
||||
@@ -158,11 +158,11 @@ def update_window_modifier_representation(context, obj):
|
||||
if tool.Ifc.get_schema() != "IFC2X3":
|
||||
element.PartitioningType = props.window_type
|
||||
|
||||
# occurences attributes
|
||||
occurences = tool.Ifc.get_all_element_occurences(element)
|
||||
for occurence in occurences:
|
||||
occurence.OverallWidth = props.overall_width / si_conversion
|
||||
occurence.OverallHeight = props.overall_height / si_conversion
|
||||
# occurrences attributes
|
||||
occurrences = tool.Ifc.get_all_element_occurrences(element)
|
||||
for occurrence in occurrences:
|
||||
occurrence.OverallWidth = props.overall_width / si_conversion
|
||||
occurrence.OverallHeight = props.overall_height / si_conversion
|
||||
|
||||
update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class BimTool(WorkSpaceTool):
|
||||
("bim.hotkey", {"type": "K", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_K")]}),
|
||||
("bim.hotkey", {"type": "M", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_M")]}),
|
||||
("bim.hotkey", {"type": "O", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_O")]}),
|
||||
("bim.hotkey", {"type": "L", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_L")]}),
|
||||
("bim.hotkey", {"type": "Q", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Q")]}),
|
||||
("bim.hotkey", {"type": "R", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_R")]}),
|
||||
("bim.hotkey", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_T")]}),
|
||||
@@ -77,9 +78,11 @@ class WallTool(BimTool):
|
||||
bl_description = "Create and edit walls"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.wall")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcWallType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcWallType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class SlabTool(BimTool):
|
||||
@@ -90,9 +93,11 @@ class SlabTool(BimTool):
|
||||
bl_description = "Create and edit slabs"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.slab")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcSlabType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcSlabType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class DoorTool(BimTool):
|
||||
@@ -103,9 +108,11 @@ class DoorTool(BimTool):
|
||||
bl_description = "Create and edit doors"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.door")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcDoorType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcDoorType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class WindowTool(BimTool):
|
||||
@@ -116,9 +123,11 @@ class WindowTool(BimTool):
|
||||
bl_description = "Create and edit windows"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.window")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcWindowType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcWindowType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class ColumnTool(BimTool):
|
||||
@@ -129,9 +138,11 @@ class ColumnTool(BimTool):
|
||||
bl_description = "Create and edit columns"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.column")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcColumnType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcColumnType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class BeamTool(BimTool):
|
||||
@@ -142,9 +153,11 @@ class BeamTool(BimTool):
|
||||
bl_description = "Create and edit beams"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.beam")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcBeamType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcBeamType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class DuctTool(BimTool):
|
||||
@@ -155,9 +168,11 @@ class DuctTool(BimTool):
|
||||
bl_description = "Create and edit ducks" # No, not a typo.
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.duct")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcDuctSegmentType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcDuctSegmentType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
class PipeTool(BimTool):
|
||||
@@ -168,9 +183,11 @@ class PipeTool(BimTool):
|
||||
bl_description = "Create and edit pipes"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.pipe")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcPipeSegmentType"
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type="IfcPipeSegmentType")
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
BimToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
|
||||
def add_layout_hotkey_operator(layout, text, hotkey, description):
|
||||
@@ -393,6 +410,13 @@ class BimToolUI:
|
||||
else:
|
||||
row.operator("bim.show_openings", icon="HIDE_OFF", text="")
|
||||
|
||||
if AuthoringData.data["active_class"] in ("IfcOpeningElement",):
|
||||
if len(context.selected_objects) == 2:
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_L")
|
||||
row.operator("bim.clone_opening", text="Clone Opening")
|
||||
|
||||
cls.layout.row(align=True).label(text="Align")
|
||||
add_layout_hotkey_operator(cls.layout, "Align Exterior", "S_X", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Align Centerline", "S_C", "")
|
||||
@@ -544,7 +568,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
if self.active_material_usage == "LAYER2":
|
||||
bpy.ops.bim.align_wall(align_type="CENTERLINE")
|
||||
if bpy.ops.bim.align_wall.poll():
|
||||
bpy.ops.bim.align_wall(align_type="CENTERLINE")
|
||||
else:
|
||||
bpy.ops.bim.align_product(align_type="CENTERLINE")
|
||||
|
||||
@@ -726,6 +751,11 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.props.y = self.y
|
||||
self.props.z = self.z
|
||||
|
||||
def hotkey_S_L(self):
|
||||
if AuthoringData.data["active_class"] in ("IfcOpeningElement",):
|
||||
if len(bpy.context.selected_objects) == 2:
|
||||
bpy.ops.bim.clone_opening()
|
||||
|
||||
def hotkey_A_D(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
@@ -744,3 +774,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.bim.edit_openings()
|
||||
else:
|
||||
bpy.ops.bim.show_openings()
|
||||
|
||||
|
||||
LIST_OF_TOOLS = [cls.bl_idname for cls in (BimTool.__subclasses__() + [BimTool])]
|
||||
TOOLS_TO_CLASSES_MAP = {cls.bl_idname: cls.ifc_element_type for cls in BimTool.__subclasses__()}
|
||||
|
||||
@@ -29,6 +29,7 @@ classes = (
|
||||
operator.EnableEditingArbitraryProfile,
|
||||
operator.EnableEditingProfile,
|
||||
operator.LoadProfiles,
|
||||
operator.PurgeUnusedProfiles,
|
||||
operator.RemoveProfileDef,
|
||||
prop.Profile,
|
||||
prop.BIMProfileProperties,
|
||||
|
||||
@@ -36,6 +36,7 @@ class ProfileData:
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"total_profiles": cls.total_profiles(),
|
||||
"active_profile_users": cls.active_profile_users(),
|
||||
"profile_classes": cls.profile_classes(),
|
||||
"is_arbitrary_profile": cls.is_arbitrary_profile(),
|
||||
"is_editing_arbitrary_profile": cls.is_editing_arbitrary_profile(),
|
||||
@@ -46,6 +47,15 @@ class ProfileData:
|
||||
def total_profiles(cls):
|
||||
return len([p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName])
|
||||
|
||||
@classmethod
|
||||
def active_profile_users(cls):
|
||||
profiles_props = bpy.context.scene.BIMProfileProperties
|
||||
if profiles_props.active_profile_index >= len(profiles_props.profiles):
|
||||
return 0
|
||||
profile_prop = profiles_props.profiles[profiles_props.active_profile_index]
|
||||
profile_ifc = tool.Ifc.get().by_id(profile_prop.ifc_definition_id)
|
||||
return tool.Ifc.get().get_total_inverses(profile_ifc)
|
||||
|
||||
@classmethod
|
||||
def profile_classes(cls):
|
||||
classes = ["IfcArbitraryClosedProfileDef"]
|
||||
@@ -72,4 +82,9 @@ class ProfileData:
|
||||
@classmethod
|
||||
def is_editing_arbitrary_profile(cls):
|
||||
obj = bpy.context.active_object
|
||||
return obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
return (
|
||||
obj
|
||||
and obj.data
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.model.profile as model_profile
|
||||
import blenderbim.core.profile as core
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_profile
|
||||
from blenderbim.bim.module.profile.data import refresh
|
||||
@@ -213,3 +214,16 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.active_arbitrary_profile_id = 0
|
||||
|
||||
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
|
||||
|
||||
|
||||
class PurgeUnusedProfiles(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.purge_unused_profiles"
|
||||
bl_label = "Purge Unused Profiles"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMProfileProperties
|
||||
core.purge_unused_profiles(tool.Ifc, tool.Profile)
|
||||
if props.is_editing:
|
||||
refresh()
|
||||
bpy.ops.bim.load_profiles()
|
||||
|
||||
@@ -49,10 +49,14 @@ class Profile(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
def update_active_profile_index(self, context):
|
||||
ProfileData.data["active_profile_users"] = ProfileData.active_profile_users()
|
||||
|
||||
|
||||
class BIMProfileProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
profiles: CollectionProperty(name="Profiles", type=Profile)
|
||||
active_profile_index: IntProperty(name="Active Profile Index")
|
||||
active_profile_index: IntProperty(name="Active Profile Index", update=update_active_profile_index)
|
||||
active_profile_id: IntProperty(name="Active Profile Id")
|
||||
active_arbitrary_profile_id: IntProperty(name="Active Arbitrary Profile Id")
|
||||
profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute)
|
||||
|
||||
@@ -79,17 +79,23 @@ class BIM_PT_profiles(Panel):
|
||||
self.props,
|
||||
"active_profile_index",
|
||||
)
|
||||
if active_profile and active_profile.ifc_class in (
|
||||
"IfcArbitraryClosedProfileDef",
|
||||
"IfcArbitraryProfileDefWithVoids",
|
||||
):
|
||||
if self.props.active_arbitrary_profile_id:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_arbitrary_profile", text="Save Arbitrary Profile", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_arbitrary_profile", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.enable_editing_arbitrary_profile", text="Edit Arbitrary Profile", icon="GREASEPENCIL")
|
||||
if active_profile:
|
||||
if active_profile.ifc_class in (
|
||||
"IfcArbitraryClosedProfileDef",
|
||||
"IfcArbitraryProfileDefWithVoids",
|
||||
):
|
||||
if self.props.active_arbitrary_profile_id:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_arbitrary_profile", text="Save Arbitrary Profile", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_arbitrary_profile", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator(
|
||||
"bim.enable_editing_arbitrary_profile", text="Edit Arbitrary Profile", icon="GREASEPENCIL"
|
||||
)
|
||||
|
||||
users_of_profile = ProfileData.data["active_profile_users"]
|
||||
self.layout.label(icon="INFO", text=f"Profile has {users_of_profile} inverse relationship(s) in project")
|
||||
|
||||
if self.props.active_profile_id:
|
||||
self.draw_editable_ui(context)
|
||||
|
||||
@@ -41,11 +41,13 @@ classes = (
|
||||
operator.SaveLibraryFile,
|
||||
operator.SelectLibraryFile,
|
||||
operator.ToggleFilterCategories,
|
||||
operator.ToggleLinkSelectability,
|
||||
operator.ToggleLinkVisibility,
|
||||
operator.UnassignLibraryDeclaration,
|
||||
operator.UnlinkIfc,
|
||||
operator.UnloadLink,
|
||||
operator.UnloadProject,
|
||||
operator.ReloadLink,
|
||||
prop.LibraryElement,
|
||||
prop.FilterCategory,
|
||||
prop.Link,
|
||||
@@ -55,6 +57,7 @@ classes = (
|
||||
ui.BIM_PT_project,
|
||||
ui.BIM_PT_project_library,
|
||||
ui.BIM_PT_links,
|
||||
ui.BIM_PT_purge,
|
||||
ui.BIM_UL_library,
|
||||
ui.BIM_UL_filter_categories,
|
||||
ui.BIM_UL_links,
|
||||
|
||||
@@ -850,7 +850,7 @@ class UnloadLink(bpy.types.Operator):
|
||||
for scene in bpy.data.scenes:
|
||||
if scene.library and scene.library.filepath == filepath:
|
||||
bpy.data.scenes.remove(scene)
|
||||
link = context.scene.BIMProjectProperties.links.get(filepath)
|
||||
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
||||
link.is_loaded = False
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -866,7 +866,7 @@ class LoadLink(bpy.types.Operator):
|
||||
filepath = self.filepath
|
||||
if not os.path.isabs(filepath):
|
||||
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
|
||||
with bpy.data.libraries.load(self.filepath, link=True) as (data_from, data_to):
|
||||
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
|
||||
data_to.scenes = data_from.scenes
|
||||
for scene in bpy.data.scenes:
|
||||
if not scene.library or scene.library.filepath != filepath:
|
||||
@@ -875,11 +875,55 @@ class LoadLink(bpy.types.Operator):
|
||||
if "IfcProject" not in child.name:
|
||||
continue
|
||||
bpy.data.scenes[0].collection.children.link(child)
|
||||
link = context.scene.BIMProjectProperties.links.get(filepath)
|
||||
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
||||
link.is_loaded = True
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ReloadLink(bpy.types.Operator):
|
||||
bl_idname = "bim.reload_link"
|
||||
bl_label = "Reload Link"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Reload the selected file"
|
||||
filepath: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
def get_linked_ifcs():
|
||||
selected_filename = os.path.basename(self.filepath)
|
||||
return [
|
||||
c.library
|
||||
for c in bpy.data.collections
|
||||
if "IfcProject" in c.name and c.library and os.path.basename(c.library.filepath) == selected_filename
|
||||
]
|
||||
for library in get_linked_ifcs() or []:
|
||||
library.reload()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleLinkSelectability(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_link_selectability"
|
||||
bl_label = "Toggle Link Selectability"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Toggle selectability"
|
||||
link: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
self.filepath = self.link
|
||||
if not os.path.isabs(self.filepath):
|
||||
self.filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.filepath))
|
||||
for collection in self.get_linked_collections():
|
||||
collection.hide_select = not collection.hide_select
|
||||
link.is_selectable = not collection.hide_select
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_linked_collections(self):
|
||||
return [
|
||||
c for c in bpy.data.collections if "IfcProject" in c.name and c.library and c.library.filepath == self.filepath
|
||||
]
|
||||
|
||||
|
||||
class ToggleLinkVisibility(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_link_visibility"
|
||||
bl_label = "Toggle Link Visibility"
|
||||
@@ -891,6 +935,9 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
self.filepath = self.link
|
||||
if not os.path.isabs(self.filepath):
|
||||
self.filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.filepath))
|
||||
if self.mode == "WIREFRAME":
|
||||
self.toggle_wireframe(link)
|
||||
elif self.mode == "VISIBLE":
|
||||
@@ -927,7 +974,7 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
|
||||
def get_linked_collections(self):
|
||||
return [
|
||||
c for c in bpy.data.collections if "IfcProject" in c.name and c.library and c.library.filepath == self.link
|
||||
c for c in bpy.data.collections if "IfcProject" in c.name and c.library and c.library.filepath == self.filepath
|
||||
]
|
||||
|
||||
|
||||
@@ -1016,6 +1063,7 @@ class ExportIFC(bpy.types.Operator):
|
||||
settings.json_compact = self.json_compact
|
||||
|
||||
ifc_exporter = export_ifc.IfcExporter(settings)
|
||||
print("Starting export")
|
||||
settings.logger.info("Starting export")
|
||||
ifc_exporter.export()
|
||||
settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
|
||||
|
||||
@@ -93,6 +93,7 @@ class FilterCategory(PropertyGroup):
|
||||
class Link(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||
is_selectable: BoolProperty(name="Is Selectable", default=True)
|
||||
is_wireframe: BoolProperty(name="Is Wireframe", default=False)
|
||||
is_hidden: BoolProperty(name="Is Hidden", default=False)
|
||||
|
||||
|
||||
@@ -350,6 +350,13 @@ class BIM_UL_links(UIList):
|
||||
row = layout.row(align=True)
|
||||
if item.is_loaded:
|
||||
row.label(text=item.name)
|
||||
op = row.operator(
|
||||
"bim.toggle_link_selectability",
|
||||
text="",
|
||||
icon="RESTRICT_SELECT_OFF" if item.is_selectable else "RESTRICT_SELECT_ON",
|
||||
emboss=False,
|
||||
)
|
||||
op.link = item.name
|
||||
op = row.operator(
|
||||
"bim.toggle_link_visibility",
|
||||
text="",
|
||||
@@ -368,9 +375,26 @@ class BIM_UL_links(UIList):
|
||||
op.mode = "VISIBLE"
|
||||
op = row.operator("bim.unload_link", text="", icon="UNLINKED")
|
||||
op.filepath = item.name
|
||||
op = row.operator("bim.reload_link", text="", icon="FILE_REFRESH")
|
||||
op.filepath = item.name
|
||||
else:
|
||||
row.prop(item, "name", text="")
|
||||
op = row.operator("bim.load_link", text="", icon="LINKED")
|
||||
op.filepath = item.name
|
||||
op = row.operator("bim.unlink_ifc", text="", icon="X")
|
||||
op.filepath = item.name
|
||||
|
||||
|
||||
class BIM_PT_purge(Panel):
|
||||
bl_label = "Purge Data"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_quality_control"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("bim.purge_unused_profiles")
|
||||
layout.operator("bim.purge_unused_types")
|
||||
layout.operator("bim.purge_unused_representations")
|
||||
|
||||
@@ -398,6 +398,7 @@ class CalculateQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.calculate_quantity"
|
||||
bl_label = "Calculate Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Calculates the quantity with a defined formula for this exact entity and quantity"
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
@@ -424,6 +425,10 @@ class GuessQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.guess_quantity"
|
||||
bl_label = "Guess Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Calculate the quantity by guessing the formula from the quantity name. "
|
||||
"Less reliable than Calculate Quantity"
|
||||
)
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
@@ -450,7 +455,13 @@ class CopyPropertyToSelection(bpy.types.Operator, Operator):
|
||||
else:
|
||||
is_pset = context.active_object.PsetProperties.active_pset_type == "PSET"
|
||||
pset_name = context.active_object.PsetProperties.active_pset_name
|
||||
prop_value = context.active_object.PsetProperties.properties.get(self.name).metadata.get_value()
|
||||
prop = context.active_object.PsetProperties.properties.get(self.name)
|
||||
if prop.value_type == "IfcPropertySingleValue":
|
||||
prop_value = prop.metadata.get_value()
|
||||
elif prop.value_type == "IfcPropertyEnumeratedValue":
|
||||
value_name = prop.metadata.get_value_name()
|
||||
prop_value = [e[value_name] for e in prop.enumerated_value.enumerated_values if e.is_selected]
|
||||
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
core.copy_property_to_selection(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -71,6 +71,9 @@ def draw_enumerated_property(prop, layout, copy_operator=None):
|
||||
grid = layout.column_flow(columns=3)
|
||||
for e in prop.enumerated_value.enumerated_values:
|
||||
grid.prop(e, "is_selected", text=str(e[value_name]))
|
||||
if copy_operator:
|
||||
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
||||
op.name = prop.metadata.name
|
||||
|
||||
|
||||
def get_active_pset_obj_name(context, obj_type):
|
||||
@@ -370,6 +373,7 @@ class BIM_PT_task_qtos(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_work_schedules"
|
||||
bl_order = 2
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -510,6 +514,7 @@ class BIM_PT_work_schedule_psets(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_work_schedules"
|
||||
bl_order = 3
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import pathlib
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -51,17 +52,23 @@ class PsetTemplatesData:
|
||||
|
||||
@classmethod
|
||||
def pset_template_files(cls):
|
||||
files = os.listdir(os.path.join(bpy.context.scene.BIMProperties.data_dir, "pset"))
|
||||
return [(f.replace(".ifc", ""), f.replace(".ifc", ""), "") for f in files]
|
||||
results = []
|
||||
pset_dir = os.path.join(bpy.context.scene.BIMProperties.data_dir, "pset")
|
||||
files = os.listdir(pset_dir)
|
||||
for f in files:
|
||||
results.append((os.path.join(pset_dir, f), f.strip(".ifc"), "Global Pset Template"))
|
||||
|
||||
pset_dir = tool.Ifc.resolve_uri(bpy.context.scene.BIMProperties.pset_dir)
|
||||
if os.path.isdir(pset_dir):
|
||||
for path in pathlib.Path(pset_dir).glob("*.ifc"):
|
||||
results.append((str(path), os.path.basename(str(path)).strip(".ifc"), "Project Pset Template"))
|
||||
|
||||
return sorted(results, key=lambda x: x[1])
|
||||
|
||||
@classmethod
|
||||
def pset_templates(cls):
|
||||
if not IfcStore.pset_template_file:
|
||||
IfcStore.pset_template_path = os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir,
|
||||
"pset",
|
||||
bpy.context.scene.BIMPsetTemplateProperties.pset_template_files + ".ifc",
|
||||
)
|
||||
IfcStore.pset_template_path = bpy.context.scene.BIMPsetTemplateProperties.pset_template_files
|
||||
IfcStore.pset_template_file = ifcopenshell.open(IfcStore.pset_template_path)
|
||||
return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")]
|
||||
|
||||
|
||||
@@ -20,36 +20,41 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.DisableResourceEditingUI,
|
||||
operator.DisableEditingResource,
|
||||
operator.EnableEditingResource,
|
||||
operator.LoadResources,
|
||||
operator.AddResource,
|
||||
operator.AddResourceQuantity,
|
||||
operator.EditResource,
|
||||
operator.RemoveResource,
|
||||
operator.RemoveResourceQuantity,
|
||||
operator.LoadResourceProperties,
|
||||
operator.ExpandResource,
|
||||
operator.ContractResource,
|
||||
operator.AddProductivityData,
|
||||
operator.AssignResource,
|
||||
operator.UnassignResource,
|
||||
operator.EnableEditingResourceTime,
|
||||
operator.EnableEditingResourceQuantity,
|
||||
operator.EnableEditingResourceBaseQuantity,
|
||||
operator.EnableEditingResourceCosts,
|
||||
operator.EnableEditingResourceCostValueFormula,
|
||||
operator.EnableEditingResourceCostValue,
|
||||
operator.EditResourceTime,
|
||||
operator.EditResourceQuantity,
|
||||
operator.CalculateResourceWork,
|
||||
operator.ConstrainResourceWork,
|
||||
operator.ContractResource,
|
||||
operator.DisableEditingResource,
|
||||
operator.DisableEditingResourceCostValue,
|
||||
operator.DisableEditingResourceQuantity,
|
||||
operator.DisableEditingResourceTime,
|
||||
operator.DisableResourceEditingUI,
|
||||
operator.EditProductivityData,
|
||||
operator.EditResource,
|
||||
operator.EditResourceCostValue,
|
||||
operator.EditResourceCostValueFormula,
|
||||
operator.DisableEditingResourceTime,
|
||||
operator.DisableEditingResourceQuantity,
|
||||
operator.DisableEditingResourceCostValue,
|
||||
operator.CalculateResourceWork,
|
||||
operator.EditResourceQuantity,
|
||||
operator.EditResourceTime,
|
||||
operator.EnableEditingResource,
|
||||
operator.EnableEditingResourceBaseQuantity,
|
||||
operator.EnableEditingResourceCosts,
|
||||
operator.EnableEditingResourceCostValue,
|
||||
operator.CalculateResourceUsage,
|
||||
operator.EnableEditingResourceCostValueFormula,
|
||||
operator.EnableEditingResourceQuantity,
|
||||
operator.EnableEditingResourceTime,
|
||||
operator.ExpandResource,
|
||||
operator.GoToResource,
|
||||
operator.ImportResources,
|
||||
operator.EditProductivityData,
|
||||
operator.LoadResourceProperties,
|
||||
operator.LoadResources,
|
||||
operator.RemoveResource,
|
||||
operator.RemoveResourceQuantity,
|
||||
operator.RemoveUsageConstraint,
|
||||
operator.UnassignResource,
|
||||
prop.Resource,
|
||||
prop.BIMResourceProperties,
|
||||
prop.BIMResourceTreeProperties,
|
||||
|
||||
@@ -56,21 +56,75 @@ class ResourceData:
|
||||
"type": resource.is_a(),
|
||||
"BaseQuantity": base_quantity,
|
||||
}
|
||||
results[resource.id()]["Benchmarks"] = cls.get_resource_benchmarks(resource)
|
||||
if resource.is_a() in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
|
||||
results[resource.id()]["Productivity"] = {}
|
||||
results[resource.id()]["InheritedProductivity"] = {}
|
||||
productivity = cls.get_productivity(resource)
|
||||
if productivity:
|
||||
results[resource.id()]["Productivity"] = {
|
||||
"id": productivity.get("id"),
|
||||
"QuantityProduced": ifcopenshell.util.resource.get_quantity_produced(productivity),
|
||||
"TimeConsumed": ifcopenshell.util.resource.get_unit_consumed(productivity),
|
||||
"QuantityProducedName": ifcopenshell.util.resource.get_quantity_produced_name(productivity),
|
||||
}
|
||||
inherited_productivity = cls.get_parent_productivity(resource)
|
||||
if inherited_productivity:
|
||||
results[resource.id()]["InheritedProductivity"] = {
|
||||
"QuantityProduced": ifcopenshell.util.resource.get_quantity_produced(inherited_productivity),
|
||||
"TimeConsumed": ifcopenshell.util.resource.get_unit_consumed(inherited_productivity),
|
||||
"QuantityProducedName": ifcopenshell.util.resource.get_quantity_produced_name(
|
||||
inherited_productivity
|
||||
),
|
||||
}
|
||||
if resource.Usage:
|
||||
results[resource.id()]["ScheduleWork"] = (
|
||||
ifcopenshell.util.date.readable_ifc_duration(resource.Usage.ScheduleWork)
|
||||
if resource.Usage.ScheduleWork
|
||||
else None
|
||||
)
|
||||
results[resource.id()]["ScheduleUsage"] = (
|
||||
resource.Usage.ScheduleUsage if resource.Usage.ScheduleUsage else None
|
||||
)
|
||||
if resource.IsNestedBy:
|
||||
results[resource.id()]["DerivedScheduleWork"] = cls.sum_person_hours(resource)
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def sum_person_hours(cls, resource):
|
||||
sum = 0
|
||||
nested_resources = ifcopenshell.util.resource.get_nested_resources(resource)
|
||||
for nested_resource in nested_resources or []:
|
||||
if not nested_resource.Usage or not nested_resource.Usage.ScheduleWork:
|
||||
continue
|
||||
duration = ifcopenshell.util.date.ifc2datetime(nested_resource.Usage.ScheduleWork)
|
||||
sum += duration.total_seconds() / 3600
|
||||
return round(float(sum), 2) if sum else 0
|
||||
|
||||
@classmethod
|
||||
def get_resource_benchmarks(cls, resource):
|
||||
constraints = []
|
||||
for constraint in ifcopenshell.util.constraint.get_constraints(resource) or []:
|
||||
metrics = []
|
||||
for metric in ifcopenshell.util.constraint.get_metrics(constraint) or []:
|
||||
metrics.append(
|
||||
{
|
||||
"reference": ifcopenshell.util.constraint.get_metric_reference(metric),
|
||||
"Benchmark": metric.Benchmark,
|
||||
"ConstraintGrade": metric.ConstraintGrade,
|
||||
}
|
||||
)
|
||||
constraints.append({"ObjectiveQualifier": constraint.ObjectiveQualifier, "metrics": metrics})
|
||||
return constraints
|
||||
|
||||
@classmethod
|
||||
def get_productivity(cls, resource):
|
||||
return ifcopenshell.util.resource.get_productivity(resource, should_inherit=False)
|
||||
|
||||
@classmethod
|
||||
def get_parent_productivity(cls, resource):
|
||||
return ifcopenshell.util.resource.get_parent_productivity(resource)
|
||||
|
||||
@classmethod
|
||||
def cost_values(cls):
|
||||
results = []
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from blenderbim.bim.module.resource.ui import draw_productivity_ui
|
||||
import blenderbim.core.resource as core
|
||||
import blenderbim.tool as tool
|
||||
|
||||
@@ -198,6 +199,13 @@ class CalculateResourceWork(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_resource = tool.Resource.get_highlighted_resource()
|
||||
if active_resource:
|
||||
if tool.Resource.get_productivity(active_resource, should_inherit=True):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
core.calculate_resource_work(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource))
|
||||
|
||||
@@ -334,7 +342,7 @@ class EditResourceQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_idname = "import_resources.bim"
|
||||
bl_label = "Import P6"
|
||||
bl_label = "Import Resources"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".csv"
|
||||
filter_glob: bpy.props.StringProperty(default="*.csv", options={"HIDDEN"})
|
||||
@@ -348,6 +356,16 @@ class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
core.import_resources(tool.Resource, file_path=self.filepath)
|
||||
|
||||
|
||||
class AddProductivityData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_productivity_data"
|
||||
bl_description = "Apply"
|
||||
bl_label = "Add Productivity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
tool.Ifc.run("pset.add_pset", product=tool.Resource.get_highlighted_resource(), name="EPset_Productivity")
|
||||
|
||||
|
||||
class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_productivity_data"
|
||||
bl_description = "Apply"
|
||||
@@ -356,3 +374,67 @@ class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_productivity_pset(tool.Ifc, tool.Resource)
|
||||
|
||||
def draw(self, context):
|
||||
draw_productivity_ui(self, context)
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self, width=600)
|
||||
|
||||
|
||||
class ConstrainResourceWork(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_usage_constraint"
|
||||
bl_label = "Constrain Resource Work"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
attribute: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_usage_constraint(
|
||||
tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource), reference_path=self.attribute
|
||||
)
|
||||
|
||||
|
||||
class RemoveUsageConstraint(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_usage_constraint"
|
||||
bl_label = "Remove Usage Constraint"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
attribute: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_usage_constraint(
|
||||
tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource), reference_path=self.attribute
|
||||
)
|
||||
|
||||
|
||||
class GoToResource(bpy.types.Operator):
|
||||
bl_idname = "bim.go_to_resource"
|
||||
bl_label = "Go To Resource"
|
||||
bl_description = "Selects the resource in the Resource Panel"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
resource: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.go_to_resource(tool.Resource, resource=tool.Ifc.get().by_id(self.resource))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CalculateResourceUsage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_resource_usage"
|
||||
bl_label = "Calculate Resource Usage"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_resource = tool.Resource.get_highlighted_resource()
|
||||
if active_resource:
|
||||
if active_resource.Usage and active_resource.Usage.ScheduleWork:
|
||||
task = tool.Resource.get_task_assignments(active_resource)
|
||||
if task and tool.Sequence.has_duration(task):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _execute(self, context):
|
||||
core.calculate_resource_usage(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(tool.Resource.get_highlighted_resource()))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2021, 2022, 2023 Dion Moult, Yassine Oualid <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
@@ -22,6 +22,8 @@ import ifcopenshell.util.resource
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.pset.data
|
||||
import blenderbim.bim.module.resource.data
|
||||
import blenderbim.bim.module.sequence.data
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -56,6 +58,8 @@ def updateResourceName(self, context):
|
||||
if props.active_resource_id == self.ifc_definition_id:
|
||||
attribute = props.resource_attributes.get("Name")
|
||||
attribute.string_value = self.name
|
||||
blenderbim.bim.module.resource.data.refresh()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
|
||||
|
||||
def get_quantity_types(self, context):
|
||||
@@ -72,7 +76,7 @@ def get_quantity_types(self, context):
|
||||
|
||||
def update_active_resource_index(self, context):
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
if self.should_show_productivity:
|
||||
if self.should_show_resource_tools:
|
||||
tool.Resource.load_productivity_data()
|
||||
|
||||
|
||||
@@ -80,18 +84,20 @@ def updateResourceUsage(self, context):
|
||||
props = context.scene.BIMResourceProperties
|
||||
if not props.is_resource_update_enabled:
|
||||
return
|
||||
|
||||
if self.schedule_usage == "":
|
||||
if not self.schedule_usage:
|
||||
return
|
||||
resource = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if not resource.Usage:
|
||||
tool.Ifc.run(
|
||||
"resource.add_resource_time",
|
||||
resource=resource,
|
||||
)
|
||||
resource.Usage.ScheduleUsage = self.schedule_usage
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
if resource.Usage and resource.Usage.ScheduleUsage == self.schedule_usage:
|
||||
return
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={
|
||||
"ScheduleUsage": self.schedule_usage
|
||||
})
|
||||
tool.Sequence.load_task_properties()
|
||||
tool.Resource.load_resource_properties()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
blenderbim.bim.module.resource.data.refresh()
|
||||
blenderbim.bim.module.sequence.data.refresh()
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
|
||||
|
||||
class ISODuration(PropertyGroup):
|
||||
@@ -144,7 +150,7 @@ class BIMResourceProperties(PropertyGroup):
|
||||
quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types")
|
||||
is_editing_quantity: BoolProperty(name="Is Editing Quantity")
|
||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||
should_show_productivity: BoolProperty(name="Edit Productivity", update=update_active_resource_index)
|
||||
should_show_resource_tools: BoolProperty(name="Edit Productivity", update=update_active_resource_index)
|
||||
|
||||
|
||||
class BIMResourceProductivity(PropertyGroup):
|
||||
|
||||
@@ -20,20 +20,21 @@ import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.resource.data import ResourceData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_resources(Panel):
|
||||
bl_label = "Resources"
|
||||
bl_idname = "BIM_PT_resources"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_resources"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMResourceProperties
|
||||
@@ -64,58 +65,124 @@ class BIM_PT_resources(Panel):
|
||||
self.props,
|
||||
"active_resource_index",
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "should_show_productivity", icon="RECOVER_LAST")
|
||||
if self.props.should_show_productivity:
|
||||
self.draw_productivity_ui(context)
|
||||
if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES":
|
||||
self.draw_editable_resource_attributes_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY":
|
||||
self.draw_editable_resource_quantity_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS":
|
||||
self.draw_editable_resource_costs_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
|
||||
self.draw_editable_resource_time_attributes_ui()
|
||||
|
||||
if self.props.active_resource_id:
|
||||
if self.props.editing_resource_type == "ATTRIBUTES":
|
||||
self.draw_editable_resource_attributes_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY":
|
||||
self.draw_editable_resource_quantity_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS":
|
||||
self.draw_editable_resource_costs_ui()
|
||||
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
|
||||
self.draw_editable_resource_time_attributes_ui()
|
||||
self.draw_productivity_ui(context)
|
||||
|
||||
def draw_productivity_ui(self, context):
|
||||
total_resources = len(self.tprops.resources)
|
||||
if not total_resources or self.props.active_resource_index >= total_resources:
|
||||
return
|
||||
|
||||
ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id
|
||||
resource = ResourceData.data["resources"][ifc_definition_id]
|
||||
|
||||
if not resource["type"] in ["IfcConstructionEquipmentResource", "IfcLaborResource"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Resource type cannot have productivity data", icon="ERROR")
|
||||
return
|
||||
|
||||
self.productivity_props = context.scene.BIMResourceProductivity
|
||||
|
||||
if resource["Productivity"]:
|
||||
produtivitiy_rate_message = "Current Rate: {}/{}".format(
|
||||
resource["Productivity"]["QuantityProduced"], resource["Productivity"]["TimeConsumed"]
|
||||
)
|
||||
row = self.layout.row()
|
||||
row.alignment = "LEFT"
|
||||
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "LEFT"
|
||||
produtivitiy_rate_message = "No productivity data found"
|
||||
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.productivity_props, "quantity_produced", text="Quantity Produced")
|
||||
row.prop(self.productivity_props, "quantity_produced_name", text="Quantity Name")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
self.draw_duration_property(self.productivity_props.quantity_consumed, row)
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.edit_productivity_data", text="Apply", icon="CHECKMARK")
|
||||
row.prop(self.props, "should_show_resource_tools", text="Resource Tools", icon="RECOVER_LAST")
|
||||
if self.props.should_show_resource_tools:
|
||||
total_resources = len(self.tprops.resources)
|
||||
if not total_resources or self.props.active_resource_index >= total_resources:
|
||||
return
|
||||
|
||||
ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id
|
||||
resource = ResourceData.data["resources"][ifc_definition_id]
|
||||
|
||||
if not resource["type"] in ["IfcConstructionEquipmentResource", "IfcLaborResource"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Resource type cannot have productivity data", icon="ERROR")
|
||||
else:
|
||||
is_usage_locked = False
|
||||
is_work_locked = False
|
||||
for constraint in resource["Benchmarks"] or []:
|
||||
for metric in constraint["metrics"] or []:
|
||||
if (
|
||||
metric["ConstraintGrade"] == "HARD"
|
||||
and metric["reference"]
|
||||
and metric["reference"] == "Usage.ScheduleUsage"
|
||||
):
|
||||
is_usage_locked = True
|
||||
elif (
|
||||
metric["ConstraintGrade"] == "HARD"
|
||||
and metric["reference"]
|
||||
and metric["reference"] == "Usage.ScheduleWork"
|
||||
):
|
||||
is_work_locked = True
|
||||
grid = self.layout.grid_flow(columns=3, even_columns=False, even_rows=False, align=False)
|
||||
|
||||
col1 = grid.column(align=True)
|
||||
col2 = grid.column(align=False)
|
||||
col3 = grid.column(align=True)
|
||||
col1.ui_units_x = 1
|
||||
col2.ui_units_x = 1
|
||||
col3.ui_units_x = 2
|
||||
|
||||
row1_col1 = col1.row()
|
||||
row1_col1.label(text="Schedule Work")
|
||||
row1col2 = col2.row()
|
||||
schedule_work = resource.get("ScheduleWork", None)
|
||||
derived_schedule_work = resource.get("DerivedScheduleWork", None)
|
||||
row1col2.label(
|
||||
text="{}".format(schedule_work) if schedule_work else "{} h*".format(derived_schedule_work),
|
||||
icon="TIME",
|
||||
)
|
||||
|
||||
row1col3 = col3.row()
|
||||
row1col3.operator("bim.calculate_resource_work", text="", icon="TEMP").resource = ifc_definition_id
|
||||
op = row1col3.operator(
|
||||
"bim.add_usage_constraint" if not is_work_locked else "bim.remove_usage_constraint",
|
||||
text="",
|
||||
icon="LOCKED" if is_work_locked else "UNLOCKED",
|
||||
)
|
||||
op.resource = ifc_definition_id
|
||||
op.attribute = "Usage.ScheduleWork"
|
||||
row2_col1 = col1.row()
|
||||
row2_col1.label(text="Schedule Usage")
|
||||
row2col2 = col2.row()
|
||||
row2col2.prop(self.tprops.resources[self.props.active_resource_index], "schedule_usage", text="")
|
||||
row2col3 = col3.row()
|
||||
row2col3.operator("bim.calculate_resource_usage", text="", icon="TEMP")
|
||||
op = row2col3.operator(
|
||||
"bim.add_usage_constraint" if not is_usage_locked else "bim.remove_usage_constraint",
|
||||
text="",
|
||||
icon="LOCKED" if is_usage_locked else "UNLOCKED",
|
||||
)
|
||||
op.resource = ifc_definition_id
|
||||
op.attribute = "Usage.ScheduleUsage"
|
||||
|
||||
productivity = resource["Productivity"]
|
||||
parent_productivity = resource["InheritedProductivity"]
|
||||
row = self.layout.row()
|
||||
if productivity:
|
||||
produtivitiy_rate_message = "Current Productivity Rate: {} {} / {}".format(
|
||||
productivity["QuantityProduced"],
|
||||
productivity["QuantityProducedName"],
|
||||
productivity["TimeConsumed"],
|
||||
)
|
||||
row.alignment = "LEFT"
|
||||
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
|
||||
row.operator("bim.edit_productivity_data", text="", icon="GREASEPENCIL")
|
||||
op = row.operator("bim.remove_pset", text="", icon="X")
|
||||
op.pset_id = productivity["id"]
|
||||
op.obj_type = "Resource"
|
||||
op.obj = ""
|
||||
|
||||
elif parent_productivity:
|
||||
produtivitiy_rate_message = "Inherited Productivity Rate: {} {} / {}*".format(
|
||||
parent_productivity["QuantityProduced"],
|
||||
parent_productivity["QuantityProducedName"],
|
||||
parent_productivity["TimeConsumed"],
|
||||
)
|
||||
row.alignment = "LEFT"
|
||||
row.label(text="{}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
|
||||
row.operator("bim.add_productivity_data", text="", icon="ADD")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "LEFT"
|
||||
produtivitiy_rate_message = "No productivity data found"
|
||||
row.label(text="{}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
|
||||
row.operator("bim.add_productivity_data", text="", icon="ADD")
|
||||
|
||||
def draw_resource_operators(self):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -153,9 +220,6 @@ class BIM_PT_resources(Panel):
|
||||
|
||||
if not self.props.active_resource_id:
|
||||
if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
|
||||
if resource["Productivity"]:
|
||||
op = row.operator("bim.calculate_resource_work", text="", icon="TEMP")
|
||||
op.resource = ifc_definition_id
|
||||
row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES")
|
||||
op.resource = ifc_definition_id
|
||||
@@ -163,6 +227,12 @@ class BIM_PT_resources(Panel):
|
||||
op.resource = ifc_definition_id
|
||||
row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id
|
||||
row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id
|
||||
else:
|
||||
if self.props.editing_resource_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_resource", text="", icon="CHECKMARK")
|
||||
elif self.props.editing_resource_type == "USAGE":
|
||||
row.operator("bim.edit_resource_time", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_resource", text="", icon="CANCEL")
|
||||
|
||||
def draw_editable_resource_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.resource_attributes, self.layout)
|
||||
@@ -250,7 +320,6 @@ class BIM_PT_resources(Panel):
|
||||
def draw_duration_property(self, duration_props, layout):
|
||||
for duration_prop in duration_props:
|
||||
if duration_prop.name == "BaseQuantityConsumed":
|
||||
layout.label(text=duration_prop.name)
|
||||
layout.prop(duration_prop, "years", text="Y")
|
||||
layout.prop(duration_prop, "months", text="M")
|
||||
layout.prop(duration_prop, "days", text="D")
|
||||
@@ -287,7 +356,7 @@ class BIM_UL_resources(UIList):
|
||||
else:
|
||||
row.label(text="", icon="DOT")
|
||||
row.prop(item, "name", emboss=False, text="", icon=icon_map[resource["type"]])
|
||||
row.prop(item, "schedule_usage", text="", emboss=False)
|
||||
row.prop(item, "schedule_usage", text="", emboss=False) if item.schedule_usage else None
|
||||
if context.active_object and not props.active_resource_id:
|
||||
row = layout.row(align=True)
|
||||
if item.ifc_definition_id in ResourceData.data["active_resource_ids"]:
|
||||
@@ -303,3 +372,29 @@ class BIM_UL_resources(UIList):
|
||||
elif props.editing_resource_type == "USAGE":
|
||||
row.operator("bim.edit_resource_time", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_resource", text="", icon="CANCEL")
|
||||
|
||||
|
||||
def draw_productivity_ui(self, context):
|
||||
def draw_duration_property(duration_props, layout):
|
||||
for duration_prop in duration_props:
|
||||
if duration_prop.name == "BaseQuantityConsumed":
|
||||
layout.prop(duration_prop, "years", text="Y")
|
||||
layout.prop(duration_prop, "months", text="M")
|
||||
layout.prop(duration_prop, "days", text="D")
|
||||
layout.prop(duration_prop, "hours", text="H")
|
||||
layout.prop(duration_prop, "minutes", text="Min")
|
||||
layout.prop(duration_prop, "seconds", text="S")
|
||||
|
||||
productivity_props = context.scene.BIMResourceProductivity
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=False, even_rows=False, align=False)
|
||||
col1 = grid.column(align=False)
|
||||
col2 = grid.column(align=False)
|
||||
row1_col1 = col1.row()
|
||||
row1_col1.label(text="Quantity")
|
||||
row1_col2 = col2.row()
|
||||
row1_col2.prop(productivity_props, "quantity_produced", text="")
|
||||
row1_col2.prop(productivity_props, "quantity_produced_name", text="")
|
||||
row2_col1 = col1.row()
|
||||
row2_col1.label(text="Time")
|
||||
row2_col2 = col2.row()
|
||||
draw_duration_property(productivity_props.quantity_consumed, row2_col2)
|
||||
|
||||
@@ -49,8 +49,8 @@ class IfcClassData:
|
||||
@classmethod
|
||||
def ifc_products(cls):
|
||||
products = [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcElement",
|
||||
"IfcSpatialElement",
|
||||
"IfcSpatialElementType",
|
||||
"IfcGroup",
|
||||
@@ -62,8 +62,8 @@ class IfcClassData:
|
||||
version = tool.Ifc.get_schema()
|
||||
if version == "IFC2X3":
|
||||
products = [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcElement",
|
||||
"IfcSpatialStructureElement",
|
||||
"IfcGroup",
|
||||
"IfcStructuralItem",
|
||||
|
||||
@@ -26,6 +26,7 @@ classes = (
|
||||
operator.AddFilterGroup,
|
||||
operator.AddToIfcGroup,
|
||||
operator.ColourByProperty,
|
||||
operator.EditFilterQuery,
|
||||
operator.FilterModelElements,
|
||||
operator.IfcSelector,
|
||||
operator.LoadColourscheme,
|
||||
@@ -39,6 +40,7 @@ classes = (
|
||||
operator.SaveSearch,
|
||||
operator.SaveSelectorQuery,
|
||||
operator.Search,
|
||||
operator.SelectFilterElements,
|
||||
operator.SelectGlobalId,
|
||||
operator.SelectIfcClass,
|
||||
operator.SelectSimilar,
|
||||
|
||||
@@ -40,12 +40,14 @@ class SearchData:
|
||||
|
||||
@classmethod
|
||||
def saved_searches(cls):
|
||||
if not tool.Ifc.get():
|
||||
return []
|
||||
groups = tool.Ifc.get().by_type("IfcGroup")
|
||||
results = []
|
||||
for group in groups:
|
||||
try:
|
||||
data = json.loads(group.Description)
|
||||
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search":
|
||||
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search" and data.get("query", None):
|
||||
results.append(group)
|
||||
except:
|
||||
pass
|
||||
@@ -102,7 +104,7 @@ class SelectSimilarData:
|
||||
psets = ifcopenshell.util.element.get_psets(element, psets_only=True)
|
||||
for pset, properties in psets.items():
|
||||
if pset.endswith("Common"):
|
||||
keys.extend([f'r".*Common"."{name}"' for name in properties.keys() if name != "id"])
|
||||
keys.extend([f'/.*Common/."{name}"' for name in properties.keys() if name != "id"])
|
||||
else:
|
||||
keys.extend([f"{pset}.{name}" for name in properties.keys() if name != "id"])
|
||||
return [(k, k, "") for k in keys]
|
||||
|
||||
@@ -61,9 +61,14 @@ colour_list = [
|
||||
class AddFilterGroup(Operator):
|
||||
bl_idname = "bim.add_filter_group"
|
||||
bl_label = "Add Filter Group"
|
||||
module: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMSearchProperties.filter_groups.add()
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
props.filter_groups.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -71,9 +76,13 @@ class RemoveFilterGroup(Operator):
|
||||
bl_idname = "bim.remove_filter_group"
|
||||
bl_label = "Remove Filter Group"
|
||||
index: IntProperty()
|
||||
module: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
props.filter_groups.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -83,9 +92,13 @@ class RemoveFilter(Operator):
|
||||
bl_label = "Remove Filter Group"
|
||||
group_index: IntProperty()
|
||||
index: IntProperty()
|
||||
module: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
props.filter_groups[self.group_index].filters.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -95,13 +108,87 @@ class AddFilter(Operator):
|
||||
bl_label = "Add Filter"
|
||||
index: IntProperty()
|
||||
type: StringProperty()
|
||||
module: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
new = context.scene.BIMSearchProperties.filter_groups[self.index].filters.add()
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
new = props.filter_groups[self.index].filters.add()
|
||||
new.type = self.type
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectFilterElements(bpy.types.Operator):
|
||||
bl_idname = "bim.select_filter_elements"
|
||||
bl_label = "Select Filter Elements"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
group_index: IntProperty()
|
||||
index: IntProperty()
|
||||
module: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
global_ids = []
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if global_id:
|
||||
global_ids.append(global_id)
|
||||
if len(global_ids) > 50:
|
||||
# Too much to store in a string property
|
||||
name = "globalid-filter-" + ifcopenshell.guid.new()
|
||||
text_data = bpy.data.texts.new(name)
|
||||
text_data.from_string(",".join(global_ids))
|
||||
props.filter_groups[self.group_index].filters[self.index].value = f"bpy.data.texts['{name}']"
|
||||
else:
|
||||
props.filter_groups[self.group_index].filters[self.index].value = ",".join(global_ids)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditFilterQuery(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_filter_query"
|
||||
bl_label = "Edit Filter Query"
|
||||
bl_description = "Edit the underlying filter query for advanced users"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
query: StringProperty(name="Query")
|
||||
module: StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
if self.query == self.old_query:
|
||||
return
|
||||
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
try:
|
||||
tool.Search.import_filter_query(self.query, props.filter_groups)
|
||||
except:
|
||||
return
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row()
|
||||
row.prop(self, "query", text="")
|
||||
|
||||
def invoke(self, context, event):
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
self.query = tool.Search.export_filter_query(props.filter_groups)
|
||||
self.old_query = self.query
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class Search(Operator):
|
||||
bl_idname = "bim.search"
|
||||
bl_label = "Search"
|
||||
@@ -127,21 +214,36 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
||||
bl_description = "Save search filter to an IFC group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: StringProperty(name="Name")
|
||||
module: StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
if not self.name:
|
||||
return
|
||||
|
||||
query = tool.Search.export_filter_query(context.scene.BIMSearchProperties.filter_groups)
|
||||
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
try:
|
||||
query = tool.Search.export_filter_query(props.filter_groups)
|
||||
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||
except:
|
||||
return
|
||||
|
||||
description = json.dumps({"type": "BBIM_Search", "query": query})
|
||||
group = [g for g in tool.Ifc.get().by_type("IfcGroup") if g.Name == self.name]
|
||||
if group:
|
||||
group = group[0]
|
||||
group.Description = description
|
||||
else:
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=results, group=group)
|
||||
if results:
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=list(results), group=group)
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row()
|
||||
row.prop(self, "name")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
@@ -152,11 +254,15 @@ class LoadSearch(Operator, tool.Ifc.Operator):
|
||||
bl_label = "Load Search"
|
||||
bl_description = "Load search filter from an IFC group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
module: StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
group = tool.Ifc.get().by_id(int(props.saved_searches))
|
||||
query = tool.Search.import_filter_query(group, context.scene.BIMSearchProperties.filter_groups)
|
||||
if self.module == "search":
|
||||
props = context.scene.BIMSearchProperties
|
||||
elif self.module == "csv":
|
||||
props = context.scene.CsvProperties
|
||||
group = tool.Ifc.get().by_id(int(context.scene.BIMSearchProperties.saved_searches))
|
||||
query = tool.Search.import_filter_query(tool.Search.get_group_query(group), props.filter_groups)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
@@ -242,14 +348,15 @@ class SaveColourscheme(Operator, tool.Ifc.Operator):
|
||||
query = props.colourscheme_query
|
||||
|
||||
group = [g for g in tool.Ifc.get().by_type("IfcGroup") if g.Name == self.name]
|
||||
coulour_scheme = {cs.name: cs.colour[0:3] for cs in props.colourscheme}
|
||||
if group:
|
||||
group = group[0]
|
||||
description = json.loads(group.Description)
|
||||
description["colourscheme"] = {cs.name: cs.colour[0:3] for cs in props.colourscheme}
|
||||
description["colourscheme"] = coulour_scheme
|
||||
description["colourscheme_query"] = query
|
||||
group.Description = json.dumps(description)
|
||||
else:
|
||||
description = json.dumps({"type": "BBIM_Search", "colourscheme": query})
|
||||
description = json.dumps({"type": "BBIM_Search", "colourscheme": coulour_scheme,"colourscheme_query": query})
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -264,7 +371,7 @@ class LoadColourscheme(Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
group = tool.Ifc.get().by_id(int(props.saved_searches))
|
||||
group = tool.Ifc.get().by_id(int(props.saved_colourschemes))
|
||||
description = json.loads(group.Description)
|
||||
props.colourscheme_query = description.get("colourscheme_query")
|
||||
props.colourscheme.clear()
|
||||
|
||||
@@ -128,7 +128,8 @@ class BIMSearchProperties(PropertyGroup):
|
||||
("classification", "Classification", "", "OUTLINER", 4),
|
||||
("location", "Location", "", "PACKAGE", 5),
|
||||
("type", "Type", "", "FILE_VOLUME", 6),
|
||||
("instance", "GlobalId", "", "GRIP", 7),
|
||||
("query", "Query", "", "POINTCLOUD_DATA", 7),
|
||||
("instance", "GlobalId", "", "GRIP", 8),
|
||||
],
|
||||
)
|
||||
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
||||
|
||||
@@ -35,59 +36,7 @@ class BIM_PT_search(Panel):
|
||||
|
||||
props = context.scene.BIMSearchProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{len(SearchData.data['saved_searches'])} Saved Searches")
|
||||
|
||||
if SearchData.data["saved_searches"]:
|
||||
row.operator("bim.load_search", text="", icon="IMPORT")
|
||||
row.operator("bim.save_search", text="", icon="EXPORT")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD")
|
||||
|
||||
for i, filter_group in enumerate(props.filter_groups):
|
||||
box = self.layout.box()
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(props, "facet", text="")
|
||||
op = row.operator("bim.add_filter", text="Add Filter", icon="ADD")
|
||||
op.type = props.facet
|
||||
op.index = i
|
||||
|
||||
for j, ifc_filter in enumerate(filter_group.filters):
|
||||
if ifc_filter.type == "entity":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_3D")
|
||||
elif ifc_filter.type == "attribute":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="COPY_ID")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "type":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME")
|
||||
elif ifc_filter.type == "material":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="MATERIAL")
|
||||
elif ifc_filter.type == "property":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
|
||||
row.prop(ifc_filter, "name", text="")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "classification":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER")
|
||||
elif ifc_filter.type == "location":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="PACKAGE")
|
||||
elif ifc_filter.type == "instance":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="GRIP")
|
||||
op = row.operator("bim.remove_filter", text="", icon="X")
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
|
||||
row = box.row()
|
||||
row.operator("bim.remove_filter_group", icon="X").index = i
|
||||
blenderbim.bim.helper.draw_filter(self.layout, props, SearchData, "search")
|
||||
|
||||
if len(props.filter_groups):
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -20,12 +20,11 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExpandAllTasks,
|
||||
operator.ContractAllTasks,
|
||||
operator.AddAnimationCamera,
|
||||
operator.AddSummaryTask,
|
||||
operator.AddTask,
|
||||
operator.AddTaskColumn,
|
||||
operator.AddTaskBars,
|
||||
operator.AddTaskColumn,
|
||||
operator.AddTimePeriod,
|
||||
operator.AddWorkCalendar,
|
||||
operator.AddWorkPlan,
|
||||
@@ -42,9 +41,12 @@ classes = (
|
||||
operator.BlenderBIM_DatePickerSetDate,
|
||||
operator.BlenderBIM_RedrawDatePicker,
|
||||
operator.CalculateTaskDuration,
|
||||
operator.ClearPreviousAnimation,
|
||||
operator.ContractAllTasks,
|
||||
operator.ContractTask,
|
||||
operator.CopyTaskAttribute,
|
||||
operator.CopyTask,
|
||||
operator.CopyTaskAttribute,
|
||||
operator.CreateBaseline,
|
||||
operator.DisableEditingSequence,
|
||||
operator.DisableEditingTask,
|
||||
operator.DisableEditingTaskTime,
|
||||
@@ -67,27 +69,29 @@ classes = (
|
||||
operator.EnableEditingTaskCalendar,
|
||||
operator.EnableEditingTaskSequence,
|
||||
operator.EnableEditingTaskTime,
|
||||
operator.EnableEditingWorkScheduleTasks,
|
||||
operator.EnableEditingWorkCalendar,
|
||||
operator.EnableEditingWorkCalendarTimes,
|
||||
operator.EnableEditingWorkPlan,
|
||||
operator.EnableEditingWorkPlanSchedules,
|
||||
operator.EnableEditingWorkSchedule,
|
||||
operator.EnableEditingWorkScheduleTasks,
|
||||
operator.EnableEditingWorkTime,
|
||||
operator.ExpandAllTasks,
|
||||
operator.ExpandTask,
|
||||
operator.ExportMSP,
|
||||
operator.ExportP6,
|
||||
operator.GenerateGanttChart,
|
||||
operator.GuessDateRange,
|
||||
operator.ImportMSP,
|
||||
operator.GoToTask,
|
||||
operator.ImportCSV,
|
||||
operator.ImportMSP,
|
||||
operator.ImportP6,
|
||||
operator.ImportP6XER,
|
||||
operator.ImportPP,
|
||||
operator.LoadTaskInputs,
|
||||
operator.LoadTaskOutputs,
|
||||
operator.LoadAnimationColorScheme,
|
||||
operator.LoadDefaultAnimationColors,
|
||||
operator.LoadProductTasks,
|
||||
operator.LoadTaskProperties,
|
||||
operator.LoadTaskResources,
|
||||
operator.RecalculateSchedule,
|
||||
operator.RemoveTask,
|
||||
operator.RemoveTaskCalendar,
|
||||
@@ -98,10 +102,11 @@ classes = (
|
||||
operator.RemoveWorkSchedule,
|
||||
operator.RemoveWorkTime,
|
||||
operator.ReorderTask,
|
||||
operator.SelectTaskRelatedProducts,
|
||||
operator.SaveAnimationColorScheme,
|
||||
operator.SelectTaskRelatedInputs,
|
||||
operator.SelectWorkScheduleProducts,
|
||||
operator.SelectTaskRelatedProducts,
|
||||
operator.SelectUnassignedWorkScheduleProducts,
|
||||
operator.SelectWorkScheduleProducts,
|
||||
operator.SetTaskSortColumn,
|
||||
operator.SetupDefaultTaskColumns,
|
||||
operator.UnassignLagTime,
|
||||
@@ -113,17 +118,18 @@ classes = (
|
||||
operator.UnassignWorkSchedule,
|
||||
operator.VisualiseWorkScheduleDate,
|
||||
operator.VisualiseWorkScheduleDateRange,
|
||||
operator.LoadTaskAnimationColors,
|
||||
operator.DisableEditingTaskAnimationColors,
|
||||
operator.LoadProductTasks,
|
||||
operator.HighlightTask,
|
||||
operator.CreateBaseline,
|
||||
operator.EnableStatusFilters,
|
||||
operator.DisableStatusFilters,
|
||||
operator.ActivateStatusFilters,
|
||||
operator.SelectStatusFilter,
|
||||
prop.WorkPlan,
|
||||
prop.BIMWorkPlanProperties,
|
||||
prop.Task,
|
||||
prop.TaskResource,
|
||||
prop.TaskProduct,
|
||||
prop.ISODuration,
|
||||
prop.IFCStatus,
|
||||
prop.BIMStatusProperties,
|
||||
prop.BIMWorkScheduleProperties,
|
||||
prop.BIMTaskTreeProperties,
|
||||
prop.BIMTaskTypeColor,
|
||||
@@ -133,17 +139,19 @@ classes = (
|
||||
prop.BIMWorkCalendarProperties,
|
||||
prop.DatePickerProperties,
|
||||
prop.BIMDateTextProperties,
|
||||
ui.BIM_PT_status,
|
||||
ui.BIM_PT_work_plans,
|
||||
ui.BIM_PT_work_schedules,
|
||||
ui.BIM_PT_work_calendars,
|
||||
ui.BIM_PT_animation_tools,
|
||||
ui.BIM_PT_task_icom,
|
||||
ui.BIM_PT_animation_Color_Scheme,
|
||||
ui.BIM_UL_task_columns,
|
||||
ui.BIM_UL_task_inputs,
|
||||
ui.BIM_UL_task_resources,
|
||||
ui.BIM_UL_task_outputs,
|
||||
ui.BIM_UL_tasks,
|
||||
ui.BIM_PT_4D_Tools,
|
||||
ui.BIM_PT_Task_Bar_Creator,
|
||||
ui.BIM_UL_animation_colors,
|
||||
ui.BIM_UL_product_input_tasks,
|
||||
ui.BIM_UL_product_output_tasks,
|
||||
@@ -164,6 +172,7 @@ def menu_func_import(self, context):
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMStatusProperties = bpy.props.PointerProperty(type=prop.BIMStatusProperties)
|
||||
bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties)
|
||||
bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties)
|
||||
bpy.types.Scene.BIMTaskTreeProperties = bpy.props.PointerProperty(type=prop.BIMTaskTreeProperties)
|
||||
@@ -176,6 +185,7 @@ def register():
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMStatusProperties
|
||||
del bpy.types.Scene.BIMWorkPlanProperties
|
||||
del bpy.types.Scene.BIMWorkScheduleProperties
|
||||
del bpy.types.Scene.BIMTaskTreeProperties
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util.doc import get_predefined_type_doc
|
||||
import ifcopenshell.util.date as dateutil
|
||||
import json
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -28,6 +28,7 @@ def refresh():
|
||||
WorkPlansData.is_loaded = False
|
||||
TaskICOMData.is_loaded = False
|
||||
WorkScheduleData.is_loaded = False
|
||||
AnimationColorSchemeData.is_loaded = False
|
||||
|
||||
|
||||
class SequenceData:
|
||||
@@ -37,7 +38,6 @@ class SequenceData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"predefined_types": cls.get_work_schedule_types(),
|
||||
"has_work_plans": cls.has_work_plans(),
|
||||
"has_work_schedules": cls.has_work_schedules(),
|
||||
"has_work_calendars": cls.has_work_calendars(),
|
||||
@@ -230,22 +230,6 @@ class SequenceData:
|
||||
data["NestingIndex"] = rel.RelatedObjects.index(task)
|
||||
cls.data["tasks"][task.id()] = data
|
||||
|
||||
@classmethod
|
||||
def get_work_schedule_types(cls):
|
||||
results = []
|
||||
declaration = tool.Ifc().schema().declaration_by_name("IfcWorkSchedule")
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
results.extend(
|
||||
[
|
||||
(e, e, get_predefined_type_doc(version, "IfcWorkSchedule", e))
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
]
|
||||
)
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
class WorkScheduleData:
|
||||
data = {}
|
||||
@@ -344,3 +328,31 @@ class TaskICOMData:
|
||||
resource_id = resource_tprops.resources[resource_props.active_resource_index].ifc_definition_id
|
||||
return not tool.Ifc.get().by_id(resource_id).is_a("IfcCrewResource")
|
||||
return False
|
||||
|
||||
|
||||
class AnimationColorSchemeData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {}
|
||||
cls.data["saved_color_schemes"] = cls.saved_color_schemes()
|
||||
|
||||
@classmethod
|
||||
def saved_color_schemes(cls):
|
||||
groups = tool.Ifc.get().by_type("IfcGroup")
|
||||
results = []
|
||||
for group in groups:
|
||||
try:
|
||||
data = json.loads(group.Description)
|
||||
if (
|
||||
isinstance(data, dict)
|
||||
and data.get("type", None) == "BBIM_AnimationColorScheme"
|
||||
and data.get("colourscheme", None)
|
||||
):
|
||||
results.append(group)
|
||||
except:
|
||||
pass
|
||||
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
|
||||
|
||||
@@ -35,6 +35,7 @@ def parse_datetime(value):
|
||||
def parse_duration(value):
|
||||
return ifcopenshell.util.date.parse_duration(value)
|
||||
|
||||
|
||||
def canonicalise_time(time):
|
||||
if not time:
|
||||
return "-"
|
||||
|
||||
@@ -27,11 +27,98 @@ import webbrowser
|
||||
import blenderbim.core.sequence as core
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.sequence.helper as helper
|
||||
import ifcopenshell.util.sequence
|
||||
import ifcopenshell.util.selector
|
||||
from datetime import datetime
|
||||
from dateutil import parser, relativedelta
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
import ifcopenshell.util.sequence
|
||||
|
||||
|
||||
class EnableStatusFilters(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_status_filters"
|
||||
bl_label = "Enable Status Filters"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStatusProperties
|
||||
props.is_enabled = True
|
||||
|
||||
props.statuses.clear()
|
||||
|
||||
statuses = set()
|
||||
for element in tool.Ifc.get().by_type("IfcPropertyEnumeratedValue"):
|
||||
if element.Name == "Status":
|
||||
pset = element.PartOfPset[0]
|
||||
if pset.Name.startswith("Pset_") and pset.Name.endswith("Common"):
|
||||
statuses.update(element.EnumerationValues)
|
||||
elif pset.Name == "EPset_Status": # Our secret sauce
|
||||
statuses.update(element.EnumerationValues)
|
||||
elif element.Name == "UserDefinedStatus":
|
||||
statuses.add(element.NominalValue)
|
||||
|
||||
statuses = ["No Status"] + sorted([s.wrappedValue for s in statuses])
|
||||
|
||||
for status in statuses:
|
||||
new = props.statuses.add()
|
||||
new.name = status
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableStatusFilters(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_status_filters"
|
||||
bl_label = "Disable Status Filters"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStatusProperties
|
||||
props.is_enabled = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ActivateStatusFilters(bpy.types.Operator):
|
||||
bl_idname = "bim.activate_status_filters"
|
||||
bl_label = "Activate Status Filters"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStatusProperties
|
||||
|
||||
query = []
|
||||
visible_statuses = {s.name for s in props.statuses if s.is_visible}
|
||||
for name in visible_statuses:
|
||||
if name == "No Status":
|
||||
q = f"IfcProduct, /Pset_.*Common/.Status=NULL, EPset_Status.Status=NULL"
|
||||
else:
|
||||
q = f"IfcProduct, /Pset_.*Common/.Status={name} + IfcProduct, EPset_Status.Status={name}"
|
||||
query.append(q)
|
||||
query = " + ".join(query)
|
||||
|
||||
if not query:
|
||||
return {"FINISHED"}
|
||||
|
||||
visible_elements = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||
|
||||
for obj in bpy.context.view_layer.objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not element.is_a("IfcProduct"):
|
||||
continue
|
||||
obj.hide_set(element not in visible_elements)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectStatusFilter(bpy.types.Operator):
|
||||
bl_idname = "bim.select_status_filter"
|
||||
bl_label = "Select Status Filter"
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStatusProperties
|
||||
query = f"IfcProduct, /Pset_.*Common/.Status={self.name} + IfcProduct, EPset_Status.Status={self.name}"
|
||||
if self.name == "No Status":
|
||||
query = f"IfcProduct, /Pset_.*Common/.Status=NULL, EPset_Status.Status=NULL"
|
||||
for element in ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query):
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -129,9 +216,21 @@ class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_work_schedule"
|
||||
bl_label = "Add Work Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_work_schedule(tool.Ifc)
|
||||
core.add_work_schedule(tool.Ifc, tool.Sequence, name=self.name)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "name", text="Name")
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
layout.prop(self.props, "work_schedule_predefined_types", text="Type")
|
||||
if self.props.work_schedule_predefined_types == "USERDEFINED":
|
||||
layout.prop(self.props, "object_type", text="Object type")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class EditWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -874,7 +973,7 @@ class EnableEditingTaskSequence(bpy.types.Operator):
|
||||
task: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.enable_editing_task_sequence(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
||||
core.enable_editing_task_sequence(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1190,36 +1289,6 @@ class SetTaskSortColumn(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadTaskResources(bpy.types.Operator):
|
||||
bl_idname = "bim.load_task_resources"
|
||||
bl_label = "Load Task Resources"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.load_task_resources(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadTaskInputs(bpy.types.Operator):
|
||||
bl_idname = "bim.load_task_inputs"
|
||||
bl_label = "Load Task Inputs"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.load_task_inputs(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadTaskOutputs(bpy.types.Operator):
|
||||
bl_idname = "bim.load_task_outputs"
|
||||
bl_label = "Load Task Outputs"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.load_task_outputs(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CalculateTaskDuration(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_task_duration"
|
||||
bl_label = "Calculate Task Duration"
|
||||
@@ -1263,25 +1332,51 @@ class AddTaskBars(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadTaskAnimationColors(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_task_animation_colors"
|
||||
class LoadDefaultAnimationColors(bpy.types.Operator):
|
||||
bl_idname = "bim.load_default_animation_color_scheme"
|
||||
bl_label = "Load Animation Colors"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.enable_editing_task_animation_colors(tool.Sequence)
|
||||
core.load_default_animation_color_scheme(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingTaskAnimationColors(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_task_animation_colors"
|
||||
bl_label = "Disable Editing Task Animation Colors"
|
||||
class SaveAnimationColorScheme(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.save_animation_color_scheme"
|
||||
bl_label = "Save Animation Color Scheme"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Saves the current animation color scheme"
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.disable_editing_task_animation_colors(tool.Sequence)
|
||||
def _execute(self, context):
|
||||
if not self.name:
|
||||
return
|
||||
core.save_animation_color_scheme(tool.Sequence, name=self.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class LoadAnimationColorScheme(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_animation_color_scheme"
|
||||
bl_label = "Load Animation Color Scheme"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Loads the animation color scheme"
|
||||
|
||||
def _execute(self, context):
|
||||
group = tool.Ifc.get().by_id(int(context.scene.BIMAnimationProperties.saved_color_schemes))
|
||||
core.load_animation_color_scheme(tool.Sequence, scheme=group)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row()
|
||||
row.prop(props, "saved_color_schemes", text="")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class CopyTask(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.duplicate_task"
|
||||
@@ -1315,14 +1410,14 @@ class LoadProductTasks(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class HighlightTask(bpy.types.Operator):
|
||||
bl_idname = "bim.highlight_task"
|
||||
class GoToTask(bpy.types.Operator):
|
||||
bl_idname = "bim.go_to_task"
|
||||
bl_label = "Highlight Task"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
task: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
r = core.highlight_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
||||
r = core.go_to_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
||||
if isinstance(r, str):
|
||||
self.report({"WARNING"}, r)
|
||||
return {"FINISHED"}
|
||||
@@ -1389,3 +1484,22 @@ class CreateBaseline(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class ClearPreviousAnimation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.clear_previous_animation"
|
||||
bl_label = "Clear Previous Animation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.clear_previous_animation(tool.Sequence)
|
||||
|
||||
|
||||
class AddAnimationCamera(bpy.types.Operator):
|
||||
bl_idname = "bim.add_animation_camera"
|
||||
bl_label = "Add Camera to Scene"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.add_animation_camera(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -20,10 +20,12 @@ import bpy
|
||||
import isodate
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
from ifcopenshell.util.doc import get_predefined_type_doc
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.sequence as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.sequence.data import SequenceData
|
||||
from blenderbim.bim.module.sequence.data import SequenceData, AnimationColorSchemeData, refresh as refresh_sequence_data
|
||||
import blenderbim.bim.module.resource.data
|
||||
import blenderbim.bim.module.pset.data
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from dateutil import parser
|
||||
@@ -87,22 +89,30 @@ def getWorkCalendars(self, context):
|
||||
|
||||
|
||||
def update_active_task_index(self, context):
|
||||
bpy.ops.bim.load_task_inputs()
|
||||
bpy.ops.bim.load_task_resources()
|
||||
bpy.ops.bim.load_task_outputs()
|
||||
task = tool.Sequence.get_highlighted_task()
|
||||
self.highlighted_task_id = task.id() if task else 0
|
||||
tool.Sequence.update_task_ICOM(task)
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
if self.editing_task_type == "SEQUENCE":
|
||||
tool.Sequence.load_task_properties()
|
||||
|
||||
|
||||
def update_active_task_outputs(self, context):
|
||||
bpy.ops.bim.load_task_outputs()
|
||||
task = tool.Sequence.get_highlighted_task()
|
||||
outputs = tool.Sequence.get_task_outputs(task)
|
||||
tool.Sequence.load_task_outputs(outputs)
|
||||
|
||||
|
||||
def update_active_task_resources(self, context):
|
||||
bpy.ops.bim.load_task_resources()
|
||||
task = tool.Sequence.get_highlighted_task()
|
||||
resources = tool.Sequence.get_task_resources(task)
|
||||
tool.Sequence.load_task_resources(resources)
|
||||
|
||||
|
||||
def update_active_task_inputs(self, context):
|
||||
bpy.ops.bim.load_task_inputs()
|
||||
task = tool.Sequence.get_highlighted_task()
|
||||
inputs = tool.Sequence.get_task_inputs(task)
|
||||
tool.Sequence.load_task_inputs(inputs)
|
||||
|
||||
|
||||
def updateTaskName(self, context):
|
||||
@@ -208,25 +218,33 @@ def updateTaskDuration(self, context):
|
||||
self.duration = "-"
|
||||
return
|
||||
|
||||
self.file = tool.Ifc.get()
|
||||
task = self.file.by_id(self.ifc_definition_id)
|
||||
task = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if task.TaskTime:
|
||||
task_time = task.TaskTime
|
||||
else:
|
||||
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task)
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_task_time",
|
||||
self.file,
|
||||
**{"task_time": task_time, "attributes": {"ScheduleDuration": duration}},
|
||||
)
|
||||
task_time = tool.Ifc.run("sequence.add_task_time", task=task)
|
||||
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
|
||||
SequenceData.load()
|
||||
blenderbim.core.sequence.load_task_properties(tool.Sequence)
|
||||
bpy.ops.bim.load_task_properties()
|
||||
tool.Sequence.load_resources()
|
||||
|
||||
|
||||
def get_schedule_predefined_types(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
SequenceData.load()
|
||||
return SequenceData.data["predefined_types"]
|
||||
results = []
|
||||
declaration = tool.Ifc().schema().declaration_by_name("IfcWorkSchedule")
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
results.extend(
|
||||
[
|
||||
(e, e, get_predefined_type_doc(version, "IfcWorkSchedule", e))
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
if e != "BASELINE"
|
||||
]
|
||||
)
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
def update_visualisation_start(self, context):
|
||||
@@ -286,12 +304,49 @@ def update_sort_reversed(self, context):
|
||||
work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id),
|
||||
)
|
||||
|
||||
|
||||
def update_filter_by_active_schedule(self, context):
|
||||
if context.active_object:
|
||||
core.load_product_related_tasks(
|
||||
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
|
||||
|
||||
def switch_options(self, context):
|
||||
if self.should_show_visualisation_ui:
|
||||
self.should_show_snapshot_ui = False
|
||||
|
||||
|
||||
def switch_options2(self, context):
|
||||
if self.should_show_snapshot_ui:
|
||||
self.should_show_visualisation_ui = False
|
||||
|
||||
|
||||
def get_saved_color_schemes(self, context):
|
||||
if not AnimationColorSchemeData.is_loaded:
|
||||
AnimationColorSchemeData.load()
|
||||
return AnimationColorSchemeData.data["saved_color_schemes"]
|
||||
|
||||
|
||||
def updateAssignedResourceName(self, context):
|
||||
pass
|
||||
|
||||
def updateAssignedResourceUsage(self, context):
|
||||
if not self.schedule_usage:
|
||||
return
|
||||
resource = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if resource.Usage and resource.Usage.ScheduleUsage == self.schedule_usage:
|
||||
return
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={
|
||||
"ScheduleUsage": self.schedule_usage
|
||||
})
|
||||
tool.Sequence.load_task_properties()
|
||||
tool.Resource.load_resource_properties()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
blenderbim.bim.module.resource.data.refresh()
|
||||
blenderbim.bim.module.sequence.data.refresh()
|
||||
blenderbim.bim.module.pset.data.refresh()
|
||||
|
||||
class Task(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=updateTaskName)
|
||||
identification: StringProperty(name="Identification", update=updateTaskIdentification)
|
||||
@@ -319,9 +374,9 @@ class WorkPlan(PropertyGroup):
|
||||
|
||||
|
||||
class TaskResource(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
name: StringProperty(name="Name", update=updateAssignedResourceName)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
schedule_usage: FloatProperty(name="Schedule Usage")
|
||||
schedule_usage: FloatProperty(name="Schedule Usage", update=updateAssignedResourceUsage)
|
||||
|
||||
|
||||
class TaskProduct(PropertyGroup):
|
||||
@@ -348,10 +403,21 @@ class ISODuration(PropertyGroup):
|
||||
seconds: IntProperty(name="Seconds", default=0)
|
||||
|
||||
|
||||
class IFCStatus(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_visible: BoolProperty(name="Is Visible", default=True)
|
||||
|
||||
|
||||
class BIMStatusProperties(PropertyGroup):
|
||||
is_enabled: BoolProperty(name="Is Enabled")
|
||||
statuses: CollectionProperty(name="Statuses", type=IFCStatus)
|
||||
|
||||
|
||||
class BIMWorkScheduleProperties(PropertyGroup):
|
||||
work_schedule_predefined_types: EnumProperty(
|
||||
items=get_schedule_predefined_types, name="Predefined Type", default=None
|
||||
)
|
||||
object_type: StringProperty(name="Object Type")
|
||||
durations_attributes: CollectionProperty(name="Durations Attributes", type=ISODuration)
|
||||
work_calendars: EnumProperty(items=getWorkCalendars, name="Work Calendars")
|
||||
work_schedule_attributes: CollectionProperty(name="Work Schedule Attributes", type=Attribute)
|
||||
@@ -361,10 +427,11 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")
|
||||
active_task_index: IntProperty(name="Active Task Index", update=update_active_task_index)
|
||||
active_task_id: IntProperty(name="Active Task Id")
|
||||
highlighted_task_id: IntProperty(name="Highlited Task Id")
|
||||
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False)
|
||||
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=True, update=switch_options)
|
||||
should_show_task_bar_selection: BoolProperty(name="Add to task bar", default=False)
|
||||
should_show_snapshot_ui: BoolProperty(name="Should Show Snapshot UI", default=False)
|
||||
should_show_snapshot_ui: BoolProperty(name="Should Show Snapshot UI", default=False, update=switch_options2)
|
||||
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
||||
columns: CollectionProperty(name="Columns", type=Attribute)
|
||||
active_column_index: IntProperty(name="Active Column Index")
|
||||
@@ -397,9 +464,9 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
visualisation_start: StringProperty(name="Visualisation Start", update=update_visualisation_start)
|
||||
visualisation_finish: StringProperty(name="Visualisation Finish", update=update_visualisation_finish)
|
||||
speed_multiplier: FloatProperty(name="Speed Multiplier", default=10000)
|
||||
speed_animation_duration: StringProperty(name="Speed Animation Duration", default="PT1S")
|
||||
speed_animation_duration: StringProperty(name="Speed Animation Duration", default="1 s")
|
||||
speed_animation_frames: IntProperty(name="Speed Animation Frames", default=24)
|
||||
speed_real_duration: StringProperty(name="Speed Real Duration", default="P1W")
|
||||
speed_real_duration: StringProperty(name="Speed Real Duration", default="1 w")
|
||||
speed_types: EnumProperty(
|
||||
items=[
|
||||
("FRAME_SPEED", "Frame-based", "e.g. 25 frames = 1 real week"),
|
||||
@@ -425,7 +492,9 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
|
||||
show_task_operators: BoolProperty(name="Show Task Options", default=True)
|
||||
should_show_schedule_baseline_ui: BoolProperty(name="Baselines", default=False)
|
||||
filter_by_active_schedule: BoolProperty(name="Filter By Active Schedule", default=False, update = update_filter_by_active_schedule)
|
||||
filter_by_active_schedule: BoolProperty(
|
||||
name="Filter By Active Schedule", default=False, update=update_filter_by_active_schedule
|
||||
)
|
||||
|
||||
|
||||
class BIMTaskTreeProperties(PropertyGroup):
|
||||
@@ -495,16 +564,16 @@ class BIMTaskTypeColor(PropertyGroup):
|
||||
default=(1, 0, 0),
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
# update=update_task_animation_color,
|
||||
)
|
||||
|
||||
|
||||
class BIMAnimationProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Loaded", default=False)
|
||||
saved_color_schemes: EnumProperty(items=get_saved_color_schemes, name="Saved Colour Schemes")
|
||||
active_color_component_outputs_index: IntProperty(name="Active Color Component Index")
|
||||
active_color_component_inputs_index: IntProperty(name="Active Color Component Index")
|
||||
task_colors_components_inputs: CollectionProperty(name="Groups", type=BIMTaskTypeColor)
|
||||
task_colors_components_outputs: CollectionProperty(name="Groups", type=BIMTaskTypeColor)
|
||||
task_input_colors: CollectionProperty(name="Groups", type=BIMTaskTypeColor)
|
||||
task_output_colors: CollectionProperty(name="Groups", type=BIMTaskTypeColor)
|
||||
color_full: FloatVectorProperty(
|
||||
name="Full Bar",
|
||||
subtype="COLOR",
|
||||
|
||||
@@ -21,8 +21,45 @@ import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.module.sequence.data import WorkPlansData, WorkScheduleData, SequenceData, TaskICOMData
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.module.sequence.data import (
|
||||
WorkPlansData,
|
||||
WorkScheduleData,
|
||||
SequenceData,
|
||||
TaskICOMData,
|
||||
AnimationColorSchemeData,
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_status(Panel):
|
||||
bl_label = "Status"
|
||||
bl_idname = "BIM_PT_status"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_status"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMStatusProperties
|
||||
|
||||
if not self.props.is_enabled:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.enable_status_filters", icon="GREASEPENCIL")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.activate_status_filters", icon="TIME")
|
||||
row.operator("bim.disable_status_filters", icon="CANCEL", text="")
|
||||
|
||||
for status in self.props.statuses:
|
||||
row = self.layout.row()
|
||||
row.label(text=status.name)
|
||||
row.prop(status, "is_visible", text="", emboss=False, icon="HIDE_OFF" if status.is_visible else "HIDE_ON")
|
||||
row.operator("bim.select_status_filter", icon="RESTRICT_SELECT_OFF", text="").name = status.name
|
||||
|
||||
|
||||
class BIM_PT_work_plans(Panel):
|
||||
@@ -32,10 +69,12 @@ class BIM_PT_work_plans(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not WorkPlansData.is_loaded:
|
||||
@@ -54,11 +93,10 @@ class BIM_PT_work_plans(Panel):
|
||||
def draw_work_plan_ui(self, work_plan):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=work_plan["name"], icon="TEXT")
|
||||
|
||||
if self.props.active_work_plan_id == work_plan["id"]:
|
||||
if self.props.editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_work_plan", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_plan", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_plan", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_plan_id:
|
||||
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = work_plan["id"]
|
||||
else:
|
||||
@@ -101,10 +139,12 @@ class BIM_PT_work_schedules(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
@@ -113,10 +153,9 @@ class BIM_PT_work_schedules(Panel):
|
||||
WorkScheduleData.load()
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
self.tprops = context.scene.BIMTaskTreeProperties
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
|
||||
if not self.props.active_work_schedule_id:
|
||||
row = self.layout.row()
|
||||
row = self.layout.row(align=True)
|
||||
if SequenceData.data["has_work_schedules"]:
|
||||
row.label(
|
||||
text="{} Work Schedules Found".format(SequenceData.data["number_of_work_schedules_loaded"]),
|
||||
@@ -124,25 +163,25 @@ class BIM_PT_work_schedules(Panel):
|
||||
)
|
||||
else:
|
||||
row.label(text="No Work Schedules found.", icon="TEXT")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "work_schedule_predefined_types")
|
||||
row.operator("bim.add_work_schedule", text="Add", icon="ADD")
|
||||
row.operator("bim.add_work_schedule", text="", icon="ADD")
|
||||
|
||||
for work_schedule_id, work_schedule in SequenceData.data["work_schedules"].items():
|
||||
|
||||
self.draw_work_schedule_ui(work_schedule_id, work_schedule)
|
||||
|
||||
def draw_work_schedule_ui(self, work_schedule_id, work_schedule):
|
||||
if not work_schedule["PredefinedType"] == "BASELINE":
|
||||
if work_schedule["PredefinedType"] == "BASELINE":
|
||||
self.draw_readonly_work_schedule_ui(work_schedule_id)
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
|
||||
if self.props.active_work_schedule_id == work_schedule_id:
|
||||
row.label(
|
||||
text="Currently editing: {}[{}]".format(work_schedule["Name"], work_schedule["PredefinedType"]),
|
||||
icon="LINENUMBERS_ON",
|
||||
)
|
||||
if self.props.editing_type == "WORK_SCHEDULE":
|
||||
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
|
||||
row.operator("bim.edit_work_schedule", text="Apply", icon="CHECKMARK")
|
||||
elif self.props.editing_type == "TASKS":
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.alignment = "LEFT"
|
||||
@@ -177,33 +216,30 @@ class BIM_PT_work_schedules(Panel):
|
||||
row1.alignment = "RIGHT"
|
||||
row1.prop(self.props, "should_show_column_ui", text="Schedule Columns", icon="SHORTDISPLAY")
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(
|
||||
self.props, "should_show_visualisation_ui", text="Animation Options", icon="CAMERA_STEREO"
|
||||
)
|
||||
row2.prop(self.props, "should_show_snapshot_ui", text="Snapshot Options", icon="CAMERA_STEREO")
|
||||
row.operator("bim.disable_editing_work_schedule", text="Disable editing", icon="CANCEL")
|
||||
else:
|
||||
row.operator("bim.disable_editing_work_schedule", text="Cancel", icon="CANCEL")
|
||||
if not self.props.active_work_schedule_id:
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col1 = grid.column()
|
||||
col1.label(
|
||||
text="{}[{}]".format(work_schedule["Name"], work_schedule["PredefinedType"]) or "Unnamed",
|
||||
icon="LINENUMBERS_ON",
|
||||
)
|
||||
col2 = grid.column()
|
||||
row = col2.row(align=True)
|
||||
row.operator(
|
||||
"bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
|
||||
"bim.enable_editing_work_schedule_tasks", text="Tasks", icon="ACTION"
|
||||
).work_schedule = work_schedule_id
|
||||
row.operator(
|
||||
"bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL"
|
||||
"bim.enable_editing_work_schedule", text="Attributes", icon="GREASEPENCIL"
|
||||
).work_schedule = work_schedule_id
|
||||
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id
|
||||
|
||||
row.operator("bim.remove_work_schedule", text="Delete", icon="X").work_schedule = work_schedule_id
|
||||
if self.props.active_work_schedule_id == work_schedule_id:
|
||||
if self.props.editing_type == "WORK_SCHEDULE":
|
||||
self.draw_editable_work_schedule_ui()
|
||||
elif self.props.editing_type == "TASKS":
|
||||
self.draw_baseline_ui(work_schedule_id)
|
||||
self.draw_column_ui()
|
||||
if self.props.should_show_visualisation_ui:
|
||||
self.draw_visualisation_ui()
|
||||
if self.props.should_show_snapshot_ui:
|
||||
self.draw_snapshot_ui()
|
||||
self.draw_editable_task_ui(work_schedule_id)
|
||||
else:
|
||||
self.draw_readonly_work_schedule_ui(work_schedule_id)
|
||||
|
||||
def draw_task_operators(self):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -218,7 +254,9 @@ class BIM_PT_work_schedules(Panel):
|
||||
row.operator("bim.edit_task_time", text="", icon="CHECKMARK")
|
||||
elif self.props.editing_task_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_task", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_task", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_task", text="Cancel", icon="CANCEL")
|
||||
elif self.props.editing_task_type == "SEQUENCE":
|
||||
row.operator("bim.disable_editing_task", text="Cancel", icon="CANCEL")
|
||||
else:
|
||||
row.prop(self.props, "show_task_operators", text="Edit", icon="GREASEPENCIL")
|
||||
if self.props.show_task_operators:
|
||||
@@ -226,7 +264,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
row2.alignment = "RIGHT"
|
||||
|
||||
row2.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
|
||||
row2.operator("bim.enable_editing_task_sequence", text="", icon="TRACKING").task = ifc_definition_id
|
||||
row2.operator("bim.enable_editing_task_sequence", text="", icon="TRACKING")
|
||||
row2.operator("bim.enable_editing_task_time", text="", icon="TIME").task = ifc_definition_id
|
||||
row2.operator(
|
||||
"bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO"
|
||||
@@ -242,7 +280,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
if not self.props.should_show_column_ui:
|
||||
return
|
||||
row = self.layout.row()
|
||||
row.operator("bim.setup_default_task_columns", text="Add Default Columns", icon="ANCHOR_BOTTOM")
|
||||
row.operator("bim.setup_default_task_columns", text="Setup Default Columns", icon="ANCHOR_BOTTOM")
|
||||
row.alignment = "RIGHT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "column_types", text="")
|
||||
@@ -268,103 +306,6 @@ class BIM_PT_work_schedules(Panel):
|
||||
|
||||
self.layout.template_list("BIM_UL_task_columns", "", self.props, "columns", self.props, "active_column_index")
|
||||
|
||||
def draw_visualisation_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Start Date/ Date Range:")
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Start Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_finish or "Finish Date", icon="FF")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_finish"
|
||||
op = row.operator("bim.guess_date_range", text="Guess", icon="FILE_REFRESH")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Speed Settings")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "speed_types", text="")
|
||||
if self.props.speed_types == "FRAME_SPEED":
|
||||
row.prop(self.props, "speed_animation_frames", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "DURATION_SPEED":
|
||||
row.prop(self.props, "speed_animation_duration", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "MULTIPLIER_SPEED":
|
||||
row.prop(self.props, "speed_multiplier", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Display Settings")
|
||||
row = self.layout.row(align=True)
|
||||
if not self.animation_props.is_editing:
|
||||
op = row.operator(
|
||||
"bim.enable_editing_task_animation_colors", text="Customize Object Colors", icon="SEQUENCE_COLOR_04"
|
||||
)
|
||||
else:
|
||||
op = row.operator(
|
||||
"bim.disable_editing_task_animation_colors", text="Hide Object Colors", icon="SEQUENCE_COLOR_01"
|
||||
)
|
||||
|
||||
row.prop(self.animation_props, "should_show_task_bar_options", text="Task Bar", icon="NLA_PUSHDOWN")
|
||||
if self.animation_props.should_show_task_bar_options:
|
||||
row = self.layout.row()
|
||||
row.label(text="Task Bar Options", icon="NLA_PUSHDOWN")
|
||||
row.alignment = "LEFT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_show_task_bar_selection", text="Enable Selection", icon="NLA_PUSHDOWN")
|
||||
row.operator("bim.add_task_bars", text="Generate bars", icon="NLA_PUSHDOWN")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_progress")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_full")
|
||||
|
||||
if self.animation_props.is_editing:
|
||||
self.draw_visualisation_settings_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.visualise_work_schedule_date_range", text="Create Animation", icon="OUTLINER_OB_CAMERA")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_snapshot_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Construction Snapshot:")
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.visualise_work_schedule_date", text="Create SnapShot", icon="RESTRICT_RENDER_OFF")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_visualisation_settings_ui(self):
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="INPUT COLORS", icon="COLLECTION_COLOR_01")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_inputs",
|
||||
self.animation_props,
|
||||
"active_color_component_inputs_index",
|
||||
)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="OUTPUT COLORS", icon="COLLECTION_COLOR_04")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_colors_components_outputs",
|
||||
self.animation_props,
|
||||
"active_color_component_outputs_index",
|
||||
)
|
||||
|
||||
def draw_editable_work_schedule_ui(self):
|
||||
draw_attributes(self.props.work_schedule_attributes, self.layout)
|
||||
|
||||
@@ -390,13 +331,13 @@ class BIM_PT_work_schedules(Panel):
|
||||
self.draw_editable_task_attributes_ui()
|
||||
elif self.props.active_task_id and self.props.editing_task_type == "CALENDAR":
|
||||
self.draw_editable_task_calendar_ui()
|
||||
elif self.props.active_task_id and self.props.editing_task_type == "SEQUENCE":
|
||||
elif self.props.highlighted_task_id and self.props.editing_task_type == "SEQUENCE":
|
||||
self.draw_editable_task_sequence_ui()
|
||||
elif self.props.active_task_time_id and self.props.editing_task_type == "TASKTIME":
|
||||
self.draw_editable_task_time_attributes_ui()
|
||||
|
||||
def draw_editable_task_sequence_ui(self):
|
||||
task = SequenceData.data["tasks"][self.props.active_task_id]
|
||||
task = SequenceData.data["tasks"][self.props.highlighted_task_id]
|
||||
row = self.layout.row()
|
||||
row.label(text="{} Predecessors".format(len(task["IsSuccessorFrom"])), icon="BACK")
|
||||
for sequence_id in task["IsSuccessorFrom"]:
|
||||
@@ -410,6 +351,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
def draw_editable_sequence_ui(self, sequence, process_type):
|
||||
task = SequenceData.data["tasks"][sequence[process_type]]
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.go_to_task", text="", icon="RESTRICT_SELECT_OFF").task = task["id"]
|
||||
row.label(text=task["Identification"] or "XXX")
|
||||
row.label(text=task["Name"] or "Unnamed")
|
||||
row.label(text=sequence["SequenceType"] or "N/A")
|
||||
@@ -421,12 +363,12 @@ class BIM_PT_work_schedules(Panel):
|
||||
if self.props.active_sequence_id == sequence["id"]:
|
||||
if self.props.editing_sequence_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_sequence_attributes", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_sequence", text="Cancel", icon="CANCEL")
|
||||
self.draw_editable_sequence_attributes_ui()
|
||||
elif self.props.editing_sequence_type == "LAG_TIME":
|
||||
op = row.operator("bim.edit_sequence_lag_time", text="", icon="CHECKMARK")
|
||||
op.lag_time = sequence["TimeLag"]
|
||||
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_sequence", text="Cancel", icon="CANCEL")
|
||||
self.draw_editable_sequence_lag_time_ui()
|
||||
else:
|
||||
if sequence["TimeLag"]:
|
||||
@@ -491,7 +433,7 @@ class BIM_PT_work_schedules(Panel):
|
||||
"id"
|
||||
]
|
||||
baseline_row.operator(
|
||||
"bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
|
||||
"bim.enable_editing_work_schedule_tasks", text="Display Schedule", icon="ACTION"
|
||||
).work_schedule = baseline["id"]
|
||||
baseline_row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = baseline["id"]
|
||||
|
||||
@@ -521,6 +463,172 @@ class BIM_PT_work_schedules(Panel):
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_animation_tools(Panel):
|
||||
bl_label = "Animation Tools"
|
||||
bl_idname = "BIM_PT_animation_tools"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_work_schedules"
|
||||
bl_order = 4
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
if props.active_work_schedule_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "should_show_visualisation_ui", text="Animation Settings", icon="SETTINGS")
|
||||
row.prop(self.props, "should_show_snapshot_ui", text="Snapshot Settings", icon="SETTINGS")
|
||||
if self.props.should_show_visualisation_ui:
|
||||
self.draw_visualisation_ui()
|
||||
if self.props.should_show_snapshot_ui:
|
||||
self.draw_snapshot_ui()
|
||||
self.draw_processing_options()
|
||||
|
||||
def draw_processing_options(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "LEFT"
|
||||
row.label(text="Processing Tools")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_animation_camera", text="Add Camera", icon="CAMERA_DATA")
|
||||
row.operator("bim.clear_previous_animation", text="Reset Animation", icon="TRACKING_CLEAR_FORWARDS")
|
||||
|
||||
def draw_visualisation_ui(self):
|
||||
if not AnimationColorSchemeData.is_loaded:
|
||||
AnimationColorSchemeData.load()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Start Date/ Date Range:", icon="CAMERA_DATA")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Start Date", icon="REW")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_finish or "Finish Date", icon="FF")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_finish"
|
||||
op = row.operator("bim.guess_date_range", text="Guess", icon="FILE_REFRESH")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Speed Settings")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "speed_types", text="")
|
||||
if self.props.speed_types == "FRAME_SPEED":
|
||||
row.prop(self.props, "speed_animation_frames", text="")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "DURATION_SPEED":
|
||||
row.prop(self.props, "speed_animation_duration", text="")
|
||||
row.label(text="->")
|
||||
row.prop(self.props, "speed_real_duration", text="")
|
||||
elif self.props.speed_types == "MULTIPLIER_SPEED":
|
||||
row.prop(self.props, "speed_multiplier", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Display Settings")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.animation_props, "should_show_task_bar_options", text="Task Bars", icon="NLA_PUSHDOWN")
|
||||
if self.animation_props.should_show_task_bar_options:
|
||||
row = self.layout.row()
|
||||
row.label(text="Task Bar Options", icon="NLA_PUSHDOWN")
|
||||
row.alignment = "LEFT"
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "should_show_task_bar_selection", text="Enable Selection", icon="NLA_PUSHDOWN")
|
||||
row.operator("bim.add_task_bars", text="Generate bars", icon="NLA_PUSHDOWN")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_progress")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self.animation_props, "color_full")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if self.animation_props.saved_color_schemes:
|
||||
row.prop(self.animation_props, "saved_color_schemes", text="Color Scheme", icon="SEQUENCE_COLOR_04")
|
||||
else:
|
||||
row.label(text="No Color Scheme Saved", icon="INFO")
|
||||
op = row.operator("bim.visualise_work_schedule_date_range", text="Create Animation", icon="OUTLINER_OB_CAMERA")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
def draw_snapshot_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Date of Snapshot:", icon="CAMERA_STEREO")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Date", icon="PROP_PROJECTED")
|
||||
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.visualise_work_schedule_date", text="Create SnapShot", icon="CAMERA_STEREO")
|
||||
op.work_schedule = self.props.active_work_schedule_id
|
||||
|
||||
|
||||
class BIM_PT_animation_Color_Scheme(Panel):
|
||||
bl_label = "Animation Color Scheme"
|
||||
bl_idname = "BIM_PT_animation_Color_Scheme"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not AnimationColorSchemeData.is_loaded:
|
||||
AnimationColorSchemeData.load()
|
||||
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.load_default_animation_color_scheme", text="Load default", icon="SEQUENCE_COLOR_04")
|
||||
if AnimationColorSchemeData.data["saved_color_schemes"]:
|
||||
row.operator("bim.load_animation_color_scheme", text="Load Scheme", icon="IMPORT")
|
||||
row.operator("bim.save_animation_color_scheme", text="Save Scheme", icon="EXPORT")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="INPUT COLORS", icon="COLLECTION_COLOR_01")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_input_colors",
|
||||
self.animation_props,
|
||||
"active_color_component_inputs_index",
|
||||
)
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="OUTPUT COLORS", icon="COLLECTION_COLOR_04")
|
||||
row1 = col.row()
|
||||
row1.template_list(
|
||||
"BIM_UL_animation_colors",
|
||||
"",
|
||||
self.animation_props,
|
||||
"task_output_colors",
|
||||
self.animation_props,
|
||||
"active_color_component_outputs_index",
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_task_icom(Panel):
|
||||
bl_label = "Task ICOM"
|
||||
bl_idname = "BIM_PT_task_icom"
|
||||
@@ -529,6 +637,7 @@ class BIM_PT_task_icom(Panel):
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_work_schedules"
|
||||
bl_order = 1
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -657,9 +766,9 @@ class BIM_UL_task_resources(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.go_to_resource", text="", icon="STYLUS_PRESSURE").resource = item.ifc_definition_id
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
row.label(text=str(item.schedule_usage))
|
||||
|
||||
row.prop(item, "schedule_usage", emboss=False, text="")
|
||||
|
||||
class BIM_UL_animation_colors(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
@@ -682,7 +791,7 @@ class BIM_UL_product_input_tasks(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.highlight_task", text="", icon="STYLUS_PRESSURE")
|
||||
op = row.operator("bim.go_to_task", text="", icon="STYLUS_PRESSURE")
|
||||
op.task = item.ifc_definition_id
|
||||
row.split(factor=0.8)
|
||||
row.label(text=item.name)
|
||||
@@ -692,7 +801,7 @@ class BIM_UL_product_output_tasks(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.highlight_task", text="", icon="STYLUS_PRESSURE")
|
||||
op = row.operator("bim.go_to_task", text="", icon="STYLUS_PRESSURE")
|
||||
op.task = item.ifc_definition_id
|
||||
row.split(factor=0.8)
|
||||
row.label(text=item.name)
|
||||
@@ -732,19 +841,18 @@ class BIM_UL_tasks(UIList):
|
||||
)
|
||||
if self.props.enable_reorder:
|
||||
self.draw_order_operator(row, item.ifc_definition_id)
|
||||
if self.props.active_task_id:
|
||||
if self.props.editing_task_type == "SEQUENCE" and self.props.active_task_id != item.ifc_definition_id:
|
||||
if item.is_predecessor:
|
||||
op = row.operator("bim.unassign_predecessor", text="", icon="BACK", emboss=False)
|
||||
else:
|
||||
op = row.operator("bim.assign_predecessor", text="", icon="TRACKING_BACKWARDS", emboss=False)
|
||||
op.task = item.ifc_definition_id
|
||||
if self.props.editing_task_type == "SEQUENCE" and self.props.highlighted_task_id != item.ifc_definition_id:
|
||||
if item.is_predecessor:
|
||||
op = row.operator("bim.unassign_predecessor", text="", icon="BACK", emboss=False)
|
||||
else:
|
||||
op = row.operator("bim.assign_predecessor", text="", icon="TRACKING_BACKWARDS", emboss=False)
|
||||
op.task = item.ifc_definition_id
|
||||
|
||||
if item.is_successor:
|
||||
op = row.operator("bim.unassign_successor", text="", icon="FORWARD", emboss=False)
|
||||
else:
|
||||
op = row.operator("bim.assign_successor", text="", icon="TRACKING_FORWARDS", emboss=False)
|
||||
op.task = item.ifc_definition_id
|
||||
if item.is_successor:
|
||||
op = row.operator("bim.unassign_successor", text="", icon="FORWARD", emboss=False)
|
||||
else:
|
||||
op = row.operator("bim.assign_successor", text="", icon="TRACKING_FORWARDS", emboss=False)
|
||||
op.task = item.ifc_definition_id
|
||||
|
||||
def draw_order_operator(self, row, ifc_definition_id):
|
||||
task = SequenceData.data["tasks"][ifc_definition_id]
|
||||
@@ -813,10 +921,12 @@ class BIM_PT_work_calendars(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not SequenceData.is_loaded:
|
||||
@@ -841,7 +951,7 @@ class BIM_PT_work_calendars(Panel):
|
||||
if self.props.active_work_calendar_id == work_calendar_id:
|
||||
if self.props.editing_type == "ATTRIBUTES":
|
||||
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_calendar", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_calendar", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_calendar_id:
|
||||
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = work_calendar_id
|
||||
else:
|
||||
@@ -879,7 +989,7 @@ class BIM_PT_work_calendars(Panel):
|
||||
row.label(text="{} - {}".format(work_time["Start"] or "*", work_time["Finish"] or "*"))
|
||||
if self.props.active_work_time_id == work_time["id"]:
|
||||
row.operator("bim.edit_work_time", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_work_time", text="", icon="CANCEL")
|
||||
row.operator("bim.disable_editing_work_time", text="Cancel", icon="CANCEL")
|
||||
elif self.props.active_work_time_id:
|
||||
op = row.operator("bim.remove_work_time", text="", icon="X")
|
||||
op.work_time = work_time["id"]
|
||||
@@ -1000,29 +1110,3 @@ class BIM_PT_4D_Tools(Panel):
|
||||
self.props,
|
||||
"active_product_output_task_index",
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_Task_Bar_Creator(Panel):
|
||||
bl_label = "Task Bar Creator"
|
||||
bl_idname = "BIM_PT_Task_Bar_Creator"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "BIM_PT_4D_Tools"
|
||||
|
||||
def draw(self, context):
|
||||
self.animation_props = context.scene.BIMAnimationProperties
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_task_bars", text="Add Bar Visual", icon="NLA_PUSHDOWN")
|
||||
|
||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
row1 = col.row(align=True)
|
||||
row1.label(text="Bar Colors", icon="NLA_PUSHDOWN")
|
||||
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(self.animation_props, "color_progress")
|
||||
|
||||
row3 = col.row(align=True)
|
||||
row3.prop(self.animation_props, "color_full")
|
||||
|
||||
@@ -34,6 +34,7 @@ classes = (
|
||||
operator.RemovePort,
|
||||
operator.RemoveSystem,
|
||||
operator.SelectSystemProducts,
|
||||
operator.MEPConnectElements,
|
||||
operator.SetFlowDirection,
|
||||
operator.ShowPorts,
|
||||
operator.UnassignSystem,
|
||||
|
||||
@@ -23,6 +23,7 @@ import blenderbim.core.system as core
|
||||
import blenderbim.bim.handler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.system.data import PortData
|
||||
from mathutils import Matrix
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -215,6 +216,42 @@ class DisconnectPort(bpy.types.Operator, Operator):
|
||||
core.disconnect_port(tool.Ifc, port=element)
|
||||
|
||||
|
||||
class MEPConnectElements(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.mep_connect_elements"
|
||||
bl_label = "Connect MEP Elements"
|
||||
bl_description = "Connects two selected elements if they have ports with matching location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return len(context.selected_objects) == 2
|
||||
|
||||
def _execute(self, context):
|
||||
obj1 = context.active_object
|
||||
obj2 = next(o for o in context.selected_objects if o != obj1)
|
||||
|
||||
el1 = tool.Ifc.get_entity(obj1)
|
||||
el2 = tool.Ifc.get_entity(obj2)
|
||||
|
||||
obj1_ports = [p for p in tool.System.get_ports(el1) if not tool.System.get_connected_port(p)]
|
||||
obj2_ports = [p for p in tool.System.get_ports(el2) if not tool.System.get_connected_port(p)]
|
||||
|
||||
if not obj1_ports or not obj2_ports:
|
||||
self.report({"ERROR"}, "Couldn't find free ports to connect.")
|
||||
return
|
||||
|
||||
for port1 in obj1_ports:
|
||||
port1_location = tool.Model.get_element_matrix(port1).translation
|
||||
for port2 in obj2_ports:
|
||||
port2_location = tool.Model.get_element_matrix(port2).translation
|
||||
if tool.Cad.are_vectors_equal(port1_location, port2_location):
|
||||
core.connect_port(tool.Ifc, port1, port2)
|
||||
return {"FINISHED"}
|
||||
|
||||
self.report({"ERROR"}, "Couldn't find any matching ports to connect.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class SetFlowDirection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.set_flow_direction"
|
||||
bl_label = "Set Flow Direction"
|
||||
|
||||
@@ -123,6 +123,7 @@ class BIM_PT_object_systems(Panel):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
for system in ObjectSystemData.data["systems"]:
|
||||
@@ -166,6 +167,7 @@ class BIM_PT_ports(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
total_ports = PortData.data["total_ports"]
|
||||
row.label(text=f"{total_ports} Ports Found", icon="PLUGIN")
|
||||
row.operator("bim.mep_connect_elements", text="", icon="PLUGIN")
|
||||
row.operator("bim.show_ports", icon="HIDE_OFF", text="")
|
||||
row.operator("bim.hide_ports", icon="HIDE_ON", text="")
|
||||
row.operator("bim.add_port", icon="ADD", text="")
|
||||
@@ -283,6 +285,7 @@ class BIM_UL_systems(UIList):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
@@ -315,6 +318,7 @@ class BIM_UL_object_systems(UIList):
|
||||
"IfcDistributionSystem": "NETWORK_DRIVE",
|
||||
"IfcDistributionCircuit": "DRIVER",
|
||||
"IfcBuildingSystem": "MOD_BUILD",
|
||||
"IfcBuiltSystem": "MOD_BUILD",
|
||||
"IfcZone": "CUBE",
|
||||
}
|
||||
if item:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user