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-11 02:50:30 +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
2022-11-11 02:50:30 +01:00
class SvIfcCreateEntity (
bpy . types . Node , SverchCustomTreeNode , ifcsverchok . helper . SvIfcCore
) :
2020-10-01 14:47:42 +10:00
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
2022-11-11 02:50:30 +01:00
refresh_local : BoolProperty (
name = " Update Node " , description = " Update Node " , update = refresh_node
)
2022-10-10 09:01:00 +02:00
2022-11-11 02:50:30 +01:00
Names : StringProperty (
name = " Names " ,
default = " " ,
description = " Entity name or list of names. " ,
update = updateNode ,
)
Descriptions : StringProperty (
name = " Descriptions " ,
default = " " ,
description = " Entity description or list of descriptions. " ,
update = updateNode ,
)
2022-10-10 09:01:00 +02:00
IfcClass : StringProperty ( name = " IfcClass " , update = updateNode )
2022-11-11 02:50:30 +01:00
Representations : StringProperty (
name = " Representations " ,
description = ' IfcRepresentation(s). Use eg. " IFC BMesh to IFC " or " IFC Sverchok to IFC " nodes to create representations. ' ,
default = " " ,
update = updateNode ,
)
2022-11-13 15:56:33 +01:00
# Locations: FloatVectorProperty(name="Locations", default="", subtype='MATRIX', update=updateNode)
# 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 "
2022-11-11 02:50:30 +01:00
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-11-13 15:56:33 +01:00
# self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties"
2022-10-10 09:01:00 +02:00
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 )
2022-11-11 02:50:30 +01:00
self . descriptions = flatten_data (
self . inputs [ " Descriptions " ] . sv_get ( ) , target_level = 1
)
self . ifc_class = flatten_data ( self . inputs [ " IfcClass " ] . sv_get ( ) , target_level = 1 ) [
0
]
self . representations = ensure_min_nesting (
2022-11-13 15:56:33 +01:00
self . inputs [ " Representations " ] . sv_get ( ) , 3
2022-11-11 02:50:30 +01:00
)
2022-11-13 15:56:33 +01:00
self . representations = flatten_data ( self . representations , target_level = 3 )
2022-11-11 02:50:30 +01:00
self . locations = ensure_min_nesting (
2022-11-13 15:56:33 +01:00
self . inputs [ " Locations " ] . sv_get ( default = [ ] ) , 3
2022-11-11 02:50:30 +01:00
)
2022-11-13 15:56:33 +01:00
self . locations = flatten_data ( self . locations , target_level = 3 )
# self.properties = self.inputs["Properties"].sv_get()
print ( " # " * 80 , " \n Create Entity " , " \n " , " # " * 80 )
2022-10-10 09:01:00 +02:00
self . sv_input_names = [ i . name for i in self . inputs ]
if hash ( self ) not in self . node_dict :
2022-11-11 02:50:30 +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-11 02:50:30 +01:00
input = self . inputs [ self . sv_input_names [ i ] ] . sv_get (
deepcopy = True , default = [ ]
)
2022-11-09 01:49:01 +01:00
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-11-13 15:56:33 +01:00
representations = [ ]
names = [ ]
descriptions = [ ]
for group in self . representations :
try :
group_representations = [
[ self . file . by_id ( step_id ) for step_id in representation ]
for representation in group
]
representations . append ( group_representations )
except Exception as e :
raise
names . append (
self . repeat_input_unique ( self . names , len ( group_representations ) )
)
descriptions . append (
self . repeat_input_unique (
self . descriptions , len ( group_representations )
)
)
self . representations = representations
self . names = names
self . descriptions = descriptions
elif not self . representations [ 0 ] [ 0 ] [ 0 ] :
2022-11-11 02:50:30 +01:00
self . descriptions = self . repeat_input_unique (
self . descriptions , len ( self . names )
)
2022-11-13 15:56:33 +01:00
self . names = ensure_min_nesting ( self . names , 3 )
self . descriptions = ensure_min_nesting ( self . descriptions , 3 )
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-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 = [ ]
2022-11-13 15:56:33 +01:00
for i , group in enumerate ( self . names ) :
group_entities_ids = [ ]
iterator = range ( len ( group ) )
if index is not None :
iterator = [ index ]
for j in iterator :
2022-10-10 09:01:00 +02:00
try :
2022-11-13 15:56:33 +01:00
entity = ifcopenshell . api . run (
" root.create_entity " ,
self . file ,
ifc_class = self . ifc_class ,
name = self . names [ i ] [ j ] ,
description = self . descriptions [ i ] [ j ] ,
)
try :
for repr in self . representations [ i ] [ j ] :
if repr :
ifcopenshell . api . run (
" geometry.assign_representation " ,
self . file ,
product = entity ,
representation = repr ,
)
except IndexError :
pass
try :
for loc in self . locations [ i ] [ j ] :
print ( " Location: " , loc )
if isinstance ( loc , Matrix ) :
ifcopenshell . api . run (
" geometry.edit_object_placement " ,
self . file ,
product = entity ,
matrix = loc ,
)
except IndexError :
pass
group_entities_ids . append ( entity . id ( ) )
except Exception as e :
raise Exception ( " Something went wrong. Cannot create entity. " , e )
SvIfcStore . id_map . setdefault ( self . node_id , [ ] ) . append ( group_entities_ids )
entities_ids . append ( group_entities_ids )
return entities_ids
def edit ( self ) :
entities_ids = [ ]
id_map_copy = SvIfcStore . id_map [ self . node_id ] . copy ( )
for i , group in enumerate ( self . names ) :
group_entities_ids = [ ]
for j , _ in enumerate ( group ) :
try :
step_id = id_map_copy [ i ] [ j ]
except IndexError :
id = self . create ( index = j )
group_entities_ids . append ( id [ 0 ] )
continue
entity = self . file . by_id ( step_id )
entity . Name = self . names [ i ] [ j ]
entity . Description = self . descriptions [ i ] [ j ]
try :
for repr in self . representations [ i ] [ j ] :
if repr and repr . is_a ( " IfcProductDefinitionShape " ) :
entity . Representation = repr
elif repr :
2022-11-09 01:49:01 +01:00
ifcopenshell . api . run (
2022-11-11 02:50:30 +01:00
" geometry.assign_representation " ,
self . file ,
product = entity ,
representation = repr ,
2022-11-09 01:49:01 +01:00
)
2022-10-10 09:01:00 +02:00
except IndexError :
pass
2022-10-24 00:08:59 +02:00
try :
2022-11-13 15:56:33 +01:00
for loc in self . locations [ i ] [ j ] :
2022-11-11 02:50:30 +01:00
if isinstance ( loc , Matrix ) :
2022-11-09 01:49:01 +01:00
ifcopenshell . api . run (
2022-11-11 02:50:30 +01:00
" geometry.edit_object_placement " ,
self . file ,
product = entity ,
matrix = loc ,
2022-11-09 01:49:01 +01:00
)
2022-10-24 00:08:59 +02:00
except IndexError :
pass
2022-11-13 15:56:33 +01:00
if entity . is_a ( ) != self . ifc_class :
SvIfcStore . id_map [ self . node_id ] [ i ] . remove ( step_id )
entity = ifcopenshell . util . schema . reassign_class (
self . file , entity , self . ifc_class
)
group_entities_ids . append ( entity . id ( ) )
entities_ids . append ( group_entities_ids )
2022-10-21 00:11:45 +02:00
2022-11-13 15:56:33 +01:00
SvIfcStore . id_map [ self . node_id ] = entities_ids
2022-10-21 00:11:45 +02:00
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 = [
2022-11-11 02:50:30 +01:00
a if not ( s := sum ( j == a for j in input [ : i ] ) ) else f " { a } - { s + 1 } "
for i , a in enumerate ( input )
2022-11-09 01:49:01 +01:00
] # 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 )