将格子图案添加到动态绘制的 3 人棋盘中

Posted

技术标签:

【中文标题】将格子图案添加到动态绘制的 3 人棋盘中【英文标题】:Adding the checked pattern to a dynamically drawn 3 player chess board 【发布时间】:2014-04-17 13:39:02 【问题描述】:

你好,你们这群出色的天才!

我似乎在这里达到了我的知识顶峰,希望有人能指出我正确的方向。

我正在尝试使用 javascripthtml 5 画布动态绘制 3 人国际象棋/跳棋棋盘。

到目前为止,我想出了这个;

var canvas = document.getElementById('canvas')
var length = canvas.height / 2;
var center = canvas.width / 2;

var rotation = ToRadians(60);

var angle = ToRadians(30);
var height = length * Math.cos(angle);
var width = length * Math.sin(angle);
while (rotation < Math.PI * 2) 
    a = [center, length];
    b = [a[0] - height * Math.sin(rotation), a[1] + height * Math.cos(rotation)];
    c = [b[0] + width * Math.cos(rotation), b[1] + width * Math.sin(rotation)];
    d = [c[0] + width * Math.sin(angle + rotation), c[1] - width * Math.cos(angle + rotation)];

    //Drawing Main Frame and 6 segments
    var c2 = canvas.getContext('2d');
    c2.fillStyle = '#f00';
    c2.strokeStyle = "#0f0";
    c2.beginPath();
    c2.moveTo(a[0], a[1]);
    c2.lineTo(b[0], b[1]);
    c2.lineTo(c[0], c[1]);
    c2.lineTo(d[0], d[1]);
    c2.closePath();
    c2.stroke();

    //Drawing first set of divides
    ab1=[((a[0]+b[0])/2),((a[1]+b[1])/2)]
    cd1=[((c[0]+d[0])/2),((c[1]+d[1])/2)]
    ab2=[((a[0]+ab1[0])/2),((a[1]+ab1[1])/2)]
    cd2=[((d[0]+cd1[0])/2),((d[1]+cd1[1])/2)]
    ab3=[((b[0]+ab1[0])/2),((b[1]+ab1[1])/2)]
    cd3=[((c[0]+cd1[0])/2),((c[1]+cd1[1])/2)]

    c2.beginPath();
    c2.moveTo(ab1[0], ab1[1]);
    c2.lineTo(cd1[0], cd1[1]);        
    c2.moveTo(ab2[0], ab2[1]);
    c2.lineTo(cd2[0], cd2[1]);    
    c2.moveTo(ab3[0], ab3[1]);
    c2.lineTo(cd3[0], cd3[1]);   
    c2.stroke();

    //Drawing second set of divides
    bc1=[((c[0]+b[0])/2),((c[1]+b[1])/2)]
    ad1=[((a[0]+d[0])/2),((a[1]+d[1])/2)]
    bc2=[((c[0]+bc1[0])/2),((c[1]+bc1[1])/2)]
    ad2=[((d[0]+ad1[0])/2),((d[1]+ad1[1])/2)]
    bc3=[((b[0]+bc1[0])/2),((b[1]+bc1[1])/2)]
    ad3=[((a[0]+ad1[0])/2),((a[1]+ad1[1])/2)]

    c2.beginPath();
    c2.moveTo(bc1[0], bc1[1]);
    c2.lineTo(ad1[0], ad1[1]);        
    c2.moveTo(bc2[0], bc2[1]);
    c2.lineTo(ad2[0], ad2[1]);    
    c2.moveTo(bc3[0], bc3[1]);
    c2.lineTo(ad3[0], ad3[1]);   
    c2.stroke();

    rotation += ToRadians(60);

function ToRadians(degrees) 
    return degrees / (180 / Math.PI);

小提琴:http://jsfiddle.net/yd7Wv/6529/

到目前为止,我对代码非常满意,但是当我需要添加检查模式时,我已经到了这一点,我完全被难住了。我真的不知道如何去做,所以想知道是否有人能指出我正确的方向。 我知道这里有一个普遍的共识,即人们应该尝试自己做,但我根本做不到! 任何指针将不胜感激。

干杯

【问题讨论】:

请在 jsfiddle 中重现此内容。 酷板概念,如果我不工作,我很想有一个破解:P 【参考方案1】:

您正在查看的内容与四边形变换密切相关。

您可以将一个线段(“三角形”)视为只是透视变形的象限。

Fiddle demo

产生这个结果:

让我们从定义计算和循环所需的一些变量开始。

var w = canvas.width,                // width
    h = canvas.height,               // height
    cx = w * 0.5,                    // center of board
    cy = h * 0.5,
    r = cx * 0.9,                    // radius of board
    pi2 = Math.PI * 2,               // cache
    segments = 6,                    // a hexagon based shape so 6
    segment = pi2 / segments,        // angle of each segment
    hSegment = segment * 0.5,        // half segment for center line
    ul, ur, bl, br,                  // quad. corners
    check = 0.25,                    // interpolation interval (one check)
    yc = 0, xc = 0,                  // interpolation counters
    toggle = false,                  // for color
    x, y = 0, i = 0;                 // counters...

让我们通过定义其外边界的角来定义一个四边形:

第一个角将是棋盘的中心,因此很简单:

var ul = 
    x: cx,
    y: cy

第二个角在右上角:

