From 452d0f602338d6380240bad2843f7eae456d089b Mon Sep 17 00:00:00 2001 From: Gorgious Date: Fri, 21 Nov 2025 23:22:07 +0100 Subject: [PATCH] fix - handle float64 checksums from files saved in previous Blender versions Files saved in previous Blender versions stored position checksums as float64 (24 bytes for location, 72 bytes for rotation), but the current code only expected float32 format. This caused a shape mismatch error (6,) vs (3,) when comparing checksums during save operations. Added byte size detection in is_moved() methods to automatically handle both old float64 and new float32 checksum formats, ensuring backward compatibility with files from previous versions. Fixes ValueError when saving IFC files loaded from older Blender versions. --- .../bonsai/bim/module/drawing/decoration.py | 22 +++++++++++++++---- src/bonsai/bonsai/tool/ifc.py | 22 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/decoration.py b/src/bonsai/bonsai/bim/module/drawing/decoration.py index 3860dcb58c..0ffc714ac9 100644 --- a/src/bonsai/bonsai/bim/module/drawing/decoration.py +++ b/src/bonsai/bonsai/bim/module/drawing/decoration.py @@ -1739,13 +1739,27 @@ class CutDecorator: self.cache_camera_matrix() return True # Let's be conservative obj = bpy.context.scene.camera - loc_check = np.frombuffer(eval(DecoratorData.camera_location_checksum), dtype=np.float32) - loc_real = np.array(obj.matrix_world.translation).flatten() + + # Handle both old float64 and new float32 checksums for version compatibility + loc_checksum_bytes = eval(DecoratorData.camera_location_checksum) + if len(loc_checksum_bytes) == 24: # Old format: 3 * 8 bytes (float64) + loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float64).astype(np.float32) + 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 self.cache_camera_matrix() return True - rot_check = np.frombuffer(eval(DecoratorData.camera_rotation_checksum), dtype=np.float32).reshape(3, 3) - rot_real = np.array(obj.matrix_world.to_3x3()) + + # Handle both old float64 and new float32 checksums for version compatibility + rot_checksum_bytes = eval(DecoratorData.camera_rotation_checksum) + if len(rot_checksum_bytes) == 72: # Old format: 9 * 8 bytes (float64) + rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float64).astype(np.float32).reshape(3, 3) + 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) angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1)) if angle_rad > 0.0017453292519943296: # 0.1 degrees diff --git a/src/bonsai/bonsai/tool/ifc.py b/src/bonsai/bonsai/tool/ifc.py index dfb2370272..9f1c84e6d1 100644 --- a/src/bonsai/bonsai/tool/ifc.py +++ b/src/bonsai/bonsai/tool/ifc.py @@ -110,12 +110,26 @@ class Ifc(bonsai.core.tool.Ifc): oprops = tool.Blender.get_object_bim_props(obj) if not oprops.location_checksum: return True # Let's be conservative - loc_check = np.frombuffer(eval(oprops.location_checksum), dtype=np.float32) - loc_real = np.array(obj.matrix_world.translation).flatten() + + # Handle both old float64 and new float32 checksums for version compatibility + loc_checksum_bytes = eval(oprops.location_checksum) + if len(loc_checksum_bytes) == 24: # Old format: 3 * 8 bytes (float64) + loc_check = np.frombuffer(loc_checksum_bytes, dtype=np.float64).astype(np.float32) + 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 return True - rot_check = np.frombuffer(eval(oprops.rotation_checksum), dtype=np.float32).reshape(3, 3) - rot_real = np.array(obj.matrix_world.to_3x3()) + + # Handle both old float64 and new float32 checksums for version compatibility + rot_checksum_bytes = eval(oprops.rotation_checksum) + if len(rot_checksum_bytes) == 72: # Old format: 9 * 8 bytes (float64) + rot_check = np.frombuffer(rot_checksum_bytes, dtype=np.float64).astype(np.float32).reshape(3, 3) + 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) angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1)) if angle_rad > 0.0017453292519943296: # 0.1 degrees