
// CPUStrokeTessellator.cpp - infrastructure implementation of polar stroking geometry on CPU

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

#include "countof.h"

#include "CPUStrokeTessellator.hpp"

#include <Cg/any.hpp>
#include <Cg/dot.hpp>
#include <Cg/inverse.hpp>
#include <Cg/isnan.hpp>
#include <Cg/mul.hpp>
#include <Cg/radians.hpp>

#include "cg4cpp_xform.hpp"

#include "../StrokingBufferState.hpp"

StrokingResults::StrokingResults()
    : Ppos_min_ndx(-1)
    , Pneg_min_ndx(-1)
    , Ppos_max_ndx(-1)
    , Pneg_max_ndx(-1)
    , Pneg_min_angle(0)
    , Ppos_min_angle(0)
    , Pneg_max_angle(0)
    , Ppos_max_angle(0)
{}

void StrokingResults::reset()
{
    polar_offset.clear();
    normal.clear();

    Pmid.clear();
    Ppos.clear();
    Pneg.clear();

    t.clear();
    p.clear();

    Ppos_obj.clear();
    Pneg_obj.clear();
    frieze_height.clear();

    gradient.clear();   // first order gradient
    gradient2.clear();  // second order gradient
    curvature.clear();
    Pradius.clear();
    Pinfinite.clear();
    theta.clear();
}

PathDrawState::PathDrawState()
    : accumulated_path_length(0)
    , next_distance_offset(0)
{
    link_offset.resize(StrokingBufferState::max_links);
    segment_distance.resize(StrokingBufferState::max_segments);
    segment_add_ops.resize(StrokingBufferState::max_segments);
}

void PathDrawState::dumpBuffers(size_t links)
{
    typedef unsigned int uint;

    printf("next_distance_offset = %d\n", next_distance_offset);

    const size_t count = 2*links;
    assert(count <= link_offset.size());
    const uint* offsets = &link_offset[0];
    for (size_t i=0; i<count; i+=2) {
        const uint from(offsets[i]);
        const uint to(offsets[i+1]);
        printf("%u: offset = %u to %u\n", uint(i>>1), from, to);
    }

    assert(next_distance_offset <= segment_distance.size());
    for (size_t i=0; i<next_distance_offset; i++) {
        printf("%u: distance = %f\n", uint(i), segment_distance[i]);
    }

    assert(next_distance_offset <= segment_add_ops.size());
    for (size_t i=0; i<next_distance_offset; i++) {
        printf("%u: add ops = %u\n", uint(i), segment_add_ops[i]);
    }
}

CPUFakeTessellator::CPUFakeTessellator()
{
    for (int i=0; i<2; i++) {
        gl_TessLevelInner[i] = -1;
    }
    for (int i=0; i<4; i++) {
        gl_TessLevelOuter[i] = -1;
    }
}

bool CPUFakeTessellator::isCulled()
{
    if (gl_TessLevelOuter[0] == 0) {
        return true;  // Culled.
    } else {
        // These should all be written to positive values for an emitted patch.
        assert(gl_TessLevelInner[0] > 0);
        assert(gl_TessLevelInner[1] > 0);
        assert(gl_TessLevelOuter[0] > 0);
        assert(gl_TessLevelOuter[1] > 0);
        assert(gl_TessLevelOuter[2] > 0);
        assert(gl_TessLevelOuter[3] > 0);
        return false;  // NOT cullled.
    }
}

typedef StrokeConfigTypes::ShadingMode ShadingMode;

static bool useTrivialCulling(const StrokeTessellatorSettings &init, ShadingMode shading_mode)
{
    if (StrokeConfigTypes::shadingModeNeedsArcLength(shading_mode)) {
        // Arc length shading methods depend on trivial culling being disabled to
        // accumulate overall arc length.
        return false;
    } else {
        return init.TrivialCulling();
    }
}

CPUStrokeTessellatorSettingsUniforms::CPUStrokeTessellatorSettingsUniforms(const StrokeTessellatorSettings &init,
                                                                           ShadingMode shading_mode)
    : trivial_culling(useTrivialCulling(init, shading_mode))
    , max_turning_angle_delta(init.MaxTurningAngleDelta())
    , min_window_stroke_radius(init.EffectiveMinWindowStrokeRadius())
    , draw_inside_of_round_joins(computeDrawInsideOfRoundJoins(init.InsideRoundJoinMode(), shading_mode))
#if SUPPORT_POLAR_VISUALIZE
    , extra_steps(init.ExtraSteps())
#endif
{}

