// VECTOR 1886 — v2.6 — vectorscope-radius false color, BT.1886 display-referred
// Federico Malpica, 2026-07-02
//
// Maps HOW FAR each pixel's color travels from the CENTER OF THE VECTORSCOPE
// (true BT.709 CbCr geometry — the plane the scope plots):
//     Cb = (B - Y) / 1.8556      Cr = (R - Y) / 1.5748
//     radius = sqrt(Cb^2 + Cr^2) / 0.5     (1.0 ~ scope edge, 100% primaries)
//
// v2.0 (Federico's spec): the single-hue heat shades are replaced by the
// EXPOSURE-style graduated palette — 10 numbered levels, cool near center
// warming outward, plus WHITE = beyond scale. Same legend language as
// EXPOSURE 1886, so the reading habit transfers: "skin is a 3, the seat is a 5."
//
// SCALE — set directly with the Scope Range Pct slider: the full 0-9 scale
// covers that percentage of the vectorscope radius, so EACH LEVEL = Range/10
// percent of scope. Examples:
//   Range 8 (default, Federico's working value): level = 0.8% steps, white
//     beyond 8% of scope — tuned to where restrained graded material lives
//   Range 20: level = 2% steps — punchier commercial material
//   Range 50: level = 5% steps — saturated/poster material
//   Range 100: level = 10% steps, white = scope edge — full-scope survey
// THE RANGE IS THE RULER: keep it identical on reference and your own shot
// or the level numbers don't compare.
//
// PARAMS:
//   Scope Range Pct: percentage of the vectorscope covered by the 0-9 scale
//     (min 1 — the math clamps there anyway, the slider now says so).
//   Paint Opacity: blend of the false color over the original image.
//   Legend Side: 0 = left edge, 1 = right edge — put reference and own-shot
//     legends on opposite sides in a wipe/split match.
//
// LEGEND: floating strip, 12 blocks — WHITE (beyond) on top, then 9..0, then a
//   dark RANGE readout block showing the current Scope Range Pct, so every
//   screenshot self-documents the ruler ("same Range both sides").
//
// v2.2: slider min 1 · Legend Side param · Range readout block in the legend.
// v2.4: Smooth Blend removed after field feedback (hard buckets ARE the read) ·
//   Show Legend is a real checkbox (no texture params, so the May quirk n/a).
// v2.5: log-spaced levels tried (x1.585 per level, 100:1 span).
// v2.6: log scale REMOVED same day (Federico) — linear stays: even level
//   steps of Range/10 percent are the read; Range is the one ruler.

DEFINE_UI_PARAMS(scopeRange, Scope Range Pct, DCTLUI_SLIDER_FLOAT, 8.0, 1.0, 100.0, 1.0)
DEFINE_UI_PARAMS(paintOpacity, Paint Opacity, DCTLUI_SLIDER_FLOAT, 1.0, 0.0, 1.0, 0.01)
DEFINE_UI_PARAMS(showLegend, Show Legend, DCTLUI_CHECK_BOX, 1)
DEFINE_UI_PARAMS(legendSide, Legend Side, DCTLUI_SLIDER_INT, 0, 0, 1, 1)
DEFINE_UI_PARAMS(legendSize, Legend Height, DCTLUI_SLIDER_FLOAT, 0.9, 0.4, 1.0, 0.01)

__DEVICE__ float clamp01(float v)
{
    return _fminf(_fmaxf(v, 0.0f), 1.0f);
}

__DEVICE__ float3 lerp3(float3 a, float3 b, float t)
{
    float3 o;
    o.x = a.x + (b.x - a.x) * t;
    o.y = a.y + (b.y - a.y) * t;
    o.z = a.z + (b.z - a.z) * t;
    return o;
}

// 10 radius levels + white overflow — EXPOSURE palette hues, cool -> warm
__DEVICE__ float3 radius_color(int lvl)
{
    if (lvl <= 0) return make_float3(0.15f, 0.15f, 0.15f); // at/near center
    if (lvl == 1) return make_float3(0.55f, 0.10f, 0.80f); // purple
    if (lvl == 2) return make_float3(0.35f, 0.15f, 0.80f); // indigo
    if (lvl == 3) return make_float3(0.15f, 0.25f, 0.90f); // blue
    if (lvl == 4) return make_float3(0.15f, 0.55f, 0.90f); // sky
    if (lvl == 5) return make_float3(0.00f, 0.75f, 0.70f); // teal
    if (lvl == 6) return make_float3(0.20f, 0.80f, 0.25f); // green
    if (lvl == 7) return make_float3(0.95f, 0.85f, 0.10f); // yellow
    if (lvl == 8) return make_float3(1.00f, 0.50f, 0.00f); // orange
    if (lvl == 9) return make_float3(1.00f, 0.10f, 0.10f); // red
    return make_float3(1.00f, 1.00f, 1.00f);               // >= 100% beyond scale
}

