计算带旋转角度的多边形顶点时出现尺寸偏差如何解决?
多边形顶点生成函数尺寸偏差问题与解决方案
当我调用函数时传入startingAngle=0参数,可以生成尺寸正确的正常形状,示例如下:
var points = GetPolygonVertices(sides:4, radius:5, center:(5, 5), startingAngle:0), 输出结果为: 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)); // 修正旋转角度 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) //沿圆周遍历 { if (i == sides) break; // 修复浮点数精度问题 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; }
第一次更新后的函数
更新了函数,移除了switch条件,未传入角度时可以生成符合人眼视角的正置形状,但仍然存在相同的“尺寸偏差”问题:
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;//计算旋转步长 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; }
最终解决方案
根据Tim Roberts的回答,补充了通过半径计算边长、通过边长计算半径的函数,已经解决了尺寸偏差问题:
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)); }
内容的提问来源于stack exchange,提问作者Tiago Conceição
相关产品推荐
相关产品推荐

