mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +00:00
Fix #4677. Fix #4809. Major improvement to local session offset during project loading (see description)
This improves four things: 1. Previously, we either used OBJECT_PLACEMENT or CARTESIAN_POINT, but couldn't handle scenarios where simultaneously both the placement and the coords were rubbish for a single object. Now we offset all far cartesian points, so it consistently works and we keep track of a per-object offset. 2. Previously, we applied the georeferencing conversion on every cartesian point which was very slow. The new method uses a simple XYZ translation which is super fast. 3. We now use numpy which should be much faster too. 4. Previously, objects were selectively offset based on whether they fell outside the distance limit. Now, we uniformly treat all non-geometric elements at 0,0,0 as insignificant positionally. This fixes the issue where half the model is offset and the other half isn't, but maintains the fix for situations where the site (typically) is at 0,0,0 and everything else is map coords.
This commit is contained in:
@@ -743,18 +743,29 @@ class IfcImporter:
|
||||
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):
|
||||
if not obj.data and tool.Cad.is_x(matrix[0][3], 0) and tool.Cad.is_x(matrix[1][3], 0) and tool.Cad.is_x(matrix[2][3], 0):
|
||||
# We assume any non-geometric matrix at 0,0,0 is not
|
||||
# positionally significant and is left alone. This handles
|
||||
# scenarios where often spatial elements are left at 0,0,0 and
|
||||
# everything else is at map coordinates.
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
elif 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[:3, 3])):
|
||||
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
|
||||
offset_x, offset_y, offset_z = map(float, cartesian_point_offset.split(","))
|
||||
matrix[0][3] += offset_x
|
||||
matrix[1][3] += offset_y
|
||||
matrix[2][3] += offset_z
|
||||
else:
|
||||
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_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
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_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
|
||||
return mathutils.Matrix(matrix.tolist())
|
||||
|
||||
@@ -1201,7 +1212,6 @@ class IfcImporter:
|
||||
styles.extend(style.Styles)
|
||||
|
||||
def create_native_faceted_brep(self, element, mesh_name, native_data):
|
||||
# TODO: georeferencing?
|
||||
# co [x y z x y z x y z ...]
|
||||
# vertex_index [i i i i i ...]
|
||||
# loop_start [0 3 6 9 ...] (for tris)
|
||||
@@ -1226,45 +1236,27 @@ class IfcImporter:
|
||||
mesh = bpy.data.meshes.new("Native")
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
mat = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
if props.has_blender_offset and self.is_point_far_away(self.mesh_data["co"][0:3], is_meters=False):
|
||||
offset_point = np.linalg.inv(mat) @ np.array(
|
||||
(
|
||||
float(props.blender_eastings),
|
||||
float(props.blender_northings),
|
||||
float(props.blender_orthogonal_height),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
verts = [None] * len(self.mesh_data["co"])
|
||||
for i in range(0, len(self.mesh_data["co"]), 3):
|
||||
verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector(
|
||||
(
|
||||
*ifcopenshell.util.geolocation.enh2xyz(
|
||||
self.mesh_data["co"][i] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 1] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 2] * self.unit_scale,
|
||||
offset_point[0] * self.unit_scale,
|
||||
offset_point[1] * self.unit_scale,
|
||||
offset_point[2] * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
),
|
||||
1,
|
||||
)
|
||||
)
|
||||
verts_array = np.array(self.mesh_data["co"])
|
||||
verts_array *= self.unit_scale
|
||||
offset_x, offset_y, offset_z = verts_array[0:3]
|
||||
offset = np.array([-offset_x, -offset_y, -offset_z])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
|
||||
if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8):
|
||||
verts = offset_verts.tolist()
|
||||
else:
|
||||
verts = self.apply_matrix_to_flat_coords(offset_verts, native_data["matrix"])
|
||||
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = f"{offset_x},{offset_y},{offset_z}"
|
||||
else:
|
||||
verts = [None] * len(self.mesh_data["co"])
|
||||
for i in range(0, len(self.mesh_data["co"]), 3):
|
||||
verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector(
|
||||
(
|
||||
self.mesh_data["co"][i] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 1] * self.unit_scale,
|
||||
self.mesh_data["co"][i + 2] * self.unit_scale,
|
||||
1,
|
||||
)
|
||||
)
|
||||
verts_array = np.array(self.mesh_data["co"])
|
||||
verts_array *= self.unit_scale
|
||||
if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8):
|
||||
verts = verts_array.tolist()
|
||||
else:
|
||||
verts = self.apply_matrix_to_flat_coords(verts_array, native_data["matrix"])
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
mesh.vertices.add(self.mesh_data["total_verts"])
|
||||
@@ -1281,6 +1273,13 @@ class IfcImporter:
|
||||
mesh["ios_material_ids"] = self.mesh_data["material_ids"]
|
||||
return mesh
|
||||
|
||||
def apply_matrix_to_flat_coords(self, coords, matrix):
|
||||
coords_array = np.array(coords).reshape(-1, 3)
|
||||
ones = np.ones((coords_array.shape[0], 1))
|
||||
homogeneous_coords = np.hstack([coords_array, ones])
|
||||
transformed_coords = homogeneous_coords @ matrix.T
|
||||
return transformed_coords[:, :3].flatten().tolist()
|
||||
|
||||
def convert_representation_item_face_based_surface_model(self, item):
|
||||
mesh = item.get_info_2(recursive=True)
|
||||
for face_set in mesh["FbsmFaces"]:
|
||||
@@ -1924,34 +1923,14 @@ class IfcImporter:
|
||||
and geometry.verts
|
||||
and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2]))
|
||||
):
|
||||
offset_point = np.array(
|
||||
(
|
||||
float(props.blender_eastings),
|
||||
float(props.blender_northings),
|
||||
float(props.blender_orthogonal_height),
|
||||
0.0,
|
||||
)
|
||||
)
|
||||
if geometry != shape:
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(
|
||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||
)
|
||||
offset_point = np.linalg.inv(mat) @ offset_point
|
||||
verts = [None] * len(geometry.verts)
|
||||
for i in range(0, len(geometry.verts), 3):
|
||||
# Note: this enh2xyz call is crazy slow.
|
||||
verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz(
|
||||
geometry.verts[i],
|
||||
geometry.verts[i + 1],
|
||||
geometry.verts[i + 2],
|
||||
offset_point[0] * self.unit_scale,
|
||||
offset_point[1] * self.unit_scale,
|
||||
offset_point[2] * self.unit_scale,
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
# Shift geometry close to the origin based off that first vert it found
|
||||
verts_array = np.array(geometry.verts)
|
||||
offset = np.array([-geometry.verts[0], -geometry.verts[1], -geometry.verts[2]])
|
||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||
verts = offset_verts.tolist()
|
||||
|
||||
mesh["has_cartesian_point_offset"] = True
|
||||
mesh["cartesian_point_offset"] = f"{geometry.verts[0]},{geometry.verts[1]},{geometry.verts[2]}"
|
||||
else:
|
||||
verts = geometry.verts
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
Reference in New Issue
Block a user