mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Move handling legacy mathutils buffer types to separate methods
This commit is contained in:
@@ -1762,40 +1762,34 @@ class CutDecorator:
|
|||||||
shader.uniform_float("color", color)
|
shader.uniform_float("color", color)
|
||||||
batch.draw(shader)
|
batch.draw(shader)
|
||||||
|
|
||||||
def cache_camera_matrix(self):
|
def cache_camera_matrix(self) -> None:
|
||||||
|
assert bpy.context.scene and bpy.context.scene.camera
|
||||||
obj = bpy.context.scene.camera
|
obj = bpy.context.scene.camera
|
||||||
# Explicit `dtype` for Blender <5.0 compatibility.
|
|
||||||
DecoratorData.camera_location_checksum = repr(
|
DecoratorData.camera_location_checksum = repr(
|
||||||
np.array(obj.matrix_world.translation, dtype=np.float32).tobytes()
|
tool.Blender.np_array_legacy(obj.matrix_world.translation).tobytes()
|
||||||
)
|
)
|
||||||
DecoratorData.camera_rotation_checksum = repr(np.array(obj.matrix_world.to_3x3(), dtype=np.float32).tobytes())
|
DecoratorData.camera_rotation_checksum = repr(tool.Blender.np_array_legacy(obj.matrix_world.to_3x3()).tobytes())
|
||||||
|
|
||||||
def is_camera_moved(self):
|
def is_camera_moved(self) -> bool:
|
||||||
if not DecoratorData.camera_location_checksum:
|
if not DecoratorData.camera_location_checksum:
|
||||||
self.cache_camera_matrix()
|
self.cache_camera_matrix()
|
||||||
return True # Let's be conservative
|
return True # Let's be conservative
|
||||||
|
|
||||||
|
assert bpy.context.scene and bpy.context.scene.camera
|
||||||
obj = bpy.context.scene.camera
|
obj = bpy.context.scene.camera
|
||||||
|
|
||||||
# Handle both old float64 and new float32 checksums for version compatibility
|
# Handle both old float64 and new float32 checksums for version compatibility
|
||||||
loc_checksum_bytes = eval(DecoratorData.camera_location_checksum)
|
loc_checksum_bytes: bytes = eval(DecoratorData.camera_location_checksum)
|
||||||
if len(loc_checksum_bytes) == 24: # Old format: 3 * 8 bytes (float64)
|
loc_check = tool.Blender.np_frombuffer_legacy(loc_checksum_bytes, 3)
|
||||||
loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float64).astype(np.float32)
|
loc_real = tool.Blender.np_array_legacy(obj.matrix_world.translation)
|
||||||
else: # New format: 3 * 4 bytes (float32)
|
|
||||||
loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float32)
|
|
||||||
|
|
||||||
loc_real = np.array(obj.matrix_world.translation, dtype=np.float32).flatten()
|
|
||||||
if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm
|
if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm
|
||||||
self.cache_camera_matrix()
|
self.cache_camera_matrix()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Handle both old float64 and new float32 checksums for version compatibility
|
# Handle both old float64 and new float32 checksums for version compatibility
|
||||||
rot_checksum_bytes = eval(DecoratorData.camera_rotation_checksum)
|
rot_checksum_bytes: bytes = eval(DecoratorData.camera_rotation_checksum)
|
||||||
if len(rot_checksum_bytes) == 72: # Old format: 9 * 8 bytes (float64)
|
rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9)
|
||||||
rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float64).astype(np.float32).reshape(3, 3)
|
rot_real = tool.Blender.np_array_legacy(obj.matrix_world.to_3x3())
|
||||||
else: # New format: 9 * 4 bytes (float32)
|
|
||||||
rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float32).reshape(3, 3)
|
|
||||||
|
|
||||||
rot_real = np.array(obj.matrix_world.to_3x3(), dtype=np.float32)
|
|
||||||
rot_dot = np.dot(rot_check, rot_real.T)
|
rot_dot = np.dot(rot_check, rot_real.T)
|
||||||
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
|
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
|
||||||
if angle_rad > 0.0017453292519943296: # 0.1 degrees
|
if angle_rad > 0.0017453292519943296: # 0.1 degrees
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import ifcopenshell.util.element
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
from ifcopenshell import entity_instance
|
from ifcopenshell import entity_instance
|
||||||
from mathutils import Vector
|
from mathutils import Matrix, Vector
|
||||||
|
|
||||||
import bonsai.bim
|
import bonsai.bim
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
@@ -2127,3 +2127,32 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
if cls.BLENDER_5:
|
if cls.BLENDER_5:
|
||||||
return "BLENDER_EEVEE"
|
return "BLENDER_EEVEE"
|
||||||
return "BLENDER_EEVEE_NEXT"
|
return "BLENDER_EEVEE_NEXT"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def np_frombuffer_legacy(cls, bytedata: bytes, n: int) -> npt.NDArray[np.float32]:
|
||||||
|
"""
|
||||||
|
Read ``n`` float values from ``bytedata``, regardless if they are stored as ``float32`` or ``float64``.
|
||||||
|
Needed to support .blend files saved in Blender <5.0.0.
|
||||||
|
Also allows to work with .blend files from 5.0.0+ in older Blender versions.
|
||||||
|
|
||||||
|
In ``bpy.app.version >= 5.0.0`` ``mathutils`` transitioned to use ``float32`` buffer type,
|
||||||
|
while in previous version they were using ``float64``.
|
||||||
|
In some cases we are storing raw bytes (e.g. object transforms cheksums), so old .blend files
|
||||||
|
might still have ``float64`` data stored.
|
||||||
|
|
||||||
|
See https://projects.blender.org/blender/blender/issues/149283
|
||||||
|
"""
|
||||||
|
if len(bytedata) == (n * 2):
|
||||||
|
return np.frombuffer(bytedata, dtype=np.float64).astype(np.float32)
|
||||||
|
return np.frombuffer(bytedata, dtype=np.float32)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def np_array_legacy(cls, mathutils_type: Union[Vector, Matrix]) -> npt.NDArray[np.float32]:
|
||||||
|
"""
|
||||||
|
Converts ``mathutils`` types to ``np.float32`` arrays, regardless of Blender version.
|
||||||
|
|
||||||
|
See ``np_frombuffer_legacy`` for more details.
|
||||||
|
"""
|
||||||
|
if cls.BLENDER_5:
|
||||||
|
return np.array(mathutils_type)
|
||||||
|
return np.array(mathutils_type, dtype=np.float32)
|
||||||
|
|||||||
@@ -1152,9 +1152,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
def record_object_position(cls, obj: bpy.types.Object) -> None:
|
def record_object_position(cls, obj: bpy.types.Object) -> None:
|
||||||
# These are recorded separately because they have different numerical tolerances
|
# These are recorded separately because they have different numerical tolerances
|
||||||
props = tool.Blender.get_object_bim_props(obj)
|
props = tool.Blender.get_object_bim_props(obj)
|
||||||
# Explicit dtype for Blender <5.0 compatibility.
|
props.location_checksum = repr(tool.Blender.np_array_legacy(obj.matrix_world.translation).tobytes())
|
||||||
props.location_checksum = repr(np.array(obj.matrix_world.translation, dtype=np.float32).tobytes())
|
props.rotation_checksum = repr(tool.Blender.np_array_legacy(obj.matrix_world.to_3x3()).tobytes())
|
||||||
props.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3(), dtype=np.float32).tobytes())
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def remove_connection(cls, connection: ifcopenshell.entity_instance) -> None:
|
def remove_connection(cls, connection: ifcopenshell.entity_instance) -> None:
|
||||||
|
|||||||
@@ -115,24 +115,17 @@ class Ifc(bonsai.core.tool.Ifc):
|
|||||||
return True # Let's be conservative
|
return True # Let's be conservative
|
||||||
|
|
||||||
# Handle both old float64 and new float32 checksums for version compatibility
|
# Handle both old float64 and new float32 checksums for version compatibility
|
||||||
loc_checksum_bytes = eval(oprops.location_checksum)
|
loc_checksum_bytes: bytes = eval(oprops.location_checksum)
|
||||||
if len(loc_checksum_bytes) == 24: # Old format: 3 * 8 bytes (float64)
|
loc_check = tool.Blender.np_frombuffer_legacy(loc_checksum_bytes, 3)
|
||||||
loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float64).astype(np.float32)
|
loc_real = tool.Blender.np_array_legacy(obj.matrix_world.translation)
|
||||||
else: # New format: 3 * 4 bytes (float32)
|
|
||||||
loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float32)
|
|
||||||
|
|
||||||
loc_real = np.array(obj.matrix_world.translation, dtype=np.float32).flatten()
|
|
||||||
if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm
|
if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Handle both old float64 and new float32 checksums for version compatibility
|
# Handle both old float64 and new float32 checksums for version compatibility
|
||||||
rot_checksum_bytes = eval(oprops.rotation_checksum)
|
rot_checksum_bytes: bytes = eval(oprops.rotation_checksum)
|
||||||
if len(rot_checksum_bytes) == 72: # Old format: 9 * 8 bytes (float64)
|
rot_check = tool.Blender.np_frombuffer_legacy(rot_checksum_bytes, 9)
|
||||||
rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float64).astype(np.float32).reshape(3, 3)
|
rot_check = rot_check.reshape(3, 3)
|
||||||
else: # New format: 9 * 4 bytes (float32)
|
rot_real = tool.Blender.np_array_legacy(obj.matrix_world.to_3x3())
|
||||||
rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float32).reshape(3, 3)
|
|
||||||
|
|
||||||
rot_real = np.array(obj.matrix_world.to_3x3(), dtype=np.float32)
|
|
||||||
rot_dot = np.dot(rot_check, rot_real.T)
|
rot_dot = np.dot(rot_check, rot_real.T)
|
||||||
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
|
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
|
||||||
if angle_rad > 0.0017453292519943296: # 0.1 degrees
|
if angle_rad > 0.0017453292519943296: # 0.1 degrees
|
||||||
|
|||||||
@@ -323,8 +323,8 @@ class TestRecordObjectPosition(NewFile):
|
|||||||
obj = bpy.data.objects.new("Object", None)
|
obj = bpy.data.objects.new("Object", None)
|
||||||
props = tool.Blender.get_object_bim_props(obj)
|
props = tool.Blender.get_object_bim_props(obj)
|
||||||
subject.record_object_position(obj)
|
subject.record_object_position(obj)
|
||||||
assert props.location_checksum == repr(np.array(obj.matrix_world.translation, dtype=np.float32).tobytes())
|
assert props.location_checksum == repr(tool.Blender.np_array_legacy(obj.matrix_world.translation).tobytes())
|
||||||
assert props.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3(), dtype=np.float32).tobytes())
|
assert props.rotation_checksum == repr(tool.Blender.np_array_legacy(obj.matrix_world.to_3x3()).tobytes())
|
||||||
|
|
||||||
|
|
||||||
class TestRemoveConnection(NewFile):
|
class TestRemoveConnection(NewFile):
|
||||||
|
|||||||
Reference in New Issue
Block a user