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)
def profile_code(self, message):
def profile_code(self, message: str) -> None:
if not self.time:
self.time = time.time()
print("{} :: {:.2f}".format(message, time.time() - self.time))
self.time = time.time()
self.update_progress(self.progress + 1)
def update_progress(self, progress):
def update_progress(self, progress: float) -> None:
if progress <= 100:
self.progress = progress
bpy.context.window_manager.progress_update(self.progress)
def execute(self):
def execute(self) -> None:
bpy.context.window_manager.progress_begin(0, 100)
self.profile_code("Starting import process")
self.load_file()
@@ -324,7 +324,7 @@ class IfcImporter:
coords = getattr(point, "Coordinates", point)
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
# See https://github.com/Autodesk/revit-ifc/issues/187
type_priority = ["Model", "Plan", "Annotation"]
@@ -412,7 +412,7 @@ class IfcImporter:
settings.set_context_ids([context.id()])
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_limit = offset + self.ifc_import_settings.element_limit
@@ -479,7 +479,7 @@ class IfcImporter:
break
return results
def parse_native_elements(self):
def parse_native_elements(self) -> None:
if not self.ifc_import_settings.should_load_geometry:
return
for element in self.elements:
@@ -487,13 +487,13 @@ class IfcImporter:
self.native_elements.add(element)
self.elements -= self.native_elements
def is_native(self, element):
def is_native(self, element: ifcopenshell.entity_instance) -> bool:
if (
not element.Representation
or not element.Representation.Representations
or getattr(element, "HasOpenings", None)
):
return
return False
representation = None
representation_priority = None
@@ -508,7 +508,7 @@ class IfcImporter:
context = rep.ContextOfItems
if not representation:
return
return False
matrix = np.eye(4)
representation_id = None
@@ -566,8 +566,11 @@ class IfcImporter:
"type": "IfcFaceBasedSurfaceModel",
}
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)]
if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"):
if tool.Blender.Modifier.is_railing(element):
@@ -583,20 +586,20 @@ class IfcImporter:
return True
return False
def is_native_faceted_brep(self, representation):
def is_native_faceted_brep(self, representation: ifcopenshell.entity_instance) -> bool:
# TODO handle mapped items
for i in representation.Items:
if i.is_a() != "IfcFacetedBrep":
return False
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:
if i.is_a() != "IfcFaceBasedSurfaceModel":
return False
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]
for rep_map in element.RepresentationMap:
for usage in rep_map.MapUsage:
@@ -605,7 +608,7 @@ class IfcImporter:
products.extend(self.get_products_from_shape_representation(inverse_element))
return products
def predict_dense_mesh(self):
def predict_dense_mesh(self) -> None:
if self.ifc_import_settings.should_use_native_meshes:
return
@@ -632,7 +635,7 @@ class IfcImporter:
if faces and max(faces) > threshold:
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
if props.has_blender_offset:
return
@@ -650,14 +653,14 @@ class IfcImporter:
return self.guess_false_origin_and_project_north(building)
return self.guess_false_origin()
def set_manual_blender_offset(self):
def set_manual_blender_offset(self) -> None:
props = bpy.context.scene.BIMGeoreferenceProperties
props.blender_eastings = str(self.ifc_import_settings.false_origin[0])
props.blender_northings = str(self.ifc_import_settings.false_origin[1])
props.blender_orthogonal_height = str(self.ifc_import_settings.false_origin[2])
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"):
return
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.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
# ObjectPlacement is usually 0,0,0 (but not always, so we'll need to
# check for the actual transformation) but each individual coordinate of
@@ -1909,13 +1912,17 @@ class IfcImporter:
polyline.points[-1].co = mathutils.Vector(v2)
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:
if hasattr(shape, "geometry"):
# shape is ifcopenshell_wrapper.TriangulationElement
geometry: ifcopenshell_wrapper.Triangulation = shape.geometry
geometry = shape.geometry
else:
geometry: ifcopenshell_wrapper.Triangulation = shape
geometry = shape
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry))
+5 -1
View File
@@ -19,6 +19,7 @@
import re
import bpy
import bmesh
import ifcopenshell.geom
import ifcopenshell.util.element
import blenderbim.core.tool
import blenderbim.tool as tool
@@ -55,9 +56,12 @@ class Loader(blenderbim.core.tool.Loader):
return collection
@classmethod
def get_mesh_name(cls, geometry) -> str:
def get_mesh_name(cls, geometry: ifcopenshell.geom.ShapeType) -> str:
representation_id = geometry.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]))
else:
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())
def __getattr__(self, name):
def __getattr__(self, name: str) -> Any:
INVALID, FORWARD, INVERSE = range(3)
attr_cat = self.wrapped_data.get_attribute_category(name)
if attr_cat == FORWARD:
@@ -27,9 +27,13 @@ from ..entity_instance import entity_instance
from . import has_occ
from typing import TypeVar, Union, Optional
from typing import TypeVar, Union, Optional, Generator
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):
@@ -122,7 +126,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
def 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():
while True:
yield self.get()
@@ -738,7 +738,7 @@ def get_elements_by_style(
def get_elements_by_representation(
ifc_file: ifcopenshell.file, representation: ifcopenshell.entity_instance
) -> list[ifcopenshell.entity_instance]:
) -> set[ifcopenshell.entity_instance]:
"""Gets all elements using a geometric representation
:param ifc_file: The IFC file
@@ -746,7 +746,7 @@ def get_elements_by_representation(
:param representation: The IfcShapeRepresentation representation
:type representation: ifcopenshell.entity_instance
:return: The elements using the geometric representation
:rtype: list[ifcopenshell.entity_instance]
:rtype: set[ifcopenshell.entity_instance]
Example:
@@ -26,7 +26,7 @@ import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.unit
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 mathutils import Vector, Matrix
@@ -304,9 +304,9 @@ class ShapeBuilder:
x_axis_radius: float,
y_axis_radius: float,
position=Vector((0.0, 0.0)).freeze(),
trim_points: list[Vector] = (),
trim_points: Sequence[Vector] = (),
ref_x_direction: Vector = Vector((1.0, 0.0)),
trim_points_mask: list[int] = (),
trim_points_mask: Sequence[int] = (),
) -> ifcopenshell.entity_instance:
"""
Ellipse trimming points should be specified in counter clockwise order.
@@ -345,7 +345,7 @@ class ShapeBuilder:
self,
outer_curve: ifcopenshell.entity_instance,
name: Optional[str] = None,
inner_curves: list[ifcopenshell.entity_instance] = (),
inner_curves: Sequence[ifcopenshell.entity_instance] = (),
profile_type: str = "AREA",
) -> ifcopenshell.entity_instance:
# > inner_curves - list of IfcCurve;
@@ -882,8 +882,8 @@ class ShapeBuilder:
def get_simple_2dcurve_data(
self,
coords: list[Vector],
fillets: list[int] = (),
fillet_radius: list[float] = (),
fillets: Sequence[int] = (),
fillet_radius: Sequence[float] = (),
closed: bool = True,
create_ifc_curve: bool = False,
) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]: