Нет ничего более красивого, чем индуктивное определение проблемы. Для полноты здесь у вас есть версия в прологе, которая также может прояснить мысли о приведении лучей :
На основе алгоритма симуляции простоты в http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
Некоторые предикаты помощника:
exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).
inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) + X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).
get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).
Уравнение прямой, заданной 2 точками A и B (линия (A, B)):
(YB-YA)
Y - YA = ------- * (X - XA)
(XB-YB)
Важно, чтобы направление вращения линии
установлен по часовой стрелке для границ и против часовой стрелки для дырок.
Мы собираемся проверить, находится ли точка (X, Y), то есть проверенная точка слева
полуплоскость нашей линии (это вопрос вкуса, это также может быть
правая сторона, но и направление линий границ должно быть изменено в
в этом случае), это для проецирования луча из точки вправо (или влево)
и подтвердите пересечение с линией. Мы выбрали проект
луч в горизонтальном направлении (опять же, дело вкуса,
это также может быть сделано по вертикали с аналогичными ограничениями), поэтому мы имеем:
(XB-XA)
X < ------- * (Y - YA) + XA
(YB-YA)
Теперь нам нужно знать, находится ли точка слева (или справа) от
только отрезок, а не вся плоскость, поэтому нам нужно
ограничить поиск только этим сегментом, но это легко, так как
быть внутри сегмента, только одна точка в линии может быть выше
чем Y по вертикальной оси. Поскольку это более сильное ограничение,
должен быть первым, чтобы проверить, поэтому мы берем сначала только те строки
выполнение этого требования, а затем проверить его возможность. У Иордана
Теорема кривой любой луч, проецируемый на многоугольник, должен пересекаться на
четное количество строк. Итак, мы сделали, мы бросим луч к
право, а затем каждый раз, когда он пересекает линию, переключать свое состояние.
Однако в нашей реализации мы собираемся проверить длину
пакет решений, отвечающих заданным ограничениям, и решите
участие в этом. для каждой линии в многоугольнике это должно быть сделано.
is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] = [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA));
is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).
in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon), in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line), in_y_range_at_poly(Coordinate,Line,Polygon), Lines).
traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).
% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).