Difference between revisions of "User:DavidJCobb/Rotation Library"
User:DavidJCobb/Rotation Library (edit)
Revision as of 18:02, 11 February 2015
, 18:02, 11 February 2015Updated the rotation library based on my private copy. Fewer cross-script function calls. More options. Should work and be backward-compatible, if I copied and pasted the right stuff into the right places.
imported>DavidJCobb m |
imported>DavidJCobb (Updated the rotation library based on my private copy. Fewer cross-script function calls. More options. Should work and be backward-compatible, if I copied and pasted the right stuff into the right places.) |
||
Line 19: | Line 19: | ||
Import CobbLibraryMisc | Import CobbLibraryMisc | ||
Import CobbLibraryVectors | Import CobbLibraryVectors | ||
Float Function atan2(float y, float x) Global | |||
Float out = 0 | |||
If y != 0 | |||
out = Math.sqrt(x * x + y * y) - x | |||
out /= y | |||
out = Math.atan(out) * 2 | |||
Else | |||
If x == 0 | |||
return 0 | |||
EndIf | |||
out = Math.atan(y / x) | |||
If x < 0 | |||
out += 180 | |||
EndIf | |||
EndIf | |||
return out | |||
EndFunction | |||
Float Function MatrixTrace(Float[] afMatrix) Global | Float Function MatrixTrace(Float[] afMatrix) Global | ||
Line 30: | Line 48: | ||
; Source for the math: http://www.vectoralgebra.info/axisangle.html | ; Source for the math: http://www.vectoralgebra.info/axisangle.html | ||
; Source for the math: http://www.vectoralgebra.info/euleranglesvector.html | ; Source for the math: http://www.vectoralgebra.info/euleranglesvector.html | ||
; | |||
; This has been tested and confirmed to work. | |||
; | ; | ||
Float[] fOutput = new Float[4] | Float[] fOutput = new Float[4] | ||
Line 104: | Line 124: | ||
; We don't know the signs of the above terms. Per our second | ; We don't know the signs of the above terms. Per our second | ||
; source, we can start to figure that out by finding the largest | ; source, we can start to figure that out by finding the largest | ||
; term... | ; term, and then... | ||
; | ; | ||
Int iLargestIndex = 0 | Int iLargestIndex = 0 | ||
Line 120: | Line 140: | ||
Int iIndex = iLargestIndex * 3 + iIterator | Int iIndex = iLargestIndex * 3 + iIterator | ||
If iIterator != iLargestIndex | If iIterator != iLargestIndex | ||
fOutput[iIterator] = fOutput[iIterator] * | ; | ||
; Get the sign of the relevant matrix term. | |||
; | |||
Int iSign = 0 | |||
If afMatrix[iIndex] | |||
iSign = 1 | |||
If afMatrix[iIndex] < 0 | |||
iSign = -1 | |||
EndIf | |||
EndIf | |||
; | |||
; Result. | |||
; | |||
fOutput[iIterator] = fOutput[iIterator] * iSign | |||
EndIf | EndIf | ||
iIterator += 1 | iIterator += 1 | ||
Line 129: | Line 162: | ||
; | ; | ||
If VectorLength(fOutput) != 0 | If VectorLength(fOutput) != 0 | ||
Float[] fNormalized = VectorNormalize(fOutput) | |||
fOutput[0] = fNormalized[0] | |||
fOutput[1] = fNormalized[1] | |||
fOutput[2] = fNormalized[2] | |||
Else | Else | ||
; | ; | ||
Line 166: | Line 202: | ||
Float fTY | Float fTY | ||
If fCY && fCY >= 0.00000011920929 && fCYTest | If fCY && fCY >= 0.00000011920929 && fCYTest | ||
Debug.Trace("MatrixToEuler: Y == " + fY + "; cos(Y) == " + fCY) | ;Debug.Trace("MatrixToEuler: Y == " + fY + "; cos(Y) == " + fCY) | ||
fTX = afMatrix[8] / fCY | fTX = afMatrix[8] / fCY | ||
fTY = afMatrix[5] / fCY | fTY = afMatrix[5] / fCY | ||
Line 174: | Line 210: | ||
fEuler[2] = atan2(fTY, fTX) ; = atan(cosYcosZ / cosYsinZ) = atan(sin Z / cos Z) | fEuler[2] = atan2(fTY, fTX) ; = atan(cosYcosZ / cosYsinZ) = atan(sin Z / cos Z) | ||
Else | Else | ||
Debug.Trace("MatrixToEuler: cos(Y) == 0. Taking another path...") | ;Debug.Trace("MatrixToEuler: cos(Y) == 0. Taking another path...") | ||
; | ; | ||
; We can't compute X and Z by using Y, because cos(Y) is zero. Therefore, | ; We can't compute X and Z by using Y, because cos(Y) is zero. Therefore, | ||
Line 208: | Line 244: | ||
; | ; | ||
; Based on the math at: https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle | ; Based on the math at: https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle | ||
; | |||
; The source does NOT state its Euler sequence, and it isn't entirely clear about its handedness | |||
; or whether or not it's extrinsic, either. Proceed with caution. It DOES line up with the other | |||
; sites I've been using, though. | |||
; | ; | ||
Float[] fMatrix = new Float[9] | Float[] fMatrix = new Float[9] | ||
Line 239: | Line 279: | ||
Float[] Function QuaternionToAxisAngle(Float[] aqQuat) Global | Float[] Function QuaternionToAxisAngle(Float[] aqQuat) Global | ||
{Converts a unit quaternion (versor) to an axis-angle representation, returning [x, y, z, angle].} | {Converts a unit quaternion (versor) to an axis-angle representation, returning [x, y, z, angle].} | ||
; | |||
; I can't get the math working for a direct conversion. We'll do it indirectly, instead. | |||
; | |||
return MatrixToAxisAngle(QuaternionToMatrix(aqQuat)) | return MatrixToAxisAngle(QuaternionToMatrix(aqQuat)) | ||
EndFunction | EndFunction | ||
Line 296: | Line 339: | ||
{UNTESTED. Returns as a new quaternion the conjugate of the given quaternion (of the form [w, x, y, z]).} | {UNTESTED. Returns as a new quaternion the conjugate of the given quaternion (of the form [w, x, y, z]).} | ||
Float[] qOut = new Float[4] | Float[] qOut = new Float[4] | ||
Float[] v = | Float[] v = new Float[3] | ||
v[1] = aq[0] | |||
v[2] = aq[1] | |||
v = VectorNegate(v) | |||
qOut[0] = aq[0] | qOut[0] = aq[0] | ||
qOut[1] = v[1] | |||
qOut[2] = v[2] | |||
return qOut | return qOut | ||
EndFunction | |||
Float[] Function GetCoordinatesRelativeToBase(Float[] afParentPosition, Float[] afParentRotation, Float[] afOffsetPosition, Float[] afOffsetRotation) Global | |||
{Given two sets of positions and rotations -- those of a parent object, and those of a child object relative to the parent -- this function returns an array of the form [XPos, YPos, ZPos, XAng, YAng, ZAng]. These are the positions and rotations of the child object relative to the world. In other words, this function exists as an alternative to MoveObjectRelativeToObject, allowing you to move objects however you wish. | |||
Position code is based on GetPosXYZRotateAroundRef, a function authored by Chesko that can be found on the Creation Kit wiki.} | |||
; | |||
; CONSTRUCT POSITION USING CHESKO'S METHOD: | |||
; | |||
Float[] fOutput = new Float[6] | |||
Float[] fVector = new Float[3] | |||
; | |||
; Apply Z-axis rotation matrix. (Modify the final X- and Y-axis positions based on the Z rotation.) | |||
; | |||
fOutput[0] = (afOffsetPosition[0] * Math.cos(-afParentRotation[2])) + (afOffsetPosition[1] * Math.sin( afParentRotation[2])) | |||
fOutput[1] = (afOffsetPosition[0] * Math.sin(-afParentRotation[2])) + (afOffsetPosition[1] * Math.cos(-afParentRotation[2])) | |||
fOutput[2] = afOffsetPosition[2] | |||
; | |||
; Apply Y-axis rotation matrix. (Modify the final X- and Z-axis positions based on the Y rotation.) | |||
; | |||
fVector[0] = fOutput[0] ; X | |||
fVector[2] = fOutput[2] ; Z | |||
fOutput[0] = (fVector[0] * Math.cos(-afParentRotation[1])) + (fVector[2] * Math.sin(-afParentRotation[1])) | |||
fOutput[2] = (fVector[0] * Math.sin( afParentRotation[1])) + (fVector[2] * Math.cos(-afParentRotation[1])) | |||
; | |||
; Apply X-axis rotation matrix. (Modify the final Y- and Z-axis positions based on the X rotation.) | |||
; | |||
fVector[1] = fOutput[1] ; Y | |||
fVector[2] = fOutput[2] ; Z | |||
fOutput[1] = (fVector[1] * Math.cos(-afParentRotation[1])) + (fVector[2] * Math.sin( afParentRotation[1])) | |||
fOutput[2] = (fVector[1] * Math.sin(-afParentRotation[1])) + (fVector[2] * Math.cos(-afParentRotation[1])) | |||
; | |||
; Finalize coordinates. | |||
; | |||
fOutput[0] = fOutput[0] + afParentPosition[0] | |||
fOutput[1] = fOutput[1] + afParentPosition[1] | |||
fOutput[2] = fOutput[2] + afParentPosition[2] | |||
; | |||
; CONSTRUCT ROTATION USING THIS LIBRARY: | |||
; | |||
Float[] qParent = EulerToQuaternion(afParentRotation[0], afParentRotation[1], afParentRotation[2]) | |||
Float[] qChild = EulerToQuaternion(afOffsetRotation[0], afOffsetRotation[1], afOffsetRotation[2]) | |||
Float[] qDone = QuaternionMultiply(qParent, qChild) | |||
Float[] eDone = QuaternionToEuler(qDone) | |||
; | |||
; Return result. | |||
; | |||
fOutput[3] = eDone[0] | |||
fOutput[4] = eDone[1] | |||
fOutput[5] = eDone[2] | |||
Return fOutput | |||
EndFunction | EndFunction | ||
Function MoveObjectRelativeToObject(ObjectReference akChild, ObjectReference akParent, Float[] afPositionOffset, Float[] afRotationOffset) Global | Function MoveObjectRelativeToObject(ObjectReference akChild, ObjectReference akParent, Float[] afPositionOffset, Float[] afRotationOffset) Global | ||
{ | {Moves the child reference relative to the parent reference. Position code is based on GetPosXYZRotateAroundRef, a function authored by Chesko that can be found on the Creation Kit wiki.} | ||
If !afPositionOffset || !afRotationOffset || afPositionOffset.length < 3 || afRotationOffset.length < 3 | If !afPositionOffset || !afRotationOffset || afPositionOffset.length < 3 || afRotationOffset.length < 3 | ||
return | return | ||
Line 312: | Line 410: | ||
Float[] Angles = new Float[3] | Float[] Angles = new Float[3] | ||
Float[] Origin = new Float[3] | Float[] Origin = new Float[3] | ||
Float[] Output = new Float[3] | Float[] Output = new Float[3] | ||
Float[] Vector = new Float[3] | |||
Angles[0] = -akParent.GetAngleX() | Angles[0] = -akParent.GetAngleX() | ||
Line 323: | Line 421: | ||
Origin[2] = akParent.GetPositionZ() | Origin[2] = akParent.GetPositionZ() | ||
; | ; | ||
; | ; Apply Z-axis rotation matrix. (Modify the final X- and Y-axis positions based on the Z rotation.) | ||
; | ; | ||
Output[0] = (afPositionOffset[0] * Math.cos(Angles[2])) + (afPositionOffset[1] * Math.sin(-Angles[2])) | |||
Output[1] = (afPositionOffset[0] * Math.sin(Angles[2])) + (afPositionOffset[1] * Math.cos( Angles[2])) | |||
Output[2] = afPositionOffset[2] | |||
Output[0] = ( | |||
Output[1] = ( | |||
Output[2] = | |||
; | ; | ||
; | ; Apply Y-axis rotation matrix. (Modify the final X- and Z-axis positions based on the Y rotation.) | ||
; | ; | ||
Vector[0] = Output[0] | Vector[0] = Output[0] | ||
Vector[2] = Output[2] | Vector[2] = Output[2] | ||
Output[0] = (Vector[0] * Math.cos( Angles[1]) | Output[0] = (Vector[0] * Math.cos( Angles[1])) + (Vector[2] * Math.sin(Angles[1])) | ||
Output[2] = (Vector[0] * Math.sin(-Angles[1])) + (Vector[2] * Math.cos(Angles[1])) | |||
Output[2] = (Vector[0] * Math.sin(-Angles[1]) | |||
; | ; | ||
; | ; Apply X-axis rotation matrix. (Modify the final Y- and Z-axis positions based on the X rotation.) | ||
; | ; | ||
Vector[1] = Output[1] | Vector[1] = Output[1] | ||
Vector[2] = Output[2] | Vector[2] = Output[2] | ||
Output[1] = (Vector[1] * Math.cos(Angles[0])) + (Vector[2] * Math.sin(-Angles[0])) | |||
Output[1] = | Output[2] = (Vector[1] * Math.sin(Angles[0])) + (Vector[2] * Math.cos( Angles[0])) | ||
Output[2] = | |||
; | ; | ||
; Finalize coordinates. | ; Finalize coordinates. | ||
Line 363: | Line 449: | ||
; CONSTRUCT ROTATION USING THIS LIBRARY. | ; CONSTRUCT ROTATION USING THIS LIBRARY. | ||
; | ; | ||
Float[] | Float[] qParent = EulerToQuaternion(akParent.GetAngleX(), akParent.GetAngleY(), akParent.GetAngleZ()) | ||
Float[] | Float[] qChild = EulerToQuaternion(afRotationOffset[0], afRotationOffset[1], afRotationOffset[2]) | ||
Float[] qDone = QuaternionMultiply(qParent, qChild) | |||
Float[] qDone = QuaternionMultiply( | |||
Float[] eDone = QuaternionToEuler(qDone) | Float[] eDone = QuaternionToEuler(qDone) | ||
; | ; | ||
; | ; Move the child object. | ||
; | ; | ||
akChild.SetPosition(Output[0], Output[1], Output[2]) | akChild.SetPosition(Output[0], Output[1], Output[2]) | ||
Line 380: | Line 461: | ||
== Dependencies == | == Dependencies == | ||
=== Vector library === | === Vector library === | ||
Line 461: | Line 497: | ||
Float[] vOut = new Float[3] | Float[] vOut = new Float[3] | ||
If afB == 0 | If afB == 0 | ||
Debug. | Debug.TraceStack("VectorDivide: A script asked me to divide a vector by zero. I just returned a null vector instead.", 1) | ||
return vOut | return vOut | ||
EndIf | EndIf |