Difference between revisions of "User:DavidJCobb/Rotation Library"

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.
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] * Sign(afMatrix[iIndex])
            ;
            ; 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
       OverwriteFloatArrayWith(fOutput, VectorNormalize(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 = OverwriteFloatArrayWith(new Float[3], aq, 1)
   Float[] v = new Float[3]
   OverwriteFloatArrayWith(qOut, VectorNegate(v))
  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
{Positions and rotates one object relative to another. Position code is based on GetPosXYZRotateAroundRef, a function authored by Chesko that can be found on the Creation Kit wiki.}
{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[] Target = 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()
   ;
   ;
   ; Grab the parent-relative coordinates that we want to convert to
   ; Apply Z-axis rotation matrix. (Modify the final X- and Y-axis positions based on the Z rotation.)
  ; world-relative.
   ;
   ;
  Target[0] = afPositionOffset[0]
   Output[0] = (afPositionOffset[0] * Math.cos(Angles[2])) + (afPositionOffset[1] * Math.sin(-Angles[2]))
  Target[1] = afPositionOffset[1]
   Output[1] = (afPositionOffset[0] * Math.sin(Angles[2])) + (afPositionOffset[1] * Math.cos( Angles[2]))
  Target[2] = afPositionOffset[2]
   Output[2] = afPositionOffset[2]
  Float[] Vector = new Float[3]
  Vector[0] = Target[0]
  Vector[1] = Target[1]
  Vector[2] = Target[2]
   Output[0] = (Vector[0] * Math.cos(Angles[2])) + (Vector[1] * Math.sin(-Angles[2])) + (Vector[2] * 0)
   Output[1] = (Vector[0] * Math.sin(Angles[2])) + (Vector[1] * Math.cos( Angles[2])) + (Vector[2] * 0)
   Output[2] = (Vector[0] * 0) + (Vector[1] * 0) + (Vector[2] * 1)
   ;
   ;
   ; From the original: Y-axis rotation matrix.
   ; 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[1] = Output[1]
   Vector[2] = Output[2]
   Vector[2] = Output[2]
   Output[0] = (Vector[0] * Math.cos( Angles[1])) + (Vector[1] * 0) + (Vector[2] * Math.sin(Angles[1]))
   Output[0] = (Vector[0] * Math.cos( Angles[1])) + (Vector[2] * Math.sin(Angles[1]))
  Output[1] = (Vector[0] * 0) + (Vector[1] * 1) + (Vector[2] * 0)
   Output[2] = (Vector[0] * Math.sin(-Angles[1])) + (Vector[2] * Math.cos(Angles[1]))
   Output[2] = (Vector[0] * Math.sin(-Angles[1])) + (Vector[1] * 0) + (Vector[2] * Math.cos(Angles[1]))
   ;
   ;
   ; From the original: X-axis rotation matrix.
   ; Apply X-axis rotation matrix. (Modify the final Y- and Z-axis positions based on the X rotation.)
   ;
   ;
  Vector[0] = Output[0]
   Vector[1] = Output[1]
   Vector[1] = Output[1]
   Vector[2] = Output[2]
   Vector[2] = Output[2]
  Output[0] = (Vector[0] * 1) + (Vector[1] * 0) + (Vector[2] * 0)
   Output[1] = (Vector[1] * Math.cos(Angles[0])) + (Vector[2] * Math.sin(-Angles[0]))
   Output[1] = (Vector[0] * 0) + (Vector[1] * Math.cos(Angles[0])) + (Vector[2] * Math.sin(-Angles[0]))
   Output[2] = (Vector[1] * Math.sin(Angles[0])) + (Vector[2] * Math.cos( Angles[0]))
   Output[2] = (Vector[0] * 0) + (Vector[1] * Math.sin(Angles[0])) + (Vector[2] * Math.cos( Angles[0]))
   ;
   ;
   ; Finalize coordinates.
   ; Finalize coordinates.
Line 363: Line 449:
   ; CONSTRUCT ROTATION USING THIS LIBRARY.
   ; CONSTRUCT ROTATION USING THIS LIBRARY.
   ;
   ;
   Float[] qShelf = EulerToQuaternion(akParent.GetAngleX(), akParent.GetAngleY(), akParent.GetAngleZ())
   Float[] qParent = EulerToQuaternion(akParent.GetAngleX(), akParent.GetAngleY(), akParent.GetAngleZ())
   Float[] eBook = new Float[3]
   Float[] qChild  = EulerToQuaternion(afRotationOffset[0], afRotationOffset[1], afRotationOffset[2])
  eBook[0] = afRotationOffset[0]
   Float[] qDone = QuaternionMultiply(qParent, qChild)
  eBook[1] = afRotationOffset[1]
  eBook[2] = afRotationOffset[2]
  Float[] qBook = eBook
  qBook = EulerToQuaternion(qBook[0], qBook[1], qBook[2])
   Float[] qDone = QuaternionMultiply(qShelf, qBook)
   Float[] eDone = QuaternionToEuler(qDone)
   Float[] eDone = QuaternionToEuler(qDone)
   ;
   ;
   ; Spawn and return marker.
   ; 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 ==
=== Misc library ===
<pre>Scriptname CobbLibraryMisc
{Library for miscellaneous resource functions, including generic math stuff.}
Float Property FLT_EPSILON = 0.0000001192092896 AutoReadOnly
Float[] Function OverwriteFloatArrayWith(Float[] target, Float[] source, int offset = 0) Global
{Overwrites the elements of one array with the elements of another, starting at the given offset (in the array to be overwritten).}
  int iterator = 0
  While iterator < source.length
      If iterator + offset < target.length
        target[iterator + offset] = source[iterator]
      EndIf
      iterator += 1
  EndWhile
  return target
EndFunction
Int Function Sign(float a) Global
{Returns, as an integer, the sign of a float: -1, 0, or 1.}
  If a < 0
      Return -1
  ElseIf a > 0
      Return 1
  Else
      Return 0
  EndIf
EndFunction
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</pre>


=== 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.Trace("VectorDivide: A script asked me to divide a vector by zero. I just returned a null vector instead.")
       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
Anonymous user