Pers.narod.ru. Алгоритмы. Попадание точки в треугольник |
Треугольник на плоскости задан координатами вершин A, B, C. Определить, попадает ли точка D в треугольник.
Первое, что приходит в голову - посчитать сумму площадей треугольников ABD, ACD и BCD, образованных соединением точки D с вершинами треугольника:

Точка находится в треугольнике тогда и только тогда, когда сумма площадей этих 3 треугольников равна площади исходного треугольника ABC. Сами площади, при известных координатах всех точек, легко высчитать по формуле Герона (функция S).
Вот реализация на Паскале, каждая точка на плоскости описана с помощью пользовательского типа данных Point.
type point= array [1..2] of real;
function dist (a,b:point):real;
begin
dist:=sqrt(sqr(a[1]-b[1])+sqr(a[2]-b[2]));
end;
function s (a,b,c:point):real;
var l1,l2,l3,p:real;
begin
l1:=dist(a,b);
l2:=dist(a,c);
l3:=dist(b,c);
p:=(l1+l2+l3)/2;
s:=sqrt(p*(p-l1)*(p-l2)*(p-l3));
end;
const a:point=(0,0);
b:point=(4,0);
c:point=(4,5);
var d:point;
p1,p2:real;
begin
d[1]:=1;
d[2]:=1.2;
p1:=s(a,b,d)+s(a,c,d)+s(b,c,d);
p2:=s(a,b,c);
writeln (p1:8:2,p2:8:2, (abs(p1-p2)<1e-8):6);
end.
Недостатки подхода очевидны - во-первых, нужно сравнивать вещественные числа, что, вообще говоря, некорректно (в программе числа сравниваются с произвольно выбранной точностью 10-8) во-вторых, нужна операция извлечения квадратного корня, что потенциально снижает точность расчётов (попробуйте, например, для треугольника с координатами вершин (0,0), (0,10000000), (1,10000000)). В третьих, кстати, неясно, как быть, когда точка лежит на одной из сторон треугольника.
Подход можно изменить - скажем, считать расстояния от точки D до точек A, B, C и периметр исходного треугольника. Если сумма трёх расстояний меньше периметра, то точка находится внутри треугольника. Однако, недостатки метода при таком подходе сохраняются.
Это не работает, скажем, для тупоугольных треугольников. Контрпример - треугольник с вершинами (-2,2), (3,1), (0,1) и точка (0,0)
Наконец, можно попробовать обойтись только разностями между проекциями точек на оси координат и операцией умножения. Думаю, этот вариант близок к оптимальному, вот изменённая версия программы:
type point= array [1..2] of real;
function PointInTriangle (a,b,c,p:point):boolean;
begin
PointInTriangle:=false;
if ((p[1]-a[1])*(a[2]-b[2])-(p[2]-a[2])*(a[1]-b[1])>=0) and
((p[1]-b[1])*(b[2]-c[2])-(p[2]-b[2])*(b[1]-c[1])>=0) and
((p[1]-c[1])*(c[2]-a[2])-(p[2]-c[2])*(c[1]-a[1])>=0) then PointInTriangle:=true;
end;
const a:point=(0,0);
b:point=(4,0);
c:point=(4,5);
var d:point;
p1,p2:real;
begin
d[1]:=1;
d[2]:=1.2;
writeln (PointInTriangle(a,b,c,d):6);
end.
Для корректной работы этого кода вершины должны быть перечислены в порядке против часовой стрелки, так что при необходимости нужно их сначала сортировать.
|
|