
// CommonStrokeTessellatorControl.hpp - common GPU and CPU polar stroking control

#ifndef __CommonStrokeTessellatorControl_hpp__
#define __CommonStrokeTessellatorControl_hpp__

#include "CommonStrokeTessellatorPrivate.hpp"
#include "CommonStrokeTessellatorLinkType.hpp"
#include "CommonStrokeTessellatorTangentChangeMap.hpp"

#include <Cg/max.hpp>
#include <Cg/min.hpp>
#include <Cg/ceil.hpp>

// Return clamped_steps
float StrokeTessellatorMETHOD linkControlProcess(int link_type)
{
    stashed_link_type = link_type;
    
    // Post trivial culling...    

    // Is cap or join?
    if (vertex_id[1] == 0) {
        // Yes...
        // Is join and that's round (indicated by max_join_steps > 2)?
        if (vertex_id[5] != 0 && max_join_steps > 2) {
            // Yes, mark as round join.
            PER_THREAD link_state[TID] = 2;
        } else {
            // No, mark as cap or non-round join.
            PER_THREAD link_state[TID] = 1;
        }
    } else {
        // No, assign 0 for neither cap nor join (cubic or quadratic segment).
        PER_THREAD link_state[TID] = 0;
    }
    
    CONST bool is_quadratic = isQuadratic(link_type);
    FOR_THREAD(2) link_state[TID] = int(is_quadratic);  // link_state[2] passes along isQuadratic
    if (is_quadratic) {
        CONST bool discard_patch = polarQuadraticStroking();
        if (discard_patch) {
            return 0;
        }
    } else {
        bool discard_patch = polarCubicStroking(link_type);
        if (discard_patch) {
            return 0;
        }
    }

    CONST float ideal_steps = delta_steps[3];
    assert(ideal_steps <= max_steps);

    // More than 1,302 (21*62) steps would overflow GPU's maximum patch tessellation.
    float clamped_steps = min(float(max_steps), ideal_steps);    

    FOR_THREAD(3) link_state[TID] = 0;  // treat_as_miter is false
    if (isJoinOrCap(link_type)) {
        clamped_steps = miterHandling(link_type, clamped_steps);
        PER_THREAD if (TID > 0) delta_steps[TID] = clamped_steps;  
        FOR_THREAD(0) dtheta_dsteps[TID] = delta[1]/delta_steps[1];        
    } else {
        // Always draw at least one step so straight line segments render.
        clamped_steps = max(1.0f, clamped_steps);
#if 0  // DEBUGGING MODE; set to 1 to draw just joins, without path segments.
        clamped_steps = 0;
#endif
    }

    // A "none" join or cap can result in zero clamped_steps...
    bool discard_patch = (clamped_steps < 1);
    if (discard_patch) {
        return 0;
    }

    FOR_THREAD(3) {
        // delta_D0AB1 = D0AB1/clamped_steps == (D0A + DAB + DB1)/clamped_steps
        delta[TID] = delta[TID]/clamped_steps;
    }

    return clamped_steps;
}

// Return clamped_steps
float StrokeTessellatorMETHOD linkControlClipProcess(int link_type)
{
    // Trivial clipper attempts to discard entire patch with 2D view frustum test.
    if (bool(trivial_culling)) {
        bool cull = trivialCulling(link_type);
        if (cull) {
            return 0;
        }
    }

    return linkControlProcess(link_type);
}

void StrokeTessellatorMETHOD linkControlShader()
{
    PER_THREAD_COUNT(4) cp[TID] = v[TID];

    CONST int link_type = analyzeLink();
    CONST float clamped_steps = linkControlClipProcess(link_type);
    if (clamped_steps > 0) {
        outputLinkPatch(clamped_steps);
    } else {
        discardLinkPatch();
    }
}

#endif // __CommonStrokeTessellatorControl_hpp__
