Taking the vector rVec and determining its main axis direction (if two values are the same, any of the two main axes will probably work similarly) tells me which axis to consider as the 'Roll' axis. Both vectors, rVec and eVec, are rotated so that their main axes (X or Y) are now the Z-axis. With that, I can calculate using the standard DirectionVector-to-EulerAngles maths. Then I just assign the Euler vector angles to the appropriate x,y,z. Now the rotations are 'minimal' and the 'Roll' axis as determined for the vector is always 0.0.
// e- is the conformeE, r- is the conformeR // - Make both endpoints local to world origin Vector eVec = bc->GetVector(IPP_ENDPOINT) - bc->GetVector(IPP_ORIGIN); BaseContainer* bbc = base->GetDataInstance(); Vector rVec = bbc->GetVector(IPP_ENDPOINT) - bbc->GetVector(IPP_ORIGIN); // - If equivalent or either is zero vector (no direction), no more processing required if (VectorEqual(eVec, rVec, EPSILONL)) return; if (VectorEqual(eVec, zVec, EPSILONL)) return; if (VectorEqual(rVec, zVec, EPSILONL)) return; // - Determine main direction axis for vector UCHAR axis; Vector ar = Vector(Abs(rVec.x), Abs(rVec.y), Abs(rVec.z)); // -- X if ((ar.x > ar.y) && (ar.x > ar.z)) { Matrix align = MatrixRotX(RAD_90) * MatrixRotZ(RAD_90) * MatrixScale(Vector(1.0)); // new y is old x, new z is old y, new x is old z rVec *= align; eVec *= align; axis = 0; } // -- Y else if (ar.y > ar.z) { // new x is old y, new z is old x, new y is old z Matrix align = MatrixRotY(-RAD_90) * MatrixRotZ(-RAD_90) * MatrixScale(Vector(1.0)); rVec *= align; eVec *= align; axis = 1; } // -- Z else axis = 2; // - Calculate angular offset between eVec/rVec Vectors in Rotation Order // -- Calculate Euler angles of Conformer Real r; Vector eAngle; Vector rAngle; r = Sqrt(rVec.x*rVec.x + rVec.z*rVec.z); rAngle.z = 0.0f; // roll rAngle.y = (r < EPSILONS) ? 0.0f : Support::Angle(rVec.z, rVec.x); // yaw rAngle.x = -Support::Angle(rVec.y, r); // pitch // -- Concatenate Rotation-order specific matrix Matrix mat = MatrixMove(zVec) * MatrixRotX(rAngle.x) * MatrixRotY(rAngle.y) * MatrixRotZ(rAngle.z) * MatrixScale(Vector(1.0)); // -- Calculate Euler angles of Conformee // Note that eVec is placed in the coordinate system where rVec is unrotated eVec *= !mat; r = Sqrt(eVec.x*eVec.x + eVec.z*eVec.z); // These are the rotation angles from Conformee to Conformer Endpoint eAngle.z = 0.0f; // roll eAngle.y = (r < EPSILONS) ? 0.0f : -Support::Angle(eVec.z, eVec.x); // yaw eAngle.x = Support::Angle(eVec.y, r); // pitch // Reorder rotations to avoid roll rotations Vector rotVec; if (axis == 0) { rotVec.x = eAngle.z; rotVec.y = eAngle.x; rotVec.z = eAngle.y; } else if (axis == 1) { rotVec.x = eAngle.y; rotVec.y = eAngle.z; rotVec.z = eAngle.x; } else { rotVec.x = eAngle.x; rotVec.y = eAngle.y; rotVec.z = eAngle.z; }
Tests so far have all been positive. There is a likelyhood of finding an exception to this - a vector with equal non-zero values like v(10,10,10) would prove an interesting situation - but it should default to standard Z-axis Roll for better or worse.
Thanks,
Robert