在 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);
}
按照我的 ,您可以通过创建图形数据结构来完成此操作。
每个单元格都有属性 u
、d
、l
和 r
来表示单元格的周边是否有墙。如果它没有墙,则将由缺失墙连接的单元格视为邻居。否则,他们不是邻居。
这将创建一个图形结构,其中每个顶点都是一个单元格,所有相邻单元格都有边。
下一步是使用视频中描述的 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 上实施!
我从这个 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);
}
按照我的
每个单元格都有属性 u
、d
、l
和 r
来表示单元格的周边是否有墙。如果它没有墙,则将由缺失墙连接的单元格视为邻居。否则,他们不是邻居。
这将创建一个图形结构,其中每个顶点都是一个单元格,所有相邻单元格都有边。
下一步是使用视频中描述的 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 上实施!