Merge branch 'ifcsverchok_dev' of github.com:mdjska/IfcOpenShell into v0.7.0

Merging gsoc changes
This commit is contained in:
martinaCodes
2022-11-07 18:28:33 +01:00
17 changed files with 1664 additions and 170 deletions
+150 -6
View File
@@ -1,4 +1,3 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -36,10 +35,14 @@ import sverchok
from sverchok.core import sv_registration_utils, make_node_list
from sverchok.utils import auto_gather_node_classes, get_node_class_reference
from sverchok.menu import SverchNodeItem, SverchNodeCategory, register_node_panels
from sverchok.utils.extra_categories import register_extra_category_provider, unregister_extra_category_provider
from sverchok.utils.extra_categories import (
register_extra_category_provider,
unregister_extra_category_provider,
)
from sverchok.ui.nodeview_space_menu import make_extra_category_menus
from sverchok.utils.logging import info, debug
import asyncio
import time
def nodes_index():
return [
@@ -52,17 +55,26 @@ def nodes_index():
("ifc.create_entity", "SvIfcCreateEntity"),
("ifc.create_shape", "SvIfcCreateShape"),
("ifc.read_entity", "SvIfcReadEntity"),
("ifc.pick_ifc_class", "SvIfcPickIfcClass"),
("ifc.by_id", "SvIfcById"),
("ifc.by_guid", "SvIfcByGuid"),
("ifc.by_type", "SvIfcByType"),
("ifc.by_query", "SvIfcByQuery"),
("ifc.add", "SvIfcAdd"),
("ifc.add_pset", "SvIfcAddPset"),
("ifc.add_spatial_element", "SvIfcAddSpatialElement"),
("ifc.remove", "SvIfcRemove"),
("ifc.generate_guid", "SvIfcGenerateGuid"),
("ifc.get_property", "SvIfcGetProperty"),
("ifc.get_attribute", "SvIfcGetAttribute"),
("ifc.select_blender_objects", "SvIfcSelectBlenderObjects"),
("ifc.api", "SvIfcApi"),
("ifc.api_WIP", "SvIfcApiWIP"),
("ifc.bmesh_to_ifc", "SvIfcBMeshToIfcRepr"),
("ifc.sverchok_to_ifc", "SvIfcSverchokToIfcRepr"),
("ifc.create_project", "SvIfcCreateProject"),
("ifc.quick_project_setup", "SvIfcQuickProjectSetup")
],
)
]
@@ -84,7 +96,131 @@ imported_modules = make_node_list()
reload_event = False
import bpy
import os
from os.path import abspath, splitext
import ifcopenshell
from ifcsverchok.ifcstore import SvIfcStore
from sverchok.data_structure import flatten_data
class IFC_Sv_UpdateCurrent(bpy.types.Operator):
"""Update current Sverchok node tree"""
bl_idname = "ifc.sverchok_update_current"
bl_label = "Update current node tree"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
node_group: bpy.props.StringProperty(default="")
force_mode: bpy.props.BoolProperty(default=False)
def execute(self, context):
print("#"*10, "Update current node tree")
self.file = SvIfcStore.purge()
self.file = SvIfcStore.get_file()
self.file.write("/Users/martina/Documents/GSoC/CodeTests/IfcFileTest_7_11_purged.ifc")
node_tree = context.space_data.node_tree
if node_tree:
if self.force_mode or node_tree.sv_process:
try:
bpy.context.window.cursor_set("WAIT")
node_tree.force_update()
finally:
bpy.context.window.cursor_set("DEFAULT")
self.report({"INFO"}, "Node tree updated.")
return {'FINISHED'}
class IFC_Sv_write_file(bpy.types.Operator):
bl_idname = "ifc.write_file_panel"
bl_label = "Write File"
bl_options = {"REGISTER", "UNDO"}
bl_description = "File path to write to."
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
node_group: bpy.props.StringProperty(default="")
force_mode: bpy.props.BoolProperty(default=False)
@classmethod
def poll(cls, context):
return any("IFC" in n for n in context.space_data.edit_tree.nodes.keys())
def ensure_hirarchy(self, file):
elements_in_buildings = []
if not 0 <= 0 < len(file.by_type("IfcBuilding")):
my_building = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcBuilding", name="My Building")
elements = ifcopenshell.util.element.get_decomposition(my_building)
else:
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings.extend(elements)
for spatial in (file.by_type("IfcSpatialElement") or file.by_type("IfcSpatialStructureElement")):
if (not (spatial.is_a("IfcSite") or spatial.is_a("IfcBuilding")) and (spatial not in elements_in_buildings)):
elements = ifcopenshell.util.element.get_decomposition(spatial)
ifcopenshell.api.run("aggregate.assign_object", file, product=spatial, relating_object=file.by_type("IfcBuilding")[0])
elements_in_buildings_after = []
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings_after.extend(elements)
elements = file.by_type("IfcElement")
for element in elements:
if element not in elements_in_buildings:
ifcopenshell.api.run("spatial.assign_container", file, product=element, relating_structure=file.by_type("IfcBuilding")[0])
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
if not building.Decomposes:
if not 0 <= 0 < len(file.by_type("IfcSite")):
ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcSite", name="My Site")
ifcopenshell.api.run("aggregate.assign_object", file, product=building, relating_object=file.by_type("IfcSite")[0])
try:
if file.by_type("IfcSite")[0].Decomposes[0].RelatingObject.is_a("IfcProject"):
continue
except IndexError:
pass
ifcopenshell.api.run("aggregate.assign_object", file, product=file.by_type("IfcSite")[0], relating_object=file.by_type("IfcProject")[0])
self.file = file
return
def execute(self, context):
self.file = SvIfcStore.file
if not self.file:
raise Exception("No IFC file in SvIfcStore.")
_, ext = splitext(self.filepath)
if not ext:
raise Exception("Bad path. Provide a path to a file.")
else:
self.ensure_hirarchy(self.file)
self.file.write(self.filepath)
self.report({"INFO"}, f"File written to: {self.filepath}")
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class IFC_PT_write_file_panel(bpy.types.Panel):
bl_idname = "IFC_PT_write_file_panel"
bl_label = "Write IFC to file"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "NODE_EDITOR"
bl_region_type = "UI"
bl_category = "IfcSverchok"
@classmethod
def poll(cls, context):
if context.space_data.edit_tree and any("IFC" in n for n in context.space_data.edit_tree.nodes.keys()):
return True
def draw(self, context):
ng = context.space_data.node_tree
layout = self.layout
row = layout.split(factor=0.2, align=True)
row = layout.row()
row2 = layout.row()
row.operator('ifc.sverchok_update_current', text='IFC Re-run all nodes')
row2.operator("ifc.write_file_panel")
CLASSES = [IFC_Sv_UpdateCurrent,IFC_Sv_write_file, IFC_PT_write_file_panel]
def register_nodes():
node_modules = make_node_list()
@@ -109,7 +245,10 @@ def make_menu():
nodetype = item[1]
rna = get_node_class_reference(nodetype)
if not rna:
info("Node `%s' is not available (probably due to missing dependencies).", nodetype)
info(
"Node `%s' is not available (probably due to missing dependencies).",
nodetype,
)
else:
node_item = SverchNodeItem.new(nodetype)
node_items.append(node_item)
@@ -135,13 +274,16 @@ def register():
global our_menu_classes
debug("Registering ifcsverchok")
for klass in CLASSES:
bpy.utils.register_class(klass)
register_nodes()
extra_nodes = importlib.import_module(".nodes", "ifcsverchok")
auto_gather_node_classes(extra_nodes)
menu = make_menu()
menu_category_provider = SvExCategoryProvider("IFCSVERCHOK", menu)
register_extra_category_provider(menu_category_provider) # if 'IFCSVERCHOK' in nodeitems_utils._node_categories:
register_extra_category_provider(
menu_category_provider
) # if 'IFCSVERCHOK' in nodeitems_utils._node_categories:
nodeitems_utils.register_node_categories("IFCSVERCHOK", menu)
our_menu_classes = make_extra_category_menus()
@@ -158,3 +300,5 @@ def unregister():
print(e)
unregister_extra_category_provider("IFCSVERCHOK")
unregister_nodes()
for klass in CLASSES:
bpy.utils.unregister_class(klass)
+100
View File
@@ -0,0 +1,100 @@
# import os
import bpy
# import uuid
# import hashlib
# import zipfile
# import tempfile
import ifcopenshell
from ifcopenshell import template
# import blenderbim.bim.handler
# from pathlib import Path
class SvIfcStore:
path = ""
file = None
schema = None
cache = None
cache_path = None
id_map = {}
guid_map = {}
deleted_ids = set()
edited_objs = set()
pset_template_path = ""
pset_template_file = None
library_path = ""
library_file = None
element_listeners = set()
undo_redo_stack_objects = set()
undo_redo_stack_object_names = {}
current_transaction = ""
last_transaction = ""
history = []
future = []
schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def purge():
SvIfcStore.path = ""
SvIfcStore.file = None
SvIfcStore.schema = None
SvIfcStore.cache = None
SvIfcStore.cache_path = None
SvIfcStore.id_map = {}
SvIfcStore.guid_map = {}
SvIfcStore.deleted_ids = set()
SvIfcStore.edited_objs = set()
SvIfcStore.pset_template_path = ""
SvIfcStore.pset_template_file = None
SvIfcStore.library_path = ""
SvIfcStore.library_file = None
SvIfcStore.last_transaction = ""
SvIfcStore.history = []
SvIfcStore.future = []
SvIfcStore.schema_identifiers = ["IFC4", "IFC2X3"]
@staticmethod
def create_boilerplate():
file = template.create(
filename="IfcSverchokDemoFile",
organization=None,
creator=None,
project_name="IfcSverchokDemoProject",
)
if bpy.context.scene.unit_settings.system == 'IMPERIAL':
#TODO change units to imperial
pass
# model = ifcopenshell.api.run("context.add_context", file, context_type="Model")
model = ifcopenshell.util.representation.get_context(file, context="Model")
print("model: ", model)
context = ifcopenshell.api.run(
"context.add_context",
file,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model,
)
# site = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcSite", name="My Site")
# building = ifcopenshell.api.run(
# "root.create_entity", file, ifc_class="IfcBuilding", name="My Building"
# )
# building_storey = ifcopenshell.api.run(
# "root.create_entity", file, ifc_class="IfcBuildingStorey", name="My Storey"
# )
# ifcopenshell.api.run("aggregate.assign_object", file, product=site, relating_object=file.by_type("IfcProject")[0])
# ifcopenshell.api.run("aggregate.assign_object", file, product=building, relating_object=site)
# ifcopenshell.api.run("aggregate.assign_object", file, product=building_storey, relating_object=building)
SvIfcStore.file = file
return SvIfcStore.file
@staticmethod
def get_file():
if SvIfcStore.file is None:
SvIfcStore.create_boilerplate()
return SvIfcStore.file
+75
View File
@@ -0,0 +1,75 @@
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
import bpy
import json
import ifcopenshell
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcAddPset"
bl_label = "IFC Add Pset"
Name: StringProperty(name="Name", update=updateNode, default="My_Pset")
Properties: StringProperty(name="Properties", update=updateNode, default="{\"Foo\":\"Bar\"}")
Elements: StringProperty(name="Elements", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "Name").prop_name = "Name"
self.inputs.new("SvTextSocket", "Properties").prop_name = "Properties"
self.inputs.new("SvStringsSocket", "Elements").prop_name = "Elements"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "file")
def process(self):
if not any(socket.is_linked for socket in self.outputs):
return
name = self.inputs["Name"].sv_get()[0][0]
properties = self.inputs["Properties"].sv_get()[0][0]
elements = self.inputs["Elements"].sv_get()[0]
if SvIfcStore.file is None:
SvIfcStore.file = SvIfcStore.create_boilerplate()
self.file = SvIfcStore.get_file()
if self.node_id not in SvIfcStore.id_map.values():
element = self.create(name, properties, elements)
else:
element = self.edit(name, properties, elements)
self.outputs["entity"].sv_set([[element]])
self.outputs["file"].sv_set([[self.file]])
def create(self, name, properties, elements):
results = []
for element in elements:
result = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name=name)
ifcopenshell.api.run("pset.edit_pset", self.file, pset=result, properties=json.loads(properties))
SvIfcStore.id_map[result.id()] = self.node_id
results.append(result)
return result
def edit(self, name, properties, elements):
result = self.get_existing_element()
ifcopenshell.api.run("pset.edit_pset", self.file, pset=result, name=name, properties=json.loads(properties))
return result
def get_existing_element(self):
entity_id = list(SvIfcStore.id_map.keys())[list(SvIfcStore.id_map.values()).index(self.node_id)]
return self.file.by_id(entity_id)
def register():
bpy.utils.register_class(SvIfcAddPset)
def unregister():
bpy.utils.unregister_class(SvIfcAddPset)
SvIfcStore.purge()
@@ -0,0 +1,149 @@
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, flatten_data, repeat_last_for_length, ensure_min_nesting
class SvIfcAddSpatialElement(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcAddSpatialElement"
bl_label = "IFC Add Spatial Element"
node_dict = {}
Names: StringProperty(name="Name(s)", update=updateNode)
IfcClass: StringProperty(name="IFC Class", update=updateNode, default="IfcSpace")
Elements: StringProperty(name="Elements", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
self.inputs.new("SvStringsSocket", "Elements").prop_name = "Elements"
self.outputs.new("SvStringsSocket", "Entities")
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Ifc entity by type."
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict:
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 any(socket.is_linked for socket in self.outputs):
return
edit = False
edit_elements = False
for i in range(len(self.inputs)):
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]:
edit = True
if self.inputs[i].name == "Elements":
edit_elements = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[0]
self.elements = ensure_min_nesting(self.inputs["Elements"].sv_get(), 2)
self.elements = flatten_data(self.elements, target_level=2)
if not self.elements[0][0]:
raise Exception('Mandatory input "Element(s)" is missing.')
return
self.file = SvIfcStore.get_file()
self.elements = [[self.file.by_id(step_id) for step_id in element] for element in self.elements]
if "len" not in self.node_dict[hash(self)]:
self.node_dict[hash(self)]["len"] = 0
self.names = self.repeat_input_unique(self.names, len(self.elements))
if (self.node_id not in SvIfcStore.id_map) or (len(self.elements) != self.node_dict[hash(self)]["len"]):
self.remove()
elements = self.create()
self.node_dict[hash(self)]["len"] = len(self.elements)
else:
if edit is True:
elements = self.edit(edit_elements)
else:
elements = SvIfcStore.id_map[self.node_id]
self.outputs["Entities"].sv_set(elements)
def create(self, index=None):
spatial_ids = []
iterator = range(len(self.elements))
if index is not None:
iterator = [index]
for i in iterator:
result = ifcopenshell.api.run("root.create_entity", self.file, name=self.names[i], ifc_class=self.ifc_class)
for items in self.elements[i]:
if items.is_a("IfcSpatialElement") or items.is_a("IfcSpatialStructureElement"):
ifcopenshell.api.run("aggregate.assign_object", self.file, product=items, relating_object=result)
else:
ifcopenshell.api.run("spatial.assign_container", self.file, product=items, relating_structure=result)
SvIfcStore.id_map.setdefault(self.node_id, []).append(result.id())
spatial_ids.append(result.id())
return spatial_ids
def edit(self, edit_elements):
spatial_ids = []
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
for i, element in enumerate(self.elements):
try:
result_id = id_map_copy[i]
except IndexError:
id = self.create(index=i)
spatial_ids.append(id[0])
continue
result = self.file.by_id(result_id)
result.Name = self.names[i]
if edit_elements:
subelements = ifcopenshell.util.element.get_decomposition(result)
subelements = set(subelements)
for element in self.elements[i]:
element_set = set([element])
for removed_element in subelements - element_set:
if removed_element.is_a("IfcSpatialElement") or removed_element.is_a("IfcSpatialStructureElement"):
ifcopenshell.api.run("aggregate.unassign_object", self.file, product=removed_element, relating_object=result)
else:
ifcopenshell.api.run("spatial.unassign_container", self.file, product=removed_element, relating_object=result)
for added_element in element_set - subelements:
if added_element.is_a("IfcSpatialElement") or added_element.is_a("IfcSpatialStructureElement"):
ifcopenshell.api.run("aggregate.assign_object", self.file, product=added_element, relating_object=result)
else:
ifcopenshell.api.run("spatial.assign_container", self.file, product=added_element, relating_structure=result)
spatial_ids.append(result.id())
SvIfcStore.id_map[self.node_id] = spatial_ids
return spatial_ids
def remove(self):
if self.node_id in SvIfcStore.id_map:
for element_id in SvIfcStore.id_map[self.node_id]:
element = self.file.by_id(element_id)
ifcopenshell.api.run("root.remove_product", self.file, product=element)
del SvIfcStore.id_map[self.node_id]
def repeat_input_unique(self, input, count):
input = repeat_last_for_length(input, count, deepcopy=False)
if input[0]:
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
return input
def sv_free(self):
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcAddSpatialElement)
def unregister():
bpy.utils.unregister_class(SvIfcAddSpatialElement)
SvIfcStore.purge()
+2 -1
View File
@@ -27,6 +27,7 @@ from sverchok.data_structure import updateNode
def update_usecase(self, context):
print("API - running update usecase!")
module_usecase = self.get_module_usecase()
if module_usecase:
self.generate_node(*module_usecase)
@@ -104,4 +105,4 @@ def register():
def unregister():
bpy.utils.unregister_class(SvIfcApi)
bpy.utils.unregister_class(SvIfcTooltip)
bpy.utils.unregister_class(SvIfcTooltip)
+147
View File
@@ -0,0 +1,147 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcsverchok.helper
from bpy.props import StringProperty, EnumProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
import importlib
class SvIfcTooltip(bpy.types.Operator):
bl_idname = "node.sv_ifc_tooltip"
bl_label = "IFC Info"
tooltip: bpy.props.StringProperty()
@classmethod
def description(cls, context, properties):
return properties.tooltip
def execute(self, context):
return {"FINISHED"}
class SvIfcApiWIP(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
# def update_usecase(self, context):
# # update_usecase is not getting run rn
# module_usecase = self.get_module_usecase()
# if module_usecase:
# self.generate_node(*module_usecase)
bl_idname = "SvIfcApiWIP"
bl_label = "IFC API WIP"
tooltip: StringProperty(name="Tooltip")
usecase: StringProperty(name="usecase", update=updateNode)
current_usecase: StringProperty(name="current_usecase")
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "usecase").prop_name = "usecase"
#input_socket.tooltip = "ifcopenshell.api usecase, written like 'module.usecase' \n E.g.: 'project.create_file'"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "ifcopenshell.api usecase, written like 'module.usecase' \n E.g.: 'project.create_file"
# op.tooltip = self.tooltip
def process(self):
self.sv_input_names = ["usecase"]
module_usecase = self.inputs["usecase"].sv_get()[0][0]
if module_usecase:
try:
module_usecase = self.get_module_usecase()
except:
raise Exception(
f"Couldn't run generate_node(). Module usecase: {module_usecase}"
)
if '.'.join(module_usecase) != self.current_usecase:
self.generate_node(*module_usecase)
try:
for i in range(0, len(self.inputs)):
input = self.inputs[i].sv_get()
except:
# This occurs when a blender save file is reloaded
self.generate_node(*module_usecase)
self.sv_input_names = [i.name for i in self.inputs]
super().process() # super().process() uses zip_long_repeat() which doesn't take objects like bmesh
def get_module_usecase(self):
usecase = self.inputs["usecase"].sv_get()[0][0]
if usecase:
return usecase.split(".")
def generate_node(self, module, usecase):
importlib.import_module(f"ifcopenshell.api.{module}.{usecase}")
local_module = getattr(getattr(ifcopenshell.api, module), usecase)
node_inputs = {}
if hasattr(local_module.Usecase(local_module), "file"):
node_inputs["file"] = None
node_inputs.update(getattr(local_module.Usecase(local_module), "settings", {}))
# print("node inputs: ", node_inputs)
while len(self.inputs) > 1:
self.inputs.remove(self.inputs[-1])
if node_inputs:
self.tooltip = ""
for name, data in node_inputs.items():
setattr(SvIfcApiWIP, name, StringProperty(name=name))
self.inputs.new("SvStringsSocket", name).prop_name = name
if data is not None:
self.tooltip = f"{name} ({data}): {data}\n"
else:
self.tooltip = f"{name}: None\n"
self.tooltip = self.tooltip.strip()
self.current_usecase = '.'.join([module, usecase])
def process_ifc(self, usecase, *setting_values):
if usecase and setting_values:
settings = dict(zip(self.sv_input_names[1:], setting_values))
settings = {k: v for k, v in settings.items() if v != ""}
try:
if "file" in settings:
file = settings["file"]
settings.pop("file")
self.outputs["file"].sv_set([ifcopenshell.api.run(usecase, file, **settings)])
else:
self.outputs["file"].sv_set([ifcopenshell.api.run(usecase, **settings)])
except:
raise Exception(f"Couldn't run usecase.")
def register():
bpy.utils.register_class(SvIfcTooltip)
bpy.utils.register_class(SvIfcApiWIP)
def unregister():
bpy.utils.unregister_class(SvIfcApiWIP)
bpy.utils.unregister_class(SvIfcTooltip)
+219
View File
@@ -0,0 +1,219 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.tool as tool
import blenderbim.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)
from blenderbim.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
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:
updateNode(self, context)
self.refresh_local = False
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"
self.inputs.new("SvStringsSocket", "context_identifier").prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new("SvObjectSocket", "blender_objects").prop_name = "blender_objects" #no prop for now
self.outputs.new("SvVerticesSocket", "Representations")
self.outputs.new("SvMatrixSocket", "Locations")
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Blender mesh to Ifc Shape Representation. \nTakes one or multiple geometries."
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:
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()[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()
self.file = SvIfcStore.get_file()
if self.refresh_local:
edit = True
if self.node_id not in SvIfcStore.id_map:
representations, locations = self.create(blender_objects)
else:
if edit is True:
self.edit()
representations, locations = self.create(blender_objects)
else:
representations = SvIfcStore.id_map[self.node_id]["Representations"]
locations = SvIfcStore.id_map[self.node_id]["Locations"]
print("representations: ", representations)
print("locations: ", locations)
self.outputs["Representations"].sv_set(representations)
self.outputs["Locations"].sv_set(locations)
def create(self, blender_objects):
representations_ids = []
locations = []
for blender_object in blender_objects:
if blender_object.type == 'MESH':
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
blender_object.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.separate(type='LOOSE')
for obj in bpy.context.selected_objects:
representation = ifcopenshell.api.run("geometry.add_representation", self.file, should_run_listeners=False,blender_object = obj, geometry=obj.data, context = self.get_context())
if not representation:
raise Exception("Couldn't create representation. Possibly wrong context.")
representations_ids.append(representation.id())
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Representations", []).append(representation.id())
locations.append(blender_object.matrix_world)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Locations", []).append(blender_object.matrix_world)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
return representations_ids, locations
def edit(self):
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
ifcopenshell.api.run("geometry.remove_representation", self.file, representation=self.file.by_id(step_id))
del SvIfcStore.id_map[self.node_id]["Representations"]
del SvIfcStore.id_map[self.node_id]["Locations"]
return
def get_existing_element(self):
results = []
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
results.append(self.file.by_id(step_id))
return results
def get_context(self):
context = ifcopenshell.util.representation.get_context(self.file, self.context_type, self.context_identifier, self.target_view)
if not context:
parent = ifcopenshell.util.representation.get_context(self.file, self.context_type)
if not parent:
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,
)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
return context
def sv_free(self):
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
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
ifcopenshell.api.run("context.remove_context", self.file, context=context)
if parent:
if not self.file.get_inverse(parent):
ifcopenshell.api.run("context.remove_context", self.file, context=parent)
print("Removed context with step ID: ", context_id)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcBMeshToIfcRepr)
def unregister():
bpy.utils.unregister_class(SvIfcBMeshToIfcRepr)
+14 -11
View File
@@ -20,29 +20,32 @@
import bpy
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcById(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcById"
bl_label = "IFC By Id"
file: StringProperty(name="file", update=updateNode)
id: StringProperty(name="id", update=updateNode)
id: StringProperty(name="Id(s)", update=updateNode, )
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "id").prop_name = "id"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "Entities")
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Get IFC element by step id. Takes one or multiple step ids."
def process(self):
self.sv_input_names = ["file", "id"]
super().process()
def process_ifc(self, file, id):
self.outputs["entity"].sv_set([[file.by_id(int(id))]])
self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1)
print(self.ids)
if not self.ids[0]:
return
self.file = SvIfcStore.get_file()
self.entities = [self.file.by_id(int(step_id)) for step_id in self.ids]
self.outputs["Entities"].sv_set(self.entities)
def register():
bpy.utils.register_class(SvIfcById)
+22 -18
View File
@@ -21,13 +21,14 @@ import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty, EnumProperty
from ifcsverchok.ifcstore import SvIfcStore
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
from sverchok.utils.handle_blender_data import keep_enum_reference
def get_ifc_products(self, context):
ifc_products = getattr(self, "ifc_products", [])
file = self.inputs["file"].sv_get()[0][0]
file = SvIfcStore.get_file()
if not file:
return []
if ifc_products and file:
@@ -56,12 +57,11 @@ def update_ifc_products(self, context):
if hasattr(self, "ifc_classes"):
self.ifc_classes.clear()
def get_ifc_classes(self, context):
ifc_classes = getattr(self, "ifc_classes", [])
if ifc_classes:
return self.ifc_classes
file = self.inputs["file"].sv_get()[0][0]
file = SvIfcStore.get_file()
if not file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
@@ -83,31 +83,35 @@ def get_ifc_classes(self, context):
class SvIfcByType(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcByType"
bl_label = "IFC By Type"
file: StringProperty(name="file", update=updateNode)
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=update_ifc_products)
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=updateNode)
custom_ifc_class: StringProperty(name="Custom Ifc Class", update=updateNode)
ifc_product: EnumProperty(items=get_ifc_products, name="IfcProduct", description="Pick an IfcProduct from drop-down.", update=update_ifc_products)
ifc_class: EnumProperty(items=get_ifc_classes, name="IfcClass", description="Pick an IfcClass from drop-down.", update=updateNode)
custom_ifc_class: StringProperty(name="Custom IfcClass", description="Give the name of your custom IfcClass.", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.inputs.new("SvStringsSocket", "custom_ifc_class").prop_name = "custom_ifc_class"
self.outputs.new("SvStringsSocket", "entity")
self.outputs.new("SvStringsSocket", "Entity")
self.width = 200
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Get IFC element(s) in file by type. \nPick an IfcProduct and an IfcClass or give a custom IfcClass."
def process(self):
file = self.inputs["file"].sv_get()[0][0]
if file:
self.file = SvIfcStore.get_file()
if self.file:
self.ifc_products = get_ifc_products(self, bpy.context)
self.ifc_classes = get_ifc_classes(self, bpy.context)
self.sv_input_names = ["file", "ifc_product", "ifc_class", "custom_ifc_class"]
self.sv_input_names = ["ifc_product", "ifc_class", "custom_ifc_class"]
super().process()
def process_ifc(self, file, ifc_product, ifc_class, custom_ifc_class):
def process_ifc(self, ifc_product, ifc_class, custom_ifc_class):
if custom_ifc_class:
self.outputs["entity"].sv_set([file.by_type(custom_ifc_class)])
self.outputs["Entity"].sv_set([self.file.by_type(custom_ifc_class)])
elif ifc_class:
self.outputs["Entity"].sv_set([self.file.by_type(ifc_class)])
else:
self.outputs["entity"].sv_set([file.by_type(ifc_class)])
self.outputs["Entity"].sv_set([])
def register():
@@ -115,4 +119,4 @@ def register():
def unregister():
bpy.utils.unregister_class(SvIfcByType)
bpy.utils.unregister_class(SvIfcByType)
+160 -86
View File
@@ -1,111 +1,185 @@
import bpy
from mathutils import Matrix, Vector
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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/>.
# import ifcsverchok.ifc_store
# from bpy.props import StringProperty
# from sverchok.node_tree import SverchCustomTreeNode
# from sverchok.data_structure import updateNode
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from bpy.props import StringProperty, EnumProperty, IntProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data, repeat_last_for_length
class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcCreateEntity"
bl_label = "IFC Create Entity"
file: StringProperty(name="file", update=updateNode)
ifc_class: StringProperty(name="ifc_class", update=updateNode)
current_ifc_class: StringProperty(name="current_ifc_class")
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)
# Locations: FloatVectorProperty(name="Locations", default="", update=updateNode)
Properties: StringProperty(name="properties", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.outputs.new("SvStringsSocket", "file")
self.outputs.new("SvStringsSocket", "entity")
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"
self.inputs.new("SvMatrixSocket", "Locations").is_mandatory=False
self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties"
self.outputs.new("SvStringsSocket", "Entities")
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Create IFC Entity. Takes one or multiple inputs. \nIf 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used."
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 = ["file", "ifc_class"]
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
if ifc_class:
file = self.inputs["file"].sv_get()[0][0]
if file:
schema_name = file.wrapped_data.schema
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
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 = flatten_data(self.inputs["Representations"].sv_get(), target_level=1)
self.locations = flatten_data(self.inputs["Locations"].sv_get(default=[]), target_level=1)
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:
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["IfcClass"].sv_get()[0][0]:
raise Exception('Mandatory input "IfcClass" is missing.')
edit = False
for i in range(len(self.inputs)):
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]:
edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
if self.refresh_local:
edit = True
self.file = SvIfcStore.get_file()
if self.representations[0]:
try:
self.representations = [self.file.by_id(step_id) for step_id in self.representations]
except Exception as e:
raise
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))
if self.node_id not in SvIfcStore.id_map:
entities = self.create()
else:
if edit is True:
entities = self.edit()
else:
schema_name = "IFC4"
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name)
self.entity_schema = self.schema.declaration_by_name(ifc_class)
entities = SvIfcStore.id_map[self.node_id]
print("Entities: ", entities)
if ifc_class != self.current_ifc_class:
self.generate_inputs(ifc_class)
self.outputs["Entities"].sv_set(entities)
def create(self, index=None):
entities_ids = []
iterator = range(len(self.names))
if index is not None:
iterator = [index]
for i in iterator:
try:
for i in range(0, len(self.inputs)):
self.inputs[i].sv_get()
except:
# This occurs when a blender save file is reloaded
self.generate_inputs(ifc_class)
entity = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=self.ifc_class, name=self.names[i], description=self.descriptions[i])
try:
if self.representations[i]:
ifcopenshell.api.run("geometry.assign_representation", self.file, product=entity, representation=self.representations[i])
except IndexError:
pass
try:
if isinstance(self.locations[i], Matrix):
ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=entity, matrix=self.locations[i])
except IndexError:
pass
entities_ids.append(entity.id())
SvIfcStore.id_map.setdefault(self.node_id, []).append(entity.id())
except Exception as e:
raise Exception("Something went wrong. Cannot create entity.", e)
return entities_ids
for i in range(0, self.entity_schema.attribute_count()):
self.sv_input_names.append(self.entity_schema.attribute_by_index(i).name())
super().process()
def generate_inputs(self, ifc_class):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
for i in range(0, self.entity_schema.attribute_count()):
name = self.entity_schema.attribute_by_index(i).name()
setattr(SvIfcCreateEntity, name, StringProperty(name=name))
self.inputs.new("SvStringsSocket", name).prop_name = name
self.current_ifc_class = ifc_class
def process_ifc(self, file, ifc_class, *attributes):
entity = file.create_entity(ifc_class)
for i in range(0, len(entity)):
def edit(self):
entities_ids = []
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
for i, _ in enumerate(self.names):
try:
value = attributes[i]
if isinstance(value, str) and value == "":
value = None
except:
value = None
if value is None and entity.is_a("IfcRoot") and i == 0:
value = ifcopenshell.guid.new()
if value is not None:
value = self.cast_value(self.entity_schema.attribute_by_index(i), value)
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:
entity[i] = value
except:
raise Exception(
"The IFC class {} requires a valid value for {} - you provided {}".format(
ifc_class, entity.attribute_name(i), value
)
)
self.outputs["file"].sv_set([[file]])
self.outputs["entity"].sv_set([[entity]])
if self.representations[i] and self.representations[i].is_a('IfcProductDefinitionShape'):
entity.Representation = self.representations[i]
elif self.representations[i]:
ifcopenshell.api.run("geometry.assign_representation", self.file, product=entity, representation=self.representations[i])
except IndexError:
pass
try:
if isinstance(self.locations[i], Matrix):
ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=entity, matrix=self.locations[i])
except IndexError:
pass
if entity.is_a() != self.ifc_class:
SvIfcStore.id_map[self.node_id].remove(step_id)
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())
def cast_value(self, attribute, value):
data_type = self.get_attribute_data_type(attribute.type_of_attribute())
if data_type == "integer":
value = int(value)
return value
if id_map_copy>entities_ids:
SvIfcStore.id_map[self.node_id] = entities_ids
return entities_ids
def get_attribute_data_type(self, data_type):
if hasattr(data_type, "declared_type"):
return self.get_attribute_data_type(data_type.declared_type())
return data_type
def repeat_input_unique(self, input, count):
input = repeat_last_for_length(input, count, deepcopy=False)
if input[0]:
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
return input
def sv_free(self):
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
-1
View File
@@ -1,4 +1,3 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -0,0 +1,79 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
# from ifcopenshell import template
class SvIfcCreateProject(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcCreateProject"
bl_label = "IFC Create Project"
def sv_init(self, context):
input_socket = self.inputs.new("SvStringsSocket", "file")
input_socket.tooltip = "ifc file to add the project to"
input_socket = self.inputs.new("SvStringsSocket", "project_name")
input_socket.tooltip = "Project name"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Adds project, unit and context to IFC file"
#op.tooltip = self.tooltip
def process(self):
#file
file = self.inputs["file"].sv_get()[0][0]
if file:
schema_name = file.wrapped_data.schema
else:
schema_name = "IFC4"
#project name
project_name = self.inputs["project_name"].sv_get()[0][0]
self.process_ifc(file, project_name)
def process_ifc(self, file, project_name):
# create project
project = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcProject", name=str(project_name))
lengthunit = ifcopenshell.api.run("unit.add_si_unit", file, unit_type="LENGTHUNIT", name="METRE")
ifcopenshell.api.run("unit.assign_unit", file, units=[lengthunit])
model = ifcopenshell.api.run("context.add_context", file, context_type="Model")
context = ifcopenshell.api.run(
"context.add_context",
file,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model,
)
self.outputs["file"].sv_set([[file]])
def register():
bpy.utils.register_class(SvIfcCreateProject)
def unregister():
bpy.utils.unregister_class(SvIfcCreateProject)
+73 -38
View File
@@ -21,65 +21,100 @@ import bpy
import logging
import ifcopenshell
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.bim.import_ifc
from bpy.props import StringProperty
from bpy.props import StringProperty, PointerProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcCreateShapeRefresh(bpy.types.Operator):
bl_idname = "node.sv_ifc_create_shape_refresh"
bl_label = "IFC Create Shape Refresh"
bl_options = {"UNDO"}
# class SvIfcCreateShapeRefresh(bpy.types.Operator):
# bl_idname = "node.sv_ifc_create_shape_refresh"
# bl_label = "IFC Create Shape Refresh"
# bl_options = {"UNDO"}
tree_name: StringProperty(default="")
node_name: StringProperty(default="")
def execute(self, context):
node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
node.process()
return {"FINISHED"}
# tree_name: StringProperty(default="")
# node_name: StringProperty(default="")
# def execute(self, context):
# node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
# node.process()
# return {"FINISHED"}
class SvIfcCreateShape(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
"""
Triggers: Ifc create shape by entity
Tooltip: Create Blender shape by Ifc Entity
"""
is_scene_dependent = True
is_interactive = False
node_dict = {}
def refresh_node(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
refresh_local: BoolProperty(name="Create shape(s)", description="Update Node", update=refresh_node)
bl_idname = "SvIfcCreateShape"
bl_label = "IFC Create Shape"
file: StringProperty(name="file", update=updateNode)
entity: StringProperty(name="entity", update=updateNode)
bl_label = "IFC Create Blender Shape"
entity: StringProperty(name="Entities", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.inputs.new("SvStringsSocket", "Entities").prop_name = "entity"
self.outputs.new('SvStringsSocket', "Object(s)")
def draw_buttons(self, context, layout):
self.wrapper_tracked_ui_draw_op(layout, "node.sv_ifc_create_shape_refresh", icon="FILE_REFRESH", text="Refresh")
row = layout.row(align=True)
row.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Create Blender shape from IfcEntity ID. Takes one or multiple IfcEntity IDs."
row.prop(self, 'refresh_local', icon='FILE_REFRESH')
def process(self):
self.sv_input_names = ["file", "entity"]
super().process()
def process_ifc(self, file, entity):
try:
if not entity.is_a("IfcProduct"):
return
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, entity)
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = file
mesh = ifc_importer.create_mesh(entity, shape)
obj = bpy.data.objects.new("IFC Element", mesh)
bpy.context.scene.collection.objects.link(obj)
except:
print("Entity could not be converted into a shape", entity)
self.entities = flatten_data(self.inputs["Entities"].sv_get(), target_level = 1)
if not self.entities[0]:
return
if self.refresh_local or hash(self) not in self.node_dict:
self.file = SvIfcStore.get_file()
try:
self.entities = [self.file.by_id(step_id) for step_id in self.entities]
except Exception as e:
raise
blender_objects = self.create()
self.node_dict[hash(self)] = blender_objects
else:
blender_objects = self.node_dict[hash(self)]
self.outputs["Object(s)"].sv_set(blender_objects)
def create(self):
blender_objects = []
for entity in self.entities:
try:
if not entity.is_a("IfcProduct"):
return
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, entity)
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(entity, shape)
obj = bpy.data.objects.new("IFC Element", mesh)
blender_objects.append(obj)
bpy.context.scene.collection.objects.link(obj)
except:
raise Exception("Entity could not be converted into a shape. Entity: {}".format(entity))
return blender_objects
def register():
bpy.utils.register_class(SvIfcCreateShapeRefresh)
# bpy.utils.register_class(SvIfcCreateShapeRefresh)
bpy.utils.register_class(SvIfcCreateShape)
def unregister():
bpy.utils.unregister_class(SvIfcCreateShape)
bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
# bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
+108
View File
@@ -0,0 +1,108 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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/>.
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty, EnumProperty
from ifcsverchok.ifcstore import SvIfcStore
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
def get_ifc_products(self, context):
ifc_products = getattr(self, "ifc_products", [])
file = SvIfcStore.get_file()
if not file:
return []
if ifc_products and file:
return ifc_products
ifc_products = []
ifc_products.extend(
[
(e, e, "")
for e in [
"IfcElement",
"IfcElementType",
"IfcSpatialElement",
"IfcGroup",
"IfcStructuralItem",
"IfcContext",
"IfcAnnotation",
]
]
)
if file.schema == "IFC2X3":
ifc_products[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
return ifc_products
def update_ifc_products(self, context):
if hasattr(self, "ifc_classes"):
self.ifc_classes.clear()
def get_ifc_classes(self, context):
ifc_classes = getattr(self, "ifc_classes", [])
if ifc_classes:
return self.ifc_classes
file = SvIfcStore.get_file()
if not file:
return []
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = schema.declaration_by_name(self.ifc_product)
def get_classes(declaration):
results = []
if not declaration.is_abstract():
results.append(declaration.name())
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
classes = get_classes(declaration)
ifc_classes.extend([(c, c, "") for c in sorted(classes)])
return ifc_classes
class SvIfcPickIfcClass(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcPickIfcClass"
bl_label = "IFC Class Picker"
ifc_product: EnumProperty(items=get_ifc_products, name="IfcProduct", description="Pick an IfcProduct from drop-down.", update=update_ifc_products)
ifc_class: EnumProperty(items=get_ifc_classes, name="IfcClass", description="Pick an IfcClass from drop-down.", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.outputs.new("SvStringsSocket", "IfcClass")
self.width = 200
def draw_buttons(self, context, layout):
layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Ifc Class Picker"
def process(self):
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
self.outputs["IfcClass"].sv_set([ifc_class])
def register():
bpy.utils.register_class(SvIfcPickIfcClass)
def unregister():
bpy.utils.unregister_class(SvIfcPickIfcClass)
@@ -0,0 +1,93 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
# 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 email.mime import application
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from ifcopenshell import template
class SvIfcQuickProjectSetup(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcQuickProjectSetup"
bl_label = "IFC Quick Project Setup"
schema_identifier: StringProperty(name="schema_identifier", update=updateNode, default="IFC4")
timestring: StringProperty(name="timestring", update=updateNode)
application: StringProperty(name="application", update=updateNode)
application_version: StringProperty(name="application_version", update=updateNode)
timestamp: StringProperty(name="timestamp", update=updateNode)
def sv_init(self, context):
input_socket = self.inputs.new("SvStringsSocket", "filename")
input_socket.tooltip = "Ifc file name"
input_socket = self.inputs.new("SvStringsSocket", "timestring")
input_socket.tooltip = "Timestring, default = current time"
input_socket = self.inputs.new("SvStringsSocket", "organization")
input_socket.tooltip = "Organization"
input_socket = self.inputs.new("SvStringsSocket", "creator")
input_socket.tooltip = "creator"
input_socket = self.inputs.new("SvStringsSocket", "schema_identifier")
input_socket.tooltip = "Schema, default = 'IFC4'"
input_socket = self.inputs.new("SvStringsSocket", "application_version")
input_socket.tooltip = "Application version"
input_socket = self.inputs.new("SvStringsSocket", "timestamp")
input_socket.tooltip = "Timestamp, default = current time"
input_socket = self.inputs.new("SvStringsSocket", "application")
input_socket.tooltip = "Application, default = 'IfcOpenShell'"
input_socket = self.inputs.new("SvStringsSocket", "project_globalid")
input_socket.tooltip = "Project GlobalId"
input_socket = self.inputs.new("SvStringsSocket", "project_name")
input_socket.tooltip = "Project name"
self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout):
op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Quick Project Setup: creates Ifc file and sets up a basic project"
#op.tooltip = self.tooltip
def process(self):
self.sv_input_names = [i.name for i in self.inputs]
super().process()
def process_ifc(self, *setting_values):
settings = dict(zip(self.sv_input_names, setting_values))
settings = {k: v for k, v in settings.items() if v != ""}
file = template.create(
filename=settings['filename'],
timestring=settings['timestring'],
organization=settings['organization'],
creator=settings['creator'],
schema_identifier=settings['schema_identifier'],
application_version=settings['application_version'],
timestamp=settings['timestamp'],
application=settings['application'],
project_globalid=settings['project_globalid'],
project_name=settings['project_name'],
)
self.outputs["file"].sv_set([[file]])
def register():
bpy.utils.register_class(SvIfcQuickProjectSetup)
def unregister():
bpy.utils.unregister_class(SvIfcQuickProjectSetup)
@@ -0,0 +1,197 @@
# IfcSverchok - IFC Sverchok extension
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# 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
import bpy
import ifcopenshell
import ifcsverchok.helper
import ifcopenshell.api
from ifcsverchok.ifcstore import SvIfcStore
import blenderbim.tool as tool
import blenderbim.core.geometry as core
from bpy.props import StringProperty, EnumProperty, IntProperty, FloatVectorProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
"""
Triggers: Sv to Ifc Repr
Tooltip: Sverchok geometry to Ifc Shape Representation
"""
bl_idname = "SvIfcSverchokToIfcRepr"
bl_label = "IFC Sverchok to IFC Repr"
node_dict = {}
n_id: StringProperty()
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),
]
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)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type"
self.inputs.new("SvStringsSocket", "context_identifier").prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new("SvVerticesSocket", "Vertices")
self.inputs.new("SvStringsSocket", "Edges")
self.inputs.new("SvStringsSocket", "Faces")
self.outputs.new("SvVerticesSocket", "Representation(s)")
self.width = 210
self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout):
op = layout.operator(
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = "Sverchok geometry to Ifc Shape Representation. \nTakes one or multiple geometries."
def process(self):
if not any(socket.is_linked for socket in self.inputs):
return
self.file = SvIfcStore.get_file()
self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict:
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))
edit = False
for i in range(len(self.inputs)):
input = self.inputs[i].sv_get(deepcopy=False)
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
self.vertices = self.inputs["Vertices"].sv_get(deepcopy=False)
self.edges = self.inputs["Edges"].sv_get(deepcopy=False)
self.faces = self.inputs["Faces"].sv_get(deepcopy=False)
geo_data = list(zip(self.vertices, self.edges, self.faces))
if self.node_id not in SvIfcStore.id_map:
representations = self.create(geo_data)
else:
if edit is True:
self.edit()
representations = self.create(geo_data)
else:
# representations = self.get_existing_element()
representations = SvIfcStore.id_map[self.node_id]["Representations"]
self.outputs["Representation(s)"].sv_set(representations)
def create(self, geo_data):
representations_ids = []
self.context = self.get_context()
for item in geo_data:
representation = ifcopenshell.api.run(
"geometry.add_sverchok_representation",
self.file,
should_run_listeners=False,
context=self.context,
vertices=[item[0]],
edges=[item[1]],
faces=[item[2]],
)
if not representation:
raise Exception("Couldn't create representation. Possibly wrong context.")
representations_ids.append(representation.id())
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Representations", []).append(representation.id())
return representations_ids
def edit(self):
# results = self.get_existing_element()
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
ifcopenshell.api.run(
"geometry.remove_representation", self.file, representation=self.file.by_id(step_id)
)
del SvIfcStore.id_map[self.node_id]["Representations"]
return
def get_context(self):
context = ifcopenshell.util.representation.get_context(
self.file, self.context_type, self.context_identifier, self.target_view
)
if not context:
parent = ifcopenshell.util.representation.get_context(self.file, self.context_type)
if not parent:
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,
)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
return context
def sv_free(self):
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
for step_id in SvIfcStore.id_map[self.node_id]["Representations"]:
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
ifcopenshell.api.run("context.remove_context", self.file, context=context)
if parent:
if not self.file.get_inverse(parent):
ifcopenshell.api.run("context.remove_context", self.file, context=parent)
print("Removed context with step ID: ", context_id)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
print('Node was deleted')
except KeyError or AttributeError:
pass
def register():
bpy.utils.register_class(SvIfcSverchokToIfcRepr)
def unregister():
bpy.utils.unregister_class(SvIfcSverchokToIfcRepr)
+76 -9
View File
@@ -17,30 +17,97 @@
# You should have received a copy of the GNU General Public License
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
from os.path import abspath, splitext
import bpy
import ifcopenshell
import ifcsverchok.helper
from bpy.props import StringProperty
from ifcsverchok.ifcstore import SvIfcStore
from bpy.props import StringProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import updateNode, flatten_data
class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
"""
Triggers: Ifc write to file
Tooltip: Write active Sverchok Ifc file to path
"""
def refresh_node_local(self, context):
if self.refresh_local:
self.process()
self.refresh_local = False
# out = ""
refresh_local: BoolProperty(name="Write", description="Write to file", update=refresh_node_local)
bl_idname = "SvIfcWriteFile"
bl_label = "IFC Write File"
file: StringProperty(name="file", update=updateNode)
path: StringProperty(name="path", update=updateNode)
path: StringProperty(name="path", description="File path to write to. Can be relative.", update=updateNode)
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
self.inputs.new("SvStringsSocket", "path").prop_name = "path"
self.outputs.new("SvStringsSocket", "output")
def draw_buttons(self, context, layout):
row = layout.row(align=True)
row.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Writes active Ifc file to path.\n It will overwrite an existing file.\n N.B.! It's recommended to create a fresh IFC File using the 're-run all nodes' button in IfcSverchok panel before saving."
row.prop(self, 'refresh_local', icon='FILE_REFRESH')
def process(self):
self.sv_input_names = ["file", "path"]
super().process()
path = flatten_data(self.inputs["path"].sv_get(), target_level = 1)[0]
if not path:
return
path = abspath(path)
file = SvIfcStore.get_file()
_, ext = splitext(path)
if not ext:
raise Exception("Bad path. Provide a path to a file.")
if self.refresh_local and ext:
self.ensure_hirarchy(file)
file.write(path)
self.outputs["output"].sv_set(f"File written successfully to: {path}.")
def ensure_hirarchy(self, file):
elements_in_buildings = []
if not 0 <= 0 < len(file.by_type("IfcBuilding")):
my_building = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcBuilding", name="My Building")
elements = ifcopenshell.util.element.get_decomposition(my_building)
else:
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings.extend(elements)
def process_ifc(self, file, path):
file.write(path)
for spatial in (file.by_type("IfcSpatialElement") or file.by_type("IfcSpatialStructureElement")):
if (not (spatial.is_a("IfcSite") or spatial.is_a("IfcBuilding")) and (spatial not in elements_in_buildings)):
elements = ifcopenshell.util.element.get_decomposition(spatial)
ifcopenshell.api.run("aggregate.assign_object", file, product=spatial, relating_object=file.by_type("IfcBuilding")[0])
elements_in_buildings_after = []
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
elements_in_buildings_after.extend(elements)
elements = file.by_type("IfcElement")
for element in elements:
if element not in elements_in_buildings:
ifcopenshell.api.run("spatial.assign_container", file, product=element, relating_structure=file.by_type("IfcBuilding")[0])
for building in file.by_type("IfcBuilding"):
elements = ifcopenshell.util.element.get_decomposition(building)
if not building.Decomposes:
if not 0 <= 0 < len(file.by_type("IfcSite")):
ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcSite", name="My Site")
ifcopenshell.api.run("aggregate.assign_object", file, product=building, relating_object=file.by_type("IfcSite")[0])
try:
if file.by_type("IfcSite")[0].Decomposes[0].RelatingObject.is_a("IfcProject"):
continue
except IndexError:
pass
ifcopenshell.api.run("aggregate.assign_object", file, product=file.by_type("IfcSite")[0], relating_object=file.by_type("IfcProject")[0])
self.file = file
return
def register():