在 p5.js 中为 Hitomezashi 缝合图案添加颜色

Adding colors to Hitomezashi stitch pattern in p5.js

我从这个 Numberphile 剧集中重新创建了这张程序图 https://www.youtube.com/watch?v=JbfhzlMk2eY&ab_channel=Numberphile

它根据一些随机序列的真假生成偏移或不偏移的线条,以生成这些有趣的模式。

我的下一个目标现在是能够使用这些图案的颜色,例如根据簇的大小使一个簇变暗,我真的不知道如何从这里继续因此,我们将不胜感激。

function setup() {
  var canvas = createCanvas(1600,800);
  // Move the canvas so it’s inside our <div id="sketch-holder">.
  canvas.parent('sketch-holder');
  background(255, 0, 200);
  
}


var _horzRows = 17*2;
var _horzCols = 17*2;
var _vertRows = 8*2;
var _vertCols = 34*2;

var rows = [1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0];
var cols = [1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0];

var x = 0;
var y = 0;
var xOffset = 0;
var yOffset = 0;

function horzGrid(l, t, stitchLength) {
  for (var j = 0; j < _horzCols; j++) {
    for (var k = 0; k < _horzRows; k++) {
      x = l + j*(stitchLength*2); // stitch + skip
      y = t + k*(stitchLength);
      if (rows[k] == 1) {
        xOffset = stitchLength;
      } else {
        xOffset = 0;
      }
      line(x+xOffset, y, x+xOffset + stitchLength, y);
    }
  }
}

function vertGrid(l, t, stitchLength) {
  for (var m = 0; m < _vertCols; m++) {
    for (var n = 0; n < _vertRows; n++) {
      x = l + m*(stitchLength);
      y = t + n*(stitchLength*2); // stitch + skip
      if (cols[m] == 1) {
        yOffset = stitchLength;
      } else {
        yOffset = 0;
      }
      line(x, y+yOffset, x, y+yOffset + stitchLength);
    }
  }
}


function draw() {
  horzGrid(30, 40, 25);
  vertGrid(30, 40, 25);
  size(920, 480);
  background(255);
  strokeWeight(2);
  
}

按照我的 ,您可以通过创建图形数据结构来完成此操作。

每个单元格都有属性 udlr 来表示单元格的周边是否有墙。如果它没有墙,则将由缺失墙连接的单元格视为邻居。否则,他们不是邻居。

这将创建一个图形结构,其中每个顶点都是一个单元格,所有相邻单元格都有边。

下一步是使用视频中描述的 Hitomezashi 算法随机填充单元格之间的链接。

最后,在图形数据结构就绪的情况下,运行 每个单元格的 flood fill 以确定其连接组件的大小。组件的大小将决定它的颜色,然后将其传递到第二个填充,为每组连接的单元分配颜色。

最后,迭代并绘制所有单元格。

请注意,我还没有时间 DRY 把这段代码拿出来,所以它相当混乱。递归在大型网格上也是不安全的,因此将其视为概念证明。

const w = document.documentElement.clientWidth;
const h = document.documentElement.clientHeight;

const gridSize = 10;
const grid = [...Array(~~(h / gridSize) + 1)].map(() =>
  [...Array(~~(w / gridSize) + 1)].map(() => ({
    u: false, d: false, l: false, r: false,
  }))
);

const addHitomezashi = grid => {
  for (let i = 0; i < grid.length; i++) {
    const offset = ~~random(2);

    for (let j = offset; j < grid[i].length; j += 2) {
      grid[i][j].d = true;

      if (grid[i+1]) {
        grid[i+1][j].u = true;
      }
    }
  }

  for (let j = 0; j < grid[0].length; j++) {
    const offset = ~~random(2);

    for (let i = offset; i < grid.length; i += 2) {
      grid[i][j].r = true;

      if (grid[i][j+1]) {
        grid[i][j+1].l = true;
      }
    }
  }
};

const colorHitomezashi = grid => {
  const visited = new Set();
  const getSize = (x, y) => {
    if (x < 0 || y < 0 || 
        y >= grid.length || x >= grid[y].length ||
        visited.has(`${x} ${y}`)) {
      return 0;
    }

    let size = 0;
    visited.add(`${x} ${y}`);

    if (!grid[y][x].u) {
      size = max(size, getSize(x, y - 1));
    }
    if (!grid[y][x].d) {
      size = max(size, getSize(x, y + 1));
    }
    if (!grid[y][x].l) {
      size = max(size, getSize(x - 1, y));
    }
    if (!grid[y][x].r) {
      size = max(size, getSize(x + 1, y));
    }

    return size + 1;
  };

  const floodFill = (x, y, color) => {
    if (x < 0 || y < 0 ||
        y >= grid.length || x >= grid[y].length ||
        grid[y][x].color !== undefined) {
      return 0;
    }

    grid[y][x].color = color;

    if (!grid[y][x].u) {
      floodFill(x, y - 1, color);
    }
    if (!grid[y][x].d) {
      floodFill(x, y + 1, color);
    }
    if (!grid[y][x].l) {
      floodFill(x - 1, y, color);
    }
    if (!grid[y][x].r) {
      floodFill(x + 1, y, color);
    }
  };

  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[i].length; j++) {
      const color = 180 - getSize(j, i);
      floodFill(j, i, color);
    }
  }
};

function setup() {
  createCanvas(w, h);
  noLoop();
  addHitomezashi(grid);
  colorHitomezashi(grid);
}

function draw() {
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[i].length; j++) {
      const y = i * gridSize + 0.5;
      const x = j * gridSize + 0.5;
      
      fill(grid[i][j].color);
      noStroke();
      rect(x, y, gridSize + 1, gridSize + 1);
      stroke(0);

      if (grid[i][j].u) {
        line(x, y, x + gridSize, y);
      }
      if (grid[i][j].d) {
        line(x, y + gridSize, x + gridSize, y + gridSize);
      }
      if (grid[i][j].l) {
        line(x, y, x, y + gridSize);
      }
      if (grid[i][j].r) {
        line(x + gridSize, y, x + gridSize, y + gridSize);
      }
    }
  }
}
body {
  margin: 0;
}
canvas {
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>

下一步:在视频中描述的 isometric grid 上实施!