
// CommonStrokeTessellatorMath.hpp - common GPU and CPU polar stroking routines

#ifndef __CommonStrokeTessellatorMath_hpp__
#define __CommonStrokeTessellatorMath_hpp__

#include "CommonStrokeTessellatorPrivate.hpp"

#include <Cg/abs.hpp>
#include <Cg/dot.hpp>
#include <Cg/max.hpp>
#include <Cg/min.hpp>
#include <Cg/isfinite.hpp>  // for assertions

typedef double2 dvec2;
typedef double3 dvec3;

STATIC const float PI = 3.14159265358979323846f;
STATIC const float DOUBLE_PI = 2*PI;

STATIC double sqdist(dvec2 v)
{ 
    return dot(v,v);
}

// Compute 2D squared distance
STATIC float sqdist(vec2 v)
{ 
    return dot(v,v);
}

// Return orthogonal 2D vector (same as 90 degree rotation).
STATIC vec2 ortho(vec2 v)
{
    return vec2(v.y, -v.x);
}

// Return inverse orthogonal 2D vector (same as -90 degree rotation).
STATIC vec2 unortho(vec2 v)
{
    return vec2(-v.y, v.x);
}

// Reminder: determinant of a & b is twice the signed area of the triangle
// with verexes (0,0), (a.x,a.y), and (b.x,b.y)
STATIC float determinant(vec2 a, vec2 b)
{
    return a[0]*b[1] - a[1]*b[0];
}

STATIC double determinant(dvec2 a, dvec2 b)
{
    return a[0]*b[1] - a[1]*b[0];
}

STATIC double determinant(dvec2 a, vec2 b)
{
    return a.x*b.y - a.y*b.x;
}

// Saturate value to the [0,1] range, careful to return zero if value is NaN.
STATIC float saturate(float value)
{
    if (value > 1) {
        return 1;
    } else if (0 < value) {
        return value;
    } else {
        return 0;
    }
}

// Compute a-b when s is non-negative and otherwise b-a, making sure the return value is between 0 and 2*Pi.
STATIC float angleDelta(float a, float b, float s)
{
    float r;
    if(s >= 0) {
        r = b-a;
    } else{
        r = a-b;
    }
    if (r >= DOUBLE_PI) {
        r -= DOUBLE_PI;
    }
    if (r < 0) {
        r += DOUBLE_PI;
    }
    if (r == DOUBLE_PI){
        r = 0;
    }
    // r between 0 and 2PI
    return r;
}

// Compute absolute angle difference of theta1 and theta0 in radians.
STATIC float absoluteAngleDifference(float theta1, float theta0)
{
    CONST float absolute_difference = abs(theta1 - theta0);
    // Is absolute difference between angles greater than 180 degrees?
    if (absolute_difference > PI) {
        // Yes, return absolute difference minus 360 degrees.
        return abs(absolute_difference - DOUBLE_PI);
    } else {
        // No, return absolute difference as is.
        return absolute_difference;
    }
}

// Numerical approximation of arc tangent.
STATIC float approximate_atan2 (float y, float x)
{
    float t0, t1, t3, t4;

    t3 = abs(x);
    t1 = abs(y);
    t0 = max(t3, t1);
    t1 = min(t3, t1);
    t3 = 1 / t0;
    t3 = t1 * t3;

    t4 = t3 * t3;
    t0 =         - 0.013480470f;
    t0 = t0 * t4 + 0.057477314f;
    t0 = t0 * t4 - 0.121239071f;
    t0 = t0 * t4 + 0.195635925f;
    t0 = t0 * t4 - 0.332994597f;
    t0 = t0 * t4 + 0.999995630f;
    t3 = t0 * t3;

    t3 = abs(y) > abs(x) ? 1.570796327f - t3 : t3;
    t3 = x < 0 ?  3.141592654f - t3 : t3;
    t3 = y < 0 ? -t3 : t3;

    return t3;
}

extern bool use_native_atan2;

STATIC float my_atan2(float y, float x)
{
    if (use_native_atan2) {
        return atan2(y, x);
    } else {
        return approximate_atan2(y, x);
    }
}

// Perform perspective divide.
STATIC vec2 project(vec3 v)
{
    float w = v.z;
    return v.xy/w;
}

#endif // __CommonStrokeTessellatorMath_hpp__
