removed old code

This commit is contained in:
zzzzrrr 2010-01-17 11:39:19 -05:00
parent 2c67b8cf39
commit 29bbeb2973
46 changed files with 0 additions and 15394 deletions

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="15Point.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="375"
inkscape:cy="829.15262"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="795"
inkscape:window-height="711"
inkscape:window-x="284"
inkscape:window-y="20">
<inkscape:grid
type="xygrid"
id="grid2383" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 400,472.36218 L 500,392.36218 L 520,272.36218 L 460,232.36218 L 580,212.36218 L 480,152.36218 L 360,172.36218 L 360,52.362183 L 300,112.36218 L 200,32.362183 L 120,92.362183 L 200,72.362183 L 340,272.36218 L 200,212.36218 L 180,352.36218 L 300,312.36218 L 400,472.36218 z"
id="path2385" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,8 +0,0 @@
474.80999000000003 555.15656999999999
474.80999000000003 530.87086999999997
509.09570000000002 530.87086999999997
543.38142000000005 530.87086999999997
543.38142000000005 555.15656999999999
543.38142000000005 579.44227000000001
509.09570000000002 579.44227000000001
474.80999000000003 579.44227000000001

View File

@ -1,275 +0,0 @@
4.57998 4.03402
4.06435 4.06435
3.51839 4.21601
3.09376 4.42832
2.60846 4.57998
2.09284 4.7013
1.51655 4.82263
0.909929 4.94395
0.242648 5.06527
-0.30331 5.0956
-1.15258 5.12594
-1.72887 5.12594
-2.48714 5.12594
-2.85111 5.03494
-3.36674 5.30792
-3.70038 5.52024
-4.15534 5.9752
-4.7013 6.27851
-5.0956 6.61215
-5.73255 6.67281
-6.55149 6.73348
-6.88513 6.61215
-7.46142 6.36951
-7.88605 6.18752
-8.25003 5.91454
-8.64433 5.61123
-8.88698 5.30792
-9.06896 5.00461
-9.25095 4.88329
-9.94856 4.73163
-10.6462 4.64064
-11.1011 4.54965
-11.3741 4.42832
-11.5561 4.21601
-11.0101 4.21601
-10.1305 3.94303
-9.61492 3.73071
-9.15996 3.4274
-8.73532 3.00277
-8.34102 2.6388
-7.97705 2.36582
-7.61308 2.03218
-7.18844 1.45589
-6.79414 1.12225
-6.64248 0.788605
-6.36951 0.242648
-6.24818 -0.212317
-6.00553 -0.515627
-5.73255 -0.818936
-5.24726 -1.2739
-4.7923 -1.60754
-4.42832 -2.00184
-3.67005 -2.21416
-3.18475 -2.39615
-2.5478 -2.69946
-1.91085 -2.79045
-1.06158 -2.88144
-0.333641 -2.88144
0.242648 -2.85111
0.94026 -2.82078
1.2739 -2.85111
1.42556 -3.0331
1.42556 -3.30608
1.33456 -3.57905
1.15258 -4.00369
1.03125 -4.57998
0.849267 -5.15627
0.63695 -5.5809
0.30331 -5.91454
0.060662 -6.15719
-0.333641 -6.27851
-0.697612 -6.27851
-1.15258 -6.36951
-1.57721 -6.39984
-2.09284 -6.52116
-2.36582 -6.79414
-2.48714 -7.06712
-2.18383 -6.97612
-1.85019 -6.79414
-1.42556 -6.76381
-1.15258 -6.79414
-1.36489 -6.88513
-1.69853 -6.97612
-1.97151 -7.12778
-2.12317 -7.37043
-2.27482 -7.64341
-2.39615 -7.91639
-2.36582 -8.21969
-2.03218 -7.85572
-1.81986 -7.7344
-1.57721 -7.67374
-1.36489 -7.49175
-1.21324 -7.40076
-0.849267 -7.2491
-0.60662 -7.12778
-0.242648 -6.91546
0.030331 -6.70315
0.363972 -6.4605
0.242648 -6.61215
0.152837 -6.72007
-0.092855 -6.88818
-0.506653 -7.15974
-0.765276 -7.31491
-1.01097 -7.41836
-1.16614 -7.5606
-1.32132 -7.71577
-1.45063 -7.81922
-1.50235 -8.06492
-1.50235 -8.29768
-1.46356 -8.53044
-1.38597 -8.29768
-1.28252 -8.05199
-1.14028 -7.87095
-0.985106 -7.84509
-0.817001 -7.84509
-0.623033 -7.70284
-0.390272 -7.52181
-0.105787 -7.31491
0.178699 -7.06922
0.489047 -6.84939
0.670083 -6.66835
0.928707 -6.47438
1.16147 -6.33214
1.47182 -6.13817
1.82096 -5.91834
2.04079 -5.84076
2.15717 -5.71144
2.18303 -5.45282
2.06665 -5.28472
1.87268 -5.3623
1.49768 -5.63386
1.22612 -5.81489
1.03216 -5.91834
0.876982 -5.95714
0.954569 -5.80196
1.00629 -5.60799
1.16147 -5.29765
1.3425 -4.9873
1.45888 -4.65109
1.47182 -4.4054
1.73044 -3.95281
1.84682 -3.6166
1.98906 -3.30625
2.14424 -2.95711
2.26062 -2.75021
2.42872 -2.59503
2.63562 -2.50452
2.98476 -2.51745
3.12701 -2.71141
3.06235 -3.09935
2.9589 -3.4097
2.86838 -3.75884
2.79079 -4.12091
2.70028 -4.43126
2.55803 -4.75454
2.48045 -5.03902
2.3382 -5.37523
2.29941 -5.59506
2.23475 -5.90541
2.11837 -6.21576
1.7951 -6.65542
1.39423 -7.05628
1.09681 -7.26318
0.838188 -7.37956
0.41146 -7.49594
-0.002337 -7.62526
-0.416135 -7.7675
-0.687689 -8.05199
-0.907519 -8.40113
-0.70062 -8.19423
-0.312685 -8.05199
-0.015268 -7.89681
0.217493 -7.89681
0.243355 -7.90974
0.023525 -8.1425
-0.157511 -8.25888
-0.403203 -8.43992
-0.648896 -8.75027
-0.778207 -8.90544
-0.881657 -9.18993
-0.80407 -9.60372
-0.597171 -9.177
-0.14458 -8.9701
0.269217 -8.62096
0.695946 -8.28475
1.13561 -8.00026
1.52354 -7.62526
1.82096 -7.26318
1.95027 -7.09508
1.9632 -7.15974
1.66578 -7.58646
1.45888 -7.84509
1.13561 -8.20716
0.760601 -8.65975
0.450253 -8.99596
0.269217 -9.28045
0.126974 -9.65545
0.19163 -10.2761
0.333873 -9.84942
0.63129 -9.68131
0.980431 -9.26751
1.26492 -8.72441
1.60113 -8.31061
1.98906 -7.7675
2.36407 -7.34077
2.79079 -7.00456
3.13994 -6.7718
3.68304 -6.46145
4.14857 -6.33214
4.7434 -6.09938
5.19599 -6.13817
4.85978 -5.87955
4.29081 -5.76317
3.77356 -5.81489
3.34683 -6.07352
2.77786 -6.47438
2.41579 -6.60369
2.41579 -6.28042
2.59683 -5.84076
2.79079 -5.42696
2.99769 -4.90971
3.25632 -4.30195
3.50201 -3.52608
3.83822 -2.63383
4.07098 -2.40107
4.39426 -2.28469
4.79512 -2.23296
4.54943 -2.02606
4.49771 -1.6252
4.54943 -1.50882
4.91151 -1.50882
5.54513 -1.45709
6.12704 -1.39244
6.85118 -1.32778
7.44601 -1.14674
7.85981 -0.78467
7.79516 -0.409667
7.49774 -0.151043
7.84688 0.042924
8.23481 0.314479
8.64861 0.702414
8.70034 1.09035
8.41585 1.42656
8.11843 1.62053
8.3512 2.06019
8.53223 2.38347
8.67447 2.74554
8.66154 3.22399
8.80379 3.87055
8.90724 4.36193
9.1012 4.85332
9.43741 5.40936
9.90293 6.04298
10.3167 6.58609
10.7047 7.3749
10.9374 7.96973
11.1573 8.40939
11.1573 8.84905
10.9374 9.05595
10.6659 9.28871
10.3426 9.37922
9.99345 9.34043
9.63138 8.97836
9.20465 8.48697
8.86844 8.1249
8.50637 7.72404
8.17016 7.28438
7.74343 6.88351
7.43308 6.5473
7.16153 6.1723
6.70894 5.71971
6.20462 5.25418
5.72617 4.80159
5.13134 4.41366
4.87271 4.16797

View File

@ -1,94 +0,0 @@
280.35714 648.79075
286.78571 662.8979
263.28607 661.17871
262.31092 671.41548
250.53571 677.00504
250.53571 683.43361
256.42857 685.21933
297.14286 669.50504
289.28571 649.50504
285 631.6479
285 608.79075
292.85714 585.21932
306.42857 563.79075
323.57143 548.79075
339.28571 545.21932
357.85714 547.36218
375 550.21932
391.42857 568.07647
404.28571 588.79075
413.57143 612.36218
417.14286 628.07647
438.57143 619.1479
438.03572 618.96932
437.5 609.50504
426.96429 609.86218
424.64286 615.57647
419.82143 615.04075
420.35714 605.04075
428.39286 598.43361
437.85714 599.68361
443.57143 613.79075
450.71429 610.21933
431.42857 575.21932
405.71429 550.21932
372.85714 534.50504
349.28571 531.6479
346.42857 521.6479
346.42857 511.6479
350.71429 496.6479
367.85714 476.6479
377.14286 460.93361
385.71429 445.21932
388.57143 404.50504
360 352.36218
337.14286 325.93361
330.71429 334.50504
347.14286 354.50504
337.85714 370.21932
333.57143 359.50504
319.28571 353.07647
312.85714 366.6479
350.71429 387.36218
368.57143 408.07647
375.71429 431.6479
372.14286 454.50504
366.42857 462.36218
352.85714 462.36218
336.42857 456.6479
332.85714 438.79075
338.57143 423.79075
338.57143 411.6479
327.85714 405.93361
320.71429 407.36218
315.71429 423.07647
314.28571 440.21932
325 447.71932
324.82143 460.93361
317.85714 470.57647
304.28571 483.79075
287.14286 491.29075
263.03571 498.61218
251.60714 503.07647
251.25 533.61218
260.71429 533.61218
272.85714 528.43361
286.07143 518.61218
297.32143 508.25504
297.85714 507.36218
298.39286 506.46932
307.14286 496.6479
312.67857 491.6479
317.32143 503.07647
322.5 514.1479
325.53571 521.11218
327.14286 525.75504
326.96429 535.04075
311.78571 540.04075
291.07143 552.71932
274.82143 568.43361
259.10714 592.8979
254.28571 604.50504
251.07143 621.11218
250.53571 649.1479
268.1955 654.36208

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="dude.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective2473"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 526.18109 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="450.63058"
inkscape:cy="608.37669"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:snap-global="false"
inkscape:window-width="1271"
inkscape:window-height="749"
inkscape:window-x="0"
inkscape:window-y="0">
<inkscape:grid
type="xygrid"
id="grid2383" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 320.72342,481.626 L 338.90617,465.96863 L 347.99754,480.61584 L 329.8148,510.41534 L 339.91632,480.11077 L 334.86556,478.09046 L 320.72342,481.626 z"
id="chestHole"
inkscape:label="#path2486" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 325,437.8979 L 320.71429,423.61218 L 329.82143,413.61218 L 332.67857,423.79075 L 325,437.8979 z"
id="headHole"
inkscape:label="#path2498" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="dude">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 280.35714,648.79075 L 286.78571,662.8979 L 263.28607,661.17871 L 262.31092,671.41548 L 250.53571,677.00504 L 250.53571,683.43361 L 256.42857,685.21933 L 297.14286,669.50504 L 289.28571,649.50504 L 285,631.6479 L 285,608.79075 L 292.85714,585.21932 L 306.42857,563.79075 L 323.57143,548.79075 L 339.28571,545.21932 L 357.85714,547.36218 L 375,550.21932 L 391.42857,568.07647 L 404.28571,588.79075 L 413.57143,612.36218 L 417.14286,628.07647 C 438.57143,619.1479 438.03572,618.96932 438.03572,618.96932 L 437.5,609.50504 L 426.96429,609.86218 L 424.64286,615.57647 L 419.82143,615.04075 L 420.35714,605.04075 L 428.39286,598.43361 L 437.85714,599.68361 L 443.57143,613.79075 L 450.71429,610.21933 L 431.42857,575.21932 L 405.71429,550.21932 L 372.85714,534.50504 L 349.28571,531.6479 L 346.42857,521.6479 L 346.42857,511.6479 L 350.71429,496.6479 L 367.85714,476.6479 L 377.14286,460.93361 L 385.71429,445.21932 L 388.57143,404.50504 L 360,352.36218 L 337.14286,325.93361 L 330.71429,334.50504 L 347.14286,354.50504 L 347.14286,374.50504 L 337.85714,370.21932 L 333.57143,359.50504 L 319.28571,353.07647 L 312.85714,366.6479 L 350.71429,387.36218 L 368.57143,408.07647 L 375.71429,431.6479 L 372.14286,454.50504 L 366.42857,462.36218 L 352.85714,462.36218 L 336.42857,456.6479 L 332.85714,438.79075 L 338.57143,423.79075 L 338.57143,411.6479 L 327.85714,405.93361 L 320.71429,407.36218 L 315.71429,423.07647 L 314.28571,440.21932 L 325,447.71932 L 324.82143,460.93361 L 317.85714,470.57647 L 304.28571,483.79075 L 287.14286,491.29075 L 263.03571,498.61218 L 251.60714,503.07647 L 251.25,533.61218 L 260.71429,533.61218 L 272.85714,528.43361 L 286.07143,518.61218 C 286.07143,518.61218 297.32143,508.25504 297.85714,507.36218 C 298.39286,506.46932 307.14286,496.6479 307.14286,496.6479 L 312.67857,491.6479 L 317.32143,503.07647 L 322.5,514.1479 L 325.53571,521.11218 L 327.14286,525.75504 L 326.96429,535.04075 L 311.78571,540.04075 L 291.07143,552.71932 L 274.82143,568.43361 L 259.10714,592.8979 L 254.28571,604.50504 L 251.07143,621.11218 L 250.53571,649.1479 L 268.1955,654.36208 L 280.35714,648.79075 z"
id="path2482"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccscccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1,18 +0,0 @@
0 0
10 7
12 3
20 8
13 17
10 12
12 14
14 9
8 10
6 14
10 15
7 18
0 16
1 13
3 15
5 8
-2 9
5 5

View File

@ -1,12 +0,0 @@
10 0
20 10
30 0
40 10
50 0
50 10
40 20
30 10
20 20
10 10
0 20
0 10

View File

@ -1,4 +0,0 @@
0 0
10 0
10 10
0 10

View File

@ -1,3 +0,0 @@
0 0
1 0
1 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +0,0 @@
350 75
379 161
469 161
397 215
423 301
350 250
277 301
303 215
231 161
321 161

View File

@ -1,16 +0,0 @@
400 472
500 392
520 272
460 232
580 212
480 152
360 172
360 52
300 112
200 32
120 92
200 72
340 272
208 212
180 352
300 312

View File

@ -1,55 +0,0 @@
-37.317758 260.65677
-37.317758 317.44301
-34.741139 339.73612
-27.330761 354.21222
59.525828 406.25724
444.17643 404.87856
538.9604 368.68832
555.15984 355.59089
558.95121 344.90615
559.64054 325.94936
568.19908 315.85885
572.586 322.56108
584.65003 322.31737
568.80837 296.11771
557.59736 289.78104
539.56224 286.49085
443.31476 286.82943
389.89106 280.79772
405.74583 272.00866
412.98388 262.01326
475.5413 262.1856
480.71134 267.01096
514.66123 266.66629
520.34827 262.01326
669.93463 262.01326
670.45162 264.08127
676.91417 263.82277
678.03434 262.09943
680.61936 261.66859
683.03204 255.46455
682.51504 249.94985
677.862 243.91814
668.81445 243.4873
665.28159 247.02016
520.86527 246.33082
514.66123 240.12678
479.67733 239.95444
475.5413 243.57347
412.98388 243.05647
397.64611 227.54636
324.74862 221.16998
323.88695 214.79361
328.36764 211.86392
326.6443 207.03856
300.79412 207.03856
295.62409 211.69159
285.28402 208.2449
272.01426 211.17458
96.577738 209.62357
80.205961 211.86392
58.491817 232.7164
74.863594 254.94755
168.61356 269.25131
175.16228 276.83403
87.271676 260.11758

View File

@ -1,4 +0,0 @@
#!/bin/sh
touch framework/framework.pyx
rm -rf build
python setup.py build_ext -i

View File

@ -1,136 +0,0 @@
##
## GL convenience layer
##
from math import pi as PI
from gl cimport *
include "polydecomp.pxi"
include "seidel.pxi"
cdef extern from 'math.h':
double cos(double)
double sin(double)
double sqrt(double)
SEGMENTS = 25
INCREMENT = 2.0 * PI / SEGMENTS
def init_gl(width, height):
glEnable(GL_LINE_SMOOTH)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glClearColor(0.0, 0.0, 0.0, 0.0)
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST)
def reset_zoom(float zoom, center, size):
zinv = 1.0 / zoom
left = -size[0] * zinv
right = size[0] * zinv
bottom = -size[1] * zinv
top = size[1] * zinv
# Reset viewport
glLoadIdentity()
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# Reset ortho view
glOrtho(left, right, bottom, top, 1, -1)
glTranslatef(-center[0], -center[1], 0)
glMatrixMode(GL_MODELVIEW)
glDisable(GL_DEPTH_TEST)
glLoadIdentity()
# Clear the screen
glClear(GL_COLOR_BUFFER_BIT)
def draw_polygon(verts, color):
r, g, b = color
glColor3f(r, g, b)
glBegin(GL_LINE_LOOP)
for v in verts:
glVertex2f(v[0], v[1])
glEnd()
def draw_line(p1, p2, color):
r, g, b = color
glColor3f(r, g, b)
glBegin(GL_LINES)
glVertex2f(p1[0], p1[1])
glVertex2f(p2[0], p2[1])
glEnd()
##
## Game engine / main loop / UI
##
from glfw cimport *
import sys
# Keyboard callback wrapper
kbd_callback_method = None
cdef extern void __stdcall kbd_callback(int id, int state):
kbd_callback_method(id, state)
cdef class Game:
title = "Poly2Tri"
def __init__(self, window_width, window_height):
glfwInit()
# 16 bit color, no depth, alpha or stencil buffers, windowed
if not glfwOpenWindow(window_width, window_height, 8, 8, 8, 8, 24, 0, GLFW_WINDOW):
glfwTerminate()
raise SystemError('Unable to create GLFW window')
glfwEnable(GLFW_STICKY_KEYS)
glfwSwapInterval(1) #VSync on
def register_kbd_callback(self, f):
global kbd_callback_method
glfwSetKeyCallback(kbd_callback)
kbd_callback_method = f
def main_loop(self):
frame_count = 1
start_time = glfwGetTime()
running = True
while running:
current_time = glfwGetTime()
#Calculate and display FPS (frames per second)
if (current_time - start_time) > 1 or frame_count == 0:
frame_rate = frame_count / (current_time - start_time)
t = self.title + " (%d FPS)" % frame_rate
glfwSetWindowTitle(t)
start_time = current_time
frame_count = 0
frame_count = frame_count + 1
# Check if the ESC key was pressed or the window was closed
running = ((not glfwGetKey(GLFW_KEY_ESC))
and glfwGetWindowParam(GLFW_OPENED))
self.update()
self.render()
glfwSwapBuffers()
glfwTerminate()
property window_title:
def __set__(self, title): self.title = title
property time:
def __get__(self): return glfwGetTime()
def __set__(self, t): glfwSetTime(t)

File diff suppressed because it is too large Load Diff

View File

@ -1,70 +0,0 @@
# GLFW Declarations
# Copyright 2009 Mason Green & Tom Novelli
#
# This file is part of OpenMelee.
#
# OpenMelee is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# OpenMelee is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenMelee. If not, see <http://www.gnu.org/licenses/>.
cdef extern from "GL/glfw.h":
ctypedef enum:
GLFW_WINDOW
GLFW_FULLSCREEN
GLFW_STICKY_KEYS
GLFW_OPENED
GLFW_KEY_ESC
GLFW_PRESS
# Function pointer types
ctypedef void (__stdcall * GLFWwindowsizefun)(int, int)
ctypedef int (__stdcall * GLFWwindowclosefun)()
ctypedef void (__stdcall * GLFWwindowrefreshfun)()
ctypedef void (__stdcall * GLFWmousebuttonfun)(int, int)
ctypedef void (__stdcall * GLFWmouseposfun)(int, int)
ctypedef void (__stdcall * GLFWmousewheelfun)(int)
ctypedef void (__stdcall * GLFWkeyfun)(int, int)
ctypedef void (__stdcall * GLFWcharfun)(int, int)
ctypedef void (__stdcall * GLFWthreadfun)()
int glfwInit()
void glfwTerminate()
void glfwSetWindowTitle(char *title)
int glfwOpenWindow( int width, int height,
int redbits, int greenbits, int bluebits, int alphabits,
int depthbits, int stencilbits, int mode )
void glfwSwapInterval( int interval )
void glfwSwapBuffers()
void glfwEnable( int token )
int glfwGetWindowParam( int param )
int glfwGetKey( int key )
int glfwGetWindowParam( int param )
void glfwGetWindowSize( int *width, int *height )
double glfwGetTime()
void glfwSetTime(double time)
# Input handling
void glfwPollEvents()
void glfwWaitEvents()
int glfwGetKey( int key )
int glfwGetMouseButton( int button )
void glfwGetMousePos( int *xpos, int *ypos )
void glfwSetMousePos( int xpos, int ypos )
int glfwGetMouseWheel()
void glfwSetMouseWheel( int pos )
void glfwSetKeyCallback(GLFWkeyfun cbfun)
void glfwSetCharCallback( GLFWcharfun cbfun )
void glfwSetMouseButtonCallback( GLFWmousebuttonfun cbfun )
void glfwSetMousePosCallback( GLFWmouseposfun cbfun )
void glfwSetMouseWheelCallback( GLFWmousewheelfun cbfun )

