diff --git a/src/ifcsverchok/__init__.py b/src/ifcsverchok/__init__.py index e332afbd03..b9e8b7d4af 100644 --- a/src/ifcsverchok/__init__.py +++ b/src/ifcsverchok/__init__.py @@ -60,6 +60,8 @@ def nodes_index(): ("ifc.by_type", "SvIfcByType"), ("ifc.by_query", "SvIfcByQuery"), ("ifc.add", "SvIfcAdd"), + ("ifc.add_pset", "SvIfcAddPset"), + ("ifc.add_spatial_element", "SvIfcAddSpatialElement"), ("ifc.remove", "SvIfcRemove"), ("ifc.generate_guid", "SvIfcGenerateGuid"), ("ifc.get_property", "SvIfcGetProperty"), @@ -68,6 +70,7 @@ def nodes_index(): ("ifc.api", "SvIfcApi"), ("ifc.api_WIP", "SvIfcApiWIP"), ("ifc.bmesh_to_ifc", "SvIfcBMeshToIfcGeo"), + ("ifc.bmesh_to_ifc2", "SvIfcBMeshToIfcRepr"), ("ifc.create_project", "SvIfcCreateProject"), ("ifc.quick_project_setup", "SvIfcQuickProjectSetup") diff --git a/src/ifcsverchok/ifcstore.py b/src/ifcsverchok/ifcstore.py index cc0303165b..c334e952fc 100644 --- a/src/ifcsverchok/ifcstore.py +++ b/src/ifcsverchok/ifcstore.py @@ -93,4 +93,6 @@ class SvIfcStore: @staticmethod def get_file(): + if SvIfcStore.file is None: + SvIfcStore.create_boilerplate() return SvIfcStore.file diff --git a/src/ifcsverchok/nodes/ifc/add_pset.py b/src/ifcsverchok/nodes/ifc/add_pset.py new file mode 100644 index 0000000000..bb56caffd1 --- /dev/null +++ b/src/ifcsverchok/nodes/ifc/add_pset.py @@ -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() diff --git a/src/ifcsverchok/nodes/ifc/add_spatial_element.py b/src/ifcsverchok/nodes/ifc/add_spatial_element.py new file mode 100644 index 0000000000..3673f5d3de --- /dev/null +++ b/src/ifcsverchok/nodes/ifc/add_spatial_element.py @@ -0,0 +1,81 @@ +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 SvIfcAddSpatialElement(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore): + bl_idname = "SvIfcAddSpatialElement" + bl_label = "IFC Add Spatial Element" + Name: StringProperty(name="Name", 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", "Name").prop_name = "Name" + self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass" + 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] + ifc_class = self.inputs["IfcClass"].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, ifc_class, elements) + else: + element = self.edit(name, ifc_class, elements) + + self.outputs["entity"].sv_set([[element]]) + self.outputs["file"].sv_set([[self.file]]) + + def create(self, name, ifc_class, elements): + results = [] + result = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class) + for element in elements: + ifcopenshell.api.run("spatial.assign_container", self.file, product=element, relating_structure=result) + SvIfcStore.id_map[result.id()] = self.node_id + results.append(result) + return result + + def edit(self, name, ifc_class, elements): + result = self.get_existing_element() + subelements = set(ifcopenshell.util.element.get_decomposition(result)) + elements_set = set(elements) + for removed_element in subelements - elements_set: + # Just realised I don't have a spatial.unassign_container, but if so we'd do it here + pass + for added_element in elements_set - subelements: + ifcopenshell.api.run("spatial.assign_container", self.file, product=added_element, relating_structure=result) + 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(SvIfcAddSpatialElement) + + +def unregister(): + bpy.utils.unregister_class(SvIfcAddSpatialElement) + SvIfcStore.purge() diff --git a/src/ifcsverchok/nodes/ifc/bmesh_to_ifc.py b/src/ifcsverchok/nodes/ifc/bmesh_to_ifc.py index 18b8a2112f..bbde010dce 100644 --- a/src/ifcsverchok/nodes/ifc/bmesh_to_ifc.py +++ b/src/ifcsverchok/nodes/ifc/bmesh_to_ifc.py @@ -34,7 +34,7 @@ class SvIfcBMeshToIfcGeo(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helpe Tooltip: Blender mesh to Ifc Geometric Representation """ bl_idname = "SvIfcBMeshToIfcGeo" - bl_label = "IFC Blender Mesh to IFC Geo" + bl_label = "IFC Blender Mesh to IFC Geo (old)" tooltip: StringProperty(name="Tooltip") context_id: bpy.props.IntProperty() diff --git a/src/ifcsverchok/nodes/ifc/bmesh_to_ifc2.py b/src/ifcsverchok/nodes/ifc/bmesh_to_ifc2.py new file mode 100644 index 0000000000..f208d3f928 --- /dev/null +++ b/src/ifcsverchok/nodes/ifc/bmesh_to_ifc2.py @@ -0,0 +1,166 @@ +# IfcSverchok - IFC Sverchok extension +# Copyright (C) 2020, 2021 Dion Moult +# +# 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 . + +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 +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode +from blenderbim.bim.module.root.prop import get_contexts + + +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" + blender_objects: StringProperty(name="Blender Mesh(es)", description="Blender Mesh Object(s)", update=updateNode) + ifc_representation_class: StringProperty(name="Ifc Representation Class", description="Whether to cast a mesh into a particular class", update=updateNode) + + 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), + ] + paradigms = [ + ('Tessellation', 'Tessellation', 'Geometry paradigm: Tessellation', 0), + ('Extrusion', 'Extrusion', 'Geometry paradigm: Extrusion', 1), + ] + + 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) + paradigm: EnumProperty(name="Paradigm", description="Which geometry type to convert to. Choose between tessellation or extrusion. Default: Tessellation.",default="Tessellation",items=paradigms, update=updateNode) + tooltip: StringProperty(name="Tooltip") + context_id: bpy.props.IntProperty() + + def sv_init(self, context): + self.inputs.new("SvObjectSocket", "blender_objects") #no prop for now + self.inputs.new("SvStringsSocket", "ifc_representation_class").prop_name = "ifc_representation_class" + 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("SvStringsSocket", "paradigm").prop_name = "paradigm" + self.outputs.new("SvVerticesSocket", "file") + self.outputs.new("SvVerticesSocket", "Representation") + + def draw_buttons(self, context, layout): + op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = "Blender mesh to Ifc Shape Representation" + # layout.prop(self, 'context_type', text='') + # layout.prop(self, 'context_identifier', text='') + # layout.prop(self, 'target_view', text='') + # layout.prop(self, 'paradigm', text='') + #op.tooltip = self.tooltip + + def process(self): + # if not self.inputs['blender_objects'].is_linked: + # return + + self.file = SvIfcStore.get_file() + + + self.sv_input_names = [i.name for i in self.inputs] + for i in range(0, len(self.inputs)): + print("self.sv_input_names[i]: ", self.sv_input_names[i]) + print("self.inputs[i].sv_get(): ", self.inputs[i].sv_get()) + if self.sv_input_names[i] != "blender_objects": + setattr(self, self.sv_input_names[i], self.inputs[i].sv_get()) #doesn't work for lists? + + self.blender_objects = self.inputs["blender_object"].sv_get() + self.create_context() + + if self.node_id not in SvIfcStore.id_map.values(): + for blender_object in self.blender_objects: + geometry = blender_object.data + self.process_ifc(self.file, blender_object, geometry) + else: + #TODO: edit existing representation + raise Exception("Editing not yet implemented") + + + SvIfcStore.file = self.file + try: + self.outputs["file"].sv_set([[self.file]]) #only for testing + self.outputs["Representation"].sv_set([self.representation]) + except: + raise Exception( + "Couldn't write to file. Representation: {}".format( + self.representation + ) + ) + + def process_ifc(self, blender_object, geometry): + try: + self.representation = [ifcopenshell.api.run("geometry.add_representation", self.file, should_run_listeners=False,blender_object = blender_object, geometry=geometry, context = self.context)] + except: + raise Exception("Couldn't create representation. Representation: {}".format( + self.representation + )) + SvIfcStore.id_map[self.representation.id()] = self.node_id + + def create_context(self): + if self.file.by_type("IFCGEOMETRICREPRESENTATIONCONTEXT", include_subtypes=True): + for context in self.file.by_type("IFCGEOMETRICREPRESENTATIONCONTEXT", include_subtypes=True): + if context.get_info()["ContextType"] == "Model" and context.get_info()["ContextIdentifier"] == "Body": + self.context = context + elif context.get_info()["ContextType"] == "Model": + self.context= context + else: + model = ifcopenshell.api.run("context.add_context", self.file, context_type=self.context_type) + self.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=model, + ) + SvIfcStore.id_map[self.context.id()] = self.node_id + + def sv_free(self): + try: + SvIfcStore.id_map.pop(self.representation.id()) + SvIfcStore.id_map.pop(self.context.id()) + except KeyError or AttributeError: + pass + +def register(): + bpy.utils.register_class(SvIfcBMeshToIfcRepr) + + +def unregister(): + bpy.utils.unregister_class(SvIfcBMeshToIfcRepr) diff --git a/src/ifcsverchok/nodes/ifc/create_entity2.py b/src/ifcsverchok/nodes/ifc/create_entity2.py index 112d21cd75..d581814b54 100644 --- a/src/ifcsverchok/nodes/ifc/create_entity2.py +++ b/src/ifcsverchok/nodes/ifc/create_entity2.py @@ -19,44 +19,55 @@ from sverchok.data_structure import updateNode class SvIfcCreateEntity2(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore): bl_idname = "SvIfcCreateEntity2" bl_label = "IFC Create Entity2" - Name: StringProperty(name="Name", update=updateNode) - Description: StringProperty(name="Description", update=updateNode) - ifc_class: StringProperty(name="ifc_class", update=updateNode) - representation: StringProperty(name="representation", update=updateNode) + Name: StringProperty(name="Name",default="", update=updateNode) + Description: StringProperty(name="Description", default="", update=updateNode) + ifc_class: StringProperty(name="IfcClass", update=updateNode) + representation: StringProperty(name="representation", default="", update=updateNode) + properties: StringProperty(name="properties", update=updateNode) def sv_init(self, context): self.inputs.new("SvStringsSocket", "Name").prop_name = "Name" - self.inputs.new("SvTextSocket", "Description").prop_name = "Description" + self.inputs.new("SvStringsSocket", "Description").prop_name = "Description" self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "ifc_class" self.inputs.new("SvStringsSocket", "Representation").prop_name = "representation" + self.inputs.new("SvStringsSocket", "Properties").prop_name = "properties" self.outputs.new("SvStringsSocket", "entity") self.outputs.new("SvStringsSocket", "file") #only for testing def process(self): - if not any(socket.is_linked for socket in self.outputs): + if not self.inputs['IfcClass'].is_linked: return + self.sv_input_names = [i.name for i in self.inputs] + name = self.inputs["Name"].sv_get()[0][0] description = self.inputs['Description'].sv_get()[0][0] ifc_class = self.inputs['IfcClass'].sv_get()[0][0] - representation = self.inputs['Representation'].sv_get()[0][0] - - if SvIfcStore.file is None: - self.file = SvIfcStore.create_boilerplate() - - else: - self.file = SvIfcStore.get_file() + representation = self.inputs['Representation'].sv_get()[0][0] + self.file = SvIfcStore.get_file() print("file: " , self.file) + print(SvIfcStore.file) if self.node_id not in SvIfcStore.id_map.values(): self.process_ifc(name, description, ifc_class, representation) else: entity_id = list(SvIfcStore.id_map.keys())[list(SvIfcStore.id_map.values()).index(self.node_id)] self.entity = self.file.by_id(entity_id) + print(name) self.entity.Name = name self.entity.Description = description - self.entity.Representation = representation + + if representation and not self.file.by_type("IFCPRODUCTDEFINITIONSHAPE"): + ifcopenshell.api.run("geometry.assign_representation", self.file, product=self.entity, representation=representation) + elif representation: + self.entity.Representation = representation + else: + pass + + if self.entity.is_a() != ifc_class: + # This crashes blender + ifcopenshell.util.schema.reassign_class(self.file, self.entity, ifc_class) SvIfcStore.file = self.file self.outputs["entity"].sv_set([[self.entity]]) @@ -64,23 +75,31 @@ class SvIfcCreateEntity2(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helpe def process_ifc(self, name, description, ifc_class, representation): - data = { - 'GlobalId': ifcopenshell.guid.new(), - 'Name': name, - 'Description': description, - 'Representation': representation + try: + self.entity = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name, description=description) + if representation: + ifcopenshell.api.run("geometry.assign_representation", self.file, product=self.entity, representation=representation) + except: + raise Exception("Something went wrong. Cannot create entity.") - } - self.entity = self.file.create_entity(str(ifc_class), **data) print("entity: ", self.entity) SvIfcStore.id_map[self.entity.id()] = self.node_id print("id_map: ", SvIfcStore.id_map) + def sv_free(self): + try: + SvIfcStore.id_map.pop(self.entity.id()) + except KeyError or AttributeError: + print("Either KeyError or AttributeError occurred.") + pass + + print("I'm free!") + + def register(): bpy.utils.register_class(SvIfcCreateEntity2) def unregister(): bpy.utils.unregister_class(SvIfcCreateEntity2) - SvIfcStore.purge()