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

This commit is contained in:
Thomas Krijnen
2021-08-23 13:13:40 +02:00
330 changed files with 9637 additions and 2189 deletions
+84 -37
View File
@@ -7,6 +7,8 @@ import webbrowser
import http.server
import base64
from werkzeug.datastructures import HeaderSet
client_id, client_secret = "", ""
@@ -141,42 +143,67 @@ class BcfClient:
def get(self, endpoint, params=None, is_auth_required=False):
# TODO: handle error http status codes and raise exception. Follow error.json standard.
headers = {"Authorization": "Bearer " + self.foundation_client.get_access_token()}
return requests.get(f"{self.baseurl}{endpoint}", headers=headers, params=params or None).json()
response = requests.get(f"{self.baseurl}{endpoint}", headers=headers, params=params or None)
try:
response = requests.get(f"{self.baseurl}{endpoint}", headers=headers, params=params or None)
if response.status_code == 200:
return response.json()
response.raise_for_status()
except requests.exceptions.HTTPError as e:
print(f"message: {response.reason}' '{response.status_code}' '{ e }")
def post(self, endpoint, data=None, params=None):
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/json",
}
resp = requests.post(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
data=data or None,
)
return resp.status_code, resp.text
try:
response = requests.post(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
data=data or None,
)
if response.status_code == 201:
return response.status_code, response.text
response.raise_for_status()
except requests.exceptions.HTTPError as errh:
print(f"message: {response.reason}' '{response.status_code}, {errh}")
def put(self, endpoint, data=None, params=None):
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/json",
}
resp = requests.put(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
data=data or None,
)
return resp.status_code, resp.text
try:
response = requests.put(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
data=data or None,
)
if response.status_code == 200:
return response.status_code, response.text
response.raise_for_status()
except requests.exceptions.HTTPError as errh:
print(f"message: {response.reason}' '{response.status_code}, {errh}")
def delete(self, endpoint, params=None):
headers = {"Authorization": "Bearer " + self.foundation_client.get_access_token()}
resp = requests.put(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
)
return resp.status_code
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/json",
}
try:
response = requests.delete(
f"{self.baseurl}{endpoint}",
headers=headers,
params=params or None,
)
if response.status_code == 200:
return response.status_code, response.text
response.raise_for_status()
except requests.exceptions.HTTPError as errh:
print(f"message: {response.reason}' '{response.status_code}, {errh}")
def get_projects(self) -> list:
return self.get(
@@ -246,16 +273,30 @@ class BcfClient:
return self.delete(f"/projects/{project_id}/topics/{topic_id}")
def get_snippet(self, project_id="", topic_id="") -> str:
return self.get(
f"/projects/{project_id}/topics/{topic_id}/snippet",
{
"project_id": project_id,
"topic_id": topic_id,
},
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/octet-stream",
}
response = requests.get(
f"{self.baseurl}/projects/{project_id}/topics/{topic_id}/snippet",
headers=headers,
)
# TODO: write to tmpdir
with open(f"{project_id}_{topic_id}_snippet.txt", "w") as f:
f.write(response.content.decode("utf-8"))
return response.status_code, response.content
def update_snippet(self, project_id="", topic_id="", data=None):
return self.put(f"/projects/{project_id}/topics", data=data)
def update_snippet(self, project_id="", topic_id="", files=None, data=None):
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/octet-stream",
}
response = requests.put(
f"{self.baseurl}/projects/{project_id}/topics/{topic_id}/snippet",
headers=headers,
files=files,
)
return response.status_code
def get_files_information(self, project_id="") -> list:
return self.get(
@@ -478,6 +519,7 @@ class BcfClient:
project_id="",
topic_id="",
guid=None,
files=None,
data=None,
):
headers = {
@@ -488,18 +530,23 @@ class BcfClient:
f"/projects/{project_id}/topics/{topic_id}/documents",
data=data,
params={guid},
files=files,
headers=headers,
)
return response.status_code
def get_document(self, project_id="", topic_id="", document_id="") -> str:
return self.get(
f"/projects/{project_id}/topics/{topic_id}/documents/{document_id}",
{
"project_id": project_id,
"topic_id": topic_id,
"document_id": document_id,
},
headers = {
"Authorization": "Bearer " + self.foundation_client.get_access_token(),
"Content-type": "application/octet-stream",
}
response = requests.get(
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:
f.write(response.content.decode("utf-8"))
return response.status_code, response.content
def get_topics_events(self, project_id="") -> list:
return self.get(
+7
View File
@@ -402,6 +402,13 @@ endif
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Provides OpenLCA fucntionality
mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/85/33/f91b96e9e8608ff65a003b692e8a9cdd60f2178f60617e5b1d21334c009c/olca-ipc-0.0.10.tar.gz
cd dist/working && tar -xzvf olca-ipc*
cd dist/working/olca-ipc-0.0.10/ && cp -r olca ../../blenderbim/libs/site/packages/
rm -rf dist/working
cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py
cd dist && zip -r blenderbim-$(VERSION)-$(PYVERSION)-$(PLATFORM).zip ./*
rm -rf dist/blenderbim
-119
View File
@@ -1,119 +0,0 @@
import bpy
from bpy.types import Operator
class Object_OT_RenameObjects(Operator):
bl_idname = "object.renameobjects"
bl_label = "Rename Object(s)"
# Multi Object rename UI
BNameCB: bpy.props.BoolProperty(name="Base Name:")
BaseName: bpy.props.StringProperty(name="")
PreFixCB: bpy.props.BoolProperty(name="Prefix:")
PreFix: bpy.props.StringProperty(name="")
RemFrst: bpy.props.BoolProperty(name="Remove First")
DgtFrst: bpy.props.IntProperty(name="Digits")
SuffixCB: bpy.props.BoolProperty(name="Suffix")
Suffix: bpy.props.StringProperty(name="")
RemLast: bpy.props.BoolProperty(name="Remove Last")
DgtLast: bpy.props.IntProperty(name="Digits")
NumbredCB: bpy.props.BoolProperty(name="Numbred")
BaseNum: bpy.props.IntProperty(name="Base Number")
Step: bpy.props.IntProperty(name="Step", default=1)
findCB: bpy.props.BoolProperty(name="Replace")
find: bpy.props.StringProperty(name="")
replace: bpy.props.StringProperty(name="")
# Single rename UI
Name: bpy.props.StringProperty(name="Name")
def draw(self, ctx):
SelCount = len(bpy.context.selected_objects)
if SelCount > 1:
box = self.layout.box()
row = box.row()
row.prop(self, "BNameCB")
row.prop(self, "BaseName")
row = box.row()
row.prop(self, "PreFixCB")
row.prop(self, "PreFix")
row = box.row()
row.prop(self, "RemFrst")
row.prop(self, "DgtFrst")
row = box.row()
row.prop(self, "SuffixCB")
row.prop(self, "Suffix")
row = box.row()
row.prop(self, "RemLast")
row.prop(self, "DgtLast")
row = box.row(align=True)
row.prop(self, "NumbredCB")
row.prop(self, "BaseNum")
row.prop(self, "Step")
row = box.row(align=True)
row.prop(self, "findCB")
row.prop(self, "find")
row.prop(self, "replace")
if SelCount == 1:
box = self.layout.box()
row = box.row()
row.prop(self, "Name")
if SelCount == 0:
box = self.layout.box()
row = box.row()
row.label("No Selected Object")
def __init__(self):
if len(bpy.context.selected_objects) == 1:
self.Name = bpy.context.selected_objects[0].name
def execute(self, context):
SelCount = len(bpy.context.selected_objects)
if SelCount > 1:
SelObj = bpy.context.selected_objects
Index = self.BaseNum
for i in range(0, SelCount):
# Get Object Original Name #
NewName = SelObj[i].name
# Set the Base name #
if self.BNameCB:
NewName = self.BaseName
# Remove First characters #
if self.RemFrst:
NewName = NewName[self.DgtFrst : self.DgtFrst + len(NewName)]
# Remove Last Characters #
if self.RemLast:
NewName = NewName[1 : len(NewName) - self.DgtLast]
# Add Prefix to the new name #
if self.PreFixCB:
NewName = self.PreFix + NewName
# Add Suffix to the new name #
if self.SuffixCB:
NewName = NewName + self.Suffix
# Add Digits to end of new name #
if self.NumbredCB:
NewName += str(Index)
Index += self.Step
# Find and Replace #
if self.findCB:
NewName = NewName.replace(self.find, self.replace)
# Set the new name to the object #
SelObj[i].name = NewName
elif SelCount == 1:
bpy.context.selected_objects[0].name = self.Name
return {"FINISHED"}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def register():
bpy.utils.register_class(Object_OT_RenameObjects)
def unregister():
bpy.utils.unregister_class(Object_OT_RenameObjects)
if __name__ == "__main__":
register()
+19
View File
@@ -1,3 +1,22 @@
# 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/>.
bl_info = {
"name": "BlenderBIM",
"description": "Author, import, and export files in the " "Industry Foundation Classes (.ifc) file format",
+20
View File
@@ -1,3 +1,22 @@
# 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/>.
# Check if we are running in Blender before loading, to allow for multiprocessing
import sys
@@ -44,6 +63,7 @@ if bpy is not None:
"document": None,
"pset_template": None,
"clash": None,
"lca": None,
"csv": None,
"bimtester": None,
"diff": None,
@@ -1,3 +1,23 @@
/*
* 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/>.
*/
* { stroke-linecap: round; stroke-linejoin: round; }
*[id] { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
@@ -1,3 +1,23 @@
/*
* 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/>.
*/
.cut { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
import json
+71 -52
View File
@@ -1,7 +1,27 @@
# 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/>.
import bpy
import json
import addon_utils
import ifcopenshell.api.owner.settings
from blenderbim.bim.module.drawing.prop import RasterStyleProperty
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.owner.prop import getPersons, getOrganisations
@@ -133,8 +153,7 @@ def purge_module_data():
def loadIfcStore(scene):
IfcStore.purge()
purge_module_data()
ifc_file = IfcStore.get_file()
if not ifc_file:
if not IfcStore.get_file():
return
IfcStore.get_schema()
IfcStore.reload_linked_elements()
@@ -212,12 +231,12 @@ def setDefaultProperties(scene):
)
ifcopenshell.api.owner.settings.get_person = (
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_person))
if getPersons(None, bpy.context) and bpy.context.scene.BIMOwnerProperties.user_person
if getPersons(None, None) and bpy.context.scene.BIMOwnerProperties.user_person
else None
)
ifcopenshell.api.owner.settings.get_organisation = (
lambda ifc: ifc.by_id(int(bpy.context.scene.BIMOwnerProperties.user_organisation))
if getOrganisations(None, bpy.context) and bpy.context.scene.BIMOwnerProperties.user_organisation
if getOrganisations(None, None) and bpy.context.scene.BIMOwnerProperties.user_organisation
else None
)
ifcopenshell.api.owner.settings.get_application = get_application
@@ -227,30 +246,30 @@ def setDefaultProperties(scene):
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": False,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "FLAT",
"bpy.context.scene.display.shading.color_type": "SINGLE",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": False,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
RasterStyleProperty.WORLD_COLOR.value: (1, 1, 1),
RasterStyleProperty.RENDER_ENGINE.value: "BLENDER_WORKBENCH",
RasterStyleProperty.RENDER_TRANSPARENT.value: False,
RasterStyleProperty.SHADING_SHOW_OBJECT_OUTLINE.value: True,
RasterStyleProperty.SHADING_SHOW_CAVITY.value: False,
RasterStyleProperty.SHADING_CAVITY_TYPE.value: "BOTH",
RasterStyleProperty.SHADING_CURVATURE_RIDGE_FACTOR.value: 1,
RasterStyleProperty.SHADING_CURVATURE_VALLEY_FACTOR.value: 1,
RasterStyleProperty.VIEW_TRANSFORM.value: "Standard",
RasterStyleProperty.SHADING_LIGHT.value: "FLAT",
RasterStyleProperty.SHADING_COLOR_TYPE.value: "SINGLE",
RasterStyleProperty.SHADING_SINGLE_COLOR.value: (1, 1, 1),
RasterStyleProperty.SHADING_SHOW_SHADOWS.value: False,
RasterStyleProperty.SHADING_SHADOW_INTENSITY.value: 0.5,
RasterStyleProperty.DISPLAY_LIGHT_DIRECTION.value: (0.5, 0.5, 0.5),
RasterStyleProperty.VIEW_USE_CURVE_MAPPING.value: False,
RasterStyleProperty.OVERLAY_SHOW_WIREFRAMES.value: True,
RasterStyleProperty.OVERLAY_WIREFRAME_THRESHOLD.value: 0,
RasterStyleProperty.OVERLAY_SHOW_FLOOR.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_X.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_Y.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_Z.value: False,
RasterStyleProperty.OVERLAY_SHOW_OBJECT_ORIGINS.value: False,
RasterStyleProperty.OVERLAY_SHOW_RELATIONSHIP_LINES.value: False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
@@ -258,30 +277,30 @@ def setDefaultProperties(scene):
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": True,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "STUDIO",
"bpy.context.scene.display.shading.color_type": "MATERIAL",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": True,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
RasterStyleProperty.WORLD_COLOR.value: (1, 1, 1),
RasterStyleProperty.RENDER_ENGINE.value: "BLENDER_WORKBENCH",
RasterStyleProperty.RENDER_TRANSPARENT.value: False,
RasterStyleProperty.SHADING_SHOW_OBJECT_OUTLINE.value: True,
RasterStyleProperty.SHADING_SHOW_CAVITY.value: True,
RasterStyleProperty.SHADING_CAVITY_TYPE.value: "BOTH",
RasterStyleProperty.SHADING_CURVATURE_RIDGE_FACTOR.value: 1,
RasterStyleProperty.SHADING_CURVATURE_VALLEY_FACTOR.value: 1,
RasterStyleProperty.VIEW_TRANSFORM.value: "Standard",
RasterStyleProperty.SHADING_LIGHT.value: "STUDIO",
RasterStyleProperty.SHADING_COLOR_TYPE.value: "MATERIAL",
RasterStyleProperty.SHADING_SINGLE_COLOR.value: (1, 1, 1),
RasterStyleProperty.SHADING_SHOW_SHADOWS.value: True,
RasterStyleProperty.SHADING_SHADOW_INTENSITY.value: 0.5,
RasterStyleProperty.DISPLAY_LIGHT_DIRECTION.value: (0.5, 0.5, 0.5),
RasterStyleProperty.VIEW_USE_CURVE_MAPPING.value: False,
RasterStyleProperty.OVERLAY_SHOW_WIREFRAMES.value: True,
RasterStyleProperty.OVERLAY_WIREFRAME_THRESHOLD.value: 0,
RasterStyleProperty.OVERLAY_SHOW_FLOOR.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_X.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_Y.value: False,
RasterStyleProperty.OVERLAY_SHOW_AXIS_Z.value: False,
RasterStyleProperty.OVERLAY_SHOW_OBJECT_ORIGINS.value: False,
RasterStyleProperty.OVERLAY_SHOW_RELATIONSHIP_LINES.value: False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
+40 -36
View File
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import math
@@ -11,33 +30,30 @@ from blenderbim.bim.ifc import IfcStore
def draw_attributes(props, layout, copy_operator=None):
for attribute in props:
row = layout.row(align=True)
value = None
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
value = attribute.string_value
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
value = attribute.bool_value
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
value = attribute.int_value
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
value = attribute.float_value
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
value = attribute.enum_value
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = row.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.data = json.dumps({"name": attribute.name, "value": value, "is_null": attribute.is_null})
draw_attribute(attribute, row, copy_operator)
def draw_attribute(attribute, layout, copy_operator=None):
value_name = attribute.get_value_name()
if not value_name:
layout.label(text=attribute.name)
return
layout.prop(
attribute,
value_name,
text=attribute.name,
)
if attribute.is_optional:
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
op.data = json.dumps({"name": attribute.name, "value": attribute.get_value(), "is_null": attribute.is_null})
def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
if isinstance(data_type, tuple) or data_type == "entity":
callback(attribute.name(), None, data) if callback else None
continue
new = props.add()
@@ -69,18 +85,6 @@ def export_attributes(props, callback=None):
for prop in props:
is_handled_by_callback = callback(attributes, prop) if callback else False
if is_handled_by_callback:
continue # Our job is done
if prop.is_null:
attributes[prop.name] = None
elif prop.data_type == "string":
attributes[prop.name] = prop.string_value
elif prop.data_type == "boolean":
attributes[prop.name] = prop.bool_value
elif prop.data_type == "integer":
attributes[prop.name] = prop.int_value
elif prop.data_type == "float":
attributes[prop.name] = prop.float_value
elif prop.data_type == "enum":
attributes[prop.name] = prop.enum_value
continue # Our job is done
attributes[prop.name] = prop.get_value()
return attributes
+36 -2
View File
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import uuid
import ifcopenshell
@@ -41,11 +60,26 @@ class IfcStore:
IfcStore.path = bpy.context.scene.BIMProperties.ifc_file
if IfcStore.path:
try:
IfcStore.file = ifcopenshell.open(IfcStore.path)
IfcStore.load_file(IfcStore.path)
except:
IfcStore.file
pass
return IfcStore.file
@staticmethod
def load_file(path):
extension = path.split(".")[-1]
if extension.lower() == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
with zipfile.ZipFile(path, "r") as zip_ref:
zip_ref.extractall(unzipped_path)
for filename in Path(unzipped_path).glob("**/*.ifc"):
IfcStore.file = ifcopenshell.open(filename)
return
elif extension.lower() == "ifcxml":
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
elif extension.lower() == "ifc":
IfcStore.file = ifcopenshell.open(path)
@staticmethod
def get_schema():
if IfcStore.file is None:
+51 -38
View File
@@ -1,3 +1,22 @@
# 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/>.
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.geolocation
@@ -195,8 +214,6 @@ class IfcImporter:
self.profile_code("Purge diffs")
self.load_file()
self.profile_code("Loading file")
self.set_ifc_file()
self.profile_code("Setting file")
self.calculate_unit_scale()
self.profile_code("Calculate unit scale")
self.calculate_model_offset()
@@ -207,6 +224,8 @@ class IfcImporter:
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()
@@ -217,8 +236,6 @@ class IfcImporter:
self.profile_code("Create materials")
self.create_styles()
self.profile_code("Create styles")
self.process_element_filter()
self.profile_code("Process element filter")
self.parse_native_elements()
self.profile_code("Parsing native elements")
self.create_grids()
@@ -273,16 +290,20 @@ class IfcImporter:
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
def process_element_filter(self):
if not self.ifc_import_settings.ifc_selector:
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
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.exclude_elements = set(elements)
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 parse_native_elements(self):
if self.filter_mode == "WHITELIST":
@@ -620,16 +641,9 @@ class IfcImporter:
def create_empty_and_2d_elements(self):
curve_products = []
if self.filter_mode == "WHITELIST":
self.elements = self.include_elements
elif self.filter_mode == "BLACKLIST":
self.elements = [e for e in self.file.by_type("IfcElement") if e not in self.exclude_elements]
else:
self.elements = self.file.by_type("IfcElement")
for element in self.elements:
if element.id() in self.added_data:
continue
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:
@@ -954,25 +968,8 @@ class IfcImporter:
def load_file(self):
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
extension = self.ifc_import_settings.input_file.split(".")[-1]
if extension.lower() == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
with zipfile.ZipFile(self.ifc_import_settings.input_file, "r") as zip_ref:
zip_ref.extractall(unzipped_path)
for filename in Path(unzipped_path).glob("**/*.ifc"):
self.file = ifcopenshell.open(filename)
break
elif extension.lower() == "ifcxml":
self.file = ifcopenshell.file(
ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(self.ifc_import_settings.input_file)
)
elif extension.lower() == "ifc":
self.file = ifcopenshell.open(self.ifc_import_settings.input_file)
IfcStore.file = self.file
def set_ifc_file(self):
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
IfcStore.path = self.ifc_import_settings.input_file
self.file = IfcStore.get_file()
def calculate_unit_scale(self):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -1038,9 +1035,17 @@ class IfcImporter:
self.add_related_objects(collection, rel_aggregate.RelatedObjects)
def create_aggregates(self):
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
if self.filter_mode in ["WHITELIST", "BLACKLIST"]:
rel_aggregates = [e.IsDecomposedBy[0].RelatingObject 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")
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION"
else:
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
def create_aggregate_tree(self):
for aggregate in self.aggregates.values():
@@ -1191,8 +1196,15 @@ class IfcImporter:
):
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:
collection = self.aggregates[element.Decomposes[0].RelatingObject.GlobalId]["blender"]
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:
@@ -1472,6 +1484,7 @@ class IfcImportSettings:
self.model_offset_coordinates = (0, 0, 0)
self.ifc_import_filter = "NONE"
self.ifc_selector = ""
self.collection_mode = "DECOMPOSITION"
@staticmethod
def factory(context, input_file, logger):
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from ifcopenshell.api.aggregate.data import Data
from blenderbim.bim.ifc import IfcStore
@@ -38,7 +57,7 @@ class BIM_PT_aggregate(Panel):
row.prop(props, "relating_object", text="")
if props.relating_object:
row.operator("bim.assign_object", icon="CHECKMARK", text="").relating_object = props.relating_object.name
row.operator("bim.disable_editing_aggregate", icon="X", text="")
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import ifcopenshell
@@ -21,8 +40,7 @@ class EnableEditingAttributes(bpy.types.Operator):
obj = bpy.data.materials.get(self.obj)
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
while len(props.attributes) > 0:
props.attributes.remove(0)
props.attributes.clear()
for attribute in Data.products[oprops.ifc_definition_id]:
new = props.attributes.add()
if attribute["type"] == "entity" or (attribute["type"] == "list" and attribute["list_type"] == "entity"):
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.attribute.data import Data
@@ -91,6 +110,8 @@ class BIM_PT_material_attributes(Panel):
@classmethod
def poll(cls, context):
if not IfcStore.get_file():
return False
try:
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
except:
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
import json
@@ -1,3 +1,22 @@
# 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/>.
import bpy.props
import bpy.types
@@ -1,3 +1,22 @@
# 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/>.
import bpy.types
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -20,6 +39,7 @@ classes = (
operator.AddBcfLabel,
operator.AddBcfRelatedTopic,
operator.ViewBcfTopic,
operator.RemoveBcfTopic,
operator.RemoveBcfComment,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfReferenceLink,
@@ -1,3 +1,22 @@
# 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/>.
import bcf
import bcf.v2.bcfxml
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
import bcf
@@ -7,6 +26,7 @@ import numpy as np
import ifcopenshell
import ifcopenshell.util.unit
from . import bcfstore
import blenderbim.bim.module.bcf.prop as bcf_prop
from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry
@@ -56,13 +76,10 @@ class LoadBcfTopics(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_topics()
while len(context.scene.BCFProperties.topics) > 0:
context.scene.BCFProperties.topics.remove(0)
index = 0
for topic_guid in bcfxml.topics.keys():
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)
index += 1
return {"FINISHED"}
@@ -95,16 +112,17 @@ class LoadBcfTopic(bpy.types.Operator):
for key, value in data_map.items():
if value is not None:
setattr(new, key, str(value))
while len(new.reference_links) > 0:
new.reference_links.remove(0)
new.reference_links.clear()
for reference_link in topic.reference_links:
new2 = new.reference_links.add()
new2.name = reference_link
while len(new.labels) > 0:
new.labels.remove(0)
new_reference_link = new.reference_links.add()
new_reference_link.name = reference_link
new.labels.clear()
for label in topic.labels:
new2 = new.labels.add()
new2.name = label
new_label = new.labels.add()
new_label.name = label
if topic.bim_snippet:
data_map = {
"type": topic.bim_snippet.snippet_type,
@@ -115,10 +133,10 @@ class LoadBcfTopic(bpy.types.Operator):
for key, value in data_map.items():
if value is not None:
setattr(new.bim_snippet, key, value)
while len(new.document_references) > 0:
new.document_references.remove(0)
new.document_references.clear()
for doc in topic.document_references:
new2 = new.document_references.add()
new_document_references = new.document_references.add()
data_map = {
"reference": doc.referenced_document,
"description": doc.description,
@@ -127,12 +145,13 @@ class LoadBcfTopic(bpy.types.Operator):
}
for key, value in data_map.items():
if value is not None:
setattr(new2, key, value)
while len(new.related_topics) > 0:
new.related_topics.remove(0)
setattr(new_document_references, key, value)
new.related_topics.clear()
for related_topic in topic.related_topics:
new2 = new.related_topics.add()
new2.name = related_topic.guid
new_related_topic = new.related_topics.add()
new_related_topic.name = related_topic.guid
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
return {"FINISHED"}
@@ -147,8 +166,7 @@ class LoadBcfComments(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_comments(self.topic_guid)
blender_topic = context.scene.BCFProperties.topics.get(self.topic_guid)
while len(blender_topic.comments) > 0:
blender_topic.comments.remove(0)
blender_topic.comments.clear()
for comment in bcfxml.topics[self.topic_guid].comments.values():
new = blender_topic.comments.add()
data_map = {
@@ -196,7 +214,7 @@ class EditBcfTopicName(bpy.types.Operator):
def execute(self, context):
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic.title = blender_topic.title
@@ -211,7 +229,7 @@ class EditBcfTopic(bpy.types.Operator):
def execute(self, context):
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
@@ -225,7 +243,7 @@ class EditBcfTopic(bpy.types.Operator):
topic.topic_type = blender_topic.type or None
bcfxml.edit_topic(topic)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
props.refresh_topic(context)
return {"FINISHED"}
@@ -234,6 +252,7 @@ class SaveBcfProject(bpy.types.Operator):
bl_label = "Save BCF Project"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"})
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -250,11 +269,13 @@ class AddBcfTopic(bpy.types.Operator):
bl_label = "Add BCF Topic"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.author
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.add_topic()
new = context.scene.BCFProperties.topics.add()
new.name = "New Topic"
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -264,17 +285,28 @@ class AddBcfBimSnippet(bpy.types.Operator):
bl_label = "Add BCF BIM Snippet"
bl_options = {"REGISTER", "UNDO"}
@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"
)))
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bim_snippet = bcf.v2.data.BimSnippet()
bim_snippet.reference = blender_topic.bim_snippet_reference
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
bim_snippet.snippet_type = blender_topic.bim_snippet_type
bim_snippet.reference = props.bim_snippet_reference
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)
bim_snippet.reference = ""
bim_snippet.reference_schema = ""
bim_snippet.snippet_type = ""
return {"FINISHED"}
@@ -283,22 +315,40 @@ class AddBcfRelatedTopic(bpy.types.Operator):
bl_label = "Add BCF Related Topic"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@classmethod
def poll(cls, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
if not props.related_topic:
return False
if props.related_topic == blender_topic.title:
# Prevent adding self as related topic
return False
related_topic = None
for topic in bcfxml.topics.values():
if topic.title == blender_topic.related_topic:
if topic.title == props.related_topic:
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = topic.guid
break
if not related_topic:
return {"FINISHED"}
return False
if str(related_topic.guid) in [t.name for t in blender_topic.related_topics]:
# Prevent adding the same related topic more than once
return False
return True
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = next((t for t in bcfxml.topics.values() if t.title == props.related_topic)).guid
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)
props.related_topic = ""
return {"FINISHED"}
@@ -307,21 +357,25 @@ class AddBcfHeaderFile(bpy.types.Operator):
bl_label = "Add BCF Header File"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.file_reference
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
header_file = bcf.v2.data.HeaderFile()
header_file.reference = blender_topic.file_reference
header_file.reference = props.file_reference
if not os.path.exists(header_file.reference):
header_file.filename = header_file.reference
if len(blender_topic.file_ifc_project) == 22:
header_file.ifc_project = blender_topic.file_ifc_project
if len(blender_topic.file_ifc_spatial_structure_element) == 22:
header_file.ifc_spatial_structure_element = blender_topic.file_ifc_spatial_structure_element
if len(props.file_ifc_project) == 22:
header_file.ifc_project = props.file_ifc_project
if len(props.file_ifc_spatial_structure_element) == 22:
header_file.ifc_spatial_structure_element = props.file_ifc_spatial_structure_element
bcfxml.add_file(topic, header_file)
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
props.refresh_topic(context)
return {"FINISHED"}
@@ -333,8 +387,9 @@ class ViewBcfTopic(bpy.types.Operator):
def execute(self, context):
for index, topic in enumerate(context.scene.BCFProperties.topics):
if topic.guid.lower() == self.topic_guid.lower():
if topic.name.lower() == self.topic_guid.lower():
context.scene.BCFProperties.active_topic_index = index
break
return {"FINISHED"}
@@ -343,46 +398,50 @@ class AddBcfViewpoint(bpy.types.Operator):
bl_label = "Add BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.camera
def execute(self, context):
if not context.scene.camera:
return {"FINISHED"}
bcfxml = bcfstore.BcfStore.get_bcfxml()
blender_camera = context.scene.camera
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.v2.data.Viewpoint()
if context.scene.camera.data.type == "ORTHO":
if blender_camera.data.type == "ORTHO":
camera = bcf.v2.data.OrthogonalCamera()
camera.view_to_world_scale = context.scene.camera.data.ortho_scale
camera.view_to_world_scale = blender_camera.data.ortho_scale
viewpoint.orthogonal_camera = camera
elif context.scene.camera.data.type == "PERSP":
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.data.PerspectiveCamera()
camera.field_of_view = degrees(context.scene.camera.data.angle)
camera.field_of_view = degrees(blender_camera.data.angle)
viewpoint.perspective_camera = camera
camera.camera_view_point.x = context.scene.camera.location.x
camera.camera_view_point.y = context.scene.camera.location.y
camera.camera_view_point.z = context.scene.camera.location.z
direction = context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
camera.camera_view_point.x = blender_camera.location.x
camera.camera_view_point.y = blender_camera.location.y
camera.camera_view_point.z = blender_camera.location.z
direction = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
camera.camera_direction.x = direction.x
camera.camera_direction.y = direction.y
camera.camera_direction.z = direction.z
up = context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
up = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
camera.camera_up_vector.x = up.x
camera.camera_up_vector.y = up.y
camera.camera_up_vector.z = up.z
old_file_format = context.scene.render.image_settings.file_format
context.scene.render.image_settings.file_format = "PNG"
old_filepath = context.scene.render.filepath
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
blender_render = context.scene.render
old_file_format = blender_render.image_settings.file_format
blender_render.image_settings.file_format = "PNG"
old_filepath = blender_render.filepath
blender_render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True)
viewpoint.snapshot = context.scene.render.filepath
viewpoint.snapshot = blender_render.filepath
bcfxml.add_viewpoint(topic, viewpoint)
context.scene.render.filepath = old_filepath
context.scene.render.image_settings.file_format = old_file_format
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
blender_render.filepath = old_filepath
blender_render.image_settings.file_format = old_file_format
props.refresh_topic(context)
return {"FINISHED"}
@@ -391,14 +450,18 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bl_label = "Remove BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return bcf_prop.getBcfViewpoints(None, context)
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
viewpoint_guid = blender_topic.viewpoints
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_viewpoint(viewpoint_guid, topic)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
props.refresh_topic(context)
return {"FINISHED"}
@@ -411,10 +474,29 @@ class RemoveBcfFile(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_file(topic, self.index)
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
props.refresh_topic(context)
return {"FINISHED"}
class RemoveBcfTopic(bpy.types.Operator):
bl_idname = "bim.remove_bcf_topic"
bl_label = "Remove BCF Topic"
bl_options = {"REGISTER", "UNDO"}
guid: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.topics
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
topic_to_delete = props.active_topic
bcfxml.delete_topic(topic_to_delete.name)
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -423,17 +505,19 @@ class AddBcfReferenceLink(bpy.types.Operator):
bl_label = "Add BCF Reference Link"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.reference_link
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.reference_link:
return {"FINISHED"}
topic.reference_links.append(blender_topic.reference_link)
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)
blender_topic.reference_link = ""
props.reference_link = ""
return {"FINISHED"}
@@ -442,20 +526,22 @@ class AddBcfDocumentReference(bpy.types.Operator):
bl_label = "Add BCF Document Reference"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.document_reference
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.document_reference:
return {"FINISHED"}
document_reference = bcf.v2.data.DocumentReference()
document_reference.referenced_document = blender_topic.document_reference
document_reference.description = blender_topic.document_reference_description or None
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)
blender_topic.document_reference = ""
blender_topic.document_reference_description = ""
props.document_reference = ""
props.document_reference_description = ""
return {"FINISHED"}
@@ -464,18 +550,20 @@ class AddBcfLabel(bpy.types.Operator):
bl_label = "Add BCF Label"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.label
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.label:
return {"FINISHED"}
new = blender_topic.labels.add()
new.name = blender_topic.label
topic.labels.append(blender_topic.label)
new.name = props.label
topic.labels.append(props.label)
bcfxml.edit_topic(topic)
blender_topic.label = ""
props.label = ""
return {"FINISHED"}
@@ -487,7 +575,7 @@ class EditBcfReferenceLinks(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, reference_link in enumerate(topic.reference_links):
if reference_link == blender_topic.reference_links[index].name:
@@ -505,9 +593,11 @@ class EditBcfLabels(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, label in enumerate(blender_topic.labels):
if index >= len(topic.labels):
break
if label.name == topic.labels[index]:
continue
topic.labels[index] = blender_topic.labels[index].name
@@ -524,7 +614,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.reference_links[self.index]
bcfxml.edit_topic(topic)
@@ -541,7 +631,7 @@ class RemoveBcfLabel(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.labels[self.index]
bcfxml.edit_topic(topic)
@@ -557,7 +647,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_bim_snippet(topic)
blender_topic.bim_snippet.schema = ""
@@ -575,7 +665,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
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)
@@ -591,7 +681,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.related_topics[self.index]
bcfxml.edit_topic(topic)
@@ -608,7 +698,7 @@ class RemoveBcfComment(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
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)
@@ -624,7 +714,7 @@ class EditBcfComment(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
blender_comment = blender_topic.comments.get(self.comment_guid)
topic = bcfxml.topics[blender_topic.name]
comment = topic.comments[self.comment_guid]
@@ -640,20 +730,29 @@ class AddBcfComment(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
comment_guid: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
if not props.comment:
return False
if props.has_related_viewpoint and not bcf_prop.getBcfViewpoints(None, context):
return False
return True
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if not blender_topic.comment:
return {"FINISHED"}
comment = bcf.v2.data.Comment()
comment.comment = blender_topic.comment
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
comment.comment = props.comment
if props.has_related_viewpoint:
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)
props.comment = ""
props.has_related_viewpoint = False
return {"FINISHED"}
@@ -662,14 +761,20 @@ class ActivateBcfViewpoint(bpy.types.Operator):
bl_label = "Activate BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
return topic.viewpoints
def execute(self, context):
self.file = IfcStore.get_file()
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if not topic.viewpoints:
return {"FINISHED"}
viewpoint_guid = blender_topic.viewpoints
viewpoint = topic.viewpoints[viewpoint_guid]
@@ -682,11 +787,10 @@ 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
while len(obj.data.background_images) > 0:
obj.data.background_images.remove(obj.data.background_images[0])
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)
@@ -704,7 +808,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
area.spaces[0].region_3d.view_perspective = "CAMERA"
if self.file:
self.set_viewpoint_components(viewpoint)
self.set_viewpoint_components(viewpoint, context)
gp = bpy.data.grease_pencils.get("BCF")
if gp:
@@ -720,10 +824,10 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if viewpoint.bitmaps:
self.create_bitmaps(bcfxml, viewpoint, topic)
self.setup_camera(viewpoint, obj, cam_aspect)
self.setup_camera(viewpoint, obj, cam_aspect, context)
return {"FINISHED"}
def setup_camera(self, viewpoint, obj, cam_aspect):
def setup_camera(self, viewpoint, obj, cam_aspect, context):
if viewpoint.orthogonal_camera:
camera = viewpoint.orthogonal_camera
obj.data.type = "ORTHO"
@@ -748,7 +852,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
[x_axis[2], y_axis[2], z_axis[2], camera.camera_view_point.z],
[0, 0, 0, 1],
))
props = bpy.context.scene.BIMGeoreferenceProperties
props = context.scene.BIMGeoreferenceProperties
if props.has_blender_offset:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
matrix = ifcopenshell.util.geolocation.global2local(
@@ -761,7 +865,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
)
obj.matrix_world = Matrix(matrix.tolist())
def set_viewpoint_components(self, viewpoint):
def set_viewpoint_components(self, viewpoint, context):
if not viewpoint.components:
return
@@ -771,10 +875,10 @@ class ActivateBcfViewpoint(bpy.types.Operator):
exception_global_ids = [v.ifc_guid for v in viewpoint.components.visibility.exceptions]
if viewpoint.components.visibility.default_visibility:
old = bpy.context.area.type
bpy.context.area.type = "VIEW_3D"
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.hide_view_clear()
bpy.context.area.type = old
context.area.type = old
for global_id in exception_global_ids:
obj = IfcStore.get_element(global_id)
if obj:
@@ -786,35 +890,35 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if obj:
objs.append(obj)
if objs:
old = bpy.context.area.type
bpy.context.area.type = "VIEW_3D"
old = context.area.type
context.area.type = "VIEW_3D"
context_override = {}
context_override["object"] = context_override["active_object"] = objs[0]
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
bpy.ops.object.hide_view_set(context_override, unselected=True)
bpy.context.area.type = old
context.area.type = old
if viewpoint.components.view_setup_hints:
if not viewpoint.components.view_setup_hints.spaces_visible:
self.hide_spaces()
self.hide_spaces(context)
if viewpoint.components.view_setup_hints.openings_visible is not None:
self.set_openings_visibility(viewpoint.components.view_setup_hints.openings_visible)
self.set_openings_visibility(viewpoint.components.view_setup_hints.openings_visible, context)
else:
self.hide_spaces()
self.set_openings_visibility(False)
self.hide_spaces(context)
self.set_openings_visibility(False, context)
self.set_selection(viewpoint)
self.set_colours(viewpoint)
def hide_spaces(self):
old = bpy.context.area.type
bpy.context.area.type = "VIEW_3D"
def hide_spaces(self, context):
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.select_pattern(pattern="IfcSpace/*")
bpy.ops.object.hide_view_set({})
bpy.context.area.type = old
context.area.type = old
def set_openings_visibility(self, is_visible):
for collection in self.get_opening_collections():
def set_openings_visibility(self, is_visible, context):
for collection in self.get_opening_collections(context):
collection.hide_viewport = not is_visible
def set_selection(self, viewpoint):
@@ -835,9 +939,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if obj:
obj.color = self.hex_to_rgb(color)
def get_opening_collections(self):
def get_opening_collections(self, context):
collections = []
for collection in bpy.context.view_layer.layer_collection.children:
for collection in context.view_layer.layer_collection.children:
opening_collection = collection.children.get("IfcOpeningElements")
if opening_collection:
collections.append(opening_collection)
@@ -939,9 +1043,7 @@ class SelectBcfHeaderFile(bpy.types.Operator):
def execute(self, context):
if self.filepath:
props = context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.file_reference = self.filepath
context.scene.BCFProperties.file_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -957,9 +1059,7 @@ class SelectBcfBimSnippetReference(bpy.types.Operator):
def execute(self, context):
if self.filepath:
props = context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.bim_snippet_reference = self.filepath
context.scene.BCFProperties.bim_snippet_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -975,9 +1075,7 @@ class SelectBcfDocumentReference(bpy.types.Operator):
def execute(self, context):
if self.filepath:
props = context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
topic.document_reference = self.filepath
context.scene.BCFProperties.document_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import bcfstore
from blenderbim.bim.prop import StrProperty
@@ -33,12 +52,12 @@ def updateBcfLabel(self, context):
def updateBcfProjectName(self, context):
if context.scene.BCFProperties.is_loaded:
if self.is_loaded:
bpy.ops.bim.edit_bcf_project_name()
def updateBcfAuthor(self, context):
if context.scene.BCFProperties.is_loaded:
if self.is_loaded:
bpy.ops.bim.edit_bcf_author()
@@ -58,14 +77,8 @@ def updateBcfCommentIsEditable(self, context):
def refreshBcfTopic(self, context):
global bcfviewpoints_enum
bcfviewpoints_enum = None
props = context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = props.topics[props.active_topic_index]
header = bcfxml.get_header(topic.name)
getBcfViewpoints(self, context)
self.clear_input_fields()
getBcfViewpoints(None, context, force_update=True)
class BcfReferenceLink(PropertyGroup):
@@ -76,13 +89,13 @@ class BcfLabel(PropertyGroup):
name: StringProperty(name="Name", update=updateBcfLabel)
def getBcfViewpoints(self, context):
def getBcfViewpoints(self, context, force_update=False):
global bcfviewpoints_enum
if bcfviewpoints_enum is None:
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()
topic = props.topics[props.active_topic_index]
topic = props.active_topic
viewpoints = bcfxml.get_viewpoints(topic.name)
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints.keys())])
return bcfviewpoints_enum
@@ -127,28 +140,15 @@ class BcfTopic(PropertyGroup):
assigned_to: StringProperty(default="", name="Assigned To")
due_date: StringProperty(default="", name="Due Date")
description: StringProperty(default="", name="Description")
viewpoints: EnumProperty(items=getBcfViewpoints, name="BCF Viewpoints")
viewpoints: EnumProperty(items=lambda _, context: getBcfViewpoints(_, context), name="BCF Viewpoints")
files: CollectionProperty(name="Files", type=StrProperty)
file_reference: StringProperty(default="", name="Reference")
file_ifc_project: StringProperty(default="", name="IFC Project")
file_ifc_spatial_structure_element: StringProperty(default="", name="IFC Spatial Structure Element")
reference_links: CollectionProperty(name="Reference Links", type=BcfReferenceLink)
reference_link: StringProperty(default="", name="Reference Link")
labels: CollectionProperty(name="Labels", type=BcfLabel)
label: StringProperty(default="", name="Label")
bim_snippet: PointerProperty(type=BcfBimSnippet)
bim_snippet_type: StringProperty(default="", name="Type")
bim_snippet_reference: StringProperty(default="", name="Reference")
bim_snippet_schema: StringProperty(default="", name="Schema")
document_references: CollectionProperty(name="Document References", type=BcfDocumentReference)
document_reference: StringProperty(default="", name="Referenced Document")
document_reference_description: StringProperty(default="", name="Description")
related_topics: CollectionProperty(name="Related Topics", type=StrProperty)
related_topic: StringProperty(default="", name="Related Topic")
comments: CollectionProperty(name="Comments", type=BcfComment)
is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfTopicIsEditable)
comment: StringProperty(default="", name="Comment")
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
class BCFProperties(PropertyGroup):
@@ -158,3 +158,44 @@ class BCFProperties(PropertyGroup):
author: StringProperty(default="john@doe.com", name="Author Email", update=updateBcfAuthor)
topics: CollectionProperty(name="BCF Topics", type=BcfTopic)
active_topic_index: IntProperty(name="Active BCF Topic Index", update=refreshBcfTopic)
file_reference: StringProperty(default="", name="Reference")
file_ifc_project: StringProperty(default="", name="IFC Project")
file_ifc_spatial_structure_element: StringProperty(default="", name="IFC Spatial Structure Element")
reference_link: StringProperty(default="", name="Reference Link")
label: StringProperty(default="", name="Label")
bim_snippet_reference: StringProperty(default="", name="Reference")
bim_snippet_type: StringProperty(default="", name="Type")
bim_snippet_schema: StringProperty(default="", name="Schema")
document_reference: StringProperty(default="", name="Referenced Document")
document_reference_description: StringProperty(default="", name="Description")
related_topic: StringProperty(name="Related Topic")
comment: StringProperty(default="", name="Comment")
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
def clear_input_fields(self):
self.file_reference = ""
self.file_ifc_project = ""
self.file_ifc_spatial_structure_element = ""
self.reference_link = ""
self.label = ""
self.bim_snippet_reference = ""
self.bim_snippet_type = ""
self.bim_snippet_schema = ""
self.document_reference = ""
self.document_reference_description = ""
self.related_topic = ""
self.comment = ""
self.has_related_viewpoint = False
@property
def active_topic(self):
if len(self.topics) == 0:
return None
if self.active_topic_index < 0:
self.active_topic_index = 0
if self.active_topic_index >= len(self.topics):
self.active_topic_index = len(self.topics) - 1
return self.topics[self.active_topic_index]
def refresh_topic(self, context):
refreshBcfTopic(self, context)
+47 -21
View File
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
from . import bcfstore
@@ -39,12 +58,13 @@ class BIM_PT_bcf(Panel):
row.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
col = row.column(align=True)
col.operator("bim.add_bcf_topic", icon="ADD", text="")
col.operator("bim.remove_bcf_topic", icon="REMOVE", text="")
if props.active_topic_index < len(props.topics):
topic = props.topics[props.active_topic_index]
topic = props.active_topic
col.prop(topic, "is_editable", icon="CHECKMARK" if topic.is_editable else "GREASEPENCIL", icon_only=True)
if props.active_topic_index < len(props.topics):
topic = props.topics[props.active_topic_index]
topic = props.active_topic
row = layout.row()
row.enabled = topic.is_editable
row.prop(topic, "description", text="")
@@ -99,7 +119,7 @@ class BIM_PT_bcf_metadata(Panel):
layout.label(text="No BCF project is loaded")
return
topic = props.topics[props.active_topic_index]
topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcf_topic = bcfxml.topics[topic.name]
@@ -132,12 +152,12 @@ class BIM_PT_bcf_metadata(Panel):
row.label(text=f.ifc_spatial_structure_element)
row = layout.row(align=True)
row.prop(topic, "file_reference")
row.prop(props, "file_reference")
row.operator("bim.select_bcf_header_file", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "file_ifc_project")
row.prop(props, "file_ifc_project")
row = layout.row()
row.prop(topic, "file_ifc_spatial_structure_element")
row.prop(props, "file_ifc_spatial_structure_element")
row = layout.row()
row.operator("bim.add_bcf_header_file")
@@ -149,7 +169,7 @@ class BIM_PT_bcf_metadata(Panel):
row.operator("bim.open_uri", icon="URL", text="").uri = link.name
row.operator("bim.remove_bcf_reference_link", icon="X", text="").index = index
row = layout.row()
row.prop(topic, "reference_link")
row.prop(props, "reference_link")
row = layout.row()
row.operator("bim.add_bcf_reference_link")
@@ -159,7 +179,7 @@ class BIM_PT_bcf_metadata(Panel):
row.prop(label, "name", text="")
row.operator("bim.remove_bcf_label", icon="X", text="").index = index
row = layout.row()
row.prop(topic, "label")
row.prop(props, "label")
row = layout.row()
row.operator("bim.add_bcf_label")
@@ -180,12 +200,12 @@ class BIM_PT_bcf_metadata(Panel):
row.operator("bim.remove_bcf_bim_snippet", icon="X", text="")
else:
row = layout.row(align=True)
row.prop(topic, "bim_snippet_reference")
row.prop(props, "bim_snippet_reference")
row.operator("bim.select_bcf_bim_snippet_reference", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "bim_snippet_type")
row.prop(props, "bim_snippet_type")
row = layout.row()
row.prop(topic, "bim_snippet_schema")
row.prop(props, "bim_snippet_schema")
row = layout.row()
row.operator("bim.add_bcf_bim_snippet")
@@ -203,20 +223,26 @@ class BIM_PT_bcf_metadata(Panel):
row = box.row(align=True)
row.prop(doc, "description")
row = layout.row(align=True)
row.prop(topic, "document_reference")
row.prop(props, "document_reference")
row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(topic, "document_reference_description")
row.prop(props, "document_reference_description")
row = layout.row()
row.operator("bim.add_bcf_document_reference")
layout.label(text="Related Topics:")
for index, related_topic in enumerate(topic.related_topics):
row = layout.row(align=True)
row.operator("bim.view_bcf_topic", text=bcfxml.topics[related_topic.name.lower()].title).topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index
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}")
op.topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index
except KeyError:
pass
row = layout.row()
row.prop(topic, "related_topic")
row.prop(props, "related_topic")
row = layout.row()
row.operator("bim.add_bcf_related_topic")
@@ -245,7 +271,7 @@ class BIM_PT_bcf_comments(Panel):
row = layout.row()
row.prop(props, "comment_text_width")
topic = props.topics[props.active_topic_index]
topic = props.active_topic
for comment in topic.comments:
box = self.layout.box()
@@ -276,8 +302,8 @@ class BIM_PT_bcf_comments(Panel):
col.label(text=" ".join(line_words))
row = layout.row()
row.prop(topic, "comment")
row.prop(props, "comment")
row = layout.row()
row.prop(topic, "has_related_viewpoint")
row.prop(props, "has_related_viewpoint")
row = layout.row()
row.operator("bim.add_bcf_comment")
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
import tempfile
@@ -20,6 +39,11 @@ class ExecuteBIMTester(bpy.types.Operator):
bl_idname = "bim.execute_bim_tester"
bl_label = "Execute BIMTester"
@classmethod
def poll(cls, context):
props = context.scene.BimTesterProperties
return props.ifc_file and props.feature
def execute(self, context):
props = context.scene.BimTesterProperties
@@ -1,3 +1,22 @@
# 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/>.
import os
from pathlib import Path
from blenderbim.bim.prop import StrProperty
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import blenderbim.bim.helper
from bpy.types import Panel, UIList
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
import json
@@ -6,6 +25,7 @@ import logging
import numpy as np
from mathutils import Matrix
from math import radians
from blenderbim.bim.ifc import IfcStore
class ExportClashSets(bpy.types.Operator):
@@ -105,7 +125,7 @@ class AddClashSource(bpy.types.Operator):
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
clash_set = context.scene.BIMClashProperties.active_clash_set
source = getattr(clash_set, self.group).add()
return {"FINISHED"}
@@ -118,7 +138,7 @@ class RemoveClashSource(bpy.types.Operator):
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
clash_set = context.scene.BIMClashProperties.active_clash_set
getattr(clash_set, self.group).remove(self.index)
return {"FINISHED"}
@@ -133,7 +153,7 @@ class SelectClashSource(bpy.types.Operator):
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
clash_set = context.scene.BIMClashProperties.active_clash_set
getattr(clash_set, self.group)[self.index].name = self.filepath
return {"FINISHED"}
@@ -257,9 +277,7 @@ class SelectIfcClashResults(bpy.types.Operator):
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
with open(self.filepath) as f:
clash_sets = json.load(f)
clash_set_name = context.scene.BIMClashProperties.clash_sets[
context.scene.BIMClashProperties.active_clash_set_index
].name
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
global_ids = []
for clash_set in clash_sets:
if clash_set["name"] != clash_set_name:
@@ -284,6 +302,10 @@ class SmartClashGroup(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@classmethod
def poll(cls, context):
return context.scene.BIMClashProperties.clash_results_path
def execute(self, context):
import ifcclash
@@ -307,9 +329,7 @@ class SmartClashGroup(bpy.types.Operator):
with open(save_path, "w") as f:
f.write(json.dumps(smart_grouped_clashes))
clash_set_name = context.scene.BIMClashProperties.clash_sets[
context.scene.BIMClashProperties.active_clash_set_index
].name
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
# Reset the list of smart_clash_groups for the UI
context.scene.BIMClashProperties.smart_clash_groups.clear()
@@ -336,12 +356,14 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
bl_label = "Load Smart Groups for Active Clash Set"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BIMClashProperties.active_clash_set
def execute(self, context):
smart_groups_path = bpy.path.ensure_ext(context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
clash_set_name = context.scene.BIMClashProperties.clash_sets[
context.scene.BIMClashProperties.active_clash_set_index
].name
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
with open(smart_groups_path) as f:
smart_grouped_clashes = json.load(f)
@@ -370,11 +392,14 @@ class SelectSmartGroup(bpy.types.Operator):
bl_label = "Select Smart Group"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
def execute(self, context):
self.file = IfcStore.get_file()
# Select smart group in view
selected_smart_group = context.scene.BIMClashProperties.smart_clash_groups[
context.scene.BIMCLashProperties.active_smart_group_index
]
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
# print(selected_smart_group.number)
for obj in context.visible_objects:
@@ -440,8 +465,7 @@ class SetBlenderClashSetA(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
while len(context.scene.BIMClashProperties.blender_clash_set_a) > 0:
context.scene.BIMClashProperties.blender_clash_set_a.remove(0)
context.scene.BIMClashProperties.blender_clash_set_a.clear()
for obj in context.selected_objects:
new = context.scene.BIMClashProperties.blender_clash_set_a.add()
new.name = obj.name
@@ -454,8 +478,7 @@ class SetBlenderClashSetB(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
while len(context.scene.BIMClashProperties.blender_clash_set_b) > 0:
context.scene.BIMClashProperties.blender_clash_set_b.remove(0)
context.scene.BIMClashProperties.blender_clash_set_b.clear()
for obj in context.selected_objects:
new = context.scene.BIMClashProperties.blender_clash_set_b.add()
new.name = obj.name
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -50,3 +69,15 @@ class BIMClashProperties(PropertyGroup):
smart_clash_grouping_max_distance: IntProperty(
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
)
@property
def active_clash_set(self):
if not self.clash_sets:
return None
return self.clash_sets[self.active_clash_set_index]
@property
def active_smart_group(self):
if not self.smart_clash_groups:
return None
return self.smart_clash_groups[self.active_smart_group_index]
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from bpy.types import Panel
@@ -38,7 +57,7 @@ class BIM_PT_ifcclash(Panel):
layout.template_list("BIM_UL_clash_sets", "", props, "clash_sets", props, "active_clash_set_index")
if props.active_clash_set_index < len(props.clash_sets):
clash_set = props.clash_sets[props.active_clash_set_index]
clash_set = props.active_clash_set
row = layout.row(align=True)
row.prop(clash_set, "name")
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import ifcopenshell.api
@@ -50,8 +69,7 @@ class EnableEditingClassification(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMClassificationProperties
while len(props.classification_attributes) > 0:
props.classification_attributes.remove(0)
props.classification_attributes.clear()
classification_data = Data.classifications[self.classification]
for attribute in IfcStore.get_schema().declaration_by_name("IfcClassification").all_attributes():
new = props.classification_attributes.add()
@@ -135,8 +153,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
props = obj.BIMClassificationReferenceProperties
while len(props.reference_attributes) > 0:
props.reference_attributes.remove(0)
props.reference_attributes.clear()
reference_data = Data.references[self.reference]
for attribute in IfcStore.get_schema().declaration_by_name("IfcClassificationReference").all_attributes():
if attribute.name() == "ReferencedSource":
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from ifcopenshell.api.classification.data import Data
@@ -37,8 +56,7 @@ def updateClassification(self, context):
def getReferences(self, context, parent_id=None):
props = context.scene.BIMClassificationProperties
while len(props.available_library_references) > 0:
props.available_library_references.remove(0)
props.available_library_references.clear()
for reference in Data.library_file.by_id(parent_id).HasReferences:
new = props.available_library_references.add()
new.identification = reference.Identification or ""
@@ -1,3 +1,23 @@
# 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/>.
import blenderbim.bim.module.classification.prop as classification_prop
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.classification.data import Data
@@ -85,7 +105,7 @@ class BIM_PT_classification_references(Panel):
if self.oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
self.draw_add_ui()
self.draw_add_ui(context)
reference_ids = Data.products[self.oprops.ifc_definition_id]
if not reference_ids:
@@ -99,8 +119,8 @@ class BIM_PT_classification_references(Panel):
else:
self.draw_ui(reference_id, reference)
def draw_add_ui(self):
if not self.sprops.available_classifications:
def draw_add_ui(self, context):
if not classification_prop.getClassifications(self.sprops, context):
return
name = Data.library_classifications[int(self.sprops.available_classifications)]
@@ -127,7 +147,7 @@ class BIM_PT_classification_references(Panel):
row = self.layout.row(align=True)
row.prop(self.props.reference_attributes.get("Name"), "string_value", text="", icon="ASSET_MANAGER")
row.operator("bim.edit_classification_reference", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_classification_reference", text="", icon="X")
row.operator("bim.disable_editing_classification_reference", text="", icon="CANCEL")
for attribute in self.props.reference_attributes:
if attribute.name == "Name":
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import os
import logging
@@ -43,14 +62,19 @@ class ExecuteIfcCobie(bpy.types.Operator):
bl_label = "Execute IFCCOBie"
file_format: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
props = context.scene.COBieProperties
return props.should_load_from_memory or props.cobie_ifc_file
def execute(self, context):
from cobie import IfcCobieParser
props = context.scene.COBieProperties
output_dir = os.path.dirname(props.cobie_ifc_file)
if props.should_load_from_memory:
output_dir = tempfile.gettempdir()
else:
output_dir = os.path.dirname(props.cobie_ifc_file)
output = os.path.join(output_dir, "output")
logger = logging.getLogger("IFCtoCOBie")
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from bpy.types import PropertyGroup
from bpy.props import (
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import ifcopenshell.api
@@ -14,8 +33,7 @@ class LoadObjectives(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMConstraintProperties
while len(props.constraints) > 0:
props.constraints.remove(0)
props.constraints.clear()
for constraint_id, constraint in Data.objectives.items():
new = props.constraints.add()
new.name = constraint["Name"] or "Unnamed"
@@ -44,8 +62,7 @@ class EnableEditingConstraint(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMConstraintProperties
while len(props.constraint_attributes) > 0:
props.constraint_attributes.remove(0)
props.constraint_attributes.clear()
if props.is_editing == "IfcObjective":
data = Data.objectives[self.constraint]
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -1,5 +1,25 @@
# 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/>.
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.constraint.data import Data
@@ -44,14 +64,7 @@ class BIM_PT_constraints(Panel):
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
for attribute in self.props.constraint_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(self.props.constraint_attributes, self.layout)
class BIM_PT_object_constraints(Panel):
@@ -102,7 +115,7 @@ class BIM_PT_object_constraints(Panel):
row = self.layout.row(align=True)
icon = "LIGHT" if self.props.is_adding == "IfcObjective" else "FILE_HIDDEN"
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
row.operator("bim.disable_assigning_constraint", text="", icon="X")
row.operator("bim.disable_assigning_constraint", text="", icon="CANCEL")
self.layout.template_list(
"BIM_UL_object_constraints",
"",
@@ -125,7 +138,7 @@ class BIM_UL_constraints(UIList):
if context.scene.BIMConstraintProperties.active_constraint_id == item.ifc_definition_id:
if context.scene.BIMConstraintProperties.is_editing == "IfcObjective":
row.operator("bim.edit_objective", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_constraint", text="", icon="X")
row.operator("bim.disable_editing_constraint", text="", icon="CANCEL")
elif context.scene.BIMConstraintProperties.active_constraint_id:
row.operator("bim.remove_constraint", text="", icon="X").constraint = item.ifc_definition_id
else:
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from ifcopenshell.api.context.data import Data
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -26,6 +45,8 @@ classes = (
operator.ExpandCostItem,
operator.ContractCostItem,
operator.RemoveCostItem,
operator.AssignCostItemType,
operator.UnassignCostItemType,
operator.AssignCostItemQuantity,
operator.UnassignCostItemQuantity,
operator.AddCostItemQuantity,
@@ -37,14 +58,24 @@ classes = (
operator.SelectCostScheduleProducts,
operator.ImportCostScheduleCsv,
operator.LoadCostItemQuantities,
operator.LoadCostItemTypes,
operator.AssignCostValue,
operator.LoadScheduleOfRates,
operator.ExpandCostItemRate,
operator.ContractCostItemRate,
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
prop.BIMCostProperties,
ui.BIM_PT_cost_schedules,
ui.BIM_PT_cost_item_quantities,
ui.BIM_PT_cost_item_types,
ui.BIM_PT_cost_item_rates,
ui.BIM_UL_cost_items,
ui.BIM_UL_cost_columns,
ui.BIM_UL_cost_item_quantities,
ui.BIM_UL_cost_item_types,
ui.BIM_UL_cost_item_rates,
)
@@ -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/>.
import os
import bpy
import json
@@ -43,6 +61,7 @@ class EditCostSchedule(bpy.types.Operator):
**{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
purge()
bpy.ops.bim.disable_editing_cost_schedule()
return {"FINISHED"}
@@ -75,8 +94,7 @@ class EnableEditingCostSchedule(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.active_cost_schedule_id = self.cost_schedule
while len(self.props.cost_schedule_attributes) > 0:
self.props.cost_schedule_attributes.remove(0)
self.props.cost_schedule_attributes.clear()
self.enable_editing_cost_schedule()
self.props.is_editing = "COST_SCHEDULE"
return {"FINISHED"}
@@ -105,8 +123,7 @@ class EnableEditingCostItems(bpy.types.Operator):
self.props = context.scene.BIMCostProperties
self.props.is_cost_update_enabled = False
self.props.active_cost_schedule_id = self.cost_schedule
while len(self.props.cost_items) > 0:
self.props.cost_items.remove(0)
self.props.cost_items.clear()
self.contracted_cost_items = json.loads(self.props.contracted_cost_items)
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
@@ -145,8 +162,6 @@ class EnableEditingCostItems(bpy.types.Operator):
for related_object_id in cost_item["IsNestedBy"]:
self.create_new_cost_item_li(related_object_id, level_index + 1)
return {"FINISHED"}
class DisableEditingCostSchedule(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_schedule"
@@ -260,8 +275,7 @@ class EnableEditingCostItem(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMCostProperties
while len(props.cost_item_attributes) > 0:
props.cost_item_attributes.remove(0)
props.cost_item_attributes.clear()
data = Data.cost_items[self.cost_item]
blenderbim.bim.helper.import_attributes("IfcCostItem", props.cost_item_attributes, data)
@@ -303,6 +317,63 @@ class EditCostItem(bpy.types.Operator):
return {"FINISHED"}
class AssignCostItemType(bpy.types.Operator):
bl_idname = "bim.assign_cost_item_type"
bl_label = "Assign Cost Item Type Product"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
prop_name: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
cost_item = self.file.by_id(self.cost_item)
for obj in context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if product.is_a("IfcTypeProduct"):
ifcopenshell.api.run(
"control.assign_control", self.file, relating_control=cost_item, related_object=product
)
Data.load(self.file)
bpy.ops.bim.load_cost_item_types()
return {"FINISHED"}
class UnassignCostItemType(bpy.types.Operator):
bl_idname = "bim.unassign_cost_item_type"
bl_label = "Unassign Cost Item Type"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
cost_item = self.file.by_id(self.cost_item)
if self.related_object:
products = [self.file.by_id(self.related_object)]
else:
for obj in context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if product.is_a("IfcTypeProduct"):
products.append(product)
for product in products:
ifcopenshell.api.run(
"control.unassign_control", self.file, relating_control=cost_item, related_object=product
)
Data.load(self.file)
bpy.ops.bim.load_cost_item_types()
return {"FINISHED"}
class AssignCostItemQuantity(bpy.types.Operator):
bl_idname = "bim.assign_cost_item_quantity"
bl_label = "Assign Cost Item Quantity"
@@ -367,7 +438,7 @@ class UnassignCostItemQuantity(bpy.types.Operator):
else:
products = [
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
for o in bpy.context.selected_objects
for o in context.selected_objects
if o.BIMObjectProperties.ifc_definition_id
]
ifcopenshell.api.run(
@@ -465,8 +536,7 @@ class EnableEditingCostItemQuantity(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMCostProperties
while len(self.props.quantity_attributes) > 0:
self.props.quantity_attributes.remove(0)
self.props.quantity_attributes.clear()
self.props.active_cost_item_quantity_id = self.physical_quantity
data = Data.physical_quantities[self.physical_quantity]
blenderbim.bim.helper.import_attributes(data["type"], self.props.quantity_attributes, data)
@@ -536,6 +606,7 @@ class RemoveCostItemValue(bpy.types.Operator):
bl_idname = "bim.remove_cost_item_value"
bl_label = "Add Cost Item Value"
bl_options = {"REGISTER", "UNDO"}
parent: bpy.props.IntProperty()
cost_value: bpy.props.IntProperty()
def execute(self, context):
@@ -543,7 +614,12 @@ class RemoveCostItemValue(bpy.types.Operator):
def _execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=self.file.by_id(self.cost_value))
ifcopenshell.api.run(
"cost.remove_cost_item_value",
self.file,
parent=self.file.by_id(self.parent),
cost_value=self.file.by_id(self.cost_value),
)
Data.load(self.file)
return {"FINISHED"}
@@ -556,17 +632,19 @@ class EnableEditingCostItemValue(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMCostProperties
while len(self.props.cost_value_attributes) > 0:
self.props.cost_value_attributes.remove(0)
self.props.cost_value_attributes.clear()
self.props.active_cost_item_value_id = self.cost_value
data = Data.cost_values[self.cost_value]
blenderbim.bim.helper.import_attributes(
data["type"], self.props.cost_value_attributes, data, self.import_attributes
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):
def import_attributes(self, name, prop, data, context):
if name == "AppliedValue":
# TODO: for now, only support simple values
prop.data_type = "float"
@@ -574,7 +652,7 @@ class EnableEditingCostItemValue(bpy.types.Operator):
return True
if (
name == "UnitBasis"
and Data.cost_schedules[bpy.context.scene.BIMCostProperties.active_cost_schedule_id]["PredefinedType"]
and Data.cost_schedules[context.scene.BIMCostProperties.active_cost_schedule_id]["PredefinedType"]
== "SCHEDULEOFRATES"
):
prop = self.props.cost_value_attributes.add()
@@ -586,17 +664,28 @@ class EnableEditingCostItemValue(bpy.types.Operator):
prop.float_value = data["UnitBasis"]["ValueComponent"] or 0
else:
prop.float_value = 0
prop = self.props.cost_value_attributes.add()
prop.name = "UnitBasisUnit"
prop.data_type = "enum"
prop.is_null = prop.is_optional = False
units = {}
for unit_id, unit in UnitData.units.items():
if unit.get("UnitType", None) in ["AREAUNIT", "LENGTHUNIT", "TIMEUNIT", "VOLUMEUNIT", "MASSUNIT"]:
name = unit["Name"]
if unit.get("Prefix", None):
name = f"(unit['Prefix']) {name}"
units[unit_id] = f"{unit['UnitType']} / {name}"
if unit.get("UnitType", None) in [
"AREAUNIT",
"LENGTHUNIT",
"TIMEUNIT",
"VOLUMEUNIT",
"MASSUNIT",
"USERDEFINED",
]:
if unit["type"] == "IfcContextDependentUnit":
units[unit_id] = f"{unit['UnitType']} / {unit['Name']}"
else:
name = unit["Name"]
if unit.get("Prefix", None):
name = f"(unit['Prefix']) {name}"
units[unit_id] = f"{unit['UnitType']} / {name}"
prop.enum_items = json.dumps(units)
if data["UnitBasis"] and data["UnitBasis"]["UnitComponent"]:
prop.enum_value = str(data["UnitBasis"]["UnitComponent"])
@@ -625,7 +714,9 @@ class EditCostValue(bpy.types.Operator):
def _execute(self, context):
props = context.scene.BIMCostProperties
attributes = blenderbim.bim.helper.export_attributes(props.cost_value_attributes, self.export_attributes)
attributes = blenderbim.bim.helper.export_attributes(
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",
@@ -636,7 +727,7 @@ class EditCostValue(bpy.types.Operator):
bpy.ops.bim.disable_editing_cost_item_value()
return {"FINISHED"}
def export_attributes(self, attributes, prop):
def export_attributes(self, attributes, prop, context):
if prop.name == "UnitBasisValue":
if prop.is_null:
attributes["UnitBasis"] = None
@@ -644,7 +735,7 @@ class EditCostValue(bpy.types.Operator):
attributes["UnitBasis"] = {
"ValueComponent": prop.float_value or 1,
"UnitComponent": IfcStore.get_file().by_id(
int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
int(context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
),
}
return True
@@ -719,8 +810,12 @@ class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper):
bl_options = {"REGISTER", "UNDO"}
filename_ext = ".csv"
filter_glob: bpy.props.StringProperty(default="*.csv", options={"HIDDEN"})
is_schedule_of_rates: bpy.props.BoolProperty(name="Is Schedule Of Rates", default=False)
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
from ifc5d.csv2ifc import Csv2Ifc
self.file = IfcStore.get_file()
@@ -728,8 +823,11 @@ class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper):
csv2ifc = Csv2Ifc()
csv2ifc.csv = self.filepath
csv2ifc.file = self.file
csv2ifc.is_schedule_of_rates = self.is_schedule_of_rates
csv2ifc.execute()
Data.load(IfcStore.get_file())
UnitData.load(IfcStore.get_file())
purge()
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
@@ -771,12 +869,9 @@ class LoadCostItemQuantities(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
while len(self.props.cost_item_products) > 0:
self.props.cost_item_products.remove(0)
while len(self.props.cost_item_processes) > 0:
self.props.cost_item_processes.remove(0)
while len(self.props.cost_item_resources) > 0:
self.props.cost_item_resources.remove(0)
self.props.cost_item_products.clear()
self.props.cost_item_processes.clear()
self.props.cost_item_resources.clear()
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
for control_id, quantity_ids in Data.cost_items[ifc_definition_id]["Controls"].items():
related_object = self.file.by_id(control_id)
@@ -793,3 +888,115 @@ class LoadCostItemQuantities(bpy.types.Operator):
total_quantity += self.file.by_id(quantity_id)[3]
new.total_quantity = total_quantity
return {"FINISHED"}
class LoadCostItemTypes(bpy.types.Operator):
bl_idname = "bim.load_cost_item_types"
bl_label = "Load Cost Item Types"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
self.props.cost_item_type_products.clear()
# TODO implement process and resource types
# self.props.cost_item_processes.clear()
# self.props.cost_item_resources.clear()
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
for control_id, quantity_ids in Data.cost_items[ifc_definition_id]["Controls"].items():
related_object = self.file.by_id(control_id)
if related_object.is_a("IfcTypeProduct"):
new = self.props.cost_item_type_products.add()
# TODO implement process and resource types
# elif related_object.is_a("IfcProcess"):
# new = self.props.cost_item_processes.add()
# elif related_object.is_a("IfcResource"):
# new = self.props.cost_item_resources.add()
new.ifc_definition_id = control_id
new.name = related_object.Name or "Unnamed"
return {"FINISHED"}
class AssignCostValue(bpy.types.Operator):
bl_idname = "bim.assign_cost_value"
bl_label = "Assign Cost Value"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
cost_rate: 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.assign_cost_value",
self.file,
cost_item=self.file.by_id(self.cost_item),
cost_rate=self.file.by_id(self.cost_rate),
)
Data.load(self.file)
return {"FINISHED"}
class LoadScheduleOfRates(bpy.types.Operator):
bl_idname = "bim.load_schedule_of_rates"
bl_label = "Load Schedule of Rates"
bl_options = {"REGISTER", "UNDO"}
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.is_cost_update_enabled = False
self.props.cost_item_rates.clear()
self.contracted_cost_item_rates = json.loads(self.props.contracted_cost_item_rates)
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
self.create_new_cost_item_li(related_object_id, 0)
self.props.is_cost_update_enabled = True
return {"FINISHED"}
def create_new_cost_item_li(self, related_object_id, level_index):
cost_item = Data.cost_items[related_object_id]
new = self.props.cost_item_rates.add()
new.ifc_definition_id = related_object_id
new.name = cost_item["Name"] or "Unnamed"
new.identification = cost_item["Identification"] or "XXX"
new.is_expanded = related_object_id not in self.contracted_cost_item_rates
new.level_index = level_index
if cost_item["IsNestedBy"]:
new.has_children = True
if new.is_expanded:
for related_object_id in cost_item["IsNestedBy"]:
self.create_new_cost_item_li(related_object_id, level_index + 1)
class ExpandCostItemRate(bpy.types.Operator):
bl_idname = "bim.expand_cost_item_rate"
bl_label = "Expand Cost Item Rate"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.remove(self.cost_item)
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 ContractCostItemRate(bpy.types.Operator):
bl_idname = "bim.contract_cost_item_rate"
bl_label = "Contract Cost Item Rate"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.append(self.cost_item)
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"}
@@ -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/>.
import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
@@ -24,6 +42,7 @@ processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def purge():
@@ -34,6 +53,7 @@ def purge():
global processquantitynames_id
global resourcequantitynames_enum
global resourcequantitynames_id
global scheduleofrates_enum
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
@@ -41,12 +61,27 @@ def purge():
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def get_schedule_of_rates(self, context):
global scheduleofrates_enum
if len(scheduleofrates_enum) == 0:
scheduleofrates_enum.extend(
(str(ifc_definition_id), schedule["Name"] or "Unnamed", "")
for ifc_definition_id, schedule in Data.cost_schedules.items()
if schedule["PredefinedType"] == "SCHEDULEOFRATES"
)
return scheduleofrates_enum
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):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum = []
quantitytypes_enum.extend(
[
(t.name(), t.name(), "")
@@ -125,7 +160,10 @@ def getResourceQuantityNames(self, context):
def update_cost_item_index(self, context):
bpy.ops.bim.load_cost_item_quantities()
if Data.cost_schedules[self.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
bpy.ops.bim.load_cost_item_types()
else:
bpy.ops.bim.load_cost_item_quantities()
def updateCostItemIdentification(self, context):
@@ -175,6 +213,11 @@ class CostItemQuantity(PropertyGroup):
total_quantity: FloatProperty(name="Total Quantity")
class CostItemType(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMCostProperties(PropertyGroup):
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
@@ -213,3 +256,11 @@ class BIMCostProperties(PropertyGroup):
active_cost_item_process_index: IntProperty(name="Active Cost Item Process Index")
cost_item_resources: CollectionProperty(name="Cost Item Resources", type=CostItemQuantity)
active_cost_item_resource_index: IntProperty(name="Active Cost Item Resource Index")
cost_item_type_products: CollectionProperty(name="Cost Item Type Products", type=CostItemType)
active_cost_item_type_product_index: IntProperty(name="Active Cost Item Type Product Index")
schedule_of_rates: EnumProperty(
items=get_schedule_of_rates, name="Schedule Of Rates", update=update_schedule_of_rates
)
cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem)
active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index")
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
+245 -83
View File
@@ -1,7 +1,27 @@
# 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/>.
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
class BIM_PT_cost_schedules(Panel):
@@ -23,11 +43,19 @@ class BIM_PT_cost_schedules(Panel):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
row = self.layout.row()
row.operator("bim.add_cost_schedule", icon="ADD")
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
if self.props.active_cost_schedule_id:
self.draw_cost_schedule_ui(
self.props.active_cost_schedule_id, Data.cost_schedules[self.props.active_cost_schedule_id]
)
else:
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule):
row = self.layout.row(align=True)
@@ -66,14 +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):
for attribute in self.props.cost_schedule_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
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)
@@ -119,18 +140,7 @@ class BIM_PT_cost_schedules(Panel):
self.draw_editable_cost_item_values_ui()
def draw_editable_cost_item_attributes_ui(self):
for attribute in self.props.cost_item_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(self.props.cost_item_attributes, self.layout)
def draw_editable_cost_item_quantities_ui(self):
row = self.layout.row(align=True)
@@ -165,20 +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):
for attribute in self.props.quantity_attributes:
row = layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(self.props.quantity_attributes, self.layout)
def draw_editable_cost_item_values_ui(self):
row = self.layout.row(align=True)
@@ -200,19 +197,20 @@ class BIM_PT_cost_schedules(Panel):
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_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")
self.draw_cost_value_operator_ui(layout, cost_value_id)
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)
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, level=1):
self.draw_cost_value_operator_ui(layout, 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"])
@@ -221,9 +219,9 @@ class BIM_PT_cost_schedules(Panel):
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, level + 1)
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):
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
@@ -240,6 +238,7 @@ class BIM_PT_cost_schedules(Panel):
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.parent = parent_id
op.cost_value = cost_value_id
else:
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
@@ -250,23 +249,97 @@ class BIM_PT_cost_schedules(Panel):
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.parent = parent_id
op.cost_value = cost_value_id
def draw_editable_cost_value_ui(self, layout, cost_value):
for attribute in self.props.cost_value_attributes:
row = layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(self.props.cost_value_attributes, layout)
class BIM_PT_cost_item_types(Panel):
bl_label = "IFC Cost Item Types"
bl_idname = "BIM_PT_cost_item_types"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_cost_schedules"
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
def draw(self, context):
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
grid = self.layout.grid_flow(columns=3, even_columns=True)
# Column1
col = grid.column()
row2 = col.row(align=True)
row2.label(text="Elements")
op = row2.operator("bim.assign_cost_item_type", text="", icon="ADD")
op.cost_item = cost_item.ifc_definition_id
op = row2.operator("bim.unassign_cost_item_type", text="", icon="REMOVE")
op.cost_item = cost_item.ifc_definition_id
op.related_object = 0
op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
op.cost_item = cost_item.ifc_definition_id
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_types",
"",
self.props,
"cost_item_type_products",
self.props,
"active_cost_item_type_product_index",
)
# Column2
# TODO
col = grid.column()
row2 = col.row(align=True)
row2.label(text="Tasks")
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
"",
self.props,
"cost_item_processes",
self.props,
"active_cost_item_process_index",
)
# Column3
# TODO
col = grid.column()
row2 = col.row(align=True)
row2.label(text="Resources")
row2 = col.row()
row2.template_list(
"BIM_UL_cost_item_quantities",
"",
self.props,
"cost_item_resources",
self.props,
"active_cost_item_resource_index",
)
class BIM_PT_cost_item_quantities(Panel):
@@ -282,6 +355,10 @@ class BIM_PT_cost_item_quantities(Panel):
def poll(cls, context):
props = context.scene.BIMCostProperties
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
@@ -371,26 +448,54 @@ class BIM_PT_cost_item_quantities(Panel):
op.prop_name = self.props.resource_quantity_names
class BIM_UL_cost_items(UIList):
class BIM_PT_cost_item_rates(Panel):
bl_label = "IFC Cost Item Rates"
bl_idname = "BIM_PT_cost_item_rates"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_cost_schedules"
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
def draw(self, context):
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
if self.props.active_cost_item_rate_index < len(self.props.cost_item_rates):
op = row.operator("bim.assign_cost_value", text="", icon="COPYDOWN")
op.cost_item = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
op.cost_rate = self.props.cost_item_rates[self.props.active_cost_item_rate_index].ifc_definition_id
self.layout.template_list(
"BIM_UL_cost_item_rates",
"",
self.props,
"cost_item_rates",
self.props,
"active_cost_item_rate_index",
)
class BIM_UL_cost_items_trait:
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
self.props = context.scene.BIMCostProperties
cost_item = Data.cost_items[item.ifc_definition_id]
row = layout.row(align=True)
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
row.operator(
"bim.contract_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).cost_item = item.ifc_definition_id
else:
row.operator(
"bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).cost_item = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
self.draw_hierarchy(row, item)
split1 = row.split(factor=0.1)
split1.prop(item, "identification", emboss=False, text="")
@@ -398,24 +503,68 @@ class BIM_UL_cost_items(UIList):
split2.alignment = "RIGHT"
split2.prop(item, "name", emboss=False, text="")
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
split2.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})")
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
self.draw_quantity_column(split2, cost_item)
self.draw_value_column(split2, cost_item)
for column in self.props.columns:
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
split2.label(text="{0:.2f}".format(cost_item["TotalCostValue"]))
self.draw_buttons(split2, item, cost_item)
self.draw_total_cost_column(split2, cost_item)
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
def draw_buttons(self, row, item, cost_item):
pass # TODO: reimplement somewhere with better UX
# elif self.props.active_cost_item_id:
# if self.props.cost_item_editing_type == "VALUES":
# op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN")
# op.source = self.props.active_cost_item_id
# op.destination = item.ifc_definition_id
def draw_hierarchy(self, row, item):
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
op = row.operator(self.contract_operator, text="", emboss=False, icon="DISCLOSURE_TRI_DOWN")
else:
op = row.operator(self.expand_operator, text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT")
op.cost_item = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
def draw_total_cost_column(self, layout, cost_item):
layout.label(text="{0:.2f}".format(cost_item["TotalCost"]))
def draw_quantity_column(self, layout, cost_item):
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
self.draw_uom_column(layout, cost_item)
else:
self.draw_total_quantity_column(layout, cost_item)
def draw_value_column(self, layout, cost_item):
text = "{0:.2f}".format(cost_item["TotalAppliedValue"])
if cost_item["UnitBasisValueComponent"] not in [None, 1]:
text += " / {}".format(round(cost_item["UnitBasisValueComponent"], 2))
layout.label(text=text)
def draw_uom_column(self, layout, cost_item):
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "?" if cost_item["UnitBasisValueComponent"] else "-")
def draw_total_quantity_column(self, layout, cost_item):
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '?'}")
class BIM_UL_cost_items(BIM_UL_cost_items_trait, UIList):
def __init__(self):
self.contract_operator = "bim.contract_cost_item"
self.expand_operator = "bim.expand_cost_item"
class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
# A schedule of rates UIList is identical to a regular cost items UIList but
# we want a separate UIList instance so that you can browse both lists
# independently in Blender. So we use a trait.
def __init__(self):
self.contract_operator = "bim.contract_cost_item_rate"
self.expand_operator = "bim.expand_cost_item_rate"
def draw_quantity_column(self, layout, cost_item):
self.draw_uom_column(layout, cost_item)
def draw_total_cost_column(self, layout, cost_item):
pass # No such thing as a total cost in a schedule of rates
class BIM_UL_cost_columns(UIList):
@@ -427,6 +576,19 @@ class BIM_UL_cost_columns(UIList):
row.operator("bim.remove_cost_column", text="", icon="X").name = item.name
class BIM_UL_cost_item_types(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties
cost_item = props.cost_items[props.active_cost_item_index]
if item:
row = layout.row(align=True)
row.label(text=item.name)
op = row.operator("bim.unassign_cost_item_type", text="", icon="X")
op.cost_item = cost_item.ifc_definition_id
op.related_object = item.ifc_definition_id
class BIM_UL_cost_item_quantities(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import requests
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import ifcopenshell
@@ -1,3 +1,22 @@
# 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/>.
import bpy.props
import bpy.types
@@ -1,3 +1,22 @@
# 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/>.
import bpy.types
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import ifccsv
import ifcopenshell
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty
from bpy.types import PropertyGroup
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import logging
import ifcopenshell
@@ -146,7 +165,7 @@ class SelectHighPolygonMeshes(bpy.types.Operator):
def execute(self, context):
[o.select_set(True) for o in context.view_layer.objects
if o.type == 'MESH'
if o.type == "MESH"
and len(o.data.polygons) > context.scene.BIMDebugProperties.number_of_polygons]
return {"FINISHED"}
@@ -178,18 +197,16 @@ class InspectFromStepId(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
context.scene.BIMDebugProperties.active_step_id = self.step_id
debug_props = context.scene.BIMDebugProperties
debug_props.active_step_id = self.step_id
crumb = context.scene.BIMDebugProperties.step_id_breadcrumb.add()
crumb.name = str(self.step_id)
element = self.file.by_id(self.step_id)
while len(context.scene.BIMDebugProperties.attributes) > 0:
context.scene.BIMDebugProperties.attributes.remove(0)
while len(context.scene.BIMDebugProperties.inverse_attributes) > 0:
context.scene.BIMDebugProperties.inverse_attributes.remove(0)
while len(context.scene.BIMDebugProperties.inverse_references) > 0:
context.scene.BIMDebugProperties.inverse_references.remove(0)
debug_props.attributes.clear()
debug_props.inverse_attributes.clear()
debug_props.inverse_references.clear()
for key, value in element.get_info().items():
self.add_attribute(context.scene.BIMDebugProperties.attributes, key, value)
self.add_attribute(debug_props.attributes, key, value)
for key in dir(element):
if (
not key[0].isalpha()
@@ -198,9 +215,9 @@ class InspectFromStepId(bpy.types.Operator):
or not getattr(element, key)
):
continue
self.add_attribute(context.scene.BIMDebugProperties.inverse_attributes, key, getattr(element, key))
self.add_attribute(debug_props.inverse_attributes, key, getattr(element, key))
for inverse in self.file.get_inverse(element):
new = context.scene.BIMDebugProperties.inverse_references.add()
new = debug_props.inverse_references.add()
new.string_value = str(inverse)
new.int_value = inverse.id()
return {"FINISHED"}
@@ -1,3 +1,22 @@
# 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/>.
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from bpy.types import Panel
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import ifccsv
import ifcopenshell
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty
from bpy.types import PropertyGroup
@@ -1,3 +1,22 @@
# 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/>.
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import json
import ifcopenshell.api
@@ -14,8 +33,7 @@ class LoadInformation(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMDocumentProperties
while len(props.documents) > 0:
props.documents.remove(0)
props.documents.clear()
for information_id, information in Data.information.items():
new = props.documents.add()
new.name = information["Name"] or "Unnamed"
@@ -37,8 +55,7 @@ class LoadDocumentReferences(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMDocumentProperties
while len(props.documents) > 0:
props.documents.remove(0)
props.documents.clear()
for reference_id, reference in Data.references.items():
new = props.documents.add()
new.name = reference["Name"] or "Unnamed"
@@ -71,8 +88,7 @@ class EnableEditingDocument(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMDocumentProperties
while len(props.document_attributes) > 0:
props.document_attributes.remove(0)
props.document_attributes.clear()
if props.is_editing == "information":
data = Data.information[self.document]
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -1,5 +1,25 @@
# 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/>.
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.document.data import Data
@@ -26,7 +46,7 @@ class BIM_PT_documents(Panel):
row.label(text="{} Documents Found".format(len(Data.information)), icon="FILE")
if self.props.is_editing == "information":
row.operator("bim.add_information", text="", icon="ADD")
row.operator("bim.disable_document_editing_ui", text="", icon="X")
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
else:
row.operator("bim.load_information", text="", icon="IMPORT")
@@ -35,7 +55,7 @@ class BIM_PT_documents(Panel):
row.label(text="{} References Found".format(len(Data.references)), icon="FILE_HIDDEN")
if self.props.is_editing == "reference":
row.operator("bim.add_document_reference", text="", icon="ADD")
row.operator("bim.disable_document_editing_ui", text="", icon="X")
row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL")
else:
row.operator("bim.load_document_references", text="", icon="IMPORT")
@@ -48,14 +68,7 @@ class BIM_PT_documents(Panel):
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
for attribute in self.props.document_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(self.props.document_attributes, self.layout)
class BIM_PT_object_documents(Panel):
@@ -116,7 +129,7 @@ class BIM_PT_object_documents(Panel):
row = self.layout.row(align=True)
icon = "FILE" if self.props.is_adding == "IfcDocumentInformation" else "FILE_HIDDEN"
row.label(text="Adding {}".format(self.props.is_adding), icon=icon)
row.operator("bim.disable_assigning_document", text="", icon="X")
row.operator("bim.disable_assigning_document", text="", icon="CANCEL")
self.layout.template_list(
"BIM_UL_object_documents",
"",
@@ -142,7 +155,7 @@ class BIM_UL_documents(UIList):
row.operator("bim.edit_information", text="", icon="CHECKMARK")
elif context.scene.BIMDocumentProperties.is_editing == "reference":
row.operator("bim.edit_document_reference", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_document", text="", icon="X")
row.operator("bim.disable_editing_document", text="", icon="CANCEL")
elif context.scene.BIMDocumentProperties.active_document_id:
row.operator("bim.remove_document", text="", icon="X").document = item.ifc_definition_id
else:
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator, handler, gizmos
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import os
from mathutils import Vector
@@ -1,3 +1,22 @@
# 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/>.
"""Viewport decorations"""
import math
from functools import reduce
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import blf
import math
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import blenderbim.bim.module.drawing.decoration as decoration
from bpy.app.handlers import persistent
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import math
import mathutils.geometry
@@ -244,7 +263,7 @@ def get_active_drawing(scene):
if props.active_drawing_index is None or len(props.drawings) == 0:
return None, None
try:
drawing = props.drawings[props.active_drawing_index]
drawing = props.active_drawing
return scene.collection.children["Views"].children[f"IfcGroup/{drawing.name}"], drawing.camera
except (KeyError, IndexError):
raise RuntimeError("missing drawing collection")
@@ -1,3 +1,22 @@
# 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/>.
import os
import re
import bpy
@@ -12,11 +31,13 @@ import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.selector
import ifcopenshell.util.representation
import blenderbim.bim.schema
import blenderbim.bim.module.drawing.svgwriter as svgwriter
import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.sheeter as sheeter
import blenderbim.bim.module.drawing.scheduler as scheduler
import blenderbim.bim.module.drawing.helper as helper
from blenderbim.bim.module.drawing.prop import RasterStyleProperty
from mathutils import Vector, Matrix, Euler, geometry
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.group.data import Data as GroupData
@@ -39,6 +60,10 @@ class AddDrawing(bpy.types.Operator):
bl_label = "Add Drawing"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
@@ -72,34 +97,52 @@ class AddDrawing(bpy.types.Operator):
group = self.file.by_id(sorted(GroupData.groups.keys())[-1])
ifcopenshell.api.run("group.edit_group", self.file, **{"group": group, "attributes": {"Name": new.name}})
bpy.ops.bim.assign_group(product=camera.name, group=group.id())
bpy.ops.bim.add_pset(obj=camera.name, obj_type="Object", pset_name="EPset_Drawing")
pset_id = sorted(PsetData.products[camera.BIMObjectProperties.ifc_definition_id]["psets"])[-1]
bpy.ops.bim.edit_pset(
obj=camera.name,
obj_type="Object",
pset_id=pset_id,
properties=json.dumps({"TargetView": "PLAN_VIEW", "Scale": "1/100"}),
pset = ifcopenshell.api.run(
"pset.add_pset",
self.file,
**{
"product": self.file.by_id(camera.BIMObjectProperties.ifc_definition_id),
"name": "EPset_Drawing",
},
)
ifcopenshell.api.run(
"pset.edit_pset",
self.file,
**{
"pset": pset,
"properties": {"TargetView": "PLAN_VIEW", "Scale": "1/100"},
"pset_template": blenderbim.bim.schema.ifc.psetqto.get_by_name("EPset_Drawing"),
},
)
PsetData.load(IfcStore.get_file(), camera.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class CreateDrawing(bpy.types.Operator):
"""Creates a svg drawing
Only available if :
- IFC file is created
- Camera is in Orthographic mode
"""
bl_idname = "bim.create_drawing"
bl_label = "Create Drawing"
@classmethod
def poll(cls, context):
camera = context.scene.camera
return IfcStore.get_file() \
and camera.type == "CAMERA" and camera.data.type == "ORTHO" \
and camera.BIMObjectProperties.ifc_definition_id
def execute(self, context):
self.camera = context.scene.camera
if (
not (self.camera.type == "CAMERA" and self.camera.data.type == "ORTHO")
or not self.camera.BIMObjectProperties.ifc_definition_id
):
return
self.file = IfcStore.get_file()
self.time = None
start = time.time()
self.profile_code("Start drawing generation process")
self.props = context.scene.DocProperties
self.drawing_name = IfcStore.get_file().by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name
self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name
underlay_svg = self.generate_underlay(context)
self.profile_code("Generate underlay")
linework_svg = self.generate_linework(context)
@@ -108,7 +151,7 @@ class CreateDrawing(bpy.types.Operator):
self.profile_code("Generate annotation")
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
self.profile_code("Combine SVG layers")
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
print("Total Time: {:.2f}".format(time.time() - start))
return {"FINISHED"}
@@ -166,8 +209,8 @@ class CreateDrawing(bpy.types.Operator):
if not self.props.has_underlay:
return
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-underlay.svg")
bpy.context.scene.render.filepath = svg_path[0:-4] + ".png"
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
context.scene.render.filepath = svg_path[0:-4] + ".png"
drawing_style = context.scene.DocProperties.drawing_styles[
self.camera.data.BIMCameraProperties.active_drawing_style_index
]
@@ -178,7 +221,7 @@ class CreateDrawing(bpy.types.Operator):
for obj in self.camera.users_collection[0].objects:
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
for obj in bpy.context.visible_objects:
for obj in context.visible_objects:
if (
(not obj.data and not obj.instance_collection)
or isinstance(obj.data, bpy.types.Camera)
@@ -189,14 +232,14 @@ class CreateDrawing(bpy.types.Operator):
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
space = self.get_view_3d()
space = self.get_view_3d(context.screen.areas)
previous_shading = space.shading.type
previous_format = bpy.context.scene.render.image_settings.file_format
previous_format = context.scene.render.image_settings.file_format
space.shading.type = "RENDERED"
bpy.context.scene.render.image_settings.file_format = "PNG"
context.scene.render.image_settings.file_format = "PNG"
bpy.ops.render.opengl(write_still=True)
space.shading.type = previous_shading
bpy.context.scene.render.image_settings.file_format = previous_format
context.scene.render.image_settings.file_format = previous_format
for name, value in previous_visibility.items():
bpy.data.objects[name].hide_set(value)
@@ -210,7 +253,7 @@ class CreateDrawing(bpy.types.Operator):
svg_writer.human_scale = "NTS"
else:
svg_writer.human_scale = human_scale
render = bpy.context.scene.render
render = context.scene.render
if self.is_landscape():
width = self.camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
@@ -218,13 +261,13 @@ class CreateDrawing(bpy.types.Operator):
height = self.camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
svg_writer.output = svg_path
svg_writer.data_dir = bpy.context.scene.BIMProperties.data_dir
svg_writer.data_dir = context.scene.BIMProperties.data_dir
svg_writer.vector_style = drawing_style.vector_style
svg_writer.camera = self.camera
svg_writer.camera_width = width
svg_writer.camera_height = height
svg_writer.camera_projection = tuple(self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)))
svg_writer.background_image = bpy.context.scene.render.filepath
svg_writer.background_image = context.scene.render.filepath
svg_writer.write("underlay")
return svg_path
@@ -313,12 +356,12 @@ class CreateDrawing(bpy.types.Operator):
else:
svg_writer.human_scale = human_scale
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
drawing_style = context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
]
render = bpy.context.scene.render
if self.is_landscape():
render = context.scene.render
if self.is_landscape(render):
width = camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
@@ -327,7 +370,7 @@ class CreateDrawing(bpy.types.Operator):
svg_writer.scale = float(numerator) / float(denominator)
svg_writer.output = svg_path
svg_writer.data_dir = bpy.context.scene.BIMProperties.data_dir
svg_writer.data_dir = context.scene.BIMProperties.data_dir
svg_writer.vector_style = drawing_style.vector_style
svg_writer.camera = camera
svg_writer.camera_width = width
@@ -372,11 +415,11 @@ class CreateDrawing(bpy.types.Operator):
svg_writer.write("annotation")
return svg_writer.output
def is_landscape(self):
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
def is_landscape(self, render):
return render.resolution_x > render.resolution_y
def get_view_3d(self):
for area in bpy.context.screen.areas:
def get_view_3d(self, areas):
for area in areas:
if area.type != "VIEW_3D":
continue
for space in area.spaces:
@@ -463,12 +506,14 @@ class AddAnnotation(bpy.types.Operator):
obj_name: bpy.props.StringProperty()
data_type: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return IfcStore.get_file() and context.scene.camera
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
if not context.scene.camera:
return {"FINISHED"}
subcontext = ifcopenshell.util.representation.get_context(
IfcStore.get_file(), "Plan", "Annotation", context.scene.camera.data.BIMCameraProperties.target_view
)
@@ -493,7 +538,7 @@ class AddAnnotation(bpy.types.Operator):
bpy.ops.bim.update_representation(obj=obj.name)
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = obj
context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
@@ -506,11 +551,12 @@ class AddSheet(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
new = bpy.context.scene.DocProperties.sheets.add()
new.name = "{} - SHEET".format(len(bpy.context.scene.DocProperties.sheets))
scene = context.scene
new = scene.DocProperties.sheets.add()
new.name = "{} - SHEET".format(len(scene.DocProperties.sheets))
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock)
sheet_builder.data_dir = scene.BIMProperties.data_dir
sheet_builder.create(new.name, scene.DocProperties.titleblock)
return {"FINISHED"}
@@ -519,11 +565,11 @@ class OpenSheet(bpy.types.Operator):
bl_label = "Open Sheet"
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
open_with_user_command(
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(
bpy.context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg"
context.scene.BIMProperties.data_dir, "sheets", props.active_sheet.name + ".svg"
),
)
return {"FINISHED"}
@@ -536,14 +582,14 @@ class AddDrawingToSheet(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
try:
sheet_builder.add_drawing(
props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name
props.drawings.active_drawing.name, props.active_sheet.name
)
except:
except FileNotFoundError:
self.report({"ERROR"}, "Drawings need to be created before being added to a sheet")
return {"FINISHED"}
@@ -554,17 +600,18 @@ class CreateSheets(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
props = bpy.context.scene.DocProperties
name = props.sheets[props.active_sheet_index].name
scene = context.scene
props = scene.DocProperties
name = props.active_sheet.name
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.data_dir = scene.BIMProperties.data_dir
sheet_builder.build(name)
svg2pdf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2dxf_command
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
if svg2pdf_command:
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name)
path = os.path.join(scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
pdf = os.path.join(path, name + ".pdf")
# With great power comes great responsibility. Example:
@@ -574,7 +621,7 @@ class CreateSheets(bpy.types.Operator):
subprocess.run(command)
if svg2dxf_command:
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name)
path = os.path.join(scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
eps = os.path.join(path, name + ".eps")
dxf = os.path.join(path, name + ".dxf")
@@ -586,11 +633,11 @@ class CreateSheets(bpy.types.Operator):
subprocess.run(command)
if svg2pdf_command:
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
else:
open_with_user_command(
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name, name + ".svg"),
context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(scene.BIMProperties.data_dir, "build", name, name + ".svg"),
)
return {"FINISHED"}
@@ -602,26 +649,31 @@ class OpenView(bpy.types.Operator):
def execute(self, context):
open_with_user_command(
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"),
context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"),
)
return {"FINISHED"}
class OpenViewCamera(bpy.types.Operator):
"""Select this drawing's camera object and expand its drawing properties"""
bl_idname = "bim.open_view_camera"
bl_label = "Open View Camera"
bl_options = {"REGISTER", "UNDO"}
view_name: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return bpy.context.object.mode == "OBJECT"
def execute(self, context):
new_drawing_index = bpy.context.scene.DocProperties.drawings.find(self.view_name)
bpy.context.scene.DocProperties.active_drawing_index = new_drawing_index
drawing = bpy.context.scene.DocProperties.drawings[new_drawing_index]
bpy.context.view_layer.objects.active = drawing.camera
doc_props = context.scene.DocProperties
doc_props.active_drawing_index = doc_props.drawings.find(self.view_name)
drawing = doc_props.active_drawing
bpy.ops.object.select_all(action="DESELECT")
drawing.camera.select_set(True)
for area in bpy.context.screen.areas:
context.view_layer.objects.active = drawing.camera
for area in context.screen.areas:
if area.ui_type == "PROPERTIES":
for space in area.spaces:
space.context = "DATA"
@@ -635,22 +687,22 @@ class ActivateView(bpy.types.Operator):
drawing_index: bpy.props.IntProperty()
def execute(self, context):
camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera
camera = context.scene.DocProperties.drawings[self.drawing_index].camera
if not camera:
return {"FINISHED"}
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
is_local_view = area.spaces[0].local_view is not None
if is_local_view:
bpy.ops.view3d.localview()
bpy.context.scene.camera = camera
context.scene.camera = camera
bpy.ops.view3d.localview()
else:
bpy.context.scene.camera = camera
context.scene.camera = camera
area.spaces[0].region_3d.view_perspective = "CAMERA"
views_collection = bpy.data.collections.get("Views")
for collection in views_collection.children:
# We assume the project collection is at the top level
for project_collection in bpy.context.view_layer.layer_collection.children:
for project_collection in context.view_layer.layer_collection.children:
# We assume a convention that the 'Views' collection is directly
# in the project collection
if (
@@ -659,7 +711,7 @@ class ActivateView(bpy.types.Operator):
):
project_collection.children["Views"].children[collection.name].hide_viewport = True
bpy.data.collections.get(collection.name).hide_render = True
bpy.context.view_layer.layer_collection.children["Views"].children[
context.view_layer.layer_collection.children["Views"].children[
camera.users_collection[0].name
].hide_viewport = False
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
@@ -671,11 +723,12 @@ class SelectDocIfcFile(bpy.types.Operator):
bl_idname = "bim.select_doc_ifc_file"
bl_label = "Select Documentation IFC File"
bl_options = {"REGISTER", "UNDO"}
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files[self.index].name = self.filepath
context.scene.DocProperties.ifc_files[self.index].name = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -689,7 +742,7 @@ class GenerateReferences(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.camera = bpy.context.scene.camera
self.camera = context.scene.camera
self.filter_potential_references()
if self.camera.data.BIMCameraProperties.target_view == "PLAN_VIEW":
self.generate_grids()
@@ -758,7 +811,7 @@ class ResizeText(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
for obj in bpy.context.scene.camera.users_collection[0].objects:
for obj in context.scene.camera.users_collection[0].objects:
if isinstance(obj.data, bpy.types.TextCurve):
annotation.Annotator.resize_text(obj)
return {"FINISHED"}
@@ -770,7 +823,7 @@ class AddVariable(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.add()
context.active_object.data.BIMTextProperties.variables.add()
return {"FINISHED"}
@@ -781,7 +834,7 @@ class RemoveVariable(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index)
context.active_object.data.BIMTextProperties.variables.remove(self.index)
return {"FINISHED"}
@@ -791,8 +844,8 @@ class PropagateTextData(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
source = bpy.context.active_object
for obj in bpy.context.selected_objects:
source = context.active_object
for obj in context.selected_objects:
if obj == source:
continue
obj.data.body = source.data.body
@@ -800,8 +853,7 @@ class PropagateTextData(bpy.types.Operator):
obj.data.align_y = source.data.align_y
obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size
obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol
while len(obj.data.BIMTextProperties.variables) > 0:
obj.data.BIMTextProperties.variables.remove(0)
obj.data.BIMTextProperties.variables.clear()
for variable in source.data.BIMTextProperties.variables:
new_variable = obj.data.BIMTextProperties.variables.add()
new_variable.name = variable.name
@@ -816,7 +868,7 @@ class RemoveDrawing(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
camera = props.drawings[self.index].camera
collection = camera.users_collection[0]
for obj in collection.objects:
@@ -832,7 +884,7 @@ class AddDrawingStyle(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
new = bpy.context.scene.DocProperties.drawing_styles.add()
new = context.scene.DocProperties.drawing_styles.add()
new.name = "New Drawing Style"
return {"FINISHED"}
@@ -844,7 +896,7 @@ class RemoveDrawingStyle(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.drawing_styles.remove(self.index)
context.scene.DocProperties.drawing_styles.remove(self.index)
return {"FINISHED"}
@@ -856,42 +908,26 @@ class SaveDrawingStyle(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
space = self.get_view_3d()
style = {
"bpy.data.worlds[0].color": tuple(bpy.data.worlds[0].color),
"bpy.context.scene.render.engine": bpy.context.scene.render.engine,
"bpy.context.scene.render.film_transparent": bpy.context.scene.render.film_transparent,
"bpy.context.scene.display.shading.show_object_outline": bpy.context.scene.display.shading.show_object_outline,
"bpy.context.scene.display.shading.show_cavity": bpy.context.scene.display.shading.show_cavity,
"bpy.context.scene.display.shading.cavity_type": bpy.context.scene.display.shading.cavity_type,
"bpy.context.scene.display.shading.curvature_ridge_factor": bpy.context.scene.display.shading.curvature_ridge_factor,
"bpy.context.scene.display.shading.curvature_valley_factor": bpy.context.scene.display.shading.curvature_valley_factor,
"bpy.context.scene.view_settings.view_transform": bpy.context.scene.view_settings.view_transform,
"bpy.context.scene.display.shading.light": bpy.context.scene.display.shading.light,
"bpy.context.scene.display.shading.color_type": bpy.context.scene.display.shading.color_type,
"bpy.context.scene.display.shading.single_color": tuple(bpy.context.scene.display.shading.single_color),
"bpy.context.scene.display.shading.show_shadows": bpy.context.scene.display.shading.show_shadows,
"bpy.context.scene.display.shading.shadow_intensity": bpy.context.scene.display.shading.shadow_intensity,
"bpy.context.scene.display.light_direction": tuple(bpy.context.scene.display.light_direction),
"bpy.context.scene.view_settings.use_curve_mapping": bpy.context.scene.view_settings.use_curve_mapping,
"space.overlay.show_wireframes": space.overlay.show_wireframes,
"space.overlay.wireframe_threshold": space.overlay.wireframe_threshold,
"space.overlay.show_floor": space.overlay.show_floor,
"space.overlay.show_axis_x": space.overlay.show_axis_x,
"space.overlay.show_axis_y": space.overlay.show_axis_y,
"space.overlay.show_axis_z": space.overlay.show_axis_z,
"space.overlay.show_object_origins": space.overlay.show_object_origins,
"space.overlay.show_relationship_lines": space.overlay.show_relationship_lines,
}
space = self.get_view_3d(context) # Do not remove. It is used later in eval
scene = context.scene
style = {}
for prop in RasterStyleProperty:
value = eval(prop.value)
if not isinstance(value, str):
try:
value = tuple(value)
except TypeError:
pass
style[prop.value] = value
if self.index:
index = int(self.index)
else:
index = bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index
bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
index = context.active_object.data.BIMCameraProperties.active_drawing_style_index
scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
return {"FINISHED"}
def get_view_3d(self):
for area in bpy.context.screen.areas:
def get_view_3d(self, context):
for area in context.screen.areas:
if area.type != "VIEW_3D":
continue
for space in area.spaces:
@@ -906,56 +942,32 @@ class ActivateDrawingStyle(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
if context.scene.camera.data.BIMCameraProperties.active_drawing_style_index < len(
bpy.context.scene.DocProperties.drawing_styles
scene = context.scene
if scene.camera.data.BIMCameraProperties.active_drawing_style_index < len(
scene.DocProperties.drawing_styles
):
self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
self.drawing_style = scene.DocProperties.drawing_styles[
scene.camera.data.BIMCameraProperties.active_drawing_style_index
]
self.set_raster_style()
self.set_query()
self.set_raster_style(context)
self.set_query(context)
return {"FINISHED"}
def set_raster_style(self):
space = self.get_view_3d()
def set_raster_style(self, context):
scene = context.scene # Do not remove. It is used in exec later
space = self.get_view_3d(context) # Do not remove. It is used in exec later
style = json.loads(self.drawing_style.raster_style)
bpy.data.worlds[0].color = style["bpy.data.worlds[0].color"]
bpy.context.scene.render.engine = style["bpy.context.scene.render.engine"]
bpy.context.scene.render.film_transparent = style["bpy.context.scene.render.film_transparent"]
bpy.context.scene.display.shading.show_object_outline = style[
"bpy.context.scene.display.shading.show_object_outline"
]
bpy.context.scene.display.shading.show_cavity = style["bpy.context.scene.display.shading.show_cavity"]
bpy.context.scene.display.shading.cavity_type = style["bpy.context.scene.display.shading.cavity_type"]
bpy.context.scene.display.shading.curvature_ridge_factor = style[
"bpy.context.scene.display.shading.curvature_ridge_factor"
]
bpy.context.scene.display.shading.curvature_valley_factor = style[
"bpy.context.scene.display.shading.curvature_valley_factor"
]
bpy.context.scene.view_settings.view_transform = style["bpy.context.scene.view_settings.view_transform"]
bpy.context.scene.display.shading.light = style["bpy.context.scene.display.shading.light"]
bpy.context.scene.display.shading.color_type = style["bpy.context.scene.display.shading.color_type"]
bpy.context.scene.display.shading.single_color = style["bpy.context.scene.display.shading.single_color"]
bpy.context.scene.display.shading.show_shadows = style["bpy.context.scene.display.shading.show_shadows"]
bpy.context.scene.display.shading.shadow_intensity = style["bpy.context.scene.display.shading.shadow_intensity"]
bpy.context.scene.display.light_direction = style["bpy.context.scene.display.light_direction"]
for path, value in style.items():
if isinstance(value, str):
exec(f"{path} = '{value}'")
else:
exec(f"{path} = {value}")
bpy.context.scene.view_settings.use_curve_mapping = style["bpy.context.scene.view_settings.use_curve_mapping"]
space.overlay.show_wireframes = style["space.overlay.show_wireframes"]
space.overlay.wireframe_threshold = style["space.overlay.wireframe_threshold"]
space.overlay.show_floor = style["space.overlay.show_floor"]
space.overlay.show_axis_x = style["space.overlay.show_axis_x"]
space.overlay.show_axis_y = style["space.overlay.show_axis_y"]
space.overlay.show_axis_z = style["space.overlay.show_axis_z"]
space.overlay.show_object_origins = style["space.overlay.show_object_origins"]
space.overlay.show_relationship_lines = style["space.overlay.show_relationship_lines"]
def set_query(self):
def set_query(self, context):
self.selector = ifcopenshell.util.selector.Selector()
self.include_global_ids = []
self.exclude_global_ids = []
for ifc_file in bpy.context.scene.DocProperties.ifc_files:
for ifc_file in context.scene.DocProperties.ifc_files:
try:
ifc = ifcopenshell.open(ifc_file.name)
except:
@@ -967,15 +979,15 @@ class ActivateDrawingStyle(bpy.types.Operator):
results = self.selector.parse(ifc, self.drawing_style.exclude_query)
self.exclude_global_ids.extend([e.GlobalId for e in results])
if self.drawing_style.include_query:
self.parse_filter_query("INCLUDE")
self.parse_filter_query("INCLUDE", context)
if self.drawing_style.exclude_query:
self.parse_filter_query("EXCLUDE")
self.parse_filter_query("EXCLUDE", context)
def parse_filter_query(self, mode):
def parse_filter_query(self, mode, context):
if mode == "INCLUDE":
objects = bpy.context.scene.objects
objects = context.scene.objects
elif mode == "EXCLUDE":
objects = bpy.context.visible_objects
objects = context.visible_objects
for obj in objects:
if mode == "INCLUDE":
obj.hide_viewport = False # Note: this breaks alt-H
@@ -990,8 +1002,8 @@ class ActivateDrawingStyle(bpy.types.Operator):
if global_id in self.exclude_global_ids:
obj.hide_viewport = True # Note: this breaks alt-H
def get_view_3d(self):
for area in bpy.context.screen.areas:
def get_view_3d(self, context):
for area in context.screen.areas:
if area.type != "VIEW_3D":
continue
for space in area.spaces:
@@ -1021,7 +1033,7 @@ class RemoveSheet(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
props.sheets.remove(self.index)
return {"FINISHED"}
@@ -1032,8 +1044,8 @@ class AddSchedule(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
new = bpy.context.scene.DocProperties.schedules.add()
new.name = "SCHEDULE {}".format(len(bpy.context.scene.DocProperties.schedules))
new = context.scene.DocProperties.schedules.add()
new.name = "SCHEDULE {}".format(len(context.scene.DocProperties.schedules))
return {"FINISHED"}
@@ -1044,7 +1056,7 @@ class RemoveSchedule(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
props.schedules.remove(self.index)
return {"FINISHED"}
@@ -1058,8 +1070,8 @@ class SelectScheduleFile(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.schedules[props.active_schedule_index].file = self.filepath
props = context.scene.DocProperties
props.active_schedule.file = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -1071,13 +1083,17 @@ class BuildSchedule(bpy.types.Operator):
bl_idname = "bim.build_schedule"
bl_label = "Build Schedule"
@classmethod
def poll(cls, context):
return context.scene.DocProperties.active_schedule.file
def execute(self, context):
props = bpy.context.scene.DocProperties
schedule = props.schedules[props.active_schedule_index]
props = context.scene.DocProperties
schedule = props.active_schedule
schedule_creator = scheduler.Scheduler()
outfile = os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg")
outfile = os.path.join(context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg")
schedule_creator.schedule(schedule.file, outfile)
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, outfile)
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, outfile)
return {"FINISHED"}
@@ -1088,11 +1104,11 @@ class AddScheduleToSheet(bpy.types.Operator):
# TODO: check undo redo
def execute(self, context):
props = bpy.context.scene.DocProperties
props = context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
sheet_builder.add_schedule(
props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name
props.active_schedule.name, props.active_sheet.name
)
return {"FINISHED"}
@@ -1103,7 +1119,7 @@ class AddDrawingStyleAttribute(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
props = context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
return {"FINISHED"}
@@ -1115,7 +1131,7 @@ class RemoveDrawingStyleAttribute(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
props = context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
return {"FINISHED"}
@@ -1125,13 +1141,13 @@ class RefreshDrawingList(bpy.types.Operator):
bl_label = "Refresh Drawing List"
def execute(self, context):
while len(bpy.context.scene.DocProperties.drawings) > 0:
bpy.context.scene.DocProperties.drawings.remove(0)
for obj in bpy.context.scene.objects:
doc_props = context.scene.DocProperties
doc_props.drawings.clear()
for obj in context.scene.objects:
if not isinstance(obj.data, bpy.types.Camera):
continue
if "IfcAnnotation/" in obj.name:
new = bpy.context.scene.DocProperties.drawings.add()
new = doc_props.drawings.add()
new.name = "/".join(obj.name.split("/")[1:])
new.camera = obj
return {"FINISHED"}
@@ -1143,13 +1159,12 @@ class CleanWireframes(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
objects = bpy.data.objects
if bpy.context.selected_objects:
objects = bpy.context.selected_objects
for obj in objects:
if not isinstance(obj.data, bpy.types.Mesh):
continue
if "EDGE_SPLIT" not in [m.type for m in obj.modifiers]:
if context.selected_objects:
objects = context.selected_objects
else:
objects = context.scene.objects
for obj in (o for o in objects if o.type == "MESH"):
if "EDGE_SPLIT" not in (m.type for m in obj.modifiers):
obj.modifiers.new("EdgeSplit", "EDGE_SPLIT")
return {"FINISHED"}
@@ -1159,11 +1174,13 @@ class CopyGrid(bpy.types.Operator):
bl_label = "Add Grid"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return helper.get_active_drawing(context.scene)[0] is not None
def execute(self, context):
proj_coll = helper.get_project_collection(context.scene)
view_coll, camera = helper.get_active_drawing(context.scene)
if view_coll is None:
return {"CANCELLED"}
is_ortho = camera.data.type == "ORTHO"
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
@@ -1240,13 +1257,15 @@ class AddSectionsAnnotations(bpy.types.Operator):
bl_label = "Add Sections"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
camera = helper.get_active_drawing(context.scene)[1]
return camera and camera.data.type == "ORTHO"
def execute(self, context):
scene = context.scene
view_coll, camera = helper.get_active_drawing(scene)
is_ortho = camera.data.type == "ORTHO"
if not is_ortho:
return {"CANCELLED"}
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
bounds = helper.ortho_view_frame(camera.data)
drawings = [
d
@@ -1,7 +1,27 @@
# 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/>.
import os
import bpy
import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.decoration as decoration
import enum
from pathlib import Path
from blenderbim.bim.prop import Attribute, StrProperty
from bpy.types import PropertyGroup
@@ -38,10 +58,10 @@ def getDiagramScales(self, context):
global diagram_scales_enum
if (
len(diagram_scales_enum) < 1
or (bpy.context.scene.unit_settings.system == "IMPERIAL" and len(diagram_scales_enum) == 13)
or (bpy.context.scene.unit_settings.system == "METRIC" and len(diagram_scales_enum) == 31)
or (context.scene.unit_settings.system == "IMPERIAL" and len(diagram_scales_enum) == 13)
or (context.scene.unit_settings.system == "METRIC" and len(diagram_scales_enum) == 31)
):
if bpy.context.scene.unit_settings.system == "IMPERIAL":
if context.scene.unit_settings.system == "IMPERIAL":
diagram_scales_enum = [
("CUSTOM", "Custom", ""),
("1'=1'-0\"|1/1", "1'=1'-0\"", ""),
@@ -199,6 +219,33 @@ class DrawingStyle(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=StrProperty)
class RasterStyleProperty(enum.Enum):
WORLD_COLOR = "bpy.data.worlds[0].color"
RENDER_ENGINE = "scene.render.engine"
RENDER_TRANSPARENT = "scene.render.film_transparent"
VIEW_TRANSFORM = "scene.view_settings.view_transform"
SHADING_SHOW_OBJECT_OUTLINE = "scene.display.shading.show_object_outline"
SHADING_SHOW_CAVITY = "scene.display.shading.show_cavity"
SHADING_CAVITY_TYPE = "scene.display.shading.cavity_type"
SHADING_CURVATURE_RIDGE_FACTOR = "scene.display.shading.curvature_ridge_factor"
SHADING_CURVATURE_VALLEY_FACTOR = "scene.display.shading.curvature_valley_factor"
SHADING_LIGHT = "scene.display.shading.light"
SHADING_COLOR_TYPE = "scene.display.shading.color_type"
SHADING_SINGLE_COLOR = "scene.display.shading.single_color"
SHADING_SHOW_SHADOWS = "scene.display.shading.show_shadows"
SHADING_SHADOW_INTENSITY = "scene.display.shading.shadow_intensity"
DISPLAY_LIGHT_DIRECTION = "scene.display.light_direction"
VIEW_USE_CURVE_MAPPING = "scene.view_settings.use_curve_mapping"
OVERLAY_SHOW_WIREFRAMES = "space.overlay.show_wireframes"
OVERLAY_WIREFRAME_THRESHOLD = "space.overlay.wireframe_threshold"
OVERLAY_SHOW_FLOOR = "space.overlay.show_floor"
OVERLAY_SHOW_AXIS_X = "space.overlay.show_axis_x"
OVERLAY_SHOW_AXIS_Y = "space.overlay.show_axis_y"
OVERLAY_SHOW_AXIS_Z = "space.overlay.show_axis_z"
OVERLAY_SHOW_OBJECT_ORIGINS = "space.overlay.show_object_origins"
OVERLAY_SHOW_RELATIONSHIP_LINES = "space.overlay.show_relationship_lines"
class DocProperties(PropertyGroup):
has_underlay: BoolProperty(name="Underlay", default=False)
has_linework: BoolProperty(name="Linework", default=True)
@@ -222,6 +269,17 @@ class DocProperties(PropertyGroup):
name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
)
@property
def active_schedule(self):
return self.schedules[self.active_schedule_index]
@property
def active_drawing(self):
return self.drawings[self.active_drawing_index]
@property
def active_sheet(self):
return self.sheets[self.active_sheet_index]
class BIMCameraProperties(PropertyGroup):
view_name: StringProperty(name="View Name")
@@ -1,3 +1,22 @@
# 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/>.
import svgwrite
from odf.opendocument import load
@@ -1,3 +1,22 @@
# 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/>.
import bgl
from mathutils import Matrix
from gpu.types import GPUShader
@@ -1,3 +1,22 @@
# 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/>.
import xml.etree.ElementTree as ET
import urllib.parse
import pystache
@@ -1,3 +1,22 @@
# 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/>.
import os
import re
import bpy
@@ -1,3 +1,22 @@
# 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/>.
import os
import bpy
from bpy.types import Panel
@@ -151,9 +170,9 @@ class BIM_PT_drawings(Panel):
if props.drawings:
if props.active_drawing_index < len(props.drawings):
op = row.operator("bim.open_view", icon="URL", text="")
op.view = props.drawings[props.active_drawing_index].name
op.view = props.active_drawing.name
row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index
layout.template_list("BIM_UL_generic", "", props, "drawings", props, "active_drawing_index")
layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index")
row = layout.row()
row.operator("bim.add_ifc_file")
@@ -187,7 +206,7 @@ class BIM_PT_schedules(Panel):
layout.template_list("BIM_UL_generic", "", props, "schedules", props, "active_schedule_index")
row = layout.row()
row.prop(props.schedules[props.active_schedule_index], "file")
row.prop(props.active_schedule, "file")
row.operator("bim.select_schedule_file", icon="FILE_FOLDER", text="")
@@ -324,7 +343,7 @@ class BIM_PT_annotation_utilities(Panel):
if props.drawings:
if props.active_drawing_index < len(props.drawings):
op = row.operator("bim.open_view", icon="URL", text="")
op.view = props.drawings[props.active_drawing_index].name
op.view = props.active_drawing.name
row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index
layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index")
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import bmesh
import mathutils
@@ -1,3 +1,22 @@
# 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/>.
import bpy
import numpy as np
import ifcopenshell
@@ -228,6 +247,8 @@ class SwitchRepresentation(bpy.types.Operator):
modifier = self.element_obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
modifier.operation = "DIFFERENCE"
modifier.object = opening
modifier.solver = "EXACT"
modifier.use_self = True
else:
for modifier in self.element_obj.modifiers:
if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name:
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from bpy.types import Panel
from ifcopenshell.api.geometry.data import Data
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from . import ui, prop, operator
@@ -1,9 +1,29 @@
# 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/>.
import bpy
import json
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.attribute
import ifcopenshell.api
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.georeference.data import Data
from math import radians, degrees, atan, tan, cos, sin
@@ -18,8 +38,7 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
while len(props.projected_crs) > 0:
props.projected_crs.remove(0)
props.projected_crs.clear()
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
@@ -49,8 +68,7 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
elif props.map_unit_type == "IfcConversionBasedUnit":
props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"]
while len(props.map_conversion) > 0:
props.map_conversion.remove(0)
props.map_conversion.clear()
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
@@ -96,38 +114,13 @@ class EditGeoreferencing(bpy.types.Operator):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
projected_crs = {}
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
blender_attribute = props.projected_crs.get(attribute.name())
if blender_attribute.is_null:
projected_crs[attribute.name()] = None
elif blender_attribute.data_type == "string":
projected_crs[attribute.name()] = blender_attribute.string_value
elif blender_attribute.data_type == "float":
projected_crs[attribute.name()] = blender_attribute.float_value
elif blender_attribute.data_type == "integer":
projected_crs[attribute.name()] = blender_attribute.int_value
elif blender_attribute.data_type == "boolean":
projected_crs[attribute.name()] = blender_attribute.bool_value
projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs)
map_unit = ""
if not props.is_map_unit_null:
map_unit = props.map_unit_si if props.map_unit_type == "IfcSIUnit" else props.map_unit_imperial
map_conversion = {}
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or data_type == "select":
continue
blender_attribute = props.map_conversion.get(attribute.name())
if blender_attribute.is_null:
map_conversion[attribute.name()] = None
elif blender_attribute.data_type == "string":
# We store our floats as string to prevent single precision data loss
map_conversion[attribute.name()] = float(blender_attribute.string_value)
map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, self.export_attributes)
true_north = None
if props.has_true_north:
@@ -150,6 +143,12 @@ class EditGeoreferencing(bpy.types.Operator):
bpy.ops.bim.disable_editing_georeferencing()
return {"FINISHED"}
def export_attributes(self, attributes, prop):
if not prop.is_null and prop.data_type == "string":
# We store our floats as string to prevent single precision data loss
attributes[prop.name] = float(prop.string_value)
return True
class SetBlenderGridNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_grid_north"
@@ -238,6 +237,12 @@ class ConvertLocalToGlobal(bpy.types.Operator):
bl_label = "Convert Local To Global"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and props.coordinate_input.count(",") == 2
def execute(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
@@ -285,6 +290,13 @@ class ConvertGlobalToLocal(bpy.types.Operator):
bl_label = "Convert Global To Local"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and file.by_type("IfcUnitAssignment") \
and props.coordinate_input.count(",") == 2
def execute(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
@@ -331,6 +343,11 @@ class GetCursorLocation(bpy.types.Operator):
bl_label = "Get Cursor Location"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
file = IfcStore.get_file()
return file and file.by_type("IfcUnitAssignment")
def execute(self, context):
props = context.scene.BIMGeoreferenceProperties
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
@@ -344,6 +361,12 @@ class SetCursorLocation(bpy.types.Operator):
bl_label = "Set Cursor Location"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and file.by_type("IfcUnitAssignment") and props.coordinate_output.count(",") == 2
def execute(self, context):
props = context.scene.BIMGeoreferenceProperties
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
@@ -1,3 +1,22 @@
# 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/>.
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
@@ -33,8 +52,8 @@ class BIMGeoreferenceProperties(PropertyGroup):
default="foot",
)
is_map_unit_null: BoolProperty(name="Is Map Unit Null")
coordinate_input: StringProperty(name="Coordinate Input")
coordinate_output: StringProperty(name="Coordinate Output")
coordinate_input: StringProperty(name="Coordinate Input", description="Formatted \"x,y,z\" (without quotes)")
coordinate_output: StringProperty(name="Coordinate Output", description="Formatted \"x,y,z\" (without quotes)")
has_blender_offset: BoolProperty(name="Has Blender Offset")
blender_eastings: StringProperty(name="Blender Eastings", default="0")
blender_northings: StringProperty(name="Blender Northings", default="0")
@@ -1,7 +1,27 @@
# 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/>.
import ifcopenshell.util.geolocation
from bpy.types import Panel
from ifcopenshell.api.georeference.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes, draw_attribute
class BIM_PT_gis(Panel):
@@ -32,19 +52,9 @@ class BIM_PT_gis(Panel):
row = self.layout.row(align=True)
row.label(text="Projected CRS", icon="WORLD")
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
row.operator("bim.disable_editing_georeferencing", icon="CANCEL", text="")
for attribute in props.projected_crs:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attributes(props.projected_crs, self.layout)
row = self.layout.row(align=True)
row.prop(props, "map_unit_type", text="MapUnit")
@@ -62,17 +72,7 @@ class BIM_PT_gis(Panel):
row = self.layout.row(align=True)
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
row.operator("bim.set_blender_grid_north", text="Set Blender North")
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
draw_attribute(attribute, self.layout.row())
row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN")

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