// PROBE 1886 — v1.1 — two-point stop meter, standardized on ITU-R BT.1886
// Federico Malpica, 2026-07-04
//
// A stop-difference tape measure. Place point A (green) and point B (red) with
// the position sliders; the readout box shows, per row:
//   row 1 (green square)  A stops   + A signal level (pale blue, percent)
//   row 2 (red square)    B stops   + B signal level (pale blue, percent)
//   row 3 (white square)  D = A minus B stops (positive = A is brighter)
// White numbers = stops (light, through BT.1886). Pale blue = display signal
// percent (what the waveform shows, 0-100). Same patch, two rulers.
// v1.1: Readout Scale slider + border (visibility on letterboxed frames) +
//   signal pct columns.
// v1.2: Readout X/Y Pct sliders replace Readout Side — output blanking is
//   applied AFTER the node and covers anything under the bars, so the box is
//   freely positionable; defaults clear a 2.39 bar on 16:9.
// Put A on the key side of the face, B on the fill side: D = your lighting
// ratio in stops (1.0 = 2:1 · 1.6 = 3:1 · 2.0 = 4:1). A on key, B on the
// background: D = subject/background separation. Match the reference's D on
// your own shot through your own look = the final images match.
//
// Each point averages a square patch (side = 2 x Sample Radius) in LINEAR
// light — 9x9 grid taps, so small Sample Radius = tight probe, large = area
// mean. Marks: crosshair + patch outline at each point.
//
// TEXTURE-BASED (samples away from the current pixel):
//   - apply as a DCTL via the OFX panel, NOT the LUT browser (May finding)
//   - all toggles are 0-1 sliders — CHECK_BOX breaks compilation next to
//     __TEXTURE__ params (May finding)
//
// Mid Gray Anchor must match your EXPOSURE 1886 setting (default 0.409) so the
// A and B absolute numbers agree with what EXPOSURE 1886 paints. The D row
// does not depend on it. Readout X/Y Pct place the box (0,0 = top left,
// 100,100 = bottom right — always fully on frame).

