mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
added holes; restructured code; bug hunting
This commit is contained in:
parent
7f999024b9
commit
b67a9b4495
94
data/dude.dat
Normal file
94
data/dude.dat
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
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
|
96
data/dude.svg
Normal file
96
data/dude.svg
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?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>
|
After Width: | Height: | Size: 5.4 KiB |
@ -58,6 +58,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
// Sedidel Triangulator
|
// Sedidel Triangulator
|
||||||
var seidel: Triangulator = null
|
var seidel: Triangulator = null
|
||||||
var segments: ArrayBuffer[Segment] = null
|
var segments: ArrayBuffer[Segment] = null
|
||||||
|
var chestSegs: ArrayBuffer[Segment] = null
|
||||||
|
var headSegs: ArrayBuffer[Segment] = null
|
||||||
|
|
||||||
// EarClip Triangulator
|
// EarClip Triangulator
|
||||||
val earClip = new EarClip
|
val earClip = new EarClip
|
||||||
@ -86,8 +88,9 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
val strange = "data/strange.dat"
|
val strange = "data/strange.dat"
|
||||||
val i18 = "data/i.18"
|
val i18 = "data/i.18"
|
||||||
val tank = "data/tank.dat"
|
val tank = "data/tank.dat"
|
||||||
|
val dude = "data/dude.dat"
|
||||||
|
|
||||||
var currentModel = nazcaHeron
|
var currentModel = dude
|
||||||
var doCDT = true
|
var doCDT = true
|
||||||
|
|
||||||
var mouseButton = 0
|
var mouseButton = 0
|
||||||
@ -112,12 +115,12 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
|
|
||||||
def render(container: GameContainer, g: Graphics) {
|
def render(container: GameContainer, g: Graphics) {
|
||||||
|
|
||||||
g.drawString("'1-8' to cycle models, mouse to pan & zoom", 10, 520)
|
g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 520)
|
||||||
g.drawString("'SPACE' to show Seidel debug info", 10, 532)
|
g.drawString("'SPACE' to show Seidel debug info", 10, 532)
|
||||||
g.drawString("'m' to show trapezoidal map (Seidel debug mode)", 10, 544)
|
g.drawString("'m' to show trapezoidal map (Seidel debug mode)", 10, 544)
|
||||||
g.drawString("'e' to switch Seidel / EarClip", 10, 556)
|
g.drawString("'e' to switch Seidel / EarClip", 10, 556)
|
||||||
g.drawString("'d' to switch CDT / Seidel", 10, 568)
|
g.drawString("'d' to switch CDT / Seidel", 10, 568)
|
||||||
g.drawString("'c' to how CDT mesh", 10, 580)
|
g.drawString("'c' to show CDT mesh, 's' to draw edges", 10, 580)
|
||||||
|
|
||||||
g.scale(scaleFactor, scaleFactor)
|
g.scale(scaleFactor, scaleFactor)
|
||||||
g.translate(deltaX, deltaY)
|
g.translate(deltaX, deltaY)
|
||||||
@ -177,25 +180,25 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles
|
val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles
|
||||||
|
|
||||||
draw.foreach( t => {
|
draw.foreach( t => {
|
||||||
|
if(true) {
|
||||||
for(i <- 0 to 2) {
|
for(i <- 0 to 2) {
|
||||||
val s = t.points(i)
|
val s = t.points(i)
|
||||||
val e = if(i == 2) t.points(0) else t.points(i + 1)
|
val e = if(i == 2) t.points(0) else t.points(i + 1)
|
||||||
val j = if(i == 0) 2 else if(i == 1) 0 else 1
|
val j = if(i == 0) 2 else if(i == 1) 0 else 1
|
||||||
if(t.edges(j))
|
if(t.edges(j))
|
||||||
g.setColor(yellow)
|
g.setColor(yellow)
|
||||||
else
|
else
|
||||||
g.setColor(red)
|
g.setColor(red)
|
||||||
g.drawLine(s.x,s.y,e.x,e.y)
|
g.drawLine(s.x,s.y,e.x,e.y)
|
||||||
}
|
}
|
||||||
/*
|
} else {
|
||||||
val triangle = new Polygon
|
val triangle = new Polygon
|
||||||
triangle.addPoint(t.points(0).x, t.points(0).y)
|
triangle.addPoint(t.points(0).x, t.points(0).y)
|
||||||
triangle.addPoint(t.points(1).x, t.points(1).y)
|
triangle.addPoint(t.points(1).x, t.points(1).y)
|
||||||
triangle.addPoint(t.points(2).x, t.points(2).y)
|
triangle.addPoint(t.points(2).x, t.points(2).y)
|
||||||
g.setColor(red)
|
g.setColor(red)
|
||||||
g.draw(triangle)
|
g.draw(triangle)
|
||||||
*/
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
slCDT.debugTriangles.foreach( t => {
|
slCDT.debugTriangles.foreach( t => {
|
||||||
@ -217,6 +220,18 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,6 +304,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
if(c == '6') selectModel(i18)
|
if(c == '6') selectModel(i18)
|
||||||
if(c == '7') selectModel(nazcaHeron)
|
if(c == '7') selectModel(nazcaHeron)
|
||||||
if(c == '8') selectModel(tank)
|
if(c == '8') selectModel(tank)
|
||||||
|
if(c == '9') selectModel(dude)
|
||||||
if(c == 's') drawSegs = !drawSegs
|
if(c == 's') drawSegs = !drawSegs
|
||||||
if(c == 'c') drawcdtMesh = !drawcdtMesh
|
if(c == 'c') drawcdtMesh = !drawcdtMesh
|
||||||
if(c == 'e') {drawEarClip = !drawEarClip; drawCDT = false; selectModel(currentModel)}
|
if(c == 'e') {drawEarClip = !drawEarClip; drawCDT = false; selectModel(currentModel)}
|
||||||
@ -297,50 +313,47 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
def selectModel(model: String) {
|
def selectModel(model: String) {
|
||||||
model match {
|
model match {
|
||||||
case "data/nazca_monkey.dat" =>
|
case "data/nazca_monkey.dat" =>
|
||||||
CDT.clearPoint = 50
|
val clearPoint = Point(418, 282)
|
||||||
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500)
|
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500, clearPoint)
|
||||||
case "data/bird.dat" =>
|
case "data/bird.dat" =>
|
||||||
CDT.clearPoint = 80
|
val clearPoint = Point(400, 300)
|
||||||
loadModel(bird, 25f, Point(400, 300), 350)
|
loadModel(bird, 25f, Point(400, 300), 350, clearPoint)
|
||||||
case "data/i.snake" =>
|
case "data/i.snake" =>
|
||||||
doCDT = true; drawCDT = true
|
val clearPoint = Point(336f, 196f)
|
||||||
CDT.clearPoint = 6
|
loadModel(snake, 10f, Point(600, 300), 10, clearPoint)
|
||||||
loadModel(snake, 10f, Point(600, 300), 10)
|
|
||||||
case "data/star.dat" =>
|
case "data/star.dat" =>
|
||||||
doCDT = true; drawCDT = true
|
val clearPoint = Point(400, 204)
|
||||||
CDT.clearPoint = 6
|
loadModel(star, -1f, Point(0f, 0f), 10, clearPoint)
|
||||||
loadModel(star, -1f, Point(0f, 0f), 10)
|
|
||||||
case "data/strange.dat" =>
|
case "data/strange.dat" =>
|
||||||
doCDT = true; drawCDT = true
|
val clearPoint = Point(400, 268)
|
||||||
CDT.clearPoint = 13
|
loadModel(strange, -1f, Point(0f, 0f), 15, clearPoint)
|
||||||
loadModel(strange, -1f, Point(0f, 0f), 15)
|
|
||||||
case "data/i.18" =>
|
case "data/i.18" =>
|
||||||
doCDT = true; drawCDT = true
|
val clearPoint = Point(510, 385)
|
||||||
CDT.clearPoint = 7
|
loadModel(i18, 20f, Point(600f, 500f), 20, clearPoint)
|
||||||
loadModel(i18, 20f, Point(600f, 500f), 20)
|
|
||||||
case "data/nazca_heron_old.dat" =>
|
case "data/nazca_heron_old.dat" =>
|
||||||
//doCDT = false; drawCDT = false; drawcdtMesh = false
|
val clearPoint = Point(85, 290)
|
||||||
CDT.clearPoint = 100
|
loadModel(nazcaHeron, 4.2f, Point(400f, 300f), 1500, clearPoint)
|
||||||
loadModel(nazcaHeron, 4.2f, Point(400f, 300f), 1500)
|
|
||||||
case "data/tank.dat" =>
|
case "data/tank.dat" =>
|
||||||
//doCDT = false; drawCDT = false; drawcdtMesh = false
|
val clearPoint = Point(450, 350)
|
||||||
doCDT = true; drawCDT = true
|
loadModel(tank, -1f, Point(100f, 0f), 10, clearPoint)
|
||||||
CDT.clearPoint = 38
|
case "data/dude.dat" =>
|
||||||
loadModel(tank, -1f, Point(100f, 0f), 10)
|
val clearPoint = Point(365, 427)
|
||||||
|
loadModel(dude, -1f, Point(100f, -200f), 10, clearPoint)
|
||||||
case _ =>
|
case _ =>
|
||||||
assert(false)
|
assert(false)
|
||||||
|
|
||||||
}
|
}
|
||||||
currentModel = model
|
currentModel = model
|
||||||
}
|
}
|
||||||
|
|
||||||
def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) {
|
def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int, clearPoint: Point) {
|
||||||
|
|
||||||
println
|
println
|
||||||
println("************** " + model + " **************")
|
println("************** " + model + " **************")
|
||||||
|
|
||||||
polyX = new ArrayBuffer[Float]
|
polyX = new ArrayBuffer[Float]
|
||||||
polyY = new ArrayBuffer[Float]
|
polyY = new ArrayBuffer[Float]
|
||||||
val points = new ArrayBuffer[Point]
|
var points = new ArrayBuffer[Point]
|
||||||
|
|
||||||
val angle = Math.Pi
|
val angle = Math.Pi
|
||||||
for (line <- Source.fromFile(model).getLines) {
|
for (line <- Source.fromFile(model).getLines) {
|
||||||
@ -359,7 +372,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
segments = new ArrayBuffer[Segment]
|
segments = new ArrayBuffer[Segment]
|
||||||
for(i <- 0 until polyX.size-1)
|
for(i <- 0 until points.size-1)
|
||||||
segments += new Segment(points(i), points(i+1))
|
segments += new Segment(points(i), points(i+1))
|
||||||
segments += new Segment(points.first, points.last)
|
segments += new Segment(points.first, points.last)
|
||||||
|
|
||||||
@ -367,9 +380,49 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
println
|
println
|
||||||
|
|
||||||
if(doCDT) {
|
if(doCDT) {
|
||||||
|
|
||||||
|
val pts = points.toArray
|
||||||
|
|
||||||
val t1 = System.nanoTime
|
val t1 = System.nanoTime
|
||||||
slCDT = CDT.init(points)
|
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)
|
||||||
|
chestSegs += new Segment(headHole(i), headHole(i+1))
|
||||||
|
chestSegs += new Segment(headHole.first, headHole.last)
|
||||||
|
|
||||||
|
slCDT.addHole(headHole)
|
||||||
|
slCDT.addHole(chestHole)
|
||||||
|
}
|
||||||
|
|
||||||
|
slCDT triangulate
|
||||||
val runTime = System.nanoTime - t1
|
val runTime = System.nanoTime - t1
|
||||||
|
|
||||||
println("CDT average (ms) = " + runTime*1e-6)
|
println("CDT average (ms) = " + runTime*1e-6)
|
||||||
println("Number of triangles = " + slCDT.triangles.size)
|
println("Number of triangles = " + slCDT.triangles.size)
|
||||||
println
|
println
|
||||||
|
@ -101,7 +101,7 @@ class AFront(iTriangle: Triangle) {
|
|||||||
var marked = false
|
var marked = false
|
||||||
|
|
||||||
// Scan the advancing front and update Node triangle pointers
|
// Scan the advancing front and update Node triangle pointers
|
||||||
while(node != null && node != eNode.next) {
|
while(node != null && node != eNode) {
|
||||||
|
|
||||||
T2.foreach(t => {
|
T2.foreach(t => {
|
||||||
if(t.contains(node.point, node.next.point))
|
if(t.contains(node.point, node.next.point))
|
||||||
|
@ -30,11 +30,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.poly2tri.cdt
|
package org.poly2tri.cdt
|
||||||
|
|
||||||
import scala.collection.mutable.{ArrayBuffer, Set}
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
import shapes.{Segment, Point, Triangle}
|
import shapes.{Segment, Point, Triangle}
|
||||||
import utils.Util
|
import utils.Util
|
||||||
import seidel.MonotoneMountain
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sweep-line, Constrained Delauney Triangulation (CDT)
|
* Sweep-line, Constrained Delauney Triangulation (CDT)
|
||||||
@ -45,14 +44,25 @@ import seidel.MonotoneMountain
|
|||||||
// NOTE: May need to implement edge insertion which combines advancing front (AF)
|
// NOTE: May need to implement edge insertion which combines advancing front (AF)
|
||||||
// and triangle traversal respectively. See figure 14(a) from Domiter et al.
|
// and triangle traversal respectively. See figure 14(a) from Domiter et al.
|
||||||
// Although it may not be necessary for simple polygons....
|
// Although it may not be necessary for simple polygons....
|
||||||
object CDT {
|
|
||||||
|
|
||||||
// Inital triangle factor
|
class CDT(polyLine: Array[Point], clearPoint: Point) {
|
||||||
val ALPHA = 0.3f
|
|
||||||
var clearPoint = 0
|
|
||||||
|
|
||||||
// Triangulate simple polygon
|
// Triangle list
|
||||||
def init(points: ArrayBuffer[Point]): CDT = {
|
def triangles = mesh.triangles
|
||||||
|
def triangleMesh = mesh.map
|
||||||
|
def debugTriangles = mesh.debug
|
||||||
|
|
||||||
|
// Initialize edges
|
||||||
|
initEdges(polyLine)
|
||||||
|
|
||||||
|
// Add a hole
|
||||||
|
def addHole(holePolyLine: Array[Point]) {
|
||||||
|
initEdges(holePolyLine)
|
||||||
|
points = points ++ holePolyLine.toList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulate simple polygon with holes
|
||||||
|
def triangulate {
|
||||||
|
|
||||||
var xmax, xmin = points.first.x
|
var xmax, xmin = points.first.x
|
||||||
var ymax, ymin = points.first.y
|
var ymax, ymin = points.first.y
|
||||||
@ -71,33 +81,39 @@ object CDT {
|
|||||||
val p1 = Point(xmin - deltaX, ymin - deltaY)
|
val p1 = Point(xmin - deltaX, ymin - deltaY)
|
||||||
val p2 = Point(xmax + deltaX, p1.y)
|
val p2 = Point(xmax + deltaX, p1.y)
|
||||||
|
|
||||||
val segments = initSegments(points)
|
// Sort the points along y-axis
|
||||||
val sortedPoints = pointSort(points)
|
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
|
||||||
|
|
||||||
val tPoints = Array(sortedPoints(0), p1, p2)
|
|
||||||
val iTriangle = new Triangle(tPoints)
|
|
||||||
new CDT(sortedPoints, segments, iTriangle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create segments and connect end points; update edge event pointer
|
// Create edges and connect end points; update edge event pointer
|
||||||
private def initSegments(points: ArrayBuffer[Point]): List[Segment] = {
|
private def initEdges(pts: Array[Point]) {
|
||||||
|
|
||||||
var segments = List[Segment]()
|
// Connect pts
|
||||||
|
for(i <- 0 until pts.size-1) {
|
||||||
for(i <- 0 until points.size-1) {
|
val endPoints = validatePoints(pts(i), pts(i+1))
|
||||||
val endPoints = validatePoints(points(i), points(i+1))
|
val edge = new Segment(endPoints(0), endPoints(1))
|
||||||
segments = new Segment(endPoints(0), endPoints(1)) :: segments
|
endPoints(1).edges += edge
|
||||||
endPoints(1).edges += segments.first
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val endPoints = validatePoints(points.first, points.last)
|
// Connect endpoints
|
||||||
segments = new Segment(endPoints(0), endPoints(1)) :: segments
|
val endPoints = validatePoints(pts.first, pts.last)
|
||||||
endPoints(1).edges += segments.first
|
val edge = new Segment(endPoints(0), endPoints(1))
|
||||||
|
endPoints(1).edges += edge
|
||||||
|
|
||||||
segments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def validatePoints(p1: Point, p2: Point): List[Point] = {
|
private def validatePoints(p1: Point, p2: Point): List[Point] = {
|
||||||
|
|
||||||
if(p1.y > p2.y) {
|
if(p1.y > p2.y) {
|
||||||
// For CDT we want q to be the point with > y
|
// For CDT we want q to be the point with > y
|
||||||
@ -108,6 +124,7 @@ object CDT {
|
|||||||
if(p1.x > p2.x) {
|
if(p1.x > p2.x) {
|
||||||
return List(p2, p1)
|
return List(p2, p1)
|
||||||
} else if(p1.x == p2.x) {
|
} else if(p1.x == p2.x) {
|
||||||
|
println(p1 + "," + p2)
|
||||||
throw new Exception("Duplicate point")
|
throw new Exception("Duplicate point")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,50 +132,17 @@ object CDT {
|
|||||||
List(p1, p2)
|
List(p1, p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insertion sort is one of the fastest algorithms for sorting arrays containing
|
|
||||||
// fewer than ten elements, or for lists that are already mostly sorted.
|
|
||||||
// Merge sort: O(n log n)
|
// Merge sort: O(n log n)
|
||||||
private def pointSort(points: ArrayBuffer[Point]): List[Point] = {
|
private def pointSort: List[Point] =
|
||||||
if(points.size < 10)
|
Util.msort((p1: Point, p2: Point) => p1 > p2)(points)
|
||||||
Util.insertSort((p1: Point, p2: Point) => p1 > p2)(points).toList
|
|
||||||
else
|
|
||||||
Util.msort((p1: Point, p2: Point) => p1 > p2)(points.toList)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Triangle) {
|
|
||||||
|
|
||||||
|
|
||||||
// Triangle list
|
|
||||||
def triangles = mesh.triangles
|
|
||||||
def triangleMesh = mesh.map
|
|
||||||
def debugTriangles = mesh.debug
|
|
||||||
|
|
||||||
// The triangle mesh
|
|
||||||
private val mesh = new Mesh(iTriangle)
|
|
||||||
// Advancing front
|
|
||||||
private val aFront = new AFront(iTriangle)
|
|
||||||
|
|
||||||
private val PI_2 = Math.Pi/2
|
|
||||||
private val PI_34 = Math.Pi*3/4
|
|
||||||
|
|
||||||
// Triangle used to clean interior
|
|
||||||
var cleanTri: Triangle = null
|
|
||||||
|
|
||||||
// Sweep points; build mesh
|
|
||||||
sweep
|
|
||||||
// Finalize triangulation
|
|
||||||
finalization
|
|
||||||
|
|
||||||
// Implement sweep-line
|
// Implement sweep-line
|
||||||
private def sweep {
|
private def sweep {
|
||||||
|
|
||||||
for(i <- 1 until points.size) {
|
for(i <- 1 until 36 /*points.size*/) {
|
||||||
val point = points(i)
|
val point = points(i)
|
||||||
// Process Point event
|
// Process Point event
|
||||||
val node = pointEvent(point)
|
val node = pointEvent(point)
|
||||||
if(i == CDT.clearPoint) {cleanTri = node.triangle; mesh.debug += cleanTri}
|
|
||||||
// Process edge events
|
// Process edge events
|
||||||
point.edges.foreach(e => edgeEvent(e, node))
|
point.edges.foreach(e => edgeEvent(e, node))
|
||||||
}
|
}
|
||||||
@ -168,10 +152,16 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
// Final step in the sweep-line CDT algo
|
// Final step in the sweep-line CDT algo
|
||||||
// Clean exterior triangles
|
// Clean exterior triangles
|
||||||
private def finalization {
|
private def finalization {
|
||||||
|
var found = false
|
||||||
mesh.map.foreach(m => m.markNeighborEdges)
|
mesh.map.foreach(m => {
|
||||||
|
if(!found)
|
||||||
|
if(m.pointIn(clearPoint)) {
|
||||||
|
found = true
|
||||||
|
cleanTri = m
|
||||||
|
}
|
||||||
|
m.markNeighborEdges
|
||||||
|
})
|
||||||
mesh clean cleanTri
|
mesh clean cleanTri
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Point event
|
// Point event
|
||||||
@ -207,11 +197,8 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
tList += firstTriangle
|
tList += firstTriangle
|
||||||
|
|
||||||
// Not sure why tList.last is null sometimes....
|
// Not sure why tList.last is null sometimes....
|
||||||
while(tList.last != null && !tList.last.contains(edge.p))
|
while(!tList.last.contains(edge.p))
|
||||||
tList += tList.last.findNeighbor(edge.p - edge.q)
|
tList += tList.last.findNeighbor(edge.p)
|
||||||
|
|
||||||
if(tList.last == null)
|
|
||||||
tList -= tList.last
|
|
||||||
|
|
||||||
// Neighbor triangles
|
// Neighbor triangles
|
||||||
val nTriangles = new ArrayBuffer[Triangle]
|
val nTriangles = new ArrayBuffer[Triangle]
|
||||||
@ -221,7 +208,9 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
tList.foreach(t => {
|
tList.foreach(t => {
|
||||||
t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n)
|
t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n)
|
||||||
mesh.map -= t
|
mesh.map -= t
|
||||||
|
//mesh.debug += t
|
||||||
})
|
})
|
||||||
|
//nTriangles.foreach(n => mesh.debug += n)
|
||||||
|
|
||||||
val lPoints = new ArrayBuffer[Point]
|
val lPoints = new ArrayBuffer[Point]
|
||||||
val rPoints = new ArrayBuffer[Point]
|
val rPoints = new ArrayBuffer[Point]
|
||||||
@ -261,8 +250,11 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
val point1 = if(ahead) edge.q else edge.p
|
val point1 = if(ahead) edge.q else edge.p
|
||||||
val point2 = if(ahead) edge.p else edge.q
|
val point2 = if(ahead) edge.p else edge.q
|
||||||
|
|
||||||
val sNode = aFront.locate(point1)
|
val sNode = if(ahead) node else aFront.locate(point1).prev
|
||||||
val eNode = aFront.locate(point2)
|
val eNode = aFront.locate(point2).next
|
||||||
|
|
||||||
|
//mesh.debug += sNode.triangle
|
||||||
|
//mesh.debug += eNode.triangle
|
||||||
|
|
||||||
aFront.constrainedEdge(sNode, eNode, T1, T2, edge)
|
aFront.constrainedEdge(sNode, eNode, T1, T2, edge)
|
||||||
|
|
||||||
@ -270,8 +262,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
T1.last markEdge(point1, point2)
|
T1.last markEdge(point1, point2)
|
||||||
T2.last markEdge(point1, point2)
|
T2.last markEdge(point1, point2)
|
||||||
// Copy constraied edges from old triangles
|
// Copy constraied edges from old triangles
|
||||||
T1.foreach(t => t.markEdge(tList))
|
T1.foreach(t => {t.markEdge(tList)/*;mesh.debug += t*/})
|
||||||
T2.foreach(t => t.markEdge(tList))
|
T2.foreach(t => {t.markEdge(tList)/*;mesh.debug += t*/})
|
||||||
|
|
||||||
|
var n = sNode
|
||||||
|
while(n != eNode) {
|
||||||
|
mesh.debug += n.triangle
|
||||||
|
n = n.next
|
||||||
|
}
|
||||||
|
|
||||||
} else if(firstTriangle == null) {
|
} else if(firstTriangle == null) {
|
||||||
|
|
||||||
@ -323,11 +321,11 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
|
|
||||||
// Update neigbor pointers for edge event
|
// Update neigbor pointers for edge event
|
||||||
// Inneficient, but it works well...
|
// Inneficient, but it works well...
|
||||||
def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) {
|
private def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) {
|
||||||
|
|
||||||
for(t1 <- nTriangles)
|
for(t1 <- nTriangles)
|
||||||
for(t2 <- T)
|
for(t2 <- T)
|
||||||
t1.markNeighbor(t2)
|
t2.markNeighbor(t1)
|
||||||
|
|
||||||
for(i <- 0 until T.size)
|
for(i <- 0 until T.size)
|
||||||
for(j <- i+1 until T.size)
|
for(j <- i+1 until T.size)
|
||||||
@ -411,7 +409,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
|
|
||||||
// Circumcircle test.
|
// Circumcircle test.
|
||||||
// Determines if point d lies inside triangle abc's circumcircle
|
// Determines if point d lies inside triangle abc's circumcircle
|
||||||
def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = {
|
private def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = {
|
||||||
|
|
||||||
val ccw = Util.orient2d(a, b, c) > 0
|
val ccw = Util.orient2d(a, b, c) > 0
|
||||||
|
|
||||||
@ -476,4 +474,17 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,10 @@ import scala.collection.mutable.{HashSet, ArrayBuffer}
|
|||||||
|
|
||||||
import shapes.{Point, Triangle}
|
import shapes.{Point, Triangle}
|
||||||
|
|
||||||
class Mesh(initialTriangle: Triangle) {
|
class Mesh {
|
||||||
|
|
||||||
// Triangles that constitute the mesh
|
// Triangles that constitute the mesh
|
||||||
val map = HashSet(initialTriangle)
|
val map = HashSet.empty[Triangle]
|
||||||
// Debug triangles
|
// Debug triangles
|
||||||
val debug = HashSet.empty[Triangle]
|
val debug = HashSet.empty[Triangle]
|
||||||
val triangles = new ArrayBuffer[Triangle]
|
val triangles = new ArrayBuffer[Triangle]
|
||||||
|
@ -155,15 +155,18 @@ class Triangle(val points: Array[Point]) {
|
|||||||
|
|
||||||
// Locate next triangle crossed by edge
|
// Locate next triangle crossed by edge
|
||||||
def findNeighbor(e: Point): Triangle = {
|
def findNeighbor(e: Point): Triangle = {
|
||||||
if(Util.orient2d(points(0), points(1), e) < 0)
|
|
||||||
|
if(contains(e)) return this
|
||||||
|
|
||||||
|
if(Util.orient2d(points(1), points(0), e) > 0)
|
||||||
return neighbors(2)
|
return neighbors(2)
|
||||||
else if(Util.orient2d(points(1), points(2), e) < 0)
|
else if(Util.orient2d(points(2), points(1), e) > 0)
|
||||||
return neighbors(0)
|
return neighbors(0)
|
||||||
else if(Util.orient2d(points(2), points(0), e) < 0)
|
else if(Util.orient2d(points(0), points(2), e) > 0)
|
||||||
return neighbors(1)
|
return neighbors(1)
|
||||||
else
|
else
|
||||||
// Point must reside inside this triangle
|
throw new Exception("Point not found")
|
||||||
this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The neighbor clockwise to given point
|
// The neighbor clockwise to given point
|
||||||
@ -337,4 +340,10 @@ class Triangle(val points: Array[Point]) {
|
|||||||
(b*h*0.5f)
|
(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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ object Util {
|
|||||||
// negative if point a, b, and c are clockwise
|
// negative if point a, b, and c are clockwise
|
||||||
// zero if points are collinear
|
// zero if points are collinear
|
||||||
// See: http://www-2.cs.cmu.edu/~quake/robust.html
|
// See: http://www-2.cs.cmu.edu/~quake/robust.html
|
||||||
def orient(b: Point, a: Point, p: Point): Float = {
|
def orient(a: Point, b: Point, p: Point): Float = {
|
||||||
val acx = a.x - p.x
|
val acx = a.x - p.x
|
||||||
val bcx = b.x - p.x
|
val bcx = b.x - p.x
|
||||||
val acy = a.y - p.y
|
val acy = a.y - p.y
|
||||||
|
Loading…
Reference in New Issue
Block a user