Merge branch 'mdjska-v0.7.0' into v0.7.0
@@ -0,0 +1,120 @@
|
||||
# IfcSverchok
|
||||
**IfcSverchok** is a [Sverchok](http://nortikin.github.io/sverchok/) plugin that allows you to create native IFC models alongside your parametric Sverchok geometry.
|
||||
|
||||
N.B.! IfcSverchok nodes are WIP. You can experience Blender crashes while using them, especially if you're undoing (ctrl/cmd-Z) something in the node tree.
|
||||
|
||||
## Packaged installation[](https://blenderbim.org/docs-python/ifcsverchok/installation.html#packaged-installation "Permalink to this headline")
|
||||
|
||||
IfcSverchok is packaged like a regular Blender add-on, so installation is the same as any other Blender add-on. You can download the package for installation at the [Get BlenderBIM](https://blenderbim.org/download.html) website.
|
||||
|
||||
Like all Blender add-ons, they can be installed using `Edit > Preferences > Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox`. You can enable add-ons permanently by using `Save User Settings` from the Addons menu.
|
||||
|
||||
Before installing, you will also need to [install the BlenderBIM Add-on](https://blenderbim.org/download.html) and [install Sverchok](https://github.com/nortikin/sverchok/releases/tag/v1.1.0) (v1.1.0).
|
||||
|
||||
## Nodes
|
||||
List of nodes that have been tested to the best of ability (nodes not listed here are not considered ready for use). If you find bugs/unexpected behaviour in any of them, please open an issue or get in touch otherwise.
|
||||
|
||||
Most nodes have a help description, and input tooltips, that show show up when hovering over the "?"-icon and socket input respectively.
|
||||
|
||||
| Node name | Inputs | Outputs | Description |
|
||||
|--------------------------|-----------------------------------------------------------------------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| IFC Create Entity | - Names<br /> - Descriptions<br /> - IfcClass<br /> - Representations<br /> - Locations<br /> - Properties | - Entities Ids | Create IFC Entity. Takes one or multiple inputs.<br /> If 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used. |
|
||||
| IFC Read Entity | - Entity Id | - Id<br /> - is_a<br /> - Other entity attributes (dynamic) | Decompose an IfcEntity into its attributes. Takes one entity id as input, returns attribute values. |
|
||||
| IFC BMesh to IFC Repr | - Context type<br /> - Context identifier<br /> - Target View<br /> - Blender Objects | - Representations<br /> - Locations | Blender mesh to IfcShapeRepresentation. Takes one or multiple geometries.<br /> Deconstructs joined geometries and creates a separate representation for each. |
|
||||
| IFC Sverchok to IFC Repr | - Context type<br /> - Context identifier<br /> - Target View<br /> - Vertices<br /> - Edges<br /> - Faces | - Representations | Sverchok geometry to IfcShapeRepresentation. Takes one or multiple nested lists of inputs. |
|
||||
| IFC Create Blender Shape | - Entity Id(s) | - Blender Object(s) | Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity Ids.<br /> Creates shapes in scene and returns them in output socket. |
|
||||
| IFC Add Spatial Element | - Names<br /> - IfcClass<br /> - Elements | - Entity Id(s) | Add IfcElements to an IfcSpatialElement. Takes one or multiple nested lists of inputs.<br /> Input can also be a (list of) spacial elements to be aggregated. |
|
||||
| IFC Write File | - path | | Writes active Ifc model to path. <br />N.B.! It's recommended to use the 'Write File' panel under the "IfcSverchok" tab instead. |
|
||||
| IFC Class Picker | - IfcProduct (from drop-down)<br /> - IfcClass (from drop-down) | - IfcClass | Pick an IfcClass from drop-down list. |
|
||||
| IFC By Id | - Id | - Entities | Get IfcElement by step id. Takes one or multiple step ids. |
|
||||
| IFC By Guid | - Guid | - Entities | Get IfcElement by guid. Takes one or multiple guids. |
|
||||
| IFC By Type | - IfcProduct (from drop-down)<br /> - IfcClass (from drop-down)<br /> - Custom IfcClass | - Entities<br /> - Entity Ids | Get IFC element(s) in file by type. Pick an IfcProduct and an IfcClass or give a custom IfcClass. |
|
||||
| IFC Add Pset | - Name<br /> - Properties<br /> - Element Ids | - (Pset) Entities | Add a property set and corresponding properties, in a JSON key:value format, to IfcElements. |
|
||||
| IFC Generate Guid | | - Guid | Generate a unique GUID. |
|
||||
| IFC Get Property | - Entity Ids<br /> - Pset name<br /> - Prop name | - Value | Get the value of a property of an IfcEntity. Can take multiple entity ids. |
|
||||
| IFC Get Attribute | - Entity Ids<br /> - Attribute Name | - Value | Get the value of an attribute of an IfcEntity. Can take multiple entities. |
|
||||
|
||||
### IfcSverchok panel
|
||||
IfcSverchok creates a "IfcSverchok" tab in the Nodes panel. It includes a "Re-run all nodes" button that creates a fresh IFC File and a "Write File" button. It's recommended to re-run all nodes before saving the file either through the panel or the "Write File" node.
|
||||
|
||||
## Examples
|
||||
### Parametric Facade
|
||||
The sverchok model was kindly provided by Erindale Woodford [https://twitter.com/erindale_xyz](https://twitter.com/erindale_xyz). Check out the sverchok script and the resulting IFC model in the example_files folder.
|
||||
The example model consists of columns, floors and parametric facade slats, arranged into three buildings with three storeys each.
|
||||
|
||||
|
||||
#### Overview of the whole script
|
||||
This is the whole script. Part on the left creates the parametric Sverchok model. Part on the right converts the model to native IFC elements.
|
||||

|
||||
|
||||
#### Overview of the IFC part of the script
|
||||
The IFC of the script consists of four sections: columns, floors, slats and storeys/building.
|
||||

|
||||
|
||||
#### Create columns
|
||||
Columns begin as three blender objects, one for each storey. "Blender Mesh to IFC Repr" node is used to create an IfcRepresentation for each distinct Blender geometry. Joined geometries are split up, but the original nesting is preserved, which results in three nested lists 84 with 84 representation IDs each.
|
||||
|
||||
These are then, together with locations, fed to the "Create Entity" node, which creates the same number of entities (here: IfcColumns) and outs them in the same shape as input. (The "By ID" node here only shows the first item of the, in total, 252 columns).
|
||||

|
||||
|
||||
#### Create Floors
|
||||
Floors are created in the same manner as columns. They also begin as three Blender objects (one pr storey), with three distinct geometries in each (one per building). Therefore nine representations and nine entities are created with the proper nesting. (The "By ID" node here only shows the first item of the, in total, nine slabs).
|
||||

|
||||
|
||||
#### Create Slats
|
||||
Slats are created from Sverchok geometries instead of Blender objects. There is no other reason for this, then to showcase an alternative method. As it can be seen, 353 representations and entities are created with the "Sverchok to IFC Repr" and "Create Entity" nodes.
|
||||

|
||||
|
||||
#### Create Storeys and Building
|
||||
Columns and floors are joined pr. storey and fed into the "Add Spatial Element" node, with the IfcClass "IfcBuildingStorey". This creates three storeys with unique names.
|
||||
These storeys are then, together with slats, aggregated into an IfcBuilding using the same node.
|
||||

|
||||
|
||||
#### Saving the model
|
||||
To save IFC model we have built, you simply need to navigate to the IfcSverchok tab in the node menu and, first, click on "IFC Re-run all nodes" and afterwards click on "Write File".
|
||||
|
||||
Re-running all nodes will take a couple of seconds, depending on the size of the model. Re-running creates a fresh IFC model, and removes all the duplicated/unneeded IfcElements that were created while building the model.
|
||||
|
||||
In this example model, we have created both storeys and a building, but it's not stricly necessary with IfcSverchok. All orphaned elements and spatial elements (like IfcSpace or IfcBuildingStorey) will be put into a default building and site. So in this example, even though we didn't make an IfcSite, it will be created, and our building put into it, when writing the file.
|
||||

|
||||
|
||||
#### Opening The Created IFC Model
|
||||
Now we can open the created IFC model with any IFC viewer or program. Here it is opened with BlenderBIM.
|
||||

|
||||
|
||||
### Other examples
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<b>Miscellaneous examples</b>
|
||||
</summary>
|
||||
Miscellaneous examples that showcase the "Add Pset", "Get Attribute", "Get Property", "By Guid", "By Type" and "Read Entity" nodes.
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<b>Multiple Blender Mesh Geometries</b>
|
||||
</summary>
|
||||
This example shows the behaviour of the "Blender Mesh to IFC Repr" and "Create Entity" nodes when dealing with jagged list inputs.
|
||||
The initial inputs are two Blender objects, one containing a single geometry, and one containing two geometries (that were joined).
|
||||
"Blender Mesh to IFC Repr" node creates three IfcRepresentations and outputs them in the shape [[[repr_1], [repr_2]], [repr_3]].
|
||||
|
||||
The "Create Entity" node preserves the same shape. Notice that names and descriptions are (at least for now) only checked for uniqueness pr. object, so we have the name "testwall1" and description "descr1" repeating between the two objects.
|
||||
|
||||
The output of "Create Enity" would look identical if only one name and one description was given as input.
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<b>Multiple Sverchok Geometries</b>
|
||||
</summary>
|
||||
Similarly, when we in this example convert two sets of three boxes ([[[box1], [box2], [box3]],[[box4], [box5], [box6]]]), we get six IfcRepresentations and entities with the original shape preserved.
|
||||
Notice that the "By Id" node doesn't preserve input shape (at this point), since it should only be used for viewing.
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -29,14 +28,15 @@ bl_info = {
|
||||
"warning": "",
|
||||
}
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
import nodeitems_utils
|
||||
import sverchok
|
||||
from sverchok.core import sv_registration_utils, make_node_list
|
||||
from sverchok.core import make_node_list
|
||||
from sverchok.utils import auto_gather_node_classes, get_node_class_reference
|
||||
from sverchok.menu import SverchNodeItem, SverchNodeCategory, register_node_panels
|
||||
from sverchok.utils.extra_categories import register_extra_category_provider, unregister_extra_category_provider
|
||||
from sverchok.menu import SverchNodeItem, SverchNodeCategory
|
||||
from sverchok.utils.extra_categories import (
|
||||
register_extra_category_provider,
|
||||
unregister_extra_category_provider,
|
||||
)
|
||||
from sverchok.ui.nodeview_space_menu import make_extra_category_menus
|
||||
from sverchok.utils.logging import info, debug
|
||||
|
||||
@@ -52,17 +52,25 @@ def nodes_index():
|
||||
("ifc.create_entity", "SvIfcCreateEntity"),
|
||||
("ifc.create_shape", "SvIfcCreateShape"),
|
||||
("ifc.read_entity", "SvIfcReadEntity"),
|
||||
("ifc.pick_ifc_class", "SvIfcPickIfcClass"),
|
||||
("ifc.by_id", "SvIfcById"),
|
||||
("ifc.by_guid", "SvIfcByGuid"),
|
||||
("ifc.by_type", "SvIfcByType"),
|
||||
("ifc.by_query", "SvIfcByQuery"),
|
||||
("ifc.add", "SvIfcAdd"),
|
||||
("ifc.add_pset", "SvIfcAddPset"),
|
||||
("ifc.add_spatial_element", "SvIfcAddSpatialElement"),
|
||||
("ifc.remove", "SvIfcRemove"),
|
||||
("ifc.generate_guid", "SvIfcGenerateGuid"),
|
||||
("ifc.get_property", "SvIfcGetProperty"),
|
||||
("ifc.get_attribute", "SvIfcGetAttribute"),
|
||||
("ifc.select_blender_objects", "SvIfcSelectBlenderObjects"),
|
||||
("ifc.api", "SvIfcApi"),
|
||||
("ifc.api_WIP", "SvIfcApiWIP"),
|
||||
("ifc.bmesh_to_ifc", "SvIfcBMeshToIfcRepr"),
|
||||
("ifc.sverchok_to_ifc", "SvIfcSverchokToIfcRepr"),
|
||||
("ifc.create_project", "SvIfcCreateProject"),
|
||||
("ifc.quick_project_setup", "SvIfcQuickProjectSetup"),
|
||||
],
|
||||
)
|
||||
]
|
||||
@@ -84,6 +92,145 @@ imported_modules = make_node_list()
|
||||
reload_event = False
|
||||
|
||||
import bpy
|
||||
from os.path import splitext
|
||||
import ifcopenshell
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# FIXME For now it's fine to center around buildings.
|
||||
# But for the future it'd be good to also allow other
|
||||
# infra-related spatial structure elements, such as IfcBridge.
|
||||
# https://github.com/IfcOpenShell/IfcOpenShell/pull/2576#discussion_r1016261407
|
||||
def execute(self, context):
|
||||
self.file = SvIfcStore.purge()
|
||||
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 = set()
|
||||
if len(file.by_type("IfcBuilding")) == 0:
|
||||
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.update(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]
|
||||
)
|
||||
|
||||
for building in file.by_type("IfcBuilding"):
|
||||
elements = ifcopenshell.util.element.get_decomposition(building)
|
||||
elements_in_buildings.update(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 len(file.by_type("IfcSite")) == 0:
|
||||
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)
|
||||
# FIXME checking for extension not necessary
|
||||
if not ext:
|
||||
raise Exception("Bad path. Provide a path to a file ending with a file extension.")
|
||||
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():
|
||||
@@ -109,7 +256,10 @@ def make_menu():
|
||||
nodetype = item[1]
|
||||
rna = get_node_class_reference(nodetype)
|
||||
if not rna:
|
||||
info("Node `%s' is not available (probably due to missing dependencies).", nodetype)
|
||||
info(
|
||||
"Node `%s' is not available (probably due to missing dependencies).",
|
||||
nodetype,
|
||||
)
|
||||
else:
|
||||
node_item = SverchNodeItem.new(nodetype)
|
||||
node_items.append(node_item)
|
||||
@@ -135,7 +285,8 @@ def register():
|
||||
global our_menu_classes
|
||||
|
||||
debug("Registering ifcsverchok")
|
||||
|
||||
for klass in CLASSES:
|
||||
bpy.utils.register_class(klass)
|
||||
register_nodes()
|
||||
extra_nodes = importlib.import_module(".nodes", "ifcsverchok")
|
||||
auto_gather_node_classes(extra_nodes)
|
||||
@@ -158,3 +309,5 @@ def unregister():
|
||||
print(e)
|
||||
unregister_extra_category_provider("IFCSVERCHOK")
|
||||
unregister_nodes()
|
||||
for klass in CLASSES:
|
||||
bpy.utils.unregister_class(klass)
|
||||
|
||||
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 362 KiB |
|
After Width: | Height: | Size: 653 KiB |
|
After Width: | Height: | Size: 403 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 327 KiB |
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
from ifcopenshell import template
|
||||
|
||||
|
||||
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.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,
|
||||
)
|
||||
|
||||
SvIfcStore.file = file
|
||||
return SvIfcStore.file
|
||||
|
||||
@staticmethod
|
||||
def get_file():
|
||||
if SvIfcStore.file is None:
|
||||
SvIfcStore.create_boilerplate()
|
||||
return SvIfcStore.file
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -16,4 +15,3 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -16,4 +15,3 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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, flatten_data
|
||||
|
||||
|
||||
class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
bl_idname = "SvIfcAddPset"
|
||||
bl_label = "IFC Add Pset"
|
||||
Name: StringProperty(
|
||||
name="Name",
|
||||
description="Name of the property set. Eg. Pset_WallCommon.",
|
||||
update=updateNode,
|
||||
default="My_Pset",
|
||||
)
|
||||
Properties: StringProperty(
|
||||
name="Properties",
|
||||
description='Propertied in a JSON key:value format.Eg. {"IsExternal":"True"}',
|
||||
update=updateNode,
|
||||
default='{"Foo":"Bar"}',
|
||||
)
|
||||
Elements: StringProperty(name="Element Ids", 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")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Add a property set and corresponding properties to IfcElements."
|
||||
|
||||
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]
|
||||
element_ids = flatten_data(self.inputs["Elements"].sv_get(), target_level=1)
|
||||
|
||||
self.file = SvIfcStore.get_file()
|
||||
try:
|
||||
elements = [self.file.by_id(int(step_id)) for step_id in element_ids]
|
||||
except Exception as e:
|
||||
raise Exception("Instance ID not found", e)
|
||||
|
||||
if self.node_id not in SvIfcStore.id_map:
|
||||
element = self.create(name, properties, elements)
|
||||
else:
|
||||
element = self.edit(name, properties, elements)
|
||||
|
||||
self.outputs["Entity"].sv_set([element])
|
||||
|
||||
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.setdefault(self.node_id, []).append(result.id())
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
def edit(self, name, properties, elements):
|
||||
result_ids = SvIfcStore.id_map[self.node_id]
|
||||
results = []
|
||||
for result_id in result_ids:
|
||||
result = self.file.by_id(result_id)
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset",
|
||||
self.file,
|
||||
pset=result,
|
||||
name=name,
|
||||
properties=json.loads(properties),
|
||||
)
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(SvIfcAddPset)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(SvIfcAddPset)
|
||||
SvIfcStore.purge()
|
||||
@@ -0,0 +1,251 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
|
||||
from itertools import chain
|
||||
|
||||
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)",
|
||||
description="Name or list of names of the spacial element.",
|
||||
update=updateNode,
|
||||
)
|
||||
IfcClass: StringProperty(
|
||||
name="IFC Class",
|
||||
description='IfcClass of the spacial element. Eg. "IfcSpace".\nYou can use the "IFC Class Picker" node to find a relevant class.',
|
||||
update=updateNode,
|
||||
default="IfcSpace",
|
||||
)
|
||||
Elements: StringProperty(
|
||||
name="Elements",
|
||||
description="The IfcElements you want to add to the spacial element.\nThis can also be a (list of) spacial elements. Eg. IfcSpaces aggregated to an IfcBuildingStorey.",
|
||||
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 = "Add IfcElements to an IfcSpatialElement."
|
||||
|
||||
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)
|
||||
if isinstance(self.elements[0][0], list):
|
||||
self.elements = [list(chain.from_iterable(el)) for el in self.elements]
|
||||
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()
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -27,6 +26,7 @@ from sverchok.data_structure import updateNode
|
||||
|
||||
|
||||
def update_usecase(self, context):
|
||||
print("API - running update usecase!")
|
||||
module_usecase = self.get_module_usecase()
|
||||
if module_usecase:
|
||||
self.generate_node(*module_usecase)
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import StringProperty
|
||||
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):
|
||||
|
||||
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,310 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from copy import deepcopy
|
||||
from decimal import Context
|
||||
from email.policy import default
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
import ifcopenshell.api
|
||||
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:
|
||||
self.process()
|
||||
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.\nDeconstructs joined geometries and creates a separate representation for each."
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
|
||||
row.prop(self, "refresh_local", icon="FILE_REFRESH")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = [i.name for i in self.inputs]
|
||||
|
||||
if hash(self) not in self.node_dict:
|
||||
self.node_dict[
|
||||
hash(self)
|
||||
] = {} # happens if node is already on canvas when blender loads
|
||||
if not self.node_dict[hash(self)]:
|
||||
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
|
||||
|
||||
if not self.inputs["blender_objects"].sv_get(deepcopy=True)[0]:
|
||||
return
|
||||
edit = False
|
||||
for i in range(len(self.inputs)):
|
||||
input = self.inputs[i].sv_get(deepcopy=True)
|
||||
if (
|
||||
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
|
||||
and input != self.node_dict[hash(self)][self.inputs[i].name]
|
||||
):
|
||||
edit = True
|
||||
self.node_dict[hash(self)][self.inputs[i].name] = input.copy()
|
||||
|
||||
blender_objects = self.inputs["blender_objects"].sv_get(deepcopy=True)
|
||||
self.file = SvIfcStore.get_file()
|
||||
|
||||
if self.refresh_local:
|
||||
edit = True
|
||||
if self.node_id not in SvIfcStore.id_map:
|
||||
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"]
|
||||
|
||||
self.outputs["Representations"].sv_set(representations)
|
||||
self.outputs["Locations"].sv_set(locations)
|
||||
|
||||
def create(self, blender_objects):
|
||||
representations_ids = []
|
||||
locations = []
|
||||
context = self.get_context()
|
||||
for blender_object in blender_objects:
|
||||
bpy.context.view_layer.objects.active = blender_object
|
||||
try:
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
blender_object.select_set(True)
|
||||
try:
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
bpy.ops.mesh.separate(
|
||||
type="LOOSE"
|
||||
) # This isn't a great solution bc it creates new objects in the scene, thus changing the users model
|
||||
representations_ids_obj = []
|
||||
locations_obj = []
|
||||
try:
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
for obj in bpy.context.selected_objects:
|
||||
if blender_object.type == "MESH":
|
||||
representation = ifcopenshell.api.run(
|
||||
"geometry.add_representation",
|
||||
self.file,
|
||||
should_run_listeners=False,
|
||||
blender_object=obj,
|
||||
geometry=obj.data,
|
||||
context=context,
|
||||
)
|
||||
if not representation:
|
||||
raise Exception(
|
||||
"Couldn't create representation. Possibly wrong context."
|
||||
)
|
||||
|
||||
representations_ids_obj.append([representation.id()])
|
||||
locations_obj.append([obj.matrix_world])
|
||||
representations_ids.append(representations_ids_obj)
|
||||
locations.append(locations_obj)
|
||||
|
||||
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
|
||||
"Representations", []
|
||||
).append(representations_ids_obj)
|
||||
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
|
||||
"Locations", []
|
||||
).append(locations_obj)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
return representations_ids, locations
|
||||
|
||||
def edit(self):
|
||||
if "Representations" not in SvIfcStore.id_map[self.node_id]:
|
||||
return
|
||||
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
|
||||
for step_id in obj:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.remove_representation",
|
||||
self.file,
|
||||
representation=self.file.by_id(step_id[0]),
|
||||
)
|
||||
del SvIfcStore.id_map[self.node_id]["Representations"]
|
||||
del SvIfcStore.id_map[self.node_id]["Locations"]
|
||||
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 obj in SvIfcStore.id_map[self.node_id]["Representations"]:
|
||||
for step_id in obj:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.remove_representation",
|
||||
self.file,
|
||||
representation=self.file.by_id(step_id),
|
||||
)
|
||||
|
||||
if "Contexts" in SvIfcStore.id_map[self.node_id]:
|
||||
for context_id in SvIfcStore.id_map[self.node_id]["Contexts"]:
|
||||
context = self.file.by_id(context_id)
|
||||
if not self.file.get_inverse(context):
|
||||
if self.file.by_id(context_id).ParentContext:
|
||||
parent = self.file.by_id(context_id).ParentContext
|
||||
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
|
||||
)
|
||||
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)
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -17,31 +16,40 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import itertools
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcByGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
bl_idname = "SvIfcByGuid"
|
||||
bl_label = "IFC By Guid"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
guid: StringProperty(name="guid", update=updateNode)
|
||||
n_id: StringProperty(default="")
|
||||
guid: StringProperty(name="Guid(s)", update=updateNode)
|
||||
id_iter = itertools.count()
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "guid").prop_name = "guid"
|
||||
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 guid. Takes one or multiple guids."
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "guid"]
|
||||
super().process()
|
||||
|
||||
def process_ifc(self, file, guid):
|
||||
self.outputs["entity"].sv_set([[file.by_guid(guid)]])
|
||||
self.guids = flatten_data(self.inputs["guid"].sv_get(), target_level=1)
|
||||
if not self.guids[0]:
|
||||
return
|
||||
self.file = SvIfcStore.get_file()
|
||||
self.entities = [self.file.by_guid(guid) for guid in self.guids]
|
||||
self.outputs["Entities"].sv_set(self.entities)
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -17,31 +16,39 @@
|
||||
# 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 ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcById(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
bl_idname = "SvIfcById"
|
||||
bl_label = "IFC By Id"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
id: StringProperty(name="id", update=updateNode)
|
||||
id: StringProperty(
|
||||
name="Id(s)",
|
||||
update=updateNode,
|
||||
)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "id").prop_name = "id"
|
||||
self.outputs.new("SvStringsSocket", "entity")
|
||||
self.outputs.new("SvStringsSocket", "Entities")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Get IFC element by step id. Takes one or multiple step ids."
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "id"]
|
||||
super().process()
|
||||
|
||||
def process_ifc(self, file, id):
|
||||
self.outputs["entity"].sv_set([[file.by_id(int(id))]])
|
||||
self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1)
|
||||
if not self.ids[0]:
|
||||
return
|
||||
self.file = SvIfcStore.get_file()
|
||||
self.entities = [self.file.by_id(int(step_id)) for step_id in self.ids]
|
||||
self.outputs["Entities"].sv_set(self.entities)
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -21,6 +20,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
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
|
||||
@@ -29,21 +29,22 @@ from sverchok.data_structure import updateNode
|
||||
class SvIfcByQuery(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
bl_idname = "SvIfcByQuery"
|
||||
bl_label = "IFC By Query"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
query: StringProperty(name="query", update=updateNode)
|
||||
query: StringProperty(name="Query", update=updateNode)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "query").prop_name = "query"
|
||||
self.outputs.new("SvStringsSocket", "entity")
|
||||
self.outputs.new("SvStringsSocket", "Entity")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "query"]
|
||||
if not self.inputs["query"].sv_get()[0][0]:
|
||||
return
|
||||
self.file = SvIfcStore.get_file()
|
||||
self.sv_input_names = ["query"]
|
||||
super().process()
|
||||
|
||||
def process_ifc(self, file, query):
|
||||
def process_ifc(self, query):
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
self.outputs["entity"].sv_set([selector.parse(file, query)])
|
||||
self.outputs["Entity"].sv_set([selector.parse(self.file, query)])
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -21,13 +20,14 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import StringProperty, EnumProperty
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
|
||||
|
||||
def get_ifc_products(self, context):
|
||||
ifc_products = getattr(self, "ifc_products", [])
|
||||
file = self.inputs["file"].sv_get()[0][0]
|
||||
file = SvIfcStore.get_file()
|
||||
if not file:
|
||||
return []
|
||||
if ifc_products and file:
|
||||
@@ -48,7 +48,11 @@ def get_ifc_products(self, context):
|
||||
]
|
||||
)
|
||||
if file.schema == "IFC2X3":
|
||||
ifc_products[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
|
||||
ifc_products[2] = (
|
||||
"IfcSpatialStructureElement",
|
||||
"IfcSpatialStructureElement",
|
||||
"",
|
||||
)
|
||||
return ifc_products
|
||||
|
||||
|
||||
@@ -61,7 +65,7 @@ def get_ifc_classes(self, context):
|
||||
ifc_classes = getattr(self, "ifc_classes", [])
|
||||
if ifc_classes:
|
||||
return self.ifc_classes
|
||||
file = self.inputs["file"].sv_get()[0][0]
|
||||
file = SvIfcStore.get_file()
|
||||
if not file:
|
||||
return []
|
||||
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
|
||||
@@ -83,31 +87,59 @@ def get_ifc_classes(self, context):
|
||||
class SvIfcByType(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
bl_idname = "SvIfcByType"
|
||||
bl_label = "IFC By Type"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=update_ifc_products)
|
||||
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=updateNode)
|
||||
custom_ifc_class: StringProperty(name="Custom Ifc Class", update=updateNode)
|
||||
ifc_product: EnumProperty(
|
||||
items=get_ifc_products,
|
||||
name="IfcProduct",
|
||||
description="Pick an IfcProduct from drop-down.",
|
||||
update=update_ifc_products,
|
||||
)
|
||||
ifc_class: EnumProperty(
|
||||
items=get_ifc_classes,
|
||||
name="IfcClass",
|
||||
description="Pick an IfcClass from drop-down.",
|
||||
update=updateNode,
|
||||
)
|
||||
custom_ifc_class: StringProperty(
|
||||
name="Custom IfcClass",
|
||||
description="Give the name of your custom IfcClass.",
|
||||
update=updateNode,
|
||||
)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
|
||||
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
|
||||
self.inputs.new("SvStringsSocket", "custom_ifc_class").prop_name = "custom_ifc_class"
|
||||
self.outputs.new("SvStringsSocket", "entity")
|
||||
self.inputs.new(
|
||||
"SvStringsSocket", "custom_ifc_class"
|
||||
).prop_name = "custom_ifc_class"
|
||||
self.outputs.new("SvStringsSocket", "Entities")
|
||||
self.outputs.new("SvStringsSocket", "Entity Ids")
|
||||
self.width = 200
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Get IFC element(s) in file by type. \nPick an IfcProduct and an IfcClass or give a custom IfcClass."
|
||||
|
||||
def process(self):
|
||||
file = self.inputs["file"].sv_get()[0][0]
|
||||
if file:
|
||||
self.file = SvIfcStore.get_file()
|
||||
if self.file:
|
||||
self.ifc_products = get_ifc_products(self, bpy.context)
|
||||
self.ifc_classes = get_ifc_classes(self, bpy.context)
|
||||
self.sv_input_names = ["file", "ifc_product", "ifc_class", "custom_ifc_class"]
|
||||
self.sv_input_names = ["ifc_product", "ifc_class", "custom_ifc_class"]
|
||||
super().process()
|
||||
|
||||
def process_ifc(self, file, ifc_product, ifc_class, custom_ifc_class):
|
||||
def process_ifc(self, ifc_product, ifc_class, custom_ifc_class):
|
||||
if custom_ifc_class:
|
||||
self.outputs["entity"].sv_set([file.by_type(custom_ifc_class)])
|
||||
entities = self.file.by_type(custom_ifc_class)
|
||||
self.outputs["Entities"].sv_set(entities)
|
||||
self.outputs["Entity Ids"].sv_set([e.id() for e in entities])
|
||||
elif ifc_class:
|
||||
entities = self.file.by_type(ifc_class)
|
||||
self.outputs["Entities"].sv_set(self.file.by_type(ifc_class))
|
||||
self.outputs["Entity Ids"].sv_set([e.id() for e in entities])
|
||||
else:
|
||||
self.outputs["entity"].sv_set([file.by_type(ifc_class)])
|
||||
self.outputs["Entities"].sv_set([])
|
||||
self.outputs["Entity Ids"].sv_set([])
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -18,94 +17,285 @@
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from mathutils import Matrix
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import StringProperty
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
|
||||
from bpy.props import StringProperty, BoolProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import (
|
||||
updateNode,
|
||||
flatten_data,
|
||||
repeat_last_for_length,
|
||||
ensure_min_nesting,
|
||||
)
|
||||
|
||||
|
||||
class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
class SvIfcCreateEntity(
|
||||
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
|
||||
):
|
||||
bl_idname = "SvIfcCreateEntity"
|
||||
bl_label = "IFC Create Entity"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
ifc_class: StringProperty(name="ifc_class", update=updateNode)
|
||||
current_ifc_class: StringProperty(name="current_ifc_class")
|
||||
node_dict = {}
|
||||
|
||||
is_scene_dependent = True # if True and is_interactive then the node will be updated upon scene changes
|
||||
|
||||
def refresh_node(self, context):
|
||||
if self.refresh_local:
|
||||
self.process()
|
||||
self.refresh_local = False
|
||||
|
||||
refresh_local: BoolProperty(
|
||||
name="Update Node", description="Update Node", update=refresh_node
|
||||
)
|
||||
|
||||
Names: StringProperty(
|
||||
name="Names",
|
||||
default="",
|
||||
description="Entity name or list of names.",
|
||||
update=updateNode,
|
||||
)
|
||||
Descriptions: StringProperty(
|
||||
name="Descriptions",
|
||||
default="",
|
||||
description="Entity description or list of descriptions.",
|
||||
update=updateNode,
|
||||
)
|
||||
IfcClass: StringProperty(name="IfcClass", update=updateNode)
|
||||
Representations: StringProperty(
|
||||
name="Representations",
|
||||
description='IfcRepresentation(s). Use eg. "IFC BMesh to IFC" or "IFC Sverchok to IFC" nodes to create representations.',
|
||||
default="",
|
||||
update=updateNode,
|
||||
)
|
||||
# Locations: FloatVectorProperty(name="Locations", default="", subtype='MATRIX', update=updateNode)
|
||||
# Properties: StringProperty(name="properties", update=updateNode)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
|
||||
self.outputs.new("SvStringsSocket", "file")
|
||||
self.outputs.new("SvStringsSocket", "entity")
|
||||
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
|
||||
self.inputs.new("SvStringsSocket", "Descriptions").prop_name = "Descriptions"
|
||||
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
|
||||
self.inputs.new(
|
||||
"SvStringsSocket", "Representations"
|
||||
).prop_name = "Representations"
|
||||
self.inputs.new("SvMatrixSocket", "Locations").is_mandatory = False
|
||||
# self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties"
|
||||
self.outputs.new("SvStringsSocket", "Entities")
|
||||
self.node_dict[hash(self)] = {}
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Create IFC Entity. Takes one or multiple inputs. \nIf 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used."
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
|
||||
row.prop(self, "refresh_local", icon="FILE_REFRESH")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "ifc_class"]
|
||||
ifc_class = self.inputs["ifc_class"].sv_get()[0][0]
|
||||
if ifc_class:
|
||||
file = self.inputs["file"].sv_get()[0][0]
|
||||
if file:
|
||||
schema_name = file.wrapped_data.schema
|
||||
else:
|
||||
schema_name = "IFC4"
|
||||
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name)
|
||||
self.entity_schema = self.schema.declaration_by_name(ifc_class)
|
||||
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
|
||||
self.descriptions = flatten_data(
|
||||
self.inputs["Descriptions"].sv_get(), target_level=1
|
||||
)
|
||||
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[
|
||||
0
|
||||
]
|
||||
self.representations = ensure_min_nesting(
|
||||
self.inputs["Representations"].sv_get(), 3
|
||||
)
|
||||
self.representations = flatten_data(self.representations, target_level=3)
|
||||
self.locations = ensure_min_nesting(
|
||||
self.inputs["Locations"].sv_get(default=[]), 3
|
||||
)
|
||||
self.locations = flatten_data(self.locations, target_level=3)
|
||||
self.sv_input_names = [i.name for i in self.inputs]
|
||||
|
||||
if ifc_class != self.current_ifc_class:
|
||||
self.generate_inputs(ifc_class)
|
||||
try:
|
||||
for i in range(0, len(self.inputs)):
|
||||
self.inputs[i].sv_get()
|
||||
except:
|
||||
# This occurs when a blender save file is reloaded
|
||||
self.generate_inputs(ifc_class)
|
||||
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.')
|
||||
|
||||
for i in range(0, self.entity_schema.attribute_count()):
|
||||
self.sv_input_names.append(self.entity_schema.attribute_by_index(i).name())
|
||||
super().process()
|
||||
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()
|
||||
|
||||
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:
|
||||
value = attributes[i]
|
||||
if isinstance(value, str) and value == "":
|
||||
value = None
|
||||
except:
|
||||
value = None
|
||||
if value is None and entity.is_a("IfcRoot") and i == 0:
|
||||
value = ifcopenshell.guid.new()
|
||||
if value is not None:
|
||||
value = self.cast_value(self.entity_schema.attribute_by_index(i), value)
|
||||
try:
|
||||
entity[i] = value
|
||||
except:
|
||||
raise Exception(
|
||||
"The IFC class {} requires a valid value for {} - you provided {}".format(
|
||||
ifc_class, entity.attribute_name(i), value
|
||||
if self.refresh_local:
|
||||
edit = True
|
||||
self.file = SvIfcStore.get_file()
|
||||
if self.representations[0][0][0]:
|
||||
representations = []
|
||||
names = []
|
||||
descriptions = []
|
||||
for group in self.representations:
|
||||
try:
|
||||
group_representations = [
|
||||
[self.file.by_id(step_id) for step_id in representation]
|
||||
for representation in group
|
||||
]
|
||||
representations.append(group_representations)
|
||||
except Exception as e:
|
||||
raise
|
||||
names.append(
|
||||
self.repeat_input_unique(self.names, len(group_representations))
|
||||
)
|
||||
descriptions.append(
|
||||
self.repeat_input_unique(
|
||||
self.descriptions, len(group_representations)
|
||||
)
|
||||
)
|
||||
self.outputs["file"].sv_set([[file]])
|
||||
self.outputs["entity"].sv_set([[entity]])
|
||||
self.representations = representations
|
||||
self.names = names
|
||||
self.descriptions = descriptions
|
||||
elif not self.representations[0][0][0]:
|
||||
self.descriptions = self.repeat_input_unique(
|
||||
self.descriptions, len(self.names)
|
||||
)
|
||||
self.names = ensure_min_nesting(self.names, 2)
|
||||
self.descriptions = ensure_min_nesting(self.descriptions, 2)
|
||||
if self.node_id not in SvIfcStore.id_map:
|
||||
entities = self.create()
|
||||
else:
|
||||
if edit is True:
|
||||
entities = self.edit()
|
||||
else:
|
||||
entities = SvIfcStore.id_map[self.node_id]
|
||||
|
||||
def cast_value(self, attribute, value):
|
||||
data_type = self.get_attribute_data_type(attribute.type_of_attribute())
|
||||
if data_type == "integer":
|
||||
value = int(value)
|
||||
return value
|
||||
self.outputs["Entities"].sv_set(entities)
|
||||
|
||||
def get_attribute_data_type(self, data_type):
|
||||
if hasattr(data_type, "declared_type"):
|
||||
return self.get_attribute_data_type(data_type.declared_type())
|
||||
return data_type
|
||||
def create(self, index=None):
|
||||
entities_ids = []
|
||||
iterator1 = range(len(self.names))
|
||||
if index is not None:
|
||||
iterator1 = [index[0]]
|
||||
for i in iterator1:
|
||||
group = self.names[i]
|
||||
group_entities_ids = []
|
||||
iterator2 = range(len(group))
|
||||
if index is not None:
|
||||
iterator2 = [index[1]]
|
||||
for j in iterator2:
|
||||
try:
|
||||
entity = ifcopenshell.api.run(
|
||||
"root.create_entity",
|
||||
self.file,
|
||||
ifc_class=self.ifc_class,
|
||||
name=self.names[i][j],
|
||||
description=self.descriptions[i][j],
|
||||
)
|
||||
try:
|
||||
for repr in self.representations[i][j]:
|
||||
if repr:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation",
|
||||
self.file,
|
||||
product=entity,
|
||||
representation=repr,
|
||||
)
|
||||
except IndexError:
|
||||
pass
|
||||
try:
|
||||
for loc in self.locations[i][j]:
|
||||
if isinstance(loc, Matrix):
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement",
|
||||
self.file,
|
||||
product=entity,
|
||||
matrix=loc,
|
||||
)
|
||||
except IndexError:
|
||||
pass
|
||||
group_entities_ids.append(entity.id())
|
||||
except Exception as e:
|
||||
raise Exception("Something went wrong. Cannot create entity.", e)
|
||||
SvIfcStore.id_map.setdefault(self.node_id, []).append(group_entities_ids)
|
||||
entities_ids.append(group_entities_ids)
|
||||
return entities_ids
|
||||
|
||||
def edit(self):
|
||||
entities_ids = []
|
||||
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
|
||||
for i, group in enumerate(self.names):
|
||||
group_entities_ids = []
|
||||
for j, _ in enumerate(group):
|
||||
try:
|
||||
step_id = id_map_copy[i][j]
|
||||
except IndexError:
|
||||
id = self.create(index=[i, j])
|
||||
group_entities_ids.append(id[0][0])
|
||||
continue
|
||||
entity = self.file.by_id(step_id)
|
||||
entity.Name = self.names[i][j]
|
||||
if self.descriptions[i][j]:
|
||||
entity.Description = self.descriptions[i][j]
|
||||
try:
|
||||
for repr in self.representations[i][j]:
|
||||
if repr and repr.is_a("IfcProductDefinitionShape"):
|
||||
entity.Representation = repr
|
||||
elif repr:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation",
|
||||
self.file,
|
||||
product=entity,
|
||||
representation=repr,
|
||||
)
|
||||
except IndexError:
|
||||
pass
|
||||
try:
|
||||
for loc in self.locations[i][j]:
|
||||
if isinstance(loc, Matrix):
|
||||
ifcopenshell.api.run(
|
||||
"geometry.edit_object_placement",
|
||||
self.file,
|
||||
product=entity,
|
||||
matrix=loc,
|
||||
)
|
||||
except IndexError:
|
||||
pass
|
||||
if entity.is_a() != self.ifc_class:
|
||||
SvIfcStore.id_map[self.node_id][i].remove(step_id)
|
||||
entity = ifcopenshell.util.schema.reassign_class(
|
||||
self.file, entity, self.ifc_class
|
||||
)
|
||||
group_entities_ids.append(entity.id())
|
||||
entities_ids.append(group_entities_ids)
|
||||
|
||||
SvIfcStore.id_map[self.node_id] = entities_ids
|
||||
return entities_ids
|
||||
|
||||
def repeat_input_unique(self, input, count, flag=False):
|
||||
input = repeat_last_for_length(input, count, deepcopy=False)
|
||||
if input[0]:
|
||||
if flag:
|
||||
return [
|
||||
[a] if not (s := sum(j == a for j in input[:i])) else [f"{a}-{s+1}"]
|
||||
for i, a in enumerate(input)
|
||||
]
|
||||
input = [
|
||||
a if not (s := sum(j == a for j in input[:i])) else f"{a}-{s+1}"
|
||||
for i, a in enumerate(input)
|
||||
] # add number to duplicates
|
||||
return input
|
||||
|
||||
def sv_free(self):
|
||||
try:
|
||||
del SvIfcStore.id_map[self.node_id]
|
||||
del self.node_dict[hash(self)]
|
||||
# print('Node was deleted')
|
||||
except KeyError or AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
|
||||
|
||||
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)
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -21,65 +20,89 @@ import bpy
|
||||
import logging
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
import blenderbim.bim.import_ifc
|
||||
from bpy.props import StringProperty
|
||||
from bpy.props import StringProperty, PointerProperty, BoolProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
|
||||
|
||||
class SvIfcCreateShapeRefresh(bpy.types.Operator):
|
||||
bl_idname = "node.sv_ifc_create_shape_refresh"
|
||||
bl_label = "IFC Create Shape Refresh"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
tree_name: StringProperty(default="")
|
||||
node_name: StringProperty(default="")
|
||||
|
||||
def execute(self, context):
|
||||
node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
|
||||
node.process()
|
||||
return {"FINISHED"}
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcCreateShape(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
"""
|
||||
Triggers: Ifc create shape by entity
|
||||
Tooltip: Create Blender shape by Ifc Entity
|
||||
"""
|
||||
|
||||
is_scene_dependent = True
|
||||
is_interactive = False
|
||||
node_dict = {}
|
||||
|
||||
def refresh_node(self, context):
|
||||
if self.refresh_local:
|
||||
self.process()
|
||||
self.refresh_local = False
|
||||
|
||||
refresh_local: BoolProperty(name="Create shape(s)", description="Update Node", update=refresh_node)
|
||||
|
||||
bl_idname = "SvIfcCreateShape"
|
||||
bl_label = "IFC Create Shape"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
entity: StringProperty(name="entity", update=updateNode)
|
||||
bl_label = "IFC Create Blender Shape"
|
||||
entity: StringProperty(name="Entity Id(s)", update=updateNode)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
|
||||
self.inputs.new("SvStringsSocket", "Entities").prop_name = "entity"
|
||||
self.outputs.new("SvStringsSocket", "Object(s)")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
self.wrapper_tracked_ui_draw_op(layout, "node.sv_ifc_create_shape_refresh", icon="FILE_REFRESH", text="Refresh")
|
||||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity IDs."
|
||||
row.prop(self, "refresh_local", icon="FILE_REFRESH")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "entity"]
|
||||
super().process()
|
||||
self.entities = flatten_data(self.inputs["Entities"].sv_get(), target_level=1)
|
||||
if not self.entities[0]:
|
||||
return
|
||||
|
||||
def process_ifc(self, file, entity):
|
||||
try:
|
||||
if not entity.is_a("IfcProduct"):
|
||||
return
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
shape = ifcopenshell.geom.create_shape(settings, entity)
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
ifc_importer.file = file
|
||||
mesh = ifc_importer.create_mesh(entity, shape)
|
||||
obj = bpy.data.objects.new("IFC Element", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
except:
|
||||
print("Entity could not be converted into a shape", entity)
|
||||
if self.refresh_local or hash(self) not in self.node_dict:
|
||||
self.file = SvIfcStore.get_file()
|
||||
try:
|
||||
self.entities = [self.file.by_id(step_id) for step_id in self.entities]
|
||||
except Exception as e:
|
||||
raise
|
||||
blender_objects = self.create()
|
||||
self.node_dict[hash(self)] = blender_objects
|
||||
else:
|
||||
blender_objects = self.node_dict[hash(self)]
|
||||
|
||||
self.outputs["Object(s)"].sv_set(blender_objects)
|
||||
|
||||
def create(self):
|
||||
blender_objects = []
|
||||
for entity in self.entities:
|
||||
try:
|
||||
if not entity.is_a("IfcProduct"):
|
||||
return
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
self.ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, "", logger)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
shape = ifcopenshell.geom.create_shape(settings, entity)
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
ifc_importer.file = self.file
|
||||
mesh = ifc_importer.create_mesh(entity, shape)
|
||||
obj = bpy.data.objects.new("IFC Element", mesh)
|
||||
blender_objects.append(obj)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
except:
|
||||
raise Exception("Entity could not be converted into a shape. Entity: {}".format(entity))
|
||||
return blender_objects
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(SvIfcCreateShapeRefresh)
|
||||
# bpy.utils.register_class(SvIfcCreateShapeRefresh)
|
||||
bpy.utils.register_class(SvIfcCreateShape)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(SvIfcCreateShape)
|
||||
bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
|
||||
# bpy.utils.unregister_class(SvIfcCreateShapeRefresh)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -20,9 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
|
||||
|
||||
class SvIfcGenerateGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -21,25 +20,52 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcsverchok.helper
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcGetAttribute(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
class SvIfcGetAttribute(
|
||||
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
|
||||
):
|
||||
bl_idname = "SvIfcGetAttribute"
|
||||
bl_label = "IFC Get Attribute"
|
||||
entity: StringProperty(name="entity", update=updateNode)
|
||||
attribute_name: StringProperty(name="attribute_name", update=updateNode)
|
||||
entity: StringProperty(name="Entity Ids", update=updateNode)
|
||||
attribute_name: StringProperty(
|
||||
name="Attribute name",
|
||||
description='Name of attribute, eg. "Name".',
|
||||
update=updateNode,
|
||||
)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
|
||||
self.inputs.new("SvStringsSocket", "attribute_name").prop_name = "attribute_name"
|
||||
self.inputs.new(
|
||||
"SvStringsSocket", "attribute_name"
|
||||
).prop_name = "attribute_name"
|
||||
self.outputs.new("SvStringsSocket", "value")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = (
|
||||
"Get the value of an attribute of an IfcEntity. Can take multiple entities."
|
||||
)
|
||||
|
||||
def process(self):
|
||||
self.value_out = []
|
||||
entity_nested_inputs = self.inputs["entity"].sv_get()
|
||||
entity_nested_input_ids = flatten_data(
|
||||
self.inputs["entity"].sv_get(), target_level=1
|
||||
)
|
||||
if not entity_nested_input_ids[0]:
|
||||
return
|
||||
self.file = SvIfcStore.get_file()
|
||||
try:
|
||||
entity_nested_inputs = [
|
||||
self.file.by_id(int(step_id)) for step_id in entity_nested_input_ids
|
||||
]
|
||||
except Exception as e:
|
||||
raise Exception("Instance ID not found", e)
|
||||
attribute_name = self.inputs["attribute_name"].sv_get()[0][0]
|
||||
for entities in entity_nested_inputs:
|
||||
if hasattr(entities, "__iter__"):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -21,35 +20,64 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcsverchok.helper
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcGetProperty(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
class SvIfcGetProperty(
|
||||
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
|
||||
):
|
||||
bl_idname = "SvIfcGetProperty"
|
||||
bl_label = "IFC Get Property"
|
||||
entity: StringProperty(name="entity", update=updateNode)
|
||||
pset_name: StringProperty(name="pset_name", update=updateNode)
|
||||
prop_name: StringProperty(name="prop_name", update=updateNode)
|
||||
entity: StringProperty(name="Entity Ids", update=updateNode)
|
||||
pset_name: StringProperty(
|
||||
name="Pset Name",
|
||||
description='Name of the property set, eg. "Pset_WallCommon".',
|
||||
update=updateNode,
|
||||
)
|
||||
prop_name: StringProperty(
|
||||
name="Prop Name",
|
||||
description='Name of the property, eg. "IsExternal".',
|
||||
update=updateNode,
|
||||
)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
|
||||
self.inputs.new("SvStringsSocket", "entity_ids").prop_name = "entity"
|
||||
self.inputs.new("SvStringsSocket", "pset_name").prop_name = "pset_name"
|
||||
self.inputs.new("SvStringsSocket", "prop_name").prop_name = "prop_name"
|
||||
self.outputs.new("SvStringsSocket", "value")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["entity", "pset_name", "prop_name"]
|
||||
self.value_out = []
|
||||
super().process()
|
||||
self.outputs["value"].sv_set(self.value_out)
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = (
|
||||
"Get the value of a property of an IfcEntity. Can take multiple entity ids."
|
||||
)
|
||||
|
||||
def process_ifc(self, entity, pset_name, prop_name):
|
||||
def process(self):
|
||||
self.sv_input_names = ["entity_ids", "pset_name", "prop_name"]
|
||||
entities_ids = flatten_data(self.inputs["entity_ids"].sv_get(), target_level=1)
|
||||
pset_name = flatten_data(self.inputs["pset_name"].sv_get(), target_level=1)[0]
|
||||
prop_name = flatten_data(self.inputs["prop_name"].sv_get(), target_level=1)[0]
|
||||
if not entities_ids[0]:
|
||||
return
|
||||
self.file = SvIfcStore.get_file()
|
||||
try:
|
||||
self.value_out.append(ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name])
|
||||
except:
|
||||
pass
|
||||
self.entities = [self.file.by_id(int(step_id)) for step_id in entities_ids]
|
||||
except Exception as e:
|
||||
raise Exception(f"Invalid entity id: {e}")
|
||||
|
||||
self.value_out = []
|
||||
for entity in self.entities:
|
||||
try:
|
||||
self.value_out.append(
|
||||
ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name]
|
||||
)
|
||||
except:
|
||||
pass
|
||||
self.outputs["value"].sv_set(self.value_out)
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import 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,96 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from 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)
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -20,30 +19,48 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, ensure_min_nesting, flatten_data
|
||||
|
||||
|
||||
class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
class SvIfcReadEntity(
|
||||
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
|
||||
):
|
||||
bl_idname = "SvIfcReadEntity"
|
||||
bl_label = "IFC Read Entity"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
entity: StringProperty(name="entity", update=updateNode)
|
||||
entity: StringProperty(name="Entity Id", update=updateNode)
|
||||
current_ifc_class: StringProperty(name="current_ifc_class")
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
|
||||
self.outputs.new("SvStringsSocket", "id")
|
||||
self.outputs.new("SvStringsSocket", "is_a")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = (
|
||||
"Decompose an IfcEntity into its attributes. Takes one entity id as input"
|
||||
)
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "entity"]
|
||||
ifc_class = self.inputs["entity"].sv_get()[0][0]
|
||||
if ifc_class:
|
||||
ifc_class = ifc_class.is_a()
|
||||
file = self.inputs["file"].sv_get()[0][0]
|
||||
self.sv_input_names = ["entity"]
|
||||
entity_id = flatten_data(self.inputs["entity"].sv_get(), target_level=1)
|
||||
if not entity_id[0]:
|
||||
return
|
||||
if len(entity_id) > 1:
|
||||
raise Exception("Only one entity can be read at a time")
|
||||
self.file = SvIfcStore.get_file()
|
||||
try:
|
||||
entity = self.file.by_id(entity_id[0])
|
||||
except Exception as e:
|
||||
raise Exception("Instance with id {} not found".format(entity_id), e)
|
||||
|
||||
if entity:
|
||||
ifc_class = entity.is_a()
|
||||
file = SvIfcStore.get_file()
|
||||
if file:
|
||||
schema_name = file.wrapped_data.schema
|
||||
else:
|
||||
@@ -53,7 +70,12 @@ class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.S
|
||||
|
||||
if ifc_class != self.current_ifc_class:
|
||||
self.generate_outputs(ifc_class)
|
||||
super().process()
|
||||
|
||||
self.outputs["id"].sv_set([entity.id()])
|
||||
self.outputs["is_a"].sv_set([entity.is_a()])
|
||||
for i in range(0, self.entity_schema.attribute_count()):
|
||||
name = self.entity_schema.attribute_by_index(i).name()
|
||||
self.outputs[name].sv_set([entity[i]])
|
||||
|
||||
def generate_outputs(self, ifc_class):
|
||||
while len(self.outputs) > 2:
|
||||
@@ -63,13 +85,6 @@ class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.S
|
||||
self.outputs.new("SvStringsSocket", name).prop_name = name
|
||||
self.current_ifc_class = ifc_class
|
||||
|
||||
def process_ifc(self, file, entity):
|
||||
self.outputs["id"].sv_set([[entity.id()]])
|
||||
self.outputs["is_a"].sv_set([[entity.is_a()]])
|
||||
for i in range(0, self.entity_schema.attribute_count()):
|
||||
name = self.entity_schema.attribute_by_index(i).name()
|
||||
self.outputs[name].sv_set([[entity[i]]])
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(SvIfcReadEntity)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
# IfcSverchok is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcSverchok is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from copy import deepcopy
|
||||
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, ensure_min_nesting
|
||||
|
||||
|
||||
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 = ensure_min_nesting(
|
||||
self.inputs["Vertices"].sv_get(deepcopy=False), 4
|
||||
)
|
||||
self.edges = ensure_min_nesting(self.inputs["Edges"].sv_get(deepcopy=False), 4)
|
||||
self.faces = ensure_min_nesting(self.inputs["Faces"].sv_get(deepcopy=False), 4)
|
||||
data = list(zip(self.vertices, self.edges, self.faces))
|
||||
objects = []
|
||||
for object in data:
|
||||
objects.append(list(zip(object[0], object[1], object[2])))
|
||||
|
||||
if self.node_id not in SvIfcStore.id_map:
|
||||
representations = self.create(objects)
|
||||
else:
|
||||
if edit is True:
|
||||
self.edit()
|
||||
representations = self.create(objects)
|
||||
else:
|
||||
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 obj in geo_data:
|
||||
representations_ids_obj = []
|
||||
for item in obj:
|
||||
representation = ifcopenshell.api.run(
|
||||
"geometry.add_sverchok_representation",
|
||||
self.file,
|
||||
should_run_listeners=False,
|
||||
context=self.context,
|
||||
vertices=[list(map(tuple, item[0]))],
|
||||
edges=[list(map(tuple, item[1]))],
|
||||
faces=[list(map(tuple, item[2]))],
|
||||
)
|
||||
if not representation:
|
||||
raise Exception(
|
||||
"Couldn't create representation. Possibly wrong context."
|
||||
)
|
||||
representations_ids_obj.append([representation.id()])
|
||||
representations_ids.append(representations_ids_obj)
|
||||
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault(
|
||||
"Representations", []
|
||||
).append(representations_ids_obj)
|
||||
return representations_ids
|
||||
|
||||
def edit(self):
|
||||
if "Representations" not in SvIfcStore.id_map[self.node_id]:
|
||||
return
|
||||
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
|
||||
for step_id in obj:
|
||||
ifcopenshell.api.run(
|
||||
"geometry.remove_representation",
|
||||
self.file,
|
||||
representation=self.file.by_id(step_id[0]),
|
||||
)
|
||||
del SvIfcStore.id_map[self.node_id]["Representations"]
|
||||
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[0]),
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
# IfcSverchok - IFC Sverchok extension
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2022 Martina Jakubowska <martina@jakubowska.dk>
|
||||
#
|
||||
# This file is part of IfcSverchok.
|
||||
#
|
||||
@@ -17,30 +16,137 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with IfcSverchok. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from os.path import abspath, splitext
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcsverchok.helper
|
||||
from bpy.props import StringProperty
|
||||
from ifcsverchok.ifcstore import SvIfcStore
|
||||
from bpy.props import StringProperty, BoolProperty
|
||||
from sverchok.node_tree import SverchCustomTreeNode
|
||||
from sverchok.data_structure import updateNode
|
||||
from sverchok.data_structure import updateNode, flatten_data
|
||||
|
||||
|
||||
class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
|
||||
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
|
||||
|
||||
refresh_local: BoolProperty(
|
||||
name="Write", description="Write to file", update=refresh_node_local
|
||||
)
|
||||
|
||||
bl_idname = "SvIfcWriteFile"
|
||||
bl_label = "IFC Write File"
|
||||
file: StringProperty(name="file", update=updateNode)
|
||||
path: StringProperty(name="path", update=updateNode)
|
||||
path: StringProperty(
|
||||
name="path",
|
||||
description="File path to write to. Can be relative.",
|
||||
update=updateNode,
|
||||
)
|
||||
|
||||
def sv_init(self, context):
|
||||
self.inputs.new("SvStringsSocket", "file").prop_name = "file"
|
||||
self.inputs.new("SvStringsSocket", "path").prop_name = "path"
|
||||
self.outputs.new("SvStringsSocket", "output")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
|
||||
).tooltip = "Writes active Ifc file to path.\n It will overwrite an existing file.\n N.B.! It's recommended to create a fresh IFC File using the 're-run all nodes' button in IfcSverchok panel before saving."
|
||||
row.prop(self, "refresh_local", icon="FILE_REFRESH")
|
||||
|
||||
def process(self):
|
||||
self.sv_input_names = ["file", "path"]
|
||||
super().process()
|
||||
path = flatten_data(self.inputs["path"].sv_get(), target_level=1)[0]
|
||||
if not path:
|
||||
return
|
||||
path = abspath(path)
|
||||
file = SvIfcStore.get_file()
|
||||
_, ext = splitext(path)
|
||||
if not ext:
|
||||
raise Exception("Bad path. Provide a path to a file.")
|
||||
if self.refresh_local and ext:
|
||||
self.ensure_hirarchy(file)
|
||||
file.write(path)
|
||||
self.outputs["output"].sv_set(f"File written successfully to: {path}.")
|
||||
|
||||
def process_ifc(self, file, path):
|
||||
file.write(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 register():
|
||||
|
||||