CPUStrokeTessellatorUniforms::CPUStrokeTessellatorUniforms(const StrokeTessellatorSettings &init)
    : CPUStrokeTessellatorSettingsUniforms(init)
    , stroke_radius(0.5f)
    , max_join_steps(JoinEnumToMaxJoinSteps(ROUND_JOIN))
    , effective_miter_limit(2.0f)
    , miter_extension_limit(ComputeMiterExtensionLimit(2.0f))
    , cap_placement_to_type(CapStateToCapMask(ROUND_CAP))
    // Zero-initialize these float4 vectors...
    , viewport_scale(0)
    , u1234(0)
    , ab1ab2(0)
    , c1c2(0)
    // Uniforms for arc length texture coordinate generation
    , next_distance_offset_dummy(0)
    , next_distance_offset(next_distance_offset_dummy)
    , pass_number(0)
    , path_offset(0)
    , link_offset(NULL)
    , segment_distance(NULL)
    , segment_add_ops(NULL)
{}

CPUStrokeTessellatorUniforms::CPUStrokeTessellatorUniforms(const StrokeTessellatorSettings &init,
                                                           ShadingMode shading_mode,
                                                           PathDrawState& state)
    : CPUStrokeTessellatorSettingsUniforms(init, shading_mode)
    , stroke_radius(0.5f)
    , stroke_radius_fraction(0)
    , max_join_steps(JoinEnumToMaxJoinSteps(ROUND_JOIN))
    , effective_miter_limit(2.0f)
    , miter_extension_limit(ComputeMiterExtensionLimit(2.0f))
    , cap_placement_to_type(CapStateToCapMask(ROUND_CAP))
    // Zero-initialize these float4 vectors...
    , viewport_scale(0)
    , u1234(0)
    , ab1ab2(0)
    , c1c2(0)
    // Uniforms for arc length texture coordinate generation
    , next_distance_offset_dummy(0)
    , next_distance_offset(state.next_distance_offset)
    , pass_number(0)
    , path_offset(0)
    , link_offset(&state.link_offset[0])
    , segment_distance(&state.segment_distance[0])
    , segment_add_ops(&state.segment_add_ops[0])
{}

CPUStrokeTessellatorUniforms::CPUStrokeTessellatorUniforms(const StrokeTessellatorSettings &init,
                                                           ShadingMode shading_mode,
                                                           PathDrawState& state,
                                                           float stroke_width,
                                                           float arc_length_extension,
                                                           int mjs,
                                                           float eml,
                                                           unsigned int cptt)
    : CPUStrokeTessellatorSettingsUniforms(init, shading_mode)
    , stroke_radius(0.5f*stroke_width)
    , stroke_radius_fraction(0.5f*stroke_width*arc_length_extension)
    , max_join_steps(mjs)
    , effective_miter_limit(eml)
    , miter_extension_limit(ComputeMiterExtensionLimit(eml))
    , cap_placement_to_type(cptt)
    // Zero-initialize these float4 vectors...
    , viewport_scale(0)
    , u1234(0)
    , ab1ab2(0)
    , c1c2(0)
    // Uniforms for arc length texture coordinate generation
    , next_distance_offset_dummy(0)
    , next_distance_offset(state.next_distance_offset)
    , pass_number(0)
    , path_offset(0)
    , link_offset(&state.link_offset[0])
    , segment_distance(&state.segment_distance[0])
    , segment_add_ops(&state.segment_add_ops[0])
    , arc_length_texgen_scale(0)
{}

// Constructors that pass along to CPUStrokeTessellatorUniforms
CPUStrokeTessellator::CPUStrokeTessellator(const StrokeTessellatorSettings &init,
                                           ShadingMode shading_mode,
                                           PathDrawState& state,
                                           float stroke_width, float arc_length_extension,
                                           float max_turning_angle_delta, int max_join_steps, float effective_miter_limit, unsigned int cap_placement_to_type)
    : CPUStrokeTessellatorUniforms(init, shading_mode, state, stroke_width, arc_length_extension, max_join_steps, effective_miter_limit, cap_placement_to_type)
    , stroke_space(OBJECT_SPACE)
    , results(NULL)
{}

CPUStrokeTessellator::CPUStrokeTessellator(const StrokeTessellatorSettings &init)
    : CPUStrokeTessellatorUniforms(init)
    , stroke_space(OBJECT_SPACE)
    , results(NULL)
{}

CPUStrokeTessellator::CPUStrokeTessellator(const StrokeTessellatorSettings &init, ShadingMode shading_mode, PathDrawState& state)
    : CPUStrokeTessellatorUniforms(init, shading_mode, state)
    , stroke_space(OBJECT_SPACE)
    , results(NULL)
{}

void CPUStrokeTessellator::setStrokingResults(StrokingResults* p)
{
    results = p;
}

// The (effective) miter limit is compared to the hypotenuse of the two triangles formed for a miter.
// The miter extension limit is for the outer edge length (the non-unit length edge) of these two triangles.
float CPUStrokeTessellatorUniforms::ComputeMiterExtensionLimit(float effective_miter_limit) const
{
    // For a right triangle (A,B,C) where A is the hypotenuse and B is length 1, C's length is:
    return sqrt(effective_miter_limit*effective_miter_limit-1);
}

static double sqdist(double2 v)
{ 
    return dot(v,v);
}

static double determinant(double2 a, double2 b)
{
    return a.x*b.y - a.y*b.x;
}

void CPUStrokeTessellator::setStrokeSpace(StrokeSpaceEnum ss)
{
    stroke_space = ss;
}

void CPUStrokeTessellatorUniforms::loadTransformUniforms(const float3x3& inverse_object_to_window_space)
{
    // Name matrix elements; elements a3, b3, & c3 have no bearing on result.
    const double a1 = inverse_object_to_window_space[0][0];
    const double a2 = inverse_object_to_window_space[0][1];
    const double b1 = inverse_object_to_window_space[1][0];
    const double b2 = inverse_object_to_window_space[1][1];
    const double c1 = inverse_object_to_window_space[2][0];
    const double c2 = inverse_object_to_window_space[2][1];

    const double2 a = double2(a1,a2);
    const double2 b = double2(b1,b2);
    const double2 c = double2(c1,c2);

    // Uniform common scalar subexpressions.
    const double u1 = determinant(c,a);  // Used by AA & BB
    const double u2 = determinant(c,b);  // Used by AA & BB
    const double u3 = determinant(a,b);  // Used by just AA
    const double u4 = sqdist(c);         // Used by DD, EE, & FF

    const double2 ab1 = double2(a1,b1);
    const double2 ab2 = double2(a2,b2);

    u1234 = float4(u1, u2, u3, u4);
    ab1ab2 = float4(ab1, ab2);
    c1c2 = float4(c1, c2, 0, 0);
}

void CPUStrokeTessellatorUniforms::setStrokeRadius(float radius)
{
    stroke_radius = radius;
}

void CPUStrokeTessellatorUniforms::setStrokeWidth(float width)
{
    stroke_radius = 0.5f*width;
}

void CPUStrokeTessellatorUniforms::setStrokeRadiusFraction(float value)
{
    stroke_radius_fraction = value;
}

void CPUStrokeTessellatorUniforms::setMaxJoinSteps(int v)
{
    max_join_steps = v;
}

void CPUStrokeTessellatorUniforms::setCapMask(unsigned int cap_mask)
{
    cap_placement_to_type = cap_mask;
}

void CPUStrokeTessellatorUniforms::setEffectiveMiterLimit(float v)
{
    effective_miter_limit = v;
    miter_extension_limit = ComputeMiterExtensionLimit(effective_miter_limit);
}

void CPUStrokeTessellator::setTransforms(const float3x3& mv, const float3x3& p, const float3x3& mvp, const float2& wh)
{
    modelview = mv;
    projection = p;
    modelview_projection = mvp;
    assert(wh.x > 0);
    assert(wh.y != 0);
    viewport_scale = float4(0.5*wh, 2.0/wh);
    object_to_window_space = mul(scale(viewport_scale.xy), modelview_projection);
    inverse_object_to_window_space = inverse(object_to_window_space);

    loadTransformUniforms(inverse_object_to_window_space);
}

void CPUStrokeTessellator::setIdentityTransforms()
{
    const float3x3 identity = float3x3(1,0,0, 0,1,0, 0,0,1);

    modelview = identity;
    projection = identity;
    modelview_projection = identity;
    viewport_scale = float4(0.5, 0.5, 2.0, 2.0);
    object_to_window_space = identity;
    inverse_object_to_window_space = identity;

    loadTransformUniforms(inverse_object_to_window_space);
}

bool CPUStrokeTessellator::invalidTransforms()
{
    // Is element of any row of the modelview_projection or
    // inverse_object_to_window_space matrices not a number?
    for (int i=0; i<3; i++) {
        if (any(Cg::isnan(modelview_projection[i]))) {
            return true;
        }
        if (any(Cg::isnan(inverse_object_to_window_space[i]))) {
            return true;
        }
    }
    // Every element is a valid number so return false.
    return false;
}

// See CPUStrokeTessellatorControl.cpp for control shader implementation

// See CPUStrokeTessellatorEvaluation.cpp for evaluation shader implementation
