mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +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
|
# IfcSverchok - IFC Sverchok extension
|
||||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
# 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.core import sv_registration_utils, make_node_list
|
||||||
from sverchok.utils import auto_gather_node_classes, get_node_class_reference
|
from sverchok.utils import auto_gather_node_classes, get_node_class_reference
|
||||||
from sverchok.menu import SverchNodeItem, SverchNodeCategory, register_node_panels
|
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.ui.nodeview_space_menu import make_extra_category_menus
|
||||||
from sverchok.utils.logging import info, debug
|
from sverchok.utils.logging import info, debug
|
||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
|
||||||
def nodes_index():
|
def nodes_index():
|
||||||
return [
|
return [
|
||||||
@@ -52,17 +55,26 @@ def nodes_index():
|
|||||||
("ifc.create_entity", "SvIfcCreateEntity"),
|
("ifc.create_entity", "SvIfcCreateEntity"),
|
||||||
("ifc.create_shape", "SvIfcCreateShape"),
|
("ifc.create_shape", "SvIfcCreateShape"),
|
||||||
("ifc.read_entity", "SvIfcReadEntity"),
|
("ifc.read_entity", "SvIfcReadEntity"),
|
||||||
|
("ifc.pick_ifc_class", "SvIfcPickIfcClass"),
|
||||||
("ifc.by_id", "SvIfcById"),
|
("ifc.by_id", "SvIfcById"),
|
||||||
("ifc.by_guid", "SvIfcByGuid"),
|
("ifc.by_guid", "SvIfcByGuid"),
|
||||||
("ifc.by_type", "SvIfcByType"),
|
("ifc.by_type", "SvIfcByType"),
|
||||||
("ifc.by_query", "SvIfcByQuery"),
|
("ifc.by_query", "SvIfcByQuery"),
|
||||||
("ifc.add", "SvIfcAdd"),
|
("ifc.add", "SvIfcAdd"),
|
||||||
|
("ifc.add_pset", "SvIfcAddPset"),
|
||||||
|
("ifc.add_spatial_element", "SvIfcAddSpatialElement"),
|
||||||
("ifc.remove", "SvIfcRemove"),
|
("ifc.remove", "SvIfcRemove"),
|
||||||
("ifc.generate_guid", "SvIfcGenerateGuid"),
|
("ifc.generate_guid", "SvIfcGenerateGuid"),
|
||||||
("ifc.get_property", "SvIfcGetProperty"),
|
("ifc.get_property", "SvIfcGetProperty"),
|
||||||
("ifc.get_attribute", "SvIfcGetAttribute"),
|
("ifc.get_attribute", "SvIfcGetAttribute"),
|
||||||
("ifc.select_blender_objects", "SvIfcSelectBlenderObjects"),
|
("ifc.select_blender_objects", "SvIfcSelectBlenderObjects"),
|
||||||
("ifc.api", "SvIfcApi"),
|
("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
|
reload_event = False
|
||||||
|
|
||||||
import bpy
|
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():
|
def register_nodes():
|
||||||
node_modules = make_node_list()
|
node_modules = make_node_list()
|
||||||
@@ -109,7 +245,10 @@ def make_menu():
|
|||||||
nodetype = item[1]
|
nodetype = item[1]
|
||||||
rna = get_node_class_reference(nodetype)
|
rna = get_node_class_reference(nodetype)
|
||||||
if not rna:
|
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:
|
else:
|
||||||
node_item = SverchNodeItem.new(nodetype)
|
node_item = SverchNodeItem.new(nodetype)
|
||||||
node_items.append(node_item)
|
node_items.append(node_item)
|
||||||
@@ -135,13 +274,16 @@ def register():
|
|||||||
global our_menu_classes
|
global our_menu_classes
|
||||||
|
|
||||||
debug("Registering ifcsverchok")
|
debug("Registering ifcsverchok")
|
||||||
|
for klass in CLASSES:
|
||||||
|
bpy.utils.register_class(klass)
|
||||||
register_nodes()
|
register_nodes()
|
||||||
extra_nodes = importlib.import_module(".nodes", "ifcsverchok")
|
extra_nodes = importlib.import_module(".nodes", "ifcsverchok")
|
||||||
auto_gather_node_classes(extra_nodes)
|
auto_gather_node_classes(extra_nodes)
|
||||||
menu = make_menu()
|
menu = make_menu()
|
||||||
menu_category_provider = SvExCategoryProvider("IFCSVERCHOK", 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)
|
nodeitems_utils.register_node_categories("IFCSVERCHOK", menu)
|
||||||
our_menu_classes = make_extra_category_menus()
|
our_menu_classes = make_extra_category_menus()
|
||||||
|
|
||||||
@@ -158,3 +300,5 @@ def unregister():
|
|||||||
print(e)
|
print(e)
|
||||||
unregister_extra_category_provider("IFCSVERCHOK")
|
unregister_extra_category_provider("IFCSVERCHOK")
|
||||||
unregister_nodes()
|
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):
|
def update_usecase(self, context):
|
||||||
|
print("API - running update usecase!")
|
||||||
module_usecase = self.get_module_usecase()
|
module_usecase = self.get_module_usecase()
|
||||||
if module_usecase:
|
if module_usecase:
|
||||||
self.generate_node(*module_usecase)
|
self.generate_node(*module_usecase)
|
||||||
|
|||||||
@@ -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 bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcsverchok.helper
|
import ifcsverchok.helper
|
||||||
|
from ifcsverchok.ifcstore import SvIfcStore
|
||||||
from bpy.props import StringProperty
|
from bpy.props import StringProperty
|
||||||
from sverchok.node_tree import SverchCustomTreeNode
|
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):
|
class SvIfcById(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||||
bl_idname = "SvIfcById"
|
bl_idname = "SvIfcById"
|
||||||
bl_label = "IFC By Id"
|
bl_label = "IFC By Id"
|
||||||
file: StringProperty(name="file", update=updateNode)
|
id: StringProperty(name="Id(s)", update=updateNode, )
|
||||||
id: StringProperty(name="id", update=updateNode)
|
|
||||||
|
|
||||||
def sv_init(self, context):
|
def sv_init(self, context):
|
||||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
|
||||||
self.inputs.new("SvStringsSocket", "id").prop_name = "id"
|
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):
|
def process(self):
|
||||||
self.sv_input_names = ["file", "id"]
|
self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1)
|
||||||
super().process()
|
print(self.ids)
|
||||||
|
if not self.ids[0]:
|
||||||
def process_ifc(self, file, id):
|
return
|
||||||
self.outputs["entity"].sv_set([[file.by_id(int(id))]])
|
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():
|
def register():
|
||||||
bpy.utils.register_class(SvIfcById)
|
bpy.utils.register_class(SvIfcById)
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ import bpy
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcsverchok.helper
|
import ifcsverchok.helper
|
||||||
from bpy.props import StringProperty, EnumProperty
|
from bpy.props import StringProperty, EnumProperty
|
||||||
|
from ifcsverchok.ifcstore import SvIfcStore
|
||||||
from sverchok.node_tree import SverchCustomTreeNode
|
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):
|
def get_ifc_products(self, context):
|
||||||
ifc_products = getattr(self, "ifc_products", [])
|
ifc_products = getattr(self, "ifc_products", [])
|
||||||
file = self.inputs["file"].sv_get()[0][0]
|
file = SvIfcStore.get_file()
|
||||||
if not file:
|
if not file:
|
||||||
return []
|
return []
|
||||||
if ifc_products and file:
|
if ifc_products and file:
|
||||||
@@ -56,12 +57,11 @@ def update_ifc_products(self, context):
|
|||||||
if hasattr(self, "ifc_classes"):
|
if hasattr(self, "ifc_classes"):
|
||||||
self.ifc_classes.clear()
|
self.ifc_classes.clear()
|
||||||
|
|
||||||
|
|
||||||
def get_ifc_classes(self, context):
|
def get_ifc_classes(self, context):
|
||||||
ifc_classes = getattr(self, "ifc_classes", [])
|
ifc_classes = getattr(self, "ifc_classes", [])
|
||||||
if ifc_classes:
|
if ifc_classes:
|
||||||
return self.ifc_classes
|
return self.ifc_classes
|
||||||
file = self.inputs["file"].sv_get()[0][0]
|
file = SvIfcStore.get_file()
|
||||||
if not file:
|
if not file:
|
||||||
return []
|
return []
|
||||||
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
|
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):
|
class SvIfcByType(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||||
bl_idname = "SvIfcByType"
|
bl_idname = "SvIfcByType"
|
||||||
bl_label = "IFC By Type"
|
bl_label = "IFC By Type"
|
||||||
file: StringProperty(name="file", update=updateNode)
|
ifc_product: EnumProperty(items=get_ifc_products, name="IfcProduct", description="Pick an IfcProduct from drop-down.", update=update_ifc_products)
|
||||||
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=update_ifc_products)
|
ifc_class: EnumProperty(items=get_ifc_classes, name="IfcClass", description="Pick an IfcClass from drop-down.", update=updateNode)
|
||||||
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=updateNode)
|
custom_ifc_class: StringProperty(name="Custom IfcClass", description="Give the name of your custom IfcClass.", update=updateNode)
|
||||||
custom_ifc_class: StringProperty(name="Custom Ifc Class", update=updateNode)
|
|
||||||
|
|
||||||
def sv_init(self, context):
|
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_product").prop_name = "ifc_product"
|
||||||
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
|
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
|
||||||
self.inputs.new("SvStringsSocket", "custom_ifc_class").prop_name = "custom_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):
|
def process(self):
|
||||||
file = self.inputs["file"].sv_get()[0][0]
|
self.file = SvIfcStore.get_file()
|
||||||
if file:
|
if self.file:
|
||||||
self.ifc_products = get_ifc_products(self, bpy.context)
|
self.ifc_products = get_ifc_products(self, bpy.context)
|
||||||
self.ifc_classes = get_ifc_classes(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()
|
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:
|
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:
|
else:
|
||||||
self.outputs["entity"].sv_set([file.by_type(ifc_class)])
|
self.outputs["Entity"].sv_set([])
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|||||||
@@ -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
|
# import ifcsverchok.ifc_store
|
||||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
# from bpy.props import StringProperty
|
||||||
#
|
# from sverchok.node_tree import SverchCustomTreeNode
|
||||||
# This file is part of IfcSverchok.
|
# from sverchok.data_structure import updateNode
|
||||||
#
|
|
||||||
# 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 bpy
|
||||||
import ifcopenshell
|
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.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):
|
class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||||
bl_idname = "SvIfcCreateEntity"
|
bl_idname = "SvIfcCreateEntity"
|
||||||
bl_label = "IFC Create Entity"
|
bl_label = "IFC Create Entity"
|
||||||
file: StringProperty(name="file", update=updateNode)
|
node_dict = {}
|
||||||
ifc_class: StringProperty(name="ifc_class", update=updateNode)
|
|
||||||
current_ifc_class: StringProperty(name="current_ifc_class")
|
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):
|
def sv_init(self, context):
|
||||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
|
||||||
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
|
self.inputs.new("SvStringsSocket", "Descriptions").prop_name = "Descriptions"
|
||||||
self.outputs.new("SvStringsSocket", "file")
|
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
|
||||||
self.outputs.new("SvStringsSocket", "entity")
|
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):
|
def process(self):
|
||||||
self.sv_input_names = ["file", "ifc_class"]
|
|
||||||
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
|
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
|
||||||
if ifc_class:
|
self.descriptions = flatten_data(self.inputs["Descriptions"].sv_get(), target_level=1)
|
||||||
file = self.inputs["file"].sv_get()[0][0]
|
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[0]
|
||||||
if file:
|
self.representations = flatten_data(self.inputs["Representations"].sv_get(), target_level=1)
|
||||||
schema_name = file.wrapped_data.schema
|
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:
|
else:
|
||||||
schema_name = "IFC4"
|
entities = SvIfcStore.id_map[self.node_id]
|
||||||
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name)
|
|
||||||
self.entity_schema = self.schema.declaration_by_name(ifc_class)
|
|
||||||
|
|
||||||
if ifc_class != self.current_ifc_class:
|
print("Entities: ", entities)
|
||||||
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:
|
try:
|
||||||
for i in range(0, len(self.inputs)):
|
entity = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=self.ifc_class, name=self.names[i], description=self.descriptions[i])
|
||||||
self.inputs[i].sv_get()
|
try:
|
||||||
except:
|
if self.representations[i]:
|
||||||
# This occurs when a blender save file is reloaded
|
ifcopenshell.api.run("geometry.assign_representation", self.file, product=entity, representation=self.representations[i])
|
||||||
self.generate_inputs(ifc_class)
|
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()):
|
def edit(self):
|
||||||
self.sv_input_names.append(self.entity_schema.attribute_by_index(i).name())
|
entities_ids = []
|
||||||
super().process()
|
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
|
||||||
|
for i, _ in enumerate(self.names):
|
||||||
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)):
|
|
||||||
try:
|
try:
|
||||||
value = attributes[i]
|
step_id = id_map_copy[i]
|
||||||
if isinstance(value, str) and value == "":
|
except IndexError:
|
||||||
value = None
|
id = self.create(index=i)
|
||||||
except:
|
entities_ids.append(id[0])
|
||||||
value = None
|
continue
|
||||||
if value is None and entity.is_a("IfcRoot") and i == 0:
|
entity = self.file.by_id(step_id)
|
||||||
value = ifcopenshell.guid.new()
|
entity.Name = self.names[i]
|
||||||
if value is not None:
|
entity.Description = self.descriptions[i]
|
||||||
value = self.cast_value(self.entity_schema.attribute_by_index(i), value)
|
|
||||||
try:
|
try:
|
||||||
entity[i] = value
|
if self.representations[i] and self.representations[i].is_a('IfcProductDefinitionShape'):
|
||||||
except:
|
entity.Representation = self.representations[i]
|
||||||
raise Exception(
|
elif self.representations[i]:
|
||||||
"The IFC class {} requires a valid value for {} - you provided {}".format(
|
ifcopenshell.api.run("geometry.assign_representation", self.file, product=entity, representation=self.representations[i])
|
||||||
ifc_class, entity.attribute_name(i), value
|
except IndexError:
|
||||||
)
|
pass
|
||||||
)
|
try:
|
||||||
self.outputs["file"].sv_set([[file]])
|
if isinstance(self.locations[i], Matrix):
|
||||||
self.outputs["entity"].sv_set([[entity]])
|
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):
|
if id_map_copy>entities_ids:
|
||||||
data_type = self.get_attribute_data_type(attribute.type_of_attribute())
|
SvIfcStore.id_map[self.node_id] = entities_ids
|
||||||
if data_type == "integer":
|
return entities_ids
|
||||||
value = int(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_attribute_data_type(self, data_type):
|
def repeat_input_unique(self, input, count):
|
||||||
if hasattr(data_type, "declared_type"):
|
input = repeat_last_for_length(input, count, deepcopy=False)
|
||||||
return self.get_attribute_data_type(data_type.declared_type())
|
if input[0]:
|
||||||
return data_type
|
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():
|
def register():
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# IfcSverchok - IFC Sverchok extension
|
# IfcSverchok - IFC Sverchok extension
|
||||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
# 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 logging
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcsverchok.helper
|
import ifcsverchok.helper
|
||||||
|
from ifcsverchok.ifcstore import SvIfcStore
|
||||||
import blenderbim.bim.import_ifc
|
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.node_tree import SverchCustomTreeNode
|
||||||
from sverchok.data_structure import updateNode
|
from sverchok.data_structure import updateNode, flatten_data
|
||||||
|
|
||||||
|
|
||||||
class SvIfcCreateShapeRefresh(bpy.types.Operator):
|
# class SvIfcCreateShapeRefresh(bpy.types.Operator):
|
||||||
bl_idname = "node.sv_ifc_create_shape_refresh"
|
# bl_idname = "node.sv_ifc_create_shape_refresh"
|
||||||
bl_label = "IFC Create Shape Refresh"
|
# bl_label = "IFC Create Shape Refresh"
|
||||||
bl_options = {"UNDO"}
|
# bl_options = {"UNDO"}
|
||||||
|
|
||||||
tree_name: StringProperty(default="")
|
|
||||||
node_name: StringProperty(default="")
|
|
||||||
|
|
||||||
def execute(self, context):
|
# tree_name: StringProperty(default="")
|
||||||
node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
|
# node_name: StringProperty(default="")
|
||||||
node.process()
|
|
||||||
return {"FINISHED"}
|
# 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):
|
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_idname = "SvIfcCreateShape"
|
||||||
bl_label = "IFC Create Shape"
|
bl_label = "IFC Create Blender Shape"
|
||||||
file: StringProperty(name="file", update=updateNode)
|
entity: StringProperty(name="Entities", update=updateNode)
|
||||||
entity: StringProperty(name="entity", update=updateNode)
|
|
||||||
|
|
||||||
def sv_init(self, context):
|
def sv_init(self, context):
|
||||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
self.inputs.new("SvStringsSocket", "Entities").prop_name = "entity"
|
||||||
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
|
self.outputs.new('SvStringsSocket', "Object(s)")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
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):
|
def process(self):
|
||||||
self.sv_input_names = ["file", "entity"]
|
self.entities = flatten_data(self.inputs["Entities"].sv_get(), target_level = 1)
|
||||||
super().process()
|
if not self.entities[0]:
|
||||||
|
return
|
||||||
|
|
||||||
def process_ifc(self, file, entity):
|
if self.refresh_local or hash(self) not in self.node_dict:
|
||||||
try:
|
self.file = SvIfcStore.get_file()
|
||||||
if not entity.is_a("IfcProduct"):
|
try:
|
||||||
return
|
self.entities = [self.file.by_id(step_id) for step_id in self.entities]
|
||||||
logger = logging.getLogger("ImportIFC")
|
except Exception as e:
|
||||||
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
|
raise
|
||||||
settings = ifcopenshell.geom.settings()
|
blender_objects = self.create()
|
||||||
shape = ifcopenshell.geom.create_shape(settings, entity)
|
self.node_dict[hash(self)] = blender_objects
|
||||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
|
else:
|
||||||
ifc_importer.file = file
|
blender_objects = self.node_dict[hash(self)]
|
||||||
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.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():
|
def register():
|
||||||
bpy.utils.register_class(SvIfcCreateShapeRefresh)
|
# bpy.utils.register_class(SvIfcCreateShapeRefresh)
|
||||||
bpy.utils.register_class(SvIfcCreateShape)
|
bpy.utils.register_class(SvIfcCreateShape)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(SvIfcCreateShape)
|
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
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from os.path import abspath, splitext
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcsverchok.helper
|
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.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):
|
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_idname = "SvIfcWriteFile"
|
||||||
bl_label = "IFC Write File"
|
bl_label = "IFC Write File"
|
||||||
file: StringProperty(name="file", update=updateNode)
|
path: StringProperty(name="path", description="File path to write to. Can be relative.", update=updateNode)
|
||||||
path: StringProperty(name="path", update=updateNode)
|
|
||||||
|
|
||||||
def sv_init(self, context):
|
def sv_init(self, context):
|
||||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
|
||||||
self.inputs.new("SvStringsSocket", "path").prop_name = "path"
|
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):
|
def process(self):
|
||||||
self.sv_input_names = ["file", "path"]
|
path = flatten_data(self.inputs["path"].sv_get(), target_level = 1)[0]
|
||||||
super().process()
|
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)
|
||||||
|
|
||||||
|
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 process_ifc(self, file, path):
|
|
||||||
file.write(path)
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|||||||
Reference in New Issue
Block a user