DEFINE_UI_PARAMS(ax, Point A X Pct, DCTLUI_SLIDER_FLOAT, 30.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(ay, Point A Y Pct, DCTLUI_SLIDER_FLOAT, 40.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(bx, Point B X Pct, DCTLUI_SLIDER_FLOAT, 70.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(by, Point B Y Pct, DCTLUI_SLIDER_FLOAT, 40.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(sampleRadius, Sample Radius Px, DCTLUI_SLIDER_INT, 15, 3, 80, 1)
DEFINE_UI_PARAMS(midAnchor, Mid Gray Anchor, DCTLUI_SLIDER_FLOAT, 0.409, 0.05, 0.95, 0.001)
DEFINE_UI_PARAMS(showMarks, Show Marks, DCTLUI_SLIDER_INT, 1, 0, 1, 1)
DEFINE_UI_PARAMS(readoutX, Readout X Pct, DCTLUI_SLIDER_FLOAT, 1.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(readoutY, Readout Y Pct, DCTLUI_SLIDER_FLOAT, 16.0, 0.0, 100.0, 0.1)
DEFINE_UI_PARAMS(readoutScale, Readout Scale, DCTLUI_SLIDER_INT, 2, 1, 6, 1)

__DEVICE__ float g24_inv(float v)
{
    return _powf(_fmaxf(v, 0.0f), 2.4f); // BT.1886 EOTF (zero black lift form)
}

__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;
    if (d == 11) return 0x0002; // decimal point (bottom center)
    return 0x01C0;              // minus
}

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, __TEXTURE__ p_TexR, __TEXTURE__ p_TexG, __TEXTURE__ p_TexB)
{
    float3 src = make_float3(_tex2D(p_TexR, p_X, p_Y),
                             _tex2D(p_TexG, p_X, p_Y),
                             _tex2D(p_TexB, p_X, p_Y));

    float axp = ax * 0.01f * (float)(p_Width - 1);
    float ayp = ay * 0.01f * (float)(p_Height - 1);
    float bxp = bx * 0.01f * (float)(p_Width - 1);
    float byp = by * 0.01f * (float)(p_Height - 1);
    float rad = (float)sampleRadius;

    // ---- MARKS: crosshair + patch outline at A (green) and B (red) ----
    if (showMarks == 1)
    {
        float th = _fmaxf(1.0f, (float)p_Height / 720.0f);
        float arm = rad + 8.0f * th;
        float dxa = _fabs((float)p_X - axp);
        float dya = _fabs((float)p_Y - ayp);
        float dxb = _fabs((float)p_X - bxp);
        float dyb = _fabs((float)p_Y - byp);

        int onA = ((dxa <= th && dya <= arm) || (dya <= th && dxa <= arm) ||
                   (_fmaxf(dxa, dya) >= rad - th && _fmaxf(dxa, dya) <= rad + th)) ? 1 : 0;
        int onB = ((dxb <= th && dyb <= arm) || (dyb <= th && dxb <= arm) ||
                   (_fmaxf(dxb, dyb) >= rad - th && _fmaxf(dxb, dyb) <= rad + th)) ? 1 : 0;
        if (onA == 1) return make_float3(0.15f, 0.95f, 0.30f);
        if (onB == 1) return make_float3(1.00f, 0.20f, 0.12f);
    }

    // ---- READOUT BOX (only these pixels pay for the sampling) ----
    int sc = readoutScale * (int)_fmaxf(2.0f, _floorf((float)p_Height / 460.0f));
    int pad = 3 * sc;
    int rh = 7 * sc;                 // row height: 5*sc glyph + gap
    int bw = 36 * sc + 2 * pad;      // label + stops digits + signal pct digits
    int bh = 3 * rh + 2 * pad;
    // position as pct of the available travel: any value keeps the box on-frame;
    // default Y 14 clears a 2.39 blanking bar on a 16:9 timeline (blanking is
    // applied AFTER the node, so it covers anything drawn under the bars)
    int bx0 = (int)(readoutX * 0.01f * (float)(p_Width - bw));
    int by0 = (int)(readoutY * 0.01f * (float)(p_Height - bh));

    if (p_X >= bx0 && p_X < bx0 + bw && p_Y >= by0 && p_Y < by0 + bh)
    {
        // 9x9 grid taps over each square patch, averaged in LINEAR light
        // (inlined — texture handles stay inside transform for the Metal compiler)
        float accA = 0.0f;
        float accB = 0.0f;
        float accAd = 0.0f; // display-domain luma (waveform level)
        float accBd = 0.0f;
        for (int j = -4; j <= 4; j++)
        {
            for (int i = -4; i <= 4; i++)
            {
                float ox = (float)i * rad * 0.25f;
                float oy = (float)j * rad * 0.25f;
                float sxa = _fminf(_fmaxf(axp + ox, 0.0f), (float)(p_Width - 1));
                float sya = _fminf(_fmaxf(ayp + oy, 0.0f), (float)(p_Height - 1));
                float sxb = _fminf(_fmaxf(bxp + ox, 0.0f), (float)(p_Width - 1));
                float syb = _fminf(_fmaxf(byp + oy, 0.0f), (float)(p_Height - 1));
                float ra = _tex2D(p_TexR, (int)sxa, (int)sya);
                float ga = _tex2D(p_TexG, (int)sxa, (int)sya);
                float ba = _tex2D(p_TexB, (int)sxa, (int)sya);
                float rb = _tex2D(p_TexR, (int)sxb, (int)syb);
                float gb = _tex2D(p_TexG, (int)sxb, (int)syb);
                float bb = _tex2D(p_TexB, (int)sxb, (int)syb);
                accA += 0.2126f * g24_inv(ra) + 0.7152f * g24_inv(ga) + 0.0722f * g24_inv(ba);
                accB += 0.2126f * g24_inv(rb) + 0.7152f * g24_inv(gb) + 0.0722f * g24_inv(bb);
                accAd += 0.2126f * ra + 0.7152f * ga + 0.0722f * ba;
                accBd += 0.2126f * rb + 0.7152f * gb + 0.0722f * bb;
            }
        }
        float yref = _fmaxf(g24_inv(midAnchor), 1e-6f);
        float la = _fmaxf(accA / 81.0f, 1e-6f);
        float lb = _fmaxf(accB / 81.0f, 1e-6f);
        float stopsA = _log2f(la / yref);
        float stopsB = _log2f(lb / yref);
        float sigA = accAd / 81.0f * 100.0f; // display signal level, percent
        float sigB = accBd / 81.0f * 100.0f;

        float3 col = make_float3(0.07f, 0.07f, 0.07f);

        // border so the panel reads against black letterbox bars
        if (p_X - bx0 < 2 || (bx0 + bw - 1 - p_X) < 2 ||
            p_Y - by0 < 2 || (by0 + bh - 1 - p_Y) < 2)
            return make_float3(0.45f, 0.45f, 0.45f);

        int row = (p_Y - by0 - pad) / rh;
        int ry = (p_Y - by0 - pad) - row * rh;
        if (row >= 0 && row <= 2 && ry >= 0 && ry < 5 * sc)
        {
            float val = (row == 0) ? stopsA : ((row == 1) ? stopsB : (stopsA - stopsB));
            val = _fminf(_fmaxf(val, -9.9f), 9.9f);

            int dx = p_X - (bx0 + pad);
            int gy = ry / sc;
            int gxt = dx / sc;

            // label square in slot 0 (rows of 3x5 like a glyph, all on)
            if (gxt >= 0 && gxt <= 2)
            {
                if (row == 0) col = make_float3(0.15f, 0.95f, 0.30f);
                else if (row == 1) col = make_float3(1.00f, 0.20f, 0.12f);
                else col = make_float3(0.90f, 0.90f, 0.90f);
            }
            else if (gxt >= 4 && gxt <= 19) // stops columns
            {
                int neg = (val < -0.05f) ? 1 : 0;
                int r10 = (int)_floorf(_fabs(val) * 10.0f + 0.5f);
                int du = r10 / 10;
                int dt = r10 - du * 10;
                int nd = neg + 3; // [minus] unit point tenth
                int gi = (gxt - 4) / 4;
                int ggx = (gxt - 4) - gi * 4;
                if (gi < nd && ggx <= 2 && gy >= 0 && gy <= 4)
                {
                    int slot = gi - neg;
                    int d = 10;
                    if (slot == 0)      d = du;
                    else if (slot == 1) d = 11;
                    else if (slot == 2) d = dt;
                    if (slot >= 0 || gi == 0)
                    {
                        int bits = glyph_bits(d);
                        if (((bits >> (14 - (gy * 3 + ggx))) & 1) == 1)
                            col = make_float3(1.0f, 1.0f, 1.0f);
                    }
                }
            }
            else if (gxt >= 22 && row <= 1) // signal pct columns, A and B rows only
            {
                float sig = (row == 0) ? sigA : sigB;
                int pv = (int)_floorf(_fminf(_fmaxf(sig, 0.0f), 100.0f) + 0.5f);
                int dh = pv / 100;
                int dt2 = (pv / 10) - dh * 10;
                int du2 = pv - (pv / 10) * 10;
                int nd = (pv >= 100) ? 3 : ((pv >= 10) ? 2 : 1);
                int gi = (gxt - 22) / 4;
                int ggx = (gxt - 22) - gi * 4;
                if (gi < nd && ggx <= 2 && gy >= 0 && gy <= 4)
                {
                    int d = du2;
                    if (nd == 3)      d = (gi == 0) ? dh : ((gi == 1) ? dt2 : du2);
                    else if (nd == 2) d = (gi == 0) ? dt2 : du2;
                    int bits = glyph_bits(d);
                    if (((bits >> (14 - (gy * 3 + ggx))) & 1) == 1)
                        col = make_float3(0.55f, 0.75f, 1.00f); // pale blue = signal pct
                }
            }
        }
        return col;
    }

    return src;
}
