/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Math functions specifically for computer graphics algorithms.
 * @remarks
 * REMINDERS: - Use MATH_EXCEPTION() instead of ASSERT().
 *            - C++ <cmath> is better than C <math.h> because C++ functions are overloaded.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define MATH_GRAPHICS_CC 1
#include <utility>
#include "base/module.hh"
using namespace base;
#include "world/module.hh"
#include "world/debug.hh"  // ASSERT_LAT_LON_DEG()
using namespace world;
#include "math/module.hh"
#include "math/funcs.hh"
#include "math/funcs_trig.hh"
#include "math/matrix.hh"
#include "math/matrix_funcs.hh"
#include "math/graphics.hh"

namespace math {

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  functions  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Compute a 3D vertex on a dome from (lat,lon) coordinates.
 *****************************************************************************/
WorldVertex
ComputeVertexOnDome( const Degree lat, const Degree lon, const fp radius )
{
ASSERT_LAT_LON_DEG(lat,lon);

    // Start at north pole.
    // Rotate vertically by latitude.
    const std::pair<fp,fp> latSinCos = SinCos<fp>( Deg2Rad( lat ) );
    fp z = latSinCos.second * radius;  // altitude (vertical axis)
    fp y = latSinCos.first  * radius;

    // Rotate horizontally by longitude.
    const std::pair<fp,fp> lonSinCos = SinCos<fp>( Deg2Rad( lon ) );
    fp x = lonSinCos.second * y;  // -- ORDER-DEPENDENT --
       y = lonSinCos.first  * y;

    return WorldVertex( x, y, z );
}

/*****************************************************************************
 * Compute angle between Y axis of two matrixs.
 *
 * This is used to support cloud sprites.
 * When view matrix is recomputed, the "roll degree" can be computed
 * using the change in the Y axis of the previous/next view matrixs.
 *****************************************************************************/
Degree
ComputeMatrixRollDegree( const Matrix& matrix, const Matrix& nextMatrix )
{
    // Invert mapping of current matrix.
    Matrix inverseMatrix = MatrixInvert( matrix );
    inverseMatrix.SetPosition( Vector3( 0.0f, 0.0f, 0.0f ) );

    // Transform the Y axis of the next matrix thru the inverse of the current one.
    const Vector3 nextYAxis = inverseMatrix * Vector3( nextMatrix[Yx],
                                                       nextMatrix[Yy],
                                                       nextMatrix[Yz] );

    // Transformations produce results in absolute space which
    // allows measuring angles in 2D by ignoring a 3rd dimension.
    // Compute 2D angle of current and next Y axis.
    const Vector3 currYAxis( 0.0f, 1.0f, 0.0f );
    const Radian rad = Angle2( Vector2( currYAxis[XX], currYAxis[YY] ),
                               Vector2( nextYAxis[XX], nextYAxis[YY] ) );
    if ( nextYAxis[XX] < 0 )  // for full 360 range
        return Rad2Deg( -rad );
    else
        return Rad2Deg( rad );
}

} // namespace math
