This commit is contained in:
Andrej730
2024-05-16 16:27:41 +05:00
parent 8d4b5d83e1
commit 3884403231
10 changed files with 89 additions and 46 deletions
+11 -9
View File
@@ -26,6 +26,7 @@ import bmesh
import logging
import mathutils
import numpy as np
import numpy.typing as npt
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
@@ -303,22 +304,22 @@ class IfcImporter:
self.update_progress(100)
bpy.context.window_manager.progress_end()
def is_element_far_away(self, element):
def is_element_far_away(self, element: ifcopenshell.entity_instance) -> bool:
try:
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
point = placement[:, 3][0:3]
return self.is_point_far_away(point, is_meters=False)
except:
pass
return False
def is_point_far_away(self, point, is_meters=True):
def is_point_far_away(
self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]], is_meters: bool = True
) -> bool:
# Locations greater than 1km are not considered "small sites" according to the georeferencing guide
# Users can configure this if they have to handle larger sites but beware of surveying precision
limit = self.ifc_import_settings.distance_limit
limit = limit if is_meters else (limit / self.unit_scale)
coords = point
if hasattr(point, "Coordinates"):
coords = point.Coordinates
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):
@@ -683,7 +684,7 @@ class IfcImporter:
props.blender_orthogonal_height = str(offset_point[2])
props.has_blender_offset = True
def get_offset_point(self):
def get_offset_point(self) -> Union[npt.NDArray[np.float64], None]:
elements_checked = 0
# If more than these elements aren't far away, the file probably isn't absolutely positioned
element_checking_threshold = 10
@@ -718,7 +719,7 @@ class IfcImporter:
if self.is_point_far_away(point, is_meters=False):
return point
def does_element_likely_have_geometry_far_away(self, element):
def does_element_likely_have_geometry_far_away(self, element: ifcopenshell.entity_instance) -> bool:
for representation in element.Representation.Representations:
items = []
for item in representation.Items:
@@ -735,13 +736,14 @@ class IfcImporter:
if subelement.is_a("IfcCartesianPoint"):
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
return True
return False
def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset:
if obj.data and obj.data.get("has_cartesian_point_offset", None):
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
elif self.is_point_far_away((matrix[0, 3], matrix[1, 3], matrix[2, 3])):
elif self.is_point_far_away((matrix[:3, 3])):
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
matrix = ifcopenshell.util.geolocation.global2local(
matrix,
@@ -23,6 +23,7 @@ import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.misc as core
import blenderbim.core.geometry as core_geometry
import blenderbim.core.root
from blenderbim.bim.ifc import IfcStore
from mathutils import Vector, Matrix, Euler
@@ -144,6 +145,7 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
cutter = context.active_object
objs = [o for o in context.selected_objects if o != cutter]
objs_to_cut = []
# Splitting only works on meshes
for obj in objs:
# You cannot split meshes if the representation is mapped.
@@ -153,7 +155,13 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
if relating_type and tool.Root.does_type_have_representations(relating_type):
bpy.ops.bim.unassign_type(related_object=obj.name)
# refresh representation
representation = tool.Geometry.get_active_representation(obj)
# skip empty objects that might get in the way
if not representation:
continue
core_geometry.switch_representation(
tool.Ifc,
tool.Geometry,
@@ -168,11 +176,13 @@ class SplitAlongEdge(bpy.types.Operator, Operator):
if not tool.Geometry.is_meshlike(representation):
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="IfcTessellatedFaceSet")
new_objs = tool.Misc.split_objects_with_cutter(objs, cutter)
objs_to_cut.append(obj)
new_objs = tool.Misc.split_objects_with_cutter(objs_to_cut, cutter)
for obj in new_objs:
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
bpy.ops.bim.update_representation(obj=obj.name)
for obj in objs:
for obj in objs_to_cut:
bpy.ops.bim.update_representation(obj=obj.name)
representation = tool.Geometry.get_active_representation(obj)
+20 -12
View File
@@ -18,10 +18,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
import blenderbim.core.tool as tool
if TYPE_CHECKING:
import bpy
import ifcopenshell
import blenderbim.tool as tool
def edit_object_placement(
@@ -37,8 +38,15 @@ def edit_object_placement(
def add_representation(
ifc, geometry, style, surveyor, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
):
ifc: tool.Ifc,
geometry: tool.Geometry,
style: tool.Style,
surveyor: tool.Surveyor,
obj: bpy.types.Object,
context: ifcopenshell.entity_instance,
ifc_representation_class: Optional[str] = None,
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
) -> ifcopenshell.entity_instance:
element = ifc.get_entity(obj)
if not element:
return
@@ -89,15 +97,15 @@ def add_representation(
def switch_representation(
ifc,
geometry,
obj=None,
representation=None,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
apply_openings=True,
):
ifc: tool.Ifc,
geometry: tool.Geometry,
obj: bpy.types.Object,
representation: ifcopenshell.entity_instance,
should_reload: bool = True,
is_global: bool = True,
should_sync_changes_first: bool = False,
apply_openings: bool = True,
) -> None:
"""Function can switch to representation that wasn't yet assigned to that object. See #2766.
`should_sync_changes_first` - sync ifc representation with current state of `obj.data`;
+21 -11
View File
@@ -16,8 +16,18 @@
# 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
from typing import TYPE_CHECKING, Optional
def copy_class(ifc, collector, geometry, root, obj=None):
if TYPE_CHECKING:
import bpy
import ifcopenshell
import blenderbim.tool as tool
def copy_class(
ifc: tool.Ifc, collector: tool.Collector, geometry: tool.Geometry, root: tool.Root, obj: bpy.types.Object
) -> ifcopenshell.entity_instance:
element = ifc.get_entity(obj)
if not element:
return
@@ -48,16 +58,16 @@ def copy_class(ifc, collector, geometry, root, obj=None):
def assign_class(
ifc,
collector,
root,
obj=None,
ifc_class=None,
predefined_type=None,
should_add_representation=True,
context=None,
ifc_representation_class=None,
):
ifc: tool.Ifc,
collector: tool.Collector,
root: tool.Root,
obj: bpy.types.Object,
ifc_class: str,
context: ifcopenshell.entity_instance,
predefined_type: Optional[str] = None,
should_add_representation: bool = True,
ifc_representation_class: Optional[str] = None,
) -> ifcopenshell.entity_instance:
if ifc.get_entity(obj):
return
+1 -1
View File
@@ -18,11 +18,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Union
import blenderbim.core.tool as tool
if TYPE_CHECKING:
import bpy
import ifcopenshell
import blenderbim.tool as tool
def reference_structure(
+10 -5
View File
@@ -26,6 +26,7 @@ import ifcopenshell
import ifcopenshell.api
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.system
import blenderbim.core.tool
import blenderbim.core.drawing
@@ -319,7 +320,7 @@ class Geometry(blenderbim.core.tool.Geometry):
return new_mesh
@classmethod
def get_active_representation(cls, obj):
def get_active_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
"""< IfcShapeRepresentation or None"""
if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id:
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
@@ -459,7 +460,9 @@ class Geometry(blenderbim.core.tool.Geometry):
return f"{representation.ContextOfItems.id()}/{representation.id()}"
@classmethod
def get_styles(cls, obj, only_assigned_to_faces=False):
def get_styles(
cls, obj: bpy.types.Object, only_assigned_to_faces: bool = False
) -> list[Union[ifcopenshell.entity_instance, None]]:
styles = [tool.Style.get_style(s.material) for s in obj.material_slots if s.material]
if not only_assigned_to_faces:
return styles
@@ -572,11 +575,11 @@ class Geometry(blenderbim.core.tool.Geometry):
return not all([tool.Cad.is_x(o, 1.0) for o in obj.scale]) or obj in IfcStore.edited_objs
@classmethod
def is_mapped_representation(cls, representation):
def is_mapped_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
return representation.RepresentationType == "MappedRepresentation"
@classmethod
def is_meshlike(cls, representation):
def is_meshlike(cls, representation: ifcopenshell.entity_instance) -> bool:
if ifcopenshell.util.representation.resolve_representation(representation).RepresentationType in (
"AdvancedBrep",
"Annotation2D",
@@ -656,7 +659,9 @@ class Geometry(blenderbim.core.tool.Geometry):
bpy.data.objects.remove(obj)
@classmethod
def resolve_mapped_representation(cls, representation):
def resolve_mapped_representation(
cls, representation: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
if representation.RepresentationType == "MappedRepresentation":
return cls.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
return representation
+3 -1
View File
@@ -97,7 +97,9 @@ class Misc(blenderbim.core.tool.Misc):
IfcStore.edited_objs.add(obj)
@classmethod
def split_objects_with_cutter(cls, objs, cutter):
def split_objects_with_cutter(
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
) -> list[bpy.types.Object]:
cutter_mesh = cutter.data
bm = bmesh.new()
@@ -21,6 +21,7 @@ import bpy
import ifcopenshell
import ifcopenshell.util.unit
import blenderbim.core.tool
import blenderbim.core.root
import blenderbim.bim.schema
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
+8 -3
View File
@@ -29,6 +29,7 @@ import blenderbim.core.style
import blenderbim.tool as tool
from mathutils import Vector
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
from typing import Union, Optional
class Root(blenderbim.core.tool.Root):
@@ -129,7 +130,7 @@ class Root(blenderbim.core.tool.Root):
return ifcopenshell.util.representation.get_representation(element, context=context.ContextType)
@classmethod
def get_element_type(cls, element):
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.element.get_type(element)
@classmethod
@@ -282,8 +283,12 @@ class Root(blenderbim.core.tool.Root):
@classmethod
def run_geometry_add_representation(
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
):
cls,
obj: bpy.types.Object,
context: ifcopenshell.entity_instance,
ifc_representation_class: Optional[str] = None,
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
) -> ifcopenshell.entity_instance:
return blenderbim.core.geometry.add_representation(
tool.Ifc,
tool.Geometry,
@@ -446,13 +446,13 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
return predefined_type
def get_type(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
def get_type(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
"""Retrieves the construction type element of an element occurrence
:param element: The element occurrence
:type: ifcopenshell.entity_instance
:return: The related type element
:rtype: ifcopenshell.entity_instance
:rtype: Union[ifcopenshell.entity_instance, None]
Example: