2022-08-25 22:01:35 +02:00
# IfcSverchok - IFC Sverchok extension
2022-11-09 01:49:01 +01:00
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
2022-08-25 22:01:35 +02:00
#
# 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
from copy import deepcopy
2022-10-23 16:18:42 +02:00
from decimal import Context
2022-10-10 09:01:00 +02:00
from email . policy import default
2022-08-25 22:01:35 +02:00
import bpy
import ifcopenshell
import ifcsverchok . helper
import ifcopenshell . api
2025-03-21 12:53:13 +05:00
import ifcopenshell . api . geometry
import ifcopenshell . util . representation
2022-10-10 09:01:00 +02:00
from ifcsverchok . ifcstore import SvIfcStore
2024-08-14 14:09:45 +05:00
import bonsai . tool as tool
import bonsai . core . geometry as core
2022-11-11 02:50:30 +01:00
from bpy . props import (
StringProperty ,
EnumProperty ,
IntProperty ,
BoolProperty ,
PointerProperty ,
)
2022-08-25 22:01:35 +02:00
from sverchok . node_tree import SverchCustomTreeNode
2022-11-09 01:49:01 +01:00
from sverchok . data_structure import updateNode , flatten_data , fixed_iter , flat_iter
2024-08-14 14:09:45 +05:00
from bonsai . bim . module . root . prop import get_contexts
2022-10-10 09:01:00 +02:00
from sverchok . data_structure import zip_long_repeat , node_id
from sverchok . core . socket_data import sv_get_socket
2022-08-25 22:01:35 +02:00
2022-10-10 09:01:00 +02:00
from itertools import chain , cycle
2025-03-21 12:53:13 +05:00
from mathutils import Matrix
2022-08-25 22:01:35 +02:00
2022-11-09 01:49:01 +01:00
2024-07-26 12:13:29 +10:00
class SvIfcBMeshToIfcRepr ( bpy . types . Node , SverchCustomTreeNode , ifcsverchok . helper . SvIfcCore ) :
2022-08-25 22:01:35 +02:00
"""
2022-10-10 09:01:00 +02:00
Triggers: BMesh to Ifc Repr
Tooltip: Blender mesh to Ifc Shape Representation
2022-08-25 22:01:35 +02:00
"""
2022-11-09 01:49:01 +01:00
2022-10-10 09:01:00 +02:00
bl_idname = " SvIfcBMeshToIfcRepr "
bl_label = " IFC Blender Mesh to IFC Repr "
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 :
2022-11-11 12:52:06 +01:00
self . process ( )
2022-10-10 09:01:00 +02:00
self . refresh_local = False
2024-07-26 12:13:29 +10:00
refresh_local : BoolProperty ( name = " Update Node " , description = " Update Node " , update = refresh_node )
2022-10-10 09:01:00 +02:00
context_types = [
2022-11-09 01:49:01 +01:00
( " Model " , " Model " , " Context type: Model " , 0 ) ,
( " Plan " , " Plan " , " Context type: Plan " , 1 ) ,
2022-10-10 09:01:00 +02:00
]
context_identifiers = [
2022-11-09 01:49:01 +01:00
( " Body " , " Body " , " Context identifier: Body " , 0 ) ,
( " Annotation " , " Annotation " , " Context identifier: Annotation " , 1 ) ,
( " Box " , " Box " , " Context identifier: Box " , 2 ) ,
( " Axis " , " Axis " , " Context identifier: Axis " , 3 ) ,
2022-10-10 09:01:00 +02:00
]
target_views = [
2022-11-09 01:49:01 +01:00
( " MODEL_VIEW " , " MODEL_VIEW " , " Target View: MODEL_VIEW " , 0 ) ,
( " PLAN_VIEW " , " PLAN_VIEW " , " Target View: PLAN_VIEW " , 1 ) ,
( " GRAPH_VIEW " , " GRAPH_VIEW " , " Target View: GRAPH_VIEW " , 2 ) ,
( " SKETCH_VIEW " , " SKETCH_VIEW " , " Target View: SKETCH_VIEW " , 3 ) ,
2022-10-10 09:01:00 +02:00
]
2022-11-09 01:49:01 +01:00
blender_objects : PointerProperty (
2022-11-11 02:50:30 +01:00
name = " Blender Mesh(es) " ,
description = " Blender Mesh Object(s) " ,
update = updateNode ,
type = bpy . types . Object ,
2022-11-09 01:49:01 +01:00
)
context_type : EnumProperty (
2022-11-11 02:50:30 +01:00
name = " Context Type " ,
description = " Default: Model " ,
default = " Model " ,
items = context_types ,
update = updateNode ,
2022-11-09 01:49:01 +01:00
)
context_identifier : EnumProperty (
name = " Context Identifier " ,
description = " Default: Body " ,
default = " Body " ,
items = context_identifiers ,
update = updateNode ,
)
target_view : EnumProperty (
name = " Target View " ,
description = " Default: MODEL VIEW " ,
default = " MODEL_VIEW " ,
items = target_views ,
update = updateNode ,
)
2022-08-25 22:01:35 +02:00
tooltip : StringProperty ( name = " Tooltip " )
2022-10-10 09:01:00 +02:00
2022-08-25 22:01:35 +02:00
def sv_init ( self , context ) :
2022-10-10 09:01:00 +02:00
self . inputs . new ( " SvStringsSocket " , " context_type " ) . prop_name = " context_type "
2024-07-26 12:13:29 +10:00
self . inputs . new ( " SvStringsSocket " , " context_identifier " ) . prop_name = " context_identifier "
2022-10-10 09:01:00 +02:00
self . inputs . new ( " SvStringsSocket " , " target_view " ) . prop_name = " target_view "
2024-07-26 12:13:29 +10:00
self . inputs . new ( " SvObjectSocket " , " blender_objects " ) . prop_name = " blender_objects " # no prop for now
2022-10-10 09:01:00 +02:00
self . outputs . new ( " SvVerticesSocket " , " Representations " )
2022-10-24 00:08:59 +02:00
self . outputs . new ( " SvMatrixSocket " , " Locations " )
2022-11-09 01:49:01 +01:00
2022-08-25 22:01:35 +02:00
def draw_buttons ( self , context , layout ) :
2024-07-26 12:13:29 +10:00
layout . operator ( " node.sv_ifc_tooltip " , text = " " , icon = " QUESTION " , emboss = False ) . tooltip = (
" Blender mesh to Ifc Shape Representation. \n Takes one or multiple geometries. \n Deconstructs joined geometries and creates a separate representation for each. "
)
2022-11-09 01:49:01 +01:00
2022-10-10 09:01:00 +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-08-25 22:01:35 +02:00
2022-11-07 17:21:09 +01:00
def process ( self ) :
2022-10-10 09:01:00 +02:00
self . sv_input_names = [ i . name for i in self . inputs ]
2022-08-25 22:01:35 +02:00
2022-10-10 09:01:00 +02:00
if hash ( self ) not in self . node_dict :
2024-07-26 12:13:29 +10: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 ) )
2022-11-09 01:49:01 +01:00
2022-11-13 15:56:33 +01:00
if not self . inputs [ " blender_objects " ] . sv_get ( deepcopy = True ) [ 0 ] :
2022-10-10 09:01:00 +02:00
return
edit = False
for i in range ( len ( self . inputs ) ) :
2022-11-07 15:45:28 +01:00
input = self . inputs [ i ] . sv_get ( deepcopy = True )
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-11-09 01:49:01 +01:00
2022-11-13 15:56:33 +01:00
blender_objects = self . inputs [ " blender_objects " ] . sv_get ( deepcopy = True )
2022-10-23 16:18:42 +02:00
self . file = SvIfcStore . get_file ( )
2022-10-10 09:01:00 +02:00
if self . refresh_local :
edit = True
if self . node_id not in SvIfcStore . id_map :
2022-10-24 00:08:59 +02:00
representations , locations = self . create ( blender_objects )
2022-10-10 09:01:00 +02:00
else :
if edit is True :
self . edit ( )
2022-10-24 00:08:59 +02:00
representations , locations = self . create ( blender_objects )
2022-10-10 09:01:00 +02:00
else :
2022-10-21 00:11:45 +02:00
representations = SvIfcStore . id_map [ self . node_id ] [ " Representations " ]
2022-10-24 00:08:59 +02:00
locations = SvIfcStore . id_map [ self . node_id ] [ " Locations " ]
2022-11-07 17:21:09 +01:00
2022-10-10 09:01:00 +02:00
self . outputs [ " Representations " ] . sv_set ( representations )
2022-10-24 00:08:59 +02:00
self . outputs [ " Locations " ] . sv_set ( locations )
2022-10-10 09:01:00 +02:00
2025-03-21 12:53:13 +05:00
def create ( self , blender_objects : list [ bpy . types . Object ] ) - > tuple [ list [ list [ list [ int ] ] ] , list [ list [ list [ Matrix ] ] ] ] :
representations_ids : list [ list [ list [ int ] ] ] = [ ]
locations : list [ list [ list [ Matrix ] ] ] = [ ]
2022-11-13 15:56:33 +01:00
context = self . get_context ( )
2022-10-10 09:01:00 +02:00
for blender_object in blender_objects :
2022-11-13 15:56:33 +01:00
bpy . context . view_layer . objects . active = blender_object
try :
bpy . ops . object . mode_set ( mode = " OBJECT " )
except Exception as e :
print ( e )
pass
bpy . ops . object . select_all ( action = " DESELECT " )
blender_object . select_set ( True )
try :
bpy . ops . object . mode_set ( mode = " EDIT " )
except Exception as e :
print ( e )
pass
bpy . ops . mesh . separate (
type = " LOOSE "
) # This isn't a great solution bc it creates new objects in the scene, thus changing the users model
2025-03-21 12:53:13 +05:00
representations_ids_obj : list [ list [ int ] ] = [ ]
locations_obj : list [ list [ Matrix ] ] = [ ]
2022-11-13 15:56:33 +01:00
try :
bpy . ops . object . mode_set ( mode = " OBJECT " )
except Exception as e :
print ( e )
pass
for obj in bpy . context . selected_objects :
if blender_object . type == " MESH " :
2025-03-21 12:53:13 +05:00
representation = ifcopenshell . api . geometry . add_representation (
2022-11-09 01:49:01 +01:00
self . file ,
blender_object = obj ,
geometry = obj . data ,
2022-11-13 15:56:33 +01:00
context = context ,
2025-03-21 12:53:13 +05:00
should_run_listeners = False ,
2022-11-09 01:49:01 +01:00
)
2022-11-07 17:21:09 +01:00
if not representation :
2024-07-26 12:13:29 +10:00
raise Exception ( " Couldn ' t create representation. Possibly wrong context. " )
2025-03-21 12:53:13 +05:00
assert isinstance ( representation , ifcopenshell . entity_instance )
2022-11-09 01:49:01 +01:00
2022-11-13 15:56:33 +01:00
representations_ids_obj . append ( [ representation . id ( ) ] )
locations_obj . append ( [ obj . matrix_world ] )
representations_ids . append ( representations_ids_obj )
locations . append ( locations_obj )
2024-07-26 12:13:29 +10:00
SvIfcStore . id_map . setdefault ( self . node_id , { } ) . setdefault ( " Representations " , [ ] ) . append (
representations_ids_obj
)
SvIfcStore . id_map . setdefault ( self . node_id , { } ) . setdefault ( " Locations " , [ ] ) . append ( locations_obj )
2022-11-09 01:49:01 +01:00
bpy . ops . object . select_all ( action = " DESELECT " )
2022-10-24 00:08:59 +02:00
return representations_ids , locations
2022-10-10 09:01:00 +02:00
2025-03-21 12:53:13 +05:00
def edit ( self ) - > None :
2022-10-10 09:01:00 +02:00
if " Representations " not in SvIfcStore . id_map [ self . node_id ] :
return
2022-11-09 01:49:01 +01:00
for obj in SvIfcStore . id_map [ self . node_id ] [ " Representations " ] :
for step_id in obj :
ifcopenshell . api . run (
2022-11-11 02:50:30 +01:00
" geometry.remove_representation " ,
self . file ,
2022-11-13 15:56:33 +01:00
representation = self . file . by_id ( step_id [ 0 ] ) ,
2022-11-09 01:49:01 +01:00
)
2022-10-10 09:01:00 +02:00
del SvIfcStore . id_map [ self . node_id ] [ " Representations " ]
2022-10-24 00:08:59 +02:00
del SvIfcStore . id_map [ self . node_id ] [ " Locations " ]
2022-10-21 00:11:45 +02:00
return
2022-10-10 09:01:00 +02:00
2025-03-21 12:53:13 +05:00
def get_context ( self ) - > ifcopenshell . entity_instance :
2022-11-09 01:49:01 +01:00
context = ifcopenshell . util . representation . get_context (
self . file , self . context_type , self . context_identifier , self . target_view
)
2022-10-10 09:01:00 +02:00
if not context :
2024-07-26 12:13:29 +10:00
parent = ifcopenshell . util . representation . get_context ( self . file , self . context_type )
2022-10-10 09:01:00 +02:00
if not parent :
2024-07-26 12:13:29 +10:00
parent = ifcopenshell . api . run ( " context.add_context " , self . file , context_type = self . context_type )
2022-10-10 09:01:00 +02:00
context = ifcopenshell . api . run (
" context.add_context " ,
self . file ,
context_type = self . context_type ,
context_identifier = self . context_identifier ,
target_view = self . target_view ,
parent = parent ,
2022-08-25 22:01:35 +02:00
)
2024-07-26 12:13:29 +10:00
SvIfcStore . id_map . setdefault ( self . node_id , { } ) . setdefault ( " Contexts " , [ ] ) . append ( context . id ( ) )
2022-10-10 09:01:00 +02:00
return context
2022-11-07 17:21:09 +01:00
2025-03-21 12:53:13 +05:00
def sv_free ( self ) - > None :
2022-08-25 22:01:35 +02:00
try :
2022-10-10 09:01:00 +02:00
self . file = SvIfcStore . get_file ( )
if " Representations " in SvIfcStore . id_map [ self . node_id ] :
2022-11-09 01:49:01 +01:00
for obj in SvIfcStore . id_map [ self . node_id ] [ " Representations " ] :
for step_id in obj :
ifcopenshell . api . run (
2022-11-11 02:50:30 +01:00
" geometry.remove_representation " ,
self . file ,
representation = self . file . by_id ( step_id ) ,
2022-11-09 01:49:01 +01:00
)
2022-08-25 22:01:35 +02:00
2022-10-10 09:01:00 +02:00
if " Contexts " in SvIfcStore . id_map [ self . node_id ] :
for context_id in SvIfcStore . id_map [ self . node_id ] [ " Contexts " ] :
2022-10-23 16:18:42 +02:00
context = self . file . by_id ( context_id )
if not self . file . get_inverse ( context ) :
if self . file . by_id ( context_id ) . ParentContext :
parent = self . file . by_id ( context_id ) . ParentContext
2024-07-26 12:13:29 +10:00
ifcopenshell . api . run ( " context.remove_context " , self . file , context = context )
2022-10-23 16:18:42 +02:00
if parent :
if not self . file . get_inverse ( parent ) :
2024-07-26 12:13:29 +10:00
ifcopenshell . api . run ( " context.remove_context " , self . file , context = parent )
2022-10-10 09:01:00 +02:00
SvIfcStore . id_map [ self . node_id ] [ " Contexts " ] . remove ( context_id )
del SvIfcStore . id_map [ self . node_id ]
del self . node_dict [ hash ( self ) ]
except KeyError or AttributeError :
pass
2022-11-07 17:21:09 +01:00
2022-08-25 22:01:35 +02:00
def register ( ) :
2022-10-10 09:01:00 +02:00
bpy . utils . register_class ( SvIfcBMeshToIfcRepr )
2022-08-25 22:01:35 +02:00
def unregister ( ) :
2022-11-07 17:21:09 +01:00
bpy . utils . unregister_class ( SvIfcBMeshToIfcRepr )