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
2023-07-22 22:49:22 +10:00
import json
2021-01-23 14:17:50 +11:00
import ifcopenshell
import ifcopenshell . util . element
2023-07-22 16:36:19 +10:00
import ifcopenshell . util . selector
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
2022-07-21 17:50:43 +02:00
from blenderbim . bim . helper import close_operator_panel
2022-08-01 10:16:27 +02:00
from blenderbim . bim . module . group import ui
2023-02-11 13:19:08 +01:00
import blenderbim . core . search as core
2021-01-23 14:17:50 +11:00
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 ) ,
]
2023-07-22 16:36:19 +10:00
class AddFilterGroup ( Operator ) :
bl_idname = " bim.add_filter_group "
bl_label = " Add Filter Group "
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 16:36:19 +10:00
def execute ( self , context ) :
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
props . filter_groups . add ( )
2023-07-22 16:36:19 +10:00
return { " FINISHED " }
class RemoveFilterGroup ( Operator ) :
bl_idname = " bim.remove_filter_group "
bl_label = " Remove Filter Group "
index : IntProperty ( )
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 16:36:19 +10:00
def execute ( self , context ) :
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
2023-07-22 16:36:19 +10:00
props . filter_groups . remove ( self . index )
return { " FINISHED " }
class RemoveFilter ( Operator ) :
bl_idname = " bim.remove_filter "
bl_label = " Remove Filter Group "
group_index : IntProperty ( )
index : IntProperty ( )
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 16:36:19 +10:00
def execute ( self , context ) :
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
2023-07-22 16:36:19 +10:00
props . filter_groups [ self . group_index ] . filters . remove ( self . index )
return { " FINISHED " }
class AddFilter ( Operator ) :
bl_idname = " bim.add_filter "
bl_label = " Add Filter "
index : IntProperty ( )
type : StringProperty ( )
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 16:36:19 +10:00
def execute ( self , context ) :
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
new = props . filter_groups [ self . index ] . filters . add ( )
2023-07-22 16:36:19 +10:00
new . type = self . type
return { " FINISHED " }
2023-08-18 16:58:07 +10:00
class SelectFilterElements ( bpy . types . Operator ) :
bl_idname = " bim.select_filter_elements "
bl_label = " Select Filter Elements "
bl_options = { " REGISTER " , " UNDO " }
group_index : IntProperty ( )
index : IntProperty ( )
module : StringProperty ( )
def execute ( self , context ) :
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
global_ids = [ ]
for obj in context . selected_objects :
element = tool . Ifc . get_entity ( obj )
if element :
global_id = getattr ( element , " GlobalId " , None )
if global_id :
global_ids . append ( global_id )
if len ( global_ids ) > 50 :
# Too much to store in a string property
name = " globalid-filter- " + ifcopenshell . guid . new ( )
text_data = bpy . data . texts . new ( name )
text_data . from_string ( " , " . join ( global_ids ) )
props . filter_groups [ self . group_index ] . filters [ self . index ] . value = f " bpy.data.texts[ ' { name } ' ] "
else :
props . filter_groups [ self . group_index ] . filters [ self . index ] . value = " , " . join ( global_ids )
return { " FINISHED " }
2023-08-18 23:39:13 +10:00
class EditFilterQuery ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.edit_filter_query "
bl_label = " Edit Filter Query "
bl_description = " Edit the underlying filter query for advanced users "
bl_options = { " REGISTER " , " UNDO " }
query : StringProperty ( name = " Query " )
module : StringProperty ( )
def _execute ( self , context ) :
if self . query == self . old_query :
return
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
try :
tool . Search . import_filter_query ( self . query , props . filter_groups )
except :
return
def draw ( self , context ) :
row = self . layout . row ( )
row . prop ( self , " query " , text = " " )
def invoke ( self , context , event ) :
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
self . query = tool . Search . export_filter_query ( props . filter_groups )
self . old_query = self . query
return context . window_manager . invoke_props_dialog ( self )
2023-07-22 16:36:19 +10:00
class Search ( Operator ) :
bl_idname = " bim.search "
bl_label = " Search "
def execute ( self , context ) :
props = context . scene . BIMSearchProperties
2023-07-22 22:49:22 +10:00
results = ifcopenshell . util . selector . filter_elements (
tool . Ifc . get ( ) , tool . Search . export_filter_query ( props . filter_groups )
)
2023-07-22 16:36:19 +10:00
total_selected = 0
for element in results :
obj = tool . Ifc . get_object ( element )
if obj :
obj . select_set ( True )
self . report ( { " INFO " } , f " { len ( results ) } Results " )
return { " FINISHED " }
2023-07-22 22:49:22 +10:00
class SaveSearch ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.save_search "
bl_label = " Save Search "
bl_description = " Save search filter to an IFC group "
bl_options = { " REGISTER " , " UNDO " }
name : StringProperty ( name = " Name " )
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 22:49:22 +10:00
def _execute ( self , context ) :
if not self . name :
return
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
try :
query = tool . Search . export_filter_query ( props . filter_groups )
results = ifcopenshell . util . selector . filter_elements ( tool . Ifc . get ( ) , query )
except :
return
2023-07-22 22:49:22 +10:00
description = json . dumps ( { " type " : " BBIM_Search " , " query " : query } )
2023-07-23 15:20:14 +10:00
group = [ g for g in tool . Ifc . get ( ) . by_type ( " IfcGroup " ) if g . Name == self . name ]
if group :
group = group [ 0 ]
2023-08-21 23:44:20 +10:00
group . Description = description
2023-07-23 15:20:14 +10:00
else :
group = ifcopenshell . api . run ( " group.add_group " , tool . Ifc . get ( ) , Name = self . name , Description = description )
2023-08-18 13:43:42 +10:00
if results :
ifcopenshell . api . run ( " group.assign_group " , tool . Ifc . get ( ) , products = list ( results ) , group = group )
def draw ( self , context ) :
row = self . layout . row ( )
row . prop ( self , " name " )
2023-07-22 22:49:22 +10:00
def invoke ( self , context , event ) :
return context . window_manager . invoke_props_dialog ( self )
class LoadSearch ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.load_search "
bl_label = " Load Search "
bl_description = " Load search filter from an IFC group "
bl_options = { " REGISTER " , " UNDO " }
2023-08-18 13:43:42 +10:00
module : StringProperty ( )
2023-07-22 22:49:22 +10:00
def _execute ( self , context ) :
2023-08-18 13:43:42 +10:00
if self . module == " search " :
props = context . scene . BIMSearchProperties
elif self . module == " csv " :
props = context . scene . CsvProperties
group = tool . Ifc . get ( ) . by_id ( int ( context . scene . BIMSearchProperties . saved_searches ) )
2023-08-18 23:39:13 +10:00
query = tool . Search . import_filter_query ( tool . Search . get_group_query ( group ) , props . filter_groups )
2023-07-22 22:49:22 +10:00
def draw ( self , context ) :
props = context . scene . BIMSearchProperties
row = self . layout . row ( )
row . prop ( props , " saved_searches " , text = " " )
def invoke ( self , context , event ) :
return context . window_manager . invoke_props_dialog ( self )
2023-07-22 16:36:19 +10:00
2023-07-23 15:20:14 +10:00
class ColourByProperty ( Operator ) :
bl_idname = " bim.colour_by_property "
bl_label = " Colour by Property "
bl_options = { " REGISTER " , " UNDO " }
def execute ( self , context ) :
IfcStore . begin_transaction ( self )
self . store_state ( context )
result = self . _execute ( context )
IfcStore . add_transaction_operation ( self )
IfcStore . end_transaction ( self )
return result
def _execute ( self , context ) :
props = context . scene . BIMSearchProperties
query = props . colourscheme_query
if not query :
self . report ( { " ERROR " } , " No Query Provided " )
return { " CANCELLED " }
colours = cycle ( colour_list )
colourscheme = { }
if len ( props . colourscheme ) :
colourscheme = { cs . name : cs . colour [ 0 : 3 ] for cs in props . colourscheme }
for obj in context . visible_objects :
element = tool . Ifc . get_entity ( obj )
if not element :
continue
value = str ( ifcopenshell . util . selector . get_element_value ( element , query ) )
if value not in colourscheme :
colourscheme [ value ] = next ( colours ) [ 0 : 3 ]
obj . color = ( * colourscheme [ value ] , 1 )
areas = [ a for a in context . screen . areas if a . type == " VIEW_3D " ]
if areas :
areas [ 0 ] . spaces [ 0 ] . shading . color_type = " OBJECT "
props . colourscheme . clear ( )
for value , colour in colourscheme . items ( ) :
new = props . colourscheme . add ( )
new . name = str ( value )
new . colour = colour [ 0 : 3 ]
return { " FINISHED " }
def store_state ( self , context ) :
areas = [ a for a in context . screen . areas if a . type == " VIEW_3D " ]
if areas :
self . transaction_data = { " area " : areas [ 0 ] , " color_type " : areas [ 0 ] . spaces [ 0 ] . shading . color_type }
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 "
class SaveColourscheme ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.save_colourscheme "
bl_label = " Save Colourscheme "
bl_description = " Save colourscheme to an IFC group "
bl_options = { " REGISTER " , " UNDO " }
name : StringProperty ( name = " Name " )
def _execute ( self , context ) :
if not self . name :
return
props = context . scene . BIMSearchProperties
query = props . colourscheme_query
group = [ g for g in tool . Ifc . get ( ) . by_type ( " IfcGroup " ) if g . Name == self . name ]
2023-08-18 17:09:00 +01:00
coulour_scheme = { cs . name : cs . colour [ 0 : 3 ] for cs in props . colourscheme }
2023-07-23 15:20:14 +10:00
if group :
group = group [ 0 ]
description = json . loads ( group . Description )
2023-08-18 17:09:00 +01:00
description [ " colourscheme " ] = coulour_scheme
2023-07-23 15:20:14 +10:00
description [ " colourscheme_query " ] = query
group . Description = json . dumps ( description )
else :
2023-08-18 17:09:00 +01:00
description = json . dumps ( { " type " : " BBIM_Search " , " colourscheme " : coulour_scheme , " colourscheme_query " : query } )
2023-07-23 15:20:14 +10:00
group = ifcopenshell . api . run ( " group.add_group " , tool . Ifc . get ( ) , Name = self . name , Description = description )
def invoke ( self , context , event ) :
return context . window_manager . invoke_props_dialog ( self )
class LoadColourscheme ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.load_colourscheme "
bl_label = " Load Colourscheme "
bl_description = " Load colourscheme from an IFC group "
bl_options = { " REGISTER " , " UNDO " }
def _execute ( self , context ) :
props = context . scene . BIMSearchProperties
2023-08-18 17:09:00 +01:00
group = tool . Ifc . get ( ) . by_id ( int ( props . saved_colourschemes ) )
2023-07-23 15:20:14 +10:00
description = json . loads ( group . Description )
props . colourscheme_query = description . get ( " colourscheme_query " )
props . colourscheme . clear ( )
for name , colour in description . get ( " colourscheme " , { } ) . items ( ) :
new = props . colourscheme . add ( )
new . name = name
new . colour = colour
def draw ( self , context ) :
props = context . scene . BIMSearchProperties
row = self . layout . row ( )
row . prop ( props , " saved_colourschemes " , text = " " )
def invoke ( self , context , event ) :
return context . window_manager . invoke_props_dialog ( self )
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 ( )
2023-07-02 15:48:39 +05:00
global_id = self . global_id . strip ( )
if not global_id :
self . report ( { " ERROR " } , " Set Global ID for search. " )
return { " CANCELLED " }
try :
entity = ifc_file . by_guid ( global_id )
except RuntimeError :
self . report ( { " ERROR " } , f " No IFC entity found with guid ' { global_id } ' . " )
return { " CANCELLED " }
2021-11-19 00:06:22 +01:00
obj = tool . Ifc . get_object ( entity )
2022-08-16 11:49:24 +10:00
if not obj :
2023-07-02 15:48:39 +05:00
self . report ( { " ERROR " } , f " No Blender object found with guid ' { global_id } ' . " )
return { " CANCELLED " }
2021-11-19 00:06:22 +01:00
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 ) :
2023-07-30 08:16:30 +10:00
for element in tool . Ifc . get ( ) . by_type ( self . ifc_class ) :
obj = tool . Ifc . get_object ( element )
if obj :
2021-01-23 14:17:50 +11:00
obj . select_set ( True )
return { " FINISHED " }
2022-07-19 13:55:20 +02:00
class ResetObjectColours ( Operator ) :
2023-07-23 15:20:14 +10:00
""" Reset the colour of visible 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 ) :
2023-07-23 15:20:14 +10:00
for obj in context . visible_objects :
2021-01-23 14:17:50 +11:00
obj . color = ( 1 , 1 , 1 , 1 )
2023-07-23 15:20:14 +10:00
props = context . scene . BIMSearchProperties
props . colourscheme . clear ( )
2021-01-23 14:17:50 +11:00
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
2023-09-23 21:26:58 +01:00
elif props . filter_type == " CONTAINER " :
for building_storey in props . filter_container :
2021-12-28 23:46:22 +01:00
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
2023-07-02 15:48:39 +05:00
@classmethod
def poll ( cls , context ) :
if not context . selected_objects :
cls . poll_message_set ( " Select objects to filter. " )
return False
return True
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
2023-09-23 21:26:58 +01:00
class ActivateContainerFilter ( Operator ) :
2021-12-28 23:46:22 +01:00
""" Filter the current selection by Building Storey """
2023-09-23 21:26:58 +01:00
bl_idname = " bim.activate_ifc_container_filter "
bl_label = " Filter by Container "
2021-12-28 23:46:22 +01:00
2023-07-02 15:48:39 +05:00
@classmethod
def poll ( cls , context ) :
if not context . selected_objects :
cls . poll_message_set ( " Select objects to filter. " )
return False
return True
2021-12-28 23:46:22 +01:00
def invoke ( self , context , event ) :
props = bpy . context . scene . BIMSearchProperties
2023-09-23 21:26:58 +01:00
props . filter_container . clear ( )
2021-12-28 23:46:22 +01:00
2023-09-23 21:26:58 +01:00
containers = { }
containers . setdefault ( " None " , 0 )
2021-12-28 23:46:22 +01:00
for obj in context . selected_objects :
2023-09-23 21:26:58 +01:00
container = tool . Spatial . get_container ( tool . Ifc . get_entity ( obj ) )
if not container :
containers [ " None " ] + = 1
2021-12-28 23:46:22 +01:00
continue
2023-09-23 21:26:58 +01:00
containers . setdefault ( container . Name , 0 )
containers [ container . Name ] + = 1
2021-12-28 23:46:22 +01:00
2023-09-23 21:26:58 +01:00
for name , total in dict ( sorted ( containers . items ( ) ) ) . items ( ) :
new = props . filter_container . add ( )
2021-12-28 23:46:22 +01:00
new . name = name
new . total = total
2023-09-23 21:26:58 +01:00
props . filter_type = " CONTAINER "
2021-12-28 23:46:22 +01:00
return context . window_manager . invoke_props_dialog ( self , width = 250 )
def execute ( self , context ) :
2023-09-23 21:26:58 +01:00
bpy . context . scene . BIMSearchProperties . filter_container . clear ( )
2021-12-28 23:46:22 +01:00
return { " FINISHED " }
def draw ( self , context ) :
self . layout . template_list (
" BIM_UL_ifc_building_storey_filter " ,
" " ,
context . scene . BIMSearchProperties ,
2023-09-23 21:26:58 +01:00
" filter_container " ,
2021-12-28 23:46:22 +01:00
context . scene . BIMSearchProperties ,
2023-09-23 21:26:58 +01:00
" filter_container_index " ,
2021-12-28 23:46:22 +01:00
rows = 20
2023-09-23 21:26:58 +01:00
if len ( bpy . context . scene . BIMSearchProperties . filter_container ) > 20
else len ( bpy . context . scene . BIMSearchProperties . filter_container ) ,
2021-12-28 23:46:22 +01: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 "
2022-07-19 09:21:13 +02:00
2022-08-01 19:45:36 +10:00
2023-02-11 13:23:23 +01:00
class ShowAllElements ( Operator ) :
2023-02-11 13:19:08 +01:00
""" Show all Physical objects in the 3D View.
Warning: Pressing this button will not work if collections are excluded in the outliner Panel.
"""
2022-07-19 10:05:20 +02:00
2023-02-11 13:19:08 +01:00
bl_idname = " bim.show_scene_elements "
bl_label = " Shows All Elements "
bl_options = { " REGISTER " , " UNDO " }
2022-07-11 16:33:24 +02:00
def execute ( self , context ) :
2023-02-11 13:19:08 +01:00
core . show_scene_elements ( tool . Spatial )
2022-07-11 16:33:24 +02:00
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-08-01 19:45:36 +10: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 ) :
2023-02-11 13:19:08 +01:00
selection_props = context . scene . IfcSelectorProperties
2023-07-02 15:48:39 +05:00
selection = (
selection_props . selector_query_syntax
if selection_props . manual_override
else self . add_groups ( selection_props )
)
2023-02-11 13:19:08 +01:00
selection_props . selector_query_syntax = selection
r = core . search ( tool . Search , tool . Spatial , query = selection , action = self . option )
if isinstance ( r , str ) :
self . report ( { " WARNING " } , r )
2022-07-11 16:33:24 +02:00
return { " FINISHED " }
2023-07-02 15:48:39 +05:00
def add_groups ( self , selector : str ) - > str :
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
2023-01-16 08:08:26 +01:00
def add_queries ( self , selection : str , group : str ) - > str :
2022-07-11 16:33:24 +02:00
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
2023-01-16 08:08:26 +01:00
def add_filters ( self , selection : str , query : str ) - > str :
2022-07-11 16:33:24 +02:00
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
2023-03-24 17:48:27 +11:00
if f . value in ( " True " , " False " ) :
2023-03-06 18:10:51 +11:00
value = f . value
2023-03-24 17:48:27 +11:00
elif f . value . isnumeric ( ) and str ( float ( f . value ) ) [ 0 ] == f . value [ 0 ] :
2023-03-06 18:10:51 +11:00
value = f . value
else :
value = f ' " { f . value } " '
2022-07-11 16:33:24 +02:00
selection + = " [ "
2022-07-18 15:39:19 +02:00
if f . selector == " IfcPropertySet " :
2023-03-06 18:10:51 +11:00
selection + = f ' { f . active_option . split ( " : " ) [ 1 ] } . { f . active_sub_option . split ( " : " ) [ 1 ] } { " ! " if f . negation else " " } { f . comparison } { value } '
2022-07-11 16:33:24 +02:00
elif f . selector == " Attribute " :
2023-01-16 08:58:14 +01:00
# we're using the prop_search functionality in blender which returns the index of the option. Sometimes the user can override this and enter a value that doesn't exist in the list. In this case there is no index and we need to handle it. @vulevukusej
2023-07-02 15:48:39 +05:00
pattern = re . compile ( r " ^[0-9]+: " )
2023-01-16 08:58:14 +01:00
match = pattern . search ( f . active_option )
2023-03-06 18:10:51 +11:00
selection + = f ' { f . active_option . split ( " : " ) [ 1 ] if match else f . active_option } { " ! " if f . negation else " " } { f . comparison } { value } '
2022-07-11 16:33:24 +02:00
selection + = " ] "
return selection
2022-07-19 10:05:20 +02:00
2023-07-02 15:48:39 +05:00
2022-09-03 15:34:52 +10:00
# This needs to be moved into ui code, I know ;) - vulevukusej
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 """
2022-08-01 19:45:36 +10:00
2022-07-19 10:05:20 +02:00
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
2022-09-03 15:34:52 +10:00
2022-07-19 14:16:59 +02:00
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 """
2022-08-01 19:45:36 +10:00
2022-07-19 13:55:20 +02:00
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-08-01 19:45:36 +10:00
2022-07-22 08:20:06 +02:00
def invoke ( self , context , event ) :
close_operator_panel ( event )
return self . execute ( context )
2022-08-01 19:45:36 +10:00
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-08-01 19:45:36 +10:00
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 " )
2022-08-01 19:45:36 +10:00
2022-07-20 13:48:26 +02:00
def invoke ( self , context , event ) :
2022-08-01 10:16:27 +02:00
bpy . ops . bim . load_groups ( )
2022-07-20 13:48:26 +02:00
return context . window_manager . invoke_props_dialog ( self , width = 400 )
2022-08-01 19:45:36 +10:00
2022-07-20 13:48:26 +02:00
def draw ( self , context ) :
2022-08-01 10:16:27 +02:00
self . props = context . scene . BIMGroupProperties
row = self . layout . row ( )
row . operator ( " bim.add_group " )
2022-08-01 19:45:36 +10:00
2022-08-01 10:16:27 +02:00
self . layout . template_list (
2022-08-01 19:45:36 +10:00
" BIM_UL_groups " ,
" " ,
self . props ,
" groups " ,
self . props ,
" active_group_index " ,
)
2022-08-01 10:16:27 +02:00
if self . props . active_group_id :
for attribute in self . props . group_attributes :
if attribute . name in [ " Name " , " Description " ] :
row = self . layout . row ( align = True )
row . prop ( attribute , " string_value " , text = attribute . name )
2022-08-01 19:45:36 +10:00
2022-07-20 13:48:26 +02:00
def execute ( self , context ) :
2022-08-01 10:16:27 +02:00
active_group_index = self . props . active_group_index
ifc_definition_id = self . props . groups [ active_group_index ] . ifc_definition_id
2022-08-01 19:45:36 +10:00
bpy . ops . bim . enable_editing_group ( group = ifc_definition_id )
2022-08-01 10:16:27 +02:00
selector_query_syntax = context . scene . IfcSelectorProperties . selector_query_syntax
2022-08-01 19:45:36 +10:00
2022-08-01 10:16:27 +02:00
for attribute in self . props . group_attributes :
if attribute . name == " Description " :
if " *selector* " not in attribute . string_value :
attribute . string_value + = f " *selector* { selector_query_syntax } *selector* "
else :
new_description = attribute . string_value . split ( " *selector* " )
new_description [ 1 ] = selector_query_syntax
attribute . string_value = " *selector* " . join ( new_description )
2022-08-01 19:45:36 +10:00
2022-08-01 10:16:27 +02:00
bpy . ops . bim . edit_group ( )
bpy . ops . bim . disable_group_editing_ui ( )
2022-07-20 13:48:26 +02:00
return { " FINISHED " }
2023-07-29 20:46:07 +10:00
class SelectSimilar ( Operator , tool . Ifc . Operator ) :
bl_idname = " bim.select_similar "
bl_label = " Select Similar "
bl_options = { " REGISTER " , " UNDO " }
def _execute ( self , context ) :
props = context . scene . BIMSearchProperties
obj = context . active_object
element = tool . Ifc . get_entity ( obj )
value = ifcopenshell . util . selector . get_element_value ( element , props . element_key )
for obj in context . visible_objects :
element = tool . Ifc . get_entity ( obj )
if not element :
continue
if ifcopenshell . util . selector . get_element_value ( element , props . element_key ) == value :
obj . select_set ( True )