重复围绕枢轴点旋转点

6

最近我一直在使用下面的函数,在我的各种程序中围绕一个中心点旋转一系列点。

private Point RotatePoint(Point point, Point pivot, double radians)
{
    var cosTheta = Math.Cos(radians);
    var sinTheta = Math.Sin(radians);

    var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
    var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);

    return new Point((int)x, (int)y);
}

在旋转一个形状多次以微小角度时,这种方法一直行之有效。例如,在由4个点组成的矩形多边形上调用45°,我得到了以下结果:

foreach (var point in points)
    Rotate(point, center, Math.PI / 180f * 45);

enter image description here

但是如果我调用旋转45次1°,得到的就是这个结果:

for (var i = 0; i < 45; ++i)
    foreach (var point in points)
        Rotate(point, center, Math.PI / 180f * 1)

enter image description here

只要我只调用它一次就可以,而且似乎随着旋转角度的降低而逐渐恶化。这个函数有什么缺陷,还是我对这个函数的基本作用有误解?
如何通过小幅度重复旋转?我可以保存基准点,并在旋转改变时使用它们来更新当前点,但这是唯一的方法吗?

1
你能改成使用 PointF 吗? - TaW
2个回答

4
您的Point位置测量存在问题,这是由于RotatePoint()方法生成的整数舍入引起的。
通过在方法返回值中使用float坐标进行简单更正,可得到正确的测量结果:
为测试它,请创建一个定时器,并将其Tick事件注册为RotateTimerTick()
为了强调运动效果,增加了旋转自增量(请参见rotationSpin字段)。
PointF pivotPoint = new PointF(100F, 100F);
PointF rotatingPoint = new PointF(50F, 100F);
double rotationSpin = 0D;

private PointF RotatePoint(PointF point, PointF pivot, double radians)
{
    var cosTheta = Math.Cos(radians);
    var sinTheta = Math.Sin(radians);

    var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
    var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);

    return new PointF((float)x, (float)y);
}

private void RotateTimerTick(object sender, EventArgs e)
{
    rotationSpin += .5;
    
    if (rotationSpin > 90) rotationSpin = 0;
    rotatingPoint = RotatePoint(rotatingPoint, pivotPoint, (Math.PI / 180f) * rotationSpin);
    Panel1.Invalidate(new Rectangle(new Point(50,50), new Size(110, 110)));
}

private void Panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.FillEllipse(Brushes.White, new RectangleF(100, 100, 8, 8));
    e.Graphics.FillEllipse(Brushes.Yellow, new RectangleF(rotatingPoint, new SizeF(8, 8)));
}

这是使用float值的结果:

enter image description here

而这就是使用integer值的结果:

enter image description here


谢谢你的回答,我刚刚在这里查看答案之前意识到了这个愚蠢的小错误。 - Mars

0

如果你愿意,你可以使用Media3D仅处理矩阵并简化编码。像这样简单的代码就能运行。

public Point3D Rotate(Point3D point, Point3D rotationCenter, Vector3D rotation, double degree)
{
    // create empty matrix
    var matrix = new Matrix3D();

    // translate matrix to rotation point
    matrix.Translate(rotationCenter - new Point3D());

    // rotate it the way we need
    matrix.Rotate(new Quaternion(rotation, degree));

    // apply the matrix to our point
    point = matrix.Transform(point);

    return point;
}

然后,您只需调用该方法并指定旋转即可。假设您使用的是2D(就像您的示例中一样),并且假设我们使用的是XY平面,因此旋转在Z轴上。您可以这样做:

var rotationPoint = new Point3D(0, 0, 0);
var currentPoint = new Point3D(10, 0, 0);

// rotate the current point around the rotation point in Z by 45 degree
var newPoint = Rotate(currentPoint, rotation, new Vector3D(0, 0, 1), 45d);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接