
// EqualGradientDirectionDeltaCubicSplitter.cpp - split cubics are even gradient direction intervals

#define _USE_MATH_DEFINES  // for M_PI
#include <math.h>

#include "EqualGradientDirectionDeltaCubicSplitter.hpp"

#include "common/CommonStrokeTessellatorMath.hpp"  // for ortho

#include <Cg/all.hpp>
#include <Cg/length.hpp>
//#include <Cg/lerp.hpp>   // implements vector linear interpolation
#define lerp(a,b,t) ((a) + (t)*((b)-(a)))  // workaround VS 2008 template issue

EqualGradientDirectionDeltaCubicSplitter::EqualGradientDirectionDeltaCubicSplitter(float angle_in_radians)
    : CPUStrokeTessellator(StrokeTessellatorSettings(angle_in_radians))
{
    setIdentityTransforms();
}

void EqualGradientDirectionDeltaCubicSplitter::processCubic(EqualGradientDirectionDeltaFunc func, const float2 plist[4], void *data)
{
    const bool culled = setupCubic(plist);
    if (culled) {
        // Do nothing; no callbacks.
        return;
    }

    const CPUStrokeTessellator::uint steps = Steps();
    assert(steps >= 1);
    // Number of steps should be bounded by the maximum turning angle.
    // Limit to number of steps is 2 Pi + the maximum turning angle
    // delta because the step count is "rounded up".
    // (2.001 is 2 + epsilon.)
    assert(steps * MaxTurningAngleDelta() <= 2.001*M_PI + MaxTurningAngleDelta());

    // Is there just one step (common situation)?
    if (steps == 1) {
        // Yes, save considerable work.
        const float2 start_direction = startDirection();
        const float2 end_direction = endDirection();

        // Make sure startRibDirection & endRibDirection 
        assert(all(start_direction == unortho(evalRibIndex(0).normal)));
        assert(all(end_direction == unortho(evalRibIndex(1).normal)));

        func(plist, start_direction, end_direction, data);
    } else {
        assert(steps > 1);
        const CPUStrokeTessellator::uint first_rib = 0;
        const ParametricDirection rib = evalRibIndex(first_rib);

        float prior_t = 0;
        assert(prior_t == rib.t);  // First rib's t should be zero.
        float2 start_position = plist[0];

        // Determine direction at control point a from polar stroking
        // that guarantees a non-degenerate direction.
        float2 rib_gradient_direction = ortho(rib.normal);
        float2 start_direction = rib_gradient_direction;
        // Expect rib gradient directions to be normalized, so length close to 1.
        assert(length(start_direction) > 0.9);
        assert(length(start_direction) < 1.1);

        for (CPUStrokeTessellator::uint rib_index=1; rib_index<=steps; rib_index++) {
            const ParametricDirection rib = evalRibIndex(rib_index);
            const float this_t = rib.t;
            assert(this_t >= prior_t);

            // Important for stability:  Split first based on the (larger) this_t
            // into first and second sub-segments.  THEN split the first
            // sub-segment based on the prior t, but adjusting the prior t for
            // the first sub-segment.
            //
            // This approach is more stable than splitting first by prior_t,
            // though this approach leads to two De Casteljau splits per iteration.

            // Perform De Casteljau's algorithm to split input cubic at this_t.
            const float2 ab1 = lerp(plist[0], plist[1], this_t),
                         bc1 = lerp(plist[1], plist[2], this_t),
                         cd1 = lerp(plist[2], plist[3], this_t),
                         abc1 = lerp(ab1, bc1, this_t),
                         bcd1 = lerp(bc1, cd1, this_t),
                         abcd1 = lerp(abc1, bcd1, this_t);

            // Use actual end point of input cubic as the last sub-segment's end position
            // to seam properly.
            const float2 end_position = rib_index<steps ? abcd1 : plist[3];

            if (start_position.x == end_position.x && start_position.y == end_position.y) {
                // Degenerate, co-located initial/terminal control points!
                // Ignore.
                continue;
            }

            // Perform (partial) De Casteljau's algorithm again to split input
            // cubic at prior_t, but adjusted to for the above split.
            const float adjusted_prior_t = prior_t/this_t;
            // Notice: No need to compute lerps for ab2, abc2, and abcd2.
            const float2 bc2 = lerp(ab1, abc1, adjusted_prior_t),
                         cd2 = lerp(abc1, abcd1, adjusted_prior_t),
                         bcd2 = lerp(bc2, cd2, adjusted_prior_t);

            // Determine direction at control point d from polar stroking
            // that guarantees a non-degenerate direction.
            const float2 rib_gradient_direction = ortho(rib.normal);
            const float2 end_direction = rib_gradient_direction;
            // Expect rib gradient directions to be normalized, so length close to 1.
            assert(length(end_direction) > 0.9);
            assert(length(end_direction) < 1.1);

            const float2 cp[4] = { start_position, bcd2, cd2, end_position };
            func(cp, start_direction, end_direction, data);

            // Prepare for next iteration...
            start_direction = end_direction;  // This iteration's start_direction becomes next iteration's end_direction.
            prior_t = this_t;  // This iteration's t becomes the next iteration's prior t.
            start_position = end_position;  // This iteration's end position becomes next iteration's start position.
        }
    }
}

// See CPUStrokeTessellatorControl.cpp for control shader implementation

// See CPUStrokeTessellatorEvaluation.cpp for evaluation shader implementation
