【问题标题】:Algorithm creating polygon of n corners inside a square (HTML5 - Canvas)?在正方形内创建 n 个角的多边形的算法(HTML5 - Canvas)?
【发布时间】:2014-12-25 15:42:35
【问题描述】:

考虑以下设置:

CSS:

div {
    position :absolute;
    top: 50px;
    left: 50px;
    height: 200px;
    width: 200px;
    border: 2px solid black;
}

HTML:

<div id="container">
    <canvas id="my-canvas"/>
</div>

现在在#my-canvas 内部,我希望能够绘制一个具有任意数量的角的多边形,这些角总是位于它的一侧(而不是它的一个角)。多边形将“占据”的高度和宽度应始终等于其父级的高度和宽度(在本例中为 200 像素 X 200 像素),我们假设它始终是正方形。

是否有标准算法来实现这一点(不使用任何 JS 库)?

我想在#my-container 内绘制一个有 4 个角的多边形是最简单的,因为您基本上会创建一个大小相等的正方形:

var c = document.getElementById('my-canvas').getContext('2d');
var side = document.getElementById('container').clientHeight;
c.fillStyle = '#f00';
c.beginPath();
c.moveTo(0, 0);
c.lineTo(0, side);
c.lineTo(side, side);
c.lineTo(side, 0);
c.closePath();
c.fill();

但是有 5 个角或 n 个角的多边形呢?

var corners = 5;
var c = document.getElementById('my-canvas').getContext('2d');
var side = (document.getElementById('container').clientHeight * 4) / corners;
c.fillStyle = '#f00';
c.beginPath();
c.moveTo(0, 0);
// ok the following is totally wrong, but I'm sure there is a loop involved and that the x & y
// should increment/decrement each time around in some way, relevant to the value of *side*
for (var i=0; i<corners; i++) {
   c.lineTo((side*i), side);
} 
c.closePath();
c.fill();

提前感谢您的帮助!

注意:

我只对边长相等的多边形感兴趣(我猜这就是你所说的“常规”多边形?)。

由于我们在 HTML/CSS 世界中,允许的最小边显然是 1px 的长度。所以这是唯一的限制。

我所指的多边形类型位于this image 的左下方。显然它们属于“常规凸”类型?抱歉,我不是数学家。

【问题讨论】:

  • 内切线:P1=[sin(a*n)*r+Cx, cos(a*n)*r+Cy]; P2=[sin(a*(n+1))*r+Cx, cos(a*(n+1))*r+Cy]; n=0..Nc.
  • 我在 Java 中做了这个(作为一个多边形)。基本上,您从矩形的中心开始,并使用 x / y 半径以 n 个顶点的步长绕一圈,每个角度增加 2 * PI / # 个顶点。如果您想查看代码,请告诉我
  • 有趣的等式,但我不知道P1,P2,a,r,Cx,Cy 代表什么。我猜n 是角的数量?
  • 感谢 ControlAltDel。是的,我不想看到代码,但最好将它作为通用算法发布,这样我就可以想到它的 JS 版本。 :-)
  • 请注意,无法满足您“始终有边”的要求。即使是像六边形这样简单的东西,它的两个相邻顶点也不能在矩形的边缘上并适合矩形:如果我们将它们标记为 p1,...,p6,然后是 p1 和 p2,以及 p4 和 p5,将位于边界矩形的边缘,但 p3 和 p6 将位于该矩形之外。有平方内标算法,但额外的要求会产生几何上不可能的约束 =)

标签: javascript html algorithm canvas polygon


【解决方案1】:

这其实不是一个简单的问题。首先,让我们把问题表述得不那么含糊:

给定一个边长为 S 的正方形和一个整数 n ≥ 3,确定最大长度 L 使得规则的凸多边形n 边的长度为 L 可以这样构造:

  1. 多边形的一侧位于正方形的一侧;
  2. 多边形包含在正方形中。

请注意,这要求多边形接触正方形的任何位置,而不是位于正方形一侧的一侧。 (虽然对于n > 3,不难看出还会有其他的接触点。)

一旦边长为L,那么构造多边形本身就相当容易了。让我们先这样做(因为它有助于第一部分)。从简单的触发来看,多边形的中心将是一段距离

h = L / (2 ⋅ tan(π / n))

在多边形底边的中心之上,(从对称角度)也将是正方形底边的中心。对于剩下的部分,让我们假设多边形的中心是我们坐标系的原点,+X 轴向右,+Y 轴向上。 (请注意,我们的 Y 轴与通常的计算机图形坐标系相反。)多边形的半径(从中心到每个顶点的距离)将为

r = L / (2 ⋅ sin(π / n))

我们把上面的方程1;我们将在最后提及它。顶点将由以下方式给出:

xi = r ⋅ cos(θ0 + 2 ⋅ π ⋅ i / n)
yi = r ⋅ sin (θ0 + 2 ⋅ π ⋅ i / n)

