Files
IfcOpenShell/src/ifcsverchok/nodes/ifc/bmesh_to_ifc.py
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

289 lines
12 KiB
Python
Raw Normal View History

# 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/>.
from copy import deepcopy
from decimal import Context
from email.policy import default
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
from ifcsverchok.ifcstore import SvIfcStore
2024-08-14 14:09:45 +05:00
import bonsai.tool as tool
import bonsai.core.geometry as core
from bpy.props import (
StringProperty,
EnumProperty,
IntProperty,
BoolProperty,
PointerProperty,
)
from sverchok.node_tree import SverchCustomTreeNode
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
from sverchok.data_structure import zip_long_repeat, node_id
from sverchok.core.socket_data import sv_get_socket
from itertools import chain, cycle
2025-03-21 12:53:13 +05:00
from mathutils import Matrix
2024-07-26 12:13:29 +10:00
class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
"""
Triggers: BMesh to Ifc Repr
Tooltip: Blender mesh to Ifc Shape Representation
"""
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:
self.process()
self.refresh_local = False
2024-07-26 12:13:29 +10:00
refresh_local: BoolProperty(name="Update Node", description="Update Node", update=refresh_node)
context_types = [
("Model", "Model", "Context type: Model", 0),
("Plan", "Plan", "Context type: Plan", 1),
]
context_identifiers = [
("Body", "Body", "Context identifier: Body", 0),
("Annotation", "Annotation", "Context identifier: Annotation", 1),
("Box", "Box", "Context identifier: Box", 2),
("Axis", "Axis", "Context identifier: Axis", 3),
]
target_views = [
("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),
]
blender_objects: PointerProperty(
name="Blender Mesh(es)",
description="Blender Mesh Object(s)",
update=updateNode,
type=bpy.types.Object,
)
context_type: EnumProperty(
name="Context Type",
description="Default: Model",
default="Model",
items=context_types,
update=updateNode,
)
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,
)
tooltip: StringProperty(name="Tooltip")
def sv_init(self, context):
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"
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
self.outputs.new("SvVerticesSocket", "Representations")
2022-10-24 00:08:59 +02:00
self.outputs.new("SvMatrixSocket", "Locations")
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. \nTakes one or multiple geometries.\nDeconstructs joined geometries and creates a separate representation for each."
)
row = layout.row(align=True)
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
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
if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
if not self.inputs["blender_objects"].sv_get(deepcopy=True)[0]:
return
edit = False
for i in range(len(self.inputs)):
input = self.inputs[i].sv_get(deepcopy=True)
if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name]
):
edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
blender_objects = self.inputs["blender_objects"].sv_get(deepcopy=True)
self.file = SvIfcStore.get_file()
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)
else:
if edit is True:
self.edit()
2022-10-24 00:08:59 +02:00
representations, locations = self.create(blender_objects)
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"]
self.outputs["Representations"].sv_set(representations)
2022-10-24 00:08:59 +02:00
self.outputs["Locations"].sv_set(locations)
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]]] = []
context = self.get_context()
for blender_object in blender_objects:
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]] = []
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(
self.file,
blender_object=obj,
geometry=obj.data,
context=context,
2025-03-21 12:53:13 +05:00
should_run_listeners=False,
)
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)
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)
bpy.ops.object.select_all(action="DESELECT")
2022-10-24 00:08:59 +02:00
return representations_ids, locations
2025-03-21 12:53:13 +05:00
def edit(self) -> None:
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id[0]),
)
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
2025-03-21 12:53:13 +05:00
def get_context(self) -> ifcopenshell.entity_instance:
context = ifcopenshell.util.representation.get_context(
self.file, self.context_type, self.context_identifier, self.target_view
)
if not context:
2024-07-26 12:13:29 +10:00
parent = ifcopenshell.util.representation.get_context(self.file, self.context_type)
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)
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,
)
2024-07-26 12:13:29 +10:00
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
return context
2025-03-21 12:53:13 +05:00
def sv_free(self) -> None:
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
self.file,
representation=self.file.by_id(step_id),
)
if "Contexts" in SvIfcStore.id_map[self.node_id]:
for context_id in SvIfcStore.id_map[self.node_id]["Contexts"]:
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)
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)
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
def register():
bpy.utils.register_class(SvIfcBMeshToIfcRepr)
def unregister():
bpy.utils.unregister_class(SvIfcBMeshToIfcRepr)