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.
This commit is contained in:
Dion Moult
2024-06-16 22:28:48 +10:00
parent 99f329c13d
commit 743a197542
4 changed files with 38 additions and 28 deletions
+4 -4
View File
@@ -539,7 +539,7 @@ class IfcImporter:
elif self.ifc_import_settings.false_origin_mode == "DISABLED": elif self.ifc_import_settings.false_origin_mode == "DISABLED":
return return
elif self.ifc_import_settings.false_origin_mode == "MANUAL": 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) 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: 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" obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
matrix = ifcopenshell.util.geolocation.global2local( matrix = ifcopenshell.util.geolocation.global2local(
matrix, matrix,
float(props.blender_eastings) * self.unit_scale, float(props.blender_offset_x) * self.unit_scale,
float(props.blender_northings) * self.unit_scale, float(props.blender_offset_y) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale, float(props.blender_offset_z) * self.unit_scale,
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate), float(props.blender_x_axis_ordinate),
) )
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -49,6 +49,9 @@ class BIMGeoreferenceProperties(PropertyGroup):
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0") 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_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1")
blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0") 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) has_true_north: BoolProperty(name="Has True North", default=True)
true_north_abscissa: StringProperty(name="True North Abscissa") true_north_abscissa: StringProperty(name="True North Abscissa")
true_north_ordinate: StringProperty(name="True North Ordinate") true_north_ordinate: StringProperty(name="True North Ordinate")
@@ -90,7 +90,7 @@ class BIM_PT_gis(Panel):
if props.has_blender_offset: if props.has_blender_offset:
row = self.layout.row() 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 = self.layout.row(align=True)
row.label(text="Eastings") row.label(text="Eastings")
+29 -22
View File
@@ -555,24 +555,33 @@ class Loader(blenderbim.core.tool.Loader):
return results return results
@classmethod @classmethod
def set_manual_blender_offset(cls) -> None: def set_manual_blender_offset(cls, ifc_file: ifcopenshell.file) -> None:
props = bpy.context.scene.BIMGeoreferenceProperties model_origin = np.array(ifcopenshell.util.geolocation.auto_xyz2enh(ifc_file, 0, 0, 0))
props.blender_eastings = str(cls.settings.false_origin[0]) false_origin = np.array(cls.settings.false_origin)
props.blender_northings = str(cls.settings.false_origin[1]) model_offset = false_origin - model_origin
props.blender_orthogonal_height = str(cls.settings.false_origin[2]) zero_origin = np.array((0, 0, 0))
props.has_blender_offset = True 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 @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"): 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)
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 = bpy.context.scene.BIMGeoreferenceProperties
props.blender_eastings = str(placement[0][3]) x_axis = Vector(placement[:, 0][0:3])
props.blender_northings = str(placement[1][3]) default_x_axis = Vector((1, 0, 0))
props.blender_orthogonal_height = str(placement[2][3])
x_axis = mathutils.Vector(placement[:, 0][0:3])
default_x_axis = mathutils.Vector((1, 0, 0))
if (default_x_axis - x_axis).length > 0.01: if (default_x_axis - x_axis).length > 0.01:
props.blender_x_axis_abscissa = str(placement[0][0]) props.blender_x_axis_abscissa = str(placement[0][0])
props.blender_x_axis_ordinate = str(placement[1][0]) props.blender_x_axis_ordinate = str(placement[1][0])
@@ -622,9 +631,10 @@ class Loader(blenderbim.core.tool.Loader):
elements_checked += 1 elements_checked += 1
mat = ifcopenshell.util.shape.get_shape_matrix(shape) 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 = 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=True):
if cls.is_point_far_away(point, is_meters=False): # Arbitrary origins should be to the nearest millimeter.
return point # 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 @classmethod
def guess_false_origin_from_elements(cls, ifc_file: ifcopenshell.file) -> None: 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) offset_point = cls.get_offset_point(ifc_file)
if offset_point is None: if offset_point is None:
return return
props = bpy.context.scene.BIMGeoreferenceProperties cls.settings.false_origin = ifcopenshell.util.geolocation.auto_xyz2enh(ifc_file, *offset_point)
props.blender_eastings = str(offset_point[0]) cls.set_manual_blender_offset(ifc_file)
props.blender_northings = str(offset_point[1])
props.blender_orthogonal_height = str(offset_point[2])
props.has_blender_offset = True
@classmethod @classmethod
def guess_false_origin(cls, ifc_file: ifcopenshell.file) -> None: 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] project = ifc_file.by_type("IfcContext")[0]
site = cls.find_decomposed_ifc_class(project, "IfcSite") site = cls.find_decomposed_ifc_class(project, "IfcSite")
if site and cls.is_element_far_away(site): 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") building = cls.find_decomposed_ifc_class(project, "IfcBuilding")
if building and cls.is_element_far_away(building): 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) return cls.guess_false_origin_from_elements(ifc_file)