2021-08-17 08:55:29 +10:00
|
|
|
# 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/>.
|
|
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
import re
|
|
|
|
|
import bpy
|
|
|
|
|
import ifcopenshell
|
|
|
|
|
import ifcopenshell.util.element
|
2022-07-20 13:48:26 +02:00
|
|
|
from ifcopenshell.api.group.data import Data
|
2022-07-11 16:33:24 +02:00
|
|
|
from ifcopenshell.util.selector import Selector
|
2021-10-25 20:12:36 +11:00
|
|
|
import blenderbim.tool as tool
|
2021-01-23 14:17:50 +11:00
|
|
|
from blenderbim.bim.ifc import IfcStore
|
|
|
|
|
from itertools import cycle
|
2022-07-19 13:55:20 +02:00
|
|
|
from bpy.types import PropertyGroup, Operator
|
|
|
|
|
from bpy.props import (
|
|
|
|
|
PointerProperty,
|
|
|
|
|
StringProperty,
|
|
|
|
|
EnumProperty,
|
|
|
|
|
BoolProperty,
|
|
|
|
|
IntProperty,
|
|
|
|
|
FloatProperty,
|
|
|
|
|
FloatVectorProperty,
|
|
|
|
|
CollectionProperty,
|
|
|
|
|
)
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
colour_list = [
|
|
|
|
|
(0.651, 0.81, 0.892, 1),
|
|
|
|
|
(0.121, 0.471, 0.706, 1),
|
|
|
|
|
(0.699, 0.876, 0.54, 1),
|
|
|
|
|
(0.199, 0.629, 0.174, 1),
|
|
|
|
|
(0.983, 0.605, 0.602, 1),
|
|
|
|
|
(0.89, 0.101, 0.112, 1),
|
|
|
|
|
(0.989, 0.751, 0.427, 1),
|
|
|
|
|
(0.986, 0.497, 0.1, 1),
|
|
|
|
|
(0.792, 0.699, 0.839, 1),
|
|
|
|
|
(0.414, 0.239, 0.603, 1),
|
|
|
|
|
(0.993, 0.999, 0.6, 1),
|
|
|
|
|
(0.693, 0.349, 0.157, 1),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2021-07-29 23:08:17 +02:00
|
|
|
def does_keyword_exist(pattern, string, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
string = str(string)
|
2021-07-29 23:08:17 +02:00
|
|
|
props = context.scene.BIMSearchProperties
|
2021-09-09 21:27:23 +10:00
|
|
|
if props.should_use_regex and props.should_ignorecase and re.search(pattern, string, flags=re.IGNORECASE):
|
2021-01-23 14:17:50 +11:00
|
|
|
return True
|
2021-07-29 23:08:17 +02:00
|
|
|
elif props.should_use_regex and re.search(pattern, string):
|
2021-01-23 14:17:50 +11:00
|
|
|
return True
|
2021-07-29 23:08:17 +02:00
|
|
|
elif props.should_ignorecase and string.lower() == pattern.lower():
|
2021-01-23 14:17:50 +11:00
|
|
|
return True
|
|
|
|
|
elif string == pattern:
|
|
|
|
|
return True
|
|
|
|
|
|
2022-07-20 14:54:03 +02:00
|
|
|
# hack to close popup
|
|
|
|
|
# https://blender.stackexchange.com/a/202576/130742
|
|
|
|
|
def close_operator_panel(event):
|
|
|
|
|
x, y = event.mouse_x, event.mouse_y
|
|
|
|
|
bpy.context.window.cursor_warp(10, 10)
|
|
|
|
|
move_back = lambda: bpy.context.window.cursor_warp(x, y)
|
|
|
|
|
bpy.app.timers.register(move_back, first_interval=0.01)
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class EditBlenderCollection(Operator):
|
2022-07-11 16:33:24 +02:00
|
|
|
bl_idname = "bim.edit_blender_collection"
|
|
|
|
|
bl_label = "Add or Remove blender collection item"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2022-07-19 13:55:20 +02:00
|
|
|
option: StringProperty()
|
|
|
|
|
collection: StringProperty()
|
|
|
|
|
index: IntProperty()
|
2022-07-11 16:33:24 +02:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
if self.option == "add":
|
|
|
|
|
getattr(context.bim_prop_group, self.collection).add()
|
|
|
|
|
else:
|
|
|
|
|
getattr(context.bim_prop_group, self.collection).remove(self.index)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class SelectGlobalId(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to select the objects that match with the given Global ID"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.select_global_id"
|
|
|
|
|
bl_label = "Select GlobalId"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2022-07-19 13:55:20 +02:00
|
|
|
global_id: StringProperty()
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-11-19 00:06:22 +01:00
|
|
|
ifc_file = tool.Ifc.get()
|
2021-01-23 14:17:50 +11:00
|
|
|
props = context.scene.BIMSearchProperties
|
2021-03-25 20:29:37 +11:00
|
|
|
global_id = self.global_id or props.global_id
|
2021-11-19 00:06:22 +01:00
|
|
|
entity = ifc_file.by_guid(global_id)
|
|
|
|
|
obj = tool.Ifc.get_object(entity)
|
|
|
|
|
obj.select_set(True)
|
|
|
|
|
bpy.context.view_layer.objects.active = obj
|
2021-01-23 14:17:50 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class SelectIfcClass(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to select all objects that match with the given IFC class"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.select_ifc_class"
|
|
|
|
|
bl_label = "Select IFC Class"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2022-07-19 13:55:20 +02:00
|
|
|
ifc_class: StringProperty()
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
for obj in context.visible_objects:
|
2022-06-09 21:46:24 +10:00
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id or obj.is_library_indirect:
|
2021-01-23 14:17:50 +11:00
|
|
|
continue
|
2022-07-18 15:39:19 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-07-29 23:08:17 +02:00
|
|
|
if does_keyword_exist(self.ifc_class, element.is_a(), context):
|
2021-01-23 14:17:50 +11:00
|
|
|
obj.select_set(True)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class SelectAttribute(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to select all objects that match with the given Attribute Name and Value"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.select_attribute"
|
|
|
|
|
bl_label = "Select Attribute"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
props = context.scene.BIMSearchProperties
|
|
|
|
|
pattern = props.search_attribute_value
|
|
|
|
|
attribute_name = props.search_attribute_name
|
|
|
|
|
for obj in context.visible_objects:
|
|
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
2022-07-18 15:39:19 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-03-08 16:42:47 +11:00
|
|
|
if context.scene.BIMSearchProperties.should_ignorecase:
|
|
|
|
|
data = element.get_info()
|
2022-07-18 15:39:19 +02:00
|
|
|
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
|
2021-03-08 16:42:47 +11:00
|
|
|
else:
|
|
|
|
|
value = getattr(element, attribute_name, None)
|
2021-07-29 23:08:17 +02:00
|
|
|
if does_keyword_exist(pattern, value, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
obj.select_set(True)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class SelectPset(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to select all objects that match with the given Pset Name, Properties Name and Value"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.select_pset"
|
|
|
|
|
bl_label = "Select Pset"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
props = context.scene.BIMSearchProperties
|
|
|
|
|
search_pset_name = props.search_pset_name
|
|
|
|
|
search_prop_name = props.search_prop_name
|
|
|
|
|
pattern = props.search_pset_value
|
|
|
|
|
for obj in context.visible_objects:
|
|
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
2022-07-18 15:39:19 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-01-23 14:17:50 +11:00
|
|
|
psets = ifcopenshell.util.element.get_psets(element)
|
2021-06-06 20:55:25 +10:00
|
|
|
if search_pset_name == "":
|
|
|
|
|
props = {}
|
|
|
|
|
[props.update(p) for p in psets.values()]
|
|
|
|
|
else:
|
|
|
|
|
props = None
|
2021-03-08 16:42:47 +11:00
|
|
|
if context.scene.BIMSearchProperties.should_ignorecase:
|
2022-07-18 15:39:19 +02:00
|
|
|
props = props or next((v for k, v in psets.items() if k.lower() == search_pset_name.lower()), {})
|
|
|
|
|
value = str(next((v for k, v in props.items() if k.lower() == search_prop_name.lower()), None))
|
2021-03-08 16:42:47 +11:00
|
|
|
else:
|
2021-06-06 20:55:25 +10:00
|
|
|
props = props or psets.get(search_pset_name, {})
|
2021-03-08 16:42:47 +11:00
|
|
|
value = props.get(search_prop_name, None)
|
2021-07-29 23:08:17 +02:00
|
|
|
if does_keyword_exist(pattern, value, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
obj.select_set(True)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ColourByAttribute(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to colour different objects according to given Attribute Name"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.colour_by_attribute"
|
|
|
|
|
bl_label = "Colour by Attribute"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-07 10:25:19 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-29 23:08:17 +02:00
|
|
|
self.store_state(context)
|
2021-07-07 10:25:19 +10:00
|
|
|
result = self._execute(context)
|
|
|
|
|
IfcStore.add_transaction_operation(self)
|
|
|
|
|
IfcStore.end_transaction(self)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
colours = cycle(colour_list)
|
|
|
|
|
values = {}
|
|
|
|
|
attribute_name = context.scene.BIMSearchProperties.search_attribute_name
|
|
|
|
|
for obj in context.visible_objects:
|
|
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
2022-07-19 10:05:20 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-03-08 16:42:47 +11:00
|
|
|
if context.scene.BIMSearchProperties.should_ignorecase:
|
|
|
|
|
data = element.get_info()
|
2022-07-19 10:05:20 +02:00
|
|
|
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
|
2021-03-08 16:42:47 +11:00
|
|
|
else:
|
|
|
|
|
value = getattr(element, attribute_name, None)
|
2021-01-23 14:17:50 +11:00
|
|
|
if value not in values:
|
|
|
|
|
values[value] = next(colours)
|
|
|
|
|
obj.color = values[value]
|
2021-07-29 23:08:17 +02:00
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
|
|
|
|
areas[0].spaces[0].shading.color_type = "OBJECT"
|
2021-01-23 14:17:50 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-07-29 23:08:17 +02:00
|
|
|
def store_state(self, context):
|
|
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
2022-07-19 10:05:20 +02:00
|
|
|
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
|
2021-07-07 10:25:19 +10:00
|
|
|
|
|
|
|
|
def rollback(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = data["color_type"]
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = "OBJECT"
|
|
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ColourByPset(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to colour different objects according to given Prop Name"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.colour_by_pset"
|
|
|
|
|
bl_label = "Colour by Pset"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-07 10:25:19 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-29 23:08:17 +02:00
|
|
|
self.store_state(context)
|
2021-07-07 10:25:19 +10:00
|
|
|
result = self._execute(context)
|
|
|
|
|
IfcStore.add_transaction_operation(self)
|
|
|
|
|
IfcStore.end_transaction(self)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
colours = cycle(colour_list)
|
|
|
|
|
values = {}
|
|
|
|
|
search_pset_name = context.scene.BIMSearchProperties.search_pset_name
|
|
|
|
|
search_prop_name = context.scene.BIMSearchProperties.search_prop_name
|
|
|
|
|
for obj in context.visible_objects:
|
|
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
2022-07-19 10:05:20 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-01-23 14:17:50 +11:00
|
|
|
psets = ifcopenshell.util.element.get_psets(element)
|
2021-06-06 20:55:25 +10:00
|
|
|
if search_pset_name == "":
|
|
|
|
|
props = {}
|
|
|
|
|
[props.update(p) for p in psets.values()]
|
|
|
|
|
else:
|
|
|
|
|
props = None
|
2021-03-08 16:42:47 +11:00
|
|
|
if context.scene.BIMSearchProperties.should_ignorecase:
|
2022-07-19 10:05:20 +02:00
|
|
|
props = props or next((v for k, v in psets.items() if k.lower() == search_pset_name.lower()), {})
|
|
|
|
|
value = str(next((v for k, v in props.items() if k.lower() == search_prop_name.lower()), None))
|
2021-03-08 16:42:47 +11:00
|
|
|
else:
|
2021-06-06 20:55:25 +10:00
|
|
|
props = props or psets.get(search_pset_name, {})
|
2021-03-08 16:42:47 +11:00
|
|
|
value = str(props.get(search_prop_name, None))
|
2021-01-23 14:17:50 +11:00
|
|
|
if value not in values:
|
|
|
|
|
values[value] = next(colours)
|
|
|
|
|
obj.color = values[value]
|
2021-07-29 23:08:17 +02:00
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
|
|
|
|
areas[0].spaces[0].shading.color_type = "OBJECT"
|
2021-01-23 14:17:50 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-07-29 23:08:17 +02:00
|
|
|
def store_state(self, context):
|
|
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
2022-07-19 10:05:20 +02:00
|
|
|
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
|
2021-07-07 10:25:19 +10:00
|
|
|
|
|
|
|
|
def rollback(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = data["color_type"]
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = "OBJECT"
|
|
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ColourByClass(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Click to colour different objects according to their IFC Classes"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.colour_by_class"
|
|
|
|
|
bl_label = "Colour by Class"
|
2021-07-06 12:19:45 +02:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-01-23 14:17:50 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-07 10:25:19 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-29 23:08:17 +02:00
|
|
|
self.store_state(context)
|
2021-07-07 10:25:19 +10:00
|
|
|
result = self._execute(context)
|
|
|
|
|
IfcStore.add_transaction_operation(self)
|
|
|
|
|
IfcStore.end_transaction(self)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-01-23 14:17:50 +11:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
colours = cycle(colour_list)
|
|
|
|
|
ifc_classes = {}
|
2021-07-29 23:08:17 +02:00
|
|
|
for obj in context.visible_objects:
|
2021-01-23 14:17:50 +11:00
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
2022-07-19 10:05:20 +02:00
|
|
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
2021-01-23 14:17:50 +11:00
|
|
|
ifc_class = element.is_a()
|
|
|
|
|
if ifc_class not in ifc_classes:
|
|
|
|
|
ifc_classes[ifc_class] = next(colours)
|
|
|
|
|
obj.color = ifc_classes[ifc_class]
|
2021-07-29 23:08:17 +02:00
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
|
|
|
|
areas[0].spaces[0].shading.color_type = "OBJECT"
|
2021-01-23 14:17:50 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-07-29 23:08:17 +02:00
|
|
|
def store_state(self, context):
|
|
|
|
|
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
2021-07-07 10:25:19 +10:00
|
|
|
if areas:
|
2022-07-19 10:05:20 +02:00
|
|
|
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
|
2021-07-07 10:25:19 +10:00
|
|
|
|
|
|
|
|
def rollback(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = data["color_type"]
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
if data:
|
|
|
|
|
data["area"].spaces[0].shading.color_type = "OBJECT"
|
|
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ResetObjectColours(Operator):
|
2021-08-01 17:33:55 +08:00
|
|
|
"""Reset the colour of selected objects"""
|
2021-09-09 21:27:23 +10:00
|
|
|
|
2021-01-23 14:17:50 +11:00
|
|
|
bl_idname = "bim.reset_object_colours"
|
|
|
|
|
bl_label = "Reset Colours"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-29 23:08:17 +02:00
|
|
|
for obj in context.selected_objects:
|
2021-01-23 14:17:50 +11:00
|
|
|
obj.color = (1, 1, 1, 1)
|
|
|
|
|
return {"FINISHED"}
|
2021-10-25 16:33:57 +08:00
|
|
|
|
2021-11-04 22:23:26 +11:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ToggleFilterSelection(Operator):
|
2021-12-28 23:46:22 +01:00
|
|
|
"Click to select/deselect current selection"
|
2021-11-04 22:23:26 +11:00
|
|
|
bl_idname = "bim.toggle_filter_selection"
|
|
|
|
|
bl_label = "Toggle Filter Selection"
|
2022-07-19 13:55:20 +02:00
|
|
|
action: EnumProperty(items=(("SELECT", "Select", ""), ("DESELECT", "Deselect", "")))
|
2021-11-04 22:23:26 +11:00
|
|
|
|
2021-10-29 16:29:43 +08:00
|
|
|
def execute(self, context):
|
2021-12-28 23:46:22 +01:00
|
|
|
props = bpy.context.scene.BIMSearchProperties
|
2021-11-04 22:23:26 +11:00
|
|
|
if self.action == "SELECT":
|
2021-10-29 16:29:43 +08:00
|
|
|
self.selecting_actionbool = True
|
|
|
|
|
else:
|
|
|
|
|
self.selecting_actionbool = False
|
2021-12-28 23:46:22 +01:00
|
|
|
if props.filter_type == "CLASSES":
|
|
|
|
|
for ifc_class in props.filter_classes:
|
|
|
|
|
ifc_class.is_selected = self.selecting_actionbool
|
|
|
|
|
elif props.filter_type == "BUILDINGSTOREYS":
|
|
|
|
|
for building_storey in props.filter_building_storeys:
|
|
|
|
|
building_storey.is_selected = self.selecting_actionbool
|
2021-11-04 22:23:26 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-10-25 20:12:36 +11:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ActivateIfcClassFilter(Operator):
|
2021-12-28 23:46:22 +01:00
|
|
|
"""Filter the current selection by IFC class"""
|
2021-10-25 20:12:36 +11:00
|
|
|
|
2021-12-28 23:46:22 +01:00
|
|
|
bl_idname = "bim.activate_ifc_class_filter"
|
|
|
|
|
bl_label = "Filter by Class"
|
2021-10-25 20:12:36 +11:00
|
|
|
|
2021-12-28 23:46:22 +01:00
|
|
|
def invoke(self, context, event):
|
|
|
|
|
props = bpy.context.scene.BIMSearchProperties
|
|
|
|
|
props.filter_classes.clear()
|
2021-10-25 20:12:36 +11:00
|
|
|
ifc_types = {}
|
|
|
|
|
for obj in context.selected_objects:
|
|
|
|
|
element = tool.Ifc.get_entity(obj)
|
|
|
|
|
if not element:
|
|
|
|
|
continue
|
|
|
|
|
ifc_types.setdefault(element.is_a(), 0)
|
|
|
|
|
ifc_types[element.is_a()] += 1
|
|
|
|
|
|
2021-10-29 16:29:43 +08:00
|
|
|
for name, total in dict(sorted(ifc_types.items())).items():
|
2021-12-28 23:46:22 +01:00
|
|
|
new = props.filter_classes.add()
|
2021-10-25 20:12:36 +11:00
|
|
|
new.name = name
|
|
|
|
|
new.total = total
|
2021-12-28 23:46:22 +01:00
|
|
|
props.filter_type = "CLASSES"
|
2021-10-25 20:12:36 +11:00
|
|
|
|
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=250)
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMSearchProperties.filter_classes.clear()
|
|
|
|
|
return {"FINISHED"}
|
2021-10-25 16:33:57 +08:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
2021-10-25 20:12:36 +11:00
|
|
|
self.layout.template_list(
|
2021-12-28 23:46:22 +01:00
|
|
|
"BIM_UL_ifc_class_filter",
|
2021-10-25 16:33:57 +08:00
|
|
|
"",
|
2021-10-25 20:12:36 +11:00
|
|
|
context.scene.BIMSearchProperties,
|
|
|
|
|
"filter_classes",
|
|
|
|
|
context.scene.BIMSearchProperties,
|
|
|
|
|
"filter_classes_index",
|
2021-11-04 22:23:26 +11:00
|
|
|
rows=20
|
|
|
|
|
if len(bpy.context.scene.BIMSearchProperties.filter_classes) > 20
|
|
|
|
|
else len(bpy.context.scene.BIMSearchProperties.filter_classes),
|
2021-10-25 20:12:36 +11:00
|
|
|
)
|
2021-11-04 22:23:26 +11:00
|
|
|
row = self.layout.row(align=True)
|
2022-07-19 10:05:20 +02:00
|
|
|
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
|
|
|
|
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
|
2021-12-28 23:46:22 +01:00
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class ActivateIfcBuildingStoreyFilter(Operator):
|
2021-12-28 23:46:22 +01:00
|
|
|
"""Filter the current selection by Building Storey"""
|
|
|
|
|
|
|
|
|
|
bl_idname = "bim.activate_ifc_building_storey_filter"
|
|
|
|
|
bl_label = "Filter by Building Storey"
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
props = bpy.context.scene.BIMSearchProperties
|
|
|
|
|
props.filter_building_storeys.clear()
|
|
|
|
|
|
|
|
|
|
ifc_building_storeys = {}
|
|
|
|
|
for obj in context.selected_objects:
|
|
|
|
|
storey = tool.Misc.get_object_storey(obj)
|
|
|
|
|
if not storey:
|
|
|
|
|
continue
|
|
|
|
|
ifc_building_storeys.setdefault(storey.Name, 0)
|
|
|
|
|
ifc_building_storeys[storey.Name] += 1
|
|
|
|
|
|
|
|
|
|
for name, total in dict(sorted(ifc_building_storeys.items())).items():
|
|
|
|
|
new = props.filter_building_storeys.add()
|
|
|
|
|
new.name = name
|
|
|
|
|
new.total = total
|
|
|
|
|
|
|
|
|
|
props.filter_type = "BUILDINGSTOREYS"
|
|
|
|
|
|
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=250)
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.context.scene.BIMSearchProperties.filter_building_storeys.clear()
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
self.layout.template_list(
|
|
|
|
|
"BIM_UL_ifc_building_storey_filter",
|
|
|
|
|
"",
|
|
|
|
|
context.scene.BIMSearchProperties,
|
|
|
|
|
"filter_building_storeys",
|
|
|
|
|
context.scene.BIMSearchProperties,
|
|
|
|
|
"filter_building_storeys_index",
|
|
|
|
|
rows=20
|
|
|
|
|
if len(bpy.context.scene.BIMSearchProperties.filter_building_storeys) > 20
|
|
|
|
|
else len(bpy.context.scene.BIMSearchProperties.filter_building_storeys),
|
|
|
|
|
)
|
|
|
|
|
row = self.layout.row(align=True)
|
2022-07-19 10:05:20 +02:00
|
|
|
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
|
|
|
|
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
|
2022-07-19 09:21:13 +02:00
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class UnhideAllElements(Operator):
|
2022-07-11 16:33:24 +02:00
|
|
|
"""Filter model elements based on selection"""
|
2022-07-19 10:05:20 +02:00
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
bl_idname = "bim.reset_3d_view"
|
|
|
|
|
bl_label = "Reset 3D View"
|
2022-07-19 09:21:13 +02:00
|
|
|
bl_idname = "bim.unhide_all_elements"
|
|
|
|
|
bl_label = "Unhide All Elements"
|
2022-07-11 16:33:24 +02:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
for obj in bpy.data.scenes["Scene"].objects:
|
|
|
|
|
obj.hide_set(False)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
class FilterModelElements(Operator):
|
2022-07-11 16:33:24 +02:00
|
|
|
"""Filter model elements based on selection"""
|
2022-07-19 10:05:20 +02:00
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
bl_idname = "bim.filter_model_elements"
|
|
|
|
|
bl_label = "Filter Model Elements"
|
2022-07-19 13:55:20 +02:00
|
|
|
option: StringProperty("select|isolate|hide")
|
2022-07-11 16:33:24 +02:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
selector = context.scene.IfcSelectorProperties
|
2022-07-19 13:55:20 +02:00
|
|
|
selection = selector.selector_query_syntax if selector.manual_override else self.add_groups(selector)
|
2022-07-11 16:33:24 +02:00
|
|
|
selector.selector_query_syntax = selection
|
|
|
|
|
self.update_model_view(context, selection)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def add_groups(self, selector):
|
2022-07-19 10:05:20 +02:00
|
|
|
selection = ""
|
2022-07-11 16:33:24 +02:00
|
|
|
for group_index, group in enumerate(selector.groups):
|
|
|
|
|
if group_index != 0:
|
|
|
|
|
selection += " | "
|
2022-07-19 10:05:20 +02:00
|
|
|
selection += "(" if len(selector.groups) > 1 else ""
|
2022-07-11 16:33:24 +02:00
|
|
|
selection = self.add_queries(selection, group)
|
|
|
|
|
|
2022-07-19 10:05:20 +02:00
|
|
|
selection += ")" if len(selector.groups) > 1 else ""
|
2022-07-11 16:33:24 +02:00
|
|
|
return selection
|
|
|
|
|
|
|
|
|
|
def add_queries(self, selection, group):
|
|
|
|
|
for query_index, query in enumerate(group.queries):
|
|
|
|
|
if query_index != 0:
|
|
|
|
|
selection += " & " if query.and_or == "and" else " | "
|
|
|
|
|
|
|
|
|
|
if query.selector == "IFC Class":
|
|
|
|
|
active_option = query.active_option.split(": ")[1]
|
|
|
|
|
selection += f".{active_option}"
|
|
|
|
|
selection = self.add_filters(selection, query)
|
|
|
|
|
|
|
|
|
|
elif query.selector == "GlobalId":
|
2022-07-20 13:48:26 +02:00
|
|
|
selection += f"#{query.value}"
|
2022-07-11 16:33:24 +02:00
|
|
|
|
|
|
|
|
elif query.selector == "IfcElementType":
|
|
|
|
|
index = int(query.active_sub_option.split(":")[0])
|
|
|
|
|
selection += f"* #{query.sub_options[index].global_id}"
|
2022-07-19 10:05:20 +02:00
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
elif query.selector == "IfcSpatialElement":
|
|
|
|
|
index = int(query.active_sub_option.split(":")[0])
|
|
|
|
|
selection += f"@ #{query.sub_options[index].global_id}"
|
|
|
|
|
return selection
|
|
|
|
|
|
|
|
|
|
def add_filters(self, selection, query):
|
|
|
|
|
for f_index, f in enumerate(query.filters):
|
2022-07-19 10:05:20 +02:00
|
|
|
|
|
|
|
|
if f_index != 0:
|
2022-07-11 16:33:24 +02:00
|
|
|
selection += " & " if f.and_or == "and" else " | "
|
|
|
|
|
selection += f".{query.active_option}"
|
2022-07-19 10:05:20 +02:00
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
selection += "["
|
|
|
|
|
|
2022-07-18 15:39:19 +02:00
|
|
|
if f.selector == "IfcPropertySet":
|
|
|
|
|
selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}="{f.value}"'
|
2022-07-11 16:33:24 +02:00
|
|
|
elif f.selector == "Attribute":
|
|
|
|
|
selection += f'{f.attribute} {"!" if f.negation else ""}= "{f.value}"'
|
|
|
|
|
|
|
|
|
|
selection += "]"
|
|
|
|
|
return selection
|
2022-07-19 10:05:20 +02:00
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
def update_model_view(self, context, selection):
|
|
|
|
|
query = Selector.parse(IfcStore.file, selection)
|
2022-07-19 10:05:20 +02:00
|
|
|
sel_element_ids = [e.id() for e in query]
|
|
|
|
|
bpy.ops.object.select_all(action="DESELECT")
|
2022-07-11 16:33:24 +02:00
|
|
|
|
|
|
|
|
for obj in bpy.data.scenes["Scene"].objects:
|
2022-07-19 10:05:20 +02:00
|
|
|
obj.hide_set(False) # reset 3d view
|
|
|
|
|
|
2022-07-11 16:33:24 +02:00
|
|
|
if self.option == "select":
|
|
|
|
|
if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids:
|
|
|
|
|
obj.select_set(True)
|
|
|
|
|
elif self.option == "isolate":
|
2022-07-19 10:05:20 +02:00
|
|
|
if obj.BIMObjectProperties.ifc_definition_id not in sel_element_ids:
|
2022-07-11 16:33:24 +02:00
|
|
|
obj.hide_set(True)
|
|
|
|
|
elif self.option == "hide":
|
2022-07-19 10:05:20 +02:00
|
|
|
if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids:
|
2022-07-11 16:33:24 +02:00
|
|
|
obj.hide_set(True)
|
2022-07-19 13:55:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class IfcSelector(Operator):
|
2022-07-19 10:05:20 +02:00
|
|
|
"""Select elements in model with IFC Selector"""
|
|
|
|
|
bl_idname = "bim.ifc_selector"
|
|
|
|
|
bl_label = "Select elements with IFC Selector"
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=800)
|
2022-07-19 13:55:20 +02:00
|
|
|
|
2022-07-19 10:05:20 +02:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return IfcStore.get_file()
|
2022-07-19 13:55:20 +02:00
|
|
|
|
2022-07-19 10:05:20 +02:00
|
|
|
def execute(self, context):
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
2022-07-19 14:16:59 +02:00
|
|
|
from . import ui
|
|
|
|
|
ui.IfcSelectorUI.draw(context, self.layout)
|
2022-07-19 13:55:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SaveSelectorQuery(Operator):
|
|
|
|
|
bl_idname = "bim.save_selector_query"
|
|
|
|
|
bl_label = "Save Selector Query"
|
|
|
|
|
save_name: StringProperty()
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=400)
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.prop(self, "save_name")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
ifc_selector = context.scene.IfcSelectorProperties
|
|
|
|
|
new = ifc_selector.query_library.add()
|
|
|
|
|
new.name = self.save_name
|
|
|
|
|
new.query = ifc_selector.selector_query_syntax
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OpenQueryLibrary(Operator):
|
|
|
|
|
"""Open Query Library"""
|
|
|
|
|
bl_idname = "bim.open_query_library"
|
|
|
|
|
bl_label = "Open Query Library"
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2022-07-20 14:54:03 +02:00
|
|
|
return context.window_manager.invoke_popup(self, width=400)
|
2022-07-19 13:55:20 +02:00
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
ifc_selector = context.scene.IfcSelectorProperties
|
|
|
|
|
|
|
|
|
|
for index, query in enumerate(ifc_selector.query_library):
|
|
|
|
|
row = layout.row(align=True)
|
|
|
|
|
row.prop(query, "query", text=query.name)
|
|
|
|
|
op = row.operator("bim.load_query", icon="SORT_ASC", text="")
|
|
|
|
|
op.index = index
|
|
|
|
|
|
|
|
|
|
row.context_pointer_set(name="bim_prop_group", data=ifc_selector)
|
|
|
|
|
op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
|
|
|
|
|
op.option = "remove"
|
|
|
|
|
op.collection = "query_library"
|
|
|
|
|
op.index = index
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoadQuery(Operator):
|
|
|
|
|
bl_idname = "bim.load_query"
|
|
|
|
|
bl_label = "Load Query"
|
|
|
|
|
index: IntProperty()
|
|
|
|
|
|
2022-07-20 14:54:03 +02:00
|
|
|
def invoke(self, context, event):
|
|
|
|
|
close_operator_panel(event)
|
|
|
|
|
return self.execute(context)
|
|
|
|
|
|
2022-07-19 13:55:20 +02:00
|
|
|
def execute(self, context):
|
|
|
|
|
ifc_selector = context.scene.IfcSelectorProperties
|
|
|
|
|
ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query
|
|
|
|
|
return {"FINISHED"}
|
2022-07-20 13:48:26 +02:00
|
|
|
|
|
|
|
|
class AddToIfcGroup(Operator):
|
|
|
|
|
bl_idname = "bim.add_to_ifc_group"
|
|
|
|
|
bl_label = "Add to IFC Group"
|
|
|
|
|
group_name: StringProperty(name="Group Name")
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=400)
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.prop(self, "group_name")
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
ifc_selector = context.scene.IfcSelectorProperties
|
|
|
|
|
selector_query_syntax = ifc_selector.selector_query_syntax
|
|
|
|
|
|
2022-07-21 16:10:10 +02:00
|
|
|
group = ifcopenshell.api.run("group.add_group", self.file, **{"Name": self.group_name, "Description": f'*selector*{selector_query_syntax}*selector*'})
|
2022-07-20 13:48:26 +02:00
|
|
|
objects = Selector.parse(self.file, selector_query_syntax)
|
2022-07-21 08:37:44 +02:00
|
|
|
|
2022-07-21 08:20:25 +02:00
|
|
|
ifcopenshell.api.run("group.assign_group", self.file, **{"product": objects, "group": group})
|
2022-07-20 13:48:26 +02:00
|
|
|
Data.load(IfcStore.get_file())
|
|
|
|
|
|
|
|
|
|
return {"FINISHED"}
|