2022-11-09 01:49:01 +01:00
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
#
# This file is part of IfcSverchok.
#
# IfcSverchok 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.
#
# IfcSverchok 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 IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
2022-10-10 09:01:00 +02:00
import bpy
2022-11-09 01:49:01 +01:00
from mathutils import Matrix
2022-10-10 09:01:00 +02:00
import ifcopenshell
import ifcsverchok . helper
from ifcsverchok . ifcstore import SvIfcStore
2022-11-09 01:49:01 +01:00
from bpy . props import StringProperty , BoolProperty
2020-10-01 14:47:42 +10:00
from sverchok . node_tree import SverchCustomTreeNode
2022-11-09 01:49:01 +01:00
from sverchok . data_structure import updateNode , flatten_data , repeat_last_for_length , ensure_min_nesting
2022-10-10 09:01:00 +02:00
2020-10-01 14:47:42 +10:00
class SvIfcCreateEntity ( bpy . types . Node , SverchCustomTreeNode , ifcsverchok . helper . SvIfcCore ) :
bl_idname = " SvIfcCreateEntity "
bl_label = " IFC Create Entity "
2022-10-10 09:01:00 +02:00
node_dict = { }
is_scene_dependent = True # if True and is_interactive then the node will be updated upon scene changes
def refresh_node ( self , context ) :
if self . refresh_local :
self . process ( )
self . refresh_local = False
refresh_local : BoolProperty ( name = " Update Node " , description = " Update Node " , update = refresh_node )
Names : StringProperty ( name = " Names " , default = " " , update = updateNode )
Descriptions : StringProperty ( name = " Descriptions " , default = " " , update = updateNode )
IfcClass : StringProperty ( name = " IfcClass " , update = updateNode )
Representations : StringProperty ( name = " Representations " , default = " " , update = updateNode )
2022-10-24 00:08:59 +02:00
# Locations: FloatVectorProperty(name="Locations", default="", update=updateNode)
2022-10-10 09:01:00 +02:00
Properties : StringProperty ( name = " properties " , update = updateNode )
2020-10-01 14:47:42 +10:00
def sv_init ( self , context ) :
2022-10-10 09:01:00 +02:00
self . inputs . new ( " SvStringsSocket " , " Names " ) . prop_name = " Names "
self . inputs . new ( " SvStringsSocket " , " Descriptions " ) . prop_name = " Descriptions "
self . inputs . new ( " SvStringsSocket " , " IfcClass " ) . prop_name = " IfcClass "
self . inputs . new ( " SvStringsSocket " , " Representations " ) . prop_name = " Representations "
2022-11-09 01:49:01 +01:00
self . inputs . new ( " SvMatrixSocket " , " Locations " ) . is_mandatory = False
2022-10-10 09:01:00 +02:00
self . inputs . new ( " SvStringsSocket " , " Properties " ) . prop_name = " Properties "
self . outputs . new ( " SvStringsSocket " , " Entities " )
self . node_dict [ hash ( self ) ] = { }
2020-10-01 14:47:42 +10:00
2022-10-21 00:11:45 +02:00
def draw_buttons ( self , context , layout ) :
2022-11-09 01:49:01 +01:00
layout . operator (
" node.sv_ifc_tooltip " , text = " " , icon = " QUESTION " , emboss = False
) . tooltip = " Create IFC Entity. Takes one or multiple inputs. \n If ' Representation(s) ' is given, that determines number of output entities. Otherwise, ' Names ' is used. "
2022-10-21 00:11:45 +02:00
row = layout . row ( align = True )
2022-11-09 01:49:01 +01:00
row . prop ( self , " is_interactive " , icon = " SCENE_DATA " , icon_only = True )
row . prop ( self , " refresh_local " , icon = " FILE_REFRESH " )
2022-10-21 00:11:45 +02:00
2020-10-01 14:47:42 +10:00
def process ( self ) :
2022-10-21 00:11:45 +02:00
self . names = flatten_data ( self . inputs [ " Names " ] . sv_get ( ) , target_level = 1 )
self . descriptions = flatten_data ( self . inputs [ " Descriptions " ] . sv_get ( ) , target_level = 1 )
2022-10-31 18:50:17 +01:00
self . ifc_class = flatten_data ( self . inputs [ " IfcClass " ] . sv_get ( ) , target_level = 1 ) [ 0 ]
2022-11-09 01:49:01 +01:00
self . representations = ensure_min_nesting ( self . inputs [ " Representations " ] . sv_get ( ) , 2 )
self . representations = flatten_data ( self . representations , target_level = 2 )
self . locations = ensure_min_nesting ( self . inputs [ " Locations " ] . sv_get ( default = [ ] ) , 2 )
2022-10-10 09:01:00 +02:00
self . properties = self . inputs [ " Properties " ] . sv_get ( )
self . sv_input_names = [ i . name for i in self . inputs ]
if hash ( self ) not in self . node_dict :
2022-11-09 01:49:01 +01:00
self . node_dict [ hash ( self ) ] = { } # happens if node is already on canvas when blender loads
2022-10-10 09:01:00 +02:00
if not self . node_dict [ hash ( self ) ] :
self . node_dict [ hash ( self ) ] . update ( dict . fromkeys ( self . sv_input_names , 0 ) )
if not self . inputs [ " IfcClass " ] . sv_get ( ) [ 0 ] [ 0 ] :
2022-10-31 18:50:17 +01:00
raise Exception ( ' Mandatory input " IfcClass " is missing. ' )
2022-10-10 09:01:00 +02:00
edit = False
for i in range ( len ( self . inputs ) ) :
2022-11-09 01:49:01 +01:00
input = self . inputs [ self . sv_input_names [ i ] ] . sv_get ( deepcopy = True , default = [ ] )
if (
isinstance ( self . node_dict [ hash ( self ) ] [ self . inputs [ i ] . name ] , list )
and input != self . node_dict [ hash ( self ) ] [ self . inputs [ i ] . name ]
) :
2022-10-10 09:01:00 +02:00
edit = True
2022-11-07 15:45:28 +01:00
self . node_dict [ hash ( self ) ] [ self . inputs [ i ] . name ] = input . copy ( )
2022-10-10 09:01:00 +02:00
if self . refresh_local :
edit = True
2022-11-09 01:49:01 +01:00
2022-10-21 00:11:45 +02:00
self . file = SvIfcStore . get_file ( )
2022-11-09 01:49:01 +01:00
if self . representations [ 0 ] [ 0 ] :
2022-10-24 01:45:00 +02:00
try :
2022-11-09 01:49:01 +01:00
# self.representations = [self.file.by_id(step_id) for step_id in self.representations]
self . representations = [
[ self . file . by_id ( step_id ) for step_id in representation ] for representation in self . representations
]
2022-10-24 01:45:00 +02:00
except Exception as e :
raise
2022-10-21 00:11:45 +02:00
self . names = self . repeat_input_unique ( self . names , len ( self . representations ) )
self . descriptions = self . repeat_input_unique ( self . descriptions , len ( self . representations ) )
elif not self . representations [ 0 ] :
self . descriptions = self . repeat_input_unique ( self . descriptions , len ( self . names ) )
2022-10-10 09:01:00 +02:00
if self . node_id not in SvIfcStore . id_map :
2022-10-21 00:11:45 +02:00
entities = self . create ( )
2022-10-10 09:01:00 +02:00
else :
if edit is True :
2022-10-21 00:11:45 +02:00
entities = self . edit ( )
2022-10-10 09:01:00 +02:00
else :
2022-10-21 00:11:45 +02:00
entities = SvIfcStore . id_map [ self . node_id ]
2022-10-10 09:01:00 +02:00
2022-11-09 01:49:01 +01:00
# print("Entities: ", entities)
2022-10-21 00:11:45 +02:00
self . outputs [ " Entities " ] . sv_set ( entities )
2022-10-10 09:01:00 +02:00
2022-10-21 00:11:45 +02:00
def create ( self , index = None ) :
entities_ids = [ ]
iterator = range ( len ( self . names ) )
if index is not None :
iterator = [ index ]
for i in iterator :
2020-10-01 14:47:42 +10:00
try :
2022-11-09 01:49:01 +01:00
entity = ifcopenshell . api . run (
" root.create_entity " ,
self . file ,
ifc_class = self . ifc_class ,
name = self . names [ i ] ,
description = self . descriptions [ i ] ,
)
2022-10-10 09:01:00 +02:00
try :
2022-11-09 01:49:01 +01:00
for repr in self . representations [ i ] :
if self . representations [ i ] :
ifcopenshell . api . run (
" geometry.assign_representation " , self . file , product = entity , representation = repr
)
2022-10-10 09:01:00 +02:00
except IndexError :
pass
2022-10-24 00:08:59 +02:00
try :
2022-11-09 01:49:01 +01:00
for loc in self . locations [ i ] :
if isinstance ( self . locations [ i ] , Matrix ) :
ifcopenshell . api . run (
" geometry.edit_object_placement " , self . file , product = entity , matrix = loc
)
2022-10-24 00:08:59 +02:00
except IndexError :
pass
2022-10-21 00:11:45 +02:00
entities_ids . append ( entity . id ( ) )
SvIfcStore . id_map . setdefault ( self . node_id , [ ] ) . append ( entity . id ( ) )
2022-11-09 01:49:01 +01:00
except Exception as e :
2022-10-21 00:11:45 +02:00
raise Exception ( " Something went wrong. Cannot create entity. " , e )
return entities_ids
2022-10-10 09:01:00 +02:00
def edit ( self ) :
2022-10-21 00:11:45 +02:00
entities_ids = [ ]
2022-10-10 09:01:00 +02:00
id_map_copy = SvIfcStore . id_map [ self . node_id ] . copy ( )
2022-10-21 00:11:45 +02:00
for i , _ in enumerate ( self . names ) :
try :
step_id = id_map_copy [ i ]
except IndexError :
id = self . create ( index = i )
entities_ids . append ( id [ 0 ] )
continue
entity = self . file . by_id ( step_id )
entity . Name = self . names [ i ]
entity . Description = self . descriptions [ i ]
try :
2022-11-09 01:49:01 +01:00
for repr in self . representations [ i ] :
if repr and repr . is_a ( " IfcProductDefinitionShape " ) :
entity . Representation = repr
elif repr :
ifcopenshell . api . run (
" geometry.assign_representation " , self . file , product = entity , representation = repr
)
2022-10-21 00:11:45 +02:00
except IndexError :
2022-10-10 09:01:00 +02:00
pass
2022-10-24 00:08:59 +02:00
try :
2022-11-09 01:49:01 +01:00
for loc in self . locations [ i ] :
if isinstance ( loc , Matrix ) :
ifcopenshell . api . run ( " geometry.edit_object_placement " , self . file , product = entity , matrix = loc )
2022-10-24 00:08:59 +02:00
except IndexError :
pass
2022-10-21 00:11:45 +02:00
if entity . is_a ( ) != self . ifc_class :
2022-10-10 09:01:00 +02:00
SvIfcStore . id_map [ self . node_id ] . remove ( step_id )
2022-10-21 00:11:45 +02:00
entity = ifcopenshell . util . schema . reassign_class ( self . file , entity , self . ifc_class )
SvIfcStore . id_map . setdefault ( self . node_id , [ ] ) . append ( entity . id ( ) )
entities_ids . append ( entity . id ( ) )
2022-11-09 01:49:01 +01:00
if id_map_copy > entities_ids :
2022-10-21 00:11:45 +02:00
SvIfcStore . id_map [ self . node_id ] = entities_ids
return entities_ids
def repeat_input_unique ( self , input , count ) :
input = repeat_last_for_length ( input , count , deepcopy = False )
if input [ 0 ] :
2022-11-09 01:49:01 +01:00
input = [
a if not ( s := sum ( j == a for j in input [ : i ] ) ) else f " { a } - { s + 1 } " for i , a in enumerate ( input )
] # add number to duplicates
2022-10-21 00:11:45 +02:00
return input
2022-11-09 01:49:01 +01:00
2022-10-10 09:01:00 +02:00
def sv_free ( self ) :
try :
del SvIfcStore . id_map [ self . node_id ]
del self . node_dict [ hash ( self ) ]
2022-11-09 01:49:01 +01:00
# print('Node was deleted')
2022-10-10 09:01:00 +02:00
except KeyError or AttributeError :
pass
2020-10-01 14:47:42 +10:00
2020-11-01 19:24:01 +07:00
2020-10-01 14:47:42 +10:00
def register ( ) :
bpy . utils . register_class ( SvIfcCreateEntity )
2020-11-01 19:24:01 +07:00
2020-10-01 14:47:42 +10:00
def unregister ( ) :
bpy . utils . unregister_class ( SvIfcCreateEntity )