View File

@ -1,158 +0,0 @@
##
## Ported from PolyDeomp by Mark Bayazit
## http://mnbayazit.com/406/bayazit
##
from sys import float_info
cdef extern from 'predicates.h':
double orient2d(double *pa, double *pb, double *pc)
def make_ccw(list poly):
cdef int br = 0
# find bottom right point
for i from 1 <= i < len(poly):
if poly[i][1] < poly[br][1] or (poly[i][1] == poly[br][1] and poly[i][0] > poly[br][0]):
br = i
# reverse poly if clockwise
if not left(at(poly, br - 1), at(poly, br), at(poly, br + 1)):
poly.reverse()
cpdef list decompose_poly(list poly, list polys):
cdef list upperInt = [], lowerInt = [], p = [], closestVert = []
cdef float upperDist, lowerDist, d, closestDist
cdef int upper_index, lower_index, closest_index
cdef list lower_poly = [], upper_poly = []
for i from 0 <= i < len(poly):
if is_reflex(poly, i):
upperDist = lowerDist = float_info.max
for j from 0 <= j < len(poly):
if left(at(poly, i - 1), at(poly, i), at(poly, j)) and rightOn(at(poly, i - 1), at(poly, i), at(poly, j - 1)):
# if line intersects with an edge
# find the point of intersection
p = intersection(at(poly, i - 1), at(poly, i), at(poly, j), at(poly, j - 1))
if right(at(poly, i + 1), at(poly, i), p):
# make sure it's inside the poly
d = sqdist(poly[i], p)
if d < lowerDist:
# keep only the closest intersection
lowerDist = d
lowerInt = p
lower_index = j
if left(at(poly, i + 1), at(poly, i), at(poly, j + 1)) and rightOn(at(poly, i + 1), at(poly, i), at(poly, j)):
p = intersection(at(poly, i + 1), at(poly, i), at(poly, j), at(poly, j + 1))
if left(at(poly, i - 1), at(poly, i), p):
d = sqdist(poly[i], p)
if d < upperDist:
upperDist = d
upperInt = p
upper_index = j
# if there are no vertices to connect to, choose a point in the middle
if lower_index == (upper_index + 1) % len(poly):
p[0] = (lowerInt[0] + upperInt[0]) * 0.5
p[1] = (lowerInt[1] + upperInt[1]) * 0.5
if i < upper_index:
lower_poly.extend(poly[i:upper_index+1])
lower_poly.append(p)
upper_poly.append(p)
if lower_index != 0:
upper_poly.extend(poly[lower_index:])
upper_poly.extend(poly[:i+1])
else:
if i != 0:
lower_poly.extend(poly[i:])
lower_poly.extend(poly[:upper_index+1])
lower_poly.append(p)
upper_poly.append(p)
upper_poly.extend(poly[lower_index:i+1])
else:
# connect to the closest point within the triangle
if lower_index > upper_index:
upper_index += len(poly)
closestDist = float_info.max
for j from lower_index <= j <= upper_index:
if leftOn(at(poly, i - 1), at(poly, i), at(poly, j)) and rightOn(at(poly, i + 1), at(poly, i), at(poly, j)):
d = sqdist(at(poly, i), at(poly, j))
if d < closestDist:
closestDist = d
closestVert = at(poly, j)
closest_index = j % len(poly)
if i < closest_index:
lower_poly.extend(poly[i:closest_index+1])
if closest_index != 0:
upper_poly.extend(poly[closest_index:])
upper_poly.extend(poly[:i+1])
else:
if i != 0:
lower_poly.extend(poly[i:])
lower_poly.extend(poly[:closest_index+1])
upper_poly.extend(poly[closest_index:i+1])
# solve smallest poly first
if len(lower_poly) < len(upper_poly):
decompose_poly(lower_poly, polys)
decompose_poly(upper_poly, polys)
else:
decompose_poly(upper_poly, polys)
decompose_poly(lower_poly, polys)
return
polys.append(poly)
cdef list intersection(list p1, list p2, list q1, list q2):
cdef double pqx, pqy, bax, bay, t
pqx = p1[0] - p2[0]
pqy = p1[1] - p2[1]
t = pqy*(q1[0]-p2[0]) - pqx*(q1[1]-p2[1])
t /= pqx*(q2[1]-q1[1]) - pqy*(q2[0]-q1[0])
bax = t*(q2[0]-q1[0]) + q1[0]
bay = t*(q2[1]-q1[1]) + q1[1]
return [bax, bay]
cdef bool eq(float a, float b):
return abs(a - b) <= 1e-8
cdef list at(list v, int i):
return v[i%len(v)]
cdef float area(list a, list b, list c):
return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])))
cdef bool left(list a, list b, list c):
cdef double *x = [a[0], a[1]]
cdef double *y = [b[0], b[1]]
cdef double *z = [c[0], c[1]]
return orient2d(x, y, z) > 0.0
cdef bool leftOn(list a, list b, list c):
cdef double *x = [a[0], a[1]]
cdef double *y = [b[0], b[1]]
cdef double *z = [c[0], c[1]]
return orient2d(x, y, z) >= 0.0
cdef bool right(list a, list b, list c):
cdef double *x = [a[0], a[1]]
cdef double *y = [b[0], b[1]]
cdef double *z = [c[0], c[1]]
return orient2d(x, y, z) < 0.0
cdef bool rightOn(list a, list b, list c):
cdef double *x = [a[0], a[1]]
cdef double *y = [b[0], b[1]]
cdef double *z = [c[0], c[1]]
return orient2d(x, y, z) <= 0.0
cdef float sqdist(list a, list b):
cdef float dx = b[0] - a[0]
cdef float dy = b[1] - a[1]
return dx * dx + dy * dy
cdef bool is_reflex(list poly, int i):
return right(at(poly, i - 1), at(poly, i), at(poly, i + 1))

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
double orient2d(pa, pb, pc);
double *pa;
double *pb;
double *pc;

View File

@ -1,615 +0,0 @@
#
# Poly2Tri
# Copyright (c) 2009, Mason Green
# http://code.google.com/p/poly2tri/
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# self list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# self list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of Poly2Tri nor the names of its contributors may be
# used to endorse or promote products derived from self software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
from random import shuffle
##
## Based on Raimund Seidel'e paper "A simple and fast incremental randomized
## algorithm for computing trapezoidal decompositions and for triangulating polygons"
## (Ported from poly2tri)
##
# Shear transform. May effect numerical robustness
SHEAR = 1e-6
cdef extern from 'math.h':
double atan2(double, double)
cdef extern from 'predicates.h':
double orient2d(double *pa, double *pb, double *pc)
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.next, self.prev = None, None
def __sub__(self, other):
if isinstance(other, Point):
return Point(self.x - other.x, self.y - other.y)
else:
return Point(self.x - other, self.y - other)
def __add__(self, other):
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
else:
return Point(self.x + other, self.y + other)
def __mul__(self, f):
return Point(self.x * f, self.y * f)
def __div__(self, a):
return Point(self.x / a, self.y / a)
def cross(self, p):
return self.x * p.y - self.y * p.x
def dot(self, p):
return self.x * p.x + self.y * p.y
def length(self):
return sqrt(self.x * self.x + self.y * self.y)
def normalize(self):
return self / self.length()
def less(self, p):
return self.x < p.x
def neq(self, other):
return other.x != self.x or other.y != self.y
def clone(self):
return Point(self.x, self.y)
class Edge(object):
def __init__(self, p, q):
self.p = p
self.q = q
self.slope = (q.y - p.y) / (q.x - p.x)
self.b = p.y - (p.x * self.slope)
self.above, self.below = None, None
self.mpoints = []
self.mpoints.append(p)
self.mpoints.append(q)
def is_above(self, point):
cdef double *a = [self.p.x, self.p.y]
cdef double *b = [self.q.x, self.q.y]
cdef double *c = [point.x, point.y]
return orient2d(a, b, c) < 0.0
def is_below(self, point):
cdef double *a = [self.p.x, self.p.y]
cdef double *b = [self.q.x, self.q.y]
cdef double *c = [point.x, point.y]
return orient2d(a, b, c) > 0.0
class Trapezoid(object):
def __init__(self, left_point, right_point, top, bottom):
self.left_point = left_point
self.right_point = right_point
self.top = top
self.bottom = bottom
self.upper_left = None
self.upper_right = None
self.lower_left = None
self.lower_right = None
self.inside = True
self.sink = None
self.key = hash(self)
def update_left(self, ul, ll):
self.upper_left = ul
if ul != None: ul.upper_right = self
self.lower_left = ll
if ll != None: ll.lower_right = self
def update_right(self, ur, lr):
self.upper_right = ur
if ur != None: ur.upper_left = self
self.lower_right = lr
if lr != None: lr.lower_left = self
def update_left_right(self, ul, ll, ur, lr):
self.upper_left = ul
if ul != None: ul.upper_right = self
self.lower_left = ll
if ll != None: ll.lower_right = self
self.upper_right = ur
if ur != None: ur.upper_left = self
self.lower_right = lr
if lr != None: lr.lower_left = self
def trim_neighbors(self):
if self.inside:
self.inside = False
if self.upper_left != None: self.upper_left.trim_neighbors()
if self.lower_left != None: self.lower_left.trim_neighbors()
if self.upper_right != None: self.upper_right.trim_neighbors()
if self.lower_right != None: self.lower_right.trim_neighbors()
def contains(self, point):
return (point.x > self.left_point.x and point.x < self.right_point.x and
self.top.is_above(point) and self.bottom.is_below(point))
def vertices(self):
v1 = line_intersect(self.top, self.left_point.x)
v2 = line_intersect(self.bottom, self.left_point.x)
v3 = line_intersect(self.bottom, self.right_point.x)
v4 = line_intersect(self.top, self.right_point.x)
return v1, v2, v3, v4
def add_points(self):
if self.left_point != self.bottom.p:
self.bottom.mpoints.append(self.left_point.clone())
if self.right_point != self.bottom.q:
self.bottom.mpoints.append(self.right_point.clone())
if self.left_point != self.top.p:
self.top.mpoints.append(self.left_point.clone())
if self.right_point != self.top.q:
self.top.mpoints.append(self.right_point.clone())
def line_intersect(edge, x):
y = edge.slope * x + edge.b
return x, y
class Triangulator(object):
def __init__(self, poly_line):
self.polygons = []
self.trapezoids = []
self.xmono_poly = []
self.edge_list = self.init_edges(poly_line)
self.trapezoidal_map = TrapezoidalMap()
self.bounding_box = self.trapezoidal_map.bounding_box(self.edge_list)
self.query_graph = QueryGraph(isink(self.bounding_box))
self.process()
def triangles(self):
triangles = []
for p in self.polygons:
verts = []
for v in p:
verts.append((v.x, v.y))
triangles.append(verts)
return triangles
def trapezoid_map(self):
return self.trapezoidal_map.map
# Build the trapezoidal map and query graph
def process(self):
for edge in self.edge_list:
traps = self.query_graph.follow_edge(edge)
for t in traps:
# Remove old trapezods
del self.trapezoidal_map.map[t.key]
# Bisect old trapezoids and create new
cp = t.contains(edge.p)
cq = t.contains(edge.q)
if cp and cq:
tlist = self.trapezoidal_map.case1(t, edge)
self.query_graph.case1(t.sink, edge, tlist)
elif cp and not cq:
tlist = self.trapezoidal_map.case2(t, edge)
self.query_graph.case2(t.sink, edge, tlist)
elif not cp and not cq:
tlist = self.trapezoidal_map.case3(t, edge)
self.query_graph.case3(t.sink, edge, tlist)
else:
tlist = self.trapezoidal_map.case4(t, edge)
self.query_graph.case4(t.sink, edge, tlist)
# Add new trapezoids to map
for t in tlist:
self.trapezoidal_map.map[t.key] = t
self.trapezoidal_map.clear()
# Mark outside trapezoids w/ depth-first search
for k, t in self.trapezoidal_map.map.items():
self.mark_outside(t)
# Collect interior trapezoids
for k, t in self.trapezoidal_map.map.items():
if t.inside:
self.trapezoids.append(t)
t.add_points()
# Generate the triangles
self.create_mountains()
def mono_polies(self):
polies = []
for x in self.xmono_poly:
polies.append(x.monoPoly)
return polies
def create_mountains(self):
for edge in self.edge_list:
if len(edge.mpoints) > 2:
mountain = MonotoneMountain()
points = merge_sort(edge.mpoints)
for p in points:
mountain.add(p)
mountain.process()
for t in mountain.triangles:
self.polygons.append(t)
self.xmono_poly.append(mountain)
def mark_outside(self, t):
if t.top is self.bounding_box.top or t.bottom is self.bounding_box.bottom:
t.trim_neighbors()
def init_edges(self, points):
edge_list = []
size = len(points)
for i in range(size):
j = i + 1 if i < size-1 else 0
p = points[i][0], points[i][1]
q = points[j][0], points[j][1]
edge_list.append((p, q))
return self.order_edges(edge_list)
def order_edges(self, edge_list):
edges = []
for e in edge_list:
p = shear_transform(e[0])
q = shear_transform(e[1])
if p.x > q.x:
edges.append(Edge(q, p))
else:
edges.append(Edge(p, q))
# Randomized incremental algorithm
shuffle(edges)
return edges
def shear_transform(point):
return Point(point[0] + SHEAR * point[1], point[1])
def merge_sort(l):
if len(l)>1 :
lleft = merge_sort(l[:len(l)/2])
lright = merge_sort(l[len(l)/2:])
p1, p2, p = 0, 0, 0
while p1<len(lleft) and p2<len(lright):
if lleft[p1].x < lright[p2].x:
l[p]=lleft[p1]
p+=1
p1+=1
else:
l[p]=lright[p2]
p+=1
p2+=1
if p1<len(lleft):l[p:]=lleft[p1:]
elif p2<len(lright):l[p:]=lright[p2:]
else : print "internal error"
return l
class TrapezoidalMap(object):
def __init__(self):
self.map = {}
self.margin = 50.0
self.bcross = None
self.tcross = None
def clear(self):
self.bcross = None
self.tcross = None
def case1(self, t, e):
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, e.q, t.top, e))
trapezoids.append(Trapezoid(e.p, e.q, e, t.bottom))
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, trapezoids[3], None)
trapezoids[2].update_left_right(None, trapezoids[0], None, trapezoids[3])
trapezoids[3].update_right(t.upper_right, t.lower_right)
return trapezoids
def case2(self, t, e):
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, rp, t.top, e))
trapezoids.append(Trapezoid(e.p, rp, e, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, t.upper_right, None)
trapezoids[2].update_left_right(None, trapezoids[0], None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[1]
e.below = trapezoids[2]
return trapezoids
def case3(self, t, e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].update_right(t.upper_right, None)
trapezoids[0].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, t.top, e))
trapezoids[0].update_left_right(t.upper_left, e.above, t.upper_right, None)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].update_right(None, t.lower_right)
trapezoids[1].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, e, t.bottom))
trapezoids[1].update_left_right(e.below, t.lower_left, None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[0]
e.below = trapezoids[1]
return trapezoids
def case4(self, t, e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, t.top, e))
trapezoids[0].update_left(t.upper_left, e.above)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, e, t.bottom))
trapezoids[1].update_left(e.below, t.lower_left)
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[2].update_left_right(trapezoids[0], trapezoids[1], t.upper_right, t.lower_right)
return trapezoids
def bounding_box(self, edges):
margin = self.margin
max = edges[0].p + margin
min = edges[0].q - margin
for e in edges:
if e.p.x > max.x: max = Point(e.p.x + margin, max.y)
if e.p.y > max.y: max = Point(max.x, e.p.y + margin)
if e.q.x > max.x: max = Point(e.q.x + margin, max.y)
if e.q.y > max.y: max = Point(max.x, e.q.y + margin)
if e.p.x < min.x: min = Point(e.p.x - margin, min.y)
if e.p.y < min.y: min = Point(min.x, e.p.y - margin)
if e.q.x < min.x: min = Point(e.q.x - margin, min.y)
if e.q.y < min.y: min = Point(min.x, e.q.y - margin)
top = Edge(Point(min.x, max.y), Point(max.x, max.y))
bottom = Edge(Point(min.x, min.y), Point(max.x, min.y))
left = top.p
right = top.q
trap = Trapezoid(left, right, top, bottom)
self.map[trap.key] = trap
return trap
class Node(object):
def __init__(self, lchild, rchild):
self.parent_list = []
self.lchild = lchild
self.rchild = rchild
if lchild != None:
lchild.parent_list.append(self)
if rchild != None:
rchild.parent_list.append(self)
def replace(self, node):
for parent in node.parent_list:
if parent.lchild is node:
parent.lchild = self
else:
parent.rchild = self
self.parent_list += node.parent_list
class Sink(Node):
def __init__(self, trapezoid):
super(Sink, self).__init__(None, None)
self.trapezoid = trapezoid
trapezoid.sink = self
def locate(self, edge):
return self
def isink(trapezoid):
if trapezoid.sink is None:
return Sink(trapezoid)
return trapezoid.sink
class XNode(Node):
def __init__(self, point, lchild, rchild):
super(XNode, self).__init__(lchild, rchild)
self.point = point
def locate(self, edge):
if edge.p.x >= self.point.x:
return self.rchild.locate(edge)
return self.lchild.locate(edge)
class YNode(Node):
def __init__(self, edge, lchild, rchild):
super(YNode, self).__init__(lchild, rchild)
self.edge = edge
def locate(self, edge):
if self.edge.is_above(edge.p):
return self.rchild.locate(edge)
if self.edge.is_below(edge.p):
return self.lchild.locate(edge)
if self.edge.is_above(edge.q):
return self.rchild.locate(edge)
return self.lchild.locate(edge)
class QueryGraph:
def __init__(self, head):
self.head = head
def locate(self, edge):
return self.head.locate(edge).trapezoid
def follow_edge(self, edge):
trapezoids = [self.locate(edge)]
while(edge.q.x > trapezoids[-1].right_point.x):
if edge.is_above(trapezoids[-1].right_point):
trapezoids.append(trapezoids[-1].upper_right)
else:
trapezoids.append(trapezoids[-1].lower_right)
return trapezoids
def replace(self, sink, node):
if sink.parent_list:
node.replace(sink)
else:
self.head = node
def case1(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[1]), isink(tlist[2]))
qNode = XNode(edge.q, yNode, isink(tlist[3]))
pNode = XNode(edge.p, isink(tlist[0]), qNode)
self.replace(sink, pNode)
def case2(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[1]), isink(tlist[2]))
pNode = XNode(edge.p, isink(tlist[0]), yNode)
self.replace(sink, pNode)
def case3(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[0]), isink(tlist[1]))
self.replace(sink, yNode)
def case4(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[0]), isink(tlist[1]))
qNode = XNode(edge.q, yNode, isink(tlist[2]))
self.replace(sink, qNode)
PI_SLOP = 3.1
class MonotoneMountain:
def __init__(self):
self.size = 0
self.tail = None
self.head = None
self.positive = False
self.convex_points = []
self.mono_poly = []
self.triangles = []
self.convex_polies = []
def add(self, point):
if self.size is 0:
self.head = point
self.size = 1
elif self.size is 1:
if point.neq(self.head):
self.tail = point
self.tail.prev = self.head
self.head.next = self.tail
self.size = 2
else:
if point.neq(self.tail):
self.tail.next = point
point.prev = self.tail
self.tail = point
self.size += 1
def remove(self, point):
next = point.next
prev = point.prev
point.prev.next = next
point.next.prev = prev
self.size -= 1
def process(self):
self.positive = self.angle_sign()
self.gen_mono_poly()
p = self.head.next
while p != self.tail:
a = self.angle(p)
if a >= PI_SLOP or a <= -PI_SLOP or a == 0:
self.remove(p)
elif self.is_convex(p):
self.convex_points.append(p)
p = p.next
self.triangulate()
def triangulate(self):
while self.convex_points:
ear = self.convex_points.pop(0)
a = ear.prev
b = ear
c = ear.next
triangle = (a, b, c)
self.triangles.append(triangle)
self.remove(ear)
if self.valid(a):
self.convex_points.append(a)
if self.valid(c):
self.convex_points.append(c)
#assert self.size <= 3, "Triangulation bug, please report"
def valid(self, p):
return p != self.head and p != self.tail and self.is_convex(p)
def gen_mono_poly(self):
p = self.head
while(p != None):
self.mono_poly.append(p)
p = p.next
def angle(self, p):
a = p.next - p
b = p.prev - p
return atan2(a.cross(b), a.dot(b))
def angle_sign(self):
a = self.head.next - self.head
b = self.tail - self.head
return atan2(a.cross(b), a.dot(b)) >= 0
def is_convex(self, p):
if self.positive != (self.angle(p) >= 0):
return False
return True

View File