ur = 
    x: cx + r * Math.cos(hSegment) * 0.865,
    y: cy + r * Math.sin(hSegment) * 0.865
;

右下第三:

br = 
    x: cx + r * Math.cos(segment),
    y: cy + r * Math.sin(segment)
;

最后,左下角:

bl = 
    x: cx + r * Math.cos(hSegment + segment) * 0.865,
    y: cy + r * Math.sin(hSegment + segment) * 0.865
;

如果我们画出这个形状,我们会得到这个:

现在我们有了角,我们只需按检查间隔 (0.25) 对“正方形”中的每一行进行插值,这将得到总共 5 行。我们将只计算 4,但我们还将使用 next 行与当前值。

为了插入两个点,我们使用一个简单的函数,它接受两个点和一个标准化值 [0.0, 1.0]:

function getInt(p1, p2, t) 
    return 
        x: p1.x + (p2.x - p1.x) * t,
        y: p1.y + (p2.y - p1.y) * t,
    

我们创建了一个循环来遍历 y 和 x 点,以便我们可以系统地执行此操作:

for(y = 0, yc = 0; y < 4; y++) 
    for(x = 0, xc = 0; x < 4; x++) 

        // for upper lines (ul-ur), get first row:
        var l1a = getInt(ul, bl, yc), 
            l1b = getInt(ur, br, yc),
            l2a = getInt(ul, bl, yc + check),
            l2b = getInt(ur, br, yc + check),

            c1 = getInt(l1a, l1b, xc),
            c2 = getInt(l1a, l1b, xc + check),
            c3 = getInt(l2a, l2b, xc + check),
            c4 = getInt(l2a, l2b, xc);

        ... draw shape ...

        xc += check;
    
    yc += check;        

本节:

var l1a = getInt(ul, bl, yc),          // current line [0, 3]
    l1b = getInt(ur, br, yc),
    l2a = getInt(ul, bl, yc + check),  // next line [1, 4]
    l2b = getInt(ur, br, yc + check),

计算外部垂直线上的插值点。这为我们提供了两个新点,然后我们使用它们来计算水平线上的一个点,并使我们能够计算每个角点以进行“检查”:

c1 = getInt(l1a, l1b, xc),          // corner 1 UL
c2 = getInt(l1a, l1b, xc + check),  // corner 2 UR (next line)
c3 = getInt(l2a, l2b, xc + check),  // corner 3 BR (next line)
c4 = getInt(l2a, l2b, xc);          // corner 4 BL

现在我们只需在这些角之间绘制一个多边形并进行填充:

ctx.beginPath();
ctx.moveTo(c1.x, c1.y);
ctx.lineTo(c2.x, c2.y);
ctx.lineTo(c3.x, c3.y);
ctx.lineTo(c4.x, c4.y);

ctx.fillStyle = toggle ? '#000' : '#fff';

我们使用拨动开关来改变颜色。

这个单一的片段看起来像这样:

下一步是绘制所有线段。我们重复使用上面的代码,每次只需将画布旋转一段并进行额外的切换。

当所有代码放在一起时,我们得到这个:

for(; i < segments; i++)     // loop six segments
    toggle = !toggle;         // alter color each segment
    // loop quadrilateral grid 4x4 cells (5x5 lines exclusive)
    for(y = 0, yc = 0; y < 4; y++) 
        for(x = 0, xc = 0; x < 4; x++) 

            // for upper lines (ul-ur), get first row:
            var l1a = getInt(ul, bl, yc),
                l1b = getInt(ur, br, yc),
                l2a = getInt(ul, bl, yc + check),
                l2b = getInt(ur, br, yc + check),
                c1 = getInt(l1a, l1b, xc),
                c2 = getInt(l1a, l1b, xc + check),
                c3 = getInt(l2a, l2b, xc + check),
                c4 = getInt(l2a, l2b, xc);

            ctx.beginPath();
            ctx.moveTo(c1.x, c1.y);
            ctx.lineTo(c2.x, c2.y);
            ctx.lineTo(c3.x, c3.y);
            ctx.lineTo(c4.x, c4.y);
            ctx.fillStyle = toggle ? '#000' : '#fff';
            ctx.fill();
            toggle = !toggle;
            xc += check;
        
        yc += check;          // next segment line
        toggle = !toggle;     // toggle per line as well
    
    ctx.translate(cx, cy);    // translate to center
    ctx.rotate(segment);      // rotate one segment
    ctx.translate(-cx, -cy);  // translate back

现在您可以根据需要简单地绘制轮廓等等。

【讨论】:

+1 干得好!您的解决方案具有创建 Spitfire2k6 可以用来放置他的棋子的多边形边界的额外好处。曾几何时,我真的很喜欢下棋。 @markE 好点。我忘记了,但是是的,将每个段作为一个多边形存储在一个数组中可以很容易地检查边界。我自己从来没有下过国际象棋,但我确实觉得生活本身就像一盘国际象棋:)

以上是关于将格子图案添加到动态绘制的 3 人棋盘中的主要内容,如果未能解决你的问题,请参考以下文章

在Android中绘制动态自定义视图棋盘

在具有透明背景的绘制文本上添加图案覆盖

HarmonyOS - 方舟开发框架ArkUI 基于JSAPI实现五子棋游戏

CAD参数绘制图案填充(网页版)

如何在 Flutter 的画布上的路径内绘制图案?

OpenGLES 使用 —— 图片绘制