Московский государственный индустриальный университет
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3331 / 379 | Оценка: 4.17 / 3.79 | Длительность: 24:17:00
Специальности: Программист
Лекция 11:

Проект "Выпуклая оболочка"

Аналитическая геометрия и программирование

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

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

S = \sqrt{p (p-a) (p-b) (p-c)}
требует большого числа умножений и четырех относительно медленно выполняемых операций извлечения квадратного корня. Основанная на связи площади с векторным произведением формула
S = \frac{1}{2} |\vec a \times \vec b|
использует всего три умножения, одно из которых на самом деле сводится к сдвигу, что позволяет найти площадь треугольника значительно быстрее.

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

Расстояние d между двумя точками на плоскости можно посчитать по известной формуле d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}, которая и используется в реализации метода dist. Два объекта типа R2Point соответствуют совпадающим точкам плоскости тогда и только тогда, когда попарно равны их координаты, что позволяет реализовать метод equal.

public static double dist(R2Point a, R2Point b) {
        return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    public static boolean equal(R2Point a, R2Point b) {
        return a.x==b.x && a.y==b.y;
    }
    public static double area(R2Point a, R2Point b, R2Point c) {
        return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
    }

Для вычисления площади треугольника в методе area применена уже цитированная выше формула, основанная на следующем факте из векторной алгебры: модуль векторного произведения двух векторов равен площади параллелограмма, натянутого на эти вектора. Напомним, что векторным произведением \vec a \times \vec b двух векторов \vec a и \vec b называется вектор \vec c, перпендикулярный векторам \vec a и \vec b, такой, что |\vec
c|=|\vec a| |\vec b| \sin
\alpha, где \alpha — угол между векторами \vec
a и \vec b. Одно из двух возможных направлений вектора \vec c при этом определяется по правилу буравчика (при повороте ручки по направлению от \vec a к \vec
b буравчик перемещается по направлению вектора \vec c ).

Часто полезно использовать запись этого определения в координатах. Если \vec a = (a_x, a_y, a_z), а \vec b = (b_x, b_y,
b_z), то координаты вектора \vec c находятся по формуле

\vec c = \left| 
\begin{array}{ccc}
\vec i& \vec j& \vec k\\
a_x   & a_y   & a_z   \\
b_x   & b_y   & b_z
\end{array}
\right| = (a_y b_z - a_z b_y) \vec i + (a_z b_x - a_x b_z) \vec j +
(a_x b_y - a_y b_x) \vec k.

В том случае, когда вектора \vec a и \vec b расположены на плоскости, их векторное произведение имеет единственную не нулевую компоненту, вычисление которой и реализовано в методе area. Обратите внимание на то, что так вычисленная площадь является ориентированной, то есть может быть отрицательной.

Выяснение того факта, лежат ли три точки на одной прямой, сводится к вычислению площади треугольника и сравнению ее с нулем, а для того, чтобы точка t прямой располагалась на отрезке [a,b] этой же прямой, необходимо и достаточно принадлежности проекций этой точки проекциям на оси координат отрезка [a,b]. Вот как выглядит программная реализация этих идей.

public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
        return area(a, b, c) != 0.0;
    }
    public boolean inside(R2Point a, R2Point b) {
        return (a.x <= x && x <= b.x || a.x >= x && x >= b.x) &&
            (a.y <= y && y <= b.y || a.y >= y && y >= b.y);
    }
Освещенность ребра из точки

Рис. 11.5. Освещенность ребра из точки

Перед тем, как реализовать метод light, позволяющий выяснить, освещено ли ребро [a,b] выпуклой оболочки из точки t, дадим строгое определение освещенности.

Определение 11.3. Ребро [a,b] называется освещенным из точки t, если ориентированная площадь треугольника, образованного точками a, b и t отрицательна, либо если точка t расположена на прямой, проходящей через точки a и b, вне отрезка [a,b].

Заметим, что при таком формальном определении понятия освещенности никакое ребро многоугольника не освещено ни из одной точки внутри или на границе многоугольника, что хорошо видно на рисунке рис. 11.5.

public boolean light(R2Point a, R2Point b) {
        double s = area(a, b, this);
        return s < 0.0 || ( s == 0.0 && ! inside(a, b));
    }

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

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

Пусть координаты граничных точек первого отрезка \overrightarrow{P_1
P_2} равны (x_1, y_1) и (x_2, y_2), а второго \overrightarrow{P_3 P_4}(x_3, y_3) и (x_4, y_4). Тогда координаты левого нижнего угла ограничивающего прямоугольника первого отрезка будут равны (\widehat x_1, \widehat y_1), а правого верхнего угла — (\widehat x_2, \widehat y_2), где \widehat x_1 = \min(x_1,
x_2), \widehat y_1 = \min(y_1, y_2), \widehat x_2 = \max(x_1,
x_2) и \widehat y_2 = \max(y_1, y_2). Аналогично определяются координаты (\widehat x_3, \widehat y_3) и (\widehat x_4, \widehat
y_4) левого нижнего и правого верхнего углов ограничивающего прямоугольника второго отрезка.

Ограничивающие прямоугольники пересекаются тогда и только тогда, когда пересекаются их проекции на каждую из двух осей, что сводится к истинности следующего предиката \widehat x_2 \geqslant \widehat x_3
\land \widehat x_4 \geqslant \widehat x_1 \land
\widehat y_2 \geqslant \widehat y_3 \land 
\widehat y_4 \geqslant \widehat y_1.

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

Можно доказать (это рекомендуется сделать самостоятельно), что два отрезка пересекаются тогда и только тогда, когда пересекаются их ограничивающие прямоугольники и, кроме того, каждый из них пересекается с прямой, содержащей другой отрезок.

Проверка факта пересечения отрезка с прямой осуществляется с помощью векторных произведений следующим образом. Точки P_3 и P_4 лежат по разные стороны от прямой P_1 P_2, если векторы \overrightarrow{P_1
P_3} и \overrightarrow{P_1 P_4} имеют различную ориентацию относительно вектора \overrightarrow{P_1 P_2}, то есть, если знаки векторных произведений \overrightarrow{P_1 P_3} \times \overrightarrow{P_1 P_2} и \overrightarrow{P_1 P_4} \times \overrightarrow{P_1 P_2} различны.

Таким образом, отрезки \overrightarrow{P_1 P_2} и \overrightarrow{P_3 P_4} пересекаются тогда и только тогда, когда одновременно выполняются следующие три условия:

1) пересекаются ограничивающие их прямоугольники;

2) (\overrightarrow{P_1 P_3} \times \overrightarrow{P_1 P_2}) \cdot
    (\overrightarrow{P_1 P_4} \times
    \overrightarrow{P_1 P_2}) \leqslant 0;

3) (\overrightarrow{P_3 P_1} \times \overrightarrow{P_3 P_4}) \cdot
    (\overrightarrow{P_3 P_2} \times
    \overrightarrow{P_3 P_4}) \leqslant 0.

Более подробное изложение решения этой и ряда других подобных задач может быть найдено в разделе "Вычислительная геометрия" книги [8].

Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева