Поворот точек прямоугольника индивидуально искажает прямоугольник - PullRequest
7 голосов
/ 20 ноября 2011

enter image description here Я пытаюсь повернуть прямоугольник, вращая его точки, используя этот код

  var
 dx,dy:real;
 rotp:Tpoint;
begin
  dx := (CenterPoint.Y * Sin(angle)) - (CenterPoint.X * Cos(angle)) + CenterPoint.X;
  dy := -(CenterPoint.X * Sin(angle)) - (CenterPoint.Y * Cos(angle)) + CenterPoint.Y;
  rotP.X := round((point.X * Cos(angle)) - (point.Y * Sin(angle)) + dx);
  rotP.Y := round((point.X * Sin(angle)) + (point.Y * Cos(angle)) + dy);
  result:= rotP;
end;

но функция округления делает прямоугольник искаженным, кто-нибудь знает, как это преодолеть?

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

Ответы [ 3 ]

7 голосов
/ 20 ноября 2011

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

Обновление: Ваш комментарий выдает игру.Вы постоянно вращаетесь и накапливаете ошибки каждый раз, когда оцифровываете, преобразовывая в целое число.Сделайте это, сохранив ваши координаты как значения двойной точности и просто преобразуйте их в целое число по требованию, когда вам нужно рисовать.

На самом деле, если бы вы были на вашем месте, я бы обработал ваши основные данные как положение и угол.Оба хранятся с двойной точностью.Я бы не стал хранить координаты углов вообще.Я хотел бы сохранить положение (центр или один из углов) и угол ориентации (относительно фиксированной глобальной системы координат).Таким образом, вы всегда будете рисовать настоящий прямоугольник.На каждом шаге интеграции при необходимости увеличивайте положение и ориентацию, а затем вычисляйте положение углов на основе основных данных.Сделайте это так, и вы никогда не будете страдать от искажения своей фигуры.

4 голосов
/ 20 ноября 2011

Вычисления с плавающей точкой, особенно с тригонометрическими функциями, всегда подвержены ошибкам из-за ограниченного разрешения переменных с плавающей точкой.Вы можете повысить точность вычислений, если умножить разности координат с помощью тригонометрической функции вместо умножения координат и вычитания результатов.Вы можете попробовать этот код (при условии, что угол в радианах и используется math.pas):

var
  dx,dy,ca,sa:Extended;
  rotp:Tpoint;
begin
  SinCos(angle, sa, ca);
  dx := point.x - CenterPoint.X;
  dy := point.y - CenterPoint.Y;
  result.X := CenterPoint.X + round(dx*ca - dy*sa);
  result.Y := CenterPoint.Y + round(dx*sa + dy*ca);
end;

Обновление: И согласно отредактированному ответу Дэвида, вы не должны использовать инкрементные вращениятак как это увеличит ошибку округления.

1 голос
/ 20 ноября 2011
type
  TRectangle = record
    A, B, C, D: TPoint;
  end;

var
  Rectangle, // master rect
  TurnedRectangle: TRectangle; // turned rect

...

procedure RotateRectangle;
begin
  TurnedRectangle.A := RotatePoint(Rectangle.A);
  ...
  DrawRectangle
end

function RotatePoint(Point: TPoint): TPoint;
var
  dx, dy: Real;
  rotp: TPoint;
begin
  dx := (CenterPoint.Y * Sin(angle)) - (CenterPoint.X * Cos(angle)) + CenterPoint.X;
  dy := -(CenterPoint.X * Sin(angle)) - (CenterPoint.Y * Cos(angle)) + CenterPoint.Y;
  rotP.X := Round((Point.X * Cos(angle)) - (point.Y * Sin(angle)) + dx);
  rotP.Y := Round((Point.X * Sin(angle)) + (point.Y * Cos(angle)) + dy);
  result:= rotP;
end;

procedure DrawRectangle;
begin
  Canvas.Polygon([TurnedRectangle.A, TurnedRectangle.B, TurnedRectangle.C, TurnedRectangle.D]);
end;
...