fractalkitty looking for math? fractalkitty.com

all

Knit Hilbert

#grid#color#animation#dom#tool#fractal#scrap

hilbertPointsgridSizeupdatePatternordercellSizesubCellSizecanvasSizepointdrawPatternprimaryColorbgColorshowGridshowPathsubgridminYmaxYminXmaxXshowMarkersmarkerSpacingd2xydownloadPattern

let canvas;
let hilbertPoints = [];
let gridSize;

function setup() {
  updatePattern();
}

function updatePattern() {
  const order = parseInt(document.getElementById('order').value);
  let cellSize = parseInt(document.getElementById('cellSize').value);
  
  if (cellSize % 3 !== 0) {
    cellSize = Math.floor(cellSize / 3) * 3;
    document.getElementById('cellSize').value = cellSize;
  }
  
  const n = Math.pow(2, order);
  gridSize = n;
  const subCellSize = cellSize / 3;
  const canvasSize = gridSize * cellSize;
  
  if (canvas) {
    canvas.remove();
  }
  
  canvas = createCanvas(canvasSize, canvasSize);
  canvas.parent('canvas-container');
  
  hilbertPoints = [];
  for (let i = 0; i < n * n; i++) {
    const point = d2xy(n, i);
    hilbertPoints.push(point);
  }
  
  drawPattern();
}

function drawPattern() {
  const primaryColor = document.getElementById('primaryColor').value;
  const bgColor = document.getElementById('bgColor').value;
  const cellSize = parseInt(document.getElementById('cellSize').value);
  const subCellSize = cellSize / 3;
  const showGrid = document.getElementById('showGrid').checked;
  const showPath = document.getElementById('showPath').checked;
  
  background(bgColor);
  
  const subgrid = [];
  for (let y = 0; y < gridSize * 3; y++) {
    subgrid[y] = [];
    for (let x = 0; x < gridSize * 3; x++) {
      subgrid[y][x] = false;
    }
  }
  
  for (let i = 0; i < hilbertPoints.length; i++) {
    const x = hilbertPoints[i].x * 3 + 1;
    const y = hilbertPoints[i].y * 3 + 1;
    subgrid[y][x] = true;
  }
  
  for (let i = 0; i < hilbertPoints.length - 1; i++) {
    const x1 = hilbertPoints[i].x * 3 + 1;
    const y1 = hilbertPoints[i].y * 3 + 1;
    const x2 = hilbertPoints[i+1].x * 3 + 1;
    const y2 = hilbertPoints[i+1].y * 3 + 1;
    
    if (x1 === x2) {
      const minY = Math.min(y1, y2);
      const maxY = Math.max(y1, y2);
      for (let y = minY; y <= maxY; y++) {
        subgrid[y][x1] = true;
      }
    } else if (y1 === y2) {
      const minX = Math.min(x1, x2);
      const maxX = Math.max(x1, x2);
      for (let x = minX; x <= maxX; x++) {
        subgrid[y1][x] = true;
      }
    }
  }
  
  noStroke();
  fill(primaryColor);
  for (let y = 0; y < gridSize * 3; y++) {
    for (let x = 0; x < gridSize * 3; x++) {
      if (subgrid[y][x]) {
        rect(x * subCellSize, y * subCellSize, subCellSize, subCellSize);
      }
    }
  }
  
  if (showGrid) {
    stroke(200);
    strokeWeight(0.5);
    for (let i = 0; i <= gridSize * 3; i++) {
      line(0, i * subCellSize, width, i * subCellSize);
      line(i * subCellSize, 0, i * subCellSize, height);
    }
  }
  
  if (showPath) {
    stroke(255, 0, 0, 150);
    strokeWeight(1);
    noFill();
    beginShape();
    
    for (let i = 0; i < hilbertPoints.length; i++) {
      const x = hilbertPoints[i].x * cellSize + cellSize / 2;
      const y = hilbertPoints[i].y * cellSize + cellSize / 2;
      vertex(x, y);
    }
    
    endShape();
  }
  
  const showMarkers = document.getElementById('showMarkers').checked;
  if (showMarkers) {
    const markerSpacing = parseInt(document.getElementById('markerSpacing').value);
    
    stroke(255, 0, 0);
    strokeWeight(2);
    for (let i = 0; i <= gridSize; i += markerSpacing) {
      line(0, i * cellSize, width, i * cellSize);
      line(i * cellSize, 0, i * cellSize, height);
    }
    
    stroke(0);
    strokeWeight(1.5);
    for (let i = 0; i <= gridSize; i++) {
      if (i % markerSpacing !== 0) {
        line(0, i * cellSize, width, i * cellSize);
        line(i * cellSize, 0, i * cellSize, height);
      }
    }
  }
}

function d2xy(n, d) {
  let x = 0, y = 0;
  let t = d;
  
  for (let s = 1; s < n; s *= 2) {
    const rx = 1 & (t / 2);
    const ry = 1 & (t ^ rx);
    
    if (ry === 0) {
      if (rx === 1) {
        x = s - 1 - x;
        y = s - 1 - y;
      }
      
      const temp = x;
      x = y;
      y = temp;
    }
    
    x += s * rx;
    y += s * ry;
    
    t = Math.floor(t / 4);
  }
  
  return { x, y };
}

function downloadPattern() {
  saveCanvas('hilbert_knitting_pattern', 'png');
}