其中θ0 是决定多边形旋转的“相位角”。我们希望旋转使得多边形的底边是水平的。如果我们希望第一个顶点 (i = 0) 是多边形底部的右顶点,那么我们应该使用

θ0 = -π / 2

通过这种替换,我们的坐标方程简化为:

xi = r ⋅ sin(2 ⋅ π ⋅ i / n em>)
yi = −r ⋅ cos(2 ⋅ π ⋅ i / n)

为了将来参考,上面将是公式 2。现在我们需要确定L。我认为更容易反转问题:对于给定的 L,最小的 S 是多少,使得边长为 S 的正方形可以包含多边形的一侧包含多边形的一侧。然后我们可以缩放所有内容以匹配原始问题的给定平方。但这很容易。让我们使用L = 1。对于n偶数,多边形的直径d(多边形上点之间的最大距离)为

d = 2 ⋅ r = 1 / sin(π / n)      (n 偶数)

对于 n 奇数,不难看出直径是从底部的一个顶点到多边形顶部的顶点的距离。通过一点构造(留给读者)和余弦定律,结果是:

d = sqrt([1 + cos(π / n)] / [2 ⋅ sin2(π / n)])      (n 奇数)

在这两种情况下,很容易确定多边形的至少一个直径是水平的。因此,包含多边形的最小正方形的边(多边形底部位于正方形底部)必须至少是直径,并且(根据多边形直径的定义)正方形永远不必更大.

由于我们想要缩放所有内容,以便正方形的边测量给定数量 S,我们终于有了:

L = S / d

所以构造多边形的算法是先确定我们刚才描述的L,然后用公式1计算r,最后用公式2计算r计算顶点,这些顶点应该按顺序连接以形成多边形。

【讨论】:

    【解决方案2】:

    简单..

    • 将一个圆分成 x 个边数,360 / x = 边角
    • 计算每个形状点
    • 计算人脸的角度
    • 旋转形状,使脸平入容器中
    • 重新缩放点以适应边界

    任何疑问..只需检查下面的代码

    //load data
    var c = document.getElementById('canvas').getContext('2d');
    var width = document.getElementById('canvas').clientWidth;
    var height = document.getElementById('canvas').clientHeight;
    var corners = 5;
    //initial calculation
    var radius = 1;
    var angle = (Math.PI * 2) / corners;
    
    //build points 
    var points = [];
    for (var i=0; i<corners; i++)
    {
        var a = angle * i;
        //sin and cos are swithced,point 0 is bottom one
        var x = (Math.sin(a)*radius);
        var y = (Math.cos(a)*radius);
        points.push({
            x:x
            ,y:y
        })
    } 
    //get the angle of a side
    var sideangle = Math.atan2(points[1].y-points[0].y, points[1].x-points[0].x)
    //rotate point around bottom one
    var o = points[0];
    for (var i=1; i<points.length; i++)
    {
        points[i] = {
            x:Math.cos(sideangle) * (points[i].x - o.x) - Math.sin(sideangle) * (points[i].y-o.y) + o.x
            ,y:Math.sin(sideangle) * (points[i].x - o.x) + Math.cos(sideangle) * (points[i].y - o.y) + o.y
        };
    }
    //by this point the figure is "flat on the floor" lets measure its size
    var rect = {top:2,left:2,right:-2,bottom:-2};
    for (var i=0; i<points.length; i++)
    {
        rect.top = Math.min(rect.top,points[i].y);
        rect.bottom = Math.max(rect.bottom,points[i].y);
        rect.left = Math.min(rect.left,points[i].x);
        rect.right = Math.max(rect.right,points[i].x);
    }
    rect.width = Math.abs(rect.right - rect.left);
    rect.height = Math.abs(rect.bottom - rect.top);
    //make points relative to top left of rect
    for (var i=0; i<points.length; i++)
    {
        points[i] = {
            x: points[i].x - rect.left
            ,y: points[i].y - rect.top
        };
    }
    //lets scale and position the poly based on its rect
    var ratioX = width / rect.width
    var ratioY = height / rect.height
    var ratio = Math.min(ratioX, ratioY);
    
    for (var i=0; i<points.length; i++)
    {
        points[i] = {
            x: (points[i].x * ratio)
            ,y: (points[i].y * ratio)
        };
    }
    
    //draw path
    c.beginPath();
    
    c.moveTo(points[0].x, points[0].y);
    for (var i=1; i<points.length; i++)
        c.lineTo(points[i].x, points[i].y);
    c.closePath();
    //draw
    c.strokeStyle='#f00'
    c.stroke();
    c.fillStyle = '#f00';
    //c.fill();
    canvas{
        position :absolute;
        top: 50px;
        left: 50px;
        border: 2px solid black;
    }
    <!--size must be defined as attribute-->
    <canvas width='300' height='200' id="canvas"/>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      • 1970-01-01
      • 2011-12-19
      • 2014-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多