This commit is contained in:
Andrej730
2024-06-04 13:50:00 +05:00
parent 61c2a2c6c6
commit db98f13982
6 changed files with 48 additions and 33 deletions
+28 -21
View File
@@ -232,19 +232,19 @@ class IfcImporter:
self.material_creator = MaterialCreator(ifc_import_settings, self) self.material_creator = MaterialCreator(ifc_import_settings, self)
def profile_code(self, message): def profile_code(self, message: str) -> None:
if not self.time: if not self.time:
self.time = time.time() self.time = time.time()
print("{} :: {:.2f}".format(message, time.time() - self.time)) print("{} :: {:.2f}".format(message, time.time() - self.time))
self.time = time.time() self.time = time.time()
self.update_progress(self.progress + 1) self.update_progress(self.progress + 1)
def update_progress(self, progress): def update_progress(self, progress: float) -> None:
if progress <= 100: if progress <= 100:
self.progress = progress self.progress = progress
bpy.context.window_manager.progress_update(self.progress) bpy.context.window_manager.progress_update(self.progress)
def execute(self): def execute(self) -> None:
bpy.context.window_manager.progress_begin(0, 100) bpy.context.window_manager.progress_begin(0, 100)
self.profile_code("Starting import process") self.profile_code("Starting import process")
self.load_file() self.load_file()
@@ -324,7 +324,7 @@ class IfcImporter:
coords = getattr(point, "Coordinates", point) coords = getattr(point, "Coordinates", point)
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
def process_context_filter(self): def process_context_filter(self) -> None:
# Annotation ContextType is to accommodate broken Revit files # Annotation ContextType is to accommodate broken Revit files
# See https://github.com/Autodesk/revit-ifc/issues/187 # See https://github.com/Autodesk/revit-ifc/issues/187
type_priority = ["Model", "Plan", "Annotation"] type_priority = ["Model", "Plan", "Annotation"]
@@ -412,7 +412,7 @@ class IfcImporter:
settings.set_context_ids([context.id()]) settings.set_context_ids([context.id()])
self.gross_context_settings.append(settings) self.gross_context_settings.append(settings)
def process_element_filter(self): def process_element_filter(self) -> None:
offset = self.ifc_import_settings.element_offset offset = self.ifc_import_settings.element_offset
offset_limit = offset + self.ifc_import_settings.element_limit offset_limit = offset + self.ifc_import_settings.element_limit
@@ -479,7 +479,7 @@ class IfcImporter:
break break
return results return results
def parse_native_elements(self): def parse_native_elements(self) -> None:
if not self.ifc_import_settings.should_load_geometry: if not self.ifc_import_settings.should_load_geometry:
return return
for element in self.elements: for element in self.elements:
@@ -487,13 +487,13 @@ class IfcImporter:
self.native_elements.add(element) self.native_elements.add(element)
self.elements -= self.native_elements self.elements -= self.native_elements
def is_native(self, element): def is_native(self, element: ifcopenshell.entity_instance) -> bool:
if ( if (
not element.Representation not element.Representation
or not element.Representation.Representations or not element.Representation.Representations
or getattr(element, "HasOpenings", None) or getattr(element, "HasOpenings", None)
): ):
return return False
representation = None representation = None
representation_priority = None representation_priority = None
@@ -508,7 +508,7 @@ class IfcImporter:
context = rep.ContextOfItems context = rep.ContextOfItems
if not representation: if not representation:
return return False
matrix = np.eye(4) matrix = np.eye(4)
representation_id = None representation_id = None
@@ -566,8 +566,11 @@ class IfcImporter:
"type": "IfcFaceBasedSurfaceModel", "type": "IfcFaceBasedSurfaceModel",
} }
return True return True
return False
def is_native_swept_disk_solid(self, element, representation): def is_native_swept_disk_solid(
self, element: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
) -> bool:
items = [i["item"] for i in ifcopenshell.util.representation.resolve_items(representation)] items = [i["item"] for i in ifcopenshell.util.representation.resolve_items(representation)]
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"): if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
if tool.Blender.Modifier.is_railing(element): if tool.Blender.Modifier.is_railing(element):
@@ -583,20 +586,20 @@ class IfcImporter:
return True return True
return False return False
def is_native_faceted_brep(self, representation): def is_native_faceted_brep(self, representation: ifcopenshell.entity_instance) -> bool:
# TODO handle mapped items # TODO handle mapped items
for i in representation.Items: for i in representation.Items:
if i.is_a() != "IfcFacetedBrep": if i.is_a() != "IfcFacetedBrep":
return False return False
return True return True
def is_native_face_based_surface_model(self, representation): def is_native_face_based_surface_model(self, representation: ifcopenshell.entity_instance) -> bool:
for i in representation.Items: for i in representation.Items:
if i.is_a() != "IfcFaceBasedSurfaceModel": if i.is_a() != "IfcFaceBasedSurfaceModel":
return False return False
return True return True
def get_products_from_shape_representation(self, element): def get_products_from_shape_representation(self, element: ifcopenshell.entity_instance) -> None:
products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation] products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation]
for rep_map in element.RepresentationMap: for rep_map in element.RepresentationMap:
for usage in rep_map.MapUsage: for usage in rep_map.MapUsage:
@@ -605,7 +608,7 @@ class IfcImporter:
products.extend(self.get_products_from_shape_representation(inverse_element)) products.extend(self.get_products_from_shape_representation(inverse_element))
return products return products
def predict_dense_mesh(self): def predict_dense_mesh(self) -> None:
if self.ifc_import_settings.should_use_native_meshes: if self.ifc_import_settings.should_use_native_meshes:
return return
@@ -632,7 +635,7 @@ class IfcImporter:
if faces and max(faces) > threshold: if faces and max(faces) > threshold:
self.ifc_import_settings.should_use_native_meshes = True self.ifc_import_settings.should_use_native_meshes = True
def calculate_model_offset(self): def calculate_model_offset(self) -> None:
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset: if props.has_blender_offset:
return return
@@ -650,14 +653,14 @@ class IfcImporter:
return self.guess_false_origin_and_project_north(building) return self.guess_false_origin_and_project_north(building)
return self.guess_false_origin() return self.guess_false_origin()
def set_manual_blender_offset(self): def set_manual_blender_offset(self) -> None:
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
props.blender_eastings = str(self.ifc_import_settings.false_origin[0]) props.blender_eastings = str(self.ifc_import_settings.false_origin[0])
props.blender_northings = str(self.ifc_import_settings.false_origin[1]) props.blender_northings = str(self.ifc_import_settings.false_origin[1])
props.blender_orthogonal_height = str(self.ifc_import_settings.false_origin[2]) props.blender_orthogonal_height = str(self.ifc_import_settings.false_origin[2])
props.has_blender_offset = True props.has_blender_offset = True
def guess_false_origin_and_project_north(self, element): def guess_false_origin_and_project_north(self, element: ifcopenshell.entity_instance) -> None:
if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"): if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"):
return return
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
@@ -672,7 +675,7 @@ class IfcImporter:
props.blender_x_axis_ordinate = str(placement[1][0]) props.blender_x_axis_ordinate = str(placement[1][0])
props.has_blender_offset = True props.has_blender_offset = True
def guess_false_origin(self): def guess_false_origin(self) -> None:
# Civil BIM applications like to work in absolute coordinates, where the # Civil BIM applications like to work in absolute coordinates, where the
# ObjectPlacement is usually 0,0,0 (but not always, so we'll need to # ObjectPlacement is usually 0,0,0 (but not always, so we'll need to
# check for the actual transformation) but each individual coordinate of # check for the actual transformation) but each individual coordinate of
@@ -1909,13 +1912,17 @@ class IfcImporter:
polyline.points[-1].co = mathutils.Vector(v2) polyline.points[-1].co = mathutils.Vector(v2)
return curve return curve
def create_mesh(self, element: ifcopenshell.entity_instance, shape) -> bpy.types.Mesh: def create_mesh(
self,
element: ifcopenshell.entity_instance,
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
) -> bpy.types.Mesh:
try: try:
if hasattr(shape, "geometry"): if hasattr(shape, "geometry"):
# shape is ifcopenshell_wrapper.TriangulationElement # shape is ifcopenshell_wrapper.TriangulationElement
geometry: ifcopenshell_wrapper.Triangulation = shape.geometry geometry = shape.geometry
else: else:
geometry: ifcopenshell_wrapper.Triangulation = shape geometry = shape
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry)) mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry))
+5 -1
View File
@@ -19,6 +19,7 @@
import re import re
import bpy import bpy
import bmesh import bmesh
import ifcopenshell.geom
import ifcopenshell.util.element import ifcopenshell.util.element
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
@@ -55,9 +56,12 @@ class Loader(blenderbim.core.tool.Loader):
return collection return collection
@classmethod @classmethod
def get_mesh_name(cls, geometry) -> str: def get_mesh_name(cls, geometry: ifcopenshell.geom.ShapeType) -> str:
representation_id = geometry.id representation_id = geometry.id
if "-" in representation_id: if "-" in representation_id:
# Example: 2432-openings-2468, where
# 2432 is mapped representation id
# and 2468 is IFCRELVOIDSELEMENT
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0])) representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else: else:
representation_id = int(re.sub(r"\D", "", representation_id)) representation_id = int(re.sub(r"\D", "", representation_id))
@@ -169,7 +169,7 @@ class entity_instance:
return file.from_pointer(self.wrapped_data.file_pointer()) return file.from_pointer(self.wrapped_data.file_pointer())
def __getattr__(self, name): def __getattr__(self, name: str) -> Any:
INVALID, FORWARD, INVERSE = range(3) INVALID, FORWARD, INVERSE = range(3)
attr_cat = self.wrapped_data.get_attribute_category(name) attr_cat = self.wrapped_data.get_attribute_category(name)
if attr_cat == FORWARD: if attr_cat == FORWARD:
@@ -27,9 +27,13 @@ from ..entity_instance import entity_instance
from . import has_occ from . import has_occ
from typing import TypeVar, Union, Optional from typing import TypeVar, Union, Optional, Generator
T = TypeVar("T") T = TypeVar("T")
ShapeElementType = Union[
ifcopenshell_wrapper.BRepElement, ifcopenshell_wrapper.TriangulationElement, ifcopenshell_wrapper.SerializedElement
]
ShapeType = Union[ifcopenshell_wrapper.BRep, ifcopenshell_wrapper.Triangulation, ifcopenshell_wrapper.Serialization]
def wrap_shape_creation(settings, shape): def wrap_shape_creation(settings, shape):
@@ -122,7 +126,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
def get(self): def get(self):
return wrap_shape_creation(self.settings, ifcopenshell_wrapper.Iterator.get(self)) return wrap_shape_creation(self.settings, ifcopenshell_wrapper.Iterator.get(self))
def __iter__(self): def __iter__(self) -> Generator[ShapeElementType, None, None]:
if self.initialize(): if self.initialize():
while True: while True:
yield self.get() yield self.get()
@@ -738,7 +738,7 @@ def get_elements_by_style(
def get_elements_by_representation( def get_elements_by_representation(
ifc_file: ifcopenshell.file, representation: ifcopenshell.entity_instance ifc_file: ifcopenshell.file, representation: ifcopenshell.entity_instance
) -> list[ifcopenshell.entity_instance]: ) -> set[ifcopenshell.entity_instance]:
"""Gets all elements using a geometric representation """Gets all elements using a geometric representation
:param ifc_file: The IFC file :param ifc_file: The IFC file
@@ -746,7 +746,7 @@ def get_elements_by_representation(
:param representation: The IfcShapeRepresentation representation :param representation: The IfcShapeRepresentation representation
:type representation: ifcopenshell.entity_instance :type representation: ifcopenshell.entity_instance
:return: The elements using the geometric representation :return: The elements using the geometric representation
:rtype: list[ifcopenshell.entity_instance] :rtype: set[ifcopenshell.entity_instance]
Example: Example:
@@ -26,7 +26,7 @@ import ifcopenshell.util.element
import ifcopenshell.util.representation import ifcopenshell.util.representation
import ifcopenshell.util.unit import ifcopenshell.util.unit
from math import cos, sin, pi, tan, radians, degrees, atan, sqrt, ceil from math import cos, sin, pi, tan, radians, degrees, atan, sqrt, ceil
from typing import Union, Optional, Literal, Any from typing import Union, Optional, Literal, Any, Sequence
from itertools import chain from itertools import chain
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
@@ -304,9 +304,9 @@ class ShapeBuilder:
x_axis_radius: float, x_axis_radius: float,
y_axis_radius: float, y_axis_radius: float,
position=Vector((0.0, 0.0)).freeze(), position=Vector((0.0, 0.0)).freeze(),
trim_points: list[Vector] = (), trim_points: Sequence[Vector] = (),
ref_x_direction: Vector = Vector((1.0, 0.0)), ref_x_direction: Vector = Vector((1.0, 0.0)),
trim_points_mask: list[int] = (), trim_points_mask: Sequence[int] = (),
) -> ifcopenshell.entity_instance: ) -> ifcopenshell.entity_instance:
""" """
Ellipse trimming points should be specified in counter clockwise order. Ellipse trimming points should be specified in counter clockwise order.
@@ -345,7 +345,7 @@ class ShapeBuilder:
self, self,
outer_curve: ifcopenshell.entity_instance, outer_curve: ifcopenshell.entity_instance,
name: Optional[str] = None, name: Optional[str] = None,
inner_curves: list[ifcopenshell.entity_instance] = (), inner_curves: Sequence[ifcopenshell.entity_instance] = (),
profile_type: str = "AREA", profile_type: str = "AREA",
) -> ifcopenshell.entity_instance: ) -> ifcopenshell.entity_instance:
# > inner_curves - list of IfcCurve; # > inner_curves - list of IfcCurve;
@@ -882,8 +882,8 @@ class ShapeBuilder:
def get_simple_2dcurve_data( def get_simple_2dcurve_data(
self, self,
coords: list[Vector], coords: list[Vector],
fillets: list[int] = (), fillets: Sequence[int] = (),
fillet_radius: list[float] = (), fillet_radius: Sequence[float] = (),
closed: bool = True, closed: bool = True,
create_ifc_curve: bool = False, create_ifc_curve: bool = False,
) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]: ) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]: