
// Paint.hpp - implementation of paint

#include "Paint.hpp"

void Paint::resetToDefault()
{
    stroke_width = 1.0f;
    arc_length_extension = 0;

    // Match SVG's initial stroke-linecap property
    initial_end_cap = NO_CAP;
    terminal_end_cap = NO_CAP;
    initial_dash_cap = NO_CAP;
    terminal_dash_cap = NO_CAP;

    join_style = MITER_REVERT;  // Match SVG's initial stroke-linejoin property
    miter_limit = 4.0f;  // Match SVG's initial stroke-miterlimit property
    fill_color = float3(0,0,0);  // perceptually linear RGB
    stroke_color = float3(0,0,0);  // perceptually linear RGB
    fill_opacity = 1.0f;
    stroke_opacity = 1.0f;
}

Paint::Paint()
{
    resetToDefault();
}

Paint::Paint(const Paint& from)
{
    stroke_width = from.stroke_width;
    arc_length_extension = from.arc_length_extension;
    initial_end_cap = from.initial_end_cap;
    terminal_end_cap = from.terminal_end_cap;
    initial_dash_cap = from.initial_dash_cap;
    terminal_dash_cap = from.terminal_dash_cap;
    join_style = from.join_style;
    miter_limit = from.miter_limit;
    fill_color = from.fill_color;
    stroke_color = from.stroke_color;
    fill_opacity = from.fill_opacity;
    stroke_opacity = from.stroke_opacity;
}

void Paint::setJoinStyle(JoinEnum js)
{
    assert(join_style < JOIN_ENUM_COUNT);
    join_style = js;
}

void Paint::setStrokeWidth(float sw)
{
    stroke_width = sw;
}

void Paint::increaseStrokeWidth(float delta)
{
    stroke_width += delta;
}

void Paint::decreaseStrokeWidth(float delta)
{
    float new_width = stroke_width - delta;
    if (new_width > 0) {
        stroke_width = new_width;
    }
}

float Paint::ArcLengthExtension() const
{
    return arc_length_extension;
}

void Paint::setArcLengthExtension(float ale)
{
    arc_length_extension = ale;
}

void Paint::increaseArcLengthExtension(float delta)
{
    arc_length_extension += delta;
}

void Paint::decreaseArcLengthExtension(float delta)
{
    float new_extension = arc_length_extension - delta;
    if (new_extension > 0) {
        arc_length_extension = new_extension;
    } else {
        arc_length_extension = 0;
    }
}

void Paint::setMiterLimit(float ml)
{
    miter_limit = ml;
}

void Paint::increaseMiterLimit(float delta)
{
    miter_limit += delta;
}

void Paint::decreaseMiterLimit(float delta)
{
    float new_limit = miter_limit - delta;
    if (new_limit > 0) {
        miter_limit = new_limit;
    }
}

Paint::JoinEnum Paint::JoinStyle() const
{
    return join_style;
}

StrokeTessellator::JoinEnum Paint::StrokeTessellatorJoinStyle() const
{
    // Ensure identity mapping between StrokeTessellator::JoinEnum & Paint::JoinEnum.
    assert(JoinEnum(StrokeTessellator::ROUND_JOIN) == ROUND_JOIN);
    assert(JoinEnum(StrokeTessellator::BEVEL) == BEVEL);
    assert(JoinEnum(StrokeTessellator::TRIANGULAR_JOIN) == TRIANGULAR_JOIN);
    assert(JoinEnum(StrokeTessellator::MITER_REVERT) == MITER_REVERT);
    assert(JoinEnum(StrokeTessellator::MITER_TRUNCATE) == MITER_TRUNCATE);
    assert(JoinEnum(StrokeTessellator::NO_JOIN) == NO_JOIN);
    assert(JoinEnum(StrokeTessellator::JOIN_ENUM_COUNT) == JOIN_ENUM_COUNT);
    return static_cast<StrokeTessellator::JoinEnum>(join_style);
}

StrokeTessellator::CapEnum Paint::StrokeTessellatorCapEnum(Paint::CapEnum cap)
{
    assert(CapEnum(StrokeTessellator::NO_CAP) == Paint::NO_CAP);
    assert(CapEnum(StrokeTessellator::ROUND_CAP) == Paint::ROUND_CAP);
    assert(CapEnum(StrokeTessellator::TRIANGULAR_CAP) == Paint::TRIANGULAR_CAP);
    assert(CapEnum(StrokeTessellator::SQUARE_CAP) == Paint::SQUARE_CAP);
    return static_cast<StrokeTessellator::CapEnum>(cap);
}
unsigned int Paint::StrokeTessellatorCapMask() const
{
    return StrokeTessellator::CapStateToCapMask(StrokeTessellatorCapEnum(InitialEndCap()),
                                                StrokeTessellatorCapEnum(TerminalEndCap()),
                                                StrokeTessellatorCapEnum(InitialDashCap()),
                                                StrokeTessellatorCapEnum(TerminalDashCap()));
}
float Paint::StrokeWidth() const
{
    return stroke_width;
}
float Paint::StrokeRadius() const
{
    return 0.5f*stroke_width;
}
float Paint::MiterLimit() const
{
    return miter_limit;
}
float Paint::EffectiveMiterLimit() const
{
    if (join_style == MITER_TRUNCATE) {
        // Qt's definition of miter limit with MITER_TRUNCATE is double the shader's miter limit value.
        return 2.0f*miter_limit;
    } else {
        return miter_limit;
    }
}

Paint::CapEnum Paint::InitialEndCap() const
{
    return initial_end_cap;
}
Paint::CapEnum Paint::TerminalEndCap() const
{
    return terminal_end_cap;
}
Paint::CapEnum Paint::InitialDashCap() const
{
    return initial_dash_cap;
}
Paint::CapEnum Paint::TerminalDashCap() const
{
    return terminal_dash_cap;
}

GLenum Paint::GLInitialEndCap() const
{
    return CapEnumToGLenum(initial_end_cap);
}
GLenum Paint::GLTerminalEndCap() const
{
    return CapEnumToGLenum(terminal_end_cap);
}
GLenum Paint::GLInitialDashCap() const
{
    return CapEnumToGLenum(initial_dash_cap);
}
GLenum Paint::GLTerminalDashCap() const
{
    return CapEnumToGLenum(terminal_dash_cap);
}

void Paint::setInitialEndCap(CapEnum cap)
{
    initial_end_cap = cap;
}
void Paint::setTerminalEndCap(CapEnum cap)
{
    terminal_end_cap = cap;
}
void Paint::setInitialDashCap(CapEnum cap)
{
    initial_dash_cap = cap;
}
void Paint::setTerminalDashCap(CapEnum cap)
{
    terminal_dash_cap = cap;
}
void Paint::setCapsIdentical(CapEnum cap)
{
    initial_end_cap = cap;
    terminal_end_cap = cap;
    initial_dash_cap = cap;
    terminal_dash_cap = cap;
}
float Paint::StrokeOpacity() const
{
    return stroke_opacity;
}
float Paint::FillOpacity() const
{
    return fill_opacity;
}

float3 Paint::StrokeColor() const
{
    return stroke_color;
}

float3 Paint::FillColor() const
{
    return fill_color;
}

void Paint::setStrokeColor(const float3 &c)
{
    stroke_color = c;
}

void Paint::setStrokeOpacity(float v)
{
    stroke_opacity = v;
}

void Paint::setFillColor(const float3 &c)
{
    fill_color = c;
}

void Paint::setFillOpacity(float v)
{
    fill_opacity = v;
}

void Paint::forceNoCaps()
{
    setCapsIdentical(Paint::NO_CAP);
}