@ -1,707 +0,0 @@
#
# Poly2Tri
# Copyright (c) 2009, Mason Green
# http://code.google.com/p/poly2tri/
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# self list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# self list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of Poly2Tri nor the names of its contributors may be
# used to endorse or promote products derived from self software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
from random import shuffle
###
### Based on Raimund Seidel'e paper "A simple and fast incremental randomized
### algorithm for computing trapezoidal decompositions and for triangulating polygons"
### (Ported from poly2tri)
cdef extern from 'math.h':
double cos(double)
double sin(double)
double atan2(double, double)
double floor(double)
double sqrt(double)
class Point:
def __cinit__(self, float x, float y):
self.x = x
self.y = y
def __sub__(self, other):
if isinstance(other, Point):
return Point(self.x - other.x, self.y - other.y)
else:
return Point(self.x - other, self.y - other)
def __add__(self, other):
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
else:
return Point(self.x + other, self.y + other)
def __mul__(self, float f):
return Point(self.x * f, self.y * f)
def __div__(self, float a):
return Point(self.x / a, self.y / a)
def cross(self, p):
return self.x * p.y - self.y * p.x
def dot(self, p):
return self.x * p.x + self.y * p.y
def length(self):
return sqrt(self.x * self.x + self.y * self.y)
def normalize(self):
return self / self.length()
def less(self, p):
return self.x < p.x
def not_equal(self, p):
return not (p.x == self.x and p.y == self.y)
def clone(self):
return Point(self.x, self.y)
cdef class Edge:
cdef object above, below
cdef float slope, b
mpoints = []
def __cinit__(self, p, q):
self.p = p
self.q = q
self.slope = (q.y - p.y)/(q.x - p.x)
self.b = p.y - (p.x * self.slope)
property p:
def __get__(self): return self.p
property q:
def __get__(self): return self.q
property above:
def __get__(self): return self.above
property below:
def __get__(self): return self.below
cdef bool is_above(self, point):
return (floor(point.y) < floor(self.slope * point.x + self.b))
cdef bool is_below(self, point):
return (floor(point.y) > floor(self.slope * point.x + self.b))
cdef float intersect(self, c, d):
cdef float a1, a2, a3, a4, t
cdef Point a, b
a = self.p
b = self.q
a1 = self.signed_area(a, b, d)
a2 = self.signed_area(a, b, c)
if a1 != 0 and a2 != 0 and (a1 * a2) < 0:
a3 = self.signed_area(c, d, a)
a4 = a3 + a2 - a1
if a3 * a4 < 0:
t = a3 / (a3 - a4)
return a + ((b - a) * t)
return 0.0
cdef float signed_area(self, a, b, c):
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
cdef class Trapezoid:
cdef Edge top, bottom
cdef Trapezoid upper_left, lower_left
cdef Trapezoid upper_right, lower_right
cdef object sink
cdef bool inside
def __cinit__(self, left_point, right_point, Edge top, Edge bottom):
self.left_point = left_point
self.right_point = right_point
self.top = top
self.bottom = bottom
self.upper_left = None
self.upper_right = None
self.lower_left = None
self.lower_right = None
self.inside = True
self.sink = None
property inside:
def __get__(self): return self.inside
property top:
def __get__(self): return self.top
property bottom:
def __get__(self): return self.bottom
property left_point:
def __get__(self): return self.left_point
def __set__(self, lp): self.left_point = lp
property right_point:
def __get__(self): return self.right_point
def __set__(self, rp): self.right_point = rp
property sink:
def __get__(self): return self.sink
def __set__(self, object s): self.sink = s
property upper_left:
def __get__(self): return self.upper_left
def __set__(self, Trapezoid other): self.upper_left = other
property upper_right:
def __get__(self): return self.upper_right
def __set__(self, Trapezoid other): self.upper_right = other
property lower_left:
def __get__(self): return self.lower_left
def __set__(self, Trapezoid other): self.lower_left = other
property lower_right:
def __get__(self): return self.lower_right
def __set__(self, Trapezoid other): self.lower_right = other
def update_left(self, Trapezoid ul, Trapezoid ll):
self.upper_left = ul
self.lower_left = ll
if ul != None: ul.upper_right = self
if ll != None: ll.lower_right = self
def update_right(self, Trapezoid ur, Trapezoid lr):
self.upper_right = ur
self.lower_right = lr
if ur != None: ur.upper_left = self
if lr != None: lr.lower_left = self
def update_left_right(self, Trapezoid ul, Trapezoid ll, Trapezoid ur, Trapezoid lr):
self.upper_left = ul
self.lower_left = ll
self.upper_right = ur
self.lower_right = lr
if ul != None: ul.upper_right = self
if ll != None: ll.lower_right = self
if ur != None: ur.upper_left = self
if lr != None: lr.lower_left = self
def trim_neighbors(self):
if self.inside:
self.inside = False
if self.upper_left != None: self.upper_left.trim_neighbors()
if self.lower_left != None: self.lower_left.trim_neighbors()
if self.upper_right != None: self.upper_right.trim_neighbors()
if self.lower_right != None: self.lower_right.trim_neighbors()
def contains(self, point):
return (point.x > self.left_point.x and point.x < self.right_point.x and
self.top.is_above(point) and self.bottom.is_below(point))
def vertices(self):
cdef list verts = []
verts.append(line_intersect(self.top, self.left_point.x))
verts.append(line_intersect(self.bottom, self.left_point.x))
verts.append(line_intersect(self.bottom, self.right_point.x))
verts.append(line_intersect(self.top, self.right_point.x))
return verts
def add_points(self):
if self.left_point != self.bottom.p:
self.bottom.mpoints.append(self.left_point.clone)
if self.right_point != self.bottom.q:
self.bottom.mpoints.append(self.right_point.clone)
if self.left_point != self.top.p:
self.top.mpoints.append(self.left_point.clone)
if self.right_point != self.top.q:
self.top.mpoints.append(self.right_point.clone)
cdef Point line_intersect(Edge e, float x):
cdef float y = e.slope * x + e.b
return Point(x, y)
class Triangulator:
def __init__(self, poly_line):
self.polygons = []
self.edge_list = self.init_edges(poly_line)
self.trapezoids = []
self.trapezoidal_map = TrapezoidalMap()
self.bounding_box = self.trapezoidal_map.bounding_box(self.edge_list)
self.query_graph = QueryGraph(isink(self.bounding_box))
self.xmono_poly = []
self.process()
def trapezoidMap(self):
return self.trapezoidal_map.map
# Build the trapezoidal map and query graph
def process(self):
for e in self.edge_list:
traps = self.query_graph.follow_edge(e)
for t in traps:
try:
self.trapezoidal_map.map.remove(t)
except:
pass
for t in traps:
tlist = []
cp = t.contains(e.p)
cq = t.contains(e.q)
if cp and cq:
tlist = self.trapezoidal_map.case1(t, e)
self.query_graph.case1(t.sink, e, tlist)
elif cp and not cq:
tlist = self.trapezoidal_map.case2(t, e)
self.query_graph.case2(t.sink, e, tlist)
elif not cp and not cq:
tlist = self.trapezoidal_map.case3(t, e)
self.query_graph.case3(t.sink, e, tlist)
else:
tlist = self.trapezoidal_map.case4(t, e)
self.query_graph.case4(t.sink, e, tlist)
# Add new trapezoids to map
for t in tlist:
self.trapezoidal_map.map.append(t)
self.trapezoidal_map.clear()
# Mark outside trapezoids
for t in self.trapezoidal_map.map:
self.mark_outside(t)
# Collect interior trapezoids
for t in self.trapezoidal_map.map:
if t.inside:
self.trapezoids.append(t)
t.add_points()
self.create_mountains()
def mono_polies(self):
polies = []
for x in self.xmono_poly:
polies.append(x.monoPoly)
return polies
def create_mountains(self):
for s in self.edge_list:
if len(s.mpoints) > 0:
mountain = MonotoneMountain()
print s.mpoints
k = merge_sort(s.mpoints)
points = [s.p] + k + [s.q]
for p in points:
mountain.append(p)
mountain.process()
for t in mountain.triangles:
self.polygons.append(t)
self.xmono_poly.append(mountain)
def mark_outside(self, t):
if t.top is self.bounding_box.top or t.bottom is self.bounding_box.bottom:
t.trim_neighbors()
def init_edges(self, points):
edges = []
for i in range(len(points)-1):
p = Point(points[i][0], points[i][1])
q = Point(points[i+1][0], points[i+1][1])
edges.append(Edge(p, q))
p = Point(points[0][0], points[0][1])
q = Point(points[-1][0], points[-1][1])
edges.append(Edge(p, q))
return self.order_edges(edges)
def order_edges(self, edges):
segs = []
for s in edges:
p = self.shearTransform(s.p)
q = self.shearTransform(s.q)
if p.x > q.x:
segs.append(Edge(q, p))
elif p.x < q.x:
segs.append(Edge(p, q))
shuffle(segs)
return segs
def shearTransform(self, point):
return Point(point.x + 1e-4 * point.y, point.y)
cdef list merge_sort(l):
cdef list lleft, lright
cdef int p1, p2, p
if len(l)>1 :
lleft = merge_sort(l[:len(l)/2])
lright = merge_sort(l[len(l)/2:])
p1, p2, p = 0, 0, 0
while p1<len(lleft) and p2<len(lright):
if lleft[p1].x < lright[p2].x:
l[p]=lleft[p1]
p+=1
p1+=1
else:
l[p]=lright[p2]
p+=1
p2+=1
if p1<len(lleft):l[p:]=lleft[p1:]
elif p2<len(lright):l[p:]=lright[p2:]
else : print "internal error"
return l
class TrapezoidalMap:
map = []
margin = 50
bcross = None
tcross = None
def clear(self):
self.bcross = None
self.tcross = None
def case1(self, Trapezoid t, Edge e):
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, e.q, t.top, e))
trapezoids.append(Trapezoid(e.p, e.q, e, t.bottom))
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, trapezoids[3], None)
trapezoids[2].update_left_right(None, trapezoids[0], None, trapezoids[3])
trapezoids[3].update_right(t.upper_right, t.lower_right)
return trapezoids
def case2(self, Trapezoid t, Edge e):
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, rp, t.top, e))
trapezoids.append(Trapezoid(e.p, rp, e, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, t.upper_right, None)
trapezoids[2].update_left_right(None, trapezoids[0], None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[1]
e.below = trapezoids[2]
return trapezoids
def case3(self, Trapezoid t, Edge e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].update_right(t.upper_right, None)
trapezoids[0].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, t.top, e))
trapezoids[0].update_left_right(t.upper_left, e.above, t.upper_right, None)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].update_right(None, t.lower_right)
trapezoids[1].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, e, t.bottom))
trapezoids[1].update_left_right(e.below, t.lower_left, None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[0]
e.below = trapezoids[1]
return trapezoids
def case4(self, Trapezoid t, Edge e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, t.top, e))
trapezoids[0].update_left(t.upper_left, e.above)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, e, t.bottom))
trapezoids[1].update_left(e.below, t.lower_left)
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[2].update_left_right(trapezoids[0], trapezoids[1], t.upper_right, t.lower_right)
return trapezoids
def bounding_box(self, edges):
margin = self.margin
max = edges[0].p + margin
min = edges[0].q - margin
for e in edges:
if e.p.x > max.x: max = Point(e.p.x + margin, max.y)
if e.p.y > max.y: max = Point(max.x, e.p.y + margin)
if e.q.x > max.x: max = Point(e.q.x + margin, max.y)
if e.q.y > max.y: max = Point(max.x, e.q.y + margin)
if e.p.x < min.x: min = Point(e.p.x - margin, min.y)
if e.p.y < min.y: min = Point(min.x, e.p.y - margin)
if e.q.x < min.x: min = Point(e.q.x - margin, min.y)
if e.q.y < min.y: min = Point(min.x, e.q.y - margin)
top = Edge(Point(min.x, max.y), Point(max.x, max.y))
bottom = Edge(Point(min.x, min.y), Point(max.x, min.y))
left = bottom.p
right = top.q
return Trapezoid(left, right, top, bottom)
cdef class Node:
cdef Node left, right
cdef object parent_list
def __init__(self, Node left, Node right):
self.parent_list = []
self.left = left
self.right = right
if left != None:
left.parent_list.append(self)
if right != None:
right.parent_list.append(self)
property left:
def __get__(self): return self.left
def __set__(self, Node left): self.left = left
property right:
def __get__(self): return self.right
def __set__(self, Node right): self.right = right
property parent_list:
def __get__(self): return self.parent_list
def replace(self, Node node):
for parent in node.parent_list:
if parent.left is node:
parent.left = self
else:
parent.right = self
self.parent_list += node.parent_list
cdef class Sink(Node):
cdef Trapezoid trapezoid
def __init__(self, trapezoid):
self.trapezoid = trapezoid
super(Sink, self).__init__(None, None)
trapezoid.sink = self
property trapezoid:
def __get__(self): return self.trapezoid
def locate(self, e):
return self
cdef Sink isink(Trapezoid trapezoid):
if trapezoid.sink != None:
return trapezoid.sink
return Sink(trapezoid)
cdef class XNode(Node):
cdef Point point
def __init__(self, Point point, Node lchild, Node rchild):
super(XNode, self).__init__(lchild, rchild)
self.point = point
def locate(self, Edge e):
if e.p.x >= self.point.x:
return self.right.locate(e)
return self.left.locate(e)
cdef class YNode(Node):
cdef Edge edge
def __init__(self, Edge edge, Node lchild, Node rchild):
super(YNode, self).__init__(lchild, rchild)
self.edge = edge
def locate(self, Edge e):
if self.edge.is_above(e.p):
return self.right.locate(e)
elif self.edge.is_below(e.p):
return self.left.locate(e)
else:
if e.slope < self.edge.slope:
return self.right.locate(e)
else:
return self.left.locate(e)
cdef class QueryGraph:
cdef Node head
def __init__(self, Node head):
self.head = head
def locate(self, Edge e):
return self.head.locate(e).trapezoid
def follow_edge(self, Edge e):
trapezoids = [self.locate(e)]
cdef int j = 0
while(e.q.x > trapezoids[j].right_point.x):
if e.is_above(trapezoids[j].right_point):
trapezoids.append(trapezoids[j].upper_right)
else:
trapezoids.append(trapezoids[j].lower_right)
j += 1
return trapezoids
def replace(self, Sink sink, Node node):
if not sink.parent_list:
self.head = node
else:
node.replace(sink)
def case1(self, Sink sink, Edge e, tlist):
cdef Node yNode = YNode(e, isink(tlist[1]), isink(tlist[2]))
cdef Node qNode = XNode(e.q, yNode, isink(tlist[3]))
cdef Node pNode = XNode(e.p, isink(tlist[0]), qNode)
self.replace(sink, pNode)
def case2(self, Sink sink, Edge e, tlist):
yNode = YNode(e, isink(tlist[1]), isink(tlist[2]))
pNode = XNode(e.p, isink(tlist[0]), yNode)
self.replace(sink, pNode)
def case3(self, Sink sink, Edge e, tlist):
yNode = YNode(e, isink(tlist[0]), isink(tlist[1]))
self.replace(sink, yNode)
def case4(self, Sink sink, Edge e, tlist):
yNode = YNode(e, isink(tlist[0]), isink(tlist[1]))
qNode = XNode(e.q, yNode, isink(tlist[2]))
self.replace(sink, qNode)
cdef float PI_SLOP = 3.1
cdef class MonotoneMountain:
cdef:
Point tail, head
int size
list convex_points
list mono_poly
list triangles
list convex_polies
bool positive
def __init__(self):
self.size = 0
self.tail = None
self.head = None
self.positive = False
self.convex_points = []
self.mono_poly = []
self.triangles = []
self.convex_polies = []
def append(self, Point point):
if self.size == 0:
self.head = point
self.size += 1
elif self.size == 1:
if point.not_equal(self.head):
self.tail = point
self.tail.prev = self.head
self.head.next = self.tail
self.size += 1
else:
if point.not_equal(self.tail):
self.tail.next = point
point.prev = self.tail
self.tail = point
self.size += 1
cdef void remove(self, Point point):
cdef Point next, prev
next = point.next
prev = point.prev
point.prev.next = next
point.next.prev = prev
self.size -= 1
def process(self):
self.positive = self.angle_sign()
self.gen_mono_poly()
p = self.head.next
while p != self.tail:
a = self.angle(p)
if a >= PI_SLOP or a <= -PI_SLOP: self.remove(p)
elif self.is_convex(p): self.convex_points.append(p)
p = p.next
self.triangulate()
cdef void triangulate(self):
while not len(self.convex_points) > 0:
ear = self.convex_points.remove(0)
a = ear.prev
b = ear
c = ear.next
triangle = [a, b, c]
self.triangles.append(triangle)
self.remove(ear)
if self.valid(a): self.convex_points.append(a)
if self.valid(c): self.convex_points.append(c)
assert(self.size <= 3, "Triangulation bug, please report")
cdef bool valid(self, Point p):
return p != self.head and p != self.tail and self.is_convex(p)
cdef void gen_mono_poly(self):
cdef Point p = self.head
while(p != None):
self.mono_poly.append(p)
p = p.next
cdef float angle(self, Point p):
cdef Point a = p.next - p
cdef Point b = p.prev - p
return atan2(a.cross(b), a.dot(b))
cdef float angle_sign(self):
cdef Point a = self.head.next - self.head
cdef Point b = self.tail - self.head
return atan2(a.cross(b), a.dot(b)) >= 0
cdef bool is_convex(self, Point p):
if self.positive != (self.angle(p) >= 0): return False
return True

View File

@ -1,2 +0,0 @@
#!/bin/sh
python -O poly2tri.py

View File

@ -1,177 +0,0 @@
#!/usr/bin/env python2.6
from framework import Game, draw_polygon, reset_zoom, draw_line, decompose_poly, make_ccw, Triangulator
class Poly2Tri(Game):
screen_size = 800.0, 600.0
dude = [[174.50415,494.59368],[215.21844,478.87939],[207.36129,458.87939],[203.07558,441.02225],[203.07558,418.1651],
[210.93272,394.59367],[224.50415,373.1651],[241.64701,358.1651],[257.36129,354.59367],[275.93272,356.73653],
[293.07558,359.59367],[309.50415,377.45082],[322.36129,398.1651],[331.64701,421.73653],[335.21844,437.45082],
[356.64701,428.52225],[356.1113,428.34367],[356.1113,428.34367],[368.78987,419.59368],[349.50415,384.59367],
[323.78987,359.59367],[290.93272,343.87939],[267.36129,341.02225],[264.50415,331.02225],[264.50415,321.02225],
[268.78987,306.02225],[285.93272,286.02225],[295.21844,270.30796],[303.78987,254.59367],[306.64701,213.87939],
[320,202.36218],[265,202.36218],[286.64701,217.45082],[293.78987,241.02225],[285,257.36218],[270.93272,271.73653],
[254.50415,266.02225],[250.93272,248.1651],[256.64701,233.1651],[256.64701,221.02225],[245.93272,215.30796],
[238.78987,216.73653],[233.78987,232.45082],[232.36129,249.59367],[243.07558,257.09367],[242.89701,270.30796],
[235.93272,279.95082],[222.36129,293.1651],[205.21844,300.6651],[185,297.36218],[170,242.36218],[175,327.36218],
[185,322.36218],[195,317.36218],[230.75415,301.02225],[235.39701,312.45082],[240.57558,323.52225],
[243.61129,330.48653],[245.21844,335.12939],[245.03987,344.4151],[229.86129,349.4151],[209.14701,362.09367],
[192.89701,377.80796],[177.18272,402.27225],[172.36129,413.87939],[169.14701,430.48653],[168.61129,458.52225],
[168.61129,492.80796]]
test = [[235.04275999999999, 739.07534999999996], [218.13066000000001, 719.73902999999996],
[218.15215000000001, 709.96821], [218.17362, 700.19740000000002], [243.15215000000001, 685.27858000000003],
[268.13065, 670.35974999999996], [268.13065, 660.81429000000003], [268.13065, 651.26882999999998],
[314.55921999999998, 651.26882999999998], [360.98779000000002, 651.26882999999998],
[360.98683999999997, 666.44740000000002], [360.98046561000007, 669.27438118000009],
[360.96234088000011,672.68539864000013], [360.93345946999995, 676.58895225999993],
[360.89481504000003, 680.89354191999996], [360.84740125000002, 685.50766749999991],
[360.79221175999999, 690.33982888000003], [360.73024022999999, 695.29852593999999],
[360.66248032000004, 700.29225856000005], [360.58992569000003, 705.22952662000012],
[360.51357000000002, 710.01882999999998], [360.04131999999998, 738.41168000000005],
[310.51454999999999, 738.41168000000005], [260.98779999999999, 738.41168000000005],
[260.98779999999999, 748.41168000000005], [260.98779999999999, 758.41168000000005],
[256.47133000000002, 758.41168000000005], [251.95484999999999, 758.41168000000005]]
test2 = [[101.25, 1006.8145], [-0.0, 869.65629999999999], [0.012800000000000001, 630.57820000000004],
[0.025600000000000001, 391.5], [13.7628, 385.74239999999998], [20.536000000000001, 382.96260000000001],
[26.871200000000002, 380.49279999999999],[32.864100000000001, 378.30540000000002],
[38.610700000000001, 376.37279999999998], [44.206600000000002, 374.66730000000001],
[49.747799999999998, 373.16129999999998], [55.329900000000002, 371.82709999999997],
[61.0488, 370.63720000000001],[67.000299999999996, 369.56400000000002], [73.280299999999997, 368.5797],
[77.521299999999997, 368.07459999999998], [82.578500000000005, 367.66539999999998],
[88.263199999999998, 367.35390000000001], [94.386899999999997, 367.14170000000001],
[100.7611, 367.0308], [107.1972, 367.02269999999999], [113.5067, 367.11930000000001],
[119.5009, 367.32229999999998], [124.9913, 367.63339999999999], [129.7894, 368.05450000000002],
[136.77860000000001, 368.94200000000001], [143.9999, 370.10390000000001], [151.3793, 371.52069999999998],
[158.84270000000001, 373.17270000000002], [166.3159, 375.04050000000001], [173.72499999999999, 377.10449999999997],
[180.9957, 379.34500000000003], [188.0539, 381.74250000000001], [194.82570000000001, 384.27749999999997],
[201.23679999999999, 386.93029999999999], [212.5, 391.83980000000003], [212.08760000000001, 550.41989999999998],
[211.67509999999999, 709.0], [274.00200000000001, 709.0], [336.3288, 709.0], [335.66520000000003, 636.25],
[335.55739999999997, 623.91790000000003], [335.45409999999998, 611.09199999999998], [335.3569, 598.0163],
[335.267, 584.93499999999995], [335.18599999999998, 572.09220000000005], [335.11540000000002, 559.73199999999997],
[335.0566, 548.09860000000003], [335.0111, 537.43600000000004], [334.98039999999997, 527.98839999999996],
[334.96589999999998, 520.0], [334.93029999999999, 476.5], [264.0222, 418.0], [193.114, 359.5],
[193.05699999999999, 295.4984], [193.0, 231.49680000000001], [308.7516, 115.7484], [424.50319999999999, 0.0],
[430.71390000000002, -0.0], [436.9246, -0.0], [458.9753, 20.0], [481.02589999999998, 40.0],
[558.38530000000003, 40.0], [635.74469999999997, 40.0], [660.50120000000004, 19.9588],
[685.25779999999997, -0.082400000000000001], [692.0471, 0.20880000000000001],
[698.8365, 0.5], [809.42550000000006, 115.9161], [920.01459999999997, 231.3321], [919.75729999999999, 295.3526],
[919.5, 359.37310000000002], [850.31790000000001, 416.4366], [781.13589999999999, 473.5],
[781.06790000000001, 593.7577], [781.0, 714.0154], [842.25, 713.7577], [903.5, 713.5], [903.5, 552.5], [903.5, 391.5],
[915.5, 386.2894], [925.01319999999998, 382.34390000000002], [934.29579999999999, 378.88010000000003],
[943.42060000000004, 375.8827], [952.46050000000002,373.33629999999999], [961.48839999999996, 371.22570000000002],
[970.57719999999995, 369.53559999999999], [979.79989999999998, 368.25069999999999],
[989.22929999999997, 367.35570000000001], [998.9384, 366.83539999999999], [1009.0, 366.67430000000002],
[1018.876, 366.87939999999998], [1028.7419, 367.4649], [1038.5688, 368.42529999999999],
[1048.3277, 369.75490000000002], [1057.9897000000001, 371.44799999999998], [1067.5259000000001, 373.49900000000002],
[1076.9074000000001, 375.90219999999999], [1086.1052, 378.65210000000002], [1095.0904, 381.74290000000002],
[1103.8340000000001, 385.16899999999998], [1105.4327000000001, 385.83539999999999],
[1107.0215000000001, 386.49689999999998], [1108.5744999999999, 387.1429], [1110.0654999999999, 387.76240000000001],
[1111.4684, 388.34469999999999], [1112.7573, 388.87900000000002], [1113.9059, 389.3544],
[1114.8883000000001, 389.76010000000002], [1115.6784, 390.08530000000002], [1116.25, 390.3193],
[1119.0, 391.43849999999998], [1118.9577999999999, 633.4692], [1118.9155000000001, 875.5],[1016.0895, 1009.5],
[913.26340000000005, 1143.5], [908.63170000000002, 1143.8047999999999], [904.0, 1144.1096], [904.0, 993.0548],
[904.0, 842.0], [842.5, 842.0], [781.0, 842.0], [781.0, 918.5], [781.0, 995.0], [758.5, 995.0],
[753.20910000000003, 995.00739999999996], [748.79459999999995, 995.03269999999998],
[745.18129999999996, 995.08109999999999], [742.29399999999998, 995.15729999999996],
[740.05730000000005, 995.26639999999998], [738.39599999999996, 995.41330000000005],
[737.23490000000004, 995.60289999999998], [736.49869999999999, 995.84010000000001],
[736.11210000000005, 996.13], [736.0, 996.47739999999999], [736.09749999999997, 996.82140000000004],
[736.42740000000003, 997.11329999999998], [737.04610000000002, 997.3587], [738.01009999999997, 997.56290000000001],
[739.37559999999996, 997.73140000000001], [741.19910000000004, 997.86959999999999], [743.53679999999997, 997.9828],
[746.44510000000002, 998.07659999999998], [749.98040000000003,998.15629999999999],
[754.19910000000004, 998.22739999999999], [772.39829999999995, 998.5], [823.72850000000005, 1071.0],
[875.05859999999996, 1143.5], [558.86419999999998, 1143.7516000000001], [507.74619999999999, 1143.787],
[459.17950000000002, 1143.8106], [413.82889999999998, 1143.8226], [372.35939999999999, 1143.8232],
[335.43560000000002, 1143.8130000000001], [303.7226, 1143.7919999999999], [277.88499999999999, 1143.7606000000001],
[258.58789999999999, 1143.7192], [246.49590000000001, 1143.6679999999999], [242.2739, 1143.6072999999999],
[244.95830000000001, 1139.4844000000001], [252.41210000000001, 1128.4439], [263.45999999999998, 1112.2019],
[276.92660000000001, 1092.4740999999999], [291.63650000000001, 1070.9766], [306.41430000000003, 1049.4251999999999],
[320.0847, 1029.5360000000001], [331.47219999999999, 1013.0247000000001], [339.4015, 1001.6074],
[342.69729999999998, 997.0], [342.98259999999999, 996.91470000000004], [343.6619, 996.82510000000002],
[344.70080000000002, 996.7328], [346.06450000000001, 996.63959999999997], [347.71870000000001, 996.54729999999995],
[349.62880000000001, 996.45770000000005], [351.76029999999997, 996.37249999999995],
[354.07859999999999, 996.29349999999999], [356.54919999999998, 996.22249999999997], [359.1377, 996.16110000000003],
[363.04469999999998, 996.07169999999996], [366.26229999999998, 995.97929999999997],
[368.85410000000002, 995.87580000000003], [370.88319999999999, 995.75319999999999],
[372.41320000000002, 995.60320000000002], [373.50729999999999,995.41790000000003],
[374.22899999999998, 995.18920000000003], [374.64159999999998, 994.90880000000004],
[374.80860000000001, 994.56889999999999], [374.79329999999999, 994.16110000000003],
[374.63619999999997, 993.7568], [374.2833, 993.4194], [373.65809999999999, 993.14160000000004],
[372.6841, 992.91570000000002], [371.28500000000003, 992.73419999999999], [369.3843, 992.58960000000002],
[366.90559999999999, 992.47429999999997], [363.77249999999998, 992.38059999999996],
[359.90839999999997, 992.30119999999999], [355.2371, 992.22839999999997], [336.0, 991.95680000000004],
[336.0, 914.97839999999997], [336.0, 838.0], [274.0, 838.0], [212.0, 838.0], [212.0, 991.0], [212.0, 1144.0],
[207.25, 1143.9864], [202.5, 1143.9727]]
def __init__(self):
super(Poly2Tri, self).__init__(*self.screen_size)
# Load point set
file_name = "../data/dude.dat"
self.points = self.load_points(file_name)
# Triangulate
t1 = self.time
seidel = Triangulator(self.points)
dt = (self.time - t1) * 1000.0
self.triangles = seidel.triangles()
#self.trapezoids = seidel.trapezoids
self.trapezoids = seidel.trapezoidal_map.map
self.edges = seidel.edge_list
print "time (ms) = %f , num triangles = %d" % (dt, len(self.triangles))
for p in self.dude:
p[0] -= 75
make_ccw(self.dude)
self.decomp_poly = []
t1 = self.time
decompose_poly(self.dude, self.decomp_poly)
dt = (self.time - t1) * 1000.0
print "time (ms) = %f , num polies = %d" % (dt, len(self.decomp_poly))
self.main_loop()
def update(self):
pass
def render(self):
reset_zoom(2.0, (300, 450), self.screen_size)
red = 255, 0, 0
yellow = 255, 255, 0
green = 0, 255, 0
for t in self.triangles:
draw_polygon(t, red)
for p in self.decomp_poly:
draw_polygon(p, red)
'''
for t in self.trapezoids:
verts = self.trapezoids[t].vertices()
#verts = t.vertices()
draw_polygon(verts, yellow)
'''
for e in self.edges:
p1 = e.p.x, e.p.y
p2 = e.q.x, e.q.y
draw_line(p1, p2, green)
def load_points(self, file_name):
infile = open(file_name, "r")
points = []
while infile:
line = infile.readline()
s = line.split()
if len(s) == 0:
break
points.append((float(s[0]), float(s[1])))
return points
if __name__ == '__main__':
demo = Poly2Tri()

View File

@ -1,627 +0,0 @@
#
# Poly2Tri
# Copyright (c) 2009, Mason Green
# http://code.google.com/p/poly2tri/
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# self list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# self list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of Poly2Tri nor the names of its contributors may be
# used to endorse or promote products derived from self software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
from random import shuffle
from math import atan2
##
## Based on Raimund Seidel'e paper "A simple and fast incremental randomized
## algorithm for computing trapezoidal decompositions and for triangulating polygons"
## (Ported from poly2tri)
##
# Shear transform. May effect numerical robustness
SHEAR = 1e-6
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.next, self.prev = None, None
def __sub__(self, other):
if isinstance(other, Point):
return Point(self.x - other.x, self.y - other.y)
else:
return Point(self.x - other, self.y - other)
def __add__(self, other):
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
else:
return Point(self.x + other, self.y + other)
def __mul__(self, f):
return Point(self.x * f, self.y * f)
def __div__(self, a):
return Point(self.x / a, self.y / a)
def cross(self, p):
return self.x * p.y - self.y * p.x
def dot(self, p):
return self.x * p.x + self.y * p.y
def length(self):
return sqrt(self.x * self.x + self.y * self.y)
def normalize(self):
return self / self.length()
def less(self, p):
return self.x < p.x
def neq(self, other):
return other.x != self.x or other.y != self.y
def clone(self):
return Point(self.x, self.y)
class Edge(object):
def __init__(self, p, q):
self.p = p
self.q = q
self.slope = (q.y - p.y) / (q.x - p.x)
self.b = p.y - (p.x * self.slope)
self.above, self.below = None, None
self.mpoints = []
self.mpoints.append(p)
self.mpoints.append(q)
##
## NOTE Rounding accuracy significantly effects numerical robustness!!!
##
def is_above(self, point):
return (round(point.y, 2) < round(self.slope * point.x + self.b, 2))
def is_below(self, point):
return (round(point.y, 2) > round(self.slope * point.x + self.b, 2))
def intersect(self, c, d):
a = self.p
b = self.q
a1 = self.signed_area(a, b, d)
a2 = self.signed_area(a, b, c)
if a1 != 0.0 and a2 != 0.0 and (a1 * a2) < 0.0:
a3 = self.signed_area(c, d, a)
a4 = a3 + a2 - a1
if a3 * a4 < 0.0:
t = a3 / (a3 - a4)
return a + ((b - a) * t)
return 0.0
def signed_area(self, a, b, c):
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
class Trapezoid(object):
def __init__(self, left_point, right_point, top, bottom):
self.left_point = left_point
self.right_point = right_point
self.top = top
self.bottom = bottom
self.upper_left = None
self.upper_right = None
self.lower_left = None
self.lower_right = None
self.inside = True
self.sink = None
self.key = hash(self)
def update_left(self, ul, ll):
self.upper_left = ul
if ul != None: ul.upper_right = self
self.lower_left = ll
if ll != None: ll.lower_right = self
def update_right(self, ur, lr):
self.upper_right = ur
if ur != None: ur.upper_left = self
self.lower_right = lr
if lr != None: lr.lower_left = self
def update_left_right(self, ul, ll, ur, lr):
self.upper_left = ul
if ul != None: ul.upper_right = self
self.lower_left = ll
if ll != None: ll.lower_right = self
self.upper_right = ur
if ur != None: ur.upper_left = self
self.lower_right = lr
if lr != None: lr.lower_left = self
def trim_neighbors(self):
if self.inside:
self.inside = False
if self.upper_left != None: self.upper_left.trim_neighbors()
if self.lower_left != None: self.lower_left.trim_neighbors()
if self.upper_right != None: self.upper_right.trim_neighbors()
if self.lower_right != None: self.lower_right.trim_neighbors()
def contains(self, point):
return (point.x > self.left_point.x and point.x < self.right_point.x and
self.top.is_above(point) and self.bottom.is_below(point))
def vertices(self):
v1 = line_intersect(self.top, self.left_point.x)
v2 = line_intersect(self.bottom, self.left_point.x)
v3 = line_intersect(self.bottom, self.right_point.x)
v4 = line_intersect(self.top, self.right_point.x)
return v1, v2, v3, v4
def add_points(self):
if self.left_point != self.bottom.p:
self.bottom.mpoints.append(self.left_point.clone())
if self.right_point != self.bottom.q:
self.bottom.mpoints.append(self.right_point.clone())
if self.left_point != self.top.p:
self.top.mpoints.append(self.left_point.clone())
if self.right_point != self.top.q:
self.top.mpoints.append(self.right_point.clone())
def line_intersect(edge, x):
y = edge.slope * x + edge.b
return x, y
class Triangulator(object):
##
## Number of points should be > 3
##
def __init__(self, poly_line):
self.polygons = []
self.trapezoids = []
self.xmono_poly = []
self.edge_list = self.init_edges(poly_line)
self.trapezoidal_map = TrapezoidalMap()
self.bounding_box = self.trapezoidal_map.bounding_box(self.edge_list)
self.query_graph = QueryGraph(isink(self.bounding_box))
self.process()
def triangles(self):
triangles = []
for p in self.polygons:
verts = []
for v in p:
verts.append((v.x, v.y))
triangles.append(verts)
return triangles
def trapezoid_map(self):
return self.trapezoidal_map.map
# Build the trapezoidal map and query graph
def process(self):
for edge in self.edge_list:
traps = self.query_graph.follow_edge(edge)
for t in traps:
# Remove old trapezods
del self.trapezoidal_map.map[t.key]
# Bisect old trapezoids and create new
cp = t.contains(edge.p)
cq = t.contains(edge.q)
if cp and cq:
tlist = self.trapezoidal_map.case1(t, edge)
self.query_graph.case1(t.sink, edge, tlist)
elif cp and not cq:
tlist = self.trapezoidal_map.case2(t, edge)
self.query_graph.case2(t.sink, edge, tlist)
elif not cp and not cq:
tlist = self.trapezoidal_map.case3(t, edge)
self.query_graph.case3(t.sink, edge, tlist)
else:
tlist = self.trapezoidal_map.case4(t, edge)
self.query_graph.case4(t.sink, edge, tlist)
# Add new trapezoids to map
for t in tlist:
self.trapezoidal_map.map[t.key] = t
self.trapezoidal_map.clear()
# Mark outside trapezoids w/ depth-first search
for k, t in self.trapezoidal_map.map.items():
self.mark_outside(t)
# Collect interior trapezoids
for k, t in self.trapezoidal_map.map.items():
if t.inside:
self.trapezoids.append(t)
t.add_points()
# Generate the triangles
self.create_mountains()
def mono_polies(self):
polies = []
for x in self.xmono_poly:
polies.append(x.monoPoly)
return polies
def create_mountains(self):
for edge in self.edge_list:
if len(edge.mpoints) > 2:
mountain = MonotoneMountain()
points = merge_sort(edge.mpoints)
for p in points:
mountain.add(p)
mountain.process()
for t in mountain.triangles:
self.polygons.append(t)
self.xmono_poly.append(mountain)
def mark_outside(self, t):
if t.top is self.bounding_box.top or t.bottom is self.bounding_box.bottom:
t.trim_neighbors()
def init_edges(self, points):
edge_list = []
size = len(points)
for i in range(size):
j = i + 1 if i < size-1 else 0
p = points[i][0], points[i][1]
q = points[j][0], points[j][1]
edge_list.append((p, q))
return self.order_edges(edge_list)
def order_edges(self, edge_list):
edges = []
for e in edge_list:
p = shear_transform(e[0])
q = shear_transform(e[1])
if p.x > q.x:
edges.append(Edge(q, p))
else:
edges.append(Edge(p, q))
# Randomized incremental algorithm
shuffle(edges)
return edges
def shear_transform(point):
return Point(point[0] + SHEAR * point[1], point[1])
def merge_sort(l):
if len(l)>1 :
lleft = merge_sort(l[:len(l)/2])
lright = merge_sort(l[len(l)/2:])
p1, p2, p = 0, 0, 0
while p1<len(lleft) and p2<len(lright):
if lleft[p1].x < lright[p2].x:
l[p]=lleft[p1]
p+=1
p1+=1
else:
l[p]=lright[p2]
p+=1
p2+=1
if p1<len(lleft):l[p:]=lleft[p1:]
elif p2<len(lright):l[p:]=lright[p2:]
else : print "internal error"
return l
class TrapezoidalMap(object):
def __init__(self):
self.map = {}
self.margin = 50.0
self.bcross = None
self.tcross = None
def clear(self):
self.bcross = None
self.tcross = None
def case1(self, t, e):
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, e.q, t.top, e))
trapezoids.append(Trapezoid(e.p, e.q, e, t.bottom))
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, trapezoids[3], None)
trapezoids[2].update_left_right(None, trapezoids[0], None, trapezoids[3])
trapezoids[3].update_right(t.upper_right, t.lower_right)
return trapezoids
def case2(self, t, e):
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
trapezoids.append(Trapezoid(e.p, rp, t.top, e))
trapezoids.append(Trapezoid(e.p, rp, e, t.bottom))
trapezoids[0].update_left(t.upper_left, t.lower_left)
trapezoids[1].update_left_right(trapezoids[0], None, t.upper_right, None)
trapezoids[2].update_left_right(None, trapezoids[0], None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[1]
e.below = trapezoids[2]
return trapezoids
def case3(self, t, e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
rp = e.q if e.q.x == t.right_point.x else t.right_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].update_right(t.upper_right, None)
trapezoids[0].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, t.top, e))
trapezoids[0].update_left_right(t.upper_left, e.above, t.upper_right, None)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].update_right(None, t.lower_right)
trapezoids[1].right_point = rp
else:
trapezoids.append(Trapezoid(lp, rp, e, t.bottom))
trapezoids[1].update_left_right(e.below, t.lower_left, None, t.lower_right)
self.bcross = t.bottom
self.tcross = t.top
e.above = trapezoids[0]
e.below = trapezoids[1]
return trapezoids
def case4(self, t, e):
lp = e.p if e.p.x == t.left_point.x else t.left_point
trapezoids = []
if self.tcross is t.top:
trapezoids.append(t.upper_left)
trapezoids[0].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, t.top, e))
trapezoids[0].update_left(t.upper_left, e.above)
if self.bcross is t.bottom:
trapezoids.append(t.lower_left)
trapezoids[1].right_point = e.q
else:
trapezoids.append(Trapezoid(lp, e.q, e, t.bottom))
trapezoids[1].update_left(e.below, t.lower_left)
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
trapezoids[2].update_left_right(trapezoids[0], trapezoids[1], t.upper_right, t.lower_right)
return trapezoids
def bounding_box(self, edges):
margin = self.margin
max = edges[0].p + margin
min = edges[0].q - margin
for e in edges:
if e.p.x > max.x: max = Point(e.p.x + margin, max.y)
if e.p.y > max.y: max = Point(max.x, e.p.y + margin)
if e.q.x > max.x: max = Point(e.q.x + margin, max.y)
if e.q.y > max.y: max = Point(max.x, e.q.y + margin)
if e.p.x < min.x: min = Point(e.p.x - margin, min.y)
if e.p.y < min.y: min = Point(min.x, e.p.y - margin)
if e.q.x < min.x: min = Point(e.q.x - margin, min.y)
if e.q.y < min.y: min = Point(min.x, e.q.y - margin)
top = Edge(Point(min.x, max.y), Point(max.x, max.y))
bottom = Edge(Point(min.x, min.y), Point(max.x, min.y))
left = top.p
right = top.q
trap = Trapezoid(left, right, top, bottom)
self.map[trap.key] = trap
return trap
class Node(object):
def __init__(self, lchild, rchild):
self.parent_list = []
self.lchild = lchild
self.rchild = rchild
if lchild != None:
lchild.parent_list.append(self)
if rchild != None:
rchild.parent_list.append(self)
def replace(self, node):
for parent in node.parent_list:
if parent.lchild is node:
parent.lchild = self
else:
parent.rchild = self
self.parent_list += node.parent_list
class Sink(Node):
def __init__(self, trapezoid):
super(Sink, self).__init__(None, None)
self.trapezoid = trapezoid
trapezoid.sink = self
def locate(self, edge):
return self
def isink(trapezoid):
if trapezoid.sink is None:
return Sink(trapezoid)
return trapezoid.sink
class XNode(Node):
def __init__(self, point, lchild, rchild):
super(XNode, self).__init__(lchild, rchild)
self.point = point
def locate(self, edge):
if edge.p.x >= self.point.x:
return self.rchild.locate(edge)
return self.lchild.locate(edge)
class YNode(Node):
def __init__(self, edge, lchild, rchild):
super(YNode, self).__init__(lchild, rchild)
self.edge = edge
def locate(self, edge):
if self.edge.is_above(edge.p):
return self.rchild.locate(edge)
if self.edge.is_below(edge.p):
return self.lchild.locate(edge)
if edge.slope < self.edge.slope:
return self.rchild.locate(edge)
return self.lchild.locate(edge)
class QueryGraph:
def __init__(self, head):
self.head = head
def locate(self, edge):
return self.head.locate(edge).trapezoid
def follow_edge(self, edge):
trapezoids = [self.locate(edge)]
while(edge.q.x > trapezoids[-1].right_point.x):
if edge.is_above(trapezoids[-1].right_point):
trapezoids.append(trapezoids[-1].upper_right)
else:
trapezoids.append(trapezoids[-1].lower_right)
return trapezoids
def replace(self, sink, node):
if sink.parent_list:
node.replace(sink)
else:
self.head = node
def case1(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[1]), isink(tlist[2]))
qNode = XNode(edge.q, yNode, isink(tlist[3]))
pNode = XNode(edge.p, isink(tlist[0]), qNode)
self.replace(sink, pNode)
def case2(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[1]), isink(tlist[2]))
pNode = XNode(edge.p, isink(tlist[0]), yNode)
self.replace(sink, pNode)
def case3(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[0]), isink(tlist[1]))
self.replace(sink, yNode)
def case4(self, sink, edge, tlist):
yNode = YNode(edge, isink(tlist[0]), isink(tlist[1]))
qNode = XNode(edge.q, yNode, isink(tlist[2]))
self.replace(sink, qNode)
PI_SLOP = 3.1
class MonotoneMountain:
def __init__(self):
self.size = 0
self.tail = None
self.head = None
self.positive = False
self.convex_points = []
self.mono_poly = []
self.triangles = []
self.convex_polies = []
def add(self, point):
if self.size is 0:
self.head = point
self.size = 1
elif self.size is 1:
if point.neq(self.head):
self.tail = point
self.tail.prev = self.head
self.head.next = self.tail
self.size = 2
else:
if point.neq(self.tail):
self.tail.next = point
point.prev = self.tail
self.tail = point
self.size += 1
def remove(self, point):
next = point.next
prev = point.prev
point.prev.next = next
point.next.prev = prev
self.size -= 1
def process(self):
self.positive = self.angle_sign()
self.gen_mono_poly()
p = self.head.next
while p != self.tail:
a = self.angle(p)
if a >= PI_SLOP or a <= -PI_SLOP or a == 0:
self.remove(p)
elif self.is_convex(p):
self.convex_points.append(p)
p = p.next
self.triangulate()
def triangulate(self):
while self.convex_points:
ear = self.convex_points.pop(0)
a = ear.prev
b = ear
c = ear.next
triangle = (a, b, c)
self.triangles.append(triangle)
self.remove(ear)
if self.valid(a):
self.convex_points.append(a)
if self.valid(c):
self.convex_points.append(c)
#assert self.size <= 3, "Triangulation bug, please report"
def valid(self, p):
return p != self.head and p != self.tail and self.is_convex(p)
def gen_mono_poly(self):
p = self.head
while(p != None):
self.mono_poly.append(p)
p = p.next
def angle(self, p):
a = p.next - p
b = p.prev - p
return atan2(a.cross(b), a.dot(b))
def angle_sign(self):
a = self.head.next - self.head
b = self.tail - self.head
return atan2(a.cross(b), a.dot(b)) >= 0
def is_convex(self, p):
if self.positive != (self.angle(p) >= 0):
return False
return True

