计算具有角度的多边形顶点会产生错误大小的形状
Calculating polygon vertices with an angle produce the shape wrong size
当我用 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º 的旋转正方形。
为了解决这个问题,我添加了一个 switch/case 来偏移 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}
我尝试同时使用 floor 和 ceil 而不是 round,但它总是以丢失 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, int radius, Point center, 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 here the functions to calculate side length from radius and radius from side length, 这解决了我的问题问题。谢谢!
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) 的圆中的正方形的期望。
其他方块也是如此。
跟进
作为额外的奖励,这里有一个函数 returns 外接 N 条边长 L 的正多边形的圆的半径:
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))
当我用 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º 的旋转正方形。
为了解决这个问题,我添加了一个 switch/case 来偏移 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}
我尝试同时使用 floor 和 ceil 而不是 round,但它总是以丢失 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, int radius, Point center, 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 here the functions to calculate side length from radius and radius from side length, 这解决了我的问题问题。谢谢!
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) 的圆中的正方形的期望。
其他方块也是如此。
跟进
作为额外的奖励,这里有一个函数 returns 外接 N 条边长 L 的正多边形的圆的半径:
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))