This commit is contained in:
Andrej730
2025-04-29 15:05:25 +05:00
parent 1856eeddeb
commit ecf461e1e0
7 changed files with 47 additions and 31 deletions
+15 -5
View File
@@ -198,11 +198,16 @@ class MaterialCreator:
class IfcImporter: class IfcImporter:
file: ifcopenshell.file
"""Either provided by user as an attribute or will be loaded from ``input_file`` during ``execute()``."""
elements: set[ifcopenshell.entity_instance]
"""Set of IfcElements to import. Excluding ``gross_elements`` and ```native_elements``."""
def __init__(self, ifc_import_settings: IfcImportSettings): def __init__(self, ifc_import_settings: IfcImportSettings):
self.ifc_import_settings = ifc_import_settings self.ifc_import_settings = ifc_import_settings
tool.Loader.set_settings(ifc_import_settings) tool.Loader.set_settings(ifc_import_settings)
self.diff = None self.diff = None
self.file: ifcopenshell.file = None
self.project = None self.project = None
self.has_existing_project = False self.has_existing_project = False
# element guids to blender collections mapping # element guids to blender collections mapping
@@ -300,6 +305,7 @@ class IfcImporter:
bpy.context.window_manager.progress_end() bpy.context.window_manager.progress_end()
def process_context_filter(self) -> None: def process_context_filter(self) -> None:
"""Setup contexts. Necessary for importing elements representations."""
contexts = self.file.by_type("IfcGeometricRepresentationContext") contexts = self.file.by_type("IfcGeometricRepresentationContext")
if len(contexts) > 100: # Probably something strange happening. Encountered from Revizto. if len(contexts) > 100: # Probably something strange happening. Encountered from Revizto.
print("Warning! Excessive contexts were found and merged where applicable.") print("Warning! Excessive contexts were found and merged where applicable.")
@@ -384,6 +390,7 @@ class IfcImporter:
return results return results
def parse_native_elements(self) -> None: def parse_native_elements(self) -> None:
# TODO: move to `process_element_filter` to incapsulate all `self.elements` logic.
if not self.ifc_import_settings.should_load_geometry: if not self.ifc_import_settings.should_load_geometry:
return return
if not self.file.by_type("IfcSweptDiskSolid"): if not self.file.by_type("IfcSweptDiskSolid"):
@@ -1129,7 +1136,7 @@ class IfcImporter:
bpy.ops.view3d.view_selected() bpy.ops.view3d.view_selected()
bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_all(action="DESELECT")
def setup_arrays(self): def setup_arrays(self, elements: Optional[set[ifcopenshell.entity_instance]] = None):
for pset in self.file.by_type("IfcPropertySet"): for pset in self.file.by_type("IfcPropertySet"):
if pset.Name != "BBIM_Array": if pset.Name != "BBIM_Array":
continue continue
@@ -1144,9 +1151,10 @@ class IfcImporter:
class IfcImportSettings: class IfcImportSettings:
input_file: Union[str, None] = None
logger: Union[logging.Logger, None] = None
def __init__(self): def __init__(self):
self.logger: logging.Logger = None
self.input_file = None
self.diff_file = None self.diff_file = None
self.geometry_library = "opencascade" self.geometry_library = "opencascade"
self.should_use_cpu_multiprocessing = True self.should_use_cpu_multiprocessing = True
@@ -1177,7 +1185,9 @@ class IfcImportSettings:
self.load_indexed_maps = False self.load_indexed_maps = False
@staticmethod @staticmethod
def factory(context=None, input_file=None, logger=None): def factory(
context=None, input_file: Optional[str] = None, logger: Optional[logging.Logger] = None
) -> IfcImportSettings:
scene_diff = tool.Blender.get_diff_props() scene_diff = tool.Blender.get_diff_props()
props = tool.Project.get_project_props() props = tool.Project.get_project_props()
settings = IfcImportSettings() settings = IfcImportSettings()
@@ -58,6 +58,7 @@ from bpy_extras.image_utils import load_image
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.module.project.prop import Link from bonsai.bim.module.project.prop import Link
from bpy._typing import rna_enums
cwd = os.path.dirname(os.path.realpath(__file__)) cwd = os.path.dirname(os.path.realpath(__file__))
@@ -223,6 +224,7 @@ class CreateDrawing(bpy.types.Operator):
) )
drawing_name: str drawing_name: str
is_manifold_cache: dict[str, bool]
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@@ -1225,7 +1227,7 @@ class CreateDrawing(bpy.types.Operator):
) )
return classes return classes
def is_manifold(self, obj): def is_manifold(self, obj) -> bool:
result = self.is_manifold_cache.get(obj.data.name, None) result = self.is_manifold_cache.get(obj.data.name, None)
if result is not None: if result is not None:
return result return result
@@ -2127,14 +2129,14 @@ class ActivateModel(bpy.types.Operator):
class ActivateDrawingBase: class ActivateDrawingBase:
def invoke(self, context, event): def invoke(self, context, event) -> set["rna_enums.OperatorReturnItems"]:
if event.type == "LEFTMOUSE" and event.alt: if event.type == "LEFTMOUSE" and event.alt:
self.should_view_from_camera = False self.should_view_from_camera = False
if event.type == "LEFTMOUSE" and event.shift: if event.type == "LEFTMOUSE" and event.shift:
self.use_quick_preview = True self.use_quick_preview = True
return self.execute(context) return self.execute(context)
def execute(self, context): def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
if props.is_editing_drawings == False: if props.is_editing_drawings == False:
bpy.ops.bim.load_drawings() bpy.ops.bim.load_drawings()
+8 -7
View File
@@ -20,6 +20,7 @@ import bpy
import json import json
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.pset
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.unit import ifcopenshell.util.unit
import bonsai.tool as tool import bonsai.tool as tool
@@ -33,8 +34,9 @@ class AddArray(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
obj = context.active_object assert (obj := context.active_object)
element = tool.Ifc.get_entity(obj) assert (element := tool.Ifc.get_entity(obj))
ifc_file = tool.Ifc.get()
array = { array = {
"children": [], "children": [],
@@ -54,14 +56,13 @@ class AddArray(bpy.types.Operator, tool.Ifc.Operator):
data.append(array) data.append(array)
pset = tool.Ifc.get().by_id(pset["id"]) pset = tool.Ifc.get().by_id(pset["id"])
else: else:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Array") pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="BBIM_Array")
data = [array] data = [array]
ifcopenshell.api.run( ifcopenshell.api.pset.edit_pset(
"pset.edit_pset", ifc_file,
tool.Ifc.get(),
pset=pset, pset=pset,
properties={"Parent": element.GlobalId, "Data": tool.Ifc.get().createIfcText(json.dumps(data))}, properties={"Parent": element.GlobalId, "Data": ifc_file.create_entity("IfcText", json.dumps(data))},
) )
return {"FINISHED"} return {"FINISHED"}
+14 -7
View File
@@ -18,14 +18,17 @@
import bpy import bpy
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.grid
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.core.root import bonsai.core.root
from bpy.types import Operator from bpy.types import Operator
from bpy.props import FloatProperty, IntProperty from bpy.props import FloatProperty, IntProperty
from mathutils import Vector from mathutils import Vector
from typing import TYPE_CHECKING
def add_object(self, context): def add_object(self: "BIM_OT_add_object", context: bpy.types.Context) -> None:
ifc_file = tool.Ifc.get()
obj = bpy.data.objects.new("Grid", None) obj = bpy.data.objects.new("Grid", None)
obj.name = "Grid" obj.name = "Grid"
@@ -33,6 +36,7 @@ def add_object(self, context):
tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcGrid", should_add_representation=False tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcGrid", should_add_representation=False
) )
grid = tool.Ifc.get_entity(obj) grid = tool.Ifc.get_entity(obj)
assert grid
for i in range(0, self.total_u): for i in range(0, self.total_u):
verts = [ verts = [
@@ -46,9 +50,7 @@ def add_object(self, context):
tag = chr(ord("A") + i) tag = chr(ord("A") + i)
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh) obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
result = ifcopenshell.api.run( result = ifcopenshell.api.grid.create_grid_axis(ifc_file, axis_tag=tag, uvw_axes="UAxes", grid=grid)
"grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="UAxes", grid=grid
)
tool.Ifc.link(result, obj) tool.Ifc.link(result, obj)
tool.Model.create_axis_curve(obj, result) tool.Model.create_axis_curve(obj, result)
tool.Collector.assign(obj) tool.Collector.assign(obj)
@@ -65,9 +67,7 @@ def add_object(self, context):
tag = str(i + 1).zfill(2) tag = str(i + 1).zfill(2)
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh) obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
result = ifcopenshell.api.run( result = ifcopenshell.api.grid.create_grid_axis(ifc_file, axis_tag=tag, uvw_axes="VAxes", grid=grid)
"grid.create_grid_axis", tool.Ifc.get(), axis_tag=tag, uvw_axes="VAxes", grid=grid
)
tool.Ifc.link(result, obj) tool.Ifc.link(result, obj)
tool.Model.create_axis_curve(obj, result) tool.Model.create_axis_curve(obj, result)
tool.Collector.assign(obj) tool.Collector.assign(obj)
@@ -78,6 +78,7 @@ def add_object(self, context):
class BIM_OT_add_object(Operator, tool.Ifc.Operator): class BIM_OT_add_object(Operator, tool.Ifc.Operator):
bl_idname = "mesh.add_grid" bl_idname = "mesh.add_grid"
bl_label = "Grid" bl_label = "Grid"
bl_description = "Add IfcGrid."
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
u_spacing: FloatProperty(name="U Spacing", default=10) u_spacing: FloatProperty(name="U Spacing", default=10)
@@ -85,6 +86,12 @@ class BIM_OT_add_object(Operator, tool.Ifc.Operator):
v_spacing: FloatProperty(name="V Spacing", default=10) v_spacing: FloatProperty(name="V Spacing", default=10)
total_v: IntProperty(name="Number of V Grids", default=3) total_v: IntProperty(name="Number of V Grids", default=3)
if TYPE_CHECKING:
u_spacing: float
total_u: int
v_spacing: float
total_v: int
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return tool.Ifc.get() and context.mode == "OBJECT" return tool.Ifc.get() and context.mode == "OBJECT"
+1
View File
@@ -2123,6 +2123,7 @@ class Model(bonsai.core.tool.Model):
@classmethod @classmethod
def create_axis_curve(cls, obj: bpy.types.Object, grid_axis: ifcopenshell.entity_instance) -> None: def create_axis_curve(cls, obj: bpy.types.Object, grid_axis: ifcopenshell.entity_instance) -> None:
m = tool.Surveyor.get_absolute_matrix(obj) m = tool.Surveyor.get_absolute_matrix(obj)
assert isinstance(obj.data, bpy.types.Mesh)
points = [m @ np.array(v.co.to_4d()) for v in obj.data.vertices[0:2]] points = [m @ np.array(v.co.to_4d()) for v in obj.data.vertices[0:2]]
ifcopenshell.api.grid.create_axis_curve( ifcopenshell.api.grid.create_axis_curve(
tool.Ifc.get(), p1=points[0], p2=points[1], is_si=True, grid_axis=grid_axis tool.Ifc.get(), p1=points[0], p2=points[1], is_si=True, grid_axis=grid_axis
@@ -69,10 +69,10 @@ def create_axis_curve(
grid = [i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")][0] grid = [i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")][0]
grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement)) grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement))
grid_axis.AxisCurve = file.createIfcPolyline( grid_axis.AxisCurve = file.create_entity(
( "IfcPolyline"(
file.createIfcCartesianPoint(ifc_safe_vector_type(grid_matrix_i @ p1)), file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(grid_matrix_i @ p1)),
file.createIfcCartesianPoint(ifc_safe_vector_type(grid_matrix_i @ p2)), file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(grid_matrix_i @ p2)),
) )
) )
@@ -50,20 +50,15 @@ def create_grid_axis(
:param axis_tag: The name of the axis, that would typically be labeled :param axis_tag: The name of the axis, that would typically be labeled
on drawings or described on site during coordination, such as A, B, on drawings or described on site during coordination, such as A, B,
C, 1, 2, 3, etc. Defaults to "A". C, 1, 2, 3, etc. Defaults to "A".
:type axis_tag: str, optional
:param same_sense: Determines whether the direction of the axis's line :param same_sense: Determines whether the direction of the axis's line
is reversed. True means the direction the geometry is defined in is reversed. True means the direction the geometry is defined in
represents the direction of the axis. False means the direction is represents the direction of the axis. False means the direction is
reversed. Leave as True if unsure. Defaults to "True". reversed. Leave as True if unsure. Defaults to "True".
:type same_sense: bool, optional
:param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on
which set of axes the new axis you are adding should belong to. which set of axes the new axis you are adding should belong to.
Defaults to "UAxes". Defaults to "UAxes".
:type uvw_axes: str, optional
:param grid: The IfcGrid you are adding the axis to. :param grid: The IfcGrid you are adding the axis to.
:type grid: ifcopenshell.entity_instance
:return: The newly created IfcGridAxis :return: The newly created IfcGridAxis
:rtype: ifcopenshell.entity_instance
Example: Example: