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.

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

Рейтинг@Mail.ru

вверх гостевая; E-mail
Hosted by uCoz