Fix error applying blender offset to read-only matrix (e.g. during linking)

Example:
```
  File "\bonsai\bim\module\project\operator.py", line 1815, in execute
    self.process_occurrence(shape)
  File "\bonsai\bim\module\project\operator.py", line 1952, in process_occurrence
    obj.matrix_world = tool.Loader.apply_blender_offset_to_matrix_world(obj, mat)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "\bonsai\tool\loader.py", line 952, in apply_blender_offset_to_matrix_world
    matrix[0][3] = offset_xyz[0]
    ~~~~~~~~~^^^
    ValueError: assignment destination is read-only
```

Noticed investigating #6242
This commit is contained in:
Andrej730
2025-02-28 15:45:42 +05:00
parent edfcc3f74b
commit cc7abe6a8d
2 changed files with 15 additions and 12 deletions
+6 -3
View File
@@ -38,6 +38,7 @@ import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
from bonsai.tool.loader import OBJECT_DATA_TYPE
from typing import Dict, Union, Optional, Any, Literal
from ifcopenshell.util.shape import MatrixType
class MaterialCreator:
@@ -479,14 +480,16 @@ class IfcImporter:
if grid.WAxes:
self.create_grid_axes(grid.WAxes, grid_obj, grid_placement)
def create_grid_axes(self, axes, grid_obj, grid_placement):
def create_grid_axes(
self, axes: list[ifcopenshell.entity_instance], grid_obj: bpy.types.Object, grid_placement: MatrixType
) -> None:
for axis in axes:
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
mesh = self.create_mesh(axis, shape)
obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh)
obj.show_in_front = True
self.link_element(axis, obj)
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, grid_placement.copy()))
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, grid_placement))
def create_element_types(self):
for element_type in self.element_types:
@@ -809,7 +812,7 @@ class IfcImporter:
if shape:
# We use numpy here because Blender mathutils.Matrix is not accurate enough
mat = np.array(shape.transformation.matrix).reshape((4, 4), order="F")
mat = ifcopenshell.util.shape.get_shape_matrix(shape)
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, mat))
assert mesh # Type checker.
if not materials_updated:
+9 -9
View File
@@ -929,13 +929,14 @@ class Loader(bonsai.core.tool.Loader):
@classmethod
def apply_blender_offset_to_matrix_world(cls, obj: bpy.types.Object, matrix: np.ndarray) -> Matrix:
"""
:param matrix: 4x4 numpy matrix.
"""
# Shouldn't mutate original matrix as we return a different object anyway.
M_TRANSLATION = (slice(0, 3), 3)
oprops = tool.Blender.get_object_bim_props(obj)
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)
):
translation = matrix[M_TRANSLATION]
if not obj.data and np.allclose(translation, 0.0, atol=1e-5):
# 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
@@ -947,11 +948,10 @@ class Loader(bonsai.core.tool.Loader):
oprops.blender_offset_type = "CARTESIAN_POINT"
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
oprops.cartesian_point_offset = cartesian_point_offset
matrix = matrix.copy()
offset_xyz = list(map(float, cartesian_point_offset.split(","))) + [1.0]
offset_xyz = matrix @ offset_xyz
matrix[0][3] = offset_xyz[0]
matrix[1][3] = offset_xyz[1]
matrix[2][3] = offset_xyz[2]
matrix[M_TRANSLATION] = offset_xyz[:3]
props = tool.Georeference.get_georeference_props()
if props.has_blender_offset: