├── vdmst.pdf ├── SuDrysdale.pdf ├── makefile ├── .gitattributes ├── .gitignore ├── README~ ├── README ├── vdmst.ps └── common.c /vdmst.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexdwyer/DelaunayTriangulation/HEAD/vdmst.pdf -------------------------------------------------------------------------------- /SuDrysdale.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexdwyer/DelaunayTriangulation/HEAD/SuDrysdale.pdf -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | DESTDIR= 2 | 3 | DEMOS= dwyerdt guibasdt mst 4 | 5 | CFLAGS = -O 6 | 7 | 8 | common: common.c 9 | cc common.c -o common -lm ${WINLIBS} 10 | strip common 11 | rm -f guibasdt dwyerdt vd mst 12 | ln common guibasdt 13 | ln common dwyerdt 14 | ln common vd 15 | ln common mst 16 | 17 | clean: 18 | rm -f *.o errs core ${DEMOS} 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /README~: -------------------------------------------------------------------------------- 1 | This code for Delaunay triangulations (Voronoi diagrams) is very old 2 | now, but I occasionally get requests for it. 3 | 4 | This program is based on 5 | 6 | Dwyer, R.A., A faster divide-and-conquer algorithm for constructing 7 | Delaunay triangulations. Algorithmica 2(2):137-151, 1987. 8 | 9 | This was my very first paper. Oddly, I can't seem to find tex source 10 | (though I did find the tex source for other papers nearly as old.) 11 | You can buy the conference version (2nd Symposium on Computational 12 | Geometry -- ACM) for less than the Algorithmica version (Springer). 13 | This work is mentioned in the Wikipedia entry for "Delaunay 14 | Triangulation". 15 | 16 | There's a short int in there that may need to be changed to an 17 | int. Back when, I was optimizing for what now seems like a small 18 | number of points. Try to imagine that it took minutes to 19 | run my largest simulations, which I think involved 32K or 64K points. 20 | (I don't recall, but I may have been up against memory limitations, too!) 21 | 22 | I'm including a review of several Delaunay triangulation algorithms written 23 | by Peter Su and Scot Drysdale that showed that this was the ONE AND 24 | ONLY VERY BEST algorithm when the survey was written. 25 | 26 | There's also a little postscript file with a picture. I believe this 27 | picture was on my business cards when I was at NC State. 28 | 29 | That's all I have to say about that, but if you have questions, you 30 | can contact me at rex.dwyer@pobox.com. In fact, if you use this code, 31 | why not drop me a line to let me know? 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This code for Delaunay triangulations (Voronoi diagrams) is very old 2 | now, but I occasionally get requests for it. 3 | 4 | This program is based on 5 | 6 | Dwyer, R.A., A faster divide-and-conquer algorithm for constructing 7 | Delaunay triangulations. Algorithmica 2(2):137-151, 1987. 8 | 9 | This was my very first paper. Oddly, I can't seem to find tex source 10 | (though I did find the tex source for other papers nearly as old.) 11 | You can buy the conference version (2nd Symposium on Computational 12 | Geometry -- ACM) for less than the Algorithmica version (Springer). 13 | This work is mentioned in the Wikipedia entry for "Delaunay 14 | Triangulation". 15 | 16 | There's a short int in there that may need to be changed to an 17 | int. Back when, I was optimizing for what now seems like a small 18 | number of points. Try to imagine that it took minutes to 19 | run my largest simulations, which I think involved 32K or 64K points. 20 | (I don't recall, but I may have been up against memory limitations, too!) 21 | 22 | I'm including a review of several Delaunay triangulation algorithms written 23 | by Peter Su and Scot Drysdale that showed that this was the ONE AND 24 | ONLY VERY BEST algorithm when the survey was written. This was later confirmed 25 | by JR Shewchuk, http://www.cs.cmu.edu/~quake/tripaper/triangle2.html 26 | 27 | There's also a little postscript file with a picture. I believe this 28 | picture was on my business cards when I was at NC State. 29 | 30 | That's all I have to say about that, but if you have questions, you 31 | can contact me at rex.dwyer@pobox.com. In fact, if you use this code, 32 | why not drop me a line to let me know? 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /vdmst.ps: -------------------------------------------------------------------------------- 1 | %%Title: 2 | /drawseg {moveto lineto stroke} def 3 | /bullet {0.02 0 360 arc fill} def 4 | 3 72 mul dup scale 5 | 1.0 1.5 translate 6 | 0 setlinecap 7 | 2 setlinejoin 8 | 9 | /p1 {0.976472 0.215916} def 10 | /p2 {0.885404 0.018398 } def 11 | /p3 {0.813589 0.747151 } def 12 | /p4 {0.809956 0.912001 } def 13 | /p5 {0.640209 0.275154 } def 14 | /p6 {0.590920 0.861156 } def 15 | /p7 {0.460293 0.172633 } def 16 | /p8 {0.411892 0.684812 } def 17 | /p9 {0.397307 0.158607 } def 18 | /p10 {0.336444 0.325753 } def 19 | /p11 {0.311135 0.934993 } def 20 | /p12 {0.269669 0.880746 } def 21 | /p13 {0.256056 0.040007 } def 22 | /p14 {0.197201 0.394365 } def 23 | /p15 {0.145114 0.887101 } def 24 | /p16 {0.130166 0.135236 } def 25 | 26 | 0.7 setgray 27 | 0.03 setlinewidth 28 | [ ] 0 setdash 29 | p7 moveto 30 | p9 lineto 31 | p13 lineto 32 | p16 lineto 33 | p10 lineto 34 | p14 lineto 35 | p8 lineto 36 | p12 lineto 37 | p15 lineto 38 | p11 lineto 39 | p6 lineto 40 | p4 lineto 41 | p3 lineto 42 | p5 lineto 43 | p1 lineto 44 | p2 lineto closepath stroke 45 | 46 | 0 setgray 47 | 1 2 72 mul div setlinewidth 48 | [ ] 0 setdash 49 | p11 p12 drawseg 50 | p4 p6 drawseg 51 | p1 p2 drawseg 52 | p1 p5 drawseg 53 | p13 p16 drawseg 54 | p9 p13 drawseg 55 | p9 p10 drawseg 56 | p10 p14 drawseg 57 | p8 p14 drawseg 58 | p8 p12 drawseg 59 | p12 p15 drawseg 60 | p6 p8 drawseg 61 | p3 p4 drawseg 62 | p5 p7 drawseg 63 | p7 p9 drawseg 64 | 65 | 66 | 1 6 72 mul div setlinewidth 67 | [0.04 0.04 ] 0.02 setdash 68 | 69 | 0.261066 0.177455 70 | 0.126829 0.000000 drawseg 71 | 72 | 0.211683 0.968041 73 | 0.202464 1.000000 drawseg 74 | 75 | 0.796633 0.179080 76 | 1.000000 0.085315 drawseg 77 | 78 | 0.398879 0.824951 79 | 0.211683 0.968041 drawseg 80 | 81 | 0.714169 0.827426 82 | 0.674109 1.000000 drawseg 83 | 0.847339 0.466911 84 | 1.000000 0.513719 drawseg 85 | 86 | 0.796633 0.179080 87 | 0.847339 0.466911 drawseg 88 | 89 | 0.653614 0.042500 90 | 0.796633 0.179080 drawseg 91 | 92 | 0.638194 0.000000 93 | 0.653614 0.042500 drawseg 94 | 95 | 0.261066 0.177455 96 | 0.258845 0.202842 drawseg 97 | 98 | 0.410063 0.000000 99 | 0.261066 0.177455 drawseg 100 | 101 | 0.258845 0.202842 102 | 0.408385 0.257295 drawseg 103 | 104 | 0.258845 0.202842 105 | 0.213530 0.251906 drawseg 106 | 107 | 0.213530 0.251906 108 | 0.341739 0.512097 drawseg 109 | 110 | 0.213530 0.251906 111 | 0.000000 0.307144 drawseg 112 | 113 | 0.000000 0.622640 114 | 0.168143 0.640414 drawseg 115 | 116 | 0.168143 0.640414 117 | 0.196902 0.678342 drawseg 118 | 119 | 0.341739 0.512097 120 | 0.168143 0.640414 drawseg 121 | 122 | 0.517431 0.475179 123 | 0.341739 0.512097 drawseg 124 | 125 | 0.398879 0.824951 126 | 0.435636 0.839755 drawseg 127 | 128 | 0.196902 0.678342 129 | 0.398879 0.824951 drawseg 130 | 131 | 0.196902 0.678342 132 | 0.211683 0.968041 drawseg 133 | 134 | 0.435636 0.839755 135 | 0.477926 1.000000 drawseg 136 | 137 | 0.623061 0.649477 138 | 0.435636 0.839755 drawseg 139 | 140 | 0.714169 0.827426 141 | 1.000000 0.833724 drawseg 142 | 143 | 0.623061 0.649477 144 | 0.714169 0.827426 drawseg 145 | 146 | 0.639549 0.543239 147 | 0.623061 0.649477 drawseg 148 | 149 | 0.847339 0.466911 150 | 0.639549 0.543239 drawseg 151 | 152 | 0.517431 0.475179 153 | 0.639549 0.543239 drawseg 154 | 155 | 0.492466 0.325303 156 | 0.517431 0.475179 drawseg 157 | 158 | 0.653614 0.042500 159 | 0.492466 0.325303 drawseg 160 | 161 | 0.408385 0.257295 162 | 0.492466 0.325303 drawseg 163 | 164 | 0.465683 0.000000 165 | 0.408385 0.257295 drawseg 166 | 167 | p1 bullet 168 | p2 bullet 169 | p3 bullet 170 | p4 bullet 171 | p5 bullet 172 | p6 bullet 173 | p7 bullet 174 | p8 bullet 175 | p9 bullet 176 | p10 bullet 177 | p11 bullet 178 | p12 bullet 179 | p13 bullet 180 | p14 bullet 181 | p15 bullet 182 | p16 bullet 183 | 184 | showpage 185 | %%EndProlog 186 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | #define POSTSCRIPT 2 | /****************************************************************/ 3 | /* DELAUNAY TRIANGULATION 4 | /* VORONOI DIAGRAM 5 | /* MINIMUM SPANNING TREE 6 | /* 7 | /* Rex A. Dwyer 8 | /* 9 | /* This program implements the Delaunay triangulation and Voronoi 10 | /* diagram algorithms of: 11 | /* 12 | /* L.J. Guibas & J. Stolfi, "Primitives for the manipulation of 13 | /* general subdivisions and the computation of Voronoi diagrams", 14 | /* ACM Transactions on Graphics 4 (1985), 74-123. 15 | /* 16 | /* and 17 | /* 18 | /* R.A. Dwyer, "A simple divide-and-conquer algorithm for 19 | /* constructing Delaunay triangulations in O(n log log n) expected 20 | /* time", 2nd Symposium on Computational Geometry (1986), 276-284. 21 | /* 22 | /* The algorithm for traversing and outputting the computed graph 23 | /* without using additional storage is that of: 24 | /* 25 | /* H. Edelsbrunner, L.J. Guibas & J. Stolfi, "Optimal Point Location 26 | /* in a Monotone Subdivision", DEC SRC Report #2 (1984), 22-27. 27 | /* 28 | /* The round-robin minimum-spanning-tree algorithm is described 29 | /* in: 30 | /* 31 | /* R.E.Tarjan, Data Structures and Network Algorithms, SIAM, 1983. 32 | /****************************************************************/ 33 | 34 | #include 35 | #include 36 | #define TRUE 1 37 | #define FALSE 0 38 | 39 | #define EPSILON (1.0e-6) 40 | #define drand() (((double) rand()) / (double) 0x7fffffff) 41 | 42 | /* Clearly wrong, but good enough for now: */ 43 | #define MAXDOUBLE 1.0e10 44 | #define MINDOUBLE (-1.0e10) 45 | 46 | #define BOOLEAN int 47 | 48 | typedef int EDGE_PTR; 49 | 50 | typedef unsigned short VERTEX_PTR; 51 | 52 | struct VEC2 { 53 | double x,y; 54 | }; 55 | 56 | struct VERTEX { 57 | struct VEC2 v; 58 | union { 59 | double norm; 60 | struct { 61 | VERTEX_PTR father; 62 | short count; 63 | EDGE_PTR heap; 64 | } vstruct; 65 | } vunion; 66 | }; 67 | 68 | #define onext(a) next[a] 69 | #define oprev(a) rot(onext(rot(a))) 70 | #define lnext(a) rot(onext(rotinv(a))) 71 | #define lprev(a) sym(onext(a)) 72 | #define rnext(a) rotinv(onext(rot(a))) 73 | #define rprev(a) onext(sym(a)) 74 | #define dnext(a) sym(onext(sym(a))) 75 | #define dprev(a) rotinv(onext(rotinv(a))) 76 | 77 | #define X(a) va[a].v.x 78 | #define Y(a) va[a].v.y 79 | #define NORM(a) va[a].vunion.norm 80 | #define COUNT(a) va[a].vunion.vstruct.count 81 | #define FATHER(a) va[a].vunion.vstruct.father 82 | #define HEAP(a) va[a].vunion.vstruct.heap 83 | #define COLOR(e) (color[e>>2]) 84 | #define orig(a) org[a] 85 | #define dest(a) orig(sym(a)) 86 | 87 | #define sym(a) ((a) ^ 2) 88 | EDGE_PTR rot(); 89 | EDGE_PTR rotinv(); 90 | delete_all_edges(); 91 | EDGE_PTR alloc_edge(); 92 | EDGE_PTR makeedge(); 93 | splice(); 94 | swapedge(); 95 | EDGE_PTR connect(); 96 | deleteedge(); 97 | build_delaunay_triangulation(); 98 | BOOLEAN leftof(); 99 | 100 | VERTEX_PTR *vp; /* points to array holding indices of input points */ 101 | struct VERTEX *va; /* points to array holding domain coordinates of 102 | input points. The domain coordinates of the kth 103 | input point are X(k-1) and Y(k-1) 104 | (same as va[k-1].v.x and va[k-1].v.y) */ 105 | EDGE_PTR *next; /* points to array holding "onext" pointers of edges */ 106 | VERTEX_PTR *org; /* points to array holding "orig" pointers of edges */ 107 | double *len; /* points to an array holding the length of edges */ 108 | int *color; /* points to an array holding the color of edges */ 109 | EDGE_PTR *hq; /* heap queue for MST */ 110 | int num_vertices; /* the number of input points */ 111 | int speed; 112 | BOOLEAN plot_dt_construction; 113 | BOOLEAN plot_colorful_dt; 114 | 115 | #define GETCOLOR(e) ((COLOR(e) 0.0 ) 280 | return (TRUE); 281 | else 282 | return(FALSE); 283 | }; 284 | 285 | BOOLEAN leftof(qx,qy,e) 286 | double qx,qy; 287 | EDGE_PTR e; 288 | /* TRUE iff (qx,qy) lies to the left of the edge e */ 289 | /* similar to ccw */ 290 | { 291 | register VERTEX_PTR a,b; 292 | a = dest(e); 293 | b = orig(e); 294 | if ( ((X(a)-qx) * (Y(b)-qy) - (X(b)-qx) * (Y(a)-qy)) < 0.0 ) 295 | return (TRUE); 296 | else 297 | return(FALSE); 298 | }; 299 | 300 | /****************************************************************/ 301 | /* The Merge Procedure. See Guibas & Stolfi. 302 | /****************************************************************/ 303 | #define valid(l) ccw(orig(basel), dest(l), dest(basel)) 304 | 305 | merge_delaunay(ldo, ldi, rdi, rdo) 306 | EDGE_PTR ldi, rdi, *ldo, *rdo; 307 | { 308 | BOOLEAN rvalid, lvalid; 309 | register EDGE_PTR basel,lcand,rcand,t; 310 | int lhue, rhue; 311 | while (TRUE) { 312 | while (ccw(orig(ldi), dest(ldi), orig(rdi))) ldi = lnext(ldi); 313 | if (ccw(dest(rdi), orig(rdi), orig(ldi))) rdi = rprev(rdi); 314 | else break; 315 | } 316 | 317 | basel = connect_left(sym(rdi), ldi, 0); 318 | lcand = rprev(basel); 319 | rcand = oprev(basel); 320 | if (orig(basel) == orig(*rdo)) *rdo = basel; 321 | if (dest(basel) == orig(*ldo)) *ldo = sym(basel); 322 | lhue = 0 ; rhue = 0; 323 | 324 | while (TRUE) { 325 | if (/* valid(lcand) && */ valid(t=onext(lcand))) { 326 | while (incircle(dest(lcand), dest(t), orig(lcand), orig(basel))) { 327 | lhue += COLOR(lcand)+1; 328 | deleteedge(lcand); 329 | lcand = t; 330 | t = onext(lcand); 331 | } 332 | } 333 | if (/* valid(rcand) && */ valid(t=oprev(rcand))) { 334 | while (incircle(dest(t), dest(rcand), orig(rcand), dest(basel))) { 335 | rhue += COLOR(rcand)+1; 336 | deleteedge(rcand); 337 | rcand = t; 338 | t = oprev(rcand); 339 | } 340 | } 341 | 342 | lvalid = valid(lcand); 343 | rvalid = valid(rcand); 344 | if ((! lvalid) && (! rvalid)) return; 345 | 346 | if (!lvalid || (rvalid 347 | && incircle(dest(lcand), orig(lcand), orig(rcand), dest(rcand)))){ 348 | basel = connect_left(rcand, sym(basel), rhue); 349 | rhue = 0; 350 | rcand = lnext(sym(basel)); 351 | } 352 | else { 353 | basel = sym(connect_right(lcand, basel, lhue)); 354 | lhue = 0; 355 | lcand = rprev(basel); 356 | } 357 | } 358 | }; 359 | 360 | /****************************************************************/ 361 | /* Recursive Delaunay Triangulation Procedure. See Guibas & Stolfi. 362 | /* Contains modifications for axis-switching division. See Dwyer. 363 | /****************************************************************/ 364 | 365 | build_delaunay(vp, lo, hi, le, re, rows) 366 | VERTEX_PTR vp[]; 367 | int lo,hi,rows; 368 | EDGE_PTR *le,*re; 369 | { 370 | EDGE_PTR a,b,c,ldo,rdi,ldi,rdo; 371 | int split, lowrows; 372 | register int low, high, maxx, minx; 373 | VERTEX_PTR s1, s2, s3; 374 | 375 | low = lo; high = hi; 376 | 377 | if (low < (high-2)) { 378 | /* more than three elements; do recursion */ 379 | minx = vp[low]; maxx = vp[high]; 380 | if (rows == 1) { /* time to switch axis of division */ 381 | vpsorty(low, high); 382 | rows = 65536; 383 | } 384 | lowrows = rows>>1; 385 | split = low - 1 + (int) (0.5 + 386 | ((double)(high-low+1) * ((double)lowrows / (double)rows))); 387 | build_delaunay(vp, low, split, &ldo, &ldi, lowrows); 388 | build_delaunay(vp, split+1, high, &rdi, &rdo, (rows-lowrows)); 389 | merge_delaunay(&ldo, ldi, rdi, &rdo); 390 | while (orig(ldo) != minx) ldo = rprev(ldo); 391 | while (orig(rdo) != maxx) rdo = lprev(rdo); 392 | *le = ldo; 393 | *re = rdo; 394 | } 395 | else if (low >= (high - 1)) { /* two or one points */ 396 | a = makeedge(vp[low], vp[high], 0); 397 | *le = a; 398 | *re = sym(a); 399 | if (low==high) {fprintf(stderr,"ERROR: Only 1 point!\n"); fflush(stdout);} 400 | } 401 | else { /* (low == (high - 2)) */ /* three points */ 402 | /* 3 cases: triangles of 2 orientations, and 3 points on a line. */ 403 | a = makeedge((s1 = vp[low]), (s2 = vp[low+1]), 0); 404 | b = makeedge(s2, (s3 = vp[high]), 0); 405 | splice(sym(a), b); 406 | c = connect_left(b, a, 0); 407 | if (ccw(s1, s3, s2)) { 408 | *le = sym(c); 409 | *re = c; 410 | } 411 | else { 412 | *le = a; 413 | *re = sym(b); 414 | if (!ccw(s1, s2, s3)) deleteedge(c); /* colinear */ 415 | } 416 | } 417 | }; 418 | 419 | /****************************************************************/ 420 | /* Sorting Routines 421 | /* These are quite a bit faster than the system sort. 422 | /* We always assume the existence of vp[low-1] and vp[high+1]. 423 | /* We tinker with these, then restore them. See J. Bentley, 424 | /* Writing Efficient Code, pp. 113-121. 425 | /****************************************************************/ 426 | vpsortx(low, high) /* Sorts by increasing x-coordinate. */ 427 | int low, high; 428 | { 429 | VERTEX_PTR *lowp, *highp; 430 | double savelowx, savehighx; 431 | lowp = &(vp[low]); highp = &(vp[high]); 432 | savelowx = X(*(lowp-1)); savehighx = X(*(highp+1)); 433 | X(*(lowp-1)) = MINDOUBLE; X(*(highp+1)) = MAXDOUBLE; 434 | vpsortxh(lowp, highp); 435 | X(*(lowp-1)) = savelowx; X(*(highp+1)) = savehighx; 436 | } 437 | 438 | vpsortxh(lowp, highp) 439 | VERTEX_PTR *lowp, *highp; 440 | { 441 | if (highp - lowp <= 5) { /* Bubble Sort */ 442 | register VERTEX_PTR *p, *q, *q1, t; 443 | double maxkey, qkey; 444 | for (p=highp; p>lowp; p--) { 445 | maxkey = X(*lowp); 446 | for (q=lowp+1; q<=p; q++) { 447 | qkey = X(*q); 448 | if (maxkey > qkey) { 449 | q1 = q-1; 450 | t = *q1; *q1 = *q; *q = t; 451 | } 452 | else maxkey = qkey; 453 | } 454 | } 455 | } 456 | else { /* Quicksort */ 457 | double key; 458 | register VERTEX_PTR *hip, *lop, t, *midp; 459 | midp = lowp + (highp - lowp) / 2; 460 | t = *lowp; *lowp = *midp; *midp = t; 461 | key = X(*lowp); 462 | for (lop=lowp; X(*lop) < key; lop++); 463 | for (hip=highp; X(*hip) >= key; hip--); 464 | while (lop <= hip) { 465 | t = *lop; *lop = *hip; *hip = t; 466 | for (lop++; X(*lop) < key; lop++); 467 | for (hip--; X(*hip) >= key; hip--); 468 | } 469 | vpsortxh(lowp, lop-1); 470 | vpsortxh(hip+1, highp); 471 | } 472 | } 473 | 474 | vpsorty(low, high) /* Sorts by DEcreasing y-coordinate. */ 475 | int low, high; 476 | { 477 | VERTEX_PTR *lowp, *highp; 478 | double savelowy, savehighy; 479 | lowp = &(vp[low]); highp = &(vp[high]); 480 | savelowy = Y(*(lowp-1)); savehighy = Y(*(highp+1)); 481 | Y(*(lowp-1)) = MAXDOUBLE; Y(*(highp+1)) = MINDOUBLE; 482 | vpsortyh(lowp, highp); 483 | Y(*(lowp-1)) = savelowy; Y(*(highp+1)) = savehighy; 484 | } 485 | 486 | vpsortyh(lowp, highp) 487 | VERTEX_PTR *lowp, *highp; 488 | { 489 | if (highp - lowp <= 5) { /* Bubble Sort */ 490 | register VERTEX_PTR *p, *q, *q1, t; 491 | double minkey, qkey; 492 | for (p=highp; p>lowp; p--) { 493 | minkey = Y(*lowp); 494 | for (q=lowp+1; q<=p; q++) { 495 | qkey = Y(*q); 496 | if (minkey < qkey) { 497 | q1 = q-1; 498 | t = *q1; *q1 = *q; *q = t; 499 | } 500 | else minkey = qkey; 501 | } 502 | } 503 | } 504 | else { /* Quicksort */ 505 | double key; 506 | register VERTEX_PTR *hip, *lop, t, *midp; 507 | midp = lowp + (highp - lowp) / 2; 508 | t = *lowp; *lowp = *midp; *midp = t; 509 | key = Y(*lowp); 510 | for (lop=lowp; Y(*lop) > key; lop++); 511 | for (hip=highp; Y(*hip) <= key; hip--); 512 | while (lop <= hip) { 513 | t = *lop; *lop = *hip; *hip = t; 514 | for (lop++; Y(*lop) > key; lop++); 515 | for (hip--; Y(*hip) <= key; hip--); 516 | } 517 | vpsortyh(lowp, lop-1); 518 | vpsortyh(hip+1, highp); 519 | } 520 | } 521 | 522 | 523 | /****************************************************************/ 524 | /* Delaunay Triangulation Output Routines 525 | /* See Edelsbrunner, Guibas, & Stolfi 526 | /****************************************************************/ 527 | 528 | /* plots a site on your favorite device. */ 529 | plot_site(v) VERTEX_PTR v; { 530 | #ifdef POSTSCRIPT 531 | printf("p%d drawsite\n", v); 532 | #endif 533 | } 534 | 535 | /* plots a Delaunay-triangulation edge on your favorite device. */ 536 | plot_dt_edge(e) 537 | EDGE_PTR e; 538 | { 539 | #ifdef POSTSCRIPT 540 | printf("p%d p%d drawseg\n", orig(e), dest(e)); 541 | #endif 542 | } 543 | 544 | BOOLEAN forward(e, base) 545 | EDGE_PTR e, base; 546 | { 547 | if (e == base) return(TRUE); 548 | if (e == sym(base)) return(FALSE); 549 | return(X(orig(e)) > X(dest(e))); 550 | } 551 | 552 | 553 | output_colorful_delaunay_triangulation(left, right) 554 | EDGE_PTR left, right; 555 | { 556 | #ifdef GRAPHICS 557 | EDGE_PTR base, e; 558 | VERTEX_PTR u; 559 | int curcolor; 560 | 561 | base = connect_left(sym(left), right, -1); 562 | /* delete_all_retained_segments(); */ 563 | create_retained_segment(3); 564 | for (curcolor=1; curcolor<=MAXCOLOR; curcolor++) { 565 | u = dest(base); 566 | e = sym(dnext(base)); 567 | while (TRUE) { 568 | while ( (e!=base) && !forward(dnext(e),base) ) { 569 | u = dest(e); 570 | e = sym(dnext(e)); 571 | } 572 | if ((e!=base)&&(GETCOLOR(e)==curcolor)) plot_dt_edge(e,COLOR(e)); 573 | while (!forward(onext(e),base)) { 574 | plot_site(u); 575 | if (u == dest(base)) goto nextcolor; 576 | e = sym(onext(e)); 577 | while (forward(dnext(e),base)) e = dnext(e); 578 | u = orig(e); 579 | if (GETCOLOR(e)==curcolor) plot_dt_edge(e,COLOR(e)); 580 | } 581 | e = onext(e); 582 | } 583 | nextcolor: 584 | e=e; 585 | } 586 | close_retained_segment(); 587 | deleteedge(base); 588 | #endif 589 | } 590 | 591 | output_delaunay_triangulation(left, right) 592 | EDGE_PTR left, right; 593 | { 594 | EDGE_PTR base, e; 595 | VERTEX_PTR u; 596 | 597 | base = connect_left(sym(left), right, -1); 598 | u = dest(base); 599 | e = sym(dnext(base)); 600 | while (TRUE) { 601 | while ( (e!=base) && !forward(dnext(e),base) ) { 602 | u = dest(e); 603 | e = sym(dnext(e)); 604 | } 605 | if (e != base) plot_dt_edge(e,COLOR(e)); 606 | while (!forward(onext(e),base)) { 607 | plot_site(u); 608 | if (u == dest(base)) { 609 | deleteedge(base); 610 | return; 611 | } 612 | e = sym(onext(e)); 613 | while (forward(dnext(e),base)) e = dnext(e); 614 | u = orig(e); 615 | plot_dt_edge(e,COLOR(e)); 616 | } 617 | e = onext(e); 618 | } 619 | } 620 | 621 | 622 | 623 | /****************************************************************/ 624 | /* Convex (Affine) Interpolation routine. 625 | /* 626 | /* Input: three vertices of a triangle p1, p2, p3, 627 | /* a fourth point q=(qx,qy). 628 | /* 629 | /* Output: a, b, c such that q = a*p1 + b*p2 + c*p3. 630 | /* If q lies inside the triangle, then a,b,c>=0. 631 | /****************************************************************/ 632 | 633 | convcomb(p1,p2,p3,qx,qy,a,b,c) 634 | VERTEX_PTR p1,p2,p3; 635 | double qx, qy; 636 | double *a, *b, *c; 637 | { 638 | register double x1,x2,y1,y2,det,t; 639 | t = X(p3); 640 | x1 = X(p1) - t; 641 | x2 = X(p2) - t; 642 | qx -= t; 643 | t = Y(p3); 644 | y1 = Y(p1) - t; 645 | y2 = Y(p2) - t; 646 | qy -= t; 647 | 648 | det = (x1*y2 - x2*y1); 649 | *a = (qx*y2 - qy*x2) / det; 650 | *b = (x1*qy - y1*qx) / det; 651 | *c = 1.0 - *a - *b; 652 | /* 653 | fprintf(stderr,"\nq: (%lf,%lf) = \n", qx+X(p3), qy+X(p3)); 654 | fprintf(stderr,"%lf * (%lf,%lf) + ", *a, X(p1), Y(p1)); 655 | fprintf(stderr,"%lf * (%lf,%lf) + ", *b, X(p2), Y(p2)); 656 | fprintf(stderr,"%lf * (%lf,%lf)\n", *c, X(p3), Y(p3)); 657 | fprintf(stderr," (%lf,%lf), (%lf,%lf)\n\n", x1,y1,x2,y2); 658 | */ 659 | } 660 | 661 | /****************************************************************/ 662 | /* Locating a point in the triangulation. See Guibas & Stolfi. 663 | 664 | /* 665 | /* Input: ein, any edge in the triangulation. 666 | /* (x, y), the point to locate. 667 | /* 668 | /* Returns: an edge e of the triangle containing (x,y) such that 669 | /* (x,y) lies to the left of e 670 | /* 671 | /* This routine is intended to work only for points falling 672 | /* inside the convex hull of the triangulation. In case some 673 | /* floating-point anomaly causes it to be called with a point 674 | /* outside, we count the number of edges we've look at to break 675 | /* potential infinite loops. There are always <3*num_vertices 676 | /* edges in the triangulation. (Euler's formula) 677 | /****************************************************************/ 678 | EDGE_PTR locate(ein,x,y) 679 | EDGE_PTR ein; 680 | double x, y; 681 | { 682 | double qx, qy; 683 | register EDGE_PTR e; 684 | register int cnt; 685 | qx=x; qy=y; 686 | cnt = 3*num_vertices; 687 | e = ein; 688 | if (!leftof(qx,qy,e)) e = sym(e); 689 | while (--cnt) { 690 | if (leftof(qx,qy,onext(e))) e = onext(e); 691 | else if (leftof(qx,qy,dprev(e))) e = dprev(e); 692 | else return(e); 693 | } 694 | fprintf(stderr,"locate: breaking loop\n"); 695 | return(ein); 696 | } 697 | 698 | 699 | /****************************************************************/ 700 | /* Voronoi Diagram Routines. 701 | /* DISCARD BELOW IF ONLY DT IS NEEDED 702 | /****************************************************************/ 703 | 704 | #define origv(a) va[orig(a)].v 705 | #define destv(a) va[dest(a)].v 706 | struct VEC2 circle_center(); 707 | struct VEC2 V2_sum(); 708 | struct VEC2 V2_sub(); 709 | struct VEC2 V2_times(); 710 | double V2_cprod(); 711 | struct VEC2 V2_cross(); 712 | double V2_dot(); 713 | double V2_magn(); 714 | 715 | /****************************************************************/ 716 | /* Voronoi Output Routines 717 | /****************************************************************/ 718 | plot_line(v1,v2) 719 | struct VEC2 v1, v2; 720 | { 721 | #ifdef POSTSCRIPT 722 | printf("%f %f %f %f drawline\n", v1.x, v1.y, v2.x, v2.y); 723 | #endif 724 | } 725 | 726 | plot_infinite_vd_edge(e) 727 | EDGE_PTR e; 728 | { 729 | struct VEC2 cvxvec, center; 730 | double ln; 731 | 732 | cvxvec = V2_sub(destv(e), origv(e)); 733 | center = circle_center(origv(e), destv(e), destv(onext(e))); 734 | ln = 1.0 + 735 | V2_magn(V2_sub(center, 736 | V2_times(0.5, V2_sum(origv(e), destv(e))))); 737 | plot_line( 738 | center, 739 | V2_sum(center, V2_times(ln/V2_magn(cvxvec),V2_cross(cvxvec)))); 740 | } 741 | 742 | plot_vd_edge(e) 743 | EDGE_PTR e; 744 | { 745 | if (ccw(orig(e), dest(e), dest(onext(e))) 746 | != ccw(orig(e), dest(e), dest(oprev(e)))) { 747 | plot_line( 748 | circle_center(origv(e),destv(e),destv(onext(e))), 749 | circle_center(origv(e),destv(e),destv(oprev(e)))); 750 | } 751 | } 752 | 753 | struct VEC2 circle_center(a,b,c) 754 | /* returns the center of the circle passing through A, B & C. */ 755 | struct VEC2 a,b,c; 756 | { 757 | struct VEC2 p; 758 | /* if (V2_magn(V2_sub(b,c)) < EPSILON) { */ 759 | if (V2_magn(V2_sub(b,c)) < 0) { 760 | /* then there is no intersection point, the bisectors coincide. */ 761 | return(V2_times(0.5, V2_sum(a,b))); 762 | } 763 | else { 764 | p = V2_sum(V2_times(0.5,V2_sum(a,b)), 765 | V2_times(V2_dot(V2_sub(c,b), V2_sub(c,a)) / 766 | (-2 * V2_cprod(V2_sub(b,a), V2_sub(c,a))), 767 | V2_cross(V2_sub(b,a)))); 768 | return(p); 769 | } 770 | }; 771 | 772 | output_voronoi_diagram(left, right) 773 | EDGE_PTR left, right; 774 | { 775 | EDGE_PTR base, e; 776 | VERTEX_PTR u; 777 | 778 | 779 | /* Plot infinite VD edges. */ 780 | e = left; 781 | do { 782 | plot_infinite_vd_edge(e); 783 | e = onext(sym(e)); 784 | } while (e!=left); 785 | 786 | base = connect_left(sym(left), right, -1); 787 | u = dest(base); 788 | e = sym(dnext(base)); 789 | while (TRUE) { 790 | while ( (e!=base) && !forward(dnext(e),base) ) { 791 | u = dest(e); 792 | e = sym(dnext(e)); 793 | } 794 | if (e != base) plot_vd_edge(e,COLOR(e)); 795 | while (!forward(onext(e),base)) { 796 | plot_site(u); 797 | if (u == dest(base)) { 798 | deleteedge(base); 799 | return; 800 | } 801 | e = sym(onext(e)); 802 | while (forward(dnext(e),base)) e = dnext(e); 803 | u = orig(e); 804 | plot_vd_edge(e,COLOR(e)); 805 | } 806 | e = onext(e); 807 | } 808 | } 809 | 810 | 811 | /****************************************************************/ 812 | /* Vector Routines. 813 | /* From CMU vision library. 814 | /* They are used only for the VD, not the DT. 815 | /* They are slow because of large call-by-value parameters. 816 | /****************************************************************/ 817 | 818 | /* V2_cprod: forms triple scalar product of [u,v,k], where k = u cross v */ 819 | /* (returns the magnitude of u cross v in space)*/ 820 | 821 | double V2_cprod(u,v) 822 | struct VEC2 u,v; 823 | { 824 | return(u.x * v.y - u.y * v.x); 825 | }; 826 | 827 | 828 | /* V2_dot: vector dot product */ 829 | 830 | double V2_dot(u,v) 831 | struct VEC2 u,v; 832 | { 833 | return(u.x * v.x + u.y * v.y); 834 | }; 835 | 836 | /* V2_times: multiply a vector by a scalar */ 837 | 838 | struct VEC2 V2_times(c,v) 839 | double c; 840 | struct VEC2 v; 841 | { 842 | struct VEC2 ans; 843 | ans.x = c * v.x; 844 | ans.y = c * v.y; 845 | return(ans); 846 | } 847 | 848 | /* V2_sum, V2_sub: Vector addition and subtraction */ 849 | 850 | struct VEC2 V2_sum(u,v) 851 | struct VEC2 u,v; 852 | { 853 | struct VEC2 ans; 854 | 855 | ans.x = u.x + v.x; 856 | ans.y = u.y + v.y; 857 | return(ans); 858 | }; 859 | 860 | struct VEC2 V2_sub(u,v) 861 | struct VEC2 u,v; 862 | { 863 | struct VEC2 ans; 864 | ans.x = u.x - v.x; 865 | ans.y = u.y - v.y; 866 | return(ans); 867 | }; 868 | 869 | /* V2_magn: magnitude of vector */ 870 | 871 | double V2_magn(u) 872 | struct VEC2 u; 873 | { 874 | return(sqrt(u.x*u.x+u.y*u.y)); 875 | }; 876 | 877 | /* returns k X v (cross product). this is a vector perpendicular to v */ 878 | 879 | struct VEC2 V2_cross(v) 880 | struct VEC2 v; 881 | { 882 | struct VEC2 ans; 883 | ans.x = v.y; 884 | ans.y = -v.x; 885 | return(ans); 886 | }; 887 | 888 | 889 | #define debug(S,H1,H2) { } 890 | /* 891 | #define debug(S,H1,H2) { \ 892 | fprintf(stderr,S); \ 893 | fprintf(stderr,"(");dumpedge(H1);dumpedge(H2); \ 894 | fprintf(stderr,")\n"); fflush(stderr); \ 895 | } 896 | */ 897 | #define LENGTH(e) (len[e>>2]) 898 | #define RANK(e) orig((e)+1) 899 | #define LSON(e) onext((e)) 900 | #define RSON(e) onext((e)+1) 901 | 902 | /****************************************************************/ 903 | /* Find for Union/Find algorithm. 904 | /****************************************************************/ 905 | 906 | VERTEX_PTR find(vv) 907 | VERTEX_PTR vv; 908 | { 909 | register int /* VERTEX_PTR */ v, vset, t; 910 | t = v = vv; 911 | vset = FATHER(t); 912 | while (vset != t) { 913 | t = vset; 914 | vset = FATHER(vset); 915 | } 916 | while (v != vset) { 917 | t = FATHER(v); 918 | FATHER(v) = vset; 919 | v = t; 920 | } 921 | return(vset); 922 | } 923 | 924 | /****************************************************************/ 925 | /* Heapification. 926 | /* Melds the heaps stacked in the heapification queue hq. 927 | /****************************************************************/ 928 | int hq_last; 929 | 930 | #define init_hq() hq_last = -1 931 | #define insert_hq(e) hq[++hq_last] = (e) 932 | 933 | EDGE_PTR heapify_hq() 934 | { 935 | register int i; 936 | /* fprintf(stderr,"heapify()%d\n", hq_last); fflush(stderr); */ 937 | if (hq_last == -1) return(NYL); /* empty queue */ 938 | while (hq_last > 0) { 939 | i = 0; 940 | while (i < hq_last) { 941 | hq[i] = meld(hq[i], hq[hq_last]); 942 | i++; hq_last--; 943 | } 944 | } 945 | init_hq(); 946 | return(hq[0]); 947 | } 948 | 949 | /****************************************************************/ 950 | /* Heap Melding. 951 | /****************************************************************/ 952 | EDGE_PTR meld(h1, h2) 953 | EDGE_PTR h1,h2; 954 | { 955 | debug("meld",h1,h2); 956 | if (h1==NYL) return(h2); 957 | if (h2==NYL) return(h1); 958 | return(mesh(h1,h2)); 959 | } 960 | 961 | EDGE_PTR mesh(h1,h2) 962 | EDGE_PTR h1,h2; 963 | { 964 | register EDGE_PTR h, rt1, lt1; 965 | debug("mesh",h1,h2); 966 | if (LENGTH(h1) > LENGTH(h2)) { h = h1; h1 = h2; h2 = h; } 967 | 968 | rt1 = RSON(h1); 969 | if (rt1==NYL) rt1 = h2; 970 | else rt1 = mesh(rt1, h2); 971 | 972 | lt1 = LSON(h1); 973 | if (RANK(lt1) < RANK(rt1)) { 974 | LSON(h1) = rt1; rt1 = lt1; /* swap right & left */ 975 | } 976 | 977 | RANK(h1) = 1 + RANK(rt1); 978 | RSON(h1) = rt1; 979 | return(h1); 980 | } 981 | 982 | 983 | /****************************************************************/ 984 | /* Lazy Melding. Creates a dummy heap node. 985 | /****************************************************************/ 986 | EDGE_PTR lazymeld(h1, h2, dummy) 987 | EDGE_PTR h1, h2; 988 | VERTEX_PTR dummy; 989 | { 990 | register EDGE_PTR h; 991 | debug("lazymeld",h1,h2); 992 | if (RANK(h1) < RANK(h2)) { 993 | h = h1; h1 = h2; h2 = h; 994 | } 995 | h = alloc_edge(); 996 | LSON(h) = h1; 997 | RSON(h) = h2; 998 | RANK(h) = 1 + RANK(h2); 999 | orig(h) = dest(h) = dummy; 1000 | return(h); 1001 | } 1002 | 1003 | /****************************************************************/ 1004 | /* Purging a heap. Removes dummy nodes near root. 1005 | /* Puts real nodes on the heapfication queue. 1006 | /****************************************************************/ 1007 | purge(h,v) 1008 | EDGE_PTR h; 1009 | VERTEX_PTR v; 1010 | { 1011 | while ((h != NYL) && (find(dest(h)) == v)) { 1012 | purge(RSON(h), v); 1013 | h = LSON(h); 1014 | } 1015 | if (h != NYL) insert_hq(h); 1016 | /* 1017 | if (h != NYL) { 1018 | if (find(dest(h)) == v) { /* edge is deleted 1019 | purge(LSON(h),v); purge(RSON(h),v); 1020 | } 1021 | else insert_hq(h); 1022 | } 1023 | */ 1024 | } 1025 | 1026 | 1027 | /****************************************************************/ 1028 | /* Top-level MST procedure. 1029 | /****************************************************************/ 1030 | #define findmin(h,v) ((purge((h),(v)), heapify_hq())) 1031 | 1032 | build_min_span_tree(edge, size) 1033 | EDGE_PTR edge; 1034 | VERTEX_PTR size; 1035 | { 1036 | register EDGE_PTR i, j, k, min_edge; 1037 | register VERTEX_PTR vp_last, v; 1038 | double dx, dy; 1039 | /* 1040 | fprintf(stderr,"build_min_span_tree(%d,%d)\n", edge, size); fflush(stderr); 1041 | */ 1042 | 1043 | /* Initialize father and heap pointers for each vertex. */ 1044 | init_hq(); 1045 | for (i = 1; i <= size; i++) { 1046 | COUNT(i) = 0; 1047 | FATHER(i) = i; 1048 | HEAP(i) = NYL; 1049 | } 1050 | 1051 | /* Mark edges on free list to be ignored later. */ 1052 | for (i=avail_edge; i != NYL; i= onext(i)) { 1053 | orig(i) = 0; 1054 | dest(i) = 0; 1055 | } 1056 | avail_edge = NYL; 1057 | 1058 | /* Compute and save length of every DT edge. */ 1059 | for (i=0; i %d (%d,%lf) ", orig(e), dest(e), e,LENGTH(e)); 1118 | if (LENGTH(10)< 0.1) fprintf(stderr,"*****"); 1119 | } 1120 | 1121 | /****************************************************************/ 1122 | /* Driver 1123 | /****************************************************************/ 1124 | main(argc,argv) 1125 | int argc; 1126 | char *argv[]; 1127 | { 1128 | register EDGE_PTR e; 1129 | EDGE_PTR rowe, top, bot, left, right; 1130 | EDGE_PTR edge; 1131 | int i,n,j,k, rows, lo, hi, m, x,y, seed; 1132 | double xx, yy, curmax; 1133 | char prog; 1134 | 1135 | /* Parse command line. */ 1136 | prog = *argv[0]; 1137 | 1138 | if ((prog == 'd') || (prog == 'g')) { 1139 | /* plot_dt_construction = (n<=342) */ 1140 | plot_dt_construction = FALSE; 1141 | plot_colorful_dt = TRUE; 1142 | } 1143 | else { 1144 | plot_dt_construction = FALSE; 1145 | plot_colorful_dt = FALSE; 1146 | } 1147 | 1148 | /* Check reasonableness of n and allocate large arrays. */ 1149 | n=65534; 1150 | va = (struct VERTEX*) malloc((n+2)*sizeof(struct VERTEX)); 1151 | vp = (VERTEX_PTR*) malloc((n+2)*sizeof(VERTEX_PTR)); 1152 | org = (VERTEX_PTR*) malloc(16*n*sizeof(VERTEX_PTR)); 1153 | /* 12 ok except for mst*/ 1154 | len = (double *) malloc(3*n*sizeof(double)); 1155 | color = (int *) len; 1156 | hq = (EDGE_PTR*) malloc(n*sizeof(EDGE_PTR)); 1157 | next = (EDGE_PTR*) malloc(16*n*sizeof(EDGE_PTR)); /* 12 */ 1158 | /* next's is the largest allocation. If any failed, that one did. */ 1159 | if (next == NULL) { fprintf(stderr,"Memory allocation failed.\n"); return;} 1160 | 1161 | 1162 | #ifdef POSTSCRIPT 1163 | printf("%%!\n"); 1164 | printf("%%Title:\n"); 1165 | printf("/drawseg {moveto lineto stroke} def\n"); 1166 | printf("/drawline {moveto lineto stroke} def\n"); 1167 | printf("/drawsite {0.002 0 360 arc fill} def\n"); 1168 | printf("3 72 mul dup scale\n"); 1169 | printf("1.0 1.5 translate\n"); 1170 | printf("0 setlinecap\n"); 1171 | printf("2 setlinejoin\n"); 1172 | printf("0.003 setlinewidth\n"); 1173 | printf("[ ] 0 setdash\n"); 1174 | #endif 1175 | 1176 | /*while (TRUE)*/ { 1177 | 1178 | i=1; 1179 | while (scanf("%lf %lf\n", &(X(i)), &(Y(i)))==2) { 1180 | vp[i] = i; 1181 | NORM(i) = X(i)*X(i) + Y(i)*Y(i); 1182 | i++; 1183 | } 1184 | 1185 | n=i-1; 1186 | vp[0] = 0; vp[n+1] = n+1; /* sentinals for sorting routines */ 1187 | vpsortx(1,n); 1188 | delete_all_edges(); 1189 | if (prog == 'g') 1190 | build_delaunay(vp, 1, n, &left, &right, n); 1191 | else if (prog != 'c') 1192 | build_delaunay(vp, 1, n, &left, &right, 1193 | (int) (0.5 + sqrt((double) n / log((double) n)))); 1194 | #ifdef POSTSCRIPT 1195 | for (i=1; i<=n; i++) { 1196 | printf("/p%0d {%lf %lf} def ", i, X(i), Y(i)); 1197 | plot_site(i); 1198 | #endif 1199 | } 1200 | switch (prog) { 1201 | case 'm': 1202 | output_voronoi_diagram(left, right); 1203 | /* output_delaunay_triangulation(left, right); */ 1204 | build_min_span_tree(left, n); 1205 | break; 1206 | case 'v': 1207 | output_voronoi_diagram(left, right); 1208 | break; 1209 | case 'd': case 'g': 1210 | output_colorful_delaunay_triangulation(left, right); 1211 | break; 1212 | } 1213 | } 1214 | } 1215 | --------------------------------------------------------------------------------