Mapping: Pixel Mapping

Map canvas coordinates to image pixels with clamped u/v values.

Example 1: Accurate Pixel Sampler

Code

let img;
const points = [
  { u: 0.2, v: 0.22, label: 'A' },
  { u: 0.48, v: 0.4, label: 'B' },
  { u: 0.7, v: 0.62, label: 'C' },
  { u: 0.33, v: 0.78, label: 'D' }
];

function preload() {
  img = loadImage('../assets/images/map.jpg');
}

function setup() {
  createCanvas(400, 400);
  pixelDensity(1);
}

function draw() {
  background(240);
  const fit = fitImage(img.width, img.height, width, height, 20);
  image(img, fit.dx, fit.dy, fit.dw, fit.dh);

  const u = constrain((mouseX - fit.dx) / fit.dw, 0, 1);
  const v = constrain((mouseY - fit.dy) / fit.dh, 0, 1);
  const ix = floor(u * (img.width - 1));
  const iy = floor(v * (img.height - 1));
  const c = img.get(ix, iy);

  fill(c);
  noStroke();
  rect(14, 14, 70, 70, 8);
  fill(20);
  text('ix ' + ix + ' iy ' + iy, 14, 98);

  drawInteractivePoints(fit);
}

function drawInteractivePoints(fit) {
  textAlign(LEFT, CENTER);
  textSize(13);

  for (const p of points) {
    const x = fit.dx + p.u * fit.dw;
    const y = fit.dy + p.v * fit.dh;
    const hovered = dist(mouseX, mouseY, x, y) < 10;
    const r = hovered ? 9 : 7;

    stroke(255);
    strokeWeight(2);
    fill(255, 45, 140);
    circle(x, y, r * 2);

    noStroke();
    fill(20);
    text(p.label, x + 12, y);
  }
}

function fitImage(iw, ih, cw, ch, pad) {
  const ratio = min((cw - pad * 2) / iw, (ch - pad * 2) / ih);
  const dw = iw * ratio;
  const dh = ih * ratio;
  return { dx: (cw - dw) / 2, dy: (ch - dh) / 2, dw, dh };
}

Try this: Show nearest pixel indices and compare with get().

Open sketch in new tab

Example 2: Aligned Mosaic Sampling

Code

let img;

function preload() {
  img = loadImage('../assets/images/map.jpg');
}

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(250);
  const fit = fitImage(img.width, img.height, width, height, 20);
  const tile = 12;

  for (let y = 0; y < fit.dh; y += tile) {
    for (let x = 0; x < fit.dw; x += tile) {
      const u = constrain(x / fit.dw, 0, 1);
      const v = constrain(y / fit.dh, 0, 1);
      const ix = floor(u * (img.width - 1));
      const iy = floor(v * (img.height - 1));
      const c = img.get(ix, iy);
      fill(c);
      rect(fit.dx + x, fit.dy + y, tile, tile);
    }
  }
}

function fitImage(iw, ih, cw, ch, pad) {
  const ratio = min((cw - pad * 2) / iw, (ch - pad * 2) / ih);
  const dw = iw * ratio;
  const dh = ih * ratio;
  return { dx: (cw - dw) / 2, dy: (ch - dh) / 2, dw, dh };
}

Try this: Lower tile size for more detail, raise for abstraction.

Open sketch in new tab

Resources