Поверните точку вокруг точки поворота несколько раз - PullRequest
0 голосов
/ 22 мая 2018

Некоторое время назад я использовал следующую функцию для вращения ряда точек вокруг точки поворота в различных моих программах.

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

Это всегда прекрасно работало, пока я не попыталсявращать форму несколько раз на небольшие суммы.Например, это то, что я получаю, называя его на 45 ° для прямоугольного многоугольника, состоящего из 4 точек:

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

Пока я называю это только один раз, когда все в порядке, и этотакже кажется, что постепенно становится хуже, чем ниже степень вращения.Есть ли какой-то недостаток в функции, или я неправильно понимаю, что делает эта функция?

Как я мог несколько раз вращаться в небольших количествах?Я мог бы сохранить базовые точки и использовать их для обновления текущих точек при каждом изменении поворота, но разве это единственный способ?

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Как правильно указал TaW, ваша Point мера позиции отключена из-за целочисленного округления, сгенерированного методом RotatePoint().

Простая коррекция возвращаемого значения метода с использованием float координаты, даст правильную меру:

Чтобы проверить его, создайте таймер и зарегистрируйте его событие 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)));
}

Это результат с использованием float значений:

enter image description here

И вот что происходит с использованием integer значений:

enter image description here

0 голосов
/ 22 мая 2018

Если вы хотите, вы можете использовать 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);
...