Предикат "левый поворот" — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
Строка 55: Строка 55:
  
 
  boolean Bounding_Box(точка A, точка B, точка C, точка D)
 
  boolean Bounding_Box(точка A, точка B, точка C, точка D)
  if (TODO:)
+
  if ((A.xx > C.xx && A.xx > D.xx && B.xx > C.xx && B.xx > D.xx || A.xx < C.xx && A.xx < D.xx && B.xx < C.xx && B.xx < D.xx)
  вернуть true
+
  || (A.yy > C.yy && A.yy > D.yy && B.yy > C.yy && B.yy > D.yy || A.yy < C.yy && A.yy < D.yy && B.yy < C.yy && B.yy < D.yy))
  вернуть false
+
  return false;
 +
  return true;

Версия 21:41, 26 октября 2011

Даны два отрезка AB и CD (они могут вырождаться в точки). Требуется проверить, пересекаются они на плоскости или нет. Для упрощения определения этого факта в вычислительной геометрии используется предикат "левый поворот" (или "по часовой стрелке"). Рассмотрим возможные расположения точек и самих отрезков относительно друг друга:

Cross.png Two segments.png Touch.jpg

Одно из решений - определить, лежат ли точки концов отрезков по разные стороны от другого отрезка.

Определение:
[math] $$ Left\_Turn(a, b, c) =\left\{ \begin{array}{rl} -1 &\mbox{, for}\ (b - a)\times(c - a) \lt 0\\ 0 &\mbox{, for}\ (b - a)\times(c - a) = 0\\ 1 &\mbox{, for}\ (b - a)\times(c - a) \gt 0 \end{array} \right. $$ [/math]

Распишем подробнее: [math](b - a)\times(c - a) = (b_x - a_x)\cdot(c_y - a_y) - (b_y - a_y)\cdot(c_x - a_x) = A - B[/math]

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

NB: при сложении складываются абс. погрешности,при умножении складываются отн. погрешности.

[math] \delta (b - a)\times(c - a) = A \cdot \epsilon \cdot (\frac{(b_x + a_x)}{(b_x \cdot a_x)} + \frac{(c_y + a_y)}{(c_y \cdot a_y)}) + B \cdot \epsilon \cdot (\frac{(b_y + a_y)}{(b_y \cdot a_y)} + \frac{(c_x + a_x)}{(c_x \cdot a_x)})[/math]

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

TODO: тут еще чего-то написать...

Посмотрим внимательнее на наш предикат. Ошибка раскрывается тогда, когда угол между отрезками АВ и АС крайне мал.

Tiny angle.jpg

Bounding box

Ещё следует обратить внимание на граничные случаи, когда какие-то точки попадают на саму прямую. При этом возникает единственный особый случай, когда вышеописанные проверки ничего не дадут — случай, когда оба отрезка лежат на одной прямой. Этот случай надо рассмотреть отдельно. Для этого достаточно проверить, что проекции этих двух отрезков на оси X и Y пересекаются (часто эту проверку называют "проверкой на bounding box").

Bounting box().png Bounting box .png

Псевдокод:

boolean Bounding_Box(точка A, точка B, точка C)
if ((A.xx >= C.xx && C.xx >= B.xx || A.xx <= C.xx && C.xx <= B.xx) && (A.yy >= C.yy && C.yy >= B.yy || A.yy <= C.yy && C.yy <= B.yy))
	вернуть true
вернуть false

или

boolean Bounding_Box(точка A, точка B, точка C, точка D)
if ((A.xx > C.xx && A.xx > D.xx && B.xx > C.xx && B.xx > D.xx || A.xx < C.xx && A.xx < D.xx && B.xx < C.xx && B.xx < D.xx) 
 || (A.yy > C.yy && A.yy > D.yy && B.yy > C.yy && B.yy > D.yy || A.yy < C.yy && A.yy < D.yy && B.yy < C.yy && B.yy < D.yy))
	return false;
return true;