Publicité

# Discussion: [java] Triangulation de Delaunay (incrémentale)

1. ## [java] Triangulation de Delaunay (incrémentale)

Une implémentation Java de l'algorithme de Triangulation de Delaunay incrémentale inventé par Leonidas Guibas et Jorge Stolfi.

La classe QuadEdge qui décrit la structure de données duale (face/segment):
Code java :
```123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
import java.awt.Point;

/**
*
* @author Code by X.Philippeau - Structure by Guibas and Stolfi
*
* @see Primitives for the Manipulation of General Subdivisions
*      and the Computation of Voronoi Diagrams (Leonidas Guibas,Jorge Stolfi)
*/

// pointer to the next (direct order) QuadEdge

// pointer to the dual QuadEdge (faces graph <-> edges graph)

// origin point of the edge/face
private Point orig;

// marker for triangle generation
public boolean mark=false;

/**
* (private) constructor. Use makeEdge() to create a new QuadEdge
*
* @param onext pointer to the next QuadEdge on the ring
* @param rot   pointer to the next (direct order) crossing edge
* @param orig  Origin point
*/
this.onext = Onext;
this.rot = rot;
this.orig = orig;
}

// ----------------------------------------------------------------
//                             Getter/Setter
// ----------------------------------------------------------------

return onext;
}

return rot;
}

public Point orig() {
return orig;
}

onext = next;
}

this.rot = rot;
}

public void setOrig(Point p) {
this.orig = p;
}

// ----------------------------------------------------------------
// ----------------------------------------------------------------

/**
* @return the symetric (reverse) QuadEdge
*/
return rot.rot();
}

/**
* @return the other extremity point
*/
public Point dest() {
return sym().orig();
}

/**
* @return the symetric dual QuadEdge
*/
return rot.sym();
}

/**
* @return the previous QuadEdge (pointing to this.orig)
*/
return rot.onext().rot();
}

/**
* @return the previous QuadEdge starting from dest()
*/
return rotSym().onext().rotSym();
}

/**
* @return the next QuadEdge on left Face
*/
return rotSym().onext().rot();
}

/**
* @return the previous QuadEdge on left Face
*/
return onext().sym();
}

// ************************** STATIC ******************************

/**
* Create a new edge (i.e. a segment)
*
* @param  orig origin of the segment
* @param  dest end of the segment
* @return the QuadEdge of the origin point
*/
public static QuadEdge makeEdge(Point orig, Point dest) {

// create the segment
q0.onext = q0; q2.onext = q2; // lonely segment: no "next" quadedge
q1.onext = q3; q3.onext = q1; // in the dual: 2 communicating facets

// dual switch
q0.rot = q1; q1.rot = q2;
q2.rot = q3; q3.rot = q0;

return q0;
}

/**
* attach/detach the two edges = combine/split the two rings in the dual space
*
* @param q1,q2 the 2 QuadEdge to attach/detach
*/

a.setOnext(t1);
b.setOnext(t2);
alpha.setOnext(t3);
beta.setOnext(t4);
}

/**
*
* @param e1,e2 the 2 QuadEdges to connect
*/
splice(q, e1.lnext());
splice(q.sym(), e2);
return q;
}

public static void swapEdge(QuadEdge e) {
splice(e, a);
splice(e.sym(), b);
splice(e, a.lnext());
splice(e.sym(), b.lnext());
e.orig = a.dest();
e.sym().orig = b.dest();
}

/**
*
* @param q the QuadEdge to delete
*/
public static void deleteEdge(QuadEdge q) {
splice(q, q.oprev());
splice(q.sym(), q.sym().oprev());
}

// ----------------------------------------------------------------
//                      Geometric computation
// ----------------------------------------------------------------

/**
* Test if the Point p is on the line porting the edge
*
* @param p Point to test
* @return true/false
*/
public static boolean isOnLine(QuadEdge e, Point p) {
// test if the vector product is zero
if ((p.x-e.orig().x)*(p.y-e.dest().y)==(p.y-e.orig().y)*(p.x-e.dest().x))
return true;
return false;
}

/**
* Test if the Point p is at the right of the QuadEdge q.
*
* @param p Point to test
* @return true/false
*/
public static boolean isAtRightOf(QuadEdge q, Point p) {
return isCounterClockwise(p, q.dest(), q.orig());
}

/** return true if a, b and c turn in Counter Clockwise direction
*
* @param a,b,c the 3 points to test
* @return true if a, b and c turn in Counter Clockwise direction
*/
public static boolean isCounterClockwise(Point a, Point b, Point c) {
// test the sign of the determinant of ab x cb
if ( (a.x - b.x)*(b.y - c.y) > (a.y - b.y)*(b.x - c.x) ) return true;
return false;
}

/**
* The Delaunay criteria:
*
*   test if the point d is inside the circumscribed circle of triangle a,b,c
*
* @param a,b,c triangle
* @param d point to test
* @return  true/false
*/
public static boolean inCircle(Point a, Point b, Point c, Point d) {
/*
if "d" is strictly INSIDE the circle, then

|d² dx dy 1|
|a² ax ay 1|
det |b² bx by 1| < 0
|c² cx cy 1|

*/
long a2 = a.x*a.x + a.y*a.y;
long b2 = b.x*b.x + b.y*b.y;
long c2 = c.x*c.x + c.y*c.y;
long d2 = d.x*d.x + d.y*d.y;

long det44 = 0;
det44 += d2  * det33( a.x,a.y, 1,  b.x,b.y, 1,  c.x,c.y, 1 );
det44 -= d.x * det33( a2 ,a.y, 1,  b2 ,b.y, 1,  c2 ,c.y, 1 );
det44 += d.y * det33( a2 ,a.x, 1,  b2 ,b.x, 1,  c2 ,c.x, 1 );
det44 -= 1   * det33( a2,a.x,a.y,  b2,b.x,b.y,  c2,c.x,c.y );

if (det44<0) return true;
return false;
}

private static long det33( long... m ) {
long det33=0;
det33 += m[0] * (m[4]*m[8] - m[5]*m[7]);
det33 -= m[1] * (m[3]*m[8] - m[5]*m[6]);
det33 += m[2] * (m[3]*m[7] - m[4]*m[6]);
return det33;
}
}```

La classe Delaunay qui effectue la triangulation:
Code java :
```123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

/**
* Incremental Delaunay Triangulation
*
* @author Java-code by X.Philippeau - Pseudo-code by Guibas and Stolfi
*
* @see Primitives for the Manipulation of General Subdivisions
*      and the Computation of Voronoi Diagrams (Leonidas Guibas,Jorge Stolfi)
*/
public class Delaunay {

// starting edge for walk (see locate() method)

// list of quadEdge belonging to Delaunay triangulation

// Bounding box of the triangulation
class BoundingBox {
int minx,miny,maxx,maxy;
Point a = new Point(); // lower left
Point b = new Point(); // lower right
Point c = new Point(); // upper right
Point d = new Point(); // upper left
}
private BoundingBox bbox = new BoundingBox();

/**
* Constuctor:
*/
public Delaunay() {

bbox.minx = Integer.MAX_VALUE;
bbox.maxx = Integer.MIN_VALUE;
bbox.miny = Integer.MAX_VALUE;
bbox.maxy = Integer.MIN_VALUE;

// create the QuadEdge graph of the bounding box

this.startingEdge = ab;
}

/**
* update the dimension of the bounding box
*
* @param minx,miny,maxx,maxy summits of the rectangle
*/
public void setBoundigBox(int minx,int miny,int maxx,int maxy) {
// update saved values
bbox.minx=minx; bbox.maxx=maxx;
bbox.miny=miny; bbox.maxy=maxy;

// extend the bounding-box to surround min/max
int centerx = (minx+maxx)/2;
int centery = (miny+maxy)/2;
int x_min = (int)((minx-centerx-1)*10+centerx);
int x_max = (int)((maxx-centerx+1)*10+centerx);
int y_min = (int)((miny-centery-1)*10+centery);
int y_max = (int)((maxy-centery+1)*10+centery);

// set new positions
bbox.a.x = x_min;	bbox.a.y = y_min;
bbox.b.x = x_max;	bbox.b.y = y_min;
bbox.c.x = x_max;	bbox.c.y = y_max;
bbox.d.x = x_min;	bbox.d.y = y_max;
}

// update the size of the bounding box (cf locate() method)
private void updateBoundigBox(Point p) {
int minx = Math.min(bbox.minx, p.x);
int maxx = Math.max(bbox.maxx, p.x);
int miny = Math.min(bbox.miny, p.y);
int maxy = Math.max(bbox.maxy, p.y);
setBoundigBox(minx, miny, maxx, maxy);
//System.out.println("resizing bounding-box: "+minx+" "+miny+" "+maxx+" "+maxy);
}

/**
* Returns an edge e of the triangle containing the point p
* (Guibas and Stolfi)
*
* @param p the point to localte
* @return the edge of the triangle
*/

/* outside the bounding box ? */
if ( p.x<bbox.minx || p.x>bbox.maxx || p.y<bbox.miny || p.y>bbox.maxy ) {
updateBoundigBox(p);
}

while (true) {
/* duplicate point ? */
if(p.x==e.orig().x && p.y==e.orig().y) return e;
if(p.x==e.dest().x && p.y==e.dest().y) return e;

/* walk */
e = e.sym();
e = e.onext();
e = e.dprev();
else
return e;
}
}

/**
*  Inserts a new point into a Delaunay triangulation
*  (Guibas and Stolfi)
*
* @param p the point to insert
*/
public void insertPoint(Point p) {

// point is a duplicate -> nothing to do
if(p.x==e.orig().x && p.y==e.orig().y) return;
if(p.x==e.dest().x && p.y==e.dest().y) return;

// point is on an existing edge -> remove the edge
e = e.oprev();
}

// Connect the new point to the vertices of the containing triangle
// (or quadrilateral in case of the point is on an existing edge)

this.startingEdge = base;
do {
e = base.oprev();
} while (e.lnext() != startingEdge);

// Examine suspect edges to ensure that the Delaunay condition is satisfied.
do {

// flip triangles
e = e.oprev();
}
else if (e.onext() == startingEdge)
return; // no more suspect edges
else
e = e.onext().lprev();  // next suspect edge
} while (true);
}

/**
*  compute and return the list of edges
*/
public List<Point[]> computeEdges() {
List<Point[]> edges = new ArrayList<Point[]>();
// do not return edges pointing to/from surrouding triangle
if ( q.orig()==bbox.a || q.orig()==bbox.b || q.orig()==bbox.c || q.orig()==bbox.d ) continue;
if ( q.dest()==bbox.a || q.dest()==bbox.b || q.dest()==bbox.c || q.dest()==bbox.d ) continue;
}
return edges;
}

/**
*  compute and return the list of triangles
*/
public List<Point[]> computeTriangles() {
List<Point[]> triangles = new ArrayList<Point[]>();

// do not process edges pointing to/from surrouding triangle
// --> mark them as already computed
q.mark = false; q.sym().mark=false;
if ( q.orig()==bbox.a || q.orig()==bbox.b || q.orig()==bbox.c || q.orig()==bbox.d ) q.mark = true;
if ( q.dest()==bbox.a || q.dest()==bbox.b || q.dest()==bbox.c || q.dest()==bbox.d ) q.sym().mark=true;
}

// compute the 2 triangles associated to each quadEdge
// first triangle
if (!q1.mark && !q2.mark && !q3.mark) {
}

// second triangle
if (!qsym1.mark && !qsym2.mark && !qsym3.mark) {
}

// mark as used
qe.mark=true;
qe.sym().mark=true;
}

return triangles;
}

public List<Point[]> computeVoronoi() {
List<Point[]> voronoi = new ArrayList<Point[]>();

// do not process edges pointing to/from surrouding triangle
// --> mark them as already computed
q.mark = false; q.sym().mark=false;
if ( q.orig()==bbox.a || q.orig()==bbox.b || q.orig()==bbox.c || q.orig()==bbox.d ) q.mark = true;
if ( q.dest()==bbox.a || q.dest()==bbox.b || q.dest()==bbox.c || q.dest()==bbox.d ) q.sym().mark=true;
}

// walk throught left and right region
for(int b=0;b<=1;b++) {
if (qstart.mark) continue;

// new region start
List<Point> poly = new ArrayList<Point>();

// walk around region
while(true) {
qregion.mark=true;

// compute CircumCenter if needed
if (qregion.rot().orig()==null) {
QuadEdge q1 = qregion;		Point p0=q1.orig();
QuadEdge q2 = q1.lnext();	Point p1=q2.orig();
QuadEdge q3 = q2.lnext();	Point p2=q3.orig();

double ex=p1.x-p0.x, ey=p1.y-p0.y;
double nx=p2.y-p1.y, ny=p1.x-p2.x;
double dx=(p0.x-p2.x)*0.5, dy=(p0.y-p2.y)*0.5;
double s=(ex*dx+ey*dy)/(ex*nx+ey*ny);
double cx=(p1.x+p2.x)*0.5+s*nx;
double cy=(p1.y+p2.y)*0.5+s*ny;

Point p = new Point( (int)cx,(int)cy );
qregion.rot().setOrig(p);
}

qregion = qregion.onext();
if (qregion==qstart) break;
}

// add region to output list
}
}
return voronoi;
}
}```

et enfin un exemple d'utilisation:
Code java :
```12345678910111213141516171819
/* creation de l'instance */
Delaunay delaunay = new Delaunay();

/* ajout des points (calcul incrémental) */
delaunay.insertPoint( new Point(12,98) );
delaunay.insertPoint( new Point(34,76) );
delaunay.insertPoint( new Point(56,54) );
delaunay.insertPoint( new Point(78,32) );
/* ... */

/* retourne la liste des segments */
List<Point[]> edges = delaunay.computeEdges();

/* retourne la liste des triangles */
List<Point[]> triangles = delaunay.computeTriangles();

/* retourne la liste des régions */
List<Point[]> voronoi = delaunay.computeVoronoi();```

Edit : Modif de la méthode QuadEdge.IsOnLine(), cf. post #38 de Commander Keen.

2. Hello, merci tout d'abord pour l'implémentation :

Code :
```1234public QuadEdge sym() {
return rot.rot();
}```
Sinon ça en java, ça devient bien ça en C++ ?

Code :
```123	QuadEdge* QuadEdge::sym() {
return m_rot->getRot();
}```

3. Envoyé par Rumpel
Sinon ça en java, ça devient bien ça en C++ ?

Code :
```123	QuadEdge* QuadEdge::sym() {
return m_rot->getRot();
}```
Oui, c'est ca. C'est vrai que je n'aurais pas dû utiliser le même nom entre la méthode et l'attribut.

