Quaternion

From Director Online Wiki
Jump to: navigation, search
--***************************************************
--* QUATERNION
--***************************************************
--*
--* \file    Quaternion.ls
--* \brief   Quaternion class, new data type.
--* \author  Maltus
--* \version 1.1
--* \date    05/05/2009
--* \email   --
--* \todo    comment...
--*
--***************************************************
--* CONSTRUCTOR
--***************************************************
--*
--* <Quaternion> = script( <String> ).new()               -- default
--* <Quaternion> = script( <String> ).new( <List> )       -- <List> = <LinearList> / <PropList> / <AxisAngle>
--* <Quaternion> = script( <String> ).new( <Transform> )
--* <Quaternion> = script( <String> ).new( <Vector> )
--* <Quaternion> = script( <String> ).new( <Quaternion> ) -- copy
--*
--***************************************************
--* PUBLIC METHODS
--***************************************************
--*
--* <Symbol>     = <Quaternion>.ilk() / ilk( <Quaternion> )
--* <Boolean>    = ilk( <Quaternion>, <Symbol> )
--*
--* Predicate:
--* <Boolean>    = quaternionP( <Quaternion> )
--*
--* Operator: direct access (.x, .y, .z, .w) and bracket access ([])
--* <Float>      = getAt( <Integer> / <Symbol> / <String> )
--* <Float>      = setAt( <Integer> / <Symbol> / <String>, <Number> )
--*
--* <Void>       = invert()
--* <Void>       = oppose()
--* <Void>       = normalize()
--* <Void>       = identity()
--*
--* <Quaternion> = duplicate()
--* <Quaternion> = getInversed()
--* <Quaternion> = getOpposed()
--* <Quaternion> = getNormalized()
--*
--* <Quaternion> = multiply( <Quaternion> / <Transform> / <List> / <Vector> / <Number> )
--* <Quaternion> = add( <Quaternion> / <Transform> / <List> / <Vector> / <Number> )
--* <Quaternion> = subtract( <Quaternion> / <Transform> / <List> / <Vector> / <Number> )
--*
--* <Boolean>    = isEqualTo( <Quaternion> )
--* <Boolean>    = isIdentity()
--* <Boolean>    = isNormalized()
--*
--* <Float>      = dotProduct( <Quaternion> )
--* <Float>      = magnitude()
--*
--* <Transform>  = toTransform()
--* <Vector>     = toVector()
--* <AxisAngle>  = toAxisAngle()
--* <PropList>   = toPropList()
--* <LinearList> = toLineList()
--* <String>     = toString()
--*
--*
--***************************************************
--* PRIVATE METHODS
--***************************************************
--*
--* <Void>       = __setQuaternion()
--* <Void>       = __setQuatFromTransform( <Transform> )
--* <Void>       = __setQuatFromVector( <Vector> )
--* <Void>       = __setQuatFromUndefList( <PropList> / <LinearList> / <AxisAngle> )
--*
--* <Void>       = __setPropListFromAxisAngle( <AxisAngle> )
--* <Void>       = __listNormalize( <PropList> ) 'Optionnal'
--*
--* <Boolean>    = __isAxisAngle( <AxisAngle> )
--* <Boolean>    = __isLineList( <LinearList> )
--* <Boolean>    = __isPropList( <PropList> )
--*
--* <Float>      = __arcCos( <Number> )
--*
--* <Void>       = __throwError( <PropList> )
--* <Void>       = __throwPrivate()
--*
--*
--***************************************************
 
-- Constant
property DEGTORAD
property RADTODEG
property EPSILON
 
-- Public property
property quaternionP
 
