Merge branch 'v0.7.0' into linked_aggregate_operator_from_panel

This commit is contained in:
Bruno Perdigão
2024-05-07 21:00:03 -03:00
committed by GitHub
415 changed files with 14653 additions and 14331 deletions
+9 -5
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import os
import bpy
import json
@@ -36,6 +37,7 @@ import blenderbim.core.style
from blenderbim.bim.ifc import IfcStore
from mathutils import Vector
from typing import Union
from logging import Logger
class IfcExporter:
@@ -163,10 +165,10 @@ class IfcExporter:
bpy.ops.bim.update_representation(obj=obj.name)
tool.Geometry.record_object_position(obj)
def get_application_name(self):
def get_application_name(self) -> str:
return "BlenderBIM"
def get_application_version(self):
def get_application_version(self) -> str:
version = ".".join(
[
str(x)
@@ -184,11 +186,13 @@ class IfcExporter:
class IfcExportSettings:
def __init__(self):
self.logger = None
self.output_file = None
self.logger: Logger = None
self.output_file: str = None
self.json_version: str = None
self.json_compact: bool = None
@staticmethod
def factory(context, output_file, logger):
def factory(context: bpy.types.Context, output_file: str, logger: Logger) -> IfcExportSettings:
settings = IfcExportSettings()
settings.output_file = output_file
settings.logger = logger
+10 -2
View File
@@ -299,6 +299,7 @@ class IfcImporter:
if self.ifc_import_settings.should_setup_viewport_camera:
self.setup_viewport_camera()
self.setup_arrays()
self.profile_code("Setup arrays")
self.update_progress(100)
bpy.context.window_manager.progress_end()
@@ -602,6 +603,9 @@ class IfcImporter:
return products
def predict_dense_mesh(self):
if self.ifc_import_settings.should_use_native_meshes:
return
threshold = 10000 # Just from experience.
faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell")]
@@ -1494,7 +1498,11 @@ class IfcImporter:
# Occurs when reloading a project
pass
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
project_collection.children[self.type_collection.name].hide_viewport = True
types_collection = project_collection.children[self.type_collection.name]
types_collection.hide_viewport = False
for obj in types_collection.collection.objects: #turn off all objects inside Types collection.
obj.hide_set(True)
def clean_mesh(self):
obj = None
@@ -2041,7 +2049,7 @@ class IfcImporter:
class IfcImportSettings:
def __init__(self):
self.logger = None
self.logger: logging.Logger = None
self.input_file = None
self.diff_file = None
self.should_use_cpu_multiprocessing = True
@@ -209,6 +209,10 @@ class DisableEditingClassification(bpy.types.Operator):
class RemoveClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_classification"
bl_label = "Remove Classification"
bl_description = (
"The classification and all of its relationships, children references, "
"and relationships between objects and child references will be completely removed from a project"
)
bl_options = {"REGISTER", "UNDO"}
classification: bpy.props.IntProperty()
@@ -53,6 +53,10 @@ class AddContext(bpy.types.Operator, Operator):
class RemoveContext(bpy.types.Operator, Operator):
bl_idname = "bim.remove_context"
bl_label = "Remove Context"
bl_description = (
"Remove representation context. Any representation geometry that is assigned to the context is also removed. "
"If a context is removed, then any subcontexts are also removed"
)
bl_options = {"REGISTER", "UNDO"}
context: bpy.props.IntProperty()
@@ -246,6 +246,7 @@ class ExportIfcCsv(bpy.types.Operator):
if props.format != "csv" and props.should_generate_svg:
schedule_creator = scheduler.Scheduler()
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
self.report({"INFO"}, f"Data is exported to {props.format.upper()}.")
return {"FINISHED"}
@@ -285,6 +286,7 @@ class ImportIfcCsv(bpy.types.Operator):
if not props.should_load_from_memory:
ifc_file.write(props.csv_ifc_file)
refresh_ui_data()
self.report({"INFO"}, "Data is imported to IFC.")
return {"FINISHED"}
@@ -35,7 +35,6 @@ classes = (
operator.PrintUnusedElementStats,
operator.ProfileImportIFC,
operator.PurgeHdf5Cache,
operator.PurgeIfcLinks,
operator.PurgeUnusedElementsByClass,
operator.RewindInspector,
operator.SelectExpressFile,
@@ -104,10 +104,11 @@ class PrintIfcFile(bpy.types.Operator):
return {"FINISHED"}
class PurgeIfcLinks(bpy.types.Operator):
bl_idname = "bim.purge_ifc_links"
bl_label = "Purge IFC Links"
bl_description = "Purge all definitions and references from the file.\nWarning : Cannot be undone."
class ConvertToBlender(bpy.types.Operator):
bl_idname = "bim.convert_to_blender"
bl_label = "Convert To Blender File"
bl_description = "Removes all IFC data and revert to basic Blender objects.\nWarning : Cannot be undone."
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for obj in bpy.data.objects:
@@ -124,26 +125,6 @@ class PurgeIfcLinks(bpy.types.Operator):
return {"FINISHED"}
class ConvertToBlender(bpy.types.Operator):
bl_idname = "bim.convert_to_blender"
bl_label = "Convert To Blender File"
bl_description = "Removes all IFC data, and converts the file to a simple Blender file."
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
for o in bpy.data.objects:
if o.type in {"MESH", "EMPTY"}:
o.BIMObjectProperties.ifc_definition_id = 0
if o.data:
o.data.BIMMeshProperties.ifc_definition_id = 0
for m in bpy.data.materials:
m.BIMMaterialProperties.ifc_style_id = False
bpy.context.scene.BIMProperties.ifc_file = ""
IfcStore.purge()
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class ValidateIfcFile(bpy.types.Operator):
bl_idname = "bim.validate_ifc_file"
bl_label = "Validate IFC File"
@@ -60,9 +60,6 @@ class BIM_PT_debug(Panel):
row = layout.row()
row.operator("bim.purge_hdf5_cache")
row = layout.row()
row.operator("bim.purge_ifc_links")
row = layout.row()
row.operator("bim.update_representation", text="Manually Save Representation")
@@ -29,6 +29,7 @@ import subprocess
import numpy as np
import multiprocessing
import ifcopenshell
import ifcopenshell.ifcopenshell_wrapper
import ifcopenshell.geom
import ifcopenshell.util.selector
import ifcopenshell.util.representation
@@ -219,11 +220,12 @@ class CreateDrawing(bpy.types.Operator):
def execute(self, context):
self.props = context.scene.DocProperties
active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id
if self.print_all:
original_drawing_id = self.props.active_drawing_id
original_drawing_id = active_drawing_id
drawings_to_print = [d.ifc_definition_id for d in self.props.drawings if d.is_selected and d.is_drawing]
else:
drawings_to_print = [self.props.active_drawing_id]
drawings_to_print = [active_drawing_id]
for drawing_i, drawing_id in enumerate(drawings_to_print):
self.drawing_index = drawing_i
@@ -1451,13 +1453,19 @@ class ActivateModel(bpy.types.Operator):
CutDecorator.uninstall()
# save current visibility statuses for Views and Types collections
visibility_status: dict[bpy.types.Object, bool] = {}
for col in bpy.data.collections["Views"].children:
for obj in col.objects:
visibility_status[obj] = obj.hide_get()
for obj in bpy.data.collections["Types"].objects:
visibility_status[obj] = obj.hide_get()
if not bpy.app.background:
with context.temp_override(**tool.Blender.get_viewport_context()):
bpy.ops.object.hide_view_clear()
bpy.ops.bim.activate_status_filters()
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
for obj in context.visible_objects:
element = tool.Ifc.get_entity(obj)
if not element:
@@ -1475,6 +1483,11 @@ class ActivateModel(bpy.types.Operator):
is_global=True,
should_sync_changes_first=True,
)
# restore visibility after hide_view_clear()
for obj, hide_status in visibility_status.items():
obj.hide_set(hide_status)
tool.Blender.update_viewport()
return {"FINISHED"}
@@ -719,7 +719,7 @@ class SvgWriter:
self.svg.text(sheet_id, insert=(text_position[0], text_position[1] + 2.5), class_="ELEVATION", **text_style)
)
def get_reference_and_sheet_id_from_annotation(self, element):
def get_reference_and_sheet_id_from_annotation(self, element: ifcopenshell.entity_instance) -> tuple[str, str]:
reference_id = "-"
sheet_id = "-"
drawing = tool.Drawing.get_annotation_element(element)
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell.util.element
import blenderbim.tool as tool
import ifcopenshell.util.placement
from mathutils import Vector
@@ -222,7 +223,7 @@ class ConnectionsData:
@classmethod
def is_connection_realization(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
connections = element.IsConnectionRealization
connections = getattr(element, "IsConnectionRealization", None)
if not connections:
return
@@ -215,6 +215,13 @@ class SwitchRepresentation(bpy.types.Operator, Operator):
disable_opening_subtractions: bpy.props.BoolProperty()
should_switch_all_meshes: bpy.props.BoolProperty()
@classmethod
def poll(cls, context):
if context.active_object.mode == "OBJECT":
return True
cls.poll_message_set("Only available in OBJECT mode - Press TAB in the viewport")
return False
def _execute(self, context):
target_representation = tool.Ifc.get().by_id(self.ifc_definition_id)
target = target_representation.ContextOfItems
@@ -223,6 +230,8 @@ class SwitchRepresentation(bpy.types.Operator, Operator):
element = tool.Ifc.get_entity(obj)
if not element:
continue
if not obj.mode == "OBJECT":
continue
if obj == context.active_object:
representation = target_representation
else:
@@ -538,6 +547,8 @@ class OverrideDelete(bpy.types.Operator):
row.prop(self, "is_batch", text="Enable Faster Deletion")
def _execute(self, context):
start_time = time()
if self.is_batch:
ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get())
@@ -562,6 +573,11 @@ class OverrideDelete(bpy.types.Operator):
IfcStore.add_transaction_operation(self)
# Required otherwise gizmos are still visible
context.view_layer.objects.active = None
operator_time = time() - start_time
if operator_time > 10:
self.report({"INFO"}, "IFC Delete was finished in {:.2f} seconds".format(operator_time))
return {"FINISHED"}
def rollback(self, data):
@@ -902,7 +918,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
if pset:
pset = tool.Ifc.get().by_id(pset["id"])
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
if new[0].is_a("IfcElementAssembly"):
linked_aggregate_group = [
r.RelatingGroup
@@ -910,7 +926,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
if r.is_a("IfcRelAssignsToGroup")
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
]
tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], product=new[0])
tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], products=[new[0]])
class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro):
@@ -980,7 +996,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
else:
index = add_linked_aggregate_pset(part, index)
# index += 1
obj = tool.Ifc.get_object(part)
obj.select_set(True)
@@ -1014,9 +1030,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
return
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name)
ifcopenshell.api.run(
"group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group
)
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group)
def custom_incremental_naming_for_element_assembly(old_to_new):
for new in old_to_new.values():
@@ -1040,10 +1054,10 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
if re.findall(pattern2, new_obj.name):
split_name = new_obj.name.split(".")
new_obj.name = split_name[0] + "_" + number
def get_max_index(parts):
psets = [ifcopenshell.util.element.get_pset(p, "BBIM_Linked_Aggregate") for p in parts]
index = [i['Index'] for i in psets if i]
index = [i["Index"] for i in psets if i]
if len(index) > 0:
index = max(index)
return index
@@ -1057,14 +1071,14 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
new_pset = ifcopenshell.api.run(
"pset.add_pset", tool.Ifc.get(), product=new[0], name=self.pset_name
)
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
pset=new_pset,
properties={"Index": pset["Index"]},
)
if new[0].is_a("IfcElementAssembly"):
linked_aggregate_group = [
r.RelatingGroup
@@ -1083,6 +1097,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
location_diff = new_obj.location - base_obj_location
new_obj.location = context.scene.cursor.location + location_diff
if len(context.selected_objects) != 1:
return {"FINISHED"}
@@ -1102,7 +1117,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
select_objects_and_add_data(selected_element)
old_to_new = OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True)
tool.Root.recreate_aggregate(old_to_new)
copy_linked_aggregate_data(old_to_new)
@@ -1269,9 +1284,9 @@ class RefreshLinkedAggregate(bpy.types.Operator):
selected_matrix = selected_obj.matrix_world
object_duplicate = tool.Ifc.get_object(element)
duplicate_matrix = object_duplicate.matrix_world.decompose()
return selected_matrix, duplicate_matrix
def set_new_matrix(selected_matrix, duplicate_matrix, old_to_new):
for old, new in old_to_new.items():
new_obj = tool.Ifc.get_object(new[0])
@@ -1279,7 +1294,6 @@ class RefreshLinkedAggregate(bpy.types.Operator):
matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world
new_obj_matrix = new_base_matrix @ matrix_diff
new_obj.matrix_world = new_obj_matrix
active_element = tool.Ifc.get_entity(context.active_object)
if not active_element:
@@ -1465,14 +1479,13 @@ class OverridePasteBuffer(bpy.types.Operator):
def execute(self, context):
bpy.ops.view3d.pastebuffer()
if IfcStore.get_file():
for obj in context.selected_objects:
# Pasted objects may come from another Blender session, or even
# from the same session where the original object has since
# been deleted. As the source element may not exist, paste will
# always unlink the element. If you want to duplicate an
# element, use the duplicate commands.
tool.Root.unlink_object(obj)
for obj in context.selected_objects:
# Pasted objects may come from another Blender session, or even
# from the same session where the original object has since
# been deleted. As the source element may not exist, paste will
# always unlink the element. If you want to duplicate an
# element, use the duplicate commands.
tool.Root.unlink_object(obj)
return {"FINISHED"}
@@ -19,6 +19,7 @@
import os
import bpy
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.doc
import ifcopenshell.util.schema
import blenderbim.tool as tool
@@ -166,6 +167,9 @@ class ObjectMaterialData:
cls.data["type_material"] = cls.type_material()
cls.data["material_type"] = cls.material_type()
cls.data["active_material_constituents"] = cls.active_material_constituents()
# after material_name and type_material
cls.data["is_type_material_overridden"] = cls.is_type_material_overridden()
cls.is_loaded = True
@classmethod
@@ -294,8 +298,7 @@ class ObjectMaterialData:
@classmethod
def material_name(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
material = ifcopenshell.util.element.get_material(element)
material = cls.material
if material:
return getattr(material, "Name", None) or "Unnamed"
@@ -339,3 +342,18 @@ class ObjectMaterialData:
if not cls.material or not material.is_a("IfcMaterialConstituentSet"):
return []
return [m.Name for m in material.MaterialConstituents if m.Name]
@classmethod
def is_type_material_overridden(cls) -> bool:
if not cls.data["type_material"]:
return False
# try to avoid accessing ifc
if cls.data["material_name"] != cls.data["type_material"]:
return True
# in theory material can be overridden by the same material
# so we check occurrence material explicitly
element = tool.Ifc.get_entity(bpy.context.active_object)
occurrence_material = ifcopenshell.util.element.get_material(element, should_inherit=False)
return bool(occurrence_material)
@@ -177,7 +177,13 @@ class BIM_PT_object_material(Panel):
if ObjectMaterialData.data["type_material"]:
row = self.layout.row(align=True)
row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="CON_CHILDOF")
if ObjectMaterialData.data["is_type_material_overridden"]:
row.label(
text=f"Inherited Material Is Occurrence Overridden",
icon="CON_CHILDOF",
)
else:
row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="CON_CHILDOF")
if ObjectMaterialData.data["material_class"]:
return self.draw_material_ui()
@@ -39,6 +39,7 @@ from mathutils import Vector, Matrix
from bpy_extras.object_utils import AddObjectHelper
from . import prop
import json
from typing import Any, Union
class EnableAddType(bpy.types.Operator, tool.Ifc.Operator):
@@ -511,7 +512,7 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
)
def ensure_material_assigned(usecase_path, ifc_file, settings):
def ensure_material_assigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None:
if usecase_path == "material.assign_material":
if not settings.get("material", None):
return
@@ -524,53 +525,76 @@ def ensure_material_assigned(usecase_path, ifc_file, settings):
]:
elements.extend(rel.RelatedObjects)
for element in elements:
obj = IfcStore.get_element(element.GlobalId)
if not obj or not obj.data:
continue
element_material = ifcopenshell.util.element.get_material(element)
material = [m for m in ifc_file.traverse(element_material) if m.is_a("IfcMaterial")]
object_material_ids = [
om.BIMObjectProperties.ifc_definition_id
for om in obj.data.materials
if om is not None and om.BIMObjectProperties.ifc_definition_id
]
if material and material[0].id() in object_material_ids:
continue
if len(obj.data.materials) == 1:
obj.data.materials.clear()
if not material:
continue
obj.data.materials.append(IfcStore.get_element(material[0].id()))
update_blender_ifc_materials(elements)
def ensure_material_unassigned(usecase_path, ifc_file, settings):
def ensure_material_unassigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None:
elements = settings["products"]
if elements[0].is_a("IfcElementType"):
elements.extend(ifcopenshell.util.element.get_types(elements[0]))
update_blender_ifc_materials(elements)
def update_blender_ifc_materials(elements: list[ifcopenshell.entity_instance]) -> None:
"""update mesh blender materials that have ifc material connected to them
by replacing them with `blender_material`"""
# since different elements can share meshes (e.g. occurrecnes without openings)
# we need to make sure not to affect them accidentally
meshes_users: dict[bpy.types.Mesh, set[bpy.types.Object]] = dict()
for obj in bpy.data.objects:
if not obj.data:
continue
meshes_users.setdefault(obj.data, set()).add(obj)
objects: set[bpy.types.Object] = set()
for element in elements:
obj = tool.Ifc.get_object(element)
obj: bpy.types.Object = tool.Ifc.get_object(element)
if not obj or not obj.data:
continue
element_material = ifcopenshell.util.element.get_material(element)
if element_material:
objects.add(obj)
meshes: set[bpy.types.Mesh] = {obj.data for obj in objects}
for mesh in meshes:
mesh_users = meshes_users[mesh]
if not mesh_users.issubset(objects):
continue
to_remove = []
for i, slot in enumerate(obj.material_slots):
if not slot.material:
# NOTE: we need `obj` as removing materials and appending them to `mesh.materials`
# will mess up mesh faces material indices
# NOTE: we make an assumption here that all mesh users
# have the same material - they either inherit it from the type
# or type doesn't have a material.
#
# If we add option to UI to add materials overriding type materials
# then this assumption won't be safe anymore
obj = next(iter(mesh_users))
element = tool.Ifc.get_entity(obj)
current_material = ifcopenshell.util.element.get_material(element)
if current_material:
current_material = tool.Ifc.get_object(current_material)
material_replaced = False
for material_slot in obj.material_slots:
material = material_slot.material
if material is None:
continue
material = tool.Ifc.get_entity(slot.material)
if material:
to_remove.append(i)
total_removed = 0
for i in to_remove:
obj.active_material_index = i - total_removed
with bpy.context.temp_override(object=obj):
bpy.ops.object.material_slot_remove()
total_removed += 1
ifc_material = tool.Ifc.get_entity(material)
# it's blender material for style, so ignore it
if not ifc_material:
continue
if ifc_material == current_material:
continue
material_slot.material = current_material
material_replaced = True
if not material_replaced and current_material:
mesh.materials.append(current_material)
# clear empty slots
for i, material in reversed(list(enumerate(mesh.materials[:]))):
if material is None:
mesh.materials.pop(index=i)
@@ -56,8 +56,11 @@ class LaunchTypeManager(bpy.types.Operator):
ifc_class = props.ifc_class or AuthoringData.data["ifc_element_type"]
else:
ifc_class = AuthoringData.data["ifc_element_type"]
props.type_class = ifc_class
bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9)
# will be None if project has no types
if ifc_class is not None:
props.type_class = ifc_class
bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9)
return context.window_manager.invoke_popup(self, width=550)
def draw(self, context):
@@ -378,6 +378,7 @@ class BimToolUI:
op.depth = cls.props.extrusion_depth
add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "")
add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__)
if AuthoringData.data["active_class"] in (
"IfcCableCarrierSegment",
@@ -385,8 +386,8 @@ class BimToolUI:
"IfcDuctSegment",
"IfcPipeSegment",
):
add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_F", "")
add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_Y", "")
if context.region.type != "TOOL_HEADER":
cls.layout.operator("bim.mep_add_bend")
cls.layout.operator("bim.mep_add_transition")
@@ -394,7 +395,6 @@ class BimToolUI:
else:
add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__)
add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
@@ -719,10 +719,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.bim.flip_wall()
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
bpy.ops.bim.flip_fill()
elif self.active_class in ("IfcBeam", "IfcColumn"):
elif self.active_material_usage == "PROFILE":
bpy.ops.bim.flip_object(flip_local_axes="XZ")
elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"):
bpy.ops.bim.fit_flow_segments()
def hotkey_S_G(self):
obj = bpy.context.active_object
@@ -808,9 +807,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
return
if self.active_material_usage == "LAYER2":
bpy.ops.bim.join_wall(join_type="V")
elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"):
bpy.ops.bim.fit_flow_segments()
elif self.active_material_usage == "PROFILE":
bpy.ops.bim.extend_profile(join_type="V")
def hotkey_S_B(self):
bpy.ops.bim.add_boundary()
@@ -47,6 +47,7 @@ from mathutils import Vector, Matrix
from bpy.app.handlers import persistent
from blenderbim.bim.module.project.data import LinksData
from blenderbim.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator
from typing import Union
class NewProject(bpy.types.Operator):
@@ -301,7 +302,7 @@ class AssignLibraryDeclaration(bpy.types.Operator):
ifcopenshell.api.run(
"project.assign_declaration",
self.file,
definition=self.file.by_id(self.definition),
definitions=[self.file.by_id(self.definition)],
relating_context=self.file.by_type("IfcProjectLibrary")[0],
)
element_name = self.props.active_library_element
@@ -337,7 +338,7 @@ class UnassignLibraryDeclaration(bpy.types.Operator):
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definition=self.file.by_id(self.definition),
definitions=[self.file.by_id(self.definition)],
relating_context=self.file.by_type("IfcProjectLibrary")[0],
)
element_name = self.props.active_library_element
@@ -867,7 +868,16 @@ class LinkIfc(bpy.types.Operator):
except:
pass # Perhaps on another drive or something
new.name = filepath
bpy.ops.bim.load_link(filepath=filepath, false_origin=self.false_origin)
status = bpy.ops.bim.load_link(filepath=filepath, false_origin=self.false_origin)
if status == {"CANCELLED"}:
error_msg = (
f'Error processing IFC file "{self.filepath}" '
"was critical and blend file either wasn't saved or wasn't updated. "
"See logs above in system console for details."
)
print(error_msg)
self.report({"ERROR"}, error_msg)
return {"FINISHED"}
print(f"Finished linking {len(files)} IFCs", time.time() - start)
return {"FINISHED"}
@@ -946,10 +956,12 @@ class LoadLink(bpy.types.Operator):
if self.filepath.lower().endswith(".blend"):
self.link_blend(filepath)
elif self.filepath.lower().endswith(".ifc"):
self.link_ifc()
status = self.link_ifc()
if status:
return status
return {"FINISHED"}
def link_blend(self, filepath):
def link_blend(self, filepath: str) -> None:
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
data_to.scenes = data_from.scenes
for scene in bpy.data.scenes:
@@ -962,7 +974,7 @@ class LoadLink(bpy.types.Operator):
link = bpy.context.scene.BIMProjectProperties.links.get(self.filepath)
link.is_loaded = True
def link_ifc(self):
def link_ifc(self) -> Union[set[str], None]:
blend_filepath = self.filepath + ".cache.blend"
h5_filepath = self.filepath + ".cache.h5"
@@ -982,11 +994,14 @@ except Exception as e:
exit(1)
"""
t = time.time()
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as temp_file:
temp_file.write(code)
run = subprocess.run([bpy.app.binary_path, "-b", "--python", temp_file.name, "--python-exit-code", "1"])
if run.returncode == 1:
print("An error occurred while processing your IFC.")
if not os.path.exists(blend_filepath) or os.stat(blend_filepath).st_mtime < t:
return {"CANCELLED"}
self.link_blend(blend_filepath)
@@ -185,6 +185,8 @@ class IfcClassData:
if element:
if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"):
return False
if element.is_a() in ("IfcWindowStyle", "IfcDoorStyle"): #see https://github.com/IfcOpenShell/IfcOpenShell/issues/4622#issuecomment-2095676368
return True
for product in cls.ifc_products():
if element.is_a(product[0]):
return True
@@ -23,6 +23,7 @@ import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.style as core
import ifcopenshell.util.representation
from blenderbim.bim.module.style.prop import switch_shading
from pathlib import Path
from mathutils import Vector
@@ -126,6 +127,10 @@ class DisableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
tool.Style.reload_material_from_ifc(material)
props.is_editing_style = 0
# restore selected style type
material = tool.Ifc.get_object(style)
material.BIMStyleProperties.active_style_type = material.BIMStyleProperties.active_style_type
class EditStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_style"
@@ -246,14 +251,15 @@ class BrowseExternalStyle(bpy.types.Operator):
)
def invoke(self, context, event):
external_style = None
style_elements = None
if self.active_surface_style_id:
style = tool.Ifc.get().by_id(self.active_surface_style_id)
external_style = tool.Style.get_style_elements(style).get("IfcExternallyDefinedSurfaceStyle", None)
style_elements = tool.Style.get_style_elements(style)
# automatically select previously selected external style in file browser
# if it exists in the file
if external_style and self.filepath == "":
if style_elements and self.filepath == "" and tool.Style.has_blender_external_style(style_elements):
external_style = style_elements["IfcExternallyDefinedSurfaceStyle"]
style_path = Path(tool.Ifc.resolve_uri(external_style.Location))
self.directory = str(style_path.parent)
self.filepath = str(style_path)
@@ -310,6 +316,9 @@ class BrowseExternalStyle(bpy.types.Operator):
attributes["Location"].string_value = filepath
attributes["Identification"].string_value = f"{self.data_block_type}/{self.data_block}"
attributes["Name"].string_value = self.data_block
style = tool.Ifc.get().by_id(self.active_surface_style_id)
bpy.ops.bim.activate_external_style(material_name=tool.Ifc.get_object(style).name)
return {"FINISHED"}
@@ -325,14 +334,23 @@ class ActivateExternalStyle(bpy.types.Operator, tool.Ifc.Operator):
material = context.active_object.active_material
else:
material = bpy.data.materials[self.material_name]
external_style = tool.Style.get_style_elements(material)["IfcExternallyDefinedSurfaceStyle"]
data_block_type, data_block = external_style.Identification.split("/")
style_path = Path(tool.Ifc.resolve_uri(external_style.Location))
props = context.scene.BIMStylesProperties
if props.is_editing:
location = props.external_style_attributes["Location"].string_value
identification = props.external_style_attributes["Identification"].string_value
else:
external_style = tool.Style.get_style_elements(material)["IfcExternallyDefinedSurfaceStyle"]
location = external_style.Location
identification = external_style.Identification
data_block_type, data_block = identification.split("/")
style_path = Path(tool.Ifc.resolve_uri(location))
if style_path.suffix != ".blend":
self.report(
{"ERROR"},
f"Error loading external style for \"{material.name}\" - only Blender external styles are supported",
f'Error loading external style for "{material.name}" - only Blender external styles are supported',
)
return {"CANCELLED"}
@@ -587,7 +605,8 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
props.is_editing_class = self.ifc_class
tool.Style.set_surface_style_props()
surface_style = tool.Style.get_style_elements(style).get(self.ifc_class, None)
style_elements = tool.Style.get_style_elements(style)
surface_style = style_elements.get(self.ifc_class, None)
attributes = tool.Style.get_style_ui_props_attributes(self.ifc_class)
# lighting style require special handling since Attribute doesn't support colors
@@ -607,6 +626,17 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
attributes.clear()
blenderbim.bim.helper.import_attributes2(surface_style or self.ifc_class, attributes, callback)
material = tool.Ifc.get_object(style)
active_style_type = material.BIMStyleProperties.active_style_type
if self.ifc_class == "IfcExternallyDefinedSurfaceStyle" and active_style_type != "External":
if tool.Style.has_blender_external_style(style_elements):
switch_shading(material, "External")
elif (
self.ifc_class in ("IfcSurfaceStyleShading", "IfcSurfaceStyleRendering", "IfcSurfaceStyleWithTextures")
and active_style_type != "Shading"
):
switch_shading(material, "Shading")
class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_surface_style"
@@ -631,6 +661,10 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
self.props.is_editing_style = 0
core.load_styles(tool.Style, style_type=self.props.style_type)
# restore selected style type
material = tool.Ifc.get_object(self.style)
material.BIMStyleProperties.active_style_type = material.BIMStyleProperties.active_style_type
def edit_existing_style(self):
material = tool.Ifc.get_object(self.style)
if self.surface_style.is_a() == "IfcSurfaceStyleShading":
@@ -33,6 +33,8 @@ from bpy.props import (
)
import gettext
from typing import Literal
_ = gettext.gettext
@@ -251,19 +253,15 @@ class BIMStylesProperties(PropertyGroup):
)
def update_shading_style(self, context):
blender_material = self.id_data
style_elements = tool.Style.get_style_elements(blender_material)
if self.active_style_type == "External":
if tool.Style.has_blender_external_style(style_elements):
try:
bpy.ops.bim.activate_external_style(material_name=blender_material.name)
except RuntimeError as error:
if str(error).startswith("Error: Error loading external style for "):
return
raise error
elif self.active_style_type == "Shading":
def switch_shading(blender_material: bpy.types.Material, style_type: Literal["External", "Shading"]) -> None:
if style_type == "External":
try:
bpy.ops.bim.activate_external_style(material_name=blender_material.name)
except RuntimeError as error:
if str(error).startswith("Error: Error loading external style for "):
return
raise error
elif style_type == "Shading":
style_elements = tool.Style.get_style_elements(blender_material)
rendering_style = None
texture_style = None
@@ -279,6 +277,16 @@ def update_shading_style(self, context):
if rendering_style and texture_style:
tool.Loader.create_surface_style_with_textures(blender_material, rendering_style, texture_style)
def update_shading_style(self, context):
blender_material = self.id_data
style_elements = tool.Style.get_style_elements(blender_material)
if self.active_style_type == "External":
if tool.Style.has_blender_external_style(style_elements):
switch_shading(blender_material, self.active_style_type)
elif self.active_style_type == "Shading":
switch_shading(blender_material, self.active_style_type)
tool.Style.record_shading(blender_material)
@@ -146,23 +146,51 @@ class SelectType(bpy.types.Operator):
relating_type: bpy.props.IntProperty()
def execute(self, context):
element = tool.Ifc.get().by_id(self.relating_type)
obj = tool.Ifc.get_object(element)
if obj:
try:
tool.Blender.select_and_activate_single_object(context, obj)
except:
self.report({"INFO"}, "Type object is hidden.")
# IfcTypeProducts are only used for annotations and not part of the model interface.
if element.is_a() != "IfcTypeProduct":
try:
context.scene.BIMModelProperties.ifc_class = element.is_a()
context.scene.BIMModelProperties.relating_type_id = str(self.relating_type)
except:
# Potentially our BIM Tool is filtered to a specific element.
pass
if self.relating_type: #if operator button sends a relating_type, the iterator only selects this one type
element = tool.Ifc.get().by_id(self.relating_type)
obj = tool.Ifc.get_object(element)
selected_objs = [obj]
else: #else, the iterator selects all the types of all the selected objects
selected_objs = context.selected_objects
active_obj = context.active_object
selected_objs.append(active_obj) #update selected_objs so the active_obj is at the end of the list
last_relating_type_obj = None
types_collection_in_view_layer = self.find_collection_in_ifcproject(context, collection_name = "Types")
types_collection_in_view_layer.hide_viewport = False
types_collection = bpy.data.collections.get("Types")
for type_obj in types_collection.objects:
type_obj.hide_set(True)
for obj in selected_objs:
element = tool.Ifc.get_entity(obj)
relating_type = ifcopenshell.util.element.get_type(element)
if relating_type:
relating_type_obj = tool.Ifc.get_object(relating_type)
if relating_type_obj:
if relating_type_obj.hide_get():
relating_type_obj.hide_set(False)
relating_type_obj.select_set(True)
last_relating_type_obj = relating_type_obj
if not element.is_a("IfcTypeObject"):
obj.select_set(False)
context.view_layer.objects.active = last_relating_type_obj #makes the active_obj's type the active object
return {"FINISHED"}
def find_collection_in_ifcproject(self, context, collection_name):
ifc_project_collection = None
for child in context.view_layer.layer_collection.children:
if "IfcProject" in child.name:
ifc_project_collection = child
break
if ifc_project_collection:
collection_in_view_layer = ifc_project_collection.children.get(collection_name)
return collection_in_view_layer
class SelectSimilarType(bpy.types.Operator):
bl_idname = "bim.select_similar_type"
@@ -88,7 +88,7 @@ class BIM_PT_type(Panel):
if TypeData.data["relating_type"]:
row.label(text=TypeData.data["relating_type"]["name"])
op = row.operator("bim.select_type", icon="OBJECT_DATA", text="")
op.relating_type = TypeData.data["relating_type"]["id"]
op.relating_type = 0 #will only select the relating types of only the selected objects
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
row.operator("bim.enable_editing_type", icon="GREASEPENCIL", text="")
row.operator("bim.unassign_type", icon="X", text="")
+1 -1
View File
@@ -145,7 +145,7 @@ def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identificati
def rename_reference(ifc, drawing, reference=None, identification=None):
attributes = drawing.generate_reference_attributes(reference, Identifiaction=identification)
attributes = drawing.generate_reference_attributes(reference, Identification=identification)
ifc.run("document.edit_reference", reference=reference, attributes=attributes)
@@ -23,9 +23,14 @@ import hashlib
import logging
import numpy as np
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
import ifcopenshell.util.system
import blenderbim.core.tool
import blenderbim.core.drawing
import blenderbim.core.style
import blenderbim.core.spatial
import blenderbim.core.system
import blenderbim.core.geometry
import blenderbim.tool as tool
import blenderbim.bim.import_ifc
+8 -2
View File
@@ -160,10 +160,16 @@ class ImportFilterQueryTransformer(lark.Transformer):
return args[0]
def instance(self, args):
return {"type": "instance", "value": " ".join([a.children[0].value for a in args])}
if args[0].data == "not":
return {"type": "instance", "value": "!" + args[1].children[0].value}
else:
return {"type": "instance", "value": args[0].children[0].value}
def entity(self, args):
return {"type": "entity", "value": " ".join([a.children[0].value for a in args])}
if args[0].data == "not":
return {"type": "entity", "value": "!" + args[1].children[0].value}
else:
return {"type": "entity", "value": args[0].children[0].value}
def attribute(self, args):
name, comparison, value = args
+8 -1
View File
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell.util.element
import ifcopenshell.util.system
import blenderbim.core.tool
import blenderbim.tool as tool
@@ -145,7 +146,7 @@ class System(blenderbim.core.tool.System):
new.ifc_class = system.is_a()
@classmethod
def load_ports(cls, element, ports):
def load_ports(cls, element: ifcopenshell.entity_instance, ports: list[ifcopenshell.entity_instance]) -> None:
if not ports:
return
obj = tool.Ifc.get_object(element)
@@ -155,7 +156,13 @@ class System(blenderbim.core.tool.System):
ifc_importer.calculate_unit_scale()
ifc_importer.process_context_filter()
ifc_importer.create_generic_elements(set(ports))
container = ifcopenshell.util.element.get_container(element)
if container:
collection = tool.Ifc.get_object(container).BIMObjectProperties.collection
ifc_importer.collections[container.GlobalId] = collection
ifc_importer.place_objects_in_collections()
for port_obj in ifc_importer.added_data.values():
port_obj.parent = obj
port_obj.matrix_parent_inverse = obj.matrix_world.inverted()
+1
View File
@@ -16,6 +16,7 @@ a {
}
.sidebar-brand-text {
font-size: 1rem;
text-align: center;
}
.blockbutton {
max-width: 500px;
+6
View File
@@ -95,7 +95,10 @@ html_theme_options = {
"color-background-border": "#cfd0cb",
"color-foreground-primary": "#2e3436",
"color-sidebar-item-background--hover": "#f7f7f6",
"color-link": "#39b54a",
"color-link--visited": "#39b54a",
"color-link--hover": "#d98014",
"color-link--visited--hover": "#d98014",
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji"
},
"dark_css_variables": {
@@ -106,7 +109,10 @@ html_theme_options = {
"color-background-border": "#2e3436",
"color-foreground-primary": "#eeeeec",
"color-sidebar-item-background--hover": "#2e3436",
"color-link": "#39b54a",
"color-link--visited": "#39b54a",
"color-link--hover": "#d98014",
"color-link--visited--hover": "#d98014",
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji"
},
+12 -13
View File
@@ -92,13 +92,14 @@ For Linux or Mac:
$ ln -s $PWD/src/blenderbim/blenderbim/tool $BLENDER_ADDON_PATH/tool
$ ln -s $PWD/src/blenderbim/blenderbim/bim $BLENDER_ADDON_PATH/bim
# Remove the IfcOpenShell dependency Python code
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/api
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/util
# Copy over compiled IfcOpenShell files
$ cp $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/*_wrapper* $PWD/src/ifcopenshell-python/ifcopenshell/
# Remove the IfcOpenShell dependency
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
# Replace them with links to the Git repository
$ ln -s $PWD/src/ifcopenshell-python/ifcopenshell/api $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/api
$ ln -s $PWD/src/ifcopenshell-python/ifcopenshell/util $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/util
$ ln -s $PWD/src/ifcopenshell-python/ifcopenshell $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
# Remove and link other IfcOpenShell utilities
$ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
@@ -153,21 +154,19 @@ Before running it follow the instructions descibed after `rem` tags.
rd /S /Q "%blenderbim%\tool\"
rd /S /Q "%blenderbim%\bim\"
echo Replacing them with links to the Git repository...
mklink /D "%blenderbim%\core" "%cd%\src\blenderbim\blenderbim\core"
mklink /D "%blenderbim%\tool" "%cd%\src\blenderbim\blenderbim\tool"
mklink /D "%blenderbim%\bim" "%cd%\src\blenderbim\blenderbim\bim"
echo Copy over compiled IfcOpenShell files...
copy "%blenderbim%\libs\site\packages\ifcopenshell\*_wrapper*" "%cd%\src\ifcopenshell-python\ifcopenshell\"
echo Remove the IfcOpenShell dependency Python code...
rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell\api"
rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell\util"
echo Remove the IfcOpenShell dependency...
rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell"
echo Replacing them with links to the Git repository...
mklink /D "%blenderbim%\libs\site\packages\ifcopenshell\api" "%cd%\src\ifcopenshell-python\ifcopenshell\api"
mklink /D "%blenderbim%\libs\site\packages\ifcopenshell\util" "%cd%\src\ifcopenshell-python\ifcopenshell\util"
echo Replace them with links to the Git repository...
mklink /D "%blenderbim%\libs\site\packages\ifcopenshell" "%cd%\src\ifcopenshell-python\ifcopenshell"
echo Remove and link other IfcOpenShell utilities...
del "%blenderbim%\libs\site\packages\ifccsv.py"
@@ -40,7 +40,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="Australian Library"
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
@@ -196,7 +196,7 @@ class LibraryGenerator:
)
layer.Name = layer_data[0]
layer.LayerThickness = layer_data[2]
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_layer_type(self, ifc_class, name, thickness):
@@ -205,7 +205,7 @@ class LibraryGenerator:
layer_set = rel.RelatingMaterial
layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=layer_set, material=self.materials["TBD"]["ifc"])
layer.LayerThickness = thickness
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_profile_type(self, ifc_class, name, profile):
@@ -216,7 +216,7 @@ class LibraryGenerator:
"material.add_profile", self.file, profile_set=profile_set, material=self.materials["TBD"]["ifc"]
)
ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
def create_type(self, ifc_class, name, representations):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name)
@@ -248,7 +248,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
LibraryGenerator().generate()
@@ -35,7 +35,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library"
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"})
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
@@ -209,7 +209,7 @@ class LibraryGenerator:
layer_set = rel.RelatingMaterial
layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=layer_set, material=self.material)
layer.LayerThickness = thickness
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_profile_type(self, ifc_class, name, profile):
@@ -220,7 +220,7 @@ class LibraryGenerator:
"material.add_profile", self.file, profile_set=profile_set, material=self.material
)
ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
def create_type(self, ifc_class, name, representations):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name)
@@ -252,7 +252,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
LibraryGenerator().generate()
@@ -42,7 +42,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
@@ -131,7 +131,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
if __name__ == "__main__":
@@ -37,7 +37,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
@@ -1797,7 +1797,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation_2d
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_layer_set_type(self, name, data):
@@ -1811,7 +1811,7 @@ class LibraryGenerator:
)
layer.Name = layer_data[0]
layer.LayerThickness = layer_data[2]
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_layer_type(self, ifc_class, name, thickness):
@@ -1822,7 +1822,7 @@ class LibraryGenerator:
"material.add_layer", self.file, layer_set=layer_set, material=self.materials["TBD"]["ifc"]
)
layer.LayerThickness = thickness
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_profile_type(self, ifc_class, name, profile):
@@ -1837,7 +1837,7 @@ class LibraryGenerator:
# material=self.materials["TBD"]["ifc"]
)
ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
def create_type(self, ifc_class, name, representations):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name)
@@ -1869,7 +1869,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
if __name__ == "__main__":
@@ -319,7 +319,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
@@ -447,7 +447,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation_2d
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
return element
def create_type(self, ifc_class, name, representations):
@@ -480,7 +480,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
@@ -35,7 +35,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library"
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.library
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.library
)
ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"})
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
@@ -98,7 +98,7 @@ class LibraryGenerator:
ifcopenshell.api.run(
"geometry.assign_representation", self.file, product=element, representation=representation
)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
LibraryGenerator().generate()
@@ -43,7 +43,7 @@ class LibraryGenerator:
"root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=f"{parse_profiles_type} Steel Profiles Library"
)
ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project
"project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project
)
dim_exponents = self.file.createIfcDimensionalExponents(0, 0, 0, 0, 0, 0, 0)
length_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
@@ -182,7 +182,7 @@ class LibraryGenerator:
# material=self.materials["TBD"]["ifc"]
)
ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library)
def create_double_l_profile(self, profile, resulting_profile_name=None, profiles_gap=0, mode = "LLBB"):
def create_derived_profile(profile, mirrored=False):
@@ -91,7 +91,7 @@ def mirror_placement_test():
library = ifcopenshell.api.run(
"root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name=f"Non-structural assets library"
)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=library, relating_context=project)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[library], relating_context=project)
unit = ifcopenshell.api.run("unit.add_si_unit", ifc_file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc_file, units=[unit])
model = ifcopenshell.api.run("context.add_context", ifc_file, context_type="Model")
@@ -152,7 +152,7 @@ def mirror_placement_test():
element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcFurnitureType", name="test")
ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=element, representation=representation_3d)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[element], relating_context=library)
ifc_file.write("tmp.ifc")
@@ -165,7 +165,7 @@ def curve_between_two_points_test():
library = ifcopenshell.api.run(
"root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name=f"Non-structural assets library"
)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=library, relating_context=project)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[library], relating_context=project)
unit = ifcopenshell.api.run("unit.add_si_unit", ifc_file, unit_type="LENGTHUNIT", prefix="MILLI")
ifcopenshell.api.run("unit.assign_unit", ifc_file, units=[unit])
model = ifcopenshell.api.run("context.add_context", ifc_file, context_type="Model")
@@ -217,7 +217,7 @@ def curve_between_two_points_test():
print(representation_2d)
element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcFurnitureType", name="test")
ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=element, representation=representation_2d)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library)
ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[element], relating_context=library)
ifc_file.write("tmp.ifc")