Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0

# Conflicts:
#	.github/workflows/ci.yml
#	src/ifcparse/IfcParse.cpp
This commit is contained in:
Thomas Krijnen
2021-09-24 09:03:58 +02:00
510 changed files with 12218 additions and 3312 deletions
+2
View File
@@ -0,0 +1,2 @@
include src/bcf/v2/xsd/*.xsd
include src/bcf/v3/xsd/*.xsd
+12
View File
@@ -0,0 +1,12 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
+37
View File
@@ -0,0 +1,37 @@
[metadata]
name=bcf-client
version=0.0.1
author = Ifcopenshell
description = A simple Python implementation of BCF
url = https://github.com/IfcOpenShell/IfcOpenShell
project_urls =
Code=https://github.com/IfcOpenShell/IfcOpenShell
Issues=https://github.com/IfcOpenShell/IfcOpenShell/issues
long_description = file: README.md
long_description_content_type = text/markdown
classifiers =
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Operating System :: OS Independent
Programming Language :: Python :: 3
Topic :: Scientific/Engineering
Topic :: Utilities
keywords =
Python
file formats
engineering
[options]
package_dir =
= src
packages = find:
python_requires = >=3
install_requires=
xmlschema
include_package_data = True
[options.packages.find]
where = src
[flake8]
max-line-length = 120
ignore = E24, E121, E123, E126, E203, E226, E704, E741, W503, W504
+17
View File
@@ -0,0 +1,17 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
import os.path
import zipfile
import tempfile
+19
View File
@@ -0,0 +1,19 @@
# BCF - BCF Python library
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
import os
import uuid
import shutil
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
class Project:
def __init__(self):
self.project_id = ""
+19
View File
@@ -0,0 +1,19 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
import uuid
import time
import json
@@ -6,9 +25,8 @@ import requests
import webbrowser
import http.server
import base64
from werkzeug.datastructures import HeaderSet
import tempfile
import os
client_id, client_secret = "", ""
@@ -135,6 +153,7 @@ class BcfClient:
self.foundation_client = foundation_client
self.version_id = None
self.baseurl = None
self.filepath = tempfile.mkdtemp()
def set_version(self, version):
self.version_id = version["version_id"]
@@ -282,7 +301,7 @@ class BcfClient:
headers=headers,
)
# TODO: write to tmpdir
with open(f"{project_id}_{topic_id}_snippet.txt", "w") as f:
with open(os.path.join(self.filepath, f"{project_id}_{topic_id}_snippet.txt"), "wb") as f:
f.write(response.content.decode("utf-8"))
return response.status_code, response.content
@@ -544,7 +563,7 @@ class BcfClient:
f"{self.baseurl}/projects/{project_id}/topics/documents/{document_id}",
headers=headers,
)
with open(f"{project_id}_{topic_id}_{document_id}_document.txt", "w") as f:
with open(os.path.join(self.filepath, f"{project_id}_{topic_id}_{document_id}_document.txt"), "wb") as f:
f.write(response.content.decode("utf-8"))
return response.status_code, response.content
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
import os
import uuid
import shutil
@@ -1,3 +1,22 @@
# BCF - BCF Python library
# Copyright (C) 2021 Prabhat Singh <singh01prabhat@gmail.com>
#
# This file is part of BCF.
#
# BCF is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BCF 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BCF. If not, see <http://www.gnu.org/licenses/>.
class Project:
def __init__(self):
self.project_id = ""
+66 -2
View File
@@ -1,3 +1,21 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on 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
# GNU General Public License for more details.
#
# 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/>.
VERSION:=`date '+%y%m%d'`
PYVERSION:=py37
@@ -15,11 +33,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py37h5f1835d_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/30/c0/d0526314971fc661b083ab135747dc68446a3022686da8c16d25fcf6ef07/lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl
endif
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_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
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2
@@ -31,11 +51,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py37h2d7f23a_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/1e/3e/f0abc15d5dac50939bccc589aae336d5ead4c72e7ad1039a2e0f3630ea92/lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl
endif
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_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
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2
@@ -47,11 +69,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py37h839d6b1_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/9e/5e/171ee9d40a600f565fe691ec5bf7596247ec62cfb2edc00c91afe8ea837b/lxml-4.6.3-cp37-cp37m-win_amd64.whl
endif
ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-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
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2
@@ -69,7 +93,7 @@ endif
cp -r blenderbim/* dist/blenderbim/
# Provides IfcOpenShell Python functionality
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-$(PYNUMBER)-v0.6.0-2f3c79a-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-$(PYNUMBER)-v0.6.0-721fe47-$(PLATFORM)64.zip
cd dist/working && unzip ifcblender*
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
@@ -99,7 +123,7 @@ endif
cp dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/entity_instance.py dist/blenderbim/libs/site/packages/ifcopenshell/
cp dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/file.py dist/blenderbim/libs/site/packages/ifcopenshell/
# Provides bcf functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/src/bcf dist/blenderbim/libs/site/packages/
# Provides IFCClash functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcclash/ifcclash dist/blenderbim/libs/site/packages/
# Provides BIMTester functionality
@@ -332,6 +356,13 @@ endif
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
rm -rf dist/working
# Required by ids
mkdir dist/working
cd dist/working && wget $(LXML_URL)
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
cp -r dist/working/lxml dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by behave
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
@@ -413,6 +444,39 @@ endif
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
.PHONY: test
test:
make test-core
make test-bim
.PHONY: test-core
test-core:
pytest -p no:pytest-blender test/core
.PHONY: test-bim
test-bim:
pytest test/bim
.PHONY: qa
qa:
black .
pylint ./* --output-format=colorized --disable all --enable E --disable import-error
.PHONY: coverage
coverage:
coverage run --source blenderbim.core -m pytest -p no:pytest-blender test/core
coverage html
xdg-open htmlcov/index.html
.PHONY: license
license:
copyright-header --license GPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2021" --copyright-software "BlenderBIM Add-on" --copyright-software-description "OpenBIM Blender Add-on" -a ./ -o ./
.PHONY: clean
clean:
rm -rf dist
rm -rf htmlcov
.PHONY: dev
dev:
blender -p setup_pytest.py
+15 -22
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -17,10 +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 os
import sys
import site
bl_info = {
"name": "BlenderBIM",
"description": "Author, import, and export files in the " "Industry Foundation Classes (.ifc) file format",
"author": "Dion Moult, IfcOpenShell",
"description": "Author, import, and export data using the Industry Foundation Classes schema",
"author": "IfcOpenShell Contributors",
"blender": (2, 80, 0),
"version": (0, 0, 999999),
"location": "File > Export, File > Import, Scene / Object / Material / Mesh Properties",
@@ -28,25 +31,15 @@ bl_info = {
"category": "Import-Export",
}
import os
import site
if sys.modules.get("bpy", None):
# Process *.pth in /libs/site/packages to setup globally importable modules
# This is 3 levels deep as required by the static RPATH of ../../ from dependencies taken from Anaconda
site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
import blenderbim.bim
# process *.pth in /libs/site/packages to setup globally importable modules
# 3 levels deep required by occ static ../../ path
# TODO: 3 levels deep is no longer required as we no longer bundle OCC
cwd = os.path.dirname(os.path.realpath(__file__))
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
def register():
blenderbim.bim.register()
# main import
from .bim import *
# Explicitely expose bim.xx when imported with from blenderbim import *
# Other bim still are importable using explicit from blenderbim.bim import xxx
__all__ = ["export_ifc", "import_ifc"]
if __name__ == "__main__":
register()
def unregister():
blenderbim.bim.unregister()
+136 -142
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -17,157 +16,152 @@
# 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/>.
# Check if we are running in Blender before loading, to allow for multiprocessing
import sys
import bpy
import importlib
from . import handler, ui, prop, operator
bpy = sys.modules.get("bpy")
modules = {
"project": None,
"search": None,
"bcf": None,
"root": None,
"unit": None,
"model": None,
"georeference": None,
"context": None,
"drawing": None,
"attribute": None,
"type": None,
"spatial": None,
"void": None,
"aggregate": None,
"geometry": None,
"cobie": None,
"resource": None,
"cost": None,
"sequence": None,
"group": None,
"system": None,
"structural": None,
"boundary": None,
"profile": None,
"material": None,
"style": None,
"layer": None,
"owner": None,
"pset": None,
"qto": None,
"classification": None,
"constraint": None,
"document": None,
"pset_template": None,
"clash": None,
"lca": None,
"csv": None,
"bimtester": None,
"diff": None,
"patch": None,
"covetool": None,
"augin": None,
"debug": None,
}
if bpy is not None:
import bpy
import importlib
from . import handler, ui, prop, operator
for name in modules.keys():
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
modules = {
"project": None,
"search": None,
"bcf": None,
"root": None,
"unit": None,
"model": None,
"georeference": None,
"context": None,
"drawing": None,
"attribute": None,
"type": None,
"spatial": None,
"void": None,
"aggregate": None,
"geometry": None,
"cobie": None,
"resource": None,
"cost": None,
"sequence": None,
"group": None,
"system": None,
"structural": None,
"boundary": None,
"profile": None,
"material": None,
"style": None,
"layer": None,
"owner": None,
"pset": None,
"qto": None,
"classification": None,
"constraint": None,
"document": None,
"pset_template": None,
"clash": None,
"lca": None,
"csv": None,
"bimtester": None,
"diff": None,
"patch": None,
"covetool": None,
"augin": None,
"debug": None,
}
classes = [
operator.OpenUri,
operator.SelectDataDir,
operator.SelectSchemaDir,
operator.SelectIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.OpenUpstream,
operator.AddSectionPlane,
operator.RemoveSectionPlane,
operator.ReloadIfcFile,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SetOverrideColour,
operator.SetViewportShadowFromSun,
operator.SnapSpacesTogether,
operator.OverrideDelete,
prop.StrProperty,
prop.Attribute,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
prop.GlobalId,
prop.BIMObjectProperties,
prop.BIMMaterialProperties,
prop.BIMMeshProperties,
ui.BIM_PT_section_plane,
ui.BIM_UL_generic,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
]
for name in modules.keys():
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
for mod in modules.values():
classes.extend(mod.classes)
classes = [
operator.OpenUri,
operator.SelectDataDir,
operator.SelectSchemaDir,
operator.SelectIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.OpenUpstream,
operator.AddSectionPlane,
operator.RemoveSectionPlane,
operator.ReloadIfcFile,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SetOverrideColour,
operator.SetViewportShadowFromSun,
operator.LinkIfc,
operator.SnapSpacesTogether,
prop.StrProperty,
prop.Attribute,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
prop.GlobalId,
prop.BIMObjectProperties,
prop.BIMMaterialProperties,
prop.BIMMeshProperties,
ui.BIM_PT_section_plane,
ui.BIM_UL_generic,
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
]
for module in modules.values():
classes.extend(module.classes)
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
def menu_func_import(self, context):
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def menu_func_import(self, context):
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def on_register(scene):
handler.setDefaultProperties(scene)
bpy.app.handlers.depsgraph_update_post.remove(on_register)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.app.handlers.depsgraph_update_post.append(on_register)
bpy.app.handlers.undo_pre.append(handler.undo_pre)
bpy.app.handlers.undo_post.append(handler.undo_post)
bpy.app.handlers.redo_pre.append(handler.redo_pre)
bpy.app.handlers.redo_post.append(handler.redo_post)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(
type=prop.BIMObjectProperties
) # Check if we need this
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.PointLight.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
def on_register(scene):
handler.setDefaultProperties(scene)
bpy.app.handlers.depsgraph_update_post.remove(on_register)
for module in modules.values():
module.register()
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
del bpy.types.Collection.BIMObjectProperties # Check if we need this
del bpy.types.Material.BIMMaterialProperties
del bpy.types.Mesh.BIMMeshProperties
del bpy.types.Curve.BIMMeshProperties
del bpy.types.Camera.BIMMeshProperties
del bpy.types.PointLight.BIMMeshProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.app.handlers.depsgraph_update_post.append(on_register)
bpy.app.handlers.undo_pre.append(handler.undo_pre)
bpy.app.handlers.undo_post.append(handler.undo_post)
bpy.app.handlers.redo_pre.append(handler.redo_pre)
bpy.app.handlers.redo_post.append(handler.redo_post)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.Camera.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.PointLight.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
for module in reversed(list(modules.values())):
module.unregister()
for mod in modules.values():
mod.register()
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.ensureIfcExported)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Object.BIMObjectProperties
del bpy.types.Material.BIMObjectProperties
del bpy.types.Material.BIMMaterialProperties
del bpy.types.Mesh.BIMMeshProperties
del bpy.types.Curve.BIMMeshProperties
del bpy.types.Camera.BIMMeshProperties
del bpy.types.PointLight.BIMMeshProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
for mod in reversed(list(modules.values())):
mod.unregister()
@@ -0,0 +1,13 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((),'2;1');
FILE_NAME('EPset_Productivity.ifc','2020-01-01T00:00:00',(),(),'EPset_Productivity','EPset_Productivity',$);
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('24XxTA2ED4o83uiZBG59D_',$,'EPset_Productivity','',.PSET_TYPEDRIVENOVERRIDE.,'IfcConstructionEquipmentResource,IfcLaborResource',(#2,#3,#4));
#2=IFCSIMPLEPROPERTYTEMPLATE('3mL27FQ4X4MQn5fiGqIt8x',$,'BaseQuantityConsumed','',.P_SINGLEVALUE.,'IfcDuration',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1DdJt5_Ar2mR4UDpO$98Ws',$,'BaseQuantityProducedName','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('2OZPZ$Onb2gOvW20q7gacH',$,'BaseQuantityProducedValue','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
+2 -4
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -114,7 +113,7 @@ class IfcExporter:
IfcStore.edited_objs.clear()
def sync_object_placement(self, obj):
blender_matrix = np.matrix(obj.matrix_world)
blender_matrix = np.array(obj.matrix_world)
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if not hasattr(element, "ObjectPlacement"):
return
@@ -158,7 +157,6 @@ class IfcExporter:
else:
parent_collection = obj.users_collection[0]
parent_obj = bpy.data.objects.get(parent_collection.name)
if not parent_obj or not parent_obj.BIMObjectProperties.ifc_definition_id:
return
@@ -174,6 +172,7 @@ class IfcExporter:
try:
# This will throw an exception if the Blender object no longer exists
foo = obj.name
foo
return False
except:
return True
@@ -201,7 +200,6 @@ class IfcExportSettings:
@staticmethod
def factory(context, output_file, logger):
scene_bim = context.scene.BIMProperties
settings = IfcExportSettings()
settings.output_file = output_file
settings.logger = logger
-1
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
-1
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
+4 -2
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -19,8 +18,11 @@
import bpy
import uuid
import zipfile
import tempfile
import ifcopenshell
import blenderbim.bim.handler
from pathlib import Path
class IfcStore:
@@ -183,7 +185,7 @@ class IfcStore:
def commit_link_element(data):
obj = bpy.data.objects.get(data["obj"])
IfcStore.id_map[data["id"]] = obj
if data["guid"]:
if "guid" in data:
IfcStore.guid_map[data["guid"]] = obj
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
+213 -286
View File
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -17,31 +16,23 @@
# 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 ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.geolocation
import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.unit
import bpy
import bmesh
import os
import re
import bpy
import time
import bmesh
import shutil
import threading
import json
import time
import mathutils
import math
import multiprocessing
import zipfile
import tempfile
import numpy as np
from blenderbim.bim.module.drawing.prop import getDiagramScales
from pathlib import Path
from itertools import cycle
from datetime import datetime
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.selector
import ifcopenshell.util.geolocation
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.drawing.prop import get_diagram_scales
class FileCopy(threading.Thread):
@@ -172,12 +163,9 @@ class IfcImporter:
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.filter_mode = None
self.include_elements = set()
self.exclude_elements = set()
self.project = None
self.spatial_structure_elements = {}
self.elements = []
self.collections = {}
self.elements = set()
self.type_collection = None
self.type_products = {}
self.openings = {}
@@ -188,7 +176,6 @@ class IfcImporter:
self.added_data = {}
self.native_elements = set()
self.native_data = {}
self.aggregates = {}
self.material_creator = MaterialCreator(ifc_import_settings, self)
@@ -208,10 +195,6 @@ class IfcImporter:
bpy.context.window_manager.progress_begin(0, 100)
self.progress = 0
self.profile_code("Starting import process")
self.load_diff()
self.profile_code("Load diff")
self.purge_diff()
self.profile_code("Purge diffs")
self.load_file()
self.profile_code("Loading file")
self.calculate_unit_scale()
@@ -222,47 +205,41 @@ class IfcImporter:
self.profile_code("Set units")
self.create_project()
self.profile_code("Create project")
self.create_spatial_hierarchy()
self.profile_code("Create spatial hierarchy")
self.process_element_filter()
self.profile_code("Process element filter")
self.create_aggregates()
self.profile_code("Create aggregates")
self.create_aggregate_tree()
self.profile_code("Create aggregate tree")
self.create_collections()
self.profile_code("Create collections")
self.create_openings_collection()
self.profile_code("Create opening collection")
self.create_materials()
self.profile_code("Create materials")
self.create_styles()
self.profile_code("Create styles")
self.parse_native_elements()
self.profile_code("Parsing native elements")
self.create_grids()
self.profile_code("Create grids")
self.create_native_products()
self.profile_code("Create native products")
self.create_products()
self.profile_code("Create products")
self.create_empty_and_2d_elements()
self.profile_code("Create empty products")
self.create_type_products()
self.profile_code("Create type products")
self.create_annotation()
self.profile_code("Create annotation")
self.create_structural_elements()
self.profile_code("Create structural elements")
self.place_objects_in_spatial_tree()
self.profile_code("Placing objects in spatial tree")
self.parse_native_elements()
self.profile_code("Parsing native elements")
self.create_native_elements()
self.profile_code("Create native elements")
self.create_elements()
self.profile_code("Create elements")
self.create_grids()
self.profile_code("Create grids")
self.create_spatial_elements()
self.profile_code("Create spatial elements")
self.create_structural_items()
self.profile_code("Create structural items")
self.create_type_products()
self.profile_code("Create type products")
self.place_objects_in_collections()
self.profile_code("Place objects in collections")
if self.ifc_import_settings.should_merge_by_class:
self.merge_by_class()
self.profile_code("Merging by class")
elif self.ifc_import_settings.should_merge_by_material:
self.merge_by_material()
self.profile_code("Merging by material")
if self.ifc_import_settings.should_merge_materials_by_colour or (
self.ifc_import_settings.should_auto_set_workarounds and len(self.material_creator.materials) > 300
):
if self.ifc_import_settings.should_merge_materials_by_colour or len(self.material_creator.materials) > 300:
self.merge_materials_by_colour()
self.profile_code("Merging by colour")
self.add_project_to_scene()
@@ -290,32 +267,32 @@ class IfcImporter:
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
def process_element_filter(self):
if self.ifc_import_settings.ifc_import_filter == "NONE" or not self.ifc_import_settings.ifc_selector:
self.elements = self.file.by_type("IfcElement")
return
if self.ifc_import_settings.has_filter:
self.elements = set(self.ifc_import_settings.elements)
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
else:
self.elements = set(self.file.by_type("IfcElement"))
if self.file.schema == "IFC2X3":
self.spatial_elements = set(self.file.by_type("IfcSpatialStructureElement"))
else:
self.spatial_elements = set(self.file.by_type("IfcSpatialElement"))
selector = ifcopenshell.util.selector.Selector()
elements = selector.parse(self.file, self.ifc_import_settings.ifc_selector)
if self.ifc_import_settings.ifc_import_filter == "WHITELIST":
self.filter_mode = "WHITELIST"
self.include_elements = set(elements)
self.elements = self.include_elements
elif self.ifc_import_settings.ifc_import_filter == "BLACKLIST":
self.filter_mode = "BLACKLIST"
self.exclude_elements = set(elements)
self.elements = [e for e in self.file.by_type("IfcElement") if e not in self.exclude_elements]
def get_spatial_elements_filtered_by_elements(self, elements):
leaf_spatial_elements = set([ifcopenshell.util.element.get_container(e) for e in elements])
results = set()
for spatial_element in leaf_spatial_elements:
while True:
results.add(spatial_element)
spatial_element = ifcopenshell.util.element.get_aggregate(spatial_element)
if not spatial_element or spatial_element.is_a("IfcContext"):
break
return results
def parse_native_elements(self):
if self.filter_mode == "WHITELIST":
for element in self.include_elements:
if self.is_native(element):
self.native_elements[element.GlobalId] = element
self.include_elements -= self.native_elements
elif self.filter_mode == "BLACKLIST":
for element in set(self.file.by_type("IfcElement")) - self.exclude_elements:
if self.is_native(element):
self.native_elements.add(element)
self.exclude_elements |= self.native_elements
for element in self.elements:
if self.is_native(element):
self.native_elements.add(element)
self.elements -= self.native_elements
def is_native(self, element):
if (
@@ -408,7 +385,6 @@ class IfcImporter:
props.has_blender_offset = True
def get_offset_point(self):
offset_point = None
elements_checked = 0
# If more than these points aren't far away, the file probably isn't absolutely positioned
element_checking_threshold = 100
@@ -444,31 +420,33 @@ class IfcImporter:
if props.has_blender_offset:
if self.is_point_far_away((matrix[0, 3], matrix[1, 3], matrix[2, 3])):
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
return mathutils.Matrix(
ifcopenshell.util.geolocation.global2local(
matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
).tolist()
matrix = ifcopenshell.util.geolocation.global2local(
matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
)
else:
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
if self.ifc_import_settings.should_offset_model:
matrix[0, 3] += self.ifc_import_settings.model_offset_coordinates[0]
matrix[1, 3] += self.ifc_import_settings.model_offset_coordinates[1]
matrix[2, 3] += self.ifc_import_settings.model_offset_coordinates[2]
return mathutils.Matrix(matrix.tolist())
def find_decomposed_ifc_class(self, element, ifc_class):
results = []
if element.is_a(ifc_class):
return element
rel_aggregates = element.IsDecomposedBy
if not rel_aggregates:
return results
for rel_aggregate in rel_aggregates:
for part in rel_aggregate.RelatedObjects:
if part.is_a(ifc_class):
results.append(part)
results.extend(self.find_decomposed_ifc_class(part, ifc_class))
return results
result = self.find_decomposed_ifc_class(part, ifc_class)
if result:
return result
def create_grids(self):
grids = self.file.by_type("IfcGrid")
@@ -503,15 +481,8 @@ class IfcImporter:
grid_collection.objects.link(obj)
def create_type_products(self):
for collection in self.project["blender"].children:
if collection.name == "Types":
self.type_collection = collection
break
if not self.type_collection:
self.type_collection = bpy.data.collections.new("Types")
self.project["blender"].children.link(self.type_collection)
if self.filter_mode in ["WHITELIST", "BLACKLIST"]:
# TODO allow filtering of spatial elements too
if self.ifc_import_settings.has_filter:
type_products = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
else:
type_products = self.file.by_type("IfcTypeProduct")
@@ -553,7 +524,7 @@ class IfcImporter:
):
return representation_map
def create_native_products(self):
def create_native_elements(self):
total = 0
checkpoint = time.time()
bm = bmesh.new()
@@ -586,40 +557,51 @@ class IfcImporter:
bm.to_mesh(mesh)
bm.clear()
bm.free()
print("Done creating geometry")
def create_products(self):
def create_spatial_elements(self):
products = self.create_products(self.spatial_elements)
self.spatial_elements -= products
products = self.create_curve_products(self.spatial_elements)
self.spatial_elements -= products
for element in self.spatial_elements:
self.create_product(element)
def create_elements(self):
products = self.create_products(self.elements)
self.elements -= products
products = self.create_curve_products(self.elements)
self.elements -= products
for element in self.elements:
self.create_product(element)
def create_products(self, products):
results = set()
if not products:
return results
if self.ifc_import_settings.should_use_cpu_multiprocessing:
iterator = ifcopenshell.geom.iterator(
self.settings,
self.file,
multiprocessing.cpu_count(),
include=self.include_elements or None,
exclude=self.exclude_elements or None,
self.settings, self.file, multiprocessing.cpu_count(), include=products
)
else:
iterator = ifcopenshell.geom.iterator(
self.settings, self.file, include=self.include_elements or None, exclude=self.exclude_elements or None
)
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=products)
valid_file = iterator.initialize()
if not valid_file:
return False
return results
checkpoint = time.time()
total_created = 0
approx_total_products = len(self.include_elements) or len(self.file.by_type("IfcElement"))
total = 0
start_progress = self.progress
progress_range = 85 - start_progress
while True:
if total_created % 250 == 0:
total += 1
if total % 250 == 0:
print(
"{} / ~{} elements processed in {:.2f}s ...".format(
total_created, approx_total_products, time.time() - checkpoint
"{} ({}%) elements processed in {:.2f}s ...".format(
total, iterator.progress(), time.time() - checkpoint
)
)
checkpoint = time.time()
if approx_total_products:
self.update_progress(((total_created / approx_total_products) * progress_range) + start_progress)
#self.update_progress(((total / approx_total_products) * progress_range) + start_progress)
shape = iterator.get()
if shape:
product = self.file.by_id(shape.guid)
@@ -628,35 +610,18 @@ class IfcImporter:
if shape.context not in ["Body", "Facetation"] and IfcStore.get_element(shape.guid):
# We only load a single context, and we prioritise the Body context. See #1290.
pass
elif product.is_a("IfcAnnotation") and product.ObjectType == "DRAWING":
# We have already processed this during the create_annotation step
pass
else:
self.create_product(product, shape)
total_created += 1
results.add(product)
if not iterator.next():
break
print("Done creating geometry")
def create_empty_and_2d_elements(self):
curve_products = []
unadded_element_ids = set([e.id() for e in self.elements]) - set(self.added_data.keys())
for element_id in unadded_element_ids:
element = self.file.by_id(element_id)
if element.is_a("IfcPort"):
continue
if not element.Representation:
self.create_product(element)
else:
curve_products.append(element)
if curve_products:
self.create_curve_products(curve_products)
return results
def create_annotation(self):
self.create_curve_products(self.file.by_type("IfcAnnotation"))
def create_structural_elements(self):
def create_structural_items(self):
# Create structural collections
self.structural_member_collection = bpy.data.collections.new("Members")
self.structural_connection_collection = bpy.data.collections.new("Connections")
@@ -695,6 +660,9 @@ class IfcImporter:
self.link_element(product, obj)
def create_curve_products(self, products):
results = set()
if not products:
return results
if self.ifc_import_settings.should_use_cpu_multiprocessing:
iterator = ifcopenshell.geom.iterator(
self.settings_2d, self.file, multiprocessing.cpu_count(), include=products
@@ -703,7 +671,7 @@ class IfcImporter:
iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
valid_file = iterator.initialize()
if not valid_file:
return False
return results
checkpoint = time.time()
total = 0
while True:
@@ -713,10 +681,13 @@ class IfcImporter:
checkpoint = time.time()
shape = iterator.get()
if shape:
product = self.file.by_id(shape.guid)
self.create_product(self.file.by_id(shape.guid), shape)
results.add(product)
if not iterator.next():
break
print("Done creating geometry")
return results
def create_product(self, element, shape=None, mesh=None):
if element is None:
@@ -747,7 +718,7 @@ class IfcImporter:
if shape:
m = shape.transformation.matrix.data
# We use numpy here because Blender mathutils.Matrix is not accurate enough
mat = np.matrix(
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])
)
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, mat)
@@ -767,7 +738,7 @@ class IfcImporter:
def get_representation_item_material_name(self, item):
if not item.StyledByItem:
return
style_ids = [e.id() for e in self.ifc_importer.file.traverse(item.StyledByItem[0]) if e.is_a("IfcSurfaceStyle")]
style_ids = [e.id() for e in self.file.traverse(item.StyledByItem[0]) if e.is_a("IfcSurfaceStyle")]
return style_ids[0] if style_ids else None
def create_native_faceted_brep(self, element, mesh_name):
@@ -960,15 +931,10 @@ class IfcImporter:
return
self.openings[element.GlobalId] = obj
def load_diff(self):
if not self.ifc_import_settings.diff_file:
return
with open(self.ifc_import_settings.diff_file, "r") as file:
self.diff = json.load(file)
def load_file(self):
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
if not bpy.context.scene.BIMProperties.ifc_file:
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
self.file = IfcStore.get_file()
def calculate_unit_scale(self):
@@ -1004,10 +970,7 @@ class IfcImporter:
)
def create_project(self):
if self.file.schema == "IFC2X3":
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
else:
self.project = {"ifc": self.file.by_type("IfcContext")[0]}
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
self.project["blender"] = bpy.data.collections.new(
"{}/{}".format(self.project["ifc"].is_a(), self.project["ifc"].Name)
)
@@ -1015,66 +978,69 @@ class IfcImporter:
if obj:
self.project["blender"].objects.link(obj)
def create_spatial_hierarchy(self):
if self.project["ifc"].IsDecomposedBy:
for rel_aggregate in self.project["ifc"].IsDecomposedBy:
self.add_related_objects(self.project["blender"], rel_aggregate.RelatedObjects)
def create_collections(self):
if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
self.create_decomposition_collections()
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
self.create_spatial_decomposition_collections()
def add_related_objects(self, parent, related_objects):
def create_decomposition_collections(self):
self.create_spatial_decomposition_collections()
self.create_aggregate_collections()
def create_spatial_decomposition_collections(self):
for rel_aggregate in self.project["ifc"].IsDecomposedBy or []:
self.create_spatial_decomposition_collection(self.project["blender"], rel_aggregate.RelatedObjects)
self.create_type_collection()
def create_type_collection(self):
for collection in self.project["blender"].children:
if collection.name == "Types":
self.type_collection = collection
break
if not self.type_collection:
self.type_collection = bpy.data.collections.new("Types")
self.project["blender"].children.link(self.type_collection)
def create_spatial_decomposition_collection(self, parent, related_objects):
for element in related_objects:
if element not in self.spatial_elements:
continue
global_id = element.GlobalId
collection = bpy.data.collections.new(self.get_name(element))
self.spatial_structure_elements[global_id] = {"blender": collection}
self.collections[global_id] = collection
parent.children.link(collection)
obj = self.create_product(element)
if obj:
self.spatial_structure_elements[global_id]["blender_obj"] = obj
collection.objects.link(obj)
if element.IsDecomposedBy:
for rel_aggregate in element.IsDecomposedBy:
self.add_related_objects(collection, rel_aggregate.RelatedObjects)
self.create_spatial_decomposition_collection(collection, rel_aggregate.RelatedObjects)
def create_aggregates(self):
if self.filter_mode in ["WHITELIST", "BLACKLIST"]:
rel_aggregates = [e.IsDecomposedBy[0].RelatingObject for e in self.elements if e.IsDecomposedBy]
def create_aggregate_collections(self):
if self.ifc_import_settings.has_filter:
rel_aggregates = [e.IsDecomposedBy[0] for e in self.elements if e.IsDecomposedBy]
else:
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
if len(rel_aggregates) > 10000:
# More than 10,000 collections makes Blender unhappy
print("Falling back to SPATIAL_DECOMPOSITION collection mode")
print("Skipping aggregate collections for performance.")
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION"
else:
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
return
def create_aggregate_tree(self):
for aggregate in self.aggregates.values():
if aggregate["container"].is_a("IfcSpatialStructureElement"):
self.spatial_structure_elements[aggregate["container"].GlobalId]["blender"].children.link(
aggregate["blender"]
)
else:
self.aggregates[aggregate["container"].GlobalId]["blender"].children.link(aggregate["blender"])
aggregates = {}
for rel_aggregate in rel_aggregates:
element = rel_aggregate.RelatingObject
collection = bpy.data.collections.new(self.get_name(element))
aggregates[element.GlobalId] = {"element": element, "collection": collection}
self.collections[element.GlobalId] = collection
def create_aggregate(self, rel_aggregate):
element = rel_aggregate.RelatingObject
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
self.link_element(element, obj)
collection = bpy.data.collections.new(obj.name)
collection.objects.link(obj)
self.aggregates[element.GlobalId] = {
"blender": collection,
"blender_obj": obj,
"container": self.get_aggregate_container(element),
}
def get_aggregate_container(self, element):
if hasattr(element, "ContainedInStructure") and element.ContainedInStructure:
container = element.ContainedInStructure[0].RelatingStructure
elif hasattr(element, "Decomposes") and element.Decomposes:
container = element.Decomposes[0].RelatingObject
return container
for global_id, aggregate in aggregates.items():
parent = ifcopenshell.util.element.get_aggregate(aggregate["element"])
if parent:
self.collections[parent.GlobalId].children.link(aggregate["collection"])
continue
parent = ifcopenshell.util.element.get_container(aggregate["element"])
if parent:
self.collections[parent.GlobalId].children.link(aggregate["collection"])
def create_openings_collection(self):
self.opening_collection = bpy.data.collections.new("IfcOpeningElements")
@@ -1135,86 +1101,41 @@ class IfcImporter:
def get_name(self, element):
return "{}/{}".format(element.is_a(), element.Name)
def purge_diff(self):
if not self.diff:
return
objects_to_purge = []
for obj in bpy.data.objects:
if "GlobalId" not in obj.BIMObjectProperties.attributes:
continue
global_id = obj.BIMObjectProperties.attributes["GlobalId"].string_value
if global_id in self.diff["deleted"] or global_id in self.diff["changed"].keys():
objects_to_purge.append(obj)
bpy.ops.object.delete({"selected_objects": objects_to_purge})
def place_objects_in_spatial_tree(self):
def place_objects_in_collections(self):
for ifc_definition_id, obj in self.added_data.items():
if isinstance(obj, bpy.types.Object):
self.place_object_in_spatial_tree(self.file.by_id(ifc_definition_id), obj)
self.place_object_in_collection(self.file.by_id(ifc_definition_id), obj)
def place_object_in_spatial_tree(self, element, obj):
if element.is_a() in ["IfcProject", "IfcProjectLibrary"]:
def place_object_in_collection(self, element, obj):
if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
self.place_object_in_decomposition_collection(element, obj)
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
self.place_object_in_spatial_decomposition_collection(element, obj)
def place_object_in_decomposition_collection(self, element, obj):
if element.is_a("IfcProject"):
return
elif element.GlobalId in self.collections:
return self.collections[element.GlobalId].objects.link(obj)
elif getattr(element, "Decomposes", None):
aggregate = ifcopenshell.util.element.get_aggregate(element)
return self.collections[aggregate.GlobalId].objects.link(obj)
else:
return self.place_object_in_spatial_decomposition_collection(element, obj)
def place_object_in_spatial_decomposition_collection(self, element, obj):
if element.is_a("IfcProject"):
return
elif element.GlobalId in self.collections:
return self.collections[element.GlobalId].objects.link(obj)
elif element.is_a("IfcTypeObject"):
self.type_collection.objects.link(obj)
elif element.GlobalId in self.aggregates:
return
elif element.GlobalId in self.spatial_structure_elements:
if not obj.data:
return
# Since spatial structure elements are generated as empties, we'll replace it with the representation
spatial_obj = self.spatial_structure_elements[element.GlobalId]["blender_obj"]
spatial_collection = self.spatial_structure_elements[element.GlobalId]["blender"]
spatial_name = spatial_obj.name
spatial_collection.objects.link(obj)
bpy.data.objects.remove(spatial_obj)
obj.name = spatial_name
elif (
hasattr(element, "ContainedInStructure")
and element.ContainedInStructure
and element.ContainedInStructure[0].RelatingStructure
):
container = element.ContainedInStructure[0].RelatingStructure
if element.is_a("IfcGrid"):
grid_collection = bpy.data.collections.get(obj.name)
if grid_collection: # Just in case we ran into invalid grids from Revit
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
grid_collection.objects.link(obj)
else:
self.spatial_structure_elements[container.GlobalId]["blender"].objects.link(obj)
elif hasattr(element, "Decomposes") and element.Decomposes:
collection = None
if element.Decomposes[0].RelatingObject.is_a("IfcProject"):
collection = self.project["blender"]
elif element.Decomposes[0].RelatingObject.is_a("IfcSpatialStructureElement"):
if element.is_a("IfcSpatialStructureElement"):
global_id = element.GlobalId
if global_id in self.spatial_structure_elements:
if (
element.is_a("IfcSpatialStructureElement")
and "blender_obj" in self.spatial_structure_elements[global_id]
):
bpy.data.objects.remove(self.spatial_structure_elements[global_id]["blender_obj"])
collection = self.spatial_structure_elements[global_id]["blender"]
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
# TODO: refactor this to a more holistic collection mode feature
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
else:
aggregate = element.Decomposes[0].RelatingObject
aggregate_data = self.aggregates.get(aggregate.GlobalId)
if not aggregate_data:
return self.place_object_in_spatial_tree(aggregate, obj)
collection = aggregate_data["blender"]
if collection:
collection.objects.link(obj)
else:
self.ifc_import_settings.logger.error("An element could not be placed in the spatial tree %s", element)
return self.type_collection.objects.link(obj)
elif element.is_a("IfcOpeningElement"):
self.opening_collection.objects.link(obj)
return self.opening_collection.objects.link(obj)
elif element.is_a("IfcStructuralMember"):
self.structural_member_collection.objects.link(obj)
return self.structural_member_collection.objects.link(obj)
elif element.is_a("IfcStructuralConnection"):
self.structural_connection_collection.objects.link(obj)
return self.structural_connection_collection.objects.link(obj)
elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
view_collection = bpy.data.collections.get("Views")
if not view_collection:
@@ -1224,6 +1145,18 @@ class IfcImporter:
drawing_collection = bpy.data.collections.new("IfcGroup/" + group.Name)
view_collection.children.link(drawing_collection)
drawing_collection.objects.link(obj)
return
container = ifcopenshell.util.element.get_container(element)
if container:
if element.is_a("IfcGrid"): # TODO: refactor into a more holistic collection mode feature
grid_collection = bpy.data.collections.get(obj.name)
if grid_collection: # Just in case we run into invalid grids from Revit
self.collections[container.GlobalId].children.link(grid_collection)
grid_collection.objects.link(obj)
else:
self.collections[container.GlobalId].objects.link(obj)
else:
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
bpy.context.scene.collection.objects.link(obj)
@@ -1344,7 +1277,9 @@ class IfcImporter:
if "TargetView" in pset:
camera.BIMCameraProperties.target_view = pset["TargetView"]
if "Scale" in pset:
valid_scales = [i[0] for i in getDiagramScales(None, None) if pset["Scale"] == i[0].split("|")[-1]]
valid_scales = [
i[0] for i in get_diagram_scales(None, bpy.context) if pset["Scale"] == i[0].split("|")[-1]
]
if valid_scales:
camera.BIMCameraProperties.diagram_scale = valid_scales[0]
else:
@@ -1391,13 +1326,7 @@ class IfcImporter:
num_vertex_indices = len(geometry.faces)
mesh.vertices.add(num_vertices)
if self.ifc_import_settings.should_offset_model:
# Potentially, there is a smarter way to do this. See #1047
v_index = cycle((0, 1, 2))
verts = [v + self.ifc_import_settings.model_offset_coordinates[next(v_index)] for v in verts]
mesh.vertices.foreach_set("co", verts)
else:
mesh.vertices.foreach_set("co", verts)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", geometry.faces)
mesh.polygons.add(num_loops)
@@ -1472,7 +1401,6 @@ class IfcImportSettings:
self.logger = None
self.input_file = None
self.diff_file = None
self.should_auto_set_workarounds = True
self.should_use_cpu_multiprocessing = True
self.should_merge_by_class = False
self.should_merge_by_material = False
@@ -1482,13 +1410,12 @@ class IfcImportSettings:
self.angular_tolerance = 0.5
self.should_offset_model = False
self.model_offset_coordinates = (0, 0, 0)
self.ifc_import_filter = "NONE"
self.ifc_selector = ""
self.has_filter = None
self.elements = ""
self.collection_mode = "DECOMPOSITION"
@staticmethod
def factory(context, input_file, logger):
scene_bim = context.scene.BIMProperties
scene_diff = context.scene.DiffProperties
settings = IfcImportSettings()
settings.input_file = input_file
@@ -0,0 +1,17 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on 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
# GNU General Public License for more details.
#
# 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/>.
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -56,7 +55,9 @@ class BIM_PT_aggregate(Panel):
row = self.layout.row(align=True)
row.prop(props, "relating_object", text="")
if props.relating_object:
row.operator("bim.assign_object", icon="CHECKMARK", text="").relating_object = props.relating_object.name
row.operator(
"bim.assign_object", icon="CHECKMARK", text=""
).relating_object = props.relating_object.name
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
else:
row = self.layout.row(align=True)
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -119,9 +118,7 @@ class EditAttributes(bpy.types.Operator):
elif attribute["type"] == "enum":
attributes[attribute["name"]] = blender_attribute.enum_value
product = self.file.by_id(oprops.ifc_definition_id)
ifcopenshell.api.run(
"attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes}
)
ifcopenshell.api.run("attribute.edit_attributes", self.file, **{"product": product, "attributes": attributes})
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_attributes(obj=obj.name, obj_type=self.obj_type)
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -32,6 +31,7 @@ from bpy.props import (
CollectionProperty,
)
class BIMAttributeProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -68,6 +67,7 @@ class AuginCreateNewModel(bpy.types.Operator):
def execute(self, context):
import boto3
from botocore.config import Config
props = context.scene.AuginProperties
# Create project
@@ -135,7 +135,6 @@ class AuginCreateNewModel(bpy.types.Operator):
client.upload_file(context.scene.BIMProperties.ifc_file, result["s3_bucket"], result["model_path"])
client.upload_file(thumb_path, result["s3_bucket"], result["thumb_path"])
# Notify done
url = "https://server.auge.pro.br/API/v3/augin_rest.php/files_uploaded"
payload = {
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -20,6 +19,7 @@
import bcf
import bcf.v2.bcfxml
class BcfStore:
bcfxml = None
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -31,6 +30,7 @@ from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry
class NewBcfProject(bpy.types.Operator):
bl_idname = "bim.new_bcf_project"
bl_label = "New BCF Project"
@@ -79,7 +79,7 @@ class LoadBcfTopics(bpy.types.Operator):
context.scene.BCFProperties.topics.clear()
for index, topic_guid in enumerate(bcfxml.topics.keys()):
new = context.scene.BCFProperties.topics.add()
bpy.ops.bim.load_bcf_topic(topic_guid = topic_guid, topic_index = index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic_guid, topic_index=index)
return {"FINISHED"}
@@ -107,7 +107,7 @@ class LoadBcfTopic(bpy.types.Operator):
"modified_author": topic.modified_author,
"assigned_to": topic.assigned_to,
"due_date": topic.due_date,
"description": topic.description
"description": topic.description,
}
for key, value in data_map.items():
if value is not None:
@@ -128,7 +128,7 @@ class LoadBcfTopic(bpy.types.Operator):
"type": topic.bim_snippet.snippet_type,
"is_external": topic.bim_snippet.is_external,
"reference": topic.bim_snippet.reference,
"schema": topic.bim_snippet.reference_schema
"schema": topic.bim_snippet.reference_schema,
}
for key, value in data_map.items():
if value is not None:
@@ -141,7 +141,7 @@ class LoadBcfTopic(bpy.types.Operator):
"reference": doc.referenced_document,
"description": doc.description,
"guid": doc.guid,
"is_external": doc.is_external
"is_external": doc.is_external,
}
for key, value in data_map.items():
if value is not None:
@@ -152,7 +152,7 @@ class LoadBcfTopic(bpy.types.Operator):
new_related_topic = new.related_topics.add()
new_related_topic.name = related_topic.guid
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -287,11 +287,12 @@ class AddBcfBimSnippet(bpy.types.Operator):
@classmethod
def poll(cls, context):
return all((getattr(context.scene.BCFProperties, attr, False) for attr in (
"bim_snippet_reference",
"bim_snippet_schema",
"bim_snippet_type"
)))
return all(
(
getattr(context.scene.BCFProperties, attr, False)
for attr in ("bim_snippet_reference", "bim_snippet_schema", "bim_snippet_type")
)
)
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -303,7 +304,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
bim_snippet.reference_schema = props.bim_snippet_schema
bim_snippet.snippet_type = props.bim_snippet_type
bcfxml.add_bim_snippet(topic, bim_snippet)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
bim_snippet.reference = ""
bim_snippet.reference_schema = ""
bim_snippet.snippet_type = ""
@@ -316,7 +317,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
def poll(cls, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
@@ -347,7 +348,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
topic.related_topics.append(related_topic)
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.related_topic = ""
return {"FINISHED"}
@@ -516,7 +517,7 @@ class AddBcfReferenceLink(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
topic.reference_links.append(props.reference_link)
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.reference_link = ""
return {"FINISHED"}
@@ -539,7 +540,7 @@ class AddBcfDocumentReference(bpy.types.Operator):
document_reference.referenced_document = props.document_reference
document_reference.description = props.document_reference_description or None
bcfxml.add_document_reference(topic, document_reference)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.document_reference = ""
props.document_reference_description = ""
return {"FINISHED"}
@@ -668,7 +669,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_document_reference(topic, self.index)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -685,7 +686,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
del topic.related_topics[self.index]
bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -701,7 +702,7 @@ class RemoveBcfComment(bpy.types.Operator):
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_comment(self.comment_guid, topic)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -720,7 +721,7 @@ class EditBcfComment(bpy.types.Operator):
comment = topic.comments[self.comment_guid]
comment.comment = blender_comment.comment
bcfxml.edit_comment(comment, topic)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -750,7 +751,7 @@ class AddBcfComment(bpy.types.Operator):
comment.viewpoint = bcf.v2.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment)
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
props.comment = ""
props.has_related_viewpoint = False
return {"FINISHED"}
@@ -787,14 +788,12 @@ class ActivateBcfViewpoint(bpy.types.Operator):
cam_width = context.scene.render.resolution_x
cam_height = context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
if viewpoint.snapshot:
obj.data.show_background_images = True
obj.data.background_images.clear()
background = obj.data.background_images.new()
background.image = bpy.data.images.load(
os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot)
)
background.image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot))
src_width = background.image.size[0]
src_height = background.image.size[1]
src_aspect = src_width / src_height
@@ -839,19 +838,25 @@ class ActivateBcfViewpoint(bpy.types.Operator):
obj.data.angle = radians(camera.field_of_view)
else:
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2)))
obj.data.angle = 2 * atan(
(0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2))
)
z_axis = Vector((-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)).normalized()
z_axis = Vector(
(-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)
).normalized()
y_axis = Vector((camera.camera_up_vector.x, camera.camera_up_vector.y, camera.camera_up_vector.z)).normalized()
x_axis = y_axis.cross(z_axis).normalized()
rotation = Matrix((x_axis, y_axis, z_axis))
rotation.invert()
matrix = np.matrix((
[x_axis[0], y_axis[0], z_axis[0], camera.camera_view_point.x],
[x_axis[1], y_axis[1], z_axis[1], camera.camera_view_point.y],
[x_axis[2], y_axis[2], z_axis[2], camera.camera_view_point.z],
[0, 0, 0, 1],
))
matrix = np.matrix(
(
[x_axis[0], y_axis[0], z_axis[0], camera.camera_view_point.x],
[x_axis[1], y_axis[1], z_axis[1], camera.camera_view_point.y],
[x_axis[2], y_axis[2], z_axis[2], camera.camera_view_point.z],
[0, 0, 0, 1],
)
)
props = context.scene.BIMGeoreferenceProperties
if props.has_blender_offset:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -961,7 +966,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
stroke.points.add(len(viewpoint.lines) * 2)
coords = []
for l in viewpoint.lines:
coords.extend([l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z])
coords.extend(
[l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z]
)
stroke.points.foreach_set("co", coords)
def create_clipping_planes(self, viewpoint):
@@ -1025,6 +1032,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
t = tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3))
return [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
class OpenBcfReferenceLink(bpy.types.Operator):
bl_idname = "bim.open_bcf_reference_link"
bl_label = "Open BCF Reference Link"
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -73,7 +72,7 @@ def updateBcfTopicIsEditable(self, context):
def updateBcfCommentIsEditable(self, context):
if context.scene.BCFProperties.is_loaded and not self.is_editable:
bpy.ops.bim.edit_bcf_comment(comment_guid = self.name)
bpy.ops.bim.edit_bcf_comment(comment_guid=self.name)
def refreshBcfTopic(self, context):
@@ -91,7 +90,7 @@ class BcfLabel(PropertyGroup):
def getBcfViewpoints(self, context, force_update=False):
global bcfviewpoints_enum
if bcfviewpoints_enum is None or force_update: # Retrieving Viewpoints is slow. Make sure we only do when needed
if bcfviewpoints_enum is None or force_update: # Retrieving Viewpoints is slow. Make sure we only do when needed
bcfviewpoints_enum = []
props = context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -22,6 +21,7 @@ import bpy
from . import bcfstore
from bpy.types import Panel
class BIM_PT_bcf(Panel):
bl_label = "BCF Project"
bl_idname = "BIM_PT_bcf"
@@ -231,12 +231,12 @@ class BIM_PT_bcf_metadata(Panel):
row.operator("bim.add_bcf_document_reference")
layout.label(text="Related Topics:")
for index, related_topic in enumerate(topic.related_topics):
for index, related_topic in enumerate(topic.related_topics):
try:
row = layout.row(align=True)
op = row.operator(
"bim.view_bcf_topic",
text=f"Select {bcfxml.topics[related_topic.name.lower()].title}")
"bim.view_bcf_topic", text=f"Select {bcfxml.topics[related_topic.name.lower()].title}"
)
op.topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index
except KeyError:
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -21,7 +20,6 @@ import os
import bpy
import tempfile
import webbrowser
import ifcopenshell
try:
import bimtester
@@ -31,7 +29,6 @@ except:
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
from pathlib import Path
from itertools import cycle
from blenderbim.bim.ifc import IfcStore
@@ -42,7 +39,7 @@ class ExecuteBIMTester(bpy.types.Operator):
@classmethod
def poll(cls, context):
props = context.scene.BimTesterProperties
return props.ifc_file and props.feature
return (props.ifc_file or props.should_load_from_memory) and props.feature
def execute(self, context):
props = context.scene.BimTesterProperties
@@ -75,10 +72,6 @@ class BIMTesterPurge(bpy.types.Operator):
bl_label = "Purge Tests"
def execute(self, context):
filename = os.path.join(
context.scene.BimTesterProperties.features_dir,
context.scene.BimTesterProperties.features_file + ".feature",
)
cwd = os.getcwd()
os.chdir(context.scene.BimTesterProperties.features_dir)
bimtester.clean.TestPurger().purge()
@@ -243,19 +236,3 @@ class QAHelper:
is_in_scenario = False
destination.write(source_line)
os.remove(filename + "~")
colour_list = [
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
(0.699, 0.876, 0.54, 1),
(0.199, 0.629, 0.174, 1),
(0.983, 0.605, 0.602, 1),
(0.89, 0.101, 0.112, 1),
(0.989, 0.751, 0.427, 1),
(0.986, 0.497, 0.1, 1),
(0.792, 0.699, 0.839, 1),
(0.414, 0.239, 0.603, 1),
(0.993, 0.999, 0.6, 1),
(0.693, 0.349, 0.157, 1),
]
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -20,9 +19,7 @@
import bpy
from . import ui
classes = (
ui.BIM_PT_boundary,
)
classes = (ui.BIM_PT_boundary,)
def register():
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -232,9 +231,7 @@ class ExecuteIfcClash(bpy.types.Operator):
context.scene.render.resolution_x = 480
context.scene.render.resolution_y = 270
context.scene.render.image_settings.file_format = "PNG"
context.scene.render.filepath = os.path.join(
context.scene.BIMProperties.data_dir, "snapshot.png"
)
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True)
return context.scene.render.filepath
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -76,6 +75,7 @@ class EnableEditingClassification(bpy.types.Operator):
new.name = attribute.name()
new.is_null = classification_data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = "string"
if attribute.name() == "ReferenceTokens":
new.string_value = "" if new.is_null else json.dumps(classification_data[attribute.name()])
else:
@@ -162,6 +162,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
new.name = attribute.name()
new.is_null = reference_data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = "string"
new.string_value = "" if new.is_null else reference_data[attribute.name()]
props.active_reference_id = self.reference
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -69,13 +68,14 @@ class ExecuteIfcCobie(bpy.types.Operator):
def execute(self, context):
from cobie import IfcCobieParser
props = context.scene.COBieProperties
if props.should_load_from_memory:
output_dir = tempfile.gettempdir()
else:
output_dir = os.path.dirname(props.cobie_ifc_file)
output_dir = os.path.dirname(props.cobie_ifc_file)
output = os.path.join(output_dir, "output")
logger = logging.getLogger("IFCtoCOBie")
fh = logging.FileHandler(os.path.join(output_dir, "cobie.log"))
@@ -92,10 +92,10 @@ class ExecuteIfcCobie(bpy.types.Operator):
parser = IfcCobieParser(logger, selector)
ifc_file = IfcStore.get_file()
if not (ifc_file and props.should_load_from_memory):
ifc_file = props.cobie_ifc_file
parser.parse(
ifc_file,
props.cobie_types,
@@ -122,4 +122,3 @@ class ExecuteIfcCobie(bpy.types.Operator):
webbrowser.open("file://" + output_dir)
webbrowser.open("file://" + output_dir + "/cobie.log")
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -37,4 +36,3 @@ class COBieProperties(PropertyGroup):
cobie_components: StringProperty(default=".COBie", name="COBie Components")
cobie_json_file: StringProperty(default="", name="COBie JSON File")
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -36,11 +35,10 @@ class BIM_PT_cobie(Panel):
scene = context.scene
props = scene.COBieProperties
if IfcStore.get_file():
if IfcStore.get_file():
row = layout.row()
row.prop(props, "should_load_from_memory")
if not IfcStore.get_file() or not props.should_load_from_memory:
row = layout.row(align=True)
row.prop(props, "cobie_ifc_file")
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -187,17 +186,21 @@ class AssignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"constraint.assign_constraint",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"constraint.assign_constraint",
self.file,
**{
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -212,15 +215,19 @@ class UnassignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"constraint.unassign_constraint",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"constraint.unassign_constraint",
self.file,
**{
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -105,7 +104,7 @@ class BIM_PT_object_constraints(Panel):
constraint = Data.objectives[constraint_id]
icon = "LIGHT"
except:
pass # Metric not implemented
pass # Metric not implemented
row = self.layout.row(align=True)
row.label(text=constraint.get("Name") or "Unnamed")
row.operator("bim.unassign_constraint", text="", icon="X").constraint = constraint_id
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -26,7 +25,8 @@ classes = (
operator.EditCostSchedule,
operator.EditCostItem,
operator.EditCostItemQuantity,
operator.EditCostValue,
operator.EditCostItemValue,
operator.EditCostItemValueFormula,
operator.EnableEditingCostSchedule,
operator.EnableEditingCostItems,
operator.EnableEditingCostItem,
@@ -34,6 +34,7 @@ classes = (
operator.EnableEditingCostItemQuantity,
operator.EnableEditingCostItemValues,
operator.EnableEditingCostItemValue,
operator.EnableEditingCostItemValueFormula,
operator.DisableEditingCostItem,
operator.DisableEditingCostSchedule,
operator.DisableEditingCostItemQuantity,
@@ -63,6 +64,7 @@ classes = (
operator.LoadScheduleOfRates,
operator.ExpandCostItemRate,
operator.ContractCostItemRate,
operator.CalculateCostItemResourceValue,
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
@@ -27,6 +27,7 @@ from blenderbim.bim.ifc import IfcStore
from bpy_extras.io_utils import ImportHelper
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
from ifcopenshell.api.resource.data import Data as ResourceData
class AddCostSchedule(bpy.types.Operator):
@@ -590,20 +591,27 @@ class AddCostValue(bpy.types.Operator):
def _execute(self, context):
self.file = IfcStore.get_file()
parent = self.file.by_id(self.parent)
if self.cost_type == "FIXED":
category = None
attributes = {"AppliedValue": 0.0}
elif self.cost_type == "SUM":
category = "*"
attributes = {"Category": category}
elif self.cost_type == "CATEGORY":
category = self.cost_category
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category})
Data.load(self.file)
attributes = {"Category": category}
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent)
ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes=attributes)
if parent.is_a("IfcConstructionResource"):
ResourceData.load(self.file)
else:
Data.load(self.file)
return {"FINISHED"}
class RemoveCostItemValue(bpy.types.Operator):
bl_idname = "bim.remove_cost_item_value"
bl_idname = "bim.remove_cost_value"
bl_label = "Add Cost Item Value"
bl_options = {"REGISTER", "UNDO"}
parent: bpy.props.IntProperty()
@@ -614,13 +622,17 @@ class RemoveCostItemValue(bpy.types.Operator):
def _execute(self, context):
self.file = IfcStore.get_file()
parent = self.file.by_id(self.parent)
ifcopenshell.api.run(
"cost.remove_cost_item_value",
"cost.remove_cost_value",
self.file,
parent=self.file.by_id(self.parent),
parent=parent,
cost_value=self.file.by_id(self.cost_value),
)
Data.load(self.file)
if parent.is_a("IfcConstructionResource"):
ResourceData.load(self.file)
else:
Data.load(self.file)
return {"FINISHED"}
@@ -633,21 +645,25 @@ class EnableEditingCostItemValue(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.cost_value_attributes.clear()
self.props.active_cost_item_value_id = self.cost_value
self.props.active_cost_value_id = self.cost_value
self.props.cost_value_editing_type = "ATTRIBUTES"
data = Data.cost_values[self.cost_value]
blenderbim.bim.helper.import_attributes(
data["type"],
self.props.cost_value_attributes,
data,
lambda name, prop, data: self.import_attributes(name, prop, data, context)
data["type"],
self.props.cost_value_attributes,
data,
lambda name, prop, data: self.import_attributes(name, prop, data, context),
)
return {"FINISHED"}
def import_attributes(self, name, prop, data, context):
if name == "AppliedValue":
# TODO: for now, only support simple values
# TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure)
prop = self.props.cost_value_attributes.add()
prop.data_type = "float"
prop.name = "AppliedValue"
prop.is_optional = True
prop.float_value = 0.0 if prop.is_null else data[name]
return True
if (
@@ -699,13 +715,51 @@ class DisableEditingCostItemValue(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_value_id = 0
props.active_cost_value_id = 0
props.cost_value_editing_type = ""
return {"FINISHED"}
class EditCostValue(bpy.types.Operator):
class EnableEditingCostItemValueFormula(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_value_formula"
bl_label = "Enable Editing Cost Item Value Formula"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.cost_value_attributes.clear()
self.props.active_cost_value_id = self.cost_value
self.props.cost_value_editing_type = "FORMULA"
self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"]
return {"FINISHED"}
class EditCostItemValueFormula(bpy.types.Operator):
bl_idname = "bim.edit_cost_value_formula"
bl_label = "Edit Cost Value Formula"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_value_formula",
self.file,
**{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_item_value()
return {"FINISHED"}
class EditCostItemValue(bpy.types.Operator):
bl_idname = "bim.edit_cost_value"
bl_label = "Edit Cost Item Value"
bl_label = "Edit Cost Value"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
@@ -715,8 +769,8 @@ class EditCostValue(bpy.types.Operator):
def _execute(self, context):
props = context.scene.BIMCostProperties
attributes = blenderbim.bim.helper.export_attributes(
props.cost_value_attributes,
lambda attributes, prop: self.export_attributes(attributes, prop, context))
props.cost_value_attributes, lambda attributes, prop: self.export_attributes(attributes, prop, context)
)
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_value",
@@ -1000,3 +1054,21 @@ class ContractCostItemRate(bpy.types.Operator):
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(props.schedule_of_rates))
return {"FINISHED"}
class CalculateCostItemResourceValue(bpy.types.Operator):
bl_idname = "bim.calculate_cost_item_resource_value"
bl_label = "Calculate Cost Item Resource Value"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.calculate_cost_item_resource_value", self.file, cost_item=self.file.by_id(self.cost_item)
)
Data.load(self.file)
return {"FINISHED"}
@@ -79,7 +79,7 @@ def update_schedule_of_rates(self, context):
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(self.schedule_of_rates))
def getQuantityTypes(self, context):
def get_quantity_types(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.extend(
@@ -91,7 +91,7 @@ def getQuantityTypes(self, context):
return quantitytypes_enum
def getProductQuantityNames(self, context):
def get_product_quantity_names(self, context):
global productquantitynames_enum
global productquantitynames_count
ifc_file = IfcStore.get_file()
@@ -115,7 +115,7 @@ def getProductQuantityNames(self, context):
return productquantitynames_enum
def getProcessQuantityNames(self, context):
def get_process_quantity_names(self, context):
global processquantitynames_enum
global processquantitynames_id
ifc_file = IfcStore.get_file()
@@ -137,7 +137,7 @@ def getProcessQuantityNames(self, context):
return processquantitynames_enum
def getResourceQuantityNames(self, context):
def get_resource_quantity_names(self, context):
global resourcequantitynames_enum
global resourcequantitynames_id
ifc_file = IfcStore.get_file()
@@ -166,7 +166,7 @@ def update_active_cost_item_index(self, context):
bpy.ops.bim.load_cost_item_quantities()
def updateCostItemIdentification(self, context):
def update_cost_item_identification(self, context):
props = context.scene.BIMCostProperties
if not props.is_cost_update_enabled or self.identification == "XXX":
return
@@ -182,7 +182,7 @@ def updateCostItemIdentification(self, context):
attribute.string_value = self.identification
def updateCostItemName(self, context):
def update_cost_item_name(self, context):
props = context.scene.BIMCostProperties
if not props.is_cost_update_enabled or self.name == "Unnamed":
return
@@ -199,8 +199,8 @@ def updateCostItemName(self, context):
class CostItem(PropertyGroup):
name: StringProperty(name="Name", update=updateCostItemName)
identification: StringProperty(name="Identification", update=updateCostItemIdentification)
name: StringProperty(name="Name", update=update_cost_item_name)
identification: StringProperty(name="Identification", update=update_cost_item_identification)
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_children: BoolProperty(name="Has Children")
is_expanded: BoolProperty(name="Is Expanded")
@@ -229,10 +229,10 @@ class BIMCostProperties(PropertyGroup):
active_cost_item_index: IntProperty(name="Active Cost Item Index", update=update_active_cost_item_index)
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
product_quantity_names: EnumProperty(items=getProductQuantityNames, name="Product Quantity Names")
process_quantity_names: EnumProperty(items=getProcessQuantityNames, name="Process Quantity Names")
resource_quantity_names: EnumProperty(items=getResourceQuantityNames, name="Resource Quantity Names")
quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types")
product_quantity_names: EnumProperty(items=get_product_quantity_names, name="Product Quantity Names")
process_quantity_names: EnumProperty(items=get_process_quantity_names, name="Process Quantity Names")
resource_quantity_names: EnumProperty(items=get_resource_quantity_names, name="Resource Quantity Names")
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
cost_types: EnumProperty(
@@ -244,8 +244,10 @@ class BIMCostProperties(PropertyGroup):
name="Cost Types",
)
cost_category: StringProperty(name="Cost Category")
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
active_cost_value_id: IntProperty(name="Active Cost Item Value Id")
cost_value_editing_type: StringProperty(name="Cost Value Editing Type")
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
cost_value_formula: StringProperty(name="Cost Value Formula")
cost_column: StringProperty(name="Cost Column")
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
columns: CollectionProperty(name="Columns", type=StrProperty)
+89 -73
View File
@@ -16,10 +16,10 @@
# 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
import blenderbim.bim.module.cost.prop as CostProp
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
@@ -94,7 +94,7 @@ class BIM_PT_cost_schedules(Panel):
self.layout.template_list("BIM_UL_cost_columns", "", self.props, "columns", self.props, "active_column_index")
def draw_editable_cost_schedule_ui(self):
draw_attributes(self.props.cost_schedule_attributes, self.layout)
blenderbim.bim.helper.draw_attributes(self.props.cost_schedule_attributes, self.layout)
def draw_editable_cost_item_ui(self, cost_schedule_id):
row = self.layout.row(align=True)
@@ -140,7 +140,7 @@ class BIM_PT_cost_schedules(Panel):
self.draw_editable_cost_item_values_ui()
def draw_editable_cost_item_attributes_ui(self):
draw_attributes(self.props.cost_item_attributes, self.layout)
blenderbim.bim.helper.draw_attributes(self.props.cost_item_attributes, self.layout)
def draw_editable_cost_item_quantities_ui(self):
row = self.layout.row(align=True)
@@ -175,7 +175,7 @@ class BIM_PT_cost_schedules(Panel):
self.draw_editable_cost_item_quantity_ui(box)
def draw_editable_cost_item_quantity_ui(self, layout):
draw_attributes(self.props.quantity_attributes, self.layout)
blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout)
def draw_editable_cost_item_values_ui(self):
row = self.layout.row(align=True)
@@ -192,68 +192,46 @@ class BIM_PT_cost_schedules(Panel):
row = self.layout.row(align=True)
self.draw_readonly_cost_value_ui(row, cost_value_id)
if self.props.active_cost_item_value_id:
if self.props.cost_value_editing_type == "ATTRIBUTES":
box = self.layout.box()
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id])
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_value_id])
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
# This UI is really poor. Delete and start again.
cost_value = Data.cost_values[cost_value_id]
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
if cost_value["Category"]:
cost_value_label += " ({})".format(cost_value["Category"])
layout.label(text="", icon="DISC")
if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA":
layout.prop(self.props, "cost_value_formula", text="")
else:
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
cost_value_label += " = " + cost_value["Formula"]
layout.label(text=cost_value_label, icon="DISC")
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"])
def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, parent_id, level=1):
self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id)
cost_value = Data.cost_values[cost_value_id]
cost_value_label = ">" * level
cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"])
if cost_value["Category"]:
cost_value_label += " ({})".format(cost_value["Category"])
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"], level + 1)
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id:
if self.props.cost_value_editing_type == "ATTRIBUTES":
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
elif self.props.cost_value_editing_type == "FORMULA":
op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
elif self.props.active_cost_item_value_id:
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
elif self.props.active_cost_value_id:
op = layout.operator("bim.remove_cost_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
else:
op = layout.operator("bim.enable_editing_cost_item_value_formula", text="", icon="CON_TRANSLIKE")
op.cost_value = cost_value_id
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
op.cost_value = cost_value_id
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
op = layout.operator("bim.remove_cost_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
def draw_editable_cost_value_ui(self, layout, cost_value):
draw_attributes(self.props.cost_value_attributes, layout)
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, layout)
class BIM_PT_cost_item_types(Panel):
@@ -373,11 +351,29 @@ class BIM_PT_cost_item_quantities(Panel):
# Column1
col = grid.column()
has_quantity_names = CostProp.get_product_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Elements")
op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
op.cost_item = cost_item.ifc_definition_id
if context.selected_objects:
if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
op.related_object_type = "PRODUCT"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.product_quantity_names
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "PRODUCT"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = ""
op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE")
op.cost_item = cost_item.ifc_definition_id
op.related_object = 0
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -388,23 +384,32 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_product_index",
)
row2 = col.row(align=True)
row2.prop(self.props, "product_quantity_names", text="")
op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE")
op.cost_item = cost_item.ifc_definition_id
op.related_object = 0
if CostProp.productquantitynames_enum:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "PRODUCT"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.product_quantity_names
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "product_quantity_names", text="")
# Column2
col = grid.column()
has_quantity_names = CostProp.get_process_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Tasks")
tprops = context.scene.BIMTaskTreeProperties
wprops = context.scene.BIMWorkScheduleProperties
if tprops.tasks and wprops.active_task_index < len(tprops.tasks):
if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
op.related_object_type = "PROCESS"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.process_quantity_names
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "PROCESS"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = ""
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -415,20 +420,35 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_process_index",
)
row2 = col.row(align=True)
row2.prop(self.props, "process_quantity_names", text="")
if CostProp.processquantitynames_enum:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "PROCESS"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.process_quantity_names
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "process_quantity_names", text="")
# Column3
col = grid.column()
has_quantity_names = CostProp.get_resource_quantity_names(self, context)
row2 = col.row(align=True)
row2.label(text="Resources")
op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC")
op.cost_item = cost_item.ifc_definition_id
rtprops = context.scene.BIMResourceTreeProperties
rprops = context.scene.BIMResourceProperties
if rtprops.resources and rprops.active_resource_index < len(rtprops.resources):
if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
op.related_object_type = "RESOURCE"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.resource_quantity_names
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "RESOURCE"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = ""
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
@@ -439,13 +459,9 @@ class BIM_PT_cost_item_quantities(Panel):
"active_cost_item_resource_index",
)
row2 = col.row(align=True)
row2.prop(self.props, "resource_quantity_names", text="")
if CostProp.resourcequantitynames_enum:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
op.related_object_type = "RESOURCE"
op.cost_item = cost_item.ifc_definition_id
op.prop_name = self.props.resource_quantity_names
if has_quantity_names:
row2 = col.row()
row2.prop(self.props, "resource_quantity_names", text="")
class BIM_PT_cost_item_rates(Panel):
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -32,11 +31,9 @@ classes = (
)
def register():
bpy.types.Scene.CsvProperties = bpy.props.PointerProperty(type=prop.CsvProperties)
def unregister():
del bpy.types.Scene.CsvProperties
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -18,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import time
import logging
import ifcopenshell
import ifcopenshell.util.placement
@@ -88,10 +88,7 @@ class ProfileImportIFC(bpy.types.Operator):
import cProfile
import pstats
# For Windows
filepath = context.scene.BIMProperties.ifc_file.replace("\\", "\\\\")
cProfile.run(f"import bpy; bpy.ops.import_ifc.bim(filepath='{filepath}')", "blender.prof")
cProfile.run("import bpy; bpy.ops.bim.load_project_elements()", "blender.prof")
p = pstats.Stats("blender.prof")
p.sort_stats("cumulative").print_stats(50)
return {"FINISHED"}
@@ -116,9 +113,16 @@ class CreateAllShapes(bpy.types.Operator):
if element.GlobalId in excludes:
continue
print(f"{i}/{total}:", element)
start = time.time()
try:
shape = ifcopenshell.geom.create_shape(settings, element)
print("Success", len(shape.geometry.verts), len(shape.geometry.edges), len(shape.geometry.faces))
print(
"Success",
time.time() - start,
len(shape.geometry.verts),
len(shape.geometry.edges),
len(shape.geometry.faces),
)
except:
failures.append(element)
print("***** FAILURE *****")
@@ -164,9 +168,11 @@ class SelectHighPolygonMeshes(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
[o.select_set(True) for o in context.view_layer.objects
if o.type == "MESH"
and len(o.data.polygons) > context.scene.BIMDebugProperties.number_of_polygons]
[
o.select_set(True)
for o in context.view_layer.objects
if o.type == "MESH" and len(o.data.polygons) > context.scene.BIMDebugProperties.number_of_polygons
]
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -31,11 +30,9 @@ classes = (
)
def register():
bpy.types.Scene.DiffProperties = bpy.props.PointerProperty(type=prop.DiffProperties)
def unregister():
del bpy.types.Scene.DiffProperties
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -24,7 +23,6 @@ import json
from blenderbim.bim.ifc import IfcStore
class SelectDiffJsonFile(bpy.types.Operator):
bl_idname = "bim.select_diff_json_file"
bl_label = "Select Diff JSON File"
@@ -46,8 +44,8 @@ class VisualiseDiff(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
#ifc_file = IfcStore.get_file() # In case we get from Store
ifc_file = ifcopenshell.open(context.scene.DiffProperties.diff_new_file) # for Now refer to the new file
# ifc_file = IfcStore.get_file() # In case we get from Store
ifc_file = ifcopenshell.open(context.scene.DiffProperties.diff_new_file) # for Now refer to the new file
with open(context.scene.DiffProperties.diff_json_file, "r") as file:
diff = json.load(file)
for obj in context.visible_objects:
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -276,17 +275,21 @@ class AssignDocument(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"document.assign_document",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"document": self.file.by_id(self.document),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"document.assign_document",
self.file,
**{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -301,15 +304,19 @@ class UnassignDocument(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"document.unassign_document",
self.file,
**{
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"document": self.file.by_id(self.document),
}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run(
"document.unassign_document",
self.file,
**{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#

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