简体   繁体   English

重复旋转点绕枢轴点

[英]Rotate Point around pivot Point repeatedly

For a while now I've been using the following function to rotate a series of Points around a pivot point in various programs of mine. 一段时间以来,我一直在使用以下功能在我的各种程序中围绕枢轴点旋转一系列点。

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);
}

This has always worked great, until I tried to rotate a shape repeatedly by small amounts. 这一直很有效,直到我尝试少量重复旋转形状。 For example, this is what I get from calling it for 45° on a rectangular polygon made up of 4 points: 例如,这是我在由4个点组成的矩形多边形上调用45°时得到的结果:

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

在此输入图像描述

But this is what I get by calling rotate 45 times for 1°: 但这是我通过调用旋转45次1°得到的:

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

在此输入图像描述

As long as I call it only once it's fine, and it also seems like it gets gradually worse the lower the rotation degree is. 只要我把它称之为一次就好了,它似乎也会逐渐变差,旋转度越低。 Is there some flaw in the function, or am I misunderstanding something fundamental about what this function does? 函数中是否存在一些缺陷,或者我是否误解了这个函数的作用?

How could I rotate repeatedly by small amounts? 我怎么能少量重复旋转? I could save the base points and use them to update the current points whenever the rotation changes, but is that the only way? 我可以保存基点并使用它们在旋转变化时更新当前点,但这是唯一的方法吗?

As TaW correctly pointed out, your Point position measure is off because of the integer rounding generated by the RotatePoint() method. 正如TaW正确指出的那样,由于RotatePoint()方法生成的整数舍入,您的Point位置度量已关闭。

A simple correction in the method returned value, using float coordinates, will produce the correct measure: 使用float坐标对方法返回值进行简单校正将产生正确的度量:

To test it, create a Timer and register its Tick event as RotateTimerTick() : 要测试它,创建一个Timer并将其Tick事件注册为RotateTimerTick()

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)));
}

This is the result using float values: 这是使用float值的结果:

在此输入图像描述

And this is what happens using integer values: 这是使用integer数值发生的事情:

在此输入图像描述

If you want you can use the Media3D to only deal with matrix and simplify the coding. 如果您愿意,可以使用Media3D仅处理矩阵并简化编码。 Something as simple as this will work. 像这样简单的东西会起作用。

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;
}

Then you simply call the method and specify the rotation. 然后,您只需调用方法并指定旋转。 Lets say you work with 2D (like in your example) and lets assume we work with XY plane so the rotation is in Z. You can do something like : 让我们说你使用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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM