2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу 1, 2  След.
 
 Вращение точки вокруг другой точки [Delphi]
Сообщение11.02.2013, 00:20 
Аватара пользователя
Всем доброго времени суток.

Возникла у меня задача реализовать функцию, которая бы реализовывала расчёт координат точки после её поворота на заданный угол вокруг другой точки. Немного поискав в сети решения данной задачи я пришёл к такому решению:
Используется синтаксис Delphi
function RotatePoint(point, axis: TPoint; alpha: real): TPoint;
var rotated: TPoint;
begin
  rotated.x := Round( cos(alpha)*(point.x - axis.x) + sin(alpha)*(point.y - axis.y) ) + axis.x;
  rotated.y := Round( cos(alpha)*(point.y - axis.y) - sin(alpha)*(point.x - axis.x) ) + axis.y;
  Result := rotated;
end;
 


Здесь point - сама точка, axis - ось вращения, alpha - поворота, положителен для поворота против часовой.
TPoint - стандартный тип "запись с двумя полями: x и y типа integer".
Также прошу обратить внимание, что система координат используется левая.

Собственно сами формулы представляют собой формулы из статьи "Матрица поворота" на Википедии, с поправкой на то, что сначала координаты точки преобразуются из исходной системы координат в систему координат, началом которой является ось поворота, а потом происходит обратное преобразование.

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

Уважаемые форумчане, помогите, пожалуйста, найти проблему.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 01:50 
А что за светлые точки на рисунке?

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 02:25 
Аватара пользователя
Ну я же говорю, что это след вращения отрезка.
Видимо, в этих местах координата вращающейся точки изменялась быстрее, нежели в других местах, из-за чего многочисленные изображения отрезка накладывались неплотно.

-- 11.02.2013, 03:38 --

Решил сейчас поиграться со значением угловой скорости, чтобы получить более "гладкое" изображение и обнаружил, что фигура, по которой движется точка с помощью моей функции меняется в зависимости от угловой скорости.
При относительно больших значениях получается визуально почти окружность, при уменьшении значения получается многоугольник, количество сторон которого уменьшается с уменьшением значения угловой скорости.
Более того, при определённых значениях наблюдается явное "спиральное" движение, т.е. точка описывает некую фигуру и с каждым оборотом всё более удаляется от центра.

Очень надеюсь, что здесь найдётся кто-нибудь, кто сможет объяснить причину такой странной работы функции.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 02:48 
Округлять не надо после каждого маленького поворота. Из-за округления точка сначала идёт вдоль одной оси, потом по диагонали, а потом вдоль другой оси. Вполне может получиться и спираль, если после полного оборота радиус изменится.
Короче, либо считайте всё с плавающей точкой (при этом тоже может получиться спираль, правда с очень маленким шагом), либо поворачивайте исходную точку на постепенно увеличивающийся угол.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 05:40 
Можно вращать точку в полярных координатах, тогда радиус будет равен начальному.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 09:49 
Аватара пользователя
Проблема в округлении. Если выполнять процесс интерациононо ошибка может накапливается.

Код:
procedure FixAngle;
var point, axis: TPoint;
i:integer;
begin
axis.X:=250;
axis.y:=250;
point.x:=250-50;
point.y:=250;
for i:=0 to 360 do
  begin
  point:=RotatePoint(point,axis,Pi/180);
  Form1.Image1.Canvas.MoveTo(axis.X,axis.Y);
  Form1.Image1.Canvas.LineTo(point.X,point.Y);
  end;
end;


У Вас и есть тот лучай, когда она накапливается.

Из решений менять алгоритм. Делать его неустойчевым к ошибкам.

Самое просто что приходит на ум использоваь числа с плавающей точкой.
Но это полумера, числа в компьюторе иеют ограниченное число знаков. И при операциях с плавающей точкой они тоже округляются.
И ошибка также и иначе будет вылазить. Просто они бдут появляться реже! Так все создатели игр и делают.
Цитата:
Короче, либо считайте всё с плавающей точкой (при этом тоже может получиться спираль, правда с очень маленким шагом)

Даже небольшое округление может приводить к большим ошибкам. К примеру выполните деление $1/x$.

Поэтому надо искать алгоритм на которого ошибки окргуления не будут влиять. К примеру тут можно изменять угол.
Код:
procedure VarAngle;
var r,point, axis: TPoint;
angle:Real;
i:integer;
begin
axis.X:=250;
axis.y:=250;
point.x:=250-50;
point.y:=250;
angle:=0;
for i:=0 to 360 do
  begin
  r:=RotatePoint(point,axis,angle);
  angle:=angle+Pi/180;
  Form1.Image2.Canvas.MoveTo(axis.X,axis.Y);
  Form1.Image2.Canvas.LineTo(r.X,r.Y);
  end;
end;

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 10:28 
Аватара пользователя
Всем спасибо, судя по всему, проблема действительно была в округлении.
Переделал алгоритм для расчёта новых координат каждый раз поворотом исходных на возрастающий угол - работает прекрасно.
Забавно, никогда бы не подумал, что погрешности компьютерной математики могут иметь столь заметные последствия.

Позволю себе задать вопрос, несколько отличающийся от исходной проблемы, но тесно связанный с нею.
При изменении алгоритма у меня возникла проблема переполнения переменной, хранящей угол.
Т.к. мне необходимо "вечное" вращение, то очевидно надо как-то предотвращать переполнение.
Насколько корректным является код, подобный этому:
Используется синтаксис Delphi
function NextAngle(alpha, d: real): real;
begin
  if (alpha + d - 2*pi) < eps then Result := 0
  else Reslut := alpha + d;
end;
 

Где eps - константа порядка $ 10^5 $?

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 12:44 
Аватара пользователя
Цитата:
Насколько корректным является код, подобный этому:

Ни насколько.
1) alpha, d могут быть меньше нуля, могут быть больше 10*Pi, а также их сумма может быть меньше 0 и больше 10*Pi.
2) Фаза, при превешении 2*Pi у вас не сохраняется.
3) Сравниваете неправильно сделано.

Так по лучше будет, хотя и тут есть недостатки.
Код:
function NextAngle(alpha, d: real): real;
const
_1_2PI=1/(2*pi);

var
dd:Int64;
begin
Result:=alpha + d;
dd:=Round(Result*_1_2PI);
result:=result-dd*2*Pi;
end;

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 15:45 
Pavia в сообщении #682449 писал(а):
Цитата:

Так по лучше будет, хотя и тут есть недостатки.


Мне кажется округление здесь не подходит.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 20:11 
Аватара пользователя
Pavia, эмм, Вы не объясните, как это работает?... :oops:

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 20:37 
Округлять до целого, а потом переводить из целого в плавающее — зачем это, когда есть в Math функция Floor? (Хотя, вроде бы, Round тоже не возвращает целое, а делает это Trunc (по крайней мере, в D7 и D2005 так было).)

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

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение11.02.2013, 23:54 
arseniiv в сообщении #682600 писал(а):

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


А если измение угла очень большое (больше $2\pi$)?

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение12.02.2013, 13:15 
В суть темы не вникал, но последние посты напомнили о том, с чем я сталкивался когда писал драйвер шагового двигателя. Задача - повернуть вал на некоторый угол (в общем случае составляющий много-много оборотов) с точностью до долей микрошага (тысячных одного оборота). Если текущий угол хранить в одной переменной с плавающей точкой, то при достижении определенного значения она уже не будет меняться при прибавлении к ней малого приращения, вследствие ухудшения точности данного формата при увеличении абсолютного значения. Пара возможных решений - хранить в фиксированной точке достаточной длины или помнить отдельно целые обороты и дробную часть оборотов в микрошагах.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение12.02.2013, 14:21 
ilkos в сообщении #682693 писал(а):
А если измение угла очень большое (больше $2\pi$)?
В общем случае и низ на меньшесть нуля проверять нужно, но мне кажется, что я правильно протелепатировал, что shau-kote прибавляет только меньшие полного оборота положительные углы.

 
 
 
 Re: Вращение точки вокруг другой точки
Сообщение12.02.2013, 16:18 
Аватара пользователя
_Ivana, нет, Вы не совсем в тему. (:

Итого я пришёл к следующему решению, которое кажется мне наиболее простым, надёжным и универсальным:
Используется синтаксис Delphi
function NextAngle(alpha, d: real): real;
begin
  alpha := alpha + d;
  while alpha > 2*pi do alpha := alpha - 2*pi;
  while alpha < 0 do alpha := alpha + 2*pi;
end;
 

Таким образом, как и советовал тов. arseniiv, я просто изменяю значение угла и, если необходимо, нормализую его, сохраняя его значение в $[0, 2\pi]$.

Если кто-то видит в данном варианте недочёты, рад буду узнать и постараться исправить их.

 
 
 [ Сообщений: 18 ]  На страницу 1, 2  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group