image.png
這是一個正六邊形的棋盤愧杯,里面的格子都是由正三角形組成的
棋子也是由正三角形組成的,棋子要落在棋盤上,就要知道棋子的位置
研究的問題是朱沃,怎么去描述棋子的位置
經(jīng)過數(shù)學(xué)大神點播后,有了一點想法茅诱,分享一個思路
化簡化簡
- 格子編號
因為棋子和棋盤都是由格子組成的逗物,想了半天,我覺得給格子編號會比較方便 - 棋盤分區(qū)
正六邊形其實可以看做是6塊三角形組成的瑟俭,在平面直角坐標(biāo)系中按照原點旋轉(zhuǎn)是有公式的
x1=cos(angle)x-sin(angle)y;
y1=cos(angle)y+sin(angle)x;
那么這個正六邊形翎卓,就可以看作由1個三角形旋轉(zhuǎn)不同角度獲得的
給棋盤分為6個區(qū),那么研究的問題就變成了一個正三角形 -
建立坐標(biāo)系
選了一個自己算著舒服的三角形建立坐標(biāo)系
image.png -
分層編號摆寄,尋找規(guī)律
image.png
- 每一層的Y軸坐標(biāo)相差√3/2
- 每個編號的X軸坐標(biāo)相差1/2
- 唯一不同的是編號3是朝上的三角形失暴,而編號1坯门,2,4是朝下的三角形逗扒,但是通過分層可以發(fā)現(xiàn)古戴,每層奇數(shù)號的三角形是朝下的,偶數(shù)號的三角形是朝上的
- 而且朝上的三角形與朝下的三角形,他們的Y軸坐標(biāo)也是加減√3/2矩肩,X軸不變(見綠色三角形)
代碼實現(xiàn)
let baseAreas = 6
let baseIds = 81
const G3 = Math.sqrt(3)
// 把所有三角都看作是1,1的三角變換而來,那么保存一個1,1三角的坐標(biāo)
const defaultTri = [
[0, 0],
[1 / 2, G3 / 2],
[-1 / 2, G3 / 2],
]
// 根據(jù)area和id獲取三角形3個頂點坐標(biāo)
function getVertex(id) {
const [tier, index] = getTier(id)
// 根據(jù)11變換
let coord = transform(tier, index)
// index如果是偶數(shù),y軸坐標(biāo)需要變換
if (index % 2 === 0) {
coord = indexTransformY(coord)
}
return coord
}
// 根據(jù)id獲取在area中的層數(shù)與該層第幾個
function getTier(id) {
const tier = Math.ceil(Math.sqrt(id))
// 減去上一層的總數(shù)
const index = id - Math.pow(tier - 1, 2)
return [tier, index]
}
function transform(tier, index) {
// X=(tier-index)*1/2
// y=(tier-1)*G3/2
return defaultTri.map(([x, y]) => [x + (tier - index) * 1 / 2, y + (tier - 1) * G3 / 2])
}
function indexTransformY([
[x1, y1],
[x2, y2],
[x3, y3]
]) {
return [
[x1, y1 + G3 / 2],
[x2, y2 - G3 / 2],
[x3, y3 - G3 / 2],
]
}
/*
變換的每個area旋轉(zhuǎn)60°
x1=cos(angle)*x-sin(angle)*y;
y1=cos(angle)*y+sin(angle)*x;
*/
function rotate(area, coord) {
const arg = (area - 1) * 60 * 2 * Math.PI / 360
return coord.map(([x, y]) => [
Math.cos(arg) * x - Math.sin(arg) * y,
Math.cos(arg) * y + Math.sin(arg) * x
])
}
- 主方法是getVertex和rotate现恼,getVertex負(fù)責(zé)根據(jù)編號獲取三角形的3個頂點坐標(biāo),rotate負(fù)責(zé)根據(jù)分區(qū)旋轉(zhuǎn)這個坐標(biāo)
- getTier根據(jù)編號獲取層數(shù)
- transform 根據(jù)編號規(guī)律(層以及層內(nèi)編號)獲得三角形頂點坐標(biāo)
-
indexTransformY根據(jù)本層編號奇偶去做Y軸變換
通過拆分成幾個小問題就比較清晰拉~去實現(xiàn)每一個小問題的function就可以了
驗證
sanjiao.gif
附上一個比較直觀的畫圖方法
// 畫圖驗證
window.onload = function() {
btn.addEventListener('click', () => {
// 獲取area
let area = document.getElementById('area').value
let id = document.getElementById('id').value
tri = [area, id]
draw()
})
canvas.addEventListener('mousewheel', ({
deltaY
}) => {
if (deltaY < 0) {
zoom = zoom + 2
} else {
zoom = zoom - 2 < 0 ? 2 : zoom - 2
}
draw()
})
canvas.addEventListener('mousedown', mouseDown)
draw()
}
function mouseDown() {
window.addEventListener('mousemove', mouseMove)
window.addEventListener('mouseup', mouseUp)
}
function mouseUp() {
window.removeEventListener('mousemove', mouseMove)
window.removeEventListener('mouseup', mouseUp)
}
function mouseMove({
movementX,
movementY
}) {
origin[0]=origin[0] + movementX
origin[1]=origin[1] + movementY
draw()
}
board = [] // 棋盤
tri = [] // 三角
for (let i = 1; i <= baseAreas; i++) {
for (let j = 1; j <= baseIds; j++) {
board.push(rotate(i, getVertex(j)))
}
}
let zoom = 30
const origin = [0, 0]
function draw() {
drawBoard()
drawTarget()
}
function drawBoard() {
// 畫棋盤
const {
clientWidth: width,
clientHeight: height
} = canvas
let oX = width / 2
let oY = height / 2
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, width, height)
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
const [dx, dy] = origin
board.forEach(([
[x1, y1],
[x2, y2],
[x3, y3]
]) => {
ctx.beginPath()
// canvas y軸取反
y1 = -y1
y2 = -y2
y3 = -y3
ctx.moveTo(x1 * zoom + oX + dx, y1 * zoom + oY + dy)
ctx.lineTo(x2 * zoom + oX + dx, y2 * zoom + oY + dy)
ctx.lineTo(x3 * zoom + oX + dx, y3 * zoom + oY + dy)
ctx.closePath()
ctx.stroke();
})
}
function drawTarget() {
const {
clientWidth: width,
clientHeight: height
} = canvas
let oX = width / 2
let oY = height / 2
const ctx = canvas.getContext('2d')
const [dx, dy] = origin
const [area, id] = tri
if (area && id) {
let [
[x1, y1],
[x2, y2],
[x3, y3]
] = rotate(area, getVertex(id))
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
ctx.beginPath()
// canvas y軸取反
y1 = -y1
y2 = -y2
y3 = -y3
ctx.moveTo(x1 * zoom + oX + dx, y1 * zoom + oY + dy)
ctx.lineTo(x2 * zoom + oX + dx, y2 * zoom + oY + dy)
ctx.lineTo(x3 * zoom + oX + dx, y3 * zoom + oY + dy)
ctx.closePath()
ctx.stroke();
}
}