在 p5.js 中为 Hitomezashi 针迹图案添加颜色

发布于 2025-01-13 11:20:28 字数 2071 浏览 0 评论 0原文

我从这个 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);
  
}

I have recreated this procedural drawing from this Numberphile episode https://www.youtube.com/watch?v=JbfhzlMk2eY&ab_channel=Numberphile.

It generates lines which are either offset or not depending on some random sequence true and false, to generate these interesting patterns.

My next goal is now to be able to play with the color of these patterns, for example making one cluster darker depending on how big the cluster is, I really don't have a clue how to go on from here so any kind of help would be much appreciated.

enter image description here

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);
  
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

扶醉桌前 2025-01-20 11:20:28

跟进我的评论,您可以通过创建图形数据结构来做到这一点。

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

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

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

最后,在图形数据结构就位后,从每个单元格运行 洪水填充 以确定其连接组件的大小。组件的大小将决定其颜色,然后将其传递到第二个洪水填充,该填充为每组连接的单元分配颜色。

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

请注意,我还没有时间 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.6.0/p5.js"></script>

下一步:在视频中描述的等距网格上实现此操作!

Following up on my comment, you can do this by creating a graph data structure.

Each cell has properties u, d, l and r to denote whether the cell has a wall along that side of its perimeter. If it doesn't have a wall, then consider the cells connected by the missing wall to be neighbors. Otherwise, they're not neighbors.

This creates a graph structure, where each vertex is a cell and there are edges to all neighbor cells.

The next step is to randomly populate the links between cells using the Hitomezashi algorithm described in the video.

Finally, with the graph data structure in place, run a flood fill from each cell to determine the size of its connected component. The size of the component will determine its color, which is then passed into a second flood fill that assigns the colors for each group of connected cells.

Finally, iterate and draw all of the cells.

Note that I haven't had time to DRY this code out, so it's rather messy. The recursion is also unsafe on large grids, so consider this a proof of concept.

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.6.0/p5.js"></script>

Next step: implement this on the isometric grid described in the video!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文