4. Oui, merci dans le même genre d'idée en C++
J'ai renommé

Sinon je ne m'en sortais pas.
Bon encore une centaine d'erreurs à trouver mon Delaunay ne marche pas encore....

Sinon personne ne sait comment on fait des Getter avec un attribut pointeur et des const ?

5. j'ai une version C, si ça intéresse quelqu'un... (uniquement de la triangulation). Mais j'ai plusieurs sources free de l'ensemble...

6. Envoyé par souviron34
j'ai une version C, si ça intéresse quelqu'un... (uniquement de la triangulation). Mais j'ai plusieurs sources free de l'ensemble...
Moi ca m'intéresse, spécialement la partie construction de la liste des triangles à partir des quadedges.

Dans l'implémentation Java que j'ai postée, cette partie là est une création originale. Je me suis assez cassé la tête dessus pour avoir quelque chose d'efficace, mais je n'ai jamais démontré rigoureusement son exactitude. J'ai juste lancé plusieurs milliers de triangulations et comparé les résultats avec d'autre implémentations.

7. Hello,

En tous cas merci beaucoup à PseudoCode pour son implémentation qui est à la fois simple (relativement à la complexité du problème) et élégante

J'ai bien aimé la séparation entre ce qui est topologie et géométrie.
En ce qui concerne Splice, je n'ai pas tout bien compris ce que ça faisait, même après avoir vu les explications de l'article qui sont bien théoriques

