考虑以下设置:
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
里面我希望能够绘制一个多边形,其中任意数量的角始终位于其一侧(而不是其中一个角)。多边形将“占用”的高度和宽度应始终等于其父级的高度和宽度(在本例中为200px X 200px),我们将假设它始终为正方形。
是否有标准算法来实现(不使用任何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左下角的多边形。显然他们是“常规凸”类型?道歉,但我不是数学家。
答案 0 :(得分:3)
这实际上不是一个简单的问题。首先,让我们不那么含糊地说明问题:
给定边长 S 的正方形和整数 n ≥3,确定最大长度 L ,使得有规则的凸多边形 n 长度为 L 的边可以构造成:
- 多边形的一边位于正方形的一边;
- 多边形包含在正方形内。
醇>
请注意,不要求多边形触摸正方形以外的任何位置,而不是位于正方形一侧的一侧。 (虽然对于 n &gt; 3,表明会有其他联系点并不太难。)
一旦边长 L ,那么构造多边形本身就相当容易了。让我们先做这个(因为它对第一部分有帮助)。从简单的trig开始,多边形的中心将是距离
多边形底部中心上方的h = L /(2⋅tan(π/ n ))
(从对称性)也将是方形底部的中心。对于其余部分,我们假设多边形的中心是我们坐标系的原点,+ X轴向右,+ Y轴向上。 (请注意,我们的Y轴与通常的计算机图形坐标系相反。)多边形的半径(从中心到每个顶点的距离)将是
r = L /(2⋅sin(π/ n ))
我们打电话给上面的等式1 ;我们将在最后回顾它。顶点将由:
给出x i = r ⋅cos(θ 0 +2⋅ π⋅ i / n )
y i = r ⋅sin(θ 0 +2⋅π⋅< em> i / n )
其中θ 0 是确定多边形旋转的“相位角”。我们希望旋转使得多边形的底边是水平的。如果我们想要第一个顶点( i = 0)是多边形底部的右顶点,那么我们应该使用
θ 0 =-π/ 2
通过这种替换,我们的坐标方程简化为:
x i = r ⋅sin(2⋅π⋅ i / n )
y i = - r ⋅cos(2⋅π⋅ i / n )
为了将来参考,以上将是公式2 。现在我们需要确定 L 。我认为,反转问题更容易:对于给定的 L ,最小的 S 是什么,使得边长 S 的正方形可以包含多边形,其中一边包含多边形的一边。然后我们可以扩展所有内容以匹配原始问题的给定平方。但这很容易。让我们使用 L = 1.对于 n 偶数,多边形的直径 d (多边形上点之间的最大距离)是
d =2⋅ r = 1 / sin(π/ n )( n 偶数)< / p>
对于 n 奇数,不难看出直径是从基底上的一个顶点到多边形顶部顶点的距离。有一点结构(留给读者)和余弦定律,结果证明是:
d = sqrt([1 + cos(π/ n )] / [2·sin 2 (π/ n )])( n 奇数)
在这两种情况下,很容易确定多边形的至少一个直径是水平的。因此,包含多边形的最小正方形的一侧(多边形底座位于正方形的底部)必须至少为直径,并且(根据多边形直径的定义)正方形不必更大
由于我们想要扩展一切,以便方块的一侧测量给定数量 S ,我们最终得到:
L = S / d
因此,构造多边形的算法首先确定 L ,如我们刚刚描述的那样,然后使用公式1计算 r ,最后使用公式2计算顶点,这些顶点应按顺序连接以形成多边形。
答案 1 :(得分:3)
简单..
任何疑惑..只需检查下面的代码
//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"/>