蜂窝六角网格?
Honeycomb hexagonal grid?
我正在尝试绘制蜂窝状的六角网格。
到目前为止,我可以将它绘制成矩形,但我不知道如何将我的 for 循环转换为蜂窝状。
这是我目前拥有的
<html>
<body>
<canvas width='1080' height='720' id='hexmap'></canvas>
</body>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 36,
boardWidth = 10,
boardHeight = 10;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i,j;
//this loop generates a rectangular hexagon grid
for(i = 0; i < width; ++i) {
for(j = 0; j < height; ++j) {
drawHexagon(
ctx,
i * hexRectangleWidth + ((j % 2) * hexRadius),
j * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if(fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
})
</script>
</html>
结果是这个形状
虽然我想要实现的是这样的形状
我能够使用大约 13 个单独的 for 循环来做到这一点,每次都手动移动六边形,但它既不实用也不自动化。
如果我们设置一些条件,我们可以很容易地推导出一个算法。
让条件为:
- 宽度和高度必须相等
- 宽度和高度必须是奇数
现在让我们看看你的形状,它满足条件,因为它的宽度和高度是 13。仔细观察会发现第一行有 7 个六边形,第二行有 8 个,第三行有 9 个,依此类推第 7 行最多 13 个六边形。之后六边形的数量每行减少一个,直到到达最后一行 13。
所以每行六边形的个数可以表示为:
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
其中 i
是行。
同样,每行的水平起始位置递减半个六边形的宽度,直到到达中心。
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
现在剩下要做的就是将 for-loop 修改为从 xStart
开始到 xStart+hexagons
。
for (j = xStart; j < xStart+hexagons; j++)
这是一个完整的例子:
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 9,
boardWidth = 13,
boardHeight = 13;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i, j, hexagons, xStart;
//this loop generates a rectangular hexagon grid
for (i = 0; i < height; i++) {
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
for (j = xStart; j < xStart + hexagons; j++) {
drawHexagon(
ctx,
j * hexRectangleWidth + ((i % 2) * hexRadius),
i * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if (fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
document.getElementById("slider").oninput = (e) => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawBoard(ctx, e.target.value, e.target.value);
}
<input type="range" min="3" max="27" value="13" step="2" id="slider"><br>
<canvas width='400' height='300' id='hexmap'></canvas>
我正在尝试绘制蜂窝状的六角网格。 到目前为止,我可以将它绘制成矩形,但我不知道如何将我的 for 循环转换为蜂窝状。
这是我目前拥有的
<html>
<body>
<canvas width='1080' height='720' id='hexmap'></canvas>
</body>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 36,
boardWidth = 10,
boardHeight = 10;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i,j;
//this loop generates a rectangular hexagon grid
for(i = 0; i < width; ++i) {
for(j = 0; j < height; ++j) {
drawHexagon(
ctx,
i * hexRectangleWidth + ((j % 2) * hexRadius),
j * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if(fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
})
</script>
</html>
虽然我想要实现的是这样的形状
我能够使用大约 13 个单独的 for 循环来做到这一点,每次都手动移动六边形,但它既不实用也不自动化。
如果我们设置一些条件,我们可以很容易地推导出一个算法。 让条件为:
- 宽度和高度必须相等
- 宽度和高度必须是奇数
现在让我们看看你的形状,它满足条件,因为它的宽度和高度是 13。仔细观察会发现第一行有 7 个六边形,第二行有 8 个,第三行有 9 个,依此类推第 7 行最多 13 个六边形。之后六边形的数量每行减少一个,直到到达最后一行 13。
所以每行六边形的个数可以表示为:
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
其中 i
是行。
同样,每行的水平起始位置递减半个六边形的宽度,直到到达中心。
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
现在剩下要做的就是将 for-loop 修改为从 xStart
开始到 xStart+hexagons
。
for (j = xStart; j < xStart+hexagons; j++)
这是一个完整的例子:
var canvas = document.getElementById('hexmap');
var hexHeight,
hexRadius,
hexRectangleHeight,
hexRectangleWidth,
hexagonAngle = 0.523598776, // 30 degrees in radians
sideLength = 9,
boardWidth = 13,
boardHeight = 13;
hexHeight = Math.sin(hexagonAngle) * sideLength;
hexRadius = Math.cos(hexagonAngle) * sideLength;
hexRectangleHeight = sideLength + 2 * hexHeight;
hexRectangleWidth = 2 * hexRadius;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#CCCCCC";
ctx.lineWidth = 1;
drawBoard(ctx, boardWidth, boardHeight);
function drawBoard(canvasContext, width, height) {
var i, j, hexagons, xStart;
//this loop generates a rectangular hexagon grid
for (i = 0; i < height; i++) {
hexagons = width - (Math.abs(Math.floor(width / 2) - i));
xStart = (width - 3) % 4 == 0 ? Math.ceil((width - hexagons) / 2) : Math.floor((width - hexagons) / 2);
for (j = xStart; j < xStart + hexagons; j++) {
drawHexagon(
ctx,
j * hexRectangleWidth + ((i % 2) * hexRadius),
i * (sideLength + hexHeight),
false
);
}
}
}
function drawHexagon(canvasContext, x, y, fill) {
var fill = fill || false;
canvasContext.beginPath();
canvasContext.moveTo(x + hexRadius, y);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight);
canvasContext.lineTo(x + hexRectangleWidth, y + hexHeight + sideLength);
canvasContext.lineTo(x + hexRadius, y + hexRectangleHeight);
canvasContext.lineTo(x, y + sideLength + hexHeight);
canvasContext.lineTo(x, y + hexHeight);
canvasContext.closePath();
if (fill) {
canvasContext.fill();
} else {
canvasContext.stroke();
}
}
document.getElementById("slider").oninput = (e) => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawBoard(ctx, e.target.value, e.target.value);
}
<input type="range" min="3" max="27" value="13" step="2" id="slider"><br>
<canvas width='400' height='300' id='hexmap'></canvas>