正六边形棋盘的思路

image.png

这是一个正六边形的棋盘,里面的格子都是由正三角形组成的
棋子也是由正三角形组成的,棋子要落在棋盘上,就要知道棋子的位置
研究的问题是,怎么去描述棋子的位置
经过数学大神点播后,有了一点想法,分享一个思路

化简化简

  1. 格子编号
    因为棋子和棋盘都是由格子组成的,想了半天,我觉得给格子编号会比较方便
  2. 棋盘分区
    正六边形其实可以看做是6块三角形组成的,在平面直角坐标系中按照原点旋转是有公式的
    x1=cos(angle)x-sin(angle)y;
    y1=cos(angle)y+sin(angle)x;
    那么这个正六边形,就可以看作由1个三角形旋转不同角度获得的
    给棋盘分为6个区,那么研究的问题就变成了一个正三角形
  3. 建立坐标系
    选了一个自己算着舒服的三角形建立坐标系


    image.png
  4. 分层编号,寻找规律


    image.png
  • 每一层的Y轴坐标相差√3/2
  • 每个编号的X轴坐标相差1/2
  • 唯一不同的是编号3是朝上的三角形,而编号1,2,4是朝下的三角形,但是通过分层可以发现,每层奇数号的三角形是朝下的,偶数号的三角形是朝上的
  • 而且朝上的三角形与朝下的三角形,他们的Y轴坐标也是加减√3/2,X轴不变(见绿色三角形)

代码实现

let baseAreas = 6
let baseIds = 81
const G3 = Math.sqrt(3)
// 把所有三角都看作是1,1的三角变换而来,那么保存一个1,1三角的坐标
const defaultTri = [
    [0, 0],
    [1 / 2, G3 / 2],
    [-1 / 2, G3 / 2],
]
// 根据area和id获取三角形3个顶点坐标
function getVertex(id) {
    const [tier, index] = getTier(id)
    // 根据11变换
    let coord = transform(tier, index)
    // index如果是偶数,y轴坐标需要变换
    if (index % 2 === 0) {
        coord = indexTransformY(coord)
    }
    return coord
}

// 根据id获取在area中的层数与该层第几个
function getTier(id) {
    const tier = Math.ceil(Math.sqrt(id))
    // 减去上一层的总数
    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旋转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
    ])
}
  1. 主方法是getVertexrotate,getVertex负责根据编号获取三角形的3个顶点坐标,rotate负责根据分区旋转这个坐标
  2. getTier根据编号获取层数
  3. transform 根据编号规律(层以及层内编号)获得三角形顶点坐标
  4. indexTransformY根据本层编号奇偶去做Y轴变换
    通过拆分成几个小问题就比较清晰拉~去实现每一个小问题的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();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350