Resources Numerical accuracy Calculation policy

Sheet compare picture

Mixes the renderings of an old and a new sheet into one picture that colors what was removed and what was added.

How the precision is kept

The inputs and the output are 8-bit RGBA pixels. Each pixel's ink darkness is worked out in 64-bit from the bytes and decides the color written back as bytes. The result is a picture to look at; nothing measured comes from it.

Calculations under this policy

1 calculation in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

compare.js MarkupCraft 0.8.1 32-bit, display only 1 calculation Reviewed

Tests bin/test-markupcraft.mjs

Old and new sheet mixed per pixel

32-bit, display only

The color of each pixel of the compare picture, and counts of removed, added and unchanged pixels.

Works on 8-bit pixels of two renderings at screen resolution. The pixel counts it returns use a darkness threshold of 0.35 and depend on the rendering size.

  • compare.js
  • lines 11 to 37
  • MarkupCraft 0.8.1
/**
 * a, b: RGBA bytes of the old and new renderings; out: RGBA bytes written.
 * colors: { bg, same, old, new } as [r,g,b] 0–255.
 * Returns { changedOld, changedNew, same } pixel counts (darkness > 0.35) for a quick summary.
 */
export function combine(a, b, out, colors, balance = 0.5) {
  const wa = Math.min(1, 2 * (1 - balance)), wb = Math.min(1, 2 * balance);
  const [br, bg, bb] = colors.bg, [sr, sg, sb] = colors.same, [or, og, ob] = colors.old, [nr, ng, nb] = colors.new;
  let changedOld = 0, changedNew = 0, same = 0;
  for (let i = 0; i < out.length; i += 4) {
    const da = (1 - (a[i] * 0.299 + a[i + 1] * 0.587 + a[i + 2] * 0.114) / 255) * (a[i + 3] / 255);
    const db = (1 - (b[i] * 0.299 + b[i + 1] * 0.587 + b[i + 2] * 0.114) / 255) * (b[i + 3] / 255);
    const A = da * wa, B = db * wb;
    const common = Math.min(A, B);
    // a little dead band so anti-aliased edges of unchanged lines do not light up
    const onlyA = Math.max(0, A - common - 0.06) / 0.94, onlyB = Math.max(0, B - common - 0.06) / 0.94;
    let r = br, g = bg, bl = bb;
    r += (sr - r) * common; g += (sg - g) * common; bl += (sb - bl) * common;
    r += (or - r) * onlyA; g += (og - g) * onlyA; bl += (ob - bl) * onlyA;
    r += (nr - r) * onlyB; g += (ng - g) * onlyB; bl += (nb - bl) * onlyB;
    out[i] = r; out[i + 1] = g; out[i + 2] = bl; out[i + 3] = 255;
    if (onlyA > 0.35) changedOld++;
    else if (onlyB > 0.35) changedNew++;
    else if (common > 0.35) same++;
  }
  return { changedOld, changedNew, same };
}

In detail

Method
darkness = (1 − (0.299 R + 0.587 G + 0.114 B) ÷ 255) × alpha ÷ 255 for each sheet; ink on both is drawn neutral, ink on one only in that sheet's color.
Number types
8-bit color channels in and out; 64-bit numbers in between.
Tolerances
A dead band of 0.06 in darkness keeps anti-aliased edges of unchanged lines from lighting up. A pixel counts as changed or unchanged above 0.35.

Approach: Display only

Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.

Every policy that takes this approach