1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7

# конспект · шаг 7 из 7

Конспект: повороты, пересечения и многоугольники

выжимка - её можно унести в заметки

Понятия

Векторное произведение на плоскости - u × v = ux · vy − uy · vx: ориентированная площадь параллелограмма на u и v. Плюс - от u к v поворот против часовой стрелки, минус - по часовой.

Поворот трёх точек - (B − A) × (C − A): плюс - C слева от A → B, минус - справа, ноль - на прямой. Модуль - удвоенная площадь треугольника.

Предикат - операция с дискретным ответом, от которой зависит ветвление: сторона, пересечение, «внутри». Конструкция - новый объект: точка пересечения, расстояние, площадь.

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

Формула Пика - для многоугольника с вершинами в целых точках S = I + B/2 − 1.

Что унести

ПОВОРОТ
povorot(a, b, c) = (b.x − a.x)(c.y − a.y) − (b.y − a.y)(c.x − a.x)
  > 0 налево      < 0 направо      = 0 на прямой
  |povorot| / 2       площадь треугольника
  |povorot| / |AB|    расстояние от C до прямой AB
  меняет знак при перестановке любых двух точек
  экран (ось y вниз): плюс выглядит как поворот по часовой
ПЕРЕСЕЧЕНИЕ ОТРЕЗКОВ AB и CD
d1 = povorot(C, D, A)   d2 = povorot(C, D, B)
d3 = povorot(A, B, C)   d4 = povorot(A, B, D)
крест:     d1, d2 разных знаков  И  d3, d4 разных знаков
касание:   какой-то d = 0  И  этот конец лежит на другом отрезке
           («лежит» = обе координаты между координатами концов)
наложение: все четыре = 0, и хоть один конец на другом отрезке
точка: A + t(B − A),  t = ((C − A) × (D − C)) / ((B − A) × (D − C))
       - дробь; решать по ней - только в целых (умножить на знаменатель)
МНОГОУГОЛЬНИК
2S = Σ Pᵢ × Pᵢ₊₁          плюс - обход против часовой
GeoJSON: внешний контур против часовой, Shapefile - по часовой
точка внутри: луч вправо, ребро засчитано, если
    (a.y > p.y) != (b.y > p.y)  и пересечение правее p
    нечётно - внутри; граница - отдельной проверкой
Пик: B по сторонам = Σ НОД(|dx|, |dy|)
буква Г: 2S = 36, I = 8, B = 22
ТОЧНОСТЬ
|x|, |y| <= M  →  |povorot| <= 8M²
float точен при M <= 2^25 ≈ 3,3 · 10^7,  int64 - при M < 2^30
сетка 16 × 16 у прямой y = x с шагом 2^-53: 114 неверных знаков из 256
eps не транзитивен: упрощение ломаной зависит от направления обхода
порядок: округлить к сетке один раз → дальше считать точно
CGAL: Exact_predicates_inexact_constructions_kernel; Шевчук (1996)

Ловушки джокера

Сравнил наклоны. Деление на ноль на вертикали и смена смысла при движении справа налево.

Проверил только крест. Т-образное касание, общий конец и наложение остались «без пересечения».

Проверил соседние рёбра общим тестом. Общая вершина законна - тест кричит на каждой паре соседей. А пропустить соседей нельзя: они могут сложиться по прямой назад.

Посчитал луч через вершину дважды. Нестрогие неравенства с обеих сторон.

Доверил решение float. Около прямой знак поворота - шум, и не точечный, а целыми блоками.

Заменил == 0 на abs < 1e-9. Порог не масштабируется и ломает транзитивность.

Округлил после вычислений. Вершина переехала через соседнее ребро - контур стал невалидным.

Решил, что целые - значит точные. Координаты OSM на всём земном шаре переполняют int64 в повороте: сначала вычесть опорную точку.

Проверь себя

Почему у векторного произведения именно такая формула и что значит её знак? Какие три края есть у пересечения отрезков и какой из них ловится только проверкой «конец на отрезке»? Откуда в формуле шнурка сумма по сторонам и почему от выбора начала координат ответ не зависит? Почему луч через вершину засчитывает одно ребро при правиле «вершина лежит ниже»? До какой величины координат поворот точен в float и как это посчитать? Чем предикат отличается от конструкции и почему точными должны быть именно предикаты?

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.