This commit is contained in:
Andrej730
2025-03-21 12:53:13 +05:00
parent a567af370b
commit e075c32a5a
19 changed files with 90 additions and 77 deletions
@@ -163,7 +163,7 @@ def remove_all_listeners():
post_listeners.clear()
def extract_docs(module, usecase):
def extract_docs(module: str, usecase: str) -> dict[str, Any]:
import typing
import collections
+3
View File
@@ -161,9 +161,12 @@ class IFC_Sv_UpdateCurrent(bpy.types.Operator):
# infra-related spatial structure elements, such as IfcBridge.
# https://github.com/IfcOpenShell/IfcOpenShell/pull/2576#discussion_r1016261407
def execute(self, context):
import sverchok.node_tree
self.file = SvIfcStore.purge()
node_tree = context.space_data.node_tree
if node_tree:
assert isinstance(node_tree, sverchok.node_tree.SverchCustomTree)
if self.force_mode or node_tree.sv_process:
try:
bpy.context.window.cursor_set("WAIT")
+3 -2
View File
@@ -21,7 +21,7 @@ import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.representation
from ifcopenshell import template
from typing import Union
from typing import Union, Any
class SvIfcStore:
@@ -30,7 +30,8 @@ class SvIfcStore:
schema = None
cache = None
cache_path = None
id_map = {}
id_map: dict[str, Any] = {}
"""Mapping `{node_id: Any}`"""
guid_map = {}
deleted_ids = set()
edited_objs = set()
+3 -3
View File
@@ -38,13 +38,13 @@ class SvIfcAdd(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCor
def process(self):
self.sv_input_names = ["file", "entity"]
self.file_out = []
self.entity_out = []
self.file_out: list[ifcopenshell.file] = []
self.entity_out: list[ifcopenshell.entity_instance] = []
super().process()
self.outputs["file"].sv_set([self.file_out])
self.outputs["entity"].sv_set([self.entity_out])
def process_ifc(self, file, entity):
def process_ifc(self, file: ifcopenshell.file, entity: ifcopenshell.entity_instance) -> None:
self.entity_out.append(file.add(entity))
self.file_out.append(file)
+12 -8
View File
@@ -25,6 +25,8 @@ from ifcsverchok.ifcstore import SvIfcStore
import bpy
import json
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.pset
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
@@ -80,12 +82,13 @@ class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIf
self.outputs["Entity"].sv_set([element])
def create(self, name, properties, elements):
def create(
self, name: str, properties: str, elements: list[ifcopenshell.entity_instance]
) -> list[ifcopenshell.entity_instance]:
results = []
for element in elements:
result = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name=name)
ifcopenshell.api.run(
"pset.edit_pset",
result = ifcopenshell.api.pset.add_pset(self.file, product=element, name=name)
ifcopenshell.api.pset.edit_pset(
self.file,
pset=result,
properties=json.loads(properties),
@@ -94,13 +97,14 @@ class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIf
results.append(result)
return results
def edit(self, name, properties, elements):
def edit(
self, name: str, properties: str, elements: list[ifcopenshell.entity_instance]
) -> list[ifcopenshell.entity_instance]:
result_ids = SvIfcStore.id_map[self.node_id]
results = []
results: list[ifcopenshell.entity_instance] = []
for result_id in result_ids:
result = self.file.by_id(result_id)
ifcopenshell.api.run(
"pset.edit_pset",
ifcopenshell.api.pset.edit_pset(
self.file,
pset=result,
name=name,
@@ -221,7 +221,6 @@ class SvIfcAddSpatialElement(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
+5 -4
View File
@@ -24,11 +24,12 @@ from bpy.props import StringProperty, EnumProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
import logging
from typing import Union, Any
logger = logging.getLogger("sverchok.ifc")
def update_usecase(self, context):
def update_usecase(self: "SvIfcApi", context: bpy.types.Context) -> None:
module_usecase = self.get_module_usecase()
if module_usecase:
self.generate_node(*module_usecase)
@@ -68,12 +69,12 @@ class SvIfcApi(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCor
self.sv_input_names = [i.name for i in self.inputs]
super().process()
def get_module_usecase(self):
def get_module_usecase(self) -> Union[list[str], None]:
usecase = self.inputs["usecase"].sv_get()[0][0]
if usecase:
return usecase.split(".")
def generate_node(self, module, usecase):
def generate_node(self, module: str, usecase: str) -> None:
try:
node_data = ifcopenshell.api.extract_docs(module, usecase)
except:
@@ -92,7 +93,7 @@ class SvIfcApi(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCor
self.tooltip = f"{name}: {data['description']}\n"
self.tooltip = self.tooltip.strip()
def process_ifc(self, usecase, *setting_values):
def process_ifc(self, usecase: Union[str, None], *setting_values: Any) -> None:
if usecase:
settings = dict(zip(self.sv_input_names[1:], setting_values))
settings = {k: v for k, v in settings.items() if v != ""}
+14 -12
View File
@@ -23,6 +23,8 @@ import bpy
import ifcopenshell
import ifcsverchok.helper
import ifcopenshell.api
import ifcopenshell.api.geometry
import ifcopenshell.util.representation
from ifcsverchok.ifcstore import SvIfcStore
import bonsai.tool as tool
import bonsai.core.geometry as core
@@ -40,6 +42,7 @@ from sverchok.data_structure import zip_long_repeat, node_id
from sverchok.core.socket_data import sv_get_socket
from itertools import chain, cycle
from mathutils import Matrix
class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
@@ -162,9 +165,9 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
self.outputs["Representations"].sv_set(representations)
self.outputs["Locations"].sv_set(locations)
def create(self, blender_objects):
representations_ids = []
locations = []
def create(self, blender_objects: list[bpy.types.Object]) -> tuple[list[list[list[int]]], list[list[list[Matrix]]]]:
representations_ids: list[list[list[int]]] = []
locations: list[list[list[Matrix]]] = []
context = self.get_context()
for blender_object in blender_objects:
bpy.context.view_layer.objects.active = blender_object
@@ -183,8 +186,8 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
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 = []
representations_ids_obj: list[list[int]] = []
locations_obj: list[list[Matrix]] = []
try:
bpy.ops.object.mode_set(mode="OBJECT")
except Exception as e:
@@ -192,16 +195,16 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
pass
for obj in bpy.context.selected_objects:
if blender_object.type == "MESH":
representation = ifcopenshell.api.run(
"geometry.add_representation",
representation = ifcopenshell.api.geometry.add_representation(
self.file,
should_run_listeners=False,
blender_object=obj,
geometry=obj.data,
context=context,
should_run_listeners=False,
)
if not representation:
raise Exception("Couldn't create representation. Possibly wrong context.")
assert isinstance(representation, ifcopenshell.entity_instance)
representations_ids_obj.append([representation.id()])
locations_obj.append([obj.matrix_world])
@@ -215,7 +218,7 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
bpy.ops.object.select_all(action="DESELECT")
return representations_ids, locations
def edit(self):
def edit(self) -> None:
if "Representations" not in SvIfcStore.id_map[self.node_id]:
return
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
@@ -229,7 +232,7 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
del SvIfcStore.id_map[self.node_id]["Locations"]
return
def get_context(self):
def get_context(self) -> ifcopenshell.entity_instance:
context = ifcopenshell.util.representation.get_context(
self.file, self.context_type, self.context_identifier, self.target_view
)
@@ -248,7 +251,7 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
return context
def sv_free(self):
def sv_free(self) -> None:
try:
self.file = SvIfcStore.get_file()
if "Representations" in SvIfcStore.id_map[self.node_id]:
@@ -273,7 +276,6 @@ class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.help
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
+1
View File
@@ -33,6 +33,7 @@ class SvIfcByGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfc
n_id: StringProperty(default="")
guid: StringProperty(name="Guid(s)", update=updateNode)
id_iter = itertools.count()
guids: list[str]
def sv_init(self, context):
self.inputs.new("SvStringsSocket", "guid").prop_name = "guid"
+1 -1
View File
@@ -42,7 +42,7 @@ class SvIfcByQuery(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIf
self.sv_input_names = ["query"]
super().process()
def process_ifc(self, query):
def process_ifc(self, query: str) -> None:
selector = ifcopenshell.util.selector.Selector()
self.outputs["Entity"].sv_set([selector.parse(self.file, query)])
+14 -17
View File
@@ -20,6 +20,9 @@ import bpy
from mathutils import Matrix
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.schema
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
@@ -149,21 +152,20 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
self.outputs["Entities"].sv_set(entities)
def create(self, index=None):
entities_ids = []
def create(self, index=None) -> list[list[int]]:
entities_ids: list[list[int]] = []
iterator1 = range(len(self.names))
if index is not None:
iterator1 = [index[0]]
for i in iterator1:
group = self.names[i]
group_entities_ids = []
group_entities_ids: list[int] = []
iterator2 = range(len(group))
if index is not None:
iterator2 = [index[1]]
for j in iterator2:
try:
entity = ifcopenshell.api.run(
"root.create_entity",
entity = ifcopenshell.api.root.create_entity(
self.file,
ifc_class=self.ifc_class,
name=self.names[i][j],
@@ -172,8 +174,7 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
try:
for repr in self.representations[i][j]:
if repr:
ifcopenshell.api.run(
"geometry.assign_representation",
ifcopenshell.api.geometry.assign_representation(
self.file,
product=entity,
representation=repr,
@@ -183,8 +184,7 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
try:
for loc in self.locations[i][j]:
if isinstance(loc, Matrix):
ifcopenshell.api.run(
"geometry.edit_object_placement",
ifcopenshell.api.geometry.edit_object_placement(
self.file,
product=entity,
matrix=loc,
@@ -198,11 +198,11 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
entities_ids.append(group_entities_ids)
return entities_ids
def edit(self):
entities_ids = []
def edit(self) -> list[list[int]]:
entities_ids: list[list[int]] = []
id_map_copy = SvIfcStore.id_map[self.node_id].copy()
for i, group in enumerate(self.names):
group_entities_ids = []
group_entities_ids: list[int] = []
for j, _ in enumerate(group):
try:
step_id = id_map_copy[i][j]
@@ -219,8 +219,7 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
if repr and repr.is_a("IfcProductDefinitionShape"):
entity.Representation = repr
elif repr:
ifcopenshell.api.run(
"geometry.assign_representation",
ifcopenshell.api.geometry.assign_representation(
self.file,
product=entity,
representation=repr,
@@ -230,8 +229,7 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
try:
for loc in self.locations[i][j]:
if isinstance(loc, Matrix):
ifcopenshell.api.run(
"geometry.edit_object_placement",
ifcopenshell.api.geometry.edit_object_placement(
self.file,
product=entity,
matrix=loc,
@@ -263,7 +261,6 @@ class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper
try:
del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)]
# print('Node was deleted')
except KeyError or AttributeError:
pass
@@ -38,7 +38,6 @@ class SvIfcCreateProject(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helpe
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
@@ -19,6 +19,7 @@
import bpy
import logging
import ifcopenshell
import ifcopenshell.geom
import ifcsverchok.helper
from ifcsverchok.ifcstore import SvIfcStore
import bonsai.bim.import_ifc
@@ -63,7 +63,6 @@ class SvIfcQuickProjectSetup(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
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]
+1 -1
View File
@@ -38,7 +38,7 @@ class SvIfcReadFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvI
self.sv_input_names = ["path"]
super().process()
def process_ifc(self, path):
def process_ifc(self, path: str) -> None:
guid = ifcopenshell.guid.new()
ifcsverchok.helper.ifc_files[guid] = ifcopenshell.open(path)
self.outputs["file"].sv_set([[ifcsverchok.helper.ifc_files[guid]]])
+10 -1
View File
@@ -22,6 +22,7 @@ import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from typing import Union
class SvIfcRemove(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
@@ -36,12 +37,20 @@ class SvIfcRemove(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfc
self.outputs.new("SvStringsSocket", "file")
def process(self):
file: ifcopenshell.file
file = self.inputs["file"].sv_get()[0][0]
self.new_file = ifcopenshell.file.from_string(file.wrapped_data.to_string())
self.remove_entity(self.inputs["entity"].sv_get())
self.outputs["file"].sv_set([[self.new_file]])
def remove_entity(self, entity):
def remove_entity(
self,
entity: Union[
list[list[ifcopenshell.entity_instance]],
list[ifcopenshell.entity_instance],
ifcopenshell.entity_instance,
],
) -> None:
if isinstance(entity, (tuple, list)):
for e in entity:
self.remove_entity(e)
@@ -19,11 +19,11 @@
import bpy
import ifcopenshell
import ifcopenshell.util.selector
import bonsai.tool as tool
import ifcsverchok.helper
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from bonsai.bim.ifc import IfcStore
class SvIfcSelectBlenderObjectsRefresh(bpy.types.Operator):
@@ -35,6 +35,7 @@ class SvIfcSelectBlenderObjectsRefresh(bpy.types.Operator):
node_name: StringProperty(default="")
def execute(self, context):
node: SvIfcSelectBlenderObjects
node = bpy.data.node_groups[self.tree_name].nodes[self.node_name]
node.process()
return {"FINISHED"}
@@ -43,7 +44,9 @@ class SvIfcSelectBlenderObjectsRefresh(bpy.types.Operator):
class SvIfcSelectBlenderObjects(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bl_idname = "SvIfcSelectBlenderObjects"
bl_label = "IFC Select Blender Objects"
bl_description = "Select Blender objects based on IFC entities."
file: StringProperty(name="file", update=updateNode)
# TODO: never used.
query: StringProperty(name="query", update=updateNode)
def sv_init(self, context):
@@ -54,18 +57,18 @@ class SvIfcSelectBlenderObjects(bpy.types.Node, SverchCustomTreeNode, ifcsvercho
layout, "node.sv_ifc_select_blender_objects_refresh", icon="FILE_REFRESH", text="Refresh"
)
def process(self):
self.file = IfcStore.get_file()
def process(self) -> None:
self.sv_input_names = ["entities"]
self.guids = []
self.guids: list[str] = []
super().process()
for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
element = tool.Ifc.get_entity(obj)
if not element:
continue
if self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId in self.guids:
if getattr(element, "GlobalId", None) in self.guids:
obj.select_set(True)
def process_ifc(self, entities):
def process_ifc(self, entities: ifcopenshell.entity_instance) -> None:
self.guids.append(entities.GlobalId)
+9 -16
View File
@@ -16,15 +16,14 @@
# 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
import ifcopenshell.api.context
import ifcopenshell.api.geometry
import ifcopenshell.util.representation
from ifcsverchok.ifcstore import SvIfcStore
import bonsai.tool as tool
import bonsai.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
@@ -142,8 +141,7 @@ class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
for obj in geo_data:
representations_ids_obj = []
for item in obj:
representation = ifcopenshell.api.run(
"geometry.add_mesh_representation",
representation = ifcopenshell.api.geometry.add_mesh_representation(
self.file,
should_run_listeners=False,
context=self.context,
@@ -165,8 +163,7 @@ class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
return
for obj in SvIfcStore.id_map[self.node_id]["Representations"]:
for step_id in obj:
ifcopenshell.api.run(
"geometry.remove_representation",
ifcopenshell.api.geometry.remove_representation(
self.file,
representation=self.file.by_id(step_id[0]),
)
@@ -180,9 +177,8 @@ class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
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",
parent = ifcopenshell.api.context.add_context(self.file, context_type=self.context_type)
context = ifcopenshell.api.context.add_context(
self.file,
context_type=self.context_type,
context_identifier=self.context_identifier,
@@ -197,8 +193,7 @@ class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
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",
ifcopenshell.api.geometry.remove_representation(
self.file,
representation=self.file.by_id(step_id[0]),
)
@@ -209,15 +204,13 @@ class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
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)
ifcopenshell.api.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)
ifcopenshell.api.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
+2 -1
View File
@@ -77,7 +77,8 @@ class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.Sv
file.write(path)
self.outputs["output"].sv_set(f"File written successfully to: {path}.")
def ensure_hirarchy(self, file):
def ensure_hirarchy(self, file: ifcopenshell.file) -> None:
# TODO: same code as ifc.write_file_panel?
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")