From 743a1975426ad74908ef3df7ad7de051fcd99339 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 16 Jun 2024 22:28:48 +1000 Subject: [PATCH] False origin is always in terms of map coordinates, not as an offset. Previously, the false origin would be in terms of local coordinates. This means that the same false origin would give two different results on files with different map conversions. Now, false origin is ... well, a true map coordinate and works equally for both IFCs with and without a map conversion. --- src/blenderbim/blenderbim/bim/import_ifc.py | 8 +-- .../bim/module/georeference/prop.py | 5 +- .../blenderbim/bim/module/georeference/ui.py | 2 +- src/blenderbim/blenderbim/tool/loader.py | 51 +++++++++++-------- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 6e955cfaef..5f25a89bf7 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -539,7 +539,7 @@ class IfcImporter: elif self.ifc_import_settings.false_origin_mode == "DISABLED": return elif self.ifc_import_settings.false_origin_mode == "MANUAL": - return tool.Loader.set_manual_blender_offset() + return tool.Loader.set_manual_blender_offset(self.file) return tool.Loader.guess_false_origin(self.file) def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix: @@ -569,9 +569,9 @@ class IfcImporter: obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT" matrix = ifcopenshell.util.geolocation.global2local( matrix, - float(props.blender_eastings) * self.unit_scale, - float(props.blender_northings) * self.unit_scale, - float(props.blender_orthogonal_height) * self.unit_scale, + float(props.blender_offset_x) * self.unit_scale, + float(props.blender_offset_y) * self.unit_scale, + float(props.blender_offset_z) * self.unit_scale, float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate), ) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/prop.py b/src/blenderbim/blenderbim/bim/module/georeference/prop.py index efac699ed3..c845914740 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/prop.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/prop.py @@ -17,7 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy -from blenderbim.bim.prop import StrProperty, Attribute +from blenderbim.bim.prop import Attribute from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -49,6 +49,9 @@ class BIMGeoreferenceProperties(PropertyGroup): blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0") blender_x_axis_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1") blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0") + blender_offset_x: StringProperty(name="Blender Offset X", default="0") + blender_offset_y: StringProperty(name="Blender Offset Y", default="0") + blender_offset_z: StringProperty(name="Blender Offset Z", default="0") has_true_north: BoolProperty(name="Has True North", default=True) true_north_abscissa: StringProperty(name="True North Abscissa") true_north_ordinate: StringProperty(name="True North Ordinate") diff --git a/src/blenderbim/blenderbim/bim/module/georeference/ui.py b/src/blenderbim/blenderbim/bim/module/georeference/ui.py index abae08e25f..d2fc14c4fc 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/ui.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/ui.py @@ -90,7 +90,7 @@ class BIM_PT_gis(Panel): if props.has_blender_offset: row = self.layout.row() - row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS") + row.label(text="Blender Session Origin", icon="TRACKING_REFINE_FORWARDS") row = self.layout.row(align=True) row.label(text="Eastings") diff --git a/src/blenderbim/blenderbim/tool/loader.py b/src/blenderbim/blenderbim/tool/loader.py index c29bc26867..2d21db661e 100644 --- a/src/blenderbim/blenderbim/tool/loader.py +++ b/src/blenderbim/blenderbim/tool/loader.py @@ -555,24 +555,33 @@ class Loader(blenderbim.core.tool.Loader): return results @classmethod - def set_manual_blender_offset(cls) -> None: - props = bpy.context.scene.BIMGeoreferenceProperties - props.blender_eastings = str(cls.settings.false_origin[0]) - props.blender_northings = str(cls.settings.false_origin[1]) - props.blender_orthogonal_height = str(cls.settings.false_origin[2]) - props.has_blender_offset = True + def set_manual_blender_offset(cls, ifc_file: ifcopenshell.file) -> None: + model_origin = np.array(ifcopenshell.util.geolocation.auto_xyz2enh(ifc_file, 0, 0, 0)) + false_origin = np.array(cls.settings.false_origin) + model_offset = false_origin - model_origin + zero_origin = np.array((0, 0, 0)) + has_model_offset = not np.allclose(model_offset, zero_origin) + if has_model_offset: + props = bpy.context.scene.BIMGeoreferenceProperties + props.blender_offset_x = str(model_offset[0]) + props.blender_offset_y = str(model_offset[1]) + props.blender_offset_z = str(model_offset[2]) + props.blender_eastings = str(false_origin[0]) + props.blender_northings = str(false_origin[1]) + props.blender_orthogonal_height = str(false_origin[2]) + props.has_blender_offset = True @classmethod - def guess_false_origin_and_project_north(cls, element: ifcopenshell.entity_instance) -> None: + def guess_false_origin_and_project_north(cls, ifc_file: ifcopenshell.file, 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) + offset_point = [placement[0][3], placement[1][3], placement[2][3]] + cls.settings.false_origin = ifcopenshell.util.geolocation.auto_xyz2enh(ifc_file, *offset_point) + cls.set_manual_blender_offset(ifc_file) props = bpy.context.scene.BIMGeoreferenceProperties - props.blender_eastings = str(placement[0][3]) - props.blender_northings = str(placement[1][3]) - props.blender_orthogonal_height = str(placement[2][3]) - x_axis = mathutils.Vector(placement[:, 0][0:3]) - default_x_axis = mathutils.Vector((1, 0, 0)) + x_axis = Vector(placement[:, 0][0:3]) + default_x_axis = Vector((1, 0, 0)) if (default_x_axis - x_axis).length > 0.01: props.blender_x_axis_abscissa = str(placement[0][0]) props.blender_x_axis_ordinate = str(placement[1][0]) @@ -622,9 +631,10 @@ class Loader(blenderbim.core.tool.Loader): elements_checked += 1 mat = ifcopenshell.util.shape.get_shape_matrix(shape) point = mat @ np.array((shape.geometry.verts[0], shape.geometry.verts[1], shape.geometry.verts[2], 1.0)) - point = point / cls.unit_scale - if cls.is_point_far_away(point, is_meters=False): - return point + if cls.is_point_far_away(point, is_meters=True): + # Arbitrary origins should be to the nearest millimeter. + # Anything more precise is just ridiculous from a practical surveying perspective. + return [round(float(p), 3) / cls.unit_scale for p in point[:3]] @classmethod def guess_false_origin_from_elements(cls, ifc_file: ifcopenshell.file) -> None: @@ -635,11 +645,8 @@ class Loader(blenderbim.core.tool.Loader): offset_point = cls.get_offset_point(ifc_file) if offset_point is None: return - props = bpy.context.scene.BIMGeoreferenceProperties - props.blender_eastings = str(offset_point[0]) - props.blender_northings = str(offset_point[1]) - props.blender_orthogonal_height = str(offset_point[2]) - props.has_blender_offset = True + cls.settings.false_origin = ifcopenshell.util.geolocation.auto_xyz2enh(ifc_file, *offset_point) + cls.set_manual_blender_offset(ifc_file) @classmethod def guess_false_origin(cls, ifc_file: ifcopenshell.file) -> None: @@ -649,8 +656,8 @@ class Loader(blenderbim.core.tool.Loader): project = ifc_file.by_type("IfcContext")[0] site = cls.find_decomposed_ifc_class(project, "IfcSite") if site and cls.is_element_far_away(site): - return cls.guess_false_origin_and_project_north(site) + return cls.guess_false_origin_and_project_north(ifc_file, site) building = cls.find_decomposed_ifc_class(project, "IfcBuilding") if building and cls.is_element_far_away(building): - return cls.guess_false_origin_and_project_north(building) + return cls.guess_false_origin_and_project_north(ifc_file, building) return cls.guess_false_origin_from_elements(ifc_file)