From d174ef7a8dd4463ac30daa620dae8e1b052fad6d Mon Sep 17 00:00:00 2001 From: forbes Date: Fri, 27 Feb 2026 10:38:37 -0600 Subject: [PATCH] fix(assembly): use short-arc angle in validateNewPlacements (#338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rotation::evaluateVector() computes angle = 2*acos(w) which gives values in [0, 2*pi]. When the relative quaternion has w < 0 (opposite hemisphere), the angle exceeds pi even though q and -q represent the same rotation. This caused the validator to report ~350 degree 'flips' and reject valid solver output. Fix: map the angle to [0, pi] before comparing against the 91-degree threshold. This is the short-arc equivalent — the minimum rotation angle between two orientations regardless of quaternion sign convention. --- src/Mod/Assembly/App/AssemblyObject.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index f9fe2066e9..c5cce33416 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -659,19 +659,26 @@ bool AssemblyObject::validateNewPlacements() // Get the relative rotation: how much did the part rotate? Base::Rotation relativeRot = newRot * oldRot.inverse(); - // Get the angle of this rotation + // Ensure we measure the short-arc angle. Rotation stores + // angle = 2*acos(w) which gives [0, 2*pi]. When the + // quaternion w component is negative (opposite hemisphere), + // the angle exceeds pi even though q and -q represent the + // same rotation. Map to [0, pi] for a correct comparison. Base::Vector3d axis; double angle; relativeRot.getRawValue(axis, angle); + if (angle > M_PI) { + angle = 2.0 * M_PI - angle; + } // If the part rotated more than 90 degrees, consider it a flip // Use 91 degrees to allow for small numerical errors constexpr double maxAngle = 91.0 * M_PI / 180.0; - if (std::abs(angle) > maxAngle) { + if (angle > maxAngle) { Base::Console().warning( "Assembly : Ignoring bad solve, part (%s) flipped orientation (%.1f degrees).\n", obj->getFullLabel(), - std::abs(angle) * 180.0 / M_PI + angle * 180.0 / M_PI ); return false; } -- 2.49.1