-- Private properties
property ancestor
property p_sScriptName
 
 
--***************************************************
--* CONSTRUCTOR
--***************************************************
--*
--***************************************************
on new( me, aValue )  
 
  DEGTORAD         = PI / float(180)
  RADTODEG         = float(180) / PI
  EPSILON          = 0.0001
  --  
  __setQuaternion( VOID, me )
  --
  case ilk( aValue ) of
    #Void      : nothing
    #Transform : __setQuatFromTransform( VOID, me, aValue )
    #Vector    : __setQuatFromVector( VOID, me, aValue )
    #List      : __setQuatFromUndefList( VOID, me, aValue )
    #Quaternion: me.duplicate( aValue )
    otherwise  : __throwError( VOID, me, [#Error: "Value type mismatch !", #ID: "10505200901"] )
  end case  
  --
  return( me )
end
 
 
--***************************************************
--* GETAT
--***************************************************
--*
--***************************************************
on getAt( me, aProp )
  --  
  case ilk( aProp ) of
    #Symbol  : iPos = ([#x, #y, #z, #w]).getPos( aProp )
    #String  : iPos = ([#x, #y, #z, #w]).getPos( symbol( aProp ) )
    #Integer : iPos = aProp
    otherwise: iPos = 0
  end case
  --
  if ( (iPos <= 0) OR (iPos > 4) ) then __throwError( VOID, me, [#Error: "Argument out of range !", #ID: "10505200902"] )
  --
  return( (me.ancestor)[iPos] )
end
 
 
--***************************************************
--* SETAT
--***************************************************
--*
--***************************************************
on setAt( me, aProp, aValue )
  --
  case ilk( aProp ) of
    #Symbol  : iPos = ([#x, #y, #z, #w]).getPos( aProp )
    #String  : iPos = ([#x, #y, #z, #w]).getPos( symbol( aProp ) )
    #Integer : iPos = aProp
    otherwise: iPos = 0
  end case
  --
  if ( (iPos <= 0) OR (iPos > 4) ) then __throwError( VOID, me, [#Error: "Argument out of range !", #ID: "10505200903"] )
  if not( ilk( aValue, #Number ) ) then __throwError( VOID, me, [#Error: "Number expected !", #ID: "10505200904"] )
  (me.ancestor)[iPos] = float( aValue )
  --
  return( (me.ancestor)[iPos] )
end
 
 
--***************************************************
--* ILK
--***************************************************
--*
--***************************************************
on ilk( me )
  --
  iCnt = _movie.paramCount()
  if (iCnt > 2) then __throwError( VOID, me, [#Error: "Wrong number of argument !", #ID: "10505200905"] )
  --
  if (iCnt = 1) then
 
    if integerP( me )    then return( #Integer )
    if floatP( me )      then return( #Float )
    if stringP( me )     then return( #String )
    if symbolP( me )     then return( #Symbol )
    if voidP( me )       then return( #Void )
    if quaternionP( me ) then return( #Quaternion )
    if (objectP( me ) AND not(listP( me ))) then return( #Object ) -- << must be after your own predicate !
 
    __throwError( VOID, me, [#Error: "Unknown type !", #ID: "10505200906"] )
 
  else
 
    case( _movie.param(iCnt) ) of
      #Integer   : return( integerP( me ) )
      #Float     : return( floatP(  me ) )
      #Number    : return( integerP( me ) OR floatP( me ) )
      #String    : return( stringP( me ) )
      #Symbol    : return( symbolP( me ) )
      #Void      : return( voidP( me ) )
      #Quaternion: return( quaternionP( me ) )
      #Object    : return( (objectP( me ) AND not(listP( me ))) )     
      otherwise  : return( FALSE )
    end case    
 
  end if
  --  
end
 
 
--***************************************************
--* QUATERNIONP
--***************************************************
--*
--***************************************************
on quaternionP( me )
  --  
  if ( objectP( me ) AND not(listP( me )) ) then
    if not( voidP(me.getaProp(#p_sScriptName)) ) then
      if ( string( me ) contains (me.p_sScriptName) ) then
        return( TRUE )
      end if      
    end if    
  end if
  --
  return( FALSE )  
end
 
 
--***************************************************
--* INVERT
--***************************************************
--*
--***************************************************
on invert( me )
  --
  (me.ancestor)[1] = - (me.ancestor)[1]
  (me.ancestor)[2] = - (me.ancestor)[2]
  (me.ancestor)[3] = - (me.ancestor)[3]
  --
  return
end
 
 
--***************************************************
--* OPPOSE
--***************************************************
--*
--***************************************************
on oppose( me )
  --
  me.ancestor = (me.ancestor) * float(-1)
  --
  return
end
 
 
--***************************************************
--* NORMALIZE
--***************************************************
--*
--***************************************************
on normalize( me )
  --
  __listNormalize( VOID, me )
  --
  return
end
 
 
--***************************************************
--* IDENTITY
--***************************************************
--*
--***************************************************
on identity( me )  
  me.ancestor = [#x: float(0), #y: float(0), #z: float(0), #w: float(1)] 
  return
end
 
 
--***************************************************
--* DUPLICATE
--***************************************************
--*
--***************************************************
on duplicate( me )  
  return( _movie.script( (me.p_sScriptName) ).new( me ) ) 
end
 
 
--***************************************************
--* GETINVERSE
--***************************************************
--*
--***************************************************
on getInversed( me )  
  lInversed    = ( (me.ancestor).duplicate() ) * float(-1)
  lInversed[4] = - lInversed[4]
  return( _movie.script( (me.p_sScriptName) ).new( lInversed ) )
end
 
 
--***************************************************
--* GETOPPOSED
--***************************************************
--*
--***************************************************
on getOpposed( me )  
  lOpposed = ( (me.ancestor).duplicate() ) * float(-1)  
  return( _movie.script( (me.p_sScriptName) ).new( lOpposed ) )
end
 
 
--***************************************************
--* GETNORMALIZED
--***************************************************
--*
--***************************************************
on getNormalized( me )  
  lNormalized = (me.ancestor).duplicate()  
  __listNormalize( VOID, me, lNormalized ) 
  return( _movie.script( (me.p_sScriptName) ).new( lNormalized ) )
end
 
 
--***************************************************
--* MULTIPLY
--***************************************************
--*
--***************************************************
on multiply( me, aValue ) 
  --
  case ilk( aValue ) of
    #Quaternion: lValue = (aValue.ancestor).duplicate()
    #Number    : lValue = [#x: float(aValue), #y: float(aValue), #z: float(aValue), #w: float(aValue)]
    #Transform, #Vector, #List : lValue = (_movie.script( (me.p_sScriptName) ).new( aValue )).ancestor    
    otherwise  : __throwError( VOID, me, [#Error: "Value type mismatch !", #ID: "10505200907"] )
  end case
  --
  X1 = (me.ancestor)[1]
  Y1 = (me.ancestor)[2]
  Z1 = (me.ancestor)[3]
  W1 = (me.ancestor)[4]
  --
  X2 = lValue[1]
  Y2 = lValue[2]
  Z2 = lValue[3]
  W2 = lValue[4]
  lValue = VOID
  --  
  return( _movie.script( (me.p_sScriptName) ).new( [\
W1 * X2 + X1 * W2 + Y1 * Z2 - Z1 * Y2,\
W1 * Y2 + Y1 * W2 + Z1 * X2 - X1 * Z2,\
W1 * Z2 + Z1 * W2 + X1 * Y2 - Y1 * X2,\
W1 * W2 - X1 * X2 - Y1 * Y2 - Z1 * Z2] ) )     
end
 
 
--***************************************************
--* ADD
--***************************************************
--*
--***************************************************
on add( me, aValue ) 
  --
  case ilk( aValue ) of
    #Quaternion: lValue = (aValue.ancestor).duplicate()
    #Number    : lValue = [#x: float(aValue), #y: float(aValue), #z: float(aValue), #w: float(aValue)]
    #Transform, #Vector, #List : lValue = (_movie.script( (me.p_sScriptName) ).new( aValue )).ancestor 
    otherwise  : __throwError( VOID, me, [#Error: "Value type mismatch !", #ID: "10505200908"] )    
  end case
  --
  X = (me.ancestor)[1] + lValue[1]
  Y = (me.ancestor)[2] + lValue[2]
  Z = (me.ancestor)[3] + lValue[3]
  W = (me.ancestor)[4] + lValue[4]
  lValue = VOID
  --
  return( _movie.script( (me.p_sScriptName) ).new( [X, Y, Z, W] ) )
end
 
 
--***************************************************
--* SUBTRACT
--***************************************************
--*
--***************************************************
on subtract( me, aValue ) 
  --
  case ilk( aValue ) of
    #Quaternion: lValue = (aValue.ancestor).duplicate()
    #Number    : lValue = [#x: float(aValue), #y: float(aValue), #z: float(aValue), #w: float(aValue)]
    #Transform, #Vector, #List : lValue = (_movie.script( (me.p_sScriptName) ).new( aValue )).ancestor  
    otherwise  : __throwError( VOID, me, [#Error: "Value type mismatch !", #ID: "10505200909"] )    
  end case
  --
  X = (me.ancestor)[1] - lValue[1]
  Y = (me.ancestor)[2] - lValue[2]
  Z = (me.ancestor)[3] - lValue[3]
  W = (me.ancestor)[4] - lValue[4]
  lValue = VOID
  --
  return( _movie.script( (me.p_sScriptName) ).new( [X, Y, Z, W] ) )
end
 
 
--***************************************************
--* ISEQUALTO
--***************************************************
--*
--***************************************************
on isEqualTo( me, aValue ) 
  --
  if not( ilk( aValue, #Quaternion ) ) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200910"] )
  --
  return(\
( abs( (me.ancestor)[1] - (aValue.ancestor)[1] ) <= EPSILON ) AND\
( abs( (me.ancestor)[2] - (aValue.ancestor)[2] ) <= EPSILON ) AND\
( abs( (me.ancestor)[3] - (aValue.ancestor)[3] ) <= EPSILON ) AND\
( abs( (me.ancestor)[4] - (aValue.ancestor)[4] ) <= EPSILON ) )
end
 
 
--***************************************************
--* ISIDENTITY
--***************************************************
--*
--***************************************************
on isIdentity( me )  
  --
  if (\
( abs( (me.ancestor)[1] ) <= EPSILON ) AND\
( abs( (me.ancestor)[2] ) <= EPSILON ) AND\
( abs( (me.ancestor)[3] ) <= EPSILON ) AND\
( abs( (me.ancestor)[4] - float(1) ) <= EPSILON ) ) then return( TRUE )
  --
  return( FALSE )
end
 
 
--***************************************************
--* ISNORMALIZED
--***************************************************
--*
--***************************************************
on isNormalized( me )
  --
  if ( abs( me.magnitude() - float(1) ) <= EPSILON ) then return( TRUE )
  --
  return( FALSE )  
end
 
 
--***************************************************
--* DOTPRODUCT
--***************************************************
--*
--***************************************************
on dotProduct( me, aValue ) 
  --
  if not( ilk( aValue, #Quaternion ) ) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200911"] )
  --
  return(\
( (me.ancestor)[1] * aValue[1] ) + ( (me.ancestor)[2] * aValue[2] ) +\
( (me.ancestor)[3] * aValue[3] ) + ( (me.ancestor)[4] * aValue[4] ) )  
end
 
 
--***************************************************
--* MAGNITUDE
--***************************************************
--*
--***************************************************
on magnitude( me ) 
  --
  return( sqrt(\
(me.ancestor)[1] * (me.ancestor)[1] +\
(me.ancestor)[2] * (me.ancestor)[2] +\
(me.ancestor)[3] * (me.ancestor)[3] +\
(me.ancestor)[4] * (me.ancestor)[4] ) )  
end
 
 
--***************************************************
--* TOTRANSFORM
--***************************************************
--*
--***************************************************
on toTransform( me )
  --
  __listNormalize( VOID, me )
  --
  X1 = (me.ancestor)[1]
  Y1 = (me.ancestor)[2]
  Z1 = (me.ancestor)[3]
  W1 = (me.ancestor)[4]
  --
  X2 = X1 * X1
  Y2 = Y1 * Y1
  Z2 = Z1 * Z1
  W2 = W1 * W1
  --
  XY = X1 * Y1
  XZ = X1 * Z1
  YZ = Y1 * Z1
  --
  WX = W1 * X1
  WY = W1 * Y1
  WZ = W1 * Z1
  --
  trsf = transform()
  --
  trsf[1]  = float( 1.0 - 2.0 * (Y2 + Z2) )
  trsf[2]  = float( 2.0 * (XY + WZ) )
  trsf[3]  = float( 2.0 * (XZ - WY) )
  --
  trsf[5]  = float( 2.0 * (XY - WZ) )
  trsf[6]  = float( 1.0 - 2.0 * (X2 + Z2) )
  trsf[7]  = float( 2.0 * (YZ + WX) )
  --
  trsf[9]  = float( 2.0 * (XZ + WY) )
  trsf[10] = float( 2.0 * (YZ - WX) )
  trsf[11] = float( 1.0 - 2.0 * (X2 + Y2) )
  --
  return( trsf )
end
 
 
--***************************************************
--* TOVECTOR
--***************************************************
--*
--***************************************************
on toVector( me ) 
  --
  trsf   = toTransform( me )
  vEuler = trsf.rotation
  --
  return( vEuler )
end
 
 
--***************************************************
--* TOAXISANGLE
--***************************************************
--*
--***************************************************
on toAxisAngle( me ) 
  --
  __listNormalize( VOID, me )
  --
  X1 = (me.ancestor)[1]
  Y1 = (me.ancestor)[2]
  Z1 = (me.ancestor)[3]
  W1 = (me.ancestor)[4]  
  --
  fAngle     = __arcCos( VOID, me, W1 )
  fScale     = me.magnitude()  
  --
  if ((0 <= fAngle) OR (PI >= fAngle)) then __throwError( VOID, me, [#Error: "Angle must be between 0 and PI !", #ID: "10505200912"] )
  lAxisAngle = []
  --  
  if ( fScale <= EPSILON ) then
    lAxisAngle.add( vector(1, 0, 0) )
    lAxisAngle.add( float(0) )
  else if ( (X1 = float(0)) AND (Y1 = float(0)) AND (Z1 = float(0)) ) then
    lAxisAngle.add( vector(1, 0, 0) )
    lAxisAngle.add( float(0) )
  else
    vEuler = vector( X1 / fScale, Y1 / fScale, Z1 / fScale )
    vEuler.normalize()
    lAxisAngle.add( vEuler )
    lAxisAngle.add( fAngle * float(2) * RADTODEG )
  end if
  --
  return( lAxisAngle )
end
 
 
--***************************************************
--* TOPROPLIST
--***************************************************
--*
--***************************************************
on toPropList( me )
  --
  return( (me.ancestor).duplicate() )  
end
 
 
--***************************************************
--* TOLINELIST
--***************************************************
--*
--***************************************************
on toLineList( me )
  --  
  return( [ (me.ancestor)[1], (me.ancestor)[2], (me.ancestor)[3], (me.ancestor)[4] ] )
end
 
 
--***************************************************
--* TOSTRING
--***************************************************
--*
--***************************************************
on toString( me )
  --
  return( "Quaternion("&&"#x:"&&(me.ancestor)[1]&", #y:"&&(me.ancestor)[2]&", #z:"&&(me.ancestor)[3]&", #w:"&&(me.ancestor)[4]&&")" )
end
 
 
--***************************************************
--* __SETQUATERNION
--***************************************************
--*
--***************************************************
on __setQuaternion( PRIVATE, me )  
  --
  if not(voidP( PRIVATE )) then __throwPrivate( VOID, me )
  if not(objectP( me ))    then __throwError( VOID, me, [#Error: "Object expected !", #ID: "10505200913"] )  
  -- 
  sScript          = string( me )
  iWrdCnt          = sScript.word.count
  iStrLgth         = (sScript.word[2..iWrdCnt-2]).length
  me.p_sScriptName = (sScript.word[2..iWrdCnt]).char[2..(iStrLgth-1)]   
  --
  me.ancestor      = [ #x: float(0), #y: float(0), #z: float(0), #w: float(1) ]
  QuaternionP      = TRUE
  --
  return
end
 
 
--***************************************************
--* __SETQUATFROMTRANSFORM
--***************************************************
--*
--***************************************************
on __setQuatFromTransform( PRIVATE, me, aTransform )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200914"] )
  --    
  fTrace = 1 + aTransform[1] + aTransform[6] + aTransform[11]
  --  
  if ( fTrace > EPSILON ) then
    --
    fRoot = sqrt( fTrace ) * float(2)
    (me.ancestor)[1] = ( aTransform[10] - aTransform[7] ) / fRoot
    (me.ancestor)[2] = ( aTransform[3]  - aTransform[9] ) / fRoot
    (me.ancestor)[3] = ( aTransform[5]  - aTransform[2] ) / fRoot
    (me.ancestor)[4] = 0.25 * fRoot
    --
  else if ( (aTransform[1] > aTransform[6]) AND (aTransform[1] > aTransform[11]) ) then
    --
    fRoot = sqrt( float(1) + aTransform[1] - aTransform[6] - aTransform[11] ) * float(2)
    (me.ancestor)[1] = 0.25 * fRoot
    (me.ancestor)[2] = ( aTransform[5]  + aTransform[2] ) / fRoot
    (me.ancestor)[3] = ( aTransform[3]  + aTransform[9] ) / fRoot
    (me.ancestor)[4] = ( aTransform[10] - aTransform[7] ) / fRoot  
    --
  else if ( aTransform[6] > aTransform[11] ) then
    --
    fRoot = sqrt( float(1) + aTransform[6] - aTransform[1] - aTransform[11] ) * float(2)
    (me.ancestor)[1] = ( aTransform[5]  + aTransform[2] ) / fRoot
    (me.ancestor)[2] = 0.25 * fRoot
    (me.ancestor)[3] = ( aTransform[10] + aTransform[7] ) / fRoot
    (me.ancestor)[4] = ( aTransform[3]  - aTransform[9] ) / fRoot
    --
  else
    --
    fRoot = sqrt( float(1) + aTransform[11] - aTransform[1] - aTransform[6] ) * float(2)
    (me.ancestor)[1] = ( aTransform[3]  + aTransform[9] ) / fRoot
    (me.ancestor)[2] = ( aTransform[10] + aTransform[7] ) / fRoot
    (me.ancestor)[3] = 0.25 * fRoot
    (me.ancestor)[4] = ( aTransform[5]  - aTransform[2] ) / fRoot
    --
  end if  
  --
  __listNormalize( VOID, me )    
  --  
  return
end
 
 
--***************************************************
--* __SETQUATFROMVECTOR
--***************************************************
--*
--***************************************************
on __setQuatFromVector( PRIVATE, me, aEuler )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200915"] )
  --
  X = (aEuler.x * DEGTORAD) * 0.5
  Y = (aEuler.y * DEGTORAD) * 0.5
  Z = (aEuler.z * DEGTORAD) * 0.5
  --
  fCosRoll  = cos( X )
  fCosPitch = cos( Y )
  fCosYaw   = cos( Z )
  --
  fSinRoll  = sin( X )
  fSinPitch = sin( Y )  
  fSinYaw   = sin( Z )  
  --
  fCosPitchCosYaw = fCosPitch * fCosYaw 
  fSinPitchSinYaw = fSinPitch * fSinYaw  
  --
  (me.ancestor)[1] = fSinRoll * fCosPitchCosYaw     - fCosRoll * fSinPitchSinYaw
  (me.ancestor)[2] = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw
  (me.ancestor)[3] = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw
  (me.ancestor)[4] = fCosRoll * fCosPitchCosYaw     + fSinRoll * fSinPitchSinYaw
  --
  __listNormalize( VOID, me )
  --
  return  
end
 
 
--***************************************************
--* __SETQUATFROMUNDEFLIST
--***************************************************
--*
--***************************************************
on __setQuatFromUndefList( PRIVATE, me, aUndefList )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200916"] )
  --
  if ilk( aUndefList, #LinearList ) then
    iListCnt   = aUndefList.count()
    iAxisAngle = 2
    iLineList  = 4
    --
    if ( iListCnt = iAxisAngle ) then      
      if not( __isAxisAngle( VOID, me, aUndefList ) ) then __throwError( VOID, me, [#Error: "Axis angle expected !", #ID: "10505200917"] )
      me.ancestor = __setPropListFromAxisAngle( VOID, me, aUndefList )   
    else if ( iListCnt = iLineList ) then     
      if not( __isLineList( VOID, me, aUndefList ) ) then __throwError( VOID, me, [#Error: "Linear list expected !", #ID: "10505200918"] )
      me.ancestor = [#x: float(aUndefList[1]), #y: float(aUndefList[2]), #z: float(aUndefList[3]), #w: float(aUndefList[4])]      
    else
      __throwError( VOID, me, [#Error: "Undefined list !", #ID: "10505200919"] )
    end if
    --
  else if ilk( aUndefList, #PropList ) then   
    if not( __isPropList( VOID, me, aUndefList ) ) then __throwError( VOID, me, [#Error: "Property list expected !", #ID: "10505200920"] )
    me.ancestor = aUndefList.duplicate()
    --  
  end if
  --
  aUndefList  = VOID 
  --
  return
end
 
 
--***************************************************
--* __SETPROPLISTFROMAXISANGLE
--***************************************************
--*
--***************************************************
on __setPropListFromAxisAngle( PRIVATE, me, aAxisAngle )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200921"] )
  --
  vEuler     = aAxisAngle[1].getNormalized()
  fTheta     = aAxisAngle[2] * DEGTORAD
  fSin       = sin( fTheta * 0.5 )
  --
  lProperty   = [#x: float(0), #y: float(0), #z: float(0), #w: float(1)]
  lProperty.x = vEuler.x * fSin
  lProperty.y = vEuler.y * fSin
  lProperty.z = vEuler.z * fSin
  lProperty.w = cos( fTheta * 0.5 )
  --
  __listNormalize( VOID, me, lProperty )
  --
  return( lProperty )
end
 
 
--***************************************************
--* __LISTNORMALIZE
--***************************************************
--
--***************************************************
on __listNormalize( PRIVATE, me, aPropList )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200922"] )
  if (voidP(aPropList))     then aPropList = me.ancestor
  --
  fMagnitude = sqrt( aPropList[1] * aPropList[1] + aPropList[2] * aPropList[2] + aPropList[3] * aPropList[3] + aPropList[4] * aPropList[4] )
  --  
  if ( fMagnitude < EPSILON ) then
    aPropList[1] = float(0)
    aPropList[2] = float(0)
    aPropList[3] = float(0)
    aPropList[4] = float(1)
  else
    fLength      = float(1) / fMagnitude
    aPropList[1] = aPropList[1] * fLength
    aPropList[2] = aPropList[2] * fLength
    aPropList[3] = aPropList[3] * fLength
    aPropList[4] = aPropList[4] * fLength
  end if  
  --  
  return
end
 
 
--***************************************************
--* __ISAXISANGLE
--***************************************************
--*
--***************************************************
on __isAxisAngle( PRIVATE, me, aAxisAngle )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200923"] )
  --
  if (not(ilk( aAxisAngle, #LinearList ))) then return( FALSE )
  if ( aAxisAngle.count() <> 2 )           then return( FALSE )
  if (not(ilk( aAxisAngle[1], #Vector )))  then return( FALSE )
  if (not(ilk( aAxisAngle[2], #Number )))  then return( FALSE )
  --
  return( TRUE )
end
 
 
--***************************************************
--* __ISLINELIST
--***************************************************
--*
--***************************************************
on __isLineList( PRIVATE, me, aLineList )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200924"] )
  --
  if not(ilk( aLineList, #LinearList )) then return( FALSE )
  if ( aLineList.count() <> 4 )         then return( FALSE ) 
  if not(ilk( aLineList[1], #Number ))  then return( FALSE )
  if not(ilk( aLineList[2], #Number ))  then return( FALSE )
  if not(ilk( aLineList[3], #Number ))  then return( FALSE )
  if not(ilk( aLineList[4], #Number ))  then return( FALSE )
  --
  return( TRUE )
end
 
 
--***************************************************
--* __ISPROPLIST
--***************************************************
--*
--***************************************************
on __isPropList( PRIVATE, me, aPropList )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200925"] )
  --
  if not(ilk( aPropList, #PropList ))     then return( FALSE ) 
  if ( aPropList.count() <> 4 )           then return( FALSE )
  if ilk( aPropList.getaProp(#x), #VOID ) then return( FALSE )
  if ilk( aPropList.getaProp(#y), #VOID ) then return( FALSE )
  if ilk( aPropList.getaProp(#z), #VOID ) then return( FALSE )
  if ilk( aPropList.getaProp(#w), #VOID ) then return( FALSE )
  if not(ilk( aPropList.x, #Float ))      then return( FALSE )
  if not(ilk( aPropList.y, #Float ))      then return( FALSE )
  if not(ilk( aPropList.z, #Float ))      then return( FALSE )
  if not(ilk( aPropList.w, #Float ))      then return( FALSE )
  --
  return( TRUE )
end
 
 
--***************************************************
--* __ARCCOS
--***************************************************
--*
--***************************************************
on __arcCos( PRIVATE, me, x )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  if not(quaternionP( me )) then __throwError( VOID, me, [#Error: "Quaternion expected !", #ID: "10505200926"] )
  --
  x = float(x)
  --
  if (x < float(0)) then
    return( PI + atan( sqrt( float(1) - (x * x) ) / x ) )
  else if (x > 0) then
    return( atan( sqrt( float(1) - (x * x) ) / x ) )
  else
    return( PI * 0.5 )
  end if 
  --
end
 
 
--***************************************************
--* __THROWERROR
--***************************************************
--
--***************************************************
on __throwError( PRIVATE, me, aErrorList )
  --
  if not(voidP( PRIVATE ))  then __throwPrivate( VOID, me )
  --
  if ( ilk(_player.alertHook, #Object) AND (_system.environmentPropList[#runMode] = "Author") ) then    
    -- _player.alert(...)    
  else    
    trace( "Error:"&&aErrorList.Error )       
  end if
  --
  HALT
  --
  return
end
 
 
--***************************************************
--* __THROWPRIVATE
--***************************************************
--
--***************************************************
on __throwPrivate( PRIVATE, me )
  --
  if ( ilk(_player.alertHook, #Object) AND (_system.environmentPropList[#runMode] = "Author") ) then 
    -- _player.alert(...)     
  else
    trace( "Error: You couldn't access private method from your location !" )        
  end if
  --
  HALT
  --  
  return
end