View File

@ -1,44 +0,0 @@
import sys
import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
# Usage: python setup.py build_ext --i
version = '0.1'
sourcefiles = ['framework/framework.pyx', 'framework/predicates.c']
# Platform-dependent submodules
if sys.platform == 'win32':
# MS Windows
libs = ['glew32', 'glu32', 'glfw', 'opengl32']
elif sys.platform == 'darwin':
# Apple OSX
raise SystemError('OSX is unsupported in this version')
else:
# GNU/Linux, BSD, etc
libs = ['GLEW', 'GLU', 'glfw', 'GL']
mod_engine = Extension(
"framework",
sourcefiles,
libraries = libs,
language = 'c'
)
setup(
name = 'Poly2Tri',
version = version,
description = 'A 2D Polygon Triangulator',
author = 'Mason Green (zzzzrrr)',
author_email = '',
maintainer = '',
maintainer_email = '',
url = 'http://code.google.com/p/poly2tri/',
cmdclass = {'build_ext': build_ext},
ext_modules = [mod_engine],
)

View File

@ -1,467 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri
import org.newdawn.slick.{BasicGame, GameContainer, Graphics, Color, AppGameContainer}
import org.newdawn.slick.geom.{Polygon, Circle}
import scala.collection.mutable.ArrayBuffer
import scala.io.Source
import seidel.Triangulator
import shapes.{Segment, Point, Triangle}
import earClip.EarClip
import cdt.CDT
// TODO: Lots of documentation!
// : Add Hertel-Mehlhorn algorithm
object Poly2Tri {
def main(args: Array[String]) {
val container = new AppGameContainer(new Poly2TriDemo())
container.setDisplayMode(800,600,false)
container.start()
}
}
class Poly2TriDemo extends BasicGame("Poly2Tri") {
object Algo extends Enumeration {
type Algo = Value
val CDT, Seidel, EarClip = Value
}
import Algo._
// Sedidel Triangulator
var seidel: Triangulator = null
var segments: ArrayBuffer[Segment] = null
var chestSegs: ArrayBuffer[Segment] = null
var headSegs: ArrayBuffer[Segment] = null
// EarClip Triangulator
val earClip = new EarClip
var earClipResults: Array[poly2tri.earClip.Triangle] = null
// Sweep Line Constraied Delauney Triangulator (CDT)
var slCDT: CDT = null
var quit = false
var drawMap = false
var drawSegs = true
var drawCDTMesh = false
val nazcaMonkey = "../data/nazca_monkey.dat"
val nazcaHeron = "../data/nazca_heron_old.dat"
val bird = "../data/bird.dat"
val snake = "../data/i.snake"
val star = "../data/star.dat"
val strange = "../data/strange.dat"
val i18 = "../data/i.18"
val tank = "../data/tank.dat"
val dude = "../data/dude.dat"
val basic = "../data/basic.dat"
var currentModel = dude
var doCDT = true
// The current algorithm
var algo = CDT
var mouseButton = 0
var mousePressed = false
var mouseDrag = false
var mousePos = Point(0, 0)
var mousePosOld = Point(0, 0)
var deltaX = 0f
var deltaY = 0f
var scaleFactor = 0.85f
var gameContainer: GameContainer = null
def init(container: GameContainer) {
gameContainer = container
selectModel(currentModel)
}
def update(gc: GameContainer, delta: Int) {
if(quit) gc exit
}
def render(container: GameContainer, g: Graphics) {
val red = new Color(1f, 0f,0.0f)
val blue = new Color(0f, 0f, 1f)
val green = new Color(0f, 1f, 0f)
val yellow = new Color(1f, 1f, 0f)
g.setColor(green)
g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 522)
g.drawString("'c,s,e' to switch CDT / Seidel / EarClip algos", 10, 537)
g.drawString("'t' to show trapezoidal map (Seidel)", 10, 552)
g.drawString("'m' to show triangle mesh (CDT)", 10, 567)
g.drawString("'d' to draw edges", 10, 582)
g.scale(scaleFactor, scaleFactor)
g.translate(deltaX, deltaY)
algo match {
case Algo.Seidel => {
for(t <- seidel.polygons) {
val poly = new Polygon
t.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(red)
g.draw(poly)
}
if (drawMap) {
for(t <- seidel.trapezoidMap) {
val polygon = new Polygon()
for(v <- t.vertices) {
polygon.addPoint(v.x, v.y)
}
g.setColor(red)
g.draw(polygon)
}/*
for(mp <- seidel.monoPolies) {
val poly = new Polygon
mp.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(yellow)
g.draw(poly)
}*/
}
}
case Algo.EarClip => {
earClipResults.foreach(t => {
val triangle = new Polygon
triangle.addPoint(t.x(0), t.y(0))
triangle.addPoint(t.x(1), t.y(1))
triangle.addPoint(t.x(2), t.y(2))
g.setColor(red)
g.draw(triangle)
})
}
case Algo.CDT => {
val draw = if(drawCDTMesh) slCDT.triangleMesh else slCDT.triangles
draw.foreach( t => {
val triangle = new Polygon
triangle.addPoint(t.points(0).x, t.points(0).y)
triangle.addPoint(t.points(1).x, t.points(1).y)
triangle.addPoint(t.points(2).x, t.points(2).y)
g.setColor(red)
g.draw(triangle)
})
slCDT.debugTriangles.foreach( t => {
val triangle = new Polygon
triangle.addPoint(t.points(0).x, t.points(0).y)
triangle.addPoint(t.points(1).x, t.points(1).y)
triangle.addPoint(t.points(2).x, t.points(2).y)
g.setColor(blue)
g.draw(triangle)
})
for(i <- 0 until slCDT.cList.size) {
val circ = new Circle(slCDT.cList(i).x, slCDT.cList(i).y, 0.5f)
g.setColor(blue); g.draw(circ); g.fill(circ)
}
}
case _ =>
}
if(drawSegs) {
g.setColor(green)
for(i <- 0 until segments.size) {
val s = segments(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
if(currentModel == "../data/dude.dat" && drawSegs) {
g.setColor(green)
for(i <- 0 until chestSegs.size) {
val s = chestSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
for(i <- 0 until headSegs.size) {
val s = headSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
}
/**
* Handle mouseDown events.
* @param p The screen location that the mouse is down at.
*/
override def mousePressed(b: Int, x: Int, y: Int) {
mouseButton = b
mousePressed = true
mousePosOld = mousePos
mousePos = Point(x, y)
// Right click
// Correctly adjust for pan and zoom
if(mouseButton == 1) {
val point = mousePos/scaleFactor + Point(deltaX, deltaY)
println(point)
slCDT.addPoint(point)
slCDT.triangulate
}
}
/**
* Handle mouseUp events.
*/
override def mouseReleased(b: Int, x: Int, y: Int) {
mousePosOld = mousePos
mousePos = Point(x,y)
mousePressed = false
}
/**
* Handle mouseMove events (TestbedMain also sends mouseDragged events here)
* @param p The new mouse location (screen coordinates)
*/
override def mouseMoved(oldX: Int, oldY: Int, x: Int, y: Int) {
mousePosOld = mousePos
mousePos = Point(x,y)
if(mousePressed) {
deltaX += mousePos.x - mousePosOld.x
deltaY += mousePos.y - mousePosOld.y
}
}
override def mouseWheelMoved(notches: Int) {
if (notches < 0) {
scaleFactor = Math.min(300f, scaleFactor * 1.05f);
}
else if (notches > 0) {
scaleFactor = Math.max(.02f, scaleFactor / 1.05f);
}
}
override def keyPressed(key:Int, c:Char) {
// ESC
if(key == 1) quit = true
if(c == '1') selectModel(nazcaMonkey)
if(c == '2') selectModel(bird)
if(c == '3') selectModel(strange)
if(c == '4') selectModel(snake)
if(c == '5') selectModel(star)
if(c == '6') selectModel(i18)
if(c == '7') selectModel(nazcaHeron)
if(c == '8') selectModel(tank)
if(c == '9') selectModel(dude)
if(c == '0') selectModel(basic)
if(c == 'd') drawSegs = !drawSegs
if(c == 'm') drawCDTMesh = !drawCDTMesh
if(c == 't') drawMap = !drawMap
if(c == 's') {algo = Seidel; selectModel(currentModel) }
if(c == 'c') {algo = CDT; selectModel(currentModel) }
if(c == 'e') {algo = EarClip; selectModel(currentModel) }
// Experimental...
if(c == 'r') slCDT.refine
}
def selectModel(model: String) {
model match {
case "../data/nazca_monkey.dat" =>
val clearPoint = Point(418, 282)
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500, clearPoint)
case "../data/bird.dat" =>
val clearPoint = Point(400, 300)
loadModel(bird, 25f, Point(400, 300), 350, clearPoint)
case "../data/i.snake" =>
val clearPoint = Point(336f, 196f)
loadModel(snake, 10f, Point(600, 300), 10, clearPoint)
case "../data/star.dat" =>
val clearPoint = Point(400, 204)
loadModel(star, -1f, Point(0f, 0f), 10, clearPoint)
case "../data/strange.dat" =>
val clearPoint = Point(400, 268)
loadModel(strange, -1f, Point(0f, 0f), 15, clearPoint)
case "../data/i.18" =>
val clearPoint = Point(510, 385)
loadModel(i18, 20f, Point(600f, 500f), 20, clearPoint)
case "../data/nazca_heron_old.dat" =>
val clearPoint = Point(85, 290)
loadModel(nazcaHeron, 4.2f, Point(400f, 300f), 1500, clearPoint)
case "../data/tank.dat" =>
val clearPoint = Point(450, 350)
loadModel(tank, -1f, Point(100f, 0f), 10, clearPoint)
case "../data/dude.dat" =>
val clearPoint = Point(365, 427)
loadModel(dude, -1f, Point(100f, -200f), 10, clearPoint)
case "../data/basic.dat" =>
val clearPoint = Point(500, 450)
loadModel(basic, -5f, Point(500, 450), 10, clearPoint)
case _ =>
}
currentModel = model
}
def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int, clearPoint: Point) {
println
println("************** " + model + " **************")
val polyX = new ArrayBuffer[Float]
val polyY = new ArrayBuffer[Float]
var points = new ArrayBuffer[Point]
val angle = Math.Pi
for (line <- Source.fromFile(model).getLines) {
val s = line.replaceAll("\n", "")
val tokens = s.split("[ ]+")
if(tokens.size == 2) {
var x = tokens(0).toFloat
var y = tokens(1).toFloat
// Transform the shape
polyX += (Math.cos(angle)*x - Math.sin(angle)*y).toFloat * scale + center.x
polyY += (Math.sin(angle)*x + Math.cos(angle)*y).toFloat * scale + center.y
points += new Point(polyX.last, polyY.last)
} else {
throw new Exception("Bad input file")
}
}
segments = new ArrayBuffer[Segment]
for(i <- 0 until points.size-1)
segments += new Segment(points(i), points(i+1))
segments += new Segment(points.first, points.last)
println("Number of points = " + polyX.size)
println
algo match {
case Algo.CDT => {
val pts = points.toArray
val t1 = System.nanoTime
slCDT = new CDT(pts, clearPoint)
// Add some holes....
if(model == "../data/dude.dat") {
val headHole = Array(Point(325f,437f), Point(320f,423f), Point(329f,413f), Point(332f,423f))
val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f),
Point(347.99754f,480.61584f), Point(329.8148f,510.41534f),
Point(339.91632f,480.11077f), Point(334.86556f,478.09046f))
// Tramsform the points
for(i <- 0 until headHole.size) {
val hx = -headHole(i).x*scale + center.x
val hy = -headHole(i).y*scale + center.y
headHole(i) = Point(hx, hy)
}
for(i <- 0 until chestHole.size) {
val cx = -chestHole(i).x*scale + center.x
val cy = -chestHole(i).y*scale + center.y
chestHole(i) = Point(cx, cy)
}
chestSegs = new ArrayBuffer[Segment]
for(i <- 0 until chestHole.size-1)
chestSegs += new Segment(chestHole(i), chestHole(i+1))
chestSegs += new Segment(chestHole.first, chestHole.last)
headSegs = new ArrayBuffer[Segment]
for(i <- 0 until headHole.size-1)
headSegs += new Segment(headHole(i), headHole(i+1))
headSegs += new Segment(headHole.first, headHole.last)
// Add the holes
slCDT.addHole(headHole)
slCDT.addHole(chestHole)
}
slCDT triangulate
val runTime = System.nanoTime - t1
println("CDT average (ms) = " + runTime*1e-6)
println("Number of triangles = " + slCDT.triangles.size)
println
}
case Algo.Seidel => {
// Sediel triangulation
val t1 = System.nanoTime
seidel = new Triangulator(points)
val runTime = System.nanoTime - t1
println("Seidel average (ms) = " + runTime*1e-6)
println("Number of triangles = " + seidel.polygons.size)
}
case Algo.EarClip => {
// Earclip
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
var xVerts = polyX.toArray
var yVerts = polyY.toArray
val xv = if(currentModel != "../data/strange.dat") xVerts.reverse.toArray else xVerts
val yv = if(currentModel != "../data/strange.dat") yVerts.reverse.toArray else yVerts
val t1 = System.nanoTime
earClip.triangulatePolygon(xv, yv, xVerts.size, earClipResults)
val runTime = System.nanoTime - t1
println
println("Earclip average (ms) = " + runTime*1e-6)
println("Number of triangles = " + earClip.numTriangles)
}
case _ =>
}
}
}

View File

@ -1,165 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.cdt
import scala.collection.mutable.ArrayBuffer
import shapes.{Point, Triangle, Segment}
// Advancing front
class AFront(iTriangle: Triangle) {
// Doubly linked list
var head = new Node(iTriangle.points(1), iTriangle)
val middle = new Node(iTriangle.points(0), iTriangle)
var tail = new Node(iTriangle.points(2), null)
head.next = middle
middle.next = tail
middle.prev = head
tail.prev = middle
// TODO: Use Red-Black Tree or Interval Tree for better search performance!
def locate(point: Point): Node = {
var node = head
while(node != tail) {
if(point.x >= node.point.x && point.x < node.next.point.x)
return node
node = node.next
}
null
}
// Locate node containing given point
def locatePoint(point: Point): Node = {
var node = head
while(node != null) {
if(point == node.point)
return node
node = node.next
}
null
}
def insert(point: Point, triangle: Triangle, nNode: Node) = {
val node = new Node(point, triangle)
nNode.triangle = triangle
nNode.next.prev = node
node.next = nNode.next
node.prev = nNode
nNode.next = node
node
}
def insertLegalized(point: Point, triangle: Triangle, nNode: Node) = {
val node = new Node(triangle.points(1), triangle)
val rNode = nNode.next
rNode.prev = node
node.next = rNode
nNode.next = node
node.prev = nNode
node
}
// Update advancing front with constrained edge triangles
def constrainedEdge(sNode: Node, eNode: Node, T1: ArrayBuffer[Triangle],
T2: ArrayBuffer[Triangle], edge: Segment) {
var node = sNode.prev
val point1 = edge.q
val point2 = edge.p
// Scan the advancing front and update Node triangle pointers
// TODO: Make this more efficient
while(node != null && node != eNode.next) {
T2.foreach(t => {
if(t.contains(node.point, node.next.point))
node.triangle = t
})
T1.foreach(t => {
if(t.contains(node.point, node.next.point))
node.triangle = t
})
node = node.next
}
}
// Transition from AFront traversal to interior mesh traversal
def aboveEdge(first: Node, pNode: Node, last: Triangle,
point2: Point, ahead:Boolean): Node =
if(ahead) {
val n = new Node(point2, pNode.prev.triangle)
link (first, n, last)
n.next = pNode
pNode.prev = n
n
} else {
val n = new Node(point2, last)
link (n, first, last)
pNode.next = n
n.prev = pNode
pNode
}
def -=(tuple: Tuple3[Node, Node, Triangle]) {
val (node, kNode, triangle) = tuple
kNode.next.prev = node
node.next = kNode.next
node.triangle = triangle
}
def link(node1: Node, node2: Node, t: Triangle) {
node1.next = node2
node2.prev = node1
node1.triangle = t
}
// NOT IMPLEMENTED
def basin(node: Node) {
if(node.next != tail) {
val p1 = node.point
val p2 = node.next.point
val slope = (p1.y - p2.y) / (p1.x - p2.x)
if(slope < Math.Pi*3/4)
println("basin slope = " + slope)
}
}
}
// Advancing front node
class Node(val point: Point, var triangle: Triangle) {
var next: Node = null
var prev: Node = null
}

View File

@ -1,549 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.cdt
import scala.collection.mutable.ArrayBuffer
import shapes.{Segment, Point, Triangle}
import utils.Util
/**
* Sweep-line, Constrained Delauney Triangulation (CDT)
* See: Domiter, V. and Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
* International Journal of Geographical Information Science
*/
// clearPoint is any interior point inside the polygon
class CDT(polyLine: Array[Point], clearPoint: Point) {
// Triangle list
def triangles = mesh.triangles
def triangleMesh = mesh.map
def debugTriangles = mesh.debug
val cList = new ArrayBuffer[Point]
var refined = false
// Initialize edges
initEdges(polyLine)
// Add a hole
def addHole(holePolyLine: Array[Point]) {
initEdges(holePolyLine)
points = points ++ holePolyLine.toList
}
// Add an internal point
// Good for manually refining the mesh
def addPoint(point: Point) {
points = point :: points
}
// Triangulate simple polygon with holes
def triangulate {
mesh.map.clear
mesh.triangles.clear
mesh.debug.clear
var xmax, xmin = points.first.x
var ymax, ymin = points.first.y
// Calculate bounds
for(i <- 0 until points.size) {
val p = points(i)
if(p.x > xmax) xmax = p.x
if(p.x < xmin) xmin = p.x
if(p.y > ymax) ymax = p.y
if(p.y < ymin) ymin = p.y
}
val deltaX = ALPHA * (xmax - xmin)
val deltaY = ALPHA * (ymax - ymin)
val p1 = Point(xmin - deltaX, ymin - deltaY)
val p2 = Point(xmax + deltaX, p1.y)
// Sort the points along y-axis
points = pointSort
// Initial triangle
val iTriangle = new Triangle(Array(points(0), p1, p2))
mesh.map += iTriangle
aFront = new AFront(iTriangle)
// Sweep points; build mesh
sweep
// Finalize triangulation
finalization
}
// Create edges and connect end points; update edge event pointer
private def initEdges(pts: Array[Point]) {
// Connect pts
for(i <- 0 until pts.size-1) {
val endPoints = validatePoints(pts(i), pts(i+1))
val edge = new Segment(endPoints(0), endPoints(1))
endPoints(1).edges += edge
}
// Connect endpoints
val endPoints = validatePoints(pts.first, pts.last)
val edge = new Segment(endPoints(0), endPoints(1))
endPoints(1).edges += edge
}
private def validatePoints(p1: Point, p2: Point): List[Point] = {
if(p1.y > p2.y) {
// For CDT we want q to be the point with > y
return List(p2, p1)
} else if(p1.y == p2.y) {
// If y values are equal, make sure point with smaller x value
// is to the left
if(p1.x > p2.x) {
return List(p2, p1)
} else if(p1.x == p2.x) {
throw new Exception("Duplicate point")
}
}
List(p1, p2)
}
// Merge sort: O(n log n)
private def pointSort: List[Point] =
Util.msort((p1: Point, p2: Point) => p1 > p2)(points)
// Implement sweep-line
private def sweep {
val size = if(refined) 1 else points.size
for(i <- 1 until points.size) {
val point = points(i)
// Process Point event
val node = pointEvent(point)
// Process edge events
point.edges.foreach(e => edgeEvent(e, node))
}
}
// Final step in the sweep-line CDT
// Clean exterior triangles
private def finalization {
var found = false
mesh.map.foreach(m => {
if(!found)
// Mark the originating clean triangle
if(m.pointIn(clearPoint)) {
found = true
cleanTri = m
}
m.markNeighborEdges
})
// Collect interior triangles constrained by edges
mesh clean cleanTri
}
// Delauney Refinement: Refine triangules using Steiner points
// Probably overkill for 2D games, and will create a large number of
// triangles that are probably unoptimal for a physics engine like
// Box2D.... Better to manually enter interior points for mesh "smoothing"
// TODO: Finish implementation... Maybe!
def refine {
cList.clear
mesh.triangles.foreach(t => {
if(t.thin) {
val center = Util.circumcenter(t.points(0), t.points(1), t.points(2))
cList += center
addPoint(center)
}
})
// Retriangulate
if(cList.size > 0)
triangulate
}
// Point event
private def pointEvent(point: Point): Node = {
val node = aFront.locate(point)
// Projected point hits advancing front; create new triangle
val pts = Array(point, node.point, node.next.point)
val triangle = new Triangle(pts)
mesh.map += triangle
// Legalize
val newNode = legalization(triangle, node)
// Fill in adjacent triangles if required
scanAFront(newNode)
newNode
}
// EdgeEvent
private def edgeEvent(edge: Segment, node: Node) {
// Locate the first intersected triangle
val firstTriangle = if(!node.triangle.contains(edge.q))
node.triangle
else
node.triangle.locateFirst(edge)
if(firstTriangle != null && !firstTriangle.contains(edge)) {
// Interior mesh traversal - edge is "burried" in the mesh
// Constrained edge lies below the advancing front. Traverse through intersected triangles,
// form empty pseudo-polygons, and re-triangulate
// Collect intersected triangles
val tList = new ArrayBuffer[Triangle]
tList += firstTriangle
while(tList.last != null && !tList.last.contains(edge.p))
tList += tList.last.findNeighbor(edge.p)
// TODO: Finish implementing edge insertion which combines advancing front (AF)
// and triangle traversal respectively. See figure 14(a) from Domiter et al.
// Should only occur with complex patterns of interior points
// Already added provision for transitioning from AFront traversal to
// interior mesh traversal - may need to add the opposite case
if(tList.last == null)
throw new Exception("Not implemented yet - interior points too complex")
// Neighbor triangles
// HashMap or set may improve performance
val nTriangles = new ArrayBuffer[Triangle]
// Remove old triangles; collect neighbor triangles
// Keep duplicates out
tList.foreach(t => {
t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n)
mesh.map -= t
})
// Using a hashMap or set may improve performance
val lPoints = new ArrayBuffer[Point]
val rPoints = new ArrayBuffer[Point]
// Collect points left and right of edge
tList.foreach(t => {
t.points.foreach(p => {
if(p != edge.q && p != edge.p) {
if(Util.orient2d(edge.q, edge.p, p) > 0 ) {
// Keep duplicate points out
if(!lPoints.contains(p)) {
lPoints += p
}
} else {
// Keep duplicate points out
if(!rPoints.contains(p))
rPoints += p
}
}
})
})
// Triangulate empty areas.
val T1 = new ArrayBuffer[Triangle]
triangulate(lPoints.toArray, List(edge.q, edge.p), T1)
val T2 = new ArrayBuffer[Triangle]
triangulate(rPoints.toArray, List(edge.q, edge.p), T2)
// Update neighbors
edgeNeighbors(nTriangles, T1)
edgeNeighbors(nTriangles, T2)
T1.last.markNeighbor(T2.last)
// Update advancing front
val ahead = (edge.p.x > edge.q.x)
val point1 = if(ahead) edge.q else edge.p
val point2 = if(ahead) edge.p else edge.q
val sNode = if(ahead) node else aFront.locate(point1)
val eNode = aFront.locate(point2)
aFront.constrainedEdge(sNode, eNode, T1, T2, edge)
// Mark constrained edge
T1.last markEdge(point1, point2)
T2.last markEdge(point1, point2)
// Copy constraied edges from old triangles
T1.foreach(t => t.markEdge(tList))
T2.foreach(t => t.markEdge(tList))
} else if(firstTriangle == null) {
// AFront traversal
// No triangles are intersected by the edge; edge must lie outside the mesh
// Apply constraint; traverse the advancing front, and build triangles
var pNode, first = node
val points = new ArrayBuffer[Point]
// Neighbor triangles
val nTriangles = new ArrayBuffer[Triangle]
nTriangles += pNode.triangle
val ahead = (edge.p.x > edge.q.x)
// If this is true we transition from AFront traversal to
// interior mesh traversal
var aboveEdge = false
if(ahead) {
// Scan right
pNode = pNode.next
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
nTriangles += pNode.triangle
pNode = pNode.next
aboveEdge = edge < pNode.point
}
} else {
// Scan left
pNode = pNode.prev
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
nTriangles += pNode.triangle
pNode = pNode.prev
aboveEdge = edge < pNode.point
}
nTriangles += pNode.triangle
}
val point2 = if(aboveEdge) {
val p1 = pNode.point
val p2 = if(ahead) pNode.prev.point else pNode.next.point
edge.intersect(p1, p2)
} else {
edge.p
}
// Triangulate empty areas.
val T = new ArrayBuffer[Triangle]
triangulate(points.toArray, List(edge.q, point2), T)
// Update neighbors
edgeNeighbors(nTriangles, T)
// Mark constrained edge
T.last markEdge(edge.q, point2)
// Update advancing front
if(aboveEdge) {
val iNode = aFront.aboveEdge(first, pNode, T.last, point2, ahead)
edgeEvent(new Segment(edge.p, point2), iNode)
} else {
if(ahead)
aFront link (first, pNode, T.last)
else
aFront link (pNode, first, T.last)
}
} else if(firstTriangle.contains(edge.q, edge.p)) {
// Constrained edge lies on the side of a triangle
// Mark constrained edge
firstTriangle markEdge(edge.q, edge.p)
firstTriangle.finalized = true
} else {
throw new Exception("Triangulation error - unexpected case")
}
}
// Update neigbor pointers for edge event
// Inneficient, but it works well...
private def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) {
for(t1 <- nTriangles)
for(t2 <- T)
t2.markNeighbor(t1)
for(i <- 0 until T.size)
for(j <- i+1 until T.size)
T(i).markNeighbor(T(j))
}
// Marc Vigo Anglada's triangulate pseudo-polygon algo
// See "An improved incremental algorithm for constructing restricted Delaunay triangulations"
private def triangulate(P: Array[Point], ab: List[Point], T: ArrayBuffer[Triangle]) {
val a = ab(0)
val b = ab(1)
var i = 0
if(P.size > 1) {
var c = P(0)
for(j <- 1 until P.size) {
if(illegal(a, b, c, P(j))) {
c = P(j)
i = j
}
}
val PE = P.slice(0, i)
val PD = P.slice(i+1, P.size)
triangulate(PE, List(a, c), T)
triangulate(PD, List(c, b), T)
}
if(!P.isEmpty) {
val ccw = Util.orient2d(a, P(i), b) > 0
val points = if(ccw) Array(a, P(i), b) else Array(a, b, P(i))
T += new Triangle(points)
T.last.finalized = true
mesh.map += T.last
}
}
// Scan left and right along AFront to fill holes
private def scanAFront(n: Node) = {
var node1 = n.next
// Update right
if(node1.next != null) {
var angle = 0.0
do {
angle = fill(node1)
node1 = node1.next
} while(angle <= PI_2 && angle >= -PI_2 && node1.next != null)
}
var node2 = n.prev
// Update left
if(node2.prev != null) {
var angle = 0.0
do {
angle = fill(node2)
node2 = node2.prev
} while(angle <= PI_2 && angle >= -PI_2 && node2.prev != null)
}
}
// Fill empty space with a triangle
private def fill(node: Node): Double = {
val a = (node.prev.point - node.point)
val b = (node.next.point - node.point)
val angle = Math.atan2(a cross b, a dot b)
// Is the angle acute?
if(angle <= PI_2 && angle >= -PI_2) {
val points = Array(node.prev.point, node.point, node.next.point)
val triangle = new Triangle(points)
// Update neighbor pointers
node.prev.triangle.markNeighbor(triangle)
node.triangle.markNeighbor(triangle)
mesh.map += triangle
aFront -= (node.prev, node, triangle)
}
angle
}
// Circumcircle test.
// Determines if point d lies inside triangle abc's circumcircle
private def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = {
val ccw = Util.orient2d(a, b, c) > 0
// Make sure abc is oriented counter-clockwise
if(ccw)
Util.incircle(a, b, c, d)
else
Util.incircle(a, c, b, d)
}
// Ensure adjacent triangles are legal
// If illegal, flip edges and update triangle's pointers
private def legalization(t1: Triangle, node: Node): Node = {
val t2 = node.triangle
val point = t1.points(0)
val oPoint = t2 oppositePoint t1
// Pints are oriented ccw
val illegal = Util.incircle(t1.points(1), t1.points(2), t1.points(0), oPoint)
if(illegal && !t2.finalized) {
// Flip edge and rotate everything clockwise
// Legalize points
t1.legalize(oPoint)
t2.legalize(oPoint, point)
// Update neighbors
// Copy old neighbors
val neighbors = List(t2.neighbors(0), t2.neighbors(1), t2.neighbors(2))
// Clear old neighbors
t2.clearNeighbors
// Update new neighbors
for(n <- neighbors) {
if(n != null) {
t1.markNeighbor(n)
t2.markNeighbor(n)
}
}
t2.markNeighbor(t1)
// Don't legalize these triangles again
t2.finalized = true
t1.finalized = true
// Update advancing front
aFront.insertLegalized(t1.points(1), t1, node)
} else {
// Update neighbor
t2.markNeighbor(t1)
// Update advancing front
aFront.insert(point, t1, node)
}
}
// The triangle mesh
private val mesh = new Mesh
// Advancing front
private var aFront: AFront = null
// Sorted point list
private var points = polyLine.toList
// Half Pi
private val PI_2 = Math.Pi/2
// Inital triangle factor
private val ALPHA = 0.3f
// Triangle used to clean interior
private var cleanTri: Triangle = null
}

