From e609f105598ebf24d8f086ed01f0f6b8bb6a0a2d Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Sun, 22 Mar 2026 20:46:05 -0500 Subject: [PATCH] Fix grid axis annotation misalignment when axis is moved After moving an IfcGridAxis in Blender, the drawing annotation was not tracking the axis to its new visual position. Two issues were found and fixed: 1. generate_grid_axis_reference_points used the IFC AxisCurve geometry (via create_shape) with the grid object's matrix_world. After a save, the IFC AxisCurve is updated but the Blender mesh is not rebuilt, causing the two sources to diverge. The fix reads the axis object's Blender mesh vertices directly with axis_obj.matrix_world, which always matches what Blender renders. 2. When no Blender axis object exists, falls back to reading IFC geometry with the grid object's matrix_world (unchanged behavior). Minor refactors: extracted matrices_differ variable in sync_grid_axis_object_placement (export_ifc.py and drawing.py) and extracted grid_placement variable in create_axis_curve.py for clarity. Generated with the assistance of an AI coding tool. --- src/bonsai/bonsai/bim/export_ifc.py | 3 ++- src/bonsai/bonsai/tool/drawing.py | 27 ++++++++++++------- .../api/grid/create_axis_curve.py | 3 ++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/bonsai/bonsai/bim/export_ifc.py b/src/bonsai/bonsai/bim/export_ifc.py index f28c106e95..993622ff1c 100644 --- a/src/bonsai/bonsai/bim/export_ifc.py +++ b/src/bonsai/bonsai/bim/export_ifc.py @@ -134,7 +134,8 @@ class IfcExporter: grid_obj = tool.Ifc.get_object(grid) if grid_obj: self.sync_object_placement(grid_obj) - if grid_obj.matrix_world != obj.matrix_world: + matrices_differ = grid_obj.matrix_world != obj.matrix_world + if matrices_differ: bpy.ops.bim.update_representation(obj=obj.name) tool.Geometry.record_object_position(obj) diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 269e0ad35c..ea016bd983 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -1953,18 +1953,23 @@ class Drawing(bonsai.core.tool.Drawing): if camera.data.type != "ORTHO": return - settings = ifcopenshell.geom.settings() - settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS) - geometry = ifcopenshell.geom.create_shape(settings, axis.AxisCurve) - verts = ifcopenshell.util.shape.get_vertices(geometry) grid = (axis.PartOfU or axis.PartOfV or axis.PartOfW)[0] - grid_obj = tool.Ifc.get_object(grid) - if grid_obj: - m = np.array(grid_obj.matrix_world) + axis_obj = tool.Ifc.get_object(axis) + if axis_obj and axis_obj.data and len(axis_obj.data.vertices) >= 2: + m = np.array(axis_obj.matrix_world) + verts = [np.array(v.co) for v in axis_obj.data.vertices[:2]] else: - m = ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement) + settings = ifcopenshell.geom.settings() + settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS) + geometry = ifcopenshell.geom.create_shape(settings, axis.AxisCurve) + verts = list(ifcopenshell.util.shape.get_vertices(geometry)[:2]) + grid_obj = tool.Ifc.get_object(grid) + if grid_obj: + m = np.array(grid_obj.matrix_world) + else: + m = ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement) im = camera.matrix_world.inverted() - v1, v2 = [im @ Vector((m @ np.append(v, 1.0))[:3]) for v in verts[:2]] + v1, v2 = [im @ Vector((m @ np.append(v[:3], 1.0))[:3]) for v in verts] target_view = tool.Drawing.get_drawing_target_view(drawing) if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW"): @@ -2188,6 +2193,7 @@ class Drawing(bonsai.core.tool.Drawing): def sync_object_placement(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]: blender_matrix = np.array(obj.matrix_world) element = tool.Ifc.get_entity(obj) + is_moved = tool.Ifc.is_moved(obj) if tool.Geometry.is_scaled(obj): bpy.ops.bim.update_representation(obj=obj.name) return element @@ -2204,7 +2210,8 @@ class Drawing(bonsai.core.tool.Drawing): grid_obj = tool.Ifc.get_object(grid) if grid_obj: cls.sync_object_placement(grid_obj) - if grid_obj.matrix_world != obj.matrix_world: + matrices_differ = grid_obj.matrix_world != obj.matrix_world + if matrices_differ: bpy.ops.bim.update_representation(obj=obj.name) tool.Geometry.record_object_position(obj) diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py index a71c523c3f..9c4a9aa1c8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -78,7 +78,8 @@ def create_axis_curve( points /= unit_scale grid = next(i for i in file.get_inverse(grid_axis) if i.is_a("IfcGrid")) - grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement)) + grid_placement = ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement) + grid_matrix_i = np.linalg.inv(grid_placement) p1, p2 = ifc_safe_vector_type(np_apply_matrix(points, grid_matrix_i)) grid_axis.AxisCurve = file.create_entity( "IfcPolyline",