C'è un modo semplice per determinare se un punto è all'interno di un triangolo? È 2D, non 3D.
C'è un modo semplice per determinare se un punto è all'interno di un triangolo? È 2D, non 3D.
Risposte:
In generale, l'algoritmo più semplice (e abbastanza ottimale) sta controllando su quale lato del semipiano creato dai bordi si trova il punto.
Ecco alcune informazioni di alta qualità in questo argomento su GameDev , inclusi i problemi di prestazioni.
Ed ecco un po 'di codice per iniziare:
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);
}
Risolvi il seguente sistema di equazioni:
p = p0 + (p1 - p0) * s + (p2 - p0) * t
Il punto p
è all'interno del triangolo se 0 <= s <= 1
e 0 <= t <= 1
e s + t <= 1
.
s
, t
e 1 - s - t
sono chiamate coordinate baricentriche del punto p
.
s + t <= 1
implica s <= 1
e t <= 1
se s >= 0
e t >= 0
.
Sono d'accordo con Andreas Brinck , le coordinate baricentriche sono molto convenienti per questo compito. Si noti che non è necessario risolvere ogni volta un sistema di equazioni: basta valutare la soluzione analitica. Usando la notazione di Andreas , la soluzione è:
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);
dov'è Area
l'area (con segno) del triangolo:
Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);
Basta valutare s
, t
e 1-s-t
. Il punto p
è all'interno del triangolo se e solo se sono tutti positivi.
EDIT: nota che l'espressione sopra per l'area presume che la numerazione del nodo del triangolo sia in senso antiorario. Se la numerazione è in senso orario, questa espressione restituirà un'area negativa (ma con la grandezza corretta). Il test stesso ( s>0 && t>0 && 1-s-t>0
) non dipende però dalla direzione della numerazione, poiché anche le espressioni sopra che vengono moltiplicate 1/(2*Area)
cambiano segno se cambia l'orientamento del nodo triangolare.
EDIT 2: Per un'efficienza computazionale ancora migliore, vedere il commento di coproc di seguito (che sottolinea che se l'orientamento dei nodi del triangolo (in senso orario o antiorario) è noto in anticipo, la divisione per 2*Area
nelle espressioni per s
e t
può essere evitato). Vedi anche jsfiddle-code di Perro Azul nei commenti sotto la risposta di Andreas Brinck .
2*Area
, cioè calcolando s´=2*|Area|*s
e t´=2*|Area|*t
(se l'orientamento dei punti - orario o antiorario - non è noto, il segno di Area
deve essere verificato, ovviamente, ma altrimenti forse non lo fa nemmeno deve essere calcolato), poiché per il controllo s>0
è sufficiente controllare s´>0
. E invece di controllare 1-s-t>0
è sufficiente controllare s´+t´<2*|Area|
.
p0->p1->p2
è in senso antiorario in cartesiano (che di solito è in senso orario nelle coordinate dello schermo ), il Area
calcolato con questo metodo sarà positivo.
Ho scritto questo codice prima di un ultimo tentativo con Google e di trovare questa pagina, quindi ho pensato di condividerlo. È fondamentalmente una versione ottimizzata della risposta di Kisielewicz. Ho esaminato anche il metodo baricentrico, ma a giudicare dall'articolo di Wikipedia ho difficoltà a vedere come sia più efficiente (immagino che ci sia un'equivalenza più profonda). Comunque, questo algoritmo ha il vantaggio di non utilizzare la divisione; un potenziale problema è il comportamento del rilevamento dei bordi a seconda dell'orientamento.
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;
}
In parole, l'idea è questa: il punto s è a sinistra oa destra di entrambe le linee AB e AC? Se è vero, non può essere dentro. Se falso, è almeno all'interno dei "coni" che soddisfano la condizione. Ora poiché sappiamo che un punto all'interno di un trigone (triangolo) deve essere sullo stesso lato di AB di BC (e anche di CA), controlliamo se differiscono. Se lo fanno, le s non possono essere dentro, altrimenti le s devono essere dentro.
Alcune parole chiave nei calcoli sono semipiani lineari e determinante (prodotto incrociato 2x2). Forse un modo più pedagogico è probabilmente pensarlo come un punto che si trova all'interno se e solo se è dalla stessa parte (sinistra o destra) di ciascuna delle linee AB, BC e CA. Tuttavia, il modo sopra descritto sembrava più adatto per alcune ottimizzazioni.
Versione C # del metodo baricentrico pubblicato da andreasdr e Perro Azul. Si noti che il calcolo dell'area può essere evitato se s
e t
hanno segni opposti. Ho verificato il comportamento corretto con uno unit test piuttosto approfondito.
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);
}
[ modifica ] ha
accettato la modifica suggerita da @Pierre; vedere i commenti
Versione Java del metodo baricentrico:
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;
}
Il codice precedente funzionerà accuratamente con i numeri interi, assumendo che non vi siano overflow. Funzionerà anche con triangoli in senso orario e antiorario. Non funzionerà con i triangoli collineari (ma puoi verificarlo testando det == 0).
La versione baricentrica è più veloce se testerai punti diversi con lo stesso triangolo.
La versione baricentrica non è simmetrica nei 3 punti del triangolo, quindi è probabile che sia meno coerente della versione semipiano di bordo di Kornel Kisielewicz, a causa di errori di arrotondamento in virgola mobile.
Credito: ho creato il codice sopra dall'articolo di Wikipedia sulle coordinate baricentriche.
Utilizzando la soluzione analitica per le coordinate baricentriche (indicate da Andreas Brinck ) e:
Si può ridurre al minimo il numero di operazioni "costose":
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;
}
Il codice può essere incollato in Perro Azul jsfiddle o provalo facendo clic su "Esegui snippet di codice" di seguito
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>
Portando a:
Questo si confronta abbastanza bene con la soluzione Kornel Kisielewicz (25 richiami, 1 archiviazione, 15 sottrazioni, 6 moltiplicazioni, 5 confronti), e potrebbe essere ancora migliore se è necessario il rilevamento in senso orario / antiorario (che richiede 6 richiami, 1 addizione, 2 sottrazioni , 2 moltiplicazioni e 1 confronto in sé, utilizzando il determinante della soluzione analitica, come sottolineato da rhgb ).
Quello che faccio è precalcolare le tre normali della faccia,
in 3D per prodotto incrociato del vettore laterale e del vettore normale della faccia.
in 2D semplicemente scambiando i componenti e negandone uno,
quindi dentro / fuori per ogni lato è quando un prodotto punto del lato normale e il vertice per punto vettore, cambia segno. Ripeti per altri due (o più) lati.
Benefici:
molto è precalcolato, quindi ottimo per test su più punti sullo stesso triangolo.
rifiuto precoce del caso comune di più punti esterni che interni. (anche se la distribuzione dei punti è ponderata su un lato, è possibile testare prima quel lato.)
Ecco un'implementazione efficiente di 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 un esempio di output:
Se stai cercando velocità, ecco una procedura che potrebbe aiutarti.
Ordina i vertici del triangolo sulle loro ordinate. Ciò richiede al massimo tre confronti. Siano Y0, Y1, Y2 i tre valori ordinati. Disegnando tre orizzontali attraverso di loro, si divide l'aereo in due mezzi piani e due lastre. Sia Y l'ordinata del punto di interrogazione.
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
Costa altri due confronti. Come puoi vedere, il rifiuto rapido si ottiene per i punti al di fuori della "lastra di delimitazione".
Facoltativamente, è possibile fornire un test sulle ascisse per un rapido rigetto a sinistra ea destra ( X <= X0' or X >= X2'
). Ciò implementerà un rapido test del riquadro di delimitazione allo stesso tempo, ma dovrai anche ordinare le ascisse.
Eventualmente sarà necessario calcolare il segno del punto dato rispetto ai due lati del triangolo che delimitano la relativa lastra (superiore o inferiore). Il test ha la forma:
((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))
La discussione completa delle i, j, k
combinazioni (ce ne sono sei, in base al risultato dell'ordinamento) è fuori dallo scopo di questa risposta e "lasciata come esercizio al lettore"; per l'efficienza, dovrebbero essere hard-coded.
Se pensi che questa soluzione sia complessa, osserva che si tratta principalmente di confronti semplici (alcuni dei quali possono essere precalcolati), più 6 sottrazioni e 4 moltiplicazioni nel caso in cui il test del riquadro di delimitazione fallisca. Quest'ultimo costo è difficile da battere perché nel peggiore dei casi non puoi evitare di confrontare il punto di prova con due lati (nessun metodo in altre risposte ha un costo inferiore, alcuni lo peggiorano, come 15 sottrazioni e 6 moltiplicazioni, a volte divisioni).
AGGIORNAMENTO: più veloce con una trasformazione di taglio
Come spiegato appena sopra, è possibile individuare rapidamente il punto all'interno di una delle quattro bande orizzontali delimitate dalle tre ordinate di vertice, utilizzando due confronti.
Facoltativamente, è possibile eseguire uno o due test X aggiuntivi per verificare l'interno del riquadro di delimitazione (linee tratteggiate).
Quindi si consideri la trasformazione "taglio" data da X'= X - m Y, Y' = Y
, dove m
è la pendenza DX/DY
per il bordo più alto. Questa trasformazione renderà verticale questo lato del triangolo. E poiché sai da che parte dell'orizzontale medio ti trovi, è sufficiente testare il segno rispetto ad un solo lato del triangolo.
Supponendo che tu abbia precalcolato la pendenza m
, così come X'
per i vertici del triangolo tranciato e i coefficienti delle equazioni dei lati come X = m Y + p
, avrai bisogno nel caso peggiore
X' = X - m Y
;X >< m' Y + p'
contro il lato rilevante del triangolo tranciato.Se conosci le coordinate dei tre vertici e le coordinate del punto specifico, puoi ottenere l'area del triangolo completo. Successivamente, calcola l'area dei tre segmenti del triangolo (un punto è il punto dato e gli altri due sono due vertici qualsiasi del triangolo). Quindi, otterrai l'area dei tre segmenti triangolari. Se la somma di queste aree è uguale all'area totale (ottenuta in precedenza), il punto dovrebbe trovarsi all'interno del triangolo. Altrimenti, il punto non è all'interno del triangolo. Questo dovrebbe funzionare. Se ci sono problemi, fammelo sapere. Grazie.
Ecco una soluzione in Python che è efficiente, documentata e contiene tre unittest. È di qualità professionale ed è pronto per essere inserito nel tuo progetto sotto forma di modulo così com'è.
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)
C'è un ulteriore test grafico opzionale per l'algoritmo di cui sopra per confermare la sua validità:
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")
Produrre la seguente grafica:
Altra funzione in python , più veloce del metodo Developer (almeno per me) e ispirata alla soluzione di 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 )
Puoi testarlo con:
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')
Ci vuole molto per tracciare, ma quella griglia viene testata in 0,0195319652557 secondi contro 0,0844349861145 secondi del codice dello sviluppatore .
Infine il commento sul codice:
# 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 tornerà true
anche se è falso
Poiché non esiste una risposta JS, soluzione in
senso orario e antiorario:
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: c'era un errore di battitura per il calcolo det ( cy - ay
invece di cx - ax
), questo è stato risolto.
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>
Sto usando qui lo stesso metodo descritto sopra: un punto è all'interno di ABC se è rispettivamente sullo "stesso" lato di ciascuna linea AB, BC, CA.
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
), questo serve per determinare l'ordine di avvolgimento del triangolo, quindi il metodo funzionerà con triangoli CW e CCW (vedi jsFiddle).
let det = (bx - ax) * (cy - ay) - (by - ay) * (cy - ay)
invece di let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)
così questo è risolto, grazie per la segnalazione
Voglio solo usare una semplice matematica vettoriale per spiegare la soluzione delle coordinate baricentriche che Andreas aveva dato, sarà molto più facile da capire.
(1-s) | v0v2 | / | v0v2 | = tp | v0v1 | / | v0v1 |
otteniamo 1 - s = tp, quindi 1 = s + tp. Se qualsiasi t> tp, che 1 <s + t dove si trova sulla doppia linea tratteggiata, il vettore è esterno al triangolo, qualsiasi t <= tp, quale 1> = s + t dove si trova sulla singola linea tratteggiata, il vettore è all'interno del triangolo.
Allora se diamo una s in [0, 1], la t corrispondente deve incontrare 1> = s + t, per il vettore all'interno del triangolo.
Quindi finalmente otteniamo v = s * v02 + t * v01, v è all'interno del triangolo con la condizione s, t, s + t appartiene a [0, 1]. Quindi traduci in punto, abbiamo
p - p0 = s * (p1 - p0) + t * (p2 - p0), con s, t, s + t in [0, 1]
che è la stessa della soluzione di Andreas per risolvere il sistema di equazioni p = p0 + s * (p1 - p0) + t * (p2 - p0), con s, t, s + t appartengono a [0, 1].
Questo è il concetto più semplice per determinare se un punto è all'interno o all'esterno del triangolo o su un braccio di un triangolo.
La determinazione di un punto è all'interno di un triangolo da determinanti:
Il codice funzionante più semplice:
#-*- 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)
Ci sono condizioni di bordo fastidiose in cui un punto si trova esattamente sul bordo comune di due triangoli adiacenti. Il punto non può essere in entrambi o in nessuno dei triangoli. Hai bisogno di un modo arbitrario ma coerente per assegnare il punto. Ad esempio, traccia una linea orizzontale attraverso il punto. Se la linea si interseca con l'altro lato del triangolo a destra, il punto viene considerato come se fosse all'interno del triangolo. Se l'intersezione è a sinistra, il punto è esterno.
Se la linea su cui giace il punto è orizzontale, usa sopra / sotto.
Se il punto si trova sul vertice comune di più triangoli, utilizzare il triangolo il cui centro forma l'angolo più piccolo.
Più divertente: tre punti possono essere in linea retta (zero gradi), ad esempio (0,0) - (0,10) - (0,5). In un algoritmo di triangolazione, l '"orecchio" (0,10) deve essere tagliato, il "triangolo" generato è il caso degenere di una linea retta.
Il modo più semplice e funziona con tutti i tipi di triangoli è semplicemente determinare gli angoli degli angoli dei punti P A, B, C. Se uno qualsiasi degli angoli è più grande di 180,0 gradi, allora è esterno, se 180,0 è sulla circonferenza e se acos ti tradisce e inferiore a 180,0, allora è interno.Guarda per capire http: // math-physics -psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html
Onestamente è semplice come la risposta di Simon P Steven, tuttavia con questo approccio non hai un solido controllo sul fatto che i punti sui bordi del triangolo siano inclusi o meno.
Il mio approccio è un po 'diverso ma molto semplice. Considera il triangolo seguente;
Per avere il punto nel triangolo dobbiamo soddisfare 3 condizioni
In questo metodo hai il pieno controllo per includere o escludere il punto sui bordi individualmente. Quindi puoi controllare se un punto è nel triangolo includendo solo | AC | edge per esempio.
Quindi la mia soluzione in JavaScript sarebbe la seguente;
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);
}
Non può essere più efficiente di così! Ogni lato di un triangolo può avere posizione e orientamento indipendenti, quindi tre calcoli: l1, l2 e l3 sono sicuramente necessari che coinvolgono 2 moltiplicazioni ciascuno. Una volta che l1, l2 e l3 sono noti, il risultato è solo pochi confronti di base e operazioni booleane di distanza.
Codice presumibilmente ad alte prestazioni che ho adattato in JavaScript (articolo sotto):
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)
- per triangoli in senso antiorariopointInTriangle(p, p0, p1, p2)
- per triangoli in senso orarioGuarda in jsFiddle (test delle prestazioni incluso), c'è anche il controllo dell'avvolgimento in una funzione separata. Oppure premi "Esegui snippet di codice" di seguito
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>
Ispirato da questo: http://www.phatcode.net/articles.php?id=459
Avevo bisogno di un punto nel triangolo in "ambiente controllabile" quando sei assolutamente sicuro che i triangoli saranno in senso orario. Quindi, ho preso jsfiddle di Perro Azul e l'ho modificato come suggerito da coproc per questi casi; ha anche rimosso le moltiplicazioni ridondanti 0,5 e 2 perché si annullano a vicenda.
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>
Ecco il codice C # equivalente per Unity: Here is equivalent C # code for 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;
}
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;
}
coordinate cartesiane quasi perfette convertite da baricentriche vengono esportate all'interno dei doppi * v (x) e * w (y). Entrambi i doppi di esportazione dovrebbero avere un * carattere prima in ogni caso, probabilmente: * v e * w Code possono essere usati anche per l'altro triangolo di un quadrilatero. Con la presente firmato ha scritto solo il triangolo abc dal quad abcd in senso orario.
A---B
|..\\.o|
|....\\.|
D---C
il punto o è all'interno del triangolo ABC per il test con il secondo triangolo chiama questa funzione direzione CDA, ei risultati dovrebbero essere corretti dopo *v=1-*v;
e *w=1-*w;
per il quadrilatero
Uno dei modi più semplici per verificare se l'area formata dai vertici del triangolo (x1, y1), (x2, y2), (x3, y3) è positiva o meno.
L'area può essere calcolata con la formula:
1/2 [x1 (y2 – y3) + x2 (y3 – y1) + x3 (y1 – y2)]
o il codice Python può essere scritto come:
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])]