View File

@ -1,60 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.cdt
import scala.collection.mutable.ArrayBuffer
import shapes.{Point, Triangle}
class Mesh {
// Triangles that constitute the mesh
val map = new ArrayBuffer[Triangle]
// Debug triangles
val debug = new ArrayBuffer[Triangle]
val triangles = new ArrayBuffer[Triangle]
// Recursively collect interior triangles and clean the mesh
// Excludes exterior triangles outside constrained edges
// TODO: Investigate depth first search as an alternative
def clean(triangle: Triangle) {
if(triangle != null && !triangle.interior) {
triangle.interior = true
triangles += triangle
for(i <- 0 until 3) {
if(!triangle.edges(i))
clean(triangle.neighbors(i))
}
}
}
}

View File

@ -1,373 +0,0 @@
/**
* Ported from jBox2D. Original author: ewjordan
* Triangulates a polygon using simple ear-clipping algorithm. Returns
* size of Triangle array unless the polygon can't be triangulated.
* This should only happen if the polygon self-intersects,
* though it will not _always_ return null for a bad polygon - it is the
* caller's responsibility to check for self-intersection, and if it
* doesn't, it should at least check that the return value is non-null
* before using. You're warned!
*
* Triangles may be degenerate, especially if you have identical points
* in the input to the algorithm. Check this before you use them.
*
* This is totally unoptimized, so for large polygons it should not be part
* of the simulation loop.
*
* Returns:
* -1 if algorithm fails (self-intersection most likely)
* 0 if there are not enough vertices to triangulate anything.
* Number of triangles if triangulation was successful.
*
* results will be filled with results - ear clipping always creates vNum - 2
* or fewer (due to pinch point polygon snipping), so allocate an array of
* this size.
*/
package org.poly2tri.earClip
import shapes.Point
import utils.Util
class EarClip {
val tol = .001f
var hasPinchPoint = false
var pinchIndexA = -1
var pinchIndexB = -1
var pin: Poly = null
var numTriangles = 0
def triangulatePolygon(xv: Array[Float], yv: Array[Float], vn: Int, results: Array[Triangle]): Int = {
if (vn < 3) return 0
var vNum = vn
//Recurse and split on pinch points
val pA = new Poly
val pB = new Poly
pin = new Poly(xv, yv, vNum)
if (resolvePinchPoint(pin,pA,pB)){
val mergeA = new Array[Triangle](pA.nVertices)
val mergeB = new Array[Triangle](pB.nVertices)
for (i <- 0 until pA.nVertices) {
mergeA(i) = new Triangle();
}
for (i <- 0 until pB.nVertices) {
mergeB(i) = new Triangle();
}
val nA = triangulatePolygon(pA.x,pA.y,pA.nVertices,mergeA)
val nB = triangulatePolygon(pB.x,pB.y,pB.nVertices,mergeB)
if (nA == -1 || nB == -1){
numTriangles = -1
return numTriangles
}
for (i <- 0 until nA){
results(i).set(mergeA(i));
}
for (i <- 0 until nB){
results(nA+i).set(mergeB(i));
}
numTriangles = (nA+nB)
return numTriangles
}
val buffer = new Array[Triangle](vNum-2);
for (i <- 0 until buffer.size) {
buffer(i) = new Triangle();
}
var bufferSize = 0;
var xrem = new Array[Float](vNum)
var yrem = new Array[Float](vNum)
for (i <- 0 until vNum) {
xrem(i) = xv(i);
yrem(i) = yv(i);
}
val xremLength = vNum;
while (vNum > 3) {
//System.out.println("vNum: "+vNum);
// Find an ear
var earIndex = -1;
var earMaxMinCross = -1000.0f;
for (i <- 0 until vNum) {
if (isEar(i, xrem, yrem, vNum)) {
val lower = remainder(i-1,vNum);
val upper = remainder(i+1,vNum);
var d1 = Point(xrem(upper)-xrem(i),yrem(upper)-yrem(i));
var d2 = Point(xrem(i)-xrem(lower),yrem(i)-yrem(lower));
var d3 = Point(xrem(lower)-xrem(upper),yrem(lower)-yrem(upper));
d1 = d1.normalize
d2 = d2.normalize
d3 = d3.normalize
val cross12 = Math.abs( d1 cross d2 )
val cross23 = Math.abs( d2 cross d3 )
val cross31 = Math.abs( d3 cross d1 )
//Find the maximum minimum angle
val minCross = Math.min(cross12, Math.min(cross23,cross31))
if (minCross > earMaxMinCross){
earIndex = i
earMaxMinCross = minCross
}
}
}
// If we still haven't found an ear, we're screwed.
// Note: sometimes this is happening because the
// remaining points are collinear. Really these
// should just be thrown out without halting triangulation.
if (earIndex == -1){
System.out.println("Couldn't find an ear, dumping remaining poly:\n");
System.out.println("Please submit this dump to ewjordan at Box2d forums\n");
assert(false)
for (i <- 0 until bufferSize) {
results(i).set(buffer(i));
}
if (bufferSize > 0) return bufferSize;
else {
numTriangles = -1
return numTriangles
}
}
// Clip off the ear:
// - remove the ear tip from the list
vNum -= 1;
val newx = new Array[Float](vNum)
val newy = new Array[Float](vNum)
var currDest = 0;
for (i <- 0 until vNum) {
if (currDest == earIndex) currDest += 1
newx(i) = xrem(currDest);
newy(i) = yrem(currDest);
currDest += 1;
}
// - add the clipped triangle to the triangle list
val under = if(earIndex == 0) (vNum) else (earIndex - 1)
val over = if(earIndex == vNum) 0 else (earIndex + 1)
val toAdd = new Triangle(xrem(earIndex), yrem(earIndex), xrem(over), yrem(over), xrem(under), yrem(under));
buffer(bufferSize) = toAdd
bufferSize += 1;
// - replace the old list with the new one
xrem = newx;
yrem = newy;
}
val toAdd = new Triangle(xrem(1), yrem(1), xrem(2), yrem(2), xrem(0), yrem(0))
buffer(bufferSize) = toAdd;
bufferSize += 1;
assert(bufferSize == xremLength-2)
for (i <- 0 until bufferSize) {
results(i).set(buffer(i))
}
numTriangles = bufferSize
return numTriangles
}
/**
* Finds and fixes "pinch points," points where two polygon
* vertices are at the same point.
*
* If a pinch point is found, pin is broken up into poutA and poutB
* and true is returned; otherwise, returns false.
*
* Mostly for internal use.
*
* O(N^2) time, which sucks...
*/
private def resolvePinchPoint(pin: Poly, poutA: Poly, poutB: Poly): Boolean = {
if (pin.nVertices < 3) return false
hasPinchPoint = false
pinchIndexA = -1
pinchIndexB = -1
pinchIndex
if (hasPinchPoint){
val sizeA = pinchIndexB - pinchIndexA;
if (sizeA == pin.nVertices) return false;
val xA = new Array[Float](sizeA);
val yA = new Array[Float](sizeA);
for (i <- 0 until sizeA){
val ind = remainder(pinchIndexA+i,pin.nVertices);
xA(i) = pin.x(ind);
yA(i) = pin.y(ind);
}
val tempA = new Poly(xA,yA,sizeA);
poutA.set(tempA);
val sizeB = pin.nVertices - sizeA;
val xB = new Array[Float](sizeB);
val yB = new Array[Float](sizeB);
for (i <- 0 until sizeB){
val ind = remainder(pinchIndexB+i,pin.nVertices);
xB(i) = pin.x(ind);
yB(i) = pin.y(ind);
}
val tempB = new Poly(xB,yB,sizeB);
poutB.set(tempB);
}
return hasPinchPoint;
}
//Fix for obnoxious behavior for the % operator for negative numbers...
private def remainder(x: Int, modulus: Int): Int = {
var rem = x % modulus
while (rem < 0){
rem += modulus
}
return rem
}
def pinchIndex: Boolean = {
for (i <- 0 until pin.nVertices) {
if(!hasPinchPoint) {
for (j <- i+1 until pin.nVertices){
//Don't worry about pinch points where the points
//are actually just dupe neighbors
if (Math.abs(pin.x(i)-pin.x(j))<tol&&Math.abs(pin.y(i)-pin.y(j))<tol&&j!=i+1){
pinchIndexA = i
pinchIndexB = j
hasPinchPoint = true
return false
}
}
}
}
return true
}
/**
* Checks if vertex i is the tip of an ear in polygon defined by xv[] and
* yv[].
*
* Assumes clockwise orientation of polygon...ick
*/
private def isEar(i: Int , xv: Array[Float], yv: Array[Float], xvLength: Int): Boolean = {
var dx0, dy0, dx1, dy1 = 0f
if (i >= xvLength || i < 0 || xvLength < 3) {
return false;
}
var upper = i + 1
var lower = i - 1
if (i == 0) {
dx0 = xv(0) - xv(xvLength - 1)
dy0 = yv(0) - yv(xvLength - 1)
dx1 = xv(1) - xv(0)
dy1 = yv(1) - yv(0)
lower = xvLength - 1
}
else if (i == xvLength - 1) {
dx0 = xv(i) - xv(i - 1);
dy0 = yv(i) - yv(i - 1);
dx1 = xv(0) - xv(i);
dy1 = yv(0) - yv(i);
upper = 0;
}
else {
dx0 = xv(i) - xv(i - 1);
dy0 = yv(i) - yv(i - 1);
dx1 = xv(i + 1) - xv(i);
dy1 = yv(i + 1) - yv(i);
}
val cross = dx0 * dy1 - dx1 * dy0;
if (cross > 0)
return false;
val myTri = new Triangle(xv(i), yv(i), xv(upper), yv(upper), xv(lower), yv(lower))
for (j <- 0 until xvLength) {
if (!(j == i || j == lower || j == upper)) {
if (myTri.containsPoint(xv(j), yv(j)))
return false;
}
}
return true;
}
}
class Poly(var x: Array[Float], var y: Array[Float], var nVertices: Int) {
var areaIsSet = false
var area = 0f
def this(_x: Array[Float], _y: Array[Float]) = this(_x,_y,_x.size)
def this() = this(null, null, 0)
def set(p: Poly ) {
if (nVertices != p.nVertices){
nVertices = p.nVertices;
x = new Array[Float](nVertices)
y = new Array[Float](nVertices)
}
for (i <- 0 until nVertices) {
x(i) = p.x(i)
y(i) = p.y(i)
}
areaIsSet = false
}
}
class Triangle(var x1: Float, var y1: Float, var x2: Float, var y2: Float, var x3: Float, var y3: Float) {
def this() = this(0,0,0,0,0,0)
val x = new Array[Float](3)
val y = new Array[Float](3)
// Automatically fixes orientation to ccw
val dx1 = x2-x1
val dx2 = x3-x1
val dy1 = y2-y1
val dy2 = y3-y1
val cross = dx1*dy2-dx2*dy1
val ccw = (cross>0)
if (ccw){
x(0) = x1; x(1) = x2; x(2) = x3;
y(0) = y1; y(1) = y2; y(2) = y3;
} else{
x(0) = x1; x(1) = x3; x(2) = x2;
y(0) = y1; y(1) = y3; y(2) = y2;
}
def set(t: Triangle) {
x(0) = t.x(0)
x(1) = t.x(1)
x(2) = t.x(2)
y(0) = t.y(0)
y(1) = t.y(1)
y(2) = t.y(2)
}
def containsPoint(_x: Float, _y: Float): Boolean = {
val vx2 = _x-x(0); val vy2 = _y-y(0);
val vx1 = x(1)-x(0); val vy1 = y(1)-y(0);
val vx0 = x(2)-x(0); val vy0 = y(2)-y(0);
val dot00 = vx0*vx0+vy0*vy0;
val dot01 = vx0*vx1+vy0*vy1;
val dot02 = vx0*vx2+vy0*vy2;
val dot11 = vx1*vx1+vy1*vy1;
val dot12 = vx1*vx2+vy1*vy2;
val invDenom = 1.0f / (dot00*dot11 - dot01*dot01);
val u = (dot11*dot02 - dot01*dot12)*invDenom;
val v = (dot00*dot12 - dot01*dot02)*invDenom;
return ((u>=0)&&(v>=0)&&(u+v<=1));
}
}

