재귀적 분할

Recursive subdivision

사각형 하나를 계속 둘로 쪼개나가며 화면을 채우는 방법. 몬드리안 회화나 스위스 스타일 그리드처럼 보이는 구획을 자동으로 만듭니다.

다른 이름: Space partitioning artMondrian generator
···
js
const c = document.createElement('canvas');
document.body.appendChild(c);
c.style.width = '100%'; c.style.height = '100%';
const ctx = c.getContext('2d');
const cs = getComputedStyle(document.documentElement);
const palette = [cs.getPropertyValue('--accent').trim(), cs.getPropertyValue('--accent-2').trim(), cs.getPropertyValue('--accent-3').trim()].map(x => x || '#5b5bf7');
let w, h, leaves;
function resize() {
  const dpr = Math.min(devicePixelRatio || 1, 2);
  w = innerWidth; h = innerHeight;
  c.width = w * dpr; c.height = h * dpr;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  leaves = [{ x: 0, y: 0, w, h, col: Math.random() < 0.3 ? palette[Math.floor(Math.random() * 3)] : null }];
  for (let i = 0; i < 16; i++) split(); // 초기 상태부터 꽤 나뉘어 있도록
}
function split() {
  if (leaves.length > 46) return;
  const candidates = leaves.filter((r) => r.w > 40 && r.h > 40);
  if (!candidates.length) return;
  const r = candidates[Math.floor(Math.random() * candidates.length)];
  const idx = leaves.indexOf(r);
  const vertical = r.w > r.h;
  const ratio = 0.32 + Math.random() * 0.36;
  const col = () => (Math.random() < 0.22 ? palette[Math.floor(Math.random() * 3)] : null);
  let a, b;
  if (vertical) {
    const cut = r.w * ratio;
    a = { x: r.x, y: r.y, w: cut, h: r.h, col: col() };
    b = { x: r.x + cut, y: r.y, w: r.w - cut, h: r.h, col: col() };
  } else {
    const cut = r.h * ratio;
    a = { x: r.x, y: r.y, w: r.w, h: cut, col: col() };
    b = { x: r.x, y: r.y + cut, w: r.w, h: r.h - cut, col: col() };
  }
  leaves.splice(idx, 1, a, b);
}
addEventListener('resize', resize);
resize();

function draw() {
  ctx.fillStyle = '#0d0d12'; ctx.fillRect(0, 0, w, h);
  for (const r of leaves) {
    if (r.col) { ctx.fillStyle = r.col; ctx.fillRect(r.x, r.y, r.w, r.h); }
    ctx.strokeStyle = 'rgba(241,240,236,0.7)'; ctx.lineWidth = 2; ctx.strokeRect(r.x, r.y, r.w, r.h);
  }
}
draw();
let last = 0;
(function loop(t) {
  if (t - last > 140) { split(); draw(); last = t; }
  if (leaves.length >= 46 && t - last > 3200) { resize(); draw(); last = t; }
  requestAnimationFrame(loop);
})(0);

캔버스 전체를 사각형 하나로 두고 시작합니다. 매 단계마다 남아있는 사각형 하나를 골라, 가로 또는 세로 중 하나로 무작위 비율에서 둘로 자릅니다. 이 과정을 원하는 만큼 반복하면 크기가 제각각인 사각형들이 자연스럽게 격자를 이루는데, 매번 자르는 축과 비율이 무작위이기 때문에 규칙적인 그리드보다 훨씬 리듬감 있는 배치가 나옵니다. 이진 공간 분할(BSP)이라는 이름으로 게임 렌더링·충돌 처리에도 쓰이는 같은 개념입니다.

색을 칠할 때 일부 칸만 강조색으로 채우고 나머지는 배경색으로 비워두면 피트 몬드리안의 구성이나 스위스 스타일 포스터의 비대칭 그리드와 닮은 결과가 나옵니다. 이 데모는 일정 시간마다 칸 하나를 골라 계속 더 잘게 쪼개서, 성기게 시작해 점점 촘촘해지는 과정을 보여줍니다.

포스터·썸네일의 배경 구성, 반응형 레이아웃의 뼈대 생성, UI 목업의 자리표시자 블록을 자동으로 배치할 때 씁니다.

언제 쓰나

포스터·썸네일의 배경 구성, 레이아웃 뼈대를 빠르게 여러 개 뽑아볼 때. 색을 칠하는 칸의 비율을 낮게 유지해야 몬드리안 느낌이 삽니다.