mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 19:34:34 +00:00
Merge branch 'ifcsverchok_dev' of github.com:mdjska/IfcOpenShell into v0.7.0
Merging gsoc changes
This commit is contained in:
+150
-6
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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,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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user