View File

@ -1,165 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import scala.collection.mutable.{ArrayBuffer, Queue}
import shapes.Point
// Doubly linked list
class MonotoneMountain {
var tail, head: Point = null
var size = 0
private val convexPoints = new ArrayBuffer[Point]
// Monotone mountain points
val monoPoly = new ArrayBuffer[Point]
// Triangles that constitute the mountain
val triangles = new ArrayBuffer[Array[Point]]
// Convex polygons that constitute the mountain
val convexPolies = new ArrayBuffer[Array[Point]]
// Used to track which side of the line we are on
private var positive = false
// Almost Pi!
private val PI_SLOP = 3.1
// Append a point to the list
def +=(point: Point) {
size match {
case 0 =>
head = point
size += 1
case 1 =>
// Keep repeat points out of the list
if(point ! head) {
tail = point
tail.prev = head
head.next = tail
size += 1
}
case _ =>
// Keep repeat points out of the list
if(point ! tail) {
tail.next = point
point.prev = tail
tail = point
size += 1
}
}
}
// Remove a point from the list
def remove(point: Point) {
val next = point.next
val prev = point.prev
point.prev.next = next
point.next.prev = prev
size -= 1
}
// Partition a x-monotone mountain into triangles O(n)
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
def process {
// Establish the proper sign
positive = angleSign
// create monotone polygon - for dubug purposes
genMonoPoly
// Initialize internal angles at each nonbase vertex
// Link strictly convex vertices into a list, ignore reflex vertices
var p = head.next
while(p != tail) {
val a = angle(p)
// If the point is almost colinear with it's neighbor, remove it!
if(a >= PI_SLOP || a <= -PI_SLOP || a == 0.0)
remove(p)
else if(convex(p))
convexPoints += p
p = p.next
}
triangulate
}
private def triangulate {
while(!convexPoints.isEmpty) {
val ear = convexPoints.remove(0)
val a = ear.prev
val b = ear
val c = ear.next
val triangle = Array(a, b, c)
triangles += triangle
// Remove ear, update angles and convex list
remove(ear)
if(valid(a)) convexPoints += a
if(valid(c)) convexPoints += c
}
assert(size <= 3, "Triangulation bug, please report")
}
private def valid(p: Point) = (p != head && p != tail && convex(p))
// Create the monotone polygon
private def genMonoPoly {
var p = head
while(p != null) {
monoPoly += p
p = p.next
}
}
private def angle(p: Point) = {
val a = (p.next - p)
val b = (p.prev - p)
Math.atan2(a cross b, a dot b)
}
private def angleSign = {
val a = (head.next - head)
val b = (tail - head)
(Math.atan2(a cross b, a dot b) >= 0)
}
// Determines if the inslide angle is convex or reflex
private def convex(p: Point) = {
if(positive != (angle(p) >= 0)) false
else true
}
}

View File

@ -1,57 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import scala.collection.mutable.ArrayBuffer
import shapes.Segment
// Node for a Directed Acyclic graph (DAG)
abstract class Node(var left: Node, var right: Node) {
if(left != null) left.parentList += this
if(right != null) right.parentList += this
var parentList = new ArrayBuffer[Node]
def locate(s: Segment): Sink
// Replace a node in the graph with this node
// Make sure parent pointers are updated
def replace(node: Node) {
for(parent <- node.parentList) {
// Select the correct node to replace (left or right child)
if(parent.left == node) parent.left = this
else parent.right = this
parentList += parent
}
}
}

View File

@ -1,92 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import scala.collection.mutable.ArrayBuffer
import shapes.{Segment, Trapezoid}
// Directed Acyclic graph (DAG)
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
class QueryGraph(var head: Node) {
def locate(s: Segment) = head.locate(s).trapezoid
def followSegment(s: Segment) = {
val trapezoids = new ArrayBuffer[Trapezoid]
trapezoids += locate(s)
var j = 0
while(s.q.x > trapezoids(j).rightPoint.x) {
if(s > trapezoids(j).rightPoint) {
trapezoids += trapezoids(j).upperRight
} else {
trapezoids += trapezoids(j).lowerRight
}
j += 1
}
trapezoids
}
def replace(sink: Sink, node: Node) {
if(sink.parentList.size == 0) {
head = node
} else {
node replace sink
}
}
def case1(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
val qNode = new XNode(s.q, yNode, Sink.init(tList(3)))
val pNode = new XNode(s.p, Sink.init(tList(0)), qNode)
replace(sink, pNode)
}
def case2(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
val pNode = new XNode(s.p, Sink.init(tList(0)), yNode)
replace(sink, pNode)
}
def case3(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
replace(sink, yNode)
}
def case4(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
val qNode = new XNode(s.q, yNode, Sink.init(tList(2)))
replace(sink, qNode)
}
}

View File

@ -1,50 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import shapes.{Segment, Trapezoid}
object Sink {
def init(trapezoid: Trapezoid) = {
if(trapezoid.sink != null)
trapezoid.sink
else
new Sink(trapezoid)
}
}
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
trapezoid.sink = this
override def locate(s: Segment): Sink = this
}

View File

@ -1,187 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import scala.collection.mutable.{HashSet, ArrayBuffer}
import shapes.{Point, Segment, Trapezoid}
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
class TrapezoidalMap {
// Trapezoid container
val map = HashSet.empty[Trapezoid]
// AABB margin
var margin = 50f
// Bottom segment that spans multiple trapezoids
private var bCross: Segment = null
// Top segment that spans multiple trapezoids
private var tCross: Segment = null
def clear {
bCross = null
tCross = null
}
// Case 1: segment completely enclosed by trapezoid
// break trapezoid into 4 smaller trapezoids
def case1(t: Trapezoid, s: Segment) = {
val trapezoids = new Array[Trapezoid](4)
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
trapezoids(1) = new Trapezoid(s.p, s.q, t.top, s)
trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom)
trapezoids(3) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
trapezoids(0).updateLeft(t.upperLeft, t.lowerLeft)
trapezoids(1).updateLeftRight(trapezoids(0), null, trapezoids(3), null)
trapezoids(2).updateLeftRight(null, trapezoids(0), null, trapezoids(3))
trapezoids(3).updateRight(t.upperRight, t.lowerRight)
trapezoids
}
// Case 2: Trapezoid contains point p, q lies outside
// break trapezoid into 3 smaller trapezoids
def case2(t: Trapezoid, s: Segment) = {
val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
val trapezoids = new Array[Trapezoid](3)
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
trapezoids(1) = new Trapezoid(s.p, rp, t.top, s)
trapezoids(2) = new Trapezoid(s.p, rp, s, t.bottom)
trapezoids(0).updateLeft(t.upperLeft, t.lowerLeft)
trapezoids(1).updateLeftRight(trapezoids(0), null, t.upperRight, null)
trapezoids(2).updateLeftRight(null, trapezoids(0), null, t.lowerRight)
bCross = t.bottom
tCross = t.top
s.above = trapezoids(1)
s.below = trapezoids(2)
trapezoids
}
// Case 3: Trapezoid is bisected
def case3(t: Trapezoid, s: Segment) = {
val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint
val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
val trapezoids = new Array[Trapezoid](2)
if(tCross == t.top) {
trapezoids(0) = t.upperLeft
trapezoids(0).updateRight(t.upperRight, null)
trapezoids(0).rightPoint = rp
} else {
trapezoids(0) = new Trapezoid(lp, rp, t.top, s)
trapezoids(0).updateLeftRight(t.upperLeft, s.above, t.upperRight, null)
}
if(bCross == t.bottom) {
trapezoids(1) = t.lowerLeft
trapezoids(1).updateRight(null, t.lowerRight)
trapezoids(1).rightPoint = rp
} else {
trapezoids(1) = new Trapezoid(lp, rp, s, t.bottom)
trapezoids(1).updateLeftRight(s.below, t.lowerLeft, null, t.lowerRight)
}
bCross = t.bottom
tCross = t.top
s.above = trapezoids(0)
s.below = trapezoids(1)
trapezoids
}
// Case 4: Trapezoid contains point q, p lies outside
// break trapezoid into 3 smaller trapezoids
def case4(t: Trapezoid, s: Segment) = {
val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint
val trapezoids = new Array[Trapezoid](3)
if(tCross == t.top) {
trapezoids(0) = t.upperLeft
trapezoids(0).rightPoint = s.q
} else {
trapezoids(0) = new Trapezoid(lp, s.q, t.top, s)
trapezoids(0).updateLeft(t.upperLeft, s.above)
}
if(bCross == t.bottom) {
trapezoids(1) = t.lowerLeft
trapezoids(1).rightPoint = s.q
} else {
trapezoids(1) = new Trapezoid(lp, s.q, s, t.bottom)
trapezoids(1).updateLeft(s.below, t.lowerLeft)
}
trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
trapezoids(2).updateLeftRight(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight)
trapezoids
}
// Create an AABB around segments
def boundingBox(segments: ArrayBuffer[Segment]): Trapezoid = {
var max = segments(0).p + margin
var min = segments(0).q - margin
for(s <- segments) {
if(s.p.x > max.x) max = Point(s.p.x + margin, max.y)
if(s.p.y > max.y) max = Point(max.x, s.p.y + margin)
if(s.q.x > max.x) max = Point(s.q.x+margin, max.y)
if(s.q.y > max.y) max = Point(max.x, s.q.y+margin)
if(s.p.x < min.x) min = Point(s.p.x-margin, min.y)
if(s.p.y < min.y) min = Point(min.x, s.p.y-margin)
if(s.q.x < min.x) min = Point(s.q.x-margin, min.y)
if(s.q.y < min.y) min = Point(min.x, s.q.y-margin)
}
val top = new Segment(Point(min.x, max.y), Point(max.x, max.y))
val bottom = new Segment(Point(min.x, min.y), Point(max.x, min.y))
val left = bottom.p
val right = top.q
return new Trapezoid(left, right, top, bottom)
}
}

View File

@ -1,228 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import scala.collection.mutable.ArrayBuffer
import utils.{Util, Random}
import shapes.{Point, Segment, Trapezoid}
// Based on Raimund Seidel's paper "A simple and fast incremental randomized
// algorithm for computing trapezoidal decompositions and for triangulating polygons"
// See also: "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
// "Computational Geometry in C", 2nd edition, by Joseph O'Rourke
class Triangulator(points: ArrayBuffer[Point]) {
// Convex polygon list
var polygons = new ArrayBuffer[Array[Point]]
// Order and randomize the segments
val segmentList = initSegments
// The trapezoidal map
def trapezoidMap = trapezoidalMap.map
// Trapezoid decomposition list
var trapezoids = new ArrayBuffer[Trapezoid]
// Initialize trapezoidal map and query structure
private val trapezoidalMap = new TrapezoidalMap
private val boundingBox = trapezoidalMap.boundingBox(segmentList)
private val queryGraph = new QueryGraph(Sink.init(boundingBox))
private val xMonoPoly = new ArrayBuffer[MonotoneMountain]
process
// Build the trapezoidal map and query graph
private def process {
var i = 0
while(i < segmentList.size) {
val s = segmentList(i)
var traps = queryGraph.followSegment(s)
// Remove trapezoids from trapezoidal Map
var j = 0
while(j < traps.size) {
trapezoidalMap.map -= traps(j)
j += 1
}
j = 0
while(j < traps.size) {
val t = traps(j)
var tList: Array[Trapezoid] = null
val containsP = t.contains(s.p)
val containsQ = t.contains(s.q)
if(containsP && containsQ) {
// Case 1
tList = trapezoidalMap.case1(t,s)
queryGraph.case1(t.sink, s, tList)
} else if(containsP && !containsQ) {
// Case 2
tList = trapezoidalMap.case2(t,s)
queryGraph.case2(t.sink, s, tList)
} else if(!containsP && !containsQ) {
// Case 3
tList = trapezoidalMap.case3(t, s)
queryGraph.case3(t.sink, s, tList)
} else {
// Case 4
tList = trapezoidalMap.case4(t, s)
queryGraph.case4(t.sink, s, tList)
}
// Add new trapezoids to map
var k = 0
while(k < tList.size) {
trapezoidalMap.map += tList(k)
k += 1
}
j += 1
}
trapezoidalMap.clear
i += 1
}
// Mark outside trapezoids
for(t <- trapezoidalMap.map)
markOutside(t)
// Collect interior trapezoids
for(t <- trapezoidalMap.map)
if(t.inside) {
trapezoids += t
t addPoints
}
// Generate the triangles
createMountains
//println("# triangles = " + triangles.size)
}
// Monotone polygons - these are monotone mountains
def monoPolies: ArrayBuffer[ArrayBuffer[Point]] = {
val polies = new ArrayBuffer[ArrayBuffer[Point]]
for(i <- 0 until xMonoPoly.size)
polies += xMonoPoly(i).monoPoly
return polies
}
// Build a list of x-monotone mountains
private def createMountains {
var i = 0
while(i < segmentList.size) {
val s = segmentList(i)
if(s.mPoints.size > 0) {
val mountain = new MonotoneMountain
var k: List[Point] = null
// Sorting is a perfromance hit. Literature says this can be accomplised in
// linear time, although I don't see a way around using traditional methods
// when using a randomized incremental algorithm
if(s.mPoints.size < 10)
// Insertion sort is one of the fastest algorithms for sorting arrays containing
// fewer than ten elements, or for lists that are already mostly sorted.
k = Util.insertSort((p1: Point, p2: Point) => p1 < p2)(s.mPoints).toList
else
k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
val points = s.p :: k ::: List(s.q)
var j = 0
while(j < points.size) {
mountain += points(j)
j += 1
}
// Triangulate monotone mountain
mountain process
// Extract the triangles into a single list
j = 0
while(j < mountain.triangles.size) {
polygons += mountain.triangles(j)
j += 1
}
xMonoPoly += mountain
}
i += 1
}
}
// Mark the outside trapezoids surrounding the polygon
private def markOutside(t: Trapezoid) {
if(t.top == boundingBox.top || t.bottom == boundingBox.bottom) {
t trimNeighbors
}
}
// Create segments and connect end points; update edge event pointer
private def initSegments: ArrayBuffer[Segment] = {
var segments = List[Segment]()
for(i <- 0 until points.size-1)
segments = new Segment(points(i), points(i+1)) :: segments
segments = new Segment(points.first, points.last) :: segments
orderSegments(segments)
}
private def orderSegments(segments: List[Segment]) = {
// Ignore vertical segments!
val segs = new ArrayBuffer[Segment]
for(s <- segments) {
val p = shearTransform(s.p)
val q = shearTransform(s.q)
// Point p must be to the left of point q
if(p.x > q.x) {
segs += new Segment(q, p)
} else if(p.x < q.x) {
segs += new Segment(p, q)
}
}
// Randomized triangulation improves performance
// See Seidel's paper, or O'Rourke's book, p. 57
Random.shuffle(segs)
segs
}
// Prevents any two distinct endpoints from lying on a common vertical line, and avoiding
// the degenerate case. See Mark de Berg et al, Chapter 6.3
//val SHEER = 0.0001f
def shearTransform(point: Point) = Point(point.x + 0.0001f * point.y, point.y)
}

View File

@ -1,47 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import shapes.{Point, Segment}
class XNode(point: Point, lChild: Node, rChild: Node) extends Node(lChild, rChild) {
override def locate(s: Segment): Sink = {
if(s.p.x >= point.x) {
// Move to the right in the graph
return right.locate(s)
} else {
// Move to the left in the graph
return left.locate(s)
}
}
}

View File

@ -1,55 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.seidel
import shapes.Segment
class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, rChild) {
override def locate(s: Segment): Sink = {
if (segment > s.p) {
// Move down the graph
return right.locate(s)
} else if (segment < s.p) {
// Move up the graph
return left.locate(s)
} else {
// s and segment share the same endpoint, p
if (s.slope < segment.slope) {
// Move down the graph
return right.locate(s)
} else {
// Move up the graph
return left.locate(s)
}
}
}
}

View File

@ -1,74 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.shapes
import scala.collection.mutable.ArrayBuffer
case class Point(val x: Float, val y: Float) {
// Pointers to next and previous points in Monontone Mountain
var next, prev: Point = null
// The setment this point belongs to
var segment: Segment = null
// List of edges this point constitutes an upper ending point (CDT)
var edges = new ArrayBuffer[Segment]
@inline def -(p: Point) = Point(x - p.x, y - p.y)
@inline def +(p: Point) = Point(x + p.x, y + p.y)
@inline def +(f: Float) = Point(x + f, y + f)
@inline def -(f: Float) = Point(x - f, y - f)
@inline def *(f: Float) = Point(x * f, y * f)
@inline def /(a: Float) = Point(x / a, y / a)
@inline def cross(p: Point) = x * p.y - y * p.x
@inline def dot(p: Point) = x * p.x + y * p.y
@inline def length = Math.sqrt(x * x + y * y).toFloat
@inline def normalize = this / length
// Sort along x axis
@inline def <(p: Point) = (x < p.x)
// Sort along y axis
@inline def >(p: Point) = {
if(y < p.y)
true
else if(y > p.y)
false
else {
if(x < p.x)
true
else
false
}
}
@inline def !(p: Point) = !(p.x == x && p.y == y)
@inline override def clone = Point(x, y)
}

View File

@ -1,78 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.shapes
import scala.collection.mutable.ArrayBuffer
// Represents a simple polygon's edge
// TODO: Rename this class to Edge?
class Segment(var p: Point, var q: Point) {
// Pointers used for building trapezoidal map
var above, below: Trapezoid = null
// Montone mountain points
val mPoints = new ArrayBuffer[Point]
// Equation of a line: y = m*x + b
// Slope of the line (m)
val slope = (q.y - p.y)/(q.x - p.x)
// Y intercept
val b = p.y - (p.x * slope)
// Determines if this segment lies above the given point
def > (point: Point) = (Math.floor(point.y) < Math.floor(slope * point.x + b))
// Determines if this segment lies below the given point
def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b))
private def signedArea(a: Point, b: Point, c: Point): Float =
(a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
def intersect(c: Point, d: Point): Point = {
val a = p
val b = q
val a1 = signedArea(a, b, d)
val a2 = signedArea(a, b, c)
if (a1 != 0.0f && a2 != 0.0f && a1*a2 < 0.0f) {
val a3 = signedArea(c, d, a)
val a4 = a3 + a2 - a1
if (a3 * a4 < 0.0f) {
val t = a3 / (a3 - a4)
return a + ((b - a) * t)
}
}
throw new Exception("Error")
}
}

View File

