Chanomic Sketch

Circle Drops

"use strict";

let maskImg, fgImg;
const colorBg = '#242F9B';
const colorFg = '#F1F6F5';
let palette;
let RMax;

function setup() {
  createCanvas(windowWidth, windowHeight);
  maskImg = createGraphics(width, height);
  fgImg = createGraphics(width, height);
  palette = ['#242F9B', '#82C3EC'].map((c) => color(c));
  RMax = width/10;
}

function draw() {
  background('white');

  const [isFound, x] = findX();

  if (isFound) {
    // x1 ---- x ---- x2
    const x1 = searchNeighborLineX(x, -1);
    const x2 = searchNeighborLineX(x, 1);
    const rMax = min(x - x1, x2 - x);
    const r = random(rMax/2, min(rMax, RMax));

    const dy = 2*r + random(r/2, 2*r);
    drawShapesLine(x, dy, () => {
      fgImg.fill(colorFg);
      if (r < 2) {
        fgImg.noStroke();
      } else {
        fgImg.stroke(colorBg);
      }
      fgImg.circle(0, 0, 2*r);
    });
    drawVerticalLine(x, 2*r);
  } else {
    noLoop();
  }

  image(maskImg, 0, 0);
  image(fgImg, 0, 0);
}

const findX = () => {
  const x0 = random(0, width);
  const dx = shuffle([-1, 1]);

  for (let x = x0; x >= 0 && x < width; x += dx[0]) {
    if (!isOverlapped(x)) {
      return [true, x];
    }
  }

  for (let x = x0; x >= 0 && x < width; x += dx[1]) {
    if (!isOverlapped(x)) {
      return [true, x];
    }
  }

  return [false, null];
}

const drawVerticalLine = (x, w) => {
  const t = map(w, 0, RMax, 0, 1)
  const col = lerpColor(palette[0], palette[1], t);
  maskImg.stroke(col);
  maskImg.strokeWeight(w);
  maskImg.line(x, -height, x, height);
}

const searchNeighborLineX = (x0, dx) => {
  let x;
  for (x = x0; x >= 0 && x < width; x += dx) {
    const [, , , a] = maskImg.get(x, 0);
    if (a !== 0) {
      return x;
    }
  }
  return x;
}

const isOverlapped = (x) => {
  const [, , , a] = maskImg.get(x, 1);
  if (a !== 0) return true;
  return false;
}

const drawShapesLine = (x, dy, drawFunc) => {
  fgImg.push();
  fgImg.translate(x, 0);
  for (let y = 0; y < height; y += dy) {
    drawFunc();
    fgImg.translate(0, dy);
  }
  fgImg.pop();
};