__DEVICE__ int glyph_bits(int d)
{
    if (d == 0) return 0x7B6F;
    if (d == 1) return 0x2C97;
    if (d == 2) return 0x73E7;
    if (d == 3) return 0x73CF;
    if (d == 4) return 0x5BC9;
    if (d == 5) return 0x79CF;
    if (d == 6) return 0x79EF;
    if (d == 7) return 0x7249;
    if (d == 8) return 0x7BEF;
    if (d == 9) return 0x7BCF;
    return 0x01C0;
}

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
    // ---- LEGEND: 12 blocks — WHITE beyond-scale, levels 9..0, RANGE readout ----
    if (showLegend == 1)
    {
        int gw = (int)_fmaxf(26.0f, _floorf((float)p_Width * 0.03f));
        int lx = (legendSide == 1) ? (p_X - (p_Width - gw)) : p_X;
        int y0 = (int)((float)p_Height * (1.0f - legendSize) * 0.5f);
        int y1 = p_Height - y0;
        if (lx >= 0 && lx < gw && p_Y >= y0 && p_Y < y1)
        {
            int strip = y1 - y0;
            float hs = (float)strip / 12.0f;
            int idx = (int)((float)(p_Y - y0) / hs);
            if (idx > 11) idx = 11;

            float3 col;
            int k = 99;
            if (idx == 0)       col = make_float3(1.0f, 1.0f, 1.0f);    // beyond scale
            else if (idx == 11) col = make_float3(0.08f, 0.08f, 0.08f); // RANGE readout
            else { k = 10 - idx; col = radius_color(k); }               // 9 .. 0

            float fy = (float)(p_Y - y0) - hs * (float)idx;
            if (fy < 1.5f || fy > hs - 1.5f)
                col = make_float3(0.22f, 0.22f, 0.22f);

            if (k != 99 && k <= 9)
            {
                int sc = (int)_fmaxf(2.0f, _floorf((float)strip / 460.0f));
                float cy = (float)y0 + hs * ((float)idx + 0.5f);
                int dy = p_Y - ((int)cy - 2 * sc);
                int dx = lx - 3;
                if (dy >= 0 && dy < 5 * sc && dx >= 0)
                {
                    int gy = dy / sc;
                    int gxt = dx / sc;
                    if (gxt <= 2)
                    {
                        int bits = glyph_bits(k);
                        if (((bits >> (14 - (gy * 3 + gxt))) & 1) == 1)
                        {
                            float bl = 0.2126f * col.x + 0.7152f * col.y + 0.0722f * col.z;
                            col = (bl > 0.55f) ? make_float3(0.0f, 0.0f, 0.0f)
                                               : make_float3(1.0f, 1.0f, 1.0f);
                        }
                    }
                }
            }

            // RANGE readout: current Scope Range Pct (1..100) in the bottom block
            if (idx == 11)
            {
                int rv = (int)_floorf(_fmaxf(scopeRange, 1.0f) + 0.5f);
                if (rv > 100) rv = 100;
                int dh = rv / 100;
                int dt = (rv / 10) - dh * 10;
                int du = rv - (rv / 10) * 10;
                int nd = (rv >= 100) ? 3 : ((rv >= 10) ? 2 : 1);
                int sc = (int)_fmaxf(2.0f, _floorf((float)strip / 460.0f));
                float cy = (float)y0 + hs * ((float)idx + 0.5f);
                int dy = p_Y - ((int)cy - 2 * sc);
                int dx = lx - 3;
                if (dy >= 0 && dy < 5 * sc && dx >= 0)
                {
                    int gy = dy / sc;
                    int gxt = dx / sc;
                    int gi = gxt / 4; // glyph slot: 3 columns + 1 gap
                    int ggx = gxt - gi * 4;
                    if (gi < nd && ggx <= 2)
                    {
                        int d = du;
                        if (nd == 3)      d = (gi == 0) ? dh : ((gi == 1) ? dt : du);
                        else if (nd == 2) d = (gi == 0) ? dt : du;
                        int bits = glyph_bits(d);
                        if (((bits >> (14 - (gy * 3 + ggx))) & 1) == 1)
                            col = make_float3(1.0f, 1.0f, 1.0f);
                    }
                }
            }
            return col;
        }
    }

    // ---- MEASURE: true vectorscope radius (BT.709 CbCr) ----
    float Y = 0.2126f * p_R + 0.7152f * p_G + 0.0722f * p_B;
    float cb = (p_B - Y) / 1.8556f;
    float cr = (p_R - Y) / 1.5748f;
    // full scale = (Scope Range Pct)% of the vectorscope radius
    float frange = _fmaxf(scopeRange, 1.0f) * 0.01f * 0.5f;
    float rn = _sqrtf(cb * cb + cr * cr) / frange;

    float3 paint;
    if (rn >= 1.0f) paint = make_float3(1.0f, 1.0f, 1.0f);  // beyond scale
    else            paint = radius_color((int)_floorf(rn * 10.0f));

    float3 src = make_float3(p_R, p_G, p_B);
    return lerp3(src, paint, paintOpacity);
}
