mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 03:21:00 +00:00
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:
@@ -38,6 +38,7 @@ import bonsai.tool as tool
|
|||||||
from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
|
from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
|
||||||
from bonsai.tool.loader import OBJECT_DATA_TYPE
|
from bonsai.tool.loader import OBJECT_DATA_TYPE
|
||||||
from typing import Dict, Union, Optional, Any, Literal
|
from typing import Dict, Union, Optional, Any, Literal
|
||||||
|
from ifcopenshell.util.shape import MatrixType
|
||||||
|
|
||||||
|
|
||||||
class MaterialCreator:
|
class MaterialCreator:
|
||||||
@@ -479,14 +480,16 @@ class IfcImporter:
|
|||||||
if grid.WAxes:
|
if grid.WAxes:
|
||||||
self.create_grid_axes(grid.WAxes, grid_obj, grid_placement)
|
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:
|
for axis in axes:
|
||||||
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
||||||
mesh = self.create_mesh(axis, shape)
|
mesh = self.create_mesh(axis, shape)
|
||||||
obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh)
|
obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh)
|
||||||
obj.show_in_front = True
|
obj.show_in_front = True
|
||||||
self.link_element(axis, obj)
|
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):
|
def create_element_types(self):
|
||||||
for element_type in self.element_types:
|
for element_type in self.element_types:
|
||||||
@@ -809,7 +812,7 @@ class IfcImporter:
|
|||||||
|
|
||||||
if shape:
|
if shape:
|
||||||
# We use numpy here because Blender mathutils.Matrix is not accurate enough
|
# 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))
|
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, mat))
|
||||||
assert mesh # Type checker.
|
assert mesh # Type checker.
|
||||||
if not materials_updated:
|
if not materials_updated:
|
||||||
|
|||||||
@@ -929,13 +929,14 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def apply_blender_offset_to_matrix_world(cls, obj: bpy.types.Object, matrix: np.ndarray) -> Matrix:
|
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)
|
oprops = tool.Blender.get_object_bim_props(obj)
|
||||||
if (
|
translation = matrix[M_TRANSLATION]
|
||||||
not obj.data
|
if not obj.data and np.allclose(translation, 0.0, atol=1e-5):
|
||||||
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
|
# We assume any non-geometric matrix at 0,0,0 is not
|
||||||
# positionally significant and is left alone. This handles
|
# positionally significant and is left alone. This handles
|
||||||
# scenarios where often spatial elements are left at 0,0,0 and
|
# 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"
|
oprops.blender_offset_type = "CARTESIAN_POINT"
|
||||||
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
|
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
|
||||||
oprops.cartesian_point_offset = cartesian_point_offset
|
oprops.cartesian_point_offset = cartesian_point_offset
|
||||||
|
matrix = matrix.copy()
|
||||||
offset_xyz = list(map(float, cartesian_point_offset.split(","))) + [1.0]
|
offset_xyz = list(map(float, cartesian_point_offset.split(","))) + [1.0]
|
||||||
offset_xyz = matrix @ offset_xyz
|
offset_xyz = matrix @ offset_xyz
|
||||||
matrix[0][3] = offset_xyz[0]
|
matrix[M_TRANSLATION] = offset_xyz[:3]
|
||||||
matrix[1][3] = offset_xyz[1]
|
|
||||||
matrix[2][3] = offset_xyz[2]
|
|
||||||
|
|
||||||
props = tool.Georeference.get_georeference_props()
|
props = tool.Georeference.get_georeference_props()
|
||||||
if props.has_blender_offset:
|
if props.has_blender_offset:
|
||||||
|
|||||||
Reference in New Issue
Block a user