mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Bonsai: re-orthonormalize item placement in Item Mode (#6996)
Entering and leaving Item Mode on an element with IfcExtrudedAreaSolid items corrupts their orientation, and the corruption persists after save and reopen. get_axis2placement builds the item frame using the placement's RefDirection as the X axis without projecting it onto the plane perpendicular to Axis. IFC permits a non perpendicular RefDirection (only its projection is significant), so a legal placement yields a sheared, non orthonormal matrix. import_item assigns that to the Blender item object's matrix_world, but a Blender object transform stores only location, rotation and scale and cannot represent shear, so the next depsgraph evaluation recomposes it into a wrong rotation. sync_item_positions then reads the corrupted matrix and writes it back into IfcExtrudedAreaSolid.Position, permanently corrupting the geometry. Re-orthonormalize the frame right after get_axis2placement (keep Z, project X onto the plane perpendicular to Z, recompute Y). This is the same projection the geometry kernel already applies, so rendered geometry is unchanged; it only removes the shear Blender cannot store. No-op when RefDirection is already perpendicular or absent, guarded against the degenerate parallel case. Verified live (headless Blender, enter+leave+save over 50 solids): the maximum change in the normalized extrusion axis goes from 1.026 (segments flipping, the bug) to 7e-6 (identity within float noise), and the items are no longer flagged as moved, so their Positions are left untouched. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2248,6 +2248,23 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if position or not is_swept_area:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
position = ifcopenshell.util.placement.get_axis2placement(position)
|
||||
# get_axis2placement uses the placement's RefDirection as the X axis as
|
||||
# is, without projecting it onto the plane perpendicular to Axis. IFC
|
||||
# allows RefDirection to be non perpendicular to Axis (only its
|
||||
# projection matters), so such a legal placement yields a sheared, non
|
||||
# orthonormal matrix. A Blender object transform can only store
|
||||
# location, rotation and scale, so on the next depsgraph evaluation the
|
||||
# shear is silently baked into a wrong rotation, which flips the item's
|
||||
# orientation and, once written back by sync_item_positions, corrupts
|
||||
# the saved IfcSweptAreaSolid. Re orthonormalise the frame (keeping Z,
|
||||
# projecting X) so it matches the geometry kernel and survives Blender.
|
||||
z_axis = position[:3, 2]
|
||||
x_axis = position[:3, 0] - np.dot(position[:3, 0], z_axis) * z_axis
|
||||
x_axis_length = np.linalg.norm(x_axis)
|
||||
if x_axis_length > 1e-12:
|
||||
x_axis /= x_axis_length
|
||||
position[:3, 0] = x_axis
|
||||
position[:3, 1] = np.cross(z_axis, x_axis)
|
||||
position[:, 3][0:3] *= unit_scale
|
||||
item_matrix = np.array(rep_obj.matrix_world.copy())
|
||||
if cartesian_point_offset is not None:
|
||||
|
||||
Reference in New Issue
Block a user