Existe uma maneira fácil de determinar se um ponto está dentro de um triângulo? É 2D, não 3D.
Existe uma maneira fácil de determinar se um ponto está dentro de um triângulo? É 2D, não 3D.
Respostas:
Em geral, o algoritmo mais simples (e bastante ideal) é verificar em que lado do semiplano criado pelas arestas está o ponto.
Aqui estão algumas informações de alta qualidade neste tópico no GameDev , incluindo problemas de desempenho.
E aqui está um código para você começar:
float sign (fPoint p1, fPoint p2, fPoint p3)
{
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}
bool PointInTriangle (fPoint pt, fPoint v1, fPoint v2, fPoint v3)
{
float d1, d2, d3;
bool has_neg, has_pos;
d1 = sign(pt, v1, v2);
d2 = sign(pt, v2, v3);
d3 = sign(pt, v3, v1);
has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
return !(has_neg && has_pos);
}
Resolva o seguinte sistema de equações:
p = p0 + (p1 - p0) * s + (p2 - p0) * t
O ponto p
está dentro do triângulo se 0 <= s <= 1
e 0 <= t <= 1
e s + t <= 1
.
s
, t
e 1 - s - t
são chamadas de coordenadas barricêntricas do ponto p
.
s + t <= 1
implica s <= 1
e t <= 1
se s >= 0
e t >= 0
.
Concordo com Andreas Brinck , as coordenadas baricêntricas são muito convenientes para esta tarefa. Observe que não há necessidade de resolver um sistema de equações sempre: basta avaliar a solução analítica. Usando a notação de Andreas , a solução é:
s = 1/(2*Area)*(p0y*p2x - p0x*p2y + (p2y - p0y)*px + (p0x - p2x)*py);
t = 1/(2*Area)*(p0x*p1y - p0y*p1x + (p0y - p1y)*px + (p1x - p0x)*py);
onde Area
é a área (assinada) do triângulo:
Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);
Apenas avalie s
, t
e 1-s-t
. O ponto p
está dentro do triângulo se e somente se todos forem positivos.
EDIT: Observe que a expressão acima para a área pressupõe que a numeração dos nós do triângulo esteja no sentido anti-horário. Se a numeração for no sentido horário, essa expressão retornará uma área negativa (mas com a magnitude correta). O teste em si ( s>0 && t>0 && 1-s-t>0
) não depende da direção da numeração, no entanto, uma vez que as expressões acima que são multiplicadas por 1/(2*Area)
também mudam de sinal se a orientação do nó do triângulo for alterada.
EDIT 2: Para uma eficiência computacional ainda melhor, consulte o comentário do coproc abaixo (que salienta que, se a orientação dos nós do triângulo (sentido horário ou anti-horário) é conhecida previamente, a divisão 2*Area
nas expressões para s
e t
pode ser evitado). Veja também o código jsfiddle de Perro Azul nos comentários na resposta de Andreas Brinck .
2*Area
, ou seja, calculando s´=2*|Area|*s
e t´=2*|Area|*t
(se a orientação dos pontos - horário ou anti-horário - não for conhecida, é Area
claro que o sinal de deve ser verificado, mas, caso contrário, talvez nem precisa ser calculado), pois para verificar s>0
basta verificar s´>0
. E, em vez de verificar 1-s-t>0
, basta verificar s´+t´<2*|Area|
.
p0->p1->p2
for cartesiano no sentido anti-horário (que geralmente é no sentido horário nas coordenadas da tela ), o calculado por esse método será positivo. Area
Escrevi esse código antes de uma tentativa final com o Google e encontrei esta página, então pensei em compartilhá-lo. É basicamente uma versão otimizada da resposta Kisielewicz. Também examinei o método baricêntrico, mas, a julgar pelo artigo da Wikipedia, tenho dificuldade em ver como ele é mais eficiente (acho que há uma equivalência mais profunda). De qualquer forma, esse algoritmo tem a vantagem de não usar divisão; um problema em potencial é o comportamento da detecção de borda, dependendo da orientação.
bool intpoint_inside_trigon(intPoint s, intPoint a, intPoint b, intPoint c)
{
int as_x = s.x-a.x;
int as_y = s.y-a.y;
bool s_ab = (b.x-a.x)*as_y-(b.y-a.y)*as_x > 0;
if((c.x-a.x)*as_y-(c.y-a.y)*as_x > 0 == s_ab) return false;
if((c.x-b.x)*(s.y-b.y)-(c.y-b.y)*(s.x-b.x) > 0 != s_ab) return false;
return true;
}
Em palavras, a idéia é a seguinte: os pontos s à esquerda ou à direita das linhas AB e AC? Se for verdade, não pode estar lá dentro. Se falso, é pelo menos dentro dos "cones" que satisfazem a condição. Agora, como sabemos que um ponto dentro de um trigon (triângulo) deve estar do mesmo lado de AB que BC (e também CA), verificamos se eles diferem. Se o fizerem, s não pode estar dentro, senão s deve estar dentro.
Algumas palavras-chave nos cálculos são semiplanos de linha e o determinante (produto cruzado 2x2). Talvez uma maneira mais pedagógica seja provavelmente pensar nisso como um ponto dentro de si, se estiver do mesmo lado (esquerda ou direita) de cada uma das linhas AB, BC e CA. A maneira acima parecia melhor para algumas otimizações.
Versão em C # do método barcentric publicado por andreasdr e Perro Azul. Observe que o cálculo da área pode ser evitado se s
e t
tiver sinais opostos. Eu verifiquei o comportamento correto com um teste de unidade bastante completo.
public static bool PointInTriangle(Point p, Point p0, Point p1, Point p2)
{
var s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
var t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
if ((s < 0) != (t < 0))
return false;
var A = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
return A < 0 ?
(s <= 0 && s + t >= A) :
(s >= 0 && s + t <= A);
}
[ edit ]
aceitou a modificação sugerida pelo @Pierre; Ver comentários
Versão Java do método barricêntrico:
class Triangle {
Triangle(double x1, double y1, double x2, double y2, double x3,
double y3) {
this.x3 = x3;
this.y3 = y3;
y23 = y2 - y3;
x32 = x3 - x2;
y31 = y3 - y1;
x13 = x1 - x3;
det = y23 * x13 - x32 * y31;
minD = Math.min(det, 0);
maxD = Math.max(det, 0);
}
boolean contains(double x, double y) {
double dx = x - x3;
double dy = y - y3;
double a = y23 * dx + x32 * dy;
if (a < minD || a > maxD)
return false;
double b = y31 * dx + x13 * dy;
if (b < minD || b > maxD)
return false;
double c = det - a - b;
if (c < minD || c > maxD)
return false;
return true;
}
private final double x3, y3;
private final double y23, x32, y31, x13;
private final double det, minD, maxD;
}
O código acima funcionará corretamente com números inteiros, assumindo que não haja estouros. Também funcionará com triângulos no sentido horário e anti-horário. Não funcionará com triângulos colineares (mas você pode verificar isso testando det == 0).
A versão baricêntrica é mais rápida se você for testar pontos diferentes com o mesmo triângulo.
A versão baricêntrica não é simétrica nos 3 pontos do triângulo, portanto é provável que seja menos consistente que a versão de meio plano da aresta de Kornel Kisielewicz, devido a erros de arredondamento de ponto flutuante.
Crédito: Eu criei o código acima do artigo da Wikipedia sobre coordenadas baricêntricas.
Uma maneira simples é:
encontre os vetores que conectam o ponto a cada um dos três vértices do triângulo e some os ângulos entre esses vetores. Se a soma dos ângulos é 2 * pi, então o ponto está dentro do triângulo.
Dois bons sites que explicam alternativas são:
Usando a solução analítica para as coordenadas barentêntricas (apontadas por Andreas Brinck ) e:
Pode-se minimizar o número de operações "caras":
function ptInTriangle(p, p0, p1, p2) {
var dX = p.x-p2.x;
var dY = p.y-p2.y;
var dX21 = p2.x-p1.x;
var dY12 = p1.y-p2.y;
var D = dY12*(p0.x-p2.x) + dX21*(p0.y-p2.y);
var s = dY12*dX + dX21*dY;
var t = (p2.y-p0.y)*dX + (p0.x-p2.x)*dY;
if (D<0) return s<=0 && t<=0 && s+t>=D;
return s>=0 && t>=0 && s+t<=D;
}
O código pode ser colado no jsfiddle do Perro Azul ou tente-o clicando em "Executar trecho de código" abaixo
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = { x: W / 2, y: H / 2 };
var triangle = randomTriangle();
$("canvas").click(function(evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function(evt) {
triangle = randomTriangle();
test();
});
test();
function test() {
var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function ptInTriangle(p, p0, p1, p2) {
var A = 1/2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
var sign = A < 0 ? -1 : 1;
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;
return s > 0 && t > 0 && (s + t) < 2 * A * sign;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
return {
a: { x: rand(0, W), y: rand(0, H) },
b: { x: rand(0, W), y: rand(0, H) },
c: { x: rand(0, W), y: rand(0, H) }
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
Levando a:
Isso se compara muito bem à solução Kornel Kisielewicz (25 recalls, 1 armazenamento, 15 subtrações, 6 multiplicações, 5 comparações) e pode ser ainda melhor se for necessária a detecção no sentido horário / anti-horário (que leva 6 recalls, 1 adição e 2 subtrações , 2 multiplicações e 1 comparação em si, usando o determinante da solução analítica, conforme apontado por rhgb ).
O que eu faço é pré-calcular as três faces normais,
em 3D por produto cruzado do vetor lateral e o vetor normal da face.
em 2D simplesmente trocando componentes e negando um,
então dentro / fora para qualquer lado é quando um produto pontual do lado normal e o vértice ao vetor pontual mudam de sinal. Repita para os outros dois (ou mais) lados.
Benefícios:
muito é pré-calculado, excelente para testes de múltiplos pontos no mesmo triângulo.
rejeição precoce de casos comuns de mais pontos externos do que internos. (também se a distribuição de pontos ponderada para um lado, pode testar esse lado primeiro.)
Aqui está uma implementação eficiente do Python :
def PointInsideTriangle2(pt,tri):
'''checks if point pt(2) is inside triangle tri(3x2). @Developer'''
a = 1/(-tri[1,1]*tri[2,0]+tri[0,1]*(-tri[1,0]+tri[2,0])+ \
tri[0,0]*(tri[1,1]-tri[2,1])+tri[1,0]*tri[2,1])
s = a*(tri[2,0]*tri[0,1]-tri[0,0]*tri[2,1]+(tri[2,1]-tri[0,1])*pt[0]+ \
(tri[0,0]-tri[2,0])*pt[1])
if s<0: return False
else: t = a*(tri[0,0]*tri[1,1]-tri[1,0]*tri[0,1]+(tri[0,1]-tri[1,1])*pt[0]+ \
(tri[1,0]-tri[0,0])*pt[1])
return ((t>0) and (1-s-t>0))
e um exemplo de saída:
Se você está procurando velocidade, aqui está um procedimento que pode ajudá-lo.
Classifique os vértices do triângulo em suas ordenadas. Isso leva no máximo três comparações. Seja Y0, Y1, Y2 os três valores classificados. Ao desenhar três horizontais através deles, você divide o plano em dois semiplanos e duas lajes. Seja Y a ordenada do ponto de consulta.
if Y < Y1
if Y <= Y0 -> the point lies in the upper half plane, outside the triangle; you are done
else Y > Y0 -> the point lies in the upper slab
else
if Y >= Y2 -> the point lies in the lower half plane, outside the triangle; you are done
else Y < Y2 -> the point lies in the lower slab
Custa mais duas comparações. Como você vê, a rejeição rápida é alcançada para pontos fora da "laje delimitadora".
Opcionalmente, você pode fornecer um teste nas abscissas para rejeição rápida à esquerda e à direita ( X <= X0' or X >= X2'
). Isso implementará um teste rápido de caixa delimitadora ao mesmo tempo, mas você também precisará classificar as abscissas.
Eventualmente, você precisará calcular o sinal do ponto especificado em relação aos dois lados do triângulo que delimitam a laje relevante (superior ou inferior). O teste tem a forma:
((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))
A discussão completa das i, j, k
combinações (existem seis delas, com base no resultado do tipo) está fora do escopo desta resposta e "deixada como um exercício para o leitor"; para eficiência, eles devem ser codificados.
Se você acha que essa solução é complexa, observe que ela envolve principalmente comparações simples (algumas das quais podem ser pré-computadas), mais 6 subtrações e 4 multiplicações caso o teste da caixa delimitadora falhe. É difícil superar o último custo, pois, no pior dos casos, você não pode evitar comparar o ponto de teste com dois lados (nenhum método em outras respostas tem um custo menor, alguns pioram, como 15 subtrações e 6 multiplicações, às vezes divisões).
ATUALIZAÇÃO: Mais rápido com uma transformação de cisalhamento
Como explicado acima, você pode localizar rapidamente o ponto dentro de uma das quatro bandas horizontais delimitadas pelas três ordenadas de vértices, usando duas comparações.
Opcionalmente, você pode executar um ou dois testes X extras para verificar a falta de espaço na caixa delimitadora (linhas pontilhadas).
Em seguida, considere a transformação "cisalhamento" fornecida por X'= X - m Y, Y' = Y
, onde m
é a inclinação DX/DY
da aresta mais alta. Essa transformação tornará esse lado do triângulo vertical. E como você sabe de que lado da horizontal central você é, basta testar o sinal em relação a um único lado do triângulo.
Supondo que você precomputou a inclinação m
, bem como os X'
vértices dos triângulos cortados e os coeficientes das equações dos lados X = m Y + p
, você precisará, no pior dos casos,
X' = X - m Y
;X >< m' Y + p'
contra o lado relevante do triângulo cisalhado.Se você conhece as coordenadas dos três vértices e as coordenadas do ponto específico, pode obter a área do triângulo completo. Depois, calcule a área dos três segmentos do triângulo (um ponto é o ponto dado e os outros dois são quaisquer dois vértices do triângulo). Assim, você obterá a área dos três segmentos de triângulo. Se a soma dessas áreas for igual à área total (que você obteve anteriormente), o ponto deverá estar dentro do triângulo. Caso contrário, o ponto não está dentro do triângulo. Isso deve funcionar. Se houver algum problema, me avise. Obrigado.
Outra função em python , mais rápida que o método Developer (pelo menos para mim) e inspirada na solução Cédric Dufour :
def ptInTriang(p_test, p0, p1, p2):
dX = p_test[0] - p0[0]
dY = p_test[1] - p0[1]
dX20 = p2[0] - p0[0]
dY20 = p2[1] - p0[1]
dX10 = p1[0] - p0[0]
dY10 = p1[1] - p0[1]
s_p = (dY20*dX) - (dX20*dY)
t_p = (dX10*dY) - (dY10*dX)
D = (dX10*dY20) - (dY10*dX20)
if D > 0:
return ( (s_p >= 0) and (t_p >= 0) and (s_p + t_p) <= D )
else:
return ( (s_p <= 0) and (t_p <= 0) and (s_p + t_p) >= D )
Você pode testá-lo com:
X_size = 64
Y_size = 64
ax_x = np.arange(X_size).astype(np.float32)
ax_y = np.arange(Y_size).astype(np.float32)
coords=np.meshgrid(ax_x,ax_y)
points_unif = (coords[0].reshape(X_size*Y_size,),coords[1].reshape(X_size*Y_size,))
p_test = np.array([0 , 0])
p0 = np.array([22 , 8])
p1 = np.array([12 , 55])
p2 = np.array([7 , 19])
fig = plt.figure(dpi=300)
for i in range(0,X_size*Y_size):
p_test[0] = points_unif[0][i]
p_test[1] = points_unif[1][i]
if ptInTriang(p_test, p0, p1, p2):
plt.plot(p_test[0], p_test[1], '.g')
else:
plt.plot(p_test[0], p_test[1], '.r')
Demora muito para plotar, mas essa grade é testada em 0,0195319652557 segundos contra 0,0844349861145 segundos do código do desenvolvedor .
Finalmente o comentário do código:
# Using barycentric coordintes, any point inside can be described as:
# X = p0.x * r + p1.x * s + p2.x * t
# Y = p0.y * r + p1.y * s + p2.y * t
# with:
# r + s + t = 1 and 0 < r,s,t < 1
# then: r = 1 - s - t
# and then:
# X = p0.x * (1 - s - t) + p1.x * s + p2.x * t
# Y = p0.y * (1 - s - t) + p1.y * s + p2.y * t
#
# X = p0.x + (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y = p0.y + (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# X - p0.x = (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y - p0.y = (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# we have to solve:
#
# [ X - p0.x ] = [(p1.x-p0.x) (p2.x-p0.x)] * [ s ]
# [ Y - p0.Y ] [(p1.y-p0.y) (p2.y-p0.y)] [ t ]
#
# ---> b = A*x ; ---> x = A^-1 * b
#
# [ s ] = A^-1 * [ X - p0.x ]
# [ t ] [ Y - p0.Y ]
#
# A^-1 = 1/D * adj(A)
#
# The adjugate of A:
#
# adj(A) = [(p2.y-p0.y) -(p2.x-p0.x)]
# [-(p1.y-p0.y) (p1.x-p0.x)]
#
# The determinant of A:
#
# D = (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x)
#
# Then:
#
# s_p = { (p2.y-p0.y)*(X - p0.x) - (p2.x-p0.x)*(Y - p0.Y) }
# t_p = { (p1.x-p0.x)*(Y - p0.Y) - (p1.y-p0.y)*(X - p0.x) }
#
# s = s_p / D
# t = t_p / D
#
# Recovering r:
#
# r = 1 - (s_p + t_p)/D
#
# Since we only want to know if it is insidem not the barycentric coordinate:
#
# 0 < 1 - (s_p + t_p)/D < 1
# 0 < (s_p + t_p)/D < 1
# 0 < (s_p + t_p) < D
#
# The condition is:
# if D > 0:
# s_p > 0 and t_p > 0 and (s_p + t_p) < D
# else:
# s_p < 0 and t_p < 0 and (s_p + t_p) > D
#
# s_p = { dY20*dX - dX20*dY }
# t_p = { dX10*dY - dY10*dX }
# D = dX10*dY20 - dY10*dX20
ptInTriang([11,45],[45, 45],[45, 45] ,[44, 45])
e ele irá retornar true
embora seja falsa
Como não há resposta JS, a solução no
sentido horário e anti-horário:
function triangleContains(ax, ay, bx, by, cx, cy, x, y) {
let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
return det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&
det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&
det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0
}
EDIT: houve um erro de digitação para a computação det (em cy - ay
vez de cx - ax
), isso é corrigido.
https://jsfiddle.net/jniac/rctb3gfL/
function triangleContains(ax, ay, bx, by, cx, cy, x, y) {
let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
return det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&
det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&
det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0
}
let width = 500, height = 500
// clockwise
let triangle1 = {
A : { x: 10, y: -10 },
C : { x: 20, y: 100 },
B : { x: -90, y: 10 },
color: '#f00',
}
// counter clockwise
let triangle2 = {
A : { x: 20, y: -60 },
B : { x: 90, y: 20 },
C : { x: 20, y: 60 },
color: '#00f',
}
let scale = 2
let mouse = { x: 0, y: 0 }
// DRAW >
let wrapper = document.querySelector('div.wrapper')
wrapper.onmousemove = ({ layerX:x, layerY:y }) => {
x -= width / 2
y -= height / 2
x /= scale
y /= scale
mouse.x = x
mouse.y = y
drawInteractive()
}
function drawArrow(ctx, A, B) {
let v = normalize(sub(B, A), 3)
let I = center(A, B)
let p
p = add(I, rotate(v, 90), v)
ctx.moveTo(p.x, p.y)
ctx.lineTo(I.x, I .y)
p = add(I, rotate(v, -90), v)
ctx.lineTo(p.x, p.y)
}
function drawTriangle(ctx, { A, B, C, color }) {
ctx.beginPath()
ctx.moveTo(A.x, A.y)
ctx.lineTo(B.x, B.y)
ctx.lineTo(C.x, C.y)
ctx.closePath()
ctx.fillStyle = color + '6'
ctx.strokeStyle = color
ctx.fill()
drawArrow(ctx, A, B)
drawArrow(ctx, B, C)
drawArrow(ctx, C, A)
ctx.stroke()
}
function contains({ A, B, C }, P) {
return triangleContains(A.x, A.y, B.x, B.y, C.x, C.y, P.x, P.y)
}
function resetCanvas(canvas) {
canvas.width = width
canvas.height = height
let ctx = canvas.getContext('2d')
ctx.resetTransform()
ctx.clearRect(0, 0, width, height)
ctx.setTransform(scale, 0, 0, scale, width/2, height/2)
}
function drawDots() {
let canvas = document.querySelector('canvas#dots')
let ctx = canvas.getContext('2d')
resetCanvas(canvas)
let count = 1000
for (let i = 0; i < count; i++) {
let x = width * (Math.random() - .5)
let y = width * (Math.random() - .5)
ctx.beginPath()
ctx.ellipse(x, y, 1, 1, 0, 0, 2 * Math.PI)
if (contains(triangle1, { x, y })) {
ctx.fillStyle = '#f00'
} else if (contains(triangle2, { x, y })) {
ctx.fillStyle = '#00f'
} else {
ctx.fillStyle = '#0003'
}
ctx.fill()
}
}
function drawInteractive() {
let canvas = document.querySelector('canvas#interactive')
let ctx = canvas.getContext('2d')
resetCanvas(canvas)
ctx.beginPath()
ctx.moveTo(0, -height/2)
ctx.lineTo(0, height/2)
ctx.moveTo(-width/2, 0)
ctx.lineTo(width/2, 0)
ctx.strokeStyle = '#0003'
ctx.stroke()
drawTriangle(ctx, triangle1)
drawTriangle(ctx, triangle2)
ctx.beginPath()
ctx.ellipse(mouse.x, mouse.y, 4, 4, 0, 0, 2 * Math.PI)
if (contains(triangle1, mouse)) {
ctx.fillStyle = triangle1.color + 'a'
ctx.fill()
} else if (contains(triangle2, mouse)) {
ctx.fillStyle = triangle2.color + 'a'
ctx.fill()
} else {
ctx.strokeStyle = 'black'
ctx.stroke()
}
}
drawDots()
drawInteractive()
// trigo
function add(...points) {
let x = 0, y = 0
for (let point of points) {
x += point.x
y += point.y
}
return { x, y }
}
function center(...points) {
let x = 0, y = 0
for (let point of points) {
x += point.x
y += point.y
}
x /= points.length
y /= points.length
return { x, y }
}
function sub(A, B) {
let x = A.x - B.x
let y = A.y - B.y
return { x, y }
}
function normalize({ x, y }, length = 10) {
let r = length / Math.sqrt(x * x + y * y)
x *= r
y *= r
return { x, y }
}
function rotate({ x, y }, angle = 90) {
let length = Math.sqrt(x * x + y * y)
angle *= Math.PI / 180
angle += Math.atan2(y, x)
x = length * Math.cos(angle)
y = length * Math.sin(angle)
return { x, y }
}
* {
margin: 0;
}
html {
font-family: monospace;
}
body {
padding: 32px;
}
span.red {
color: #f00;
}
span.blue {
color: #00f;
}
canvas {
position: absolute;
border: solid 1px #ddd;
}
<p><span class="red">red triangle</span> is clockwise</p>
<p><span class="blue">blue triangle</span> is couter clockwise</p>
<br>
<div class="wrapper">
<canvas id="dots"></canvas>
<canvas id="interactive"></canvas>
</div>
Estou usando aqui o mesmo método descrito acima: um ponto está dentro do ABC, se estiver respectivamente no lado "igual" de cada linha AB, BC, CA.
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
), isso é para determinar a ordem do enrolamento do triângulo, para que o método funcione com triângulos CW e CCW (consulte jsFiddle).
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
em vez de let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
modo que este é fixo, graças para relatar
Eu só quero usar uma matemática vetorial simples para explicar a solução de coordenadas barentêntricas que Andreas havia dado, será bem mais fácil de entender.
(1-s) | v0v2 | / | v0v2 | = tp | v0v1 | / | v0v1 |
obtemos 1 - s = tp, então 1 = s + tp. Se qualquer t> tp, que 1 <s + t está na linha de traço duplo, o vetor está fora do triângulo, qualquer t <= tp, que 1> = s + t está na linha de traço único, o vetor é dentro do triângulo.
Então, se dermos algum s em [0, 1], o t correspondente deve atender 1> = s + t, para o vetor dentro do triângulo.
Então, finalmente, obtemos v = s * v02 + t * v01, v está dentro do triângulo com a condição s, t, s + t pertence a [0, 1]. Em seguida, traduza para o ponto, temos
p - p0 = s * (p1 - p0) + t * (p2 - p0), com s, t, s + t em [0, 1]
que é o mesmo que a solução de Andreas para resolver o sistema de equações p = p0 + s * (p1 - p0) + t * (p2 - p0), com s, t, s + t pertencem a [0, 1].
Aqui está uma solução em python que é eficiente, documentada e contém três unittests. É de qualidade profissional e está pronta para ser inserida em seu projeto na forma de um módulo como está.
import unittest
###############################################################################
def point_in_triangle(point, triangle):
"""Returns True if the point is inside the triangle
and returns False if it falls outside.
- The argument *point* is a tuple with two elements
containing the X,Y coordinates respectively.
- The argument *triangle* is a tuple with three elements each
element consisting of a tuple of X,Y coordinates.
It works like this:
Walk clockwise or counterclockwise around the triangle
and project the point onto the segment we are crossing
by using the dot product.
Finally, check that the vector created is on the same side
for each of the triangle's segments.
"""
# Unpack arguments
x, y = point
ax, ay = triangle[0]
bx, by = triangle[1]
cx, cy = triangle[2]
# Segment A to B
side_1 = (x - bx) * (ay - by) - (ax - bx) * (y - by)
# Segment B to C
side_2 = (x - cx) * (by - cy) - (bx - cx) * (y - cy)
# Segment C to A
side_3 = (x - ax) * (cy - ay) - (cx - ax) * (y - ay)
# All the signs must be positive or all negative
return (side_1 < 0.0) == (side_2 < 0.0) == (side_3 < 0.0)
###############################################################################
class TestPointInTriangle(unittest.TestCase):
triangle = ((22 , 8),
(12 , 55),
(7 , 19))
def test_inside(self):
point = (15, 20)
self.assertTrue(point_in_triangle(point, self.triangle))
def test_outside(self):
point = (1, 7)
self.assertFalse(point_in_triangle(point, self.triangle))
def test_border_case(self):
"""If the point is exactly on one of the triangle's edges,
we consider it is inside."""
point = (7, 19)
self.assertTrue(point_in_triangle(point, self.triangle))
###############################################################################
if __name__ == "__main__":
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestPointInTriangle)
unittest.TextTestRunner().run(suite)
Há um teste gráfico opcional adicional para o algoritmo acima para confirmar sua validade:
import random
from matplotlib import pyplot
from triangle_test import point_in_triangle
###############################################################################
# The area #
size_x = 64
size_y = 64
# The triangle #
triangle = ((22 , 8),
(12 , 55),
(7 , 19))
# Number of random points #
count_points = 10000
# Prepare the figure #
figure = pyplot.figure()
axes = figure.add_subplot(111, aspect='equal')
axes.set_title("Test the 'point_in_triangle' function")
axes.set_xlim(0, size_x)
axes.set_ylim(0, size_y)
# Plot the triangle #
from matplotlib.patches import Polygon
axes.add_patch(Polygon(triangle, linewidth=1, edgecolor='k', facecolor='none'))
# Plot the points #
for i in range(count_points):
x = random.uniform(0, size_x)
y = random.uniform(0, size_y)
if point_in_triangle((x,y), triangle): pyplot.plot(x, y, '.g')
else: pyplot.plot(x, y, '.b')
# Save it #
figure.savefig("point_in_triangle.pdf")
Produzindo o seguinte gráfico:
Existem condições de bordas traquinas em que um ponto está exatamente na borda comum de dois triângulos adjacentes. O ponto não pode estar em ambos ou em nenhum dos triângulos. Você precisa de uma maneira arbitrária, mas consistente, de atribuir o ponto. Por exemplo, desenhe uma linha horizontal através do ponto. Se a linha cruzar com o outro lado do triângulo à direita, o ponto será tratado como se estivesse dentro do triângulo. Se a interseção estiver à esquerda, o ponto estará do lado de fora.
Se a linha na qual o ponto se encontra for horizontal, use acima / abaixo.
Se o ponto estiver no vértice comum de vários triângulos, use o triângulo cujo centro o ponto forma o menor ângulo.
Mais divertido: três pontos podem estar em uma linha reta (zero graus), por exemplo (0,0) - (0,10) - (0,5). Em um algoritmo de triangulação, o "ouvido" (0,10) deve ser cortado, sendo o "triângulo" gerado o caso degenerado de uma linha reta.
Este é o conceito mais simples para determinar se um ponto está dentro ou fora do triângulo ou em um braço de um triângulo.
A determinação de um ponto está dentro de um triângulo por determinantes:
O código de trabalho mais simples:
#-*- coding: utf-8 -*-
import numpy as np
tri_points = [(1,1),(2,3),(3,1)]
def pisinTri(point,tri_points):
Dx , Dy = point
A,B,C = tri_points
Ax, Ay = A
Bx, By = B
Cx, Cy = C
M1 = np.array([ [Dx - Bx, Dy - By, 0],
[Ax - Bx, Ay - By, 0],
[1 , 1 , 1]
])
M2 = np.array([ [Dx - Ax, Dy - Ay, 0],
[Cx - Ax, Cy - Ay, 0],
[1 , 1 , 1]
])
M3 = np.array([ [Dx - Cx, Dy - Cy, 0],
[Bx - Cx, By - Cy, 0],
[1 , 1 , 1]
])
M1 = np.linalg.det(M1)
M2 = np.linalg.det(M2)
M3 = np.linalg.det(M3)
print(M1,M2,M3)
if(M1 == 0 or M2 == 0 or M3 ==0):
print("Point: ",point," lies on the arms of Triangle")
elif((M1 > 0 and M2 > 0 and M3 > 0)or(M1 < 0 and M2 < 0 and M3 < 0)):
#if products is non 0 check if all of their sign is same
print("Point: ",point," lies inside the Triangle")
else:
print("Point: ",point," lies outside the Triangle")
print("Vertices of Triangle: ",tri_points)
points = [(0,0),(1,1),(2,3),(3,1),(2,2),(4,4),(1,0),(0,4)]
for c in points:
pisinTri(c,tri_points)
A maneira mais fácil e funciona com todos os tipos de triângulos é simplesmente determinar os ângulos dos pontos P, pontos A, B e C. Se qualquer um dos ângulos são maiores do que 180,0 graus, então ele está fora, se 180,0 então é na circunferência e se acos batota em você e menos de 180,0 então é inside.Take um olhar para compreender http: // matemática-física -psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html
Honestamente, é tão simples quanto a resposta de Simon P Steven no entanto, com essa abordagem, você não tem um controle sólido sobre se deseja ou não incluir os pontos nas bordas do triângulo.
Minha abordagem é um pouco diferente, mas muito básica. Considere o seguinte triângulo;
Para ter o ponto no triângulo, temos que satisfazer 3 condições
Neste método, você tem controle total para incluir ou excluir o ponto nas arestas individualmente. Portanto, você pode verificar se um ponto está no triângulo, incluindo apenas o | AC | borda por exemplo.
Portanto, minha solução em JavaScript seria a seguinte;
function isInTriangle(t,p){
function isInBorder(a,b,c,p){
var m = (a.y - b.y) / (a.x - b.x); // calculate the slope
return Math.sign(p.y - m*p.x + m*a.x - a.y) === Math.sign(c.y - m*c.x + m*a.x - a.y);
}
function findAngle(a,b,c){ // calculate the C angle from 3 points.
var ca = Math.hypot(c.x-a.x, c.y-a.y), // ca edge length
cb = Math.hypot(c.x-b.x, c.y-b.y), // cb edge length
ab = Math.hypot(a.x-b.x, a.y-b.y); // ab edge length
return Math.acos((ca*ca + cb*cb - ab*ab) / (2*ca*cb)); // return the C angle
}
var pas = t.slice(1)
.map(tp => findAngle(p,tp,t[0])), // find the angle between (p,t[0]) with (t[1],t[0]) & (t[2],t[0])
ta = findAngle(t[1],t[2],t[0]);
return pas[0] < ta && pas[1] < ta && isInBorder(t[1],t[2],t[0],p);
}
var triangle = [{x:3, y:4},{x:10, y:8},{x:6, y:10}],
point1 = {x:3, y:9},
point2 = {x:7, y:9};
console.log(isInTriangle(triangle,point1));
console.log(isInTriangle(triangle,point2));
bool isInside( float x, float y, float x1, float y1, float x2, float y2, float x3, float y3 ) {
float l1 = (x-x1)*(y3-y1) - (x3-x1)*(y-y1),
l2 = (x-x2)*(y1-y2) - (x1-x2)*(y-y2),
l3 = (x-x3)*(y2-y3) - (x2-x3)*(y-y3);
return (l1>0 && l2>0 && l3>0) || (l1<0 && l2<0 && l3<0);
}
Não pode ser mais eficiente que isso! Cada lado de um triângulo pode ter posição e orientação independentes, portanto, três cálculos: l1, l2 e l3 são definitivamente necessários, envolvendo 2 multiplicações cada. Uma vez que l1, l2 e l3 são conhecidos, o resultado é apenas algumas comparações básicas e operações booleanas de distância.
Código supostamente de alto desempenho que adaptei em JavaScript (artigo abaixo):
function pointInTriangle (p, p0, p1, p2) {
return (((p1.y - p0.y) * (p.x - p0.x) - (p1.x - p0.x) * (p.y - p0.y)) | ((p2.y - p1.y) * (p.x - p1.x) - (p2.x - p1.x) * (p.y - p1.y)) | ((p0.y - p2.y) * (p.x - p2.x) - (p0.x - p2.x) * (p.y - p2.y))) >= 0;
}
pointInTriangle(p, p0, p1, p2)
- para triângulos no sentido anti-horáriopointInTriangle(p, p0, p1, p2)
- para triângulos no sentido horárioVeja no jsFiddle (teste de desempenho incluído), também há verificação de enrolamento em uma função separada. Ou pressione "Executar snippet de código" abaixo
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = { x: W / 2, y: H / 2 };
var triangle = randomTriangle();
$("canvas").click(function(evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function(evt) {
triangle = randomTriangle();
test();
});
document.querySelector('#performance').addEventListener('click', _testPerformance);
test();
function test() {
var result = checkClockwise(triangle.a, triangle.b, triangle.c) ? pointInTriangle(point, triangle.a, triangle.c, triangle.b) : pointInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function _testPerformance () {
var px = [], py = [], p0x = [], p0y = [], p1x = [], p1y = [], p2x = [], p2y = [], p = [], p0 = [], p1 = [], p2 = [];
for(var i = 0; i < 1000000; i++) {
p[i] = {x: Math.random() * 100, y: Math.random() * 100};
p0[i] = {x: Math.random() * 100, y: Math.random() * 100};
p1[i] = {x: Math.random() * 100, y: Math.random() * 100};
p2[i] = {x: Math.random() * 100, y: Math.random() * 100};
}
console.time('optimal: pointInTriangle');
for(var i = 0; i < 1000000; i++) {
pointInTriangle(p[i], p0[i], p1[i], p2[i]);
}
console.timeEnd('optimal: pointInTriangle');
console.time('original: ptInTriangle');
for(var i = 0; i < 1000000; i++) {
ptInTriangle(p[i], p0[i], p1[i], p2[i]);
}
console.timeEnd('original: ptInTriangle');
}
function pointInTriangle (p, p0, p1, p2) {
return (((p1.y - p0.y) * (p.x - p0.x) - (p1.x - p0.x) * (p.y - p0.y)) | ((p2.y - p1.y) * (p.x - p1.x) - (p2.x - p1.x) * (p.y - p1.y)) | ((p0.y - p2.y) * (p.x - p2.x) - (p0.x - p2.x) * (p.y - p2.y))) >= 0;
}
function ptInTriangle(p, p0, p1, p2) {
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0) return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function checkClockwise(p0, p1, p2) {
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return A > 0;
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
return {
a: { x: rand(0, W), y: rand(0, H) },
b: { x: rand(0, W), y: rand(0, H) },
c: { x: rand(0, W), y: rand(0, H) }
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="performance">Run performance test (open console)</button>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
Inspirado por isso: http://www.phatcode.net/articles.php?id=459
bool point2Dtriangle(double e,double f, double a,double b,double c, double g,double h,double i, double v, double w){
/* inputs: e=point.x, f=point.y
a=triangle.Ax, b=triangle.Bx, c=triangle.Cx
g=triangle.Ay, h=triangle.By, i=triangle.Cy */
v = 1 - (f * (b - c) + h * (c - e) + i * (e - b)) / (g * (b - c) + h * (c - a) + i * (a - b));
w = (f * (a - b) + g * (b - e) + h * (e - a)) / (g * (b - c) + h * (c - a) + i * (a - b));
if (*v > -0.0 && *v < 1.0000001 && *w > -0.0 && *w < *v) return true;//is inside
else return false;//is outside
return 0;
}
coordenadas cartesianas quase perfeitas convertidas de barricêntricas são exportadas entre * v (x) e * w (y) dobra. As duas duplas de exportação devem ter um * char antes em todos os casos, provavelmente: * ve * w O código também pode ser usado para o outro triângulo de um quadrilátero. Por este meio assinado escreveu apenas triângulo abc do quadrante abcd no sentido horário.
A---B
|..\\.o|
|....\\.|
D---C
o ponto o está dentro do triângulo ABC para testar com o segundo triângulo, chame esta função de direção CDA, e os resultados devem estar corretos após *v=1-*v;
e *w=1-*w;
para o quadrilátero
Eu precisava apontar a verificação do triângulo no "ambiente controlável" quando você tiver certeza absoluta de que os triângulos serão no sentido horário. Então, peguei o jsfiddle do Perro Azul e o modifiquei conforme sugerido pela coproc para esses casos; também removeu multiplicações redundantes de 0,5 e 2 porque elas apenas se cancelam.
http://jsfiddle.net/dog_funtom/H7D7g/
var ctx = $("canvas")[0].getContext("2d");
var W = 500;
var H = 500;
var point = {
x: W / 2,
y: H / 2
};
var triangle = randomTriangle();
$("canvas").click(function (evt) {
point.x = evt.pageX - $(this).offset().left;
point.y = evt.pageY - $(this).offset().top;
test();
});
$("canvas").dblclick(function (evt) {
triangle = randomTriangle();
test();
});
test();
function test() {
var result = ptInTriangle(point, triangle.a, triangle.b, triangle.c);
var info = "point = (" + point.x + "," + point.y + ")\n";
info += "triangle.a = (" + triangle.a.x + "," + triangle.a.y + ")\n";
info += "triangle.b = (" + triangle.b.x + "," + triangle.b.y + ")\n";
info += "triangle.c = (" + triangle.c.x + "," + triangle.c.y + ")\n";
info += "result = " + (result ? "true" : "false");
$("#result").text(info);
render();
}
function ptInTriangle(p, p0, p1, p2) {
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0) return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}
function checkClockwise(p0, p1, p2) {
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return A > 0;
}
function render() {
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
drawTriangle(triangle.a, triangle.b, triangle.c);
drawPoint(point);
}
function drawTriangle(p0, p1, p2) {
ctx.fillStyle = "#999";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#000";
ctx.font = "12px monospace";
ctx.fillText("1", p0.x, p0.y);
ctx.fillText("2", p1.x, p1.y);
ctx.fillText("3", p2.x, p2.y);
}
function drawPoint(p) {
ctx.fillStyle = "#F00";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomTriangle() {
while (true) {
var result = {
a: {
x: rand(0, W),
y: rand(0, H)
},
b: {
x: rand(0, W),
y: rand(0, H)
},
c: {
x: rand(0, W),
y: rand(0, H)
}
};
if (checkClockwise(result.a, result.b, result.c)) return result;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<pre>Click: place the point.
Double click: random triangle.</pre>
<pre id="result"></pre>
<canvas width="500" height="500"></canvas>
Aqui está o código C # equivalente para o Unity:
public static bool IsPointInClockwiseTriangle(Vector2 p, Vector2 p0, Vector2 p1, Vector2 p2)
{
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);
if (s <= 0 || t <= 0)
return false;
var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
return (s + t) < A;
}
Uma das maneiras mais fáceis de verificar se a área formada pelos vértices do triângulo (x1, y1), (x2, y2), (x3, y3) é positiva ou não.
A área pode ser calculada pela fórmula:
1/2 [x1 (y2 – y3) + x2 (y3 – y1) + x3 (y1 – y2)]
ou código python pode ser escrito como:
def triangleornot(p1,p2,p3):
return (1/ 2) [p1[0](p2[1]–p3[1]) + p2[0] (p3[1]–p1[1]) + p3[0] (p1[0]–p2[0])]