Ensuite :
Code :
```12e.orig = a.dest();
e.sym().orig = b.dest();```
orig n'est pas un membre privé ? Il ne faut pas utiliser un setter ?
(Bon mon compilateur semble l'accepter...)

Code :
`		if (d>0.01) return false;`
Ca c'est pas super heureux, je vais transformer les coordonnées des points pour avoir des flottants, selon l'échelle utilisée ensuite, ça peut causer des problèmes... Je pense définir une constante epsilon et la mettre bien en vue au début...

Code :
`		int x_min = (int)((minx-centerx-1)*10+centerx);`
Si minx-centerx == 1, alors x_min = centerx ?? C'est l'effet voulu, intuitivement j'aurais plus vu un +1 ?

Bon, après la fin je n'ai pas encore trop vu, ma traduction semble ne pas trop marcher... Bon je vais voir.

Oui, sinon d'autres implémentations pourraient m'être utiles. Je vais rester pas de temps sur le problème. (Delaunay contraint, peut être tenter des modifications dynamiques, des optimisations avec les Edge orientées (SymEdge)... des recherches de chemins à travers la triangulation - application aux jeux 3D... si tout marche Delaunay en 3D...)

Envoyez moi un MP si vous voulez mes coordonnées.
Souviron34, je t'envoie un MP, merci

8. Envoyé par Rumpel
En ce qui concerne Splice, je n'ai pas tout bien compris ce que ça faisait, même après avoir vu les explications de l'article qui sont bien théoriques
C'est plus simple si tu essayes de comprendre la transformation inverse. Si tu regardes la figure 10 (page 97), pour passer de la figure de droite à celle de gauche on met les 2 points "u" et "v" l'un sur l'autre.

orig n'est pas un membre privé ? Il ne faut pas utiliser un setter ?
(Bon mon compilateur semble l'accepter...)
Si, il faudrait. Ca marche parce qu'on est toujours dans la classe QuadEdge quand on fait "e.sym()".

Code :
`if (d>0.01) return false;`
Ca c'est pas super heureux
Oui, c'est vrai que c'est moyen . D'un autre coté je n'utilise que des coordonnées entières pour les points, donc ca passe.

Code :
`int x_min = (int)((minx-centerx-1)*10+centerx);`
Si minx-centerx == 1, alors x_min = centerx ?? C'est l'effet voulu, intuitivement j'aurais plus vu un +1 ?
"minx" est inférieur a "centerx", donc la différence est toujours négative.

9. [je poste ça cette après-midi]

10. Ok, merci PseudoCode pour tes retours

J'ai encore juste un souci.
Alors j'insère un point, => création de 4 arêtes vers la BoundingBox : OK
Insertion d'un nouveau point => création de 3 nouvelles arêtes : OK
Mais au 3è point, il semble ne pas me localiser convenablement le point

Alors coordonnées des points :
(56,0)
(81,19)
(48,59)

Pour locate (de ce dernier point), il me renvoie au point (56,0) et ensuite il me sélectionne les 3 points (56,0) (81,19) et le point haut gauche de la BoundingBox
Alors que le point (48,59) est dans le triangle (81,19) et les 2 points hauts de la BoundingBox...

Je sais pas, j'ai pourtant mot à mot le même algo que Guibas et Stolfi...
(calqué sur le tien aussi)

*-------------------------------------------------------------
Bon alors à la recherche de mon erreur

Code :
```1234567	class BoundingBox {
int minx,miny,maxx,maxy;
Point a = new Point(); // lower left
Point b = new Point(); // lower right
Point c = new Point(); // upper right
Point d = new Point(); // upper left
}```
A quoi servent minx, miny, maxx et maxy ??
Je pensais au début qu'ils étaient là pour coder de façon redondante a,b,c et d... Mais si on regarde setBoundingBox, on voit qu'ils sont updatés avant le calcul des nouveaux x_min, x_max ... et pas après...

11. Envoyé par Rumpel
A quoi servent minx, miny, maxx et maxy ??
Je pensais au début qu'ils étaient là pour coder de façon redondante a,b,c et d... Mais si on regarde setBoundingBox, on voit qu'ils sont updatés avant le calcul des nouveaux x_min, x_max ... et pas après...
Hum... C'est vrai que c'est pas clair. Le terme bounding-box est un peu faux.

(minx, miny, maxx, maxy) sont les "vraies" coordonnées de la bounding-box. C'est à dire les coordonnées X/Y minimum et maximum des points qui ont été insérés jusqu'à présent.

(a,b,c,d) sont les coordonnées d' une surounding-box. C'est à dire un rectangle qui englobe la bounding-box. Les 4 points a,b,c,d sont considérés commes des points "a l'infini" par rapport aux points qui ont été insérés jusqu'à présent.

D'ou la formule: a == (minx-centerx-1)*10+centerx;

qui envoie le point "a" dix fois plus loin, à gauche, que "minx" par rapport au centre de la bounding box.

Code :
```1234567891011121314a                                b
+------------------------------+
|                              |
|                              |
|   (minx,miny)  (maxx,miny)   |
|         +---------+          |
|         |    +    |          |
|         +---------+          |
|   (minx,maxy)  (maxx,maxy)   |
|                              |
|                              |
+------------------------------+
d                                c```

12. J'ai changé par ça, ça marche bien mieux maintenant

Code :
```1234567891011121314151617/*		m_bbox->minx=minx;
m_bbox->maxx=maxx;
m_bbox->miny=miny;
m_bbox->maxy=maxy;*/

// extend the bounding-box to surround min/max
int centerx = (minx+maxx)/2;
int centery = (miny+maxy)/2;
int x_min = (int)((minx-centerx-1)*10+centerx);
int x_max = (int)((maxx-centerx+1)*10+centerx);
int y_min = (int)((miny-centery-1)*10+centery);
int y_max = (int)((maxy-centery+1)*10+centery);

m_bbox->minx=x_min;
m_bbox->maxx=x_max;
m_bbox->miny=y_min;
m_bbox->maxy=y_max;```

13. Envoyé par Rumpel
J'ai changé par ça, ça marche bien mieux maintenant
... En théorie ca devrait marcher moins bien. Les 4 points a,b,c,d sont censés être à l'infini pour que la triangulation respecte le critère de Delaunay.

14. C'est ce que je fais, pour moi la petite boite à la même taille que la grande, donc elle se rapproche de l'infini...

15. Envoyé par Rumpel
C'est ce que je fais, pour moi la petite boite à la même taille que la grande, donc elle se rapproche de l'infini...
Bon. Je ne vois pas trop pourquoi ça marche mieux pour toi, mais tant que ça marche, c'est le principal.

16. Salut pseudoCode,

Bon je repasse sur le Forum parce que c'est plus pratique.
Sinon mon système avec Id fonctionne. (Le problème était que pour marquer une arête il faut déjà la retrouver dans le graphe... avec la structure des quadEdge c'est pas évident, surtout si tu dois faire ça avec toutes les arêtes, de plus il faut pouvoir marquer des arêtes qui n'existent pas encore, donc les créer avant, pas pratique...)

Mais maintenant j'ai un bug un peu bizarre et qui se retrouve assez peu souvent.
En fait l'algo d'insertion à un moment tu "flip" (ou "swap") les arêtes.
Quand ton polygone formé de 4 points est convexe (+ de 99% des cas), tout va pour le mieux dans le meilleur des mondes (cf p. 119). Mais quand il est pas convexe, il se produit une grosse catastrophe

Et ça l'article de Guibas et Stolfi n'en parle pas...

EDIT : Le polygone MNLX

17. Voila une figure, il faut swapper l'arête rouge au milieu de la zone jaune

18. Je ne vois pas trop dans quel cas (avec l'algo incrémental) on peut se retrouver dans un cas pareil. Tu as un exemple ?

Oups. Pas vu que tu as posté un image pendant ce temps là. Je regarde ca.

Remarque: les 4 points extrêmes dans le dessin, ce ne seraient pas ceux de la bounding-box ?

EDIT: J'ai testé la triangulation avec des points dans la même configuration que toi
Code :
```123456789Point[] points = new Point[] {
new Point(50,5), new Point(350,5), new Point(350,965), new Point(50,965),  // Bounding-box ?

new Point(200,523),
new Point(190,443),
new Point(194,426),
new Point(241,500),
new Point(246,475)
};```
Mon algo ne renvoie pas d'erreur (c'est déjà ça ). Je me demande s'il faut vraiment swaper ce edge ? La triangulation est incorrecte ?

19. En fait j'ai identifié l'erreur.
J'ai les points (dans l'ordre)

(90, 35)
(166, -326)
(166, 384)
(51, 71)

Et le test incircle me renvoit que le point (51 71) est à l'intérieur du cercle formé par les 3 autres... Ce qui est à vu de nez est manifestement faux...

20. Envoyé par Rumpel
Et le test incircle me renvoit que le point (51 71) est à l'intérieur du cercle formé par les 3 autres... Ce qui est à vu de nez est manifestement faux...
???

Si je fais:
Code java :
```12345boolean b = QuadEdge.inCircle(
new Point(90, 35), new Point(166, -326), new Point(166, 384),
new Point(51, 71)
);
System.out.println("inCircle returns "+b);```

j'obtiens le résultat:
inCircle returns false

#### Règles de messages

• Vous ne pouvez pas créer de nouvelles discussions
• Vous ne pouvez pas envoyer des réponses
• Vous ne pouvez pas envoyer des pièces jointes
• Vous ne pouvez pas modifier vos messages
•