问题描述
当我用 startingAngle=0 调用我的函数时,它会产生一个大小合适的好形状。 示例:
var points = GetpolygonVertices(sides:4,radius:5,center:(5,5),startingAngle:0),produces:
points[0] = {X = 10 Y = 5}
points[1] = {X = 5 Y = 0}
points[2] = {X = 0 Y = 5}
points[3] = {X = 5 Y = 10}
观察到的边长是 10px,这是正确的,但会产生一个与人眼成 45º 的旋转正方形。
为了解决这个问题,我添加了一个开关/外壳来偏移 startAngle,这样它就会通过旋转 45º 将正方形置于人眼的正确角度。旋转有效,但形状不再是 10x10px 的正方形,而是从侧面减少 1 到 2px:
[0] = {X = 9 Y = 1}
[1] = {X = 1 Y = 1}
[2] = {X = 1 Y = 9}
[3] = {X = 9 Y = 9}
并且随着半径的增加而变得更糟,例如 radius=10:
[0] = {X = 17 Y = 3}
[1] = {X = 3 Y = 3}
[2] = {X = 3 Y = 17}
[3] = {X = 17 Y = 17}
我尝试使用地板和天花板而不是圆形,但它总是以丢失 1 或 2px 结束... 有没有办法改进函数,无论边数和旋转角度如何都保持形状大小相等?
我的功能:
public static Point[] GetpolygonVertices(int sides,int radius,Point center,double startingAngle = 0)
{
if (sides < 3)
throw new ArgumentException("polygons can't have less than 3 sides...",nameof(sides));
// Fix rotation
switch (sides)
{
case 3:
startingAngle += 90;
break;
case 4:
startingAngle += 45;
break;
case 5:
startingAngle += 22.5;
break;
}
var points = new Point[sides];
var step = 360.0 / sides;
int i = 0;
for (var angle = startingAngle; angle < startingAngle + 360.0; angle += step) //go in a circle
{
if (i == sides) break; // Fix floating problem
double radians = angle * Math.PI / 180.0;
points[i++] = new(
(int) Math.Round(Math.Cos(radians) * radius + center.X),(int) Math.Round(Math.Sin(-radians) * radius + center.Y)
);
}
return points;
}
编辑:我更新了功能,以在未给出角度时摆脱人眼正确方向的开关条件和产品形状。它仍然遭受同样的“问题”
public static Point[] GetpolygonVertices(int sides,double startingAngle = 0,bool flipHorizontally = false,bool flipVertically = false)
{
if (sides < 3)
throw new ArgumentException("polygons can't have less than 3 sides...",nameof(sides));
var vertices = new Point[sides];
double deg = 360.0 / sides;//calculate the rotation angle
var rad = Math.PI / 180.0;
var x0 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + startingAngle) * rad);
var y0 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + startingAngle) * rad);
var x1 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + deg + startingAngle) * rad);
var y1 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + deg + startingAngle) * rad);
vertices[0] = new(
(int) Math.Round(x0),(int) Math.Round(y0)
);
vertices[1] = new(
(int) Math.Round(x1),(int) Math.Round(y1)
);
for (int i = 0; i < sides - 2; i++)
{
double dsinrot = Math.Sin((deg * (i + 1)) * rad);
double dcosrot = Math.Cos((deg * (i + 1)) * rad);
vertices[i + 2] = new(
(int)Math.Round(center.X + dcosrot * (x1 - center.X) - dsinrot * (y1 - center.Y)),(int)Math.Round(center.Y + dsinrot * (x1 - center.X) + dcosrot * (y1 - center.Y))
);
}
if (flipHorizontally)
{
var startX = center.X - radius;
var endX = center.X + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].X = endX - (vertices[i].X - startX);
}
}
if (flipVertically)
{
var startY = center.Y - radius;
var endY = center.Y + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].Y = endY - (vertices[i].Y - startY);
}
}
return vertices;
}
编辑 2: 来自 Tim Roberts anwser 这里的函数从半径计算边长和从边长计算半径,这解决了我的问题。谢谢!
public static double CalculatepolygonSideLengthFromradius(double radius,int sides)
{
return 2 * radius * Math.Sin(Math.PI / sides);
}
public static double CalculatepolygonVerticalLengthFromradius(double radius,int sides)
{
return radius * Math.Cos(Math.PI / sides);
}
public static double CalculatepolygonRadiusFromSideLength(double length,int sides)
{
var theta = 360.0 / sides;
return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
}
解决方法
你的问题是一道数学题。您说“根据观察,边长为 10px”。它绝对不是 10px。从 (10,5) 到 (5,0) 的距离是 sqrt(5*5 + 5*5),即 7.07。这正是我们对内接在半径为 5 的圆中的正方形的期望:5 x sqrt(2)。
其他方格也是如此。
跟进
作为一个额外的好处,这里有一个函数,它返回外接一个长度为 L 的 N 条边的正多边形的圆的半径:
import math
def rad(length,nsides):
theta = 360/nsides
r = length / (2 * math.cos( (90-theta/2) * math.pi / 180))
return r
for s in range(3,9):
print(s,rad(10,s))