@ -1,109 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.shapes
import seidel.Sink
class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, val bottom: Segment) {
var sink: Sink = null
var inside = true
// Neighbor pointers
var upperLeft: Trapezoid = null
var lowerLeft: Trapezoid = null
var upperRight: Trapezoid = null
var lowerRight: Trapezoid = null
// Update neighbors to the left
def updateLeft(ul: Trapezoid, ll: Trapezoid) {
upperLeft = ul; if(ul != null) ul.upperRight = this
lowerLeft = ll; if(ll != null) ll.lowerRight = this
}
// Update neighbors to the right
def updateRight(ur: Trapezoid, lr: Trapezoid) {
upperRight = ur; if(ur != null) ur.upperLeft = this
lowerRight = lr; if(lr != null) lr.lowerLeft = this
}
// Update neighbors on both sides
def updateLeftRight(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) {
upperLeft = ul; if(ul != null) ul.upperRight = this
lowerLeft = ll; if(ll != null) ll.lowerRight = this
upperRight = ur; if(ur != null) ur.upperLeft = this
lowerRight = lr; if(lr != null) lr.lowerLeft = this
}
// Recursively trim outside neighbors
def trimNeighbors {
if(inside) {
inside = false
if(upperLeft != null) upperLeft.trimNeighbors
if(lowerLeft != null) lowerLeft.trimNeighbors
if(upperRight != null) upperRight.trimNeighbors
if(lowerRight != null) lowerRight.trimNeighbors
}
}
// Determines if this point lies inside the trapezoid
def contains(point: Point) = {
(point.x > leftPoint.x && point.x < rightPoint.x && top > point && bottom < point)
}
def vertices: Array[Point] = {
val verts = new Array[Point](4)
verts(0) = lineIntersect(top, leftPoint.x)
verts(1) = lineIntersect(bottom, leftPoint.x)
verts(2) = lineIntersect(bottom, rightPoint.x)
verts(3) = lineIntersect(top, rightPoint.x)
verts
}
def lineIntersect(s: Segment, x: Float) = {
val y = s.slope * x + s.b
Point(x, y)
}
// Add points to monotone mountain
def addPoints {
if(leftPoint != bottom.p) {bottom.mPoints += leftPoint.clone}
if(rightPoint != bottom.q) {bottom.mPoints += rightPoint.clone}
if(leftPoint != top.p) {top.mPoints += leftPoint.clone}
if(rightPoint != top.q) {top.mPoints += rightPoint.clone}
}
def debugData {
println("LeftPoint = " + leftPoint + " | RightPoint = " + rightPoint)
println("Top Segment: p = " + top.p + ", q = " + top.q)
println("Bottom Segment: p = " + bottom.p + ", q = " + bottom.q)
}
}

View File

@ -1,400 +0,0 @@
/* Poly2Tri
* Copyright (c) 2009, Mason Green
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.poly2tri.shapes
import scala.collection.mutable.ArrayBuffer
import utils.Util
// Triangle-based data structures are know to have better performance than quad-edge structures
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
// "Triangulations in CGAL"
class Triangle(val points: Array[Point]) {
// Neighbor pointers
var neighbors = new Array[Triangle](3)
// Flags to determine if an edge is the final Delauney edge
val edges = Array(false, false, false)
// Finalization flag. Set true if legalized or edge constrained
var finalized = false
// Has this triangle been marked as an interoir triangle?
var interior = false
def contains(p: Point): Boolean = (p == points(0) || p == points(1) || p == points(2))
def contains(e: Segment): Boolean = (contains(e.p) && contains(e.q))
def contains(p: Point, q: Point): Boolean = (contains(p) && contains(q))
// Update neighbor pointers
private def markNeighbor(p1: Point, p2: Point, t: Triangle) {
if((p1 == points(2) && p2 == points(1)) || (p1 == points(1) && p2 == points(2)))
neighbors(0) = t
else if((p1 == points(0) && p2 == points(2)) || (p1 == points(2) && p2 == points(0)))
neighbors(1) = t
else if((p1 == points(0) && p2 == points(1)) || (p1 == points(1) && p2 == points(0)))
neighbors(2) = t
else {
throw new Exception("Neighbor error, please report!")
}
}
/* Exhaustive search to update neighbor pointers */
def markNeighbor(t: Triangle) {
if (t.contains(points(1), points(2))) {
neighbors(0) = t
t.markNeighbor(points(1), points(2), this)
} else if(t.contains(points(0), points(2))) {
neighbors(1) = t
t.markNeighbor(points(0), points(2), this)
} else if (t.contains(points(0), points(1))) {
neighbors(2) = t
t.markNeighbor(points(0), points(1), this)
}
}
def clearNeighbors {
neighbors = new Array[Triangle](3)
}
def oppositePoint(t: Triangle): Point = {
assert(t != this, "self-pointer error")
if(points(0) == t.points(1))
points(1)
else if(points(0) == t.points(2))
points(2)
else if(contains(t.points(1), t.points(2)))
points(0)
else {
t.printDebug
printDebug
println(area + " | " + t.area)
throw new Exception("Point location error, please report")
}
}
// Fast point in triangle test
def pointIn(point: Point): Boolean = {
val ij = points(1) - points(0)
val jk = points(2) - points(1)
val pab = (point - points(0)).cross(ij)
val pbc = (point - points(1)).cross(jk)
var sameSign = Math.signum(pab) == Math.signum(pbc)
if (!sameSign) return false
val ki = points(0) - points(2)
val pca = (point - points(2)).cross(ki)
sameSign = Math.signum(pab) == Math.signum(pca)
if (!sameSign) return false
true
}
// Locate first triangle crossed by constrained edge
def locateFirst(edge: Segment): Triangle = {
if(contains(edge)) return this
if(edge.q == points(0))
search(points(1), points(2), edge, neighbors(2))
else if(edge.q == points(1))
search(points(0), points(2), edge, neighbors(0))
else if(edge.q == points(2))
search(points(0), points(1), edge, neighbors(1))
else
throw new Exception("Point not found")
}
def search(p1: Point, p2: Point, edge: Segment, neighbor: Triangle): Triangle = {
val o1 = Util.orient2d(edge.q, p1, edge.p)
val o2 = Util.orient2d(edge.q, p2, edge.p)
val sameSign = Math.signum(o1) == Math.signum(o2)
// Edge crosses this triangle
if(!sameSign)
return this
// Look at neighbor
if(neighbor == null)
null
else
neighbor.locateFirst(edge)
}
// Locate next triangle crossed by edge
def findNeighbor(p: Point): Triangle = {
if(contains(p)) return this
if(Util.orient2d(points(1), points(0), p) > 0)
return neighbors(2)
else if(Util.orient2d(points(2), points(1), p) > 0)
return neighbors(0)
else if(Util.orient2d(points(0), points(2), p) > 0)
return neighbors(1)
else
return null
}
// The neighbor clockwise to given point
def neighborCW(point: Point): Triangle = {
if(point == points(0)) {
neighbors(1)
}else if(point == points(1)) {
neighbors(2)
} else
neighbors(0)
}
// The neighbor counter-clockwise to given point
def neighborCCW(oPoint: Point): Triangle = {
if(oPoint == points(0)) {
neighbors(2)
}else if(oPoint == points(1)) {
neighbors(0)
} else
neighbors(1)
}
// The neighbor clockwise to given point
def neighborAcross(point: Point): Triangle = {
if(point == points(0)) {
neighbors(0)
}else if(point == points(1)) {
neighbors(1)
} else
neighbors(2)
}
// The point counter-clockwise to given point
def pointCCW(point: Point): Point = {
if(point == points(0)) {
points(1)
} else if(point == points(1)) {
points(2)
} else if(point == points(2)){
points(0)
} else {
throw new Exception("point location error")
}
}
// The point counter-clockwise to given point
def pointCW(point: Point): Point = {
if(point == points(0)) {
points(2)
} else if(point == points(1)) {
points(0)
} else if(point == points(2)){
points(1)
} else {
throw new Exception("point location error")
}
}
// Legalized triangle by rotating clockwise around point(0)
def legalize(oPoint: Point) {
points(1) = points(0)
points(0) = points(2)
points(2) = oPoint
}
// Legalize triagnle by rotating clockwise around oPoint
def legalize(oPoint: Point, nPoint: Point) {
if(oPoint == points(0)) {
points(1) = points(0)
points(0) = points(2)
points(2) = nPoint
} else if (oPoint == points(1)) {
points(2) = points(1)
points(1) = points(0)
points(0) = nPoint
} else if (oPoint == points(2)) {
points(0) = points(2)
points(2) = points(1)
points(1) = nPoint
} else {
throw new Exception("legalization error")
}
}
// Check if legalized triangle will be collinear
def collinear(oPoint: Point): Boolean = Util.collinear(points(1), points(0), oPoint)
// Check if legalized triangle will be collinear
def collinear(oPoint: Point, nPoint: Point): Boolean = {
if(oPoint == points(0)) {
Util.collinear(points(0), points(2), nPoint)
} else if (oPoint == points(1)) {
Util.collinear(points(0), points(1), nPoint)
} else {
Util.collinear(points(2), points(1), nPoint)
}
}
// Rotate neighbors clockwise around give point. Share diagnal with triangle
def rotateNeighborsCW(oPoint: Point, triangle: Triangle) {
if(oPoint == points(0)) {
neighbors(2) = neighbors(1)
neighbors(1) = null
neighbors(0) = triangle
} else if (oPoint == points(1)) {
neighbors(0) = neighbors(2)
neighbors(2) = null
neighbors(1) = triangle
} else if (oPoint == points(2)) {
neighbors(1) = neighbors(0)
neighbors(0) = null
neighbors(2) = triangle
} else {
throw new Exception("pointer bug")
}
}
def printDebug = println(points(0) + "," + points(1) + "," + points(2))
// Finalize edge marking
def markNeighborEdges {
for(i <- 0 to 2)
if(edges(i))
i match {
case 0 => if(neighbors(0) != null) neighbors(0).markEdge(points(1), points(2))
case 1 => if(neighbors(1) != null) neighbors(1).markEdge(points(0), points(2))
case 2 => if(neighbors(2) != null) neighbors(2).markEdge(points(0), points(1))
}
}
def markEdge(triangle: Triangle) {
for(i <- 0 to 2)
if(edges(i))
i match {
case 0 => triangle.markEdge(points(1), points(2))
case 1 => triangle.markEdge(points(0), points(2))
case 2 => triangle.markEdge(points(0), points(1))
}
}
def markEdge(T: ArrayBuffer[Triangle]) {
for(t <- T) {
for(i <- 0 to 2)
if(t.edges(i))
i match {
case 0 => markEdge(t.points(1), t.points(2))
case 1 => markEdge(t.points(0), t.points(2))
case 2 => markEdge(t.points(0), t.points(1))
}
}
}
// Mark edge as constrained
def markEdge(p: Point, q: Point) {
if((q == points(0) && p == points(1)) || (q == points(1) && p == points(0))) {
finalized = true
edges(2) = true
} else if ((q == points(0) && p == points(2)) || (q == points(2) && p == points(0))) {
edges(1) = true
finalized = true
} else if ((q == points(1) && p == points(2)) || (q == points(2) && p == points(1))){
finalized = true
edges(0) = true
}
}
def area = {
val b = points(0).x - points(1).x
val h = points(2).y - points(1).y
Math.abs((b*h*0.5f))
}
def centroid: Point = {
val cx = (points(0).x + points(1).x + points(2).x)/3f
val cy = (points(0).y + points(1).y + points(2).y)/3f
Point(cx, cy)
}
def thin: Boolean = {
val a1 = (points(1) - points(0))
val b1 = (points(2) - points(0))
val a2 = (points(0) - points(1))
val b2 = (points(2) - points(1))
val angle1 = Math.abs(Math.atan2(a1 cross b1, a1 dot b1))
val angle2 = Math.abs(Math.atan2(a2 cross b2, a2 dot b2))
val angle3 = Math.Pi - angle1 - angle2
// 30 degrees
val minAngle = Math.Pi/6
(angle1 <= minAngle || angle2 <= minAngle || angle3 <= minAngle)
}
// Compute barycentric coordinates (u, v, w) for
// point p with respect to triangle
// From "Real-Time Collision Detection" by Christer Ericson
def barycentric(p: Point): List[Float] = {
val v0 = points(1) - points(0)
val v1 = points(2) - points(0)
val v2 = p - points(0)
val d00 = v0 dot v0
val d01 = v0 dot v1
val d11 = v1 dot v1
val d20 = v2 dot v0
val d21 = v2 dot v1
val denom = d00 * d11 - d01 * d01
val v = (d11 * d20 - d01 * d21) / denom
val w = (d00 * d21 - d01 * d20) / denom
val u = 1.0f - v - w
List(u, v, w)
}
}

View File

@ -1,339 +0,0 @@
package org.poly2tri.utils
import scala.collection.mutable.ArrayBuffer
import shapes.Point
object Util {
// Almost zero
val COLLINEAR_SLOP = 0.1f
val epsilon = exactinit
val ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon
val iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon
// Refursive merge sort, from "Scala By Example," by Martin Odersky
def msort[A](less: (A, A) => Boolean)(xs: List[A]): List[A] = {
def merge(xs1: List[A], xs2: List[A]): List[A] =
if (xs1.isEmpty) xs2
else if (xs2.isEmpty) xs1
else if (less(xs1.head, xs2.head)) xs1.head :: merge(xs1.tail, xs2)
else xs2.head :: merge(xs1, xs2.tail)
val n = xs.length/2
if (n == 0) xs
else merge(msort(less)(xs take n), msort(less)(xs drop n))
}
// Insertion sort - best for lists <= 10 elements
def insertSort[A](less: (A, A) => Boolean)(xs: ArrayBuffer[A]): ArrayBuffer[A] = {
var j = 1
while(j < xs.size){
val key = xs(j)
var i = j-1
while(i >= 0 && less(key, xs(i)) ){
xs(i+1) = xs(i)
i -= 1
}
xs(i+1)=key
j += 1
}
xs
}
// Tests if the given points are collinear
def collinear(p1: Point, p2: Point, p3: Point): Boolean = {
val d = Math.abs((p2-p1) cross (p1-p3))
if(d <= COLLINEAR_SLOP)
true
else
false
}
/* From Jonathan Shewchuk's "Adaptive Precision Floating-Point Arithmetic
* and Fast Robust Predicates for Computational Geometry"
* See: http://www.cs.cmu.edu/~quake/robust.html
*/
def exactinit = {
var every_other = true
var half = 0.5
var splitter = 1.0
var epsilon = 1.0
var check = 1.0
var lastcheck = 0.0
do {
lastcheck = check
epsilon *= half
if (every_other) {
splitter *= 2.0
}
every_other = !every_other
check = 1.0 + epsilon
} while ((check != 1.0) && (check != lastcheck))
epsilon
}
// Approximate 2D orientation test. Nonrobust.
// Return: positive if point a, b, and c are counterclockwise
// negative if point a, b, and c are clockwise
// zero if points are collinear
// See: http://www-2.cs.cmu.edu/~quake/robust.html
def orient(a: Point, b: Point, p: Point): Float = {
val acx = a.x - p.x
val bcx = b.x - p.x
val acy = a.y - p.y
val bcy = b.y - p.y
acx * bcy - acy * bcx
}
// Adaptive exact 2D orientation test. Robust. By Jonathan Shewchuk
// Return: positive if point a, b, and c are counterclockwise
// negative if point a, b, and c are clockwise
// zero if points are collinear
// See: http://www-2.cs.cmu.edu/~quake/robust.html
def orient2d(pa: Point, pb: Point, pc: Point): Double = {
val detleft: Double = (pa.x - pc.x) * (pb.y - pc.y)
val detright: Double = (pa.y - pc.y) * (pb.x - pc.x)
val det = detleft - detright
var detsum = 0.0
if (detleft > 0.0) {
if (detright <= 0.0) {
return det;
} else {
detsum = detleft + detright
}
} else if (detleft < 0.0) {
if (detright >= 0.0) {
return det
} else {
detsum = -detleft - detright
}
} else {
return det
}
val errbound = ccwerrboundA * detsum
if ((det >= errbound) || (-det >= errbound)) {
return det
} else {
// Cheat a little bit.... we have a degenerate triangle
val c = pc * 0.1e-6f
return orient2d(pa, pb, c)
}
}
// Original by Jonathan Shewchuk
// http://www.ics.uci.edu/~eppstein/junkyard/circumcenter.html
def circumcenter(a: Point, b: Point, c: Point): Point = {
/* Use coordinates relative to point `a' of the triangle. */
val xba = b.x - a.x
val yba = b.y - a.y
val xca = c.x - a.x
val yca = c.y - a.y
/* Squares of lengths of the edges incident to `a'. */
val balength = xba * xba + yba * yba
val calength = xca * xca + yca * yca
/* Calculate the denominator of the formulae. */
val denominator = 0.5 / orient2d(b, c, a)
/* Calculate offset (from `a') of circumcenter. */
val xcirca = (yca * balength - yba * calength) * denominator
val ycirca = (xba * calength - xca * balength) * denominator
a + Point(xcirca.toFloat, ycirca.toFloat)
}
// Returns triangle circumcircle point and radius
def circumCircle(a: Point, b: Point, c: Point): Tuple2[Point, Float] = {
val A = det(a, b, c)
val C = detC(a, b, c)
val bx1 = Point(a.x*a.x + a.y*a.y, a.y)
val bx2 = Point(b.x*b.x + b.y*b.y, b.y)
val bx3 = Point(c.x*c.x + c.y*c.y, c.y)
val bx = det(bx1, bx2, bx3)
val by1 = Point(a.x*a.x + a.y*a.y, a.x)
val by2 = Point(b.x*b.x + b.y*b.y, b.x)
val by3 = Point(c.x*c.x + c.y*c.y, c.x)
val by = det(by1, by2, by3)
val x = bx / (2*A)
val y = by / (2*A)
val center = Point(x, y)
val radius = Math.sqrt(bx*bx + by*by - 4*A*C).toFloat / (2*Math.abs(A))
(center, radius)
}
private def det(p1: Point, p2: Point, p3: Point): Float = {
val a11 = p1.x
val a12 = p1.y
val a13,a23,a33 = 1f
val a21 = p2.x
val a22 = p2.y
val a31 = p3.x
val a32 = p3.y
a11*(a22*a33-a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32-a22*a31)
}
private def detC(p1: Point, p2: Point, p3: Point): Float = {
val a11 = p1.x*p1.x + p1.y*p1.y
val a12 = p1.x
val a13 = p1.y
val a21 = p2.x*p2.x + p2.y*p2.y
val a22 = p2.x
val a23 = p2.y
val a31 = p3.x*p3.x + p3.y*p3.y
val a32 = p3.x
val a33 = p3.y
a11*(a22*a33-a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32-a22*a31)
}
/* Approximate 2D incircle test. Nonrobust. By Jonathan Shewchuk
* Return a positive value if the point pd lies inside the
* circle passing through pa, pb, and pc; a negative value if
* it lies outside; and zero if the four points are cocircular.
* The points pa, pb, and pc must be in counterclockwise
* order, or the sign of the result will be reversed.
*/
def incirclefast(pa: Point, pb: Point, pc: Point, pd: Point): Boolean = {
val adx = pa.x - pd.x
val ady = pa.y - pd.y
val bdx = pb.x - pd.x
val bdy = pb.y - pd.y
val cdx = pc.x - pd.x
val cdy = pc.y - pd.y
val abdet = adx * bdy - bdx * ady
val bcdet = bdx * cdy - cdx * bdy
val cadet = cdx * ady - adx * cdy
val alift = adx * adx + ady * ady
val blift = bdx * bdx + bdy * bdy
val clift = cdx * cdx + cdy * cdy
alift * bcdet + blift * cadet + clift * abdet >= 0
}
/* Robust 2D incircle test, modified. Original By Jonathan Shewchuk
* Return a positive value if the point pd lies inside the
* circle passing through pa, pb, and pc; a negative value if
* it lies outside; and zero if the four points are cocircular.
* The points pa, pb, and pc must be in counterclockwise
* order, or the sign of the result will be reversed.
*/
def incircle(pa: Point, pb: Point, pc: Point, pd: Point): Boolean = {
val adx = pa.x - pd.x
val bdx = pb.x - pd.x
val cdx = pc.x - pd.x
val ady = pa.y - pd.y
val bdy = pb.y - pd.y
val cdy = pc.y - pd.y
val bdxcdy = bdx * cdy
val cdxbdy = cdx * bdy
val alift = adx * adx + ady * ady
val cdxady = cdx * ady
val adxcdy = adx * cdy
val blift = bdx * bdx + bdy * bdy
val adxbdy = adx * bdy
val bdxady = bdx * ady
val clift = cdx * cdx + cdy * cdy
val det = alift * (bdxcdy - cdxbdy) +
blift * (cdxady - adxcdy) +
clift * (adxbdy - bdxady)
val permanent = (Math.abs(bdxcdy) + Math.abs(cdxbdy)) * alift +
(Math.abs(cdxady) + Math.abs(adxcdy)) * blift +
(Math.abs(adxbdy) + Math.abs(bdxady)) * clift
val errbound = iccerrboundA * permanent
if ((det > errbound) || (-det > errbound)) {
return det >= 0
} else {
// Cheat a little bit.... we have a degenerate triangle
val d = pd * 0.1e-6f
return incircle(pa, pb, pc, d)
}
}
def closestPtSegment(c: Point, a: Point, b: Point): Point = {
val ab = b - a
var t = (c - a) dot ab
if (t <= 0.0f)
return a
else {
val denom = ab dot ab
if (t >= denom)
return b
else {
t = t / denom
}
}
a + (ab * t)
}
}
/** The object <code>Random</code> offers a default implementation
* of scala.util.Random and random-related convenience methods.
*
* @since 2.8
* From Scala 2.8 standard library
*/
object Random extends scala.util.Random {
/** Returns a new sequence in random order.
* @param seq the sequence to shuffle
* @return the shuffled sequence
*/
def shuffle[T](buf: ArrayBuffer[T]): ArrayBuffer[T] = {
// It would be better if this preserved the shape of its container, but I have
// again been defeated by the lack of higher-kinded type inference. I can
// only make it work that way if it's called like
// shuffle[Int,List](List.range(0,100))
// which nicely defeats the "convenience" portion of "convenience method".
def swap(i1: Int, i2: Int) {
val tmp = buf(i1)
buf(i1) = buf(i2)
buf(i2) = tmp
}
for (n <- buf.length to 2 by -1) {
val k = nextInt(n)
swap(n - 1, k)
}
buf
}
}