
// CommonStrokeTessellatorEvalPosition.hpp - common routines to evaluate positions

#ifndef __CommonStrokeTessellatorEvalPosition_hpp__
#define __CommonStrokeTessellatorEvalPosition_hpp__

#include "CommonStrokeTessellatorPrivate.hpp"

#define lerp(a,b,t) ((a) + (t)*((b)-(a)))

StrokeTessellatorMETHOD CPtype StrokeTessellatorMETHOD evalQuadraticBezierPosition(float t) CONST
{
    CPtype ab = lerp(cp[0], cp[1], t);
    CPtype bc = lerp(cp[1], cp[3], t);

    CPtype abc = lerp(ab, bc, t);
    return abc;
}

StrokeTessellatorMETHOD CPtype StrokeTessellatorMETHOD evalCubicBezierPosition(float t) CONST
{
    CPtype ab = lerp(cp[0], cp[1], t);
    CPtype bc = lerp(cp[1], cp[2], t);
    CPtype cd = lerp(cp[2], cp[3], t);

    CPtype abc = lerp(ab, bc, t);
    CPtype bcd = lerp(bc, cd, t);

    CPtype abcd = lerp(abc, bcd, t);
    return abcd;
}

StrokeTessellatorMETHOD CPtype StrokeTessellatorMETHOD evalCurvePosition(float t)
{
    CONST bool is_quadratic = IsQuadratic();
    if (is_quadratic) {
        // Interior of quadratic curved segment so evaluate position at t.
        return evalQuadraticBezierPosition(t);
    } else {  
        // Interior of curved segment so evaluate position at t.
        return evalCubicBezierPosition(t);
    }
}

#if AFFINE
// Simpler affine version that only compiles for GPU.
vec2 StrokeTessellatorMETHOD evalSegmentPosition(ParametricDirection pd)
{
    return evalCurvePosition(pd.t);
}
#else
// Version capable of handling negative w values.
vec3 StrokeTessellatorMETHOD evalSegmentPosition(ParametricDirection pd)
{
    // In the projective use case, a NaN t value indicates the point should
    // be forced to infinity.  When a conic is projectively transformed into
    // another coordinate space (i.e. window-space), negative w values become
    // possible and a disjoint hyperbola results that connects to itself through
    // "w=0" infinity.  When this happens, carefully generate directions for
    // such positions via "w=0" positions.
    if (CgNamespace isnan(pd.t)) {
        // Is this theta direction closer to the initial theta direction, theta[0],
        // or the terminal theta direction, theta[1]?
        CONST float theta0_diff = absoluteAngleDifference(pd.theta, theta[0]);
        CONST float theta1_diff = absoluteAngleDifference(pd.theta, theta[1]);
        CONST vec2 directed_normal = (theta0_diff < theta1_diff)
            ? -pd.normal   // Coming from infinity to connect to the disjoint
                           // portion of the segment starting in the initial
                           // direction so negate.
            :  pd.normal;  // Coming from infinity towards to connect to
                           // the disjoint portion of segment endding at the
                           // terminal direction so use gradient direction as is.
        CONST vec2 direction = ortho(directed_normal);
        return vec3(direction, 0);
    } else {
        // Normal case: simply evaluate the position at t.
        return evalCurvePosition(pd.t);
    }
}
#endif

#endif // __CommonStrokeTessellatorEvalPosition_hpp__
