mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
bug hunting
This commit is contained in:
parent
7af2a3f5b5
commit
08264e50d0
@ -39,9 +39,9 @@ class MonotoneMountain {
|
|||||||
var size = 0
|
var size = 0
|
||||||
|
|
||||||
val convexPoints = new Queue[Point]
|
val convexPoints = new Queue[Point]
|
||||||
// Monotone mountain points
|
// Monotone mountain points
|
||||||
val monoPoly = new ArrayBuffer[Point]
|
val monoPoly = new ArrayBuffer[Point]
|
||||||
// Triangles that constitute the mountain
|
// Triangles that constitute the mountain
|
||||||
val triangles = new ArrayBuffer[Array[Point]]
|
val triangles = new ArrayBuffer[Array[Point]]
|
||||||
// Used to track which side of the line we are on
|
// Used to track which side of the line we are on
|
||||||
var positive = false
|
var positive = false
|
||||||
@ -83,9 +83,6 @@ class MonotoneMountain {
|
|||||||
// create monotone polygon - for dubug purposes
|
// create monotone polygon - for dubug purposes
|
||||||
genMonoPoly
|
genMonoPoly
|
||||||
|
|
||||||
if(size == 3) {
|
|
||||||
lastTriangle
|
|
||||||
} else {
|
|
||||||
// Initialize internal angles at each nonbase vertex
|
// Initialize internal angles at each nonbase vertex
|
||||||
// Link strictly convex vertices into a list, ignore reflex vertices
|
// Link strictly convex vertices into a list, ignore reflex vertices
|
||||||
var p = head.next
|
var p = head.next
|
||||||
@ -115,17 +112,17 @@ class MonotoneMountain {
|
|||||||
if(valid(c)) convexPoints.enqueue(c)
|
if(valid(c)) convexPoints.enqueue(c)
|
||||||
}
|
}
|
||||||
assert(size <= 3, "Triangulation bug")
|
assert(size <= 3, "Triangulation bug")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def valid(p: Point) = (p.prev != null && p.next != null && convex(p))
|
def valid(p: Point) = (p.prev != null && p.next != null && convex(p))
|
||||||
|
|
||||||
// Create the monotone polygon
|
// Create the monotone polygon
|
||||||
private def genMonoPoly {
|
private def genMonoPoly {
|
||||||
var p = head
|
var p = head
|
||||||
while(p != null) {
|
while(p != null) {
|
||||||
monoPoly += p
|
monoPoly += p
|
||||||
p = p.next
|
p = p.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,15 +144,4 @@ class MonotoneMountain {
|
|||||||
else true
|
else true
|
||||||
}
|
}
|
||||||
|
|
||||||
private def lastTriangle {
|
|
||||||
val triangle = new Array[Point](3)
|
|
||||||
var i = 0
|
|
||||||
var p = head
|
|
||||||
while(p != null) {
|
|
||||||
triangle(i) = p
|
|
||||||
p = p.next
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
triangles += triangle
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ case class Point(val x: Float, val y: Float) {
|
|||||||
def dot(p: Point) = x * p.x + y * p.y
|
def dot(p: Point) = x * p.x + y * p.y
|
||||||
def length = Math.sqrt(x * x + y * y).toFloat
|
def length = Math.sqrt(x * x + y * y).toFloat
|
||||||
def normalize = this / length
|
def normalize = this / length
|
||||||
|
def !(p: Point) = !(p.x == x && p.y == y)
|
||||||
|
|
||||||
def <(p: Point) = {
|
def <(p: Point) = {
|
||||||
if(p.x == x)
|
if(p.x == x)
|
||||||
|
@ -60,7 +60,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
var quit = false
|
var quit = false
|
||||||
var debug = false
|
var debug = false
|
||||||
var drawMap = false
|
var drawMap = false
|
||||||
var drawSegs = false
|
var drawSegs = true
|
||||||
var hiLighter = 0
|
var hiLighter = 0
|
||||||
|
|
||||||
def init(container: GameContainer) {
|
def init(container: GameContainer) {
|
||||||
@ -104,10 +104,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
g.setColor(red)
|
g.setColor(red)
|
||||||
g.draw(triangle)
|
g.draw(triangle)
|
||||||
}
|
}
|
||||||
val triangle = new Polygon
|
//val triangle = new Polygon
|
||||||
tesselator.triangles(hiLighter).foreach(p => triangle.addPoint(p.x, p.y))
|
//tesselator.triangles(hiLighter).foreach(p => triangle.addPoint(p.x, p.y))
|
||||||
g.setColor(blue)
|
//g.setColor(blue)
|
||||||
g.draw(triangle)
|
//g.draw(triangle)
|
||||||
} else if (debug && drawMap){
|
} else if (debug && drawMap){
|
||||||
for(mp <- tesselator.monoPolies) {
|
for(mp <- tesselator.monoPolies) {
|
||||||
val poly = new Polygon
|
val poly = new Polygon
|
||||||
@ -189,7 +189,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
segments += new Segment(p16, p1)
|
segments += new Segment(p16, p1)
|
||||||
|
|
||||||
tesselator = new Triangulator(segments)
|
tesselator = new Triangulator(segments)
|
||||||
tesselator.process
|
tesselator process
|
||||||
}
|
}
|
||||||
|
|
||||||
def star {
|
def star {
|
||||||
@ -217,7 +217,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
segments += new Segment(p9, p10)
|
segments += new Segment(p9, p10)
|
||||||
segments += new Segment(p10, p1)
|
segments += new Segment(p10, p1)
|
||||||
tesselator = new Triangulator(segments)
|
tesselator = new Triangulator(segments)
|
||||||
tesselator.process
|
tesselator process
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test #2
|
// Test #2
|
||||||
@ -252,7 +252,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
segments += new Segment(p11, p12)
|
segments += new Segment(p11, p12)
|
||||||
segments += new Segment(p12, p1)
|
segments += new Segment(p12, p1)
|
||||||
tesselator = new Triangulator(segments)
|
tesselator = new Triangulator(segments)
|
||||||
tesselator.process
|
tesselator process
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class QueryGraph(var head: Node) {
|
|||||||
def locate(s: Segment) = head.locate(s).trapezoid
|
def locate(s: Segment) = head.locate(s).trapezoid
|
||||||
|
|
||||||
def followSegment(s: Segment) = {
|
def followSegment(s: Segment) = {
|
||||||
assert(s.p.x < s.q.x)
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new ArrayBuffer[Trapezoid]
|
||||||
trapezoids += locate(s)
|
trapezoids += locate(s)
|
||||||
var j = 0
|
var j = 0
|
||||||
|
@ -41,16 +41,6 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
|
|||||||
var upperRight: Trapezoid = null
|
var upperRight: Trapezoid = null
|
||||||
var lowerRight: Trapezoid = null
|
var lowerRight: Trapezoid = null
|
||||||
|
|
||||||
def updateLeftNeighbors(ul: Trapezoid, ll: Trapezoid) {
|
|
||||||
if(upperLeft != null && upperLeft.top == top) upperLeft.upperRight = ul
|
|
||||||
if(lowerLeft != null && lowerLeft.bottom == bottom) lowerLeft.lowerRight = ll
|
|
||||||
}
|
|
||||||
|
|
||||||
def updateRightNeighbors(ur: Trapezoid, lr: Trapezoid) {
|
|
||||||
if(upperRight != null && upperRight.top == top) upperRight.upperLeft = ur
|
|
||||||
if(lowerRight != null && lowerRight.bottom == bottom) lowerRight.lowerLeft = lr
|
|
||||||
}
|
|
||||||
|
|
||||||
def update(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) {
|
def update(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) {
|
||||||
upperLeft = ul; if(ul != null) ul.upperRight = this
|
upperLeft = ul; if(ul != null) ul.upperRight = this
|
||||||
lowerLeft = ll; if(ll != null) ll.lowerRight = this
|
lowerLeft = ll; if(ll != null) ll.lowerRight = this
|
||||||
@ -62,10 +52,10 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
|
|||||||
def trimNeighbors {
|
def trimNeighbors {
|
||||||
if(inside) {
|
if(inside) {
|
||||||
inside = false
|
inside = false
|
||||||
if(upperLeft != null) {upperLeft.trimNeighbors}
|
if(upperLeft != null) upperLeft.trimNeighbors
|
||||||
if(lowerLeft != null) {lowerLeft.trimNeighbors}
|
if(lowerLeft != null) lowerLeft.trimNeighbors
|
||||||
if(upperRight != null) {upperRight.trimNeighbors}
|
if(upperRight != null) upperRight.trimNeighbors
|
||||||
if(lowerRight != null) {lowerRight.trimNeighbors}
|
if(lowerRight != null) lowerRight.trimNeighbors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,14 +75,14 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
|
|||||||
|
|
||||||
def lineIntersect(s: Segment, x: Float) = {
|
def lineIntersect(s: Segment, x: Float) = {
|
||||||
val y = s.slope * x + s.b
|
val y = s.slope * x + s.b
|
||||||
new Point(x, y)
|
Point(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add points to monotone mountain
|
// Add points to monotone mountain
|
||||||
def addPoints {
|
def addPoints {
|
||||||
if(leftPoint != bottom.p) bottom.mPoints += leftPoint
|
if(leftPoint ! bottom.p) bottom.mPoints += leftPoint.clone
|
||||||
if(rightPoint != bottom.q) bottom.mPoints += rightPoint
|
if(rightPoint ! bottom.q) bottom.mPoints += rightPoint.clone
|
||||||
if(leftPoint != top.p) top.mPoints += leftPoint
|
if(leftPoint ! top.p) top.mPoints += leftPoint.clone
|
||||||
if(rightPoint != top.q) top.mPoints += rightPoint
|
if(rightPoint ! top.q) top.mPoints += rightPoint.clone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,14 +67,11 @@ class TrapezoidalMap {
|
|||||||
// break trapezoid into 4 smaller trapezoids
|
// break trapezoid into 4 smaller trapezoids
|
||||||
def case1(t: Trapezoid, s: Segment) = {
|
def case1(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
assert(s.p.x != s.q.x)
|
|
||||||
assert(s.p.x < s.q.x)
|
|
||||||
|
|
||||||
val trapezoids = new Array[Trapezoid](4)
|
val trapezoids = new Array[Trapezoid](4)
|
||||||
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
trapezoids(0) = new Trapezoid(t.leftPoint.clone, s.p.clone, t.top, t.bottom)
|
||||||
trapezoids(1) = new Trapezoid(s.p, s.q, t.top, s)
|
trapezoids(1) = new Trapezoid(s.p.clone, s.q.clone, t.top, s)
|
||||||
trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom)
|
trapezoids(2) = new Trapezoid(s.p.clone, s.q.clone, s, t.bottom)
|
||||||
trapezoids(3) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
trapezoids(3) = new Trapezoid(s.q.clone, t.rightPoint.clone, t.top, t.bottom)
|
||||||
|
|
||||||
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null)
|
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null)
|
||||||
@ -94,9 +91,9 @@ class TrapezoidalMap {
|
|||||||
assert(s.p.x < s.q.x)
|
assert(s.p.x < s.q.x)
|
||||||
|
|
||||||
val trapezoids = new Array[Trapezoid](3)
|
val trapezoids = new Array[Trapezoid](3)
|
||||||
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
trapezoids(0) = new Trapezoid(t.leftPoint.clone, s.p.clone, t.top, t.bottom)
|
||||||
trapezoids(1) = new Trapezoid(s.p, t.rightPoint, t.top, s)
|
trapezoids(1) = new Trapezoid(s.p.clone, t.rightPoint.clone, t.top, s)
|
||||||
trapezoids(2) = new Trapezoid(s.p, t.rightPoint, s, t.bottom)
|
trapezoids(2) = new Trapezoid(s.p.clone, t.rightPoint.clone, s, t.bottom)
|
||||||
|
|
||||||
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
trapezoids(1).update(trapezoids(0), null, t.upperRight, null)
|
trapezoids(1).update(trapezoids(0), null, t.upperRight, null)
|
||||||
@ -113,20 +110,17 @@ class TrapezoidalMap {
|
|||||||
// Case 3: Trapezoid is bisected
|
// Case 3: Trapezoid is bisected
|
||||||
def case3(t: Trapezoid, s: Segment) = {
|
def case3(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
assert(s.p.x != s.q.x)
|
|
||||||
assert(s.p.x < s.q.x)
|
|
||||||
|
|
||||||
val topCross = (tCross == t.top)
|
val topCross = (tCross == t.top)
|
||||||
val bottomCross = (bCross == t.bottom)
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
val trapezoids = new Array[Trapezoid](2)
|
val trapezoids = new Array[Trapezoid](2)
|
||||||
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, t.rightPoint, t.top, s)
|
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint.clone, t.rightPoint.clone, t.top, s)
|
||||||
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, t.rightPoint, s, t.bottom)
|
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint.clone, t.rightPoint.clone, s, t.bottom)
|
||||||
|
|
||||||
if(topCross) {
|
if(topCross) {
|
||||||
trapezoids(0).upperRight = t.upperRight
|
trapezoids(0).upperRight = t.upperRight
|
||||||
if(t.upperRight != null) t.upperRight.upperLeft = trapezoids(0)
|
if(t.upperRight != null) t.upperRight.upperLeft = trapezoids(0)
|
||||||
trapezoids(0).rightPoint = t.rightPoint
|
trapezoids(0).rightPoint = t.rightPoint.clone
|
||||||
} else {
|
} else {
|
||||||
trapezoids(0).update(t.upperLeft, s.above, t.upperRight, null)
|
trapezoids(0).update(t.upperLeft, s.above, t.upperRight, null)
|
||||||
}
|
}
|
||||||
@ -134,7 +128,7 @@ class TrapezoidalMap {
|
|||||||
if(bottomCross) {
|
if(bottomCross) {
|
||||||
trapezoids(1).lowerRight = t.lowerRight
|
trapezoids(1).lowerRight = t.lowerRight
|
||||||
if(t.lowerRight != null) t.lowerRight.lowerLeft = trapezoids(1)
|
if(t.lowerRight != null) t.lowerRight.lowerLeft = trapezoids(1)
|
||||||
trapezoids(1).rightPoint = t.rightPoint
|
trapezoids(1).rightPoint = t.rightPoint.clone
|
||||||
} else {
|
} else {
|
||||||
trapezoids(1).update(s.below, t.lowerLeft, null, t.lowerRight)
|
trapezoids(1).update(s.below, t.lowerLeft, null, t.lowerRight)
|
||||||
}
|
}
|
||||||
@ -151,26 +145,24 @@ class TrapezoidalMap {
|
|||||||
// break trapezoid into 3 smaller trapezoids
|
// break trapezoid into 3 smaller trapezoids
|
||||||
def case4(t: Trapezoid, s: Segment) = {
|
def case4(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
assert(s.p.x < s.q.x)
|
|
||||||
|
|
||||||
val topCross = (tCross == t.top)
|
val topCross = (tCross == t.top)
|
||||||
val bottomCross = (bCross == t.bottom)
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
val trapezoids = new Array[Trapezoid](3)
|
val trapezoids = new Array[Trapezoid](3)
|
||||||
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, s.q, t.top, s)
|
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint.clone, s.q.clone, t.top, s)
|
||||||
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, s.q, s, t.bottom)
|
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint.clone, s.q.clone, s, t.bottom)
|
||||||
trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
trapezoids(2) = new Trapezoid(s.q.clone, t.rightPoint.clone, t.top, t.bottom)
|
||||||
|
|
||||||
if(topCross) {
|
if(topCross) {
|
||||||
trapezoids(0).upperRight = trapezoids(2)
|
trapezoids(0).upperRight = trapezoids(2)
|
||||||
trapezoids(0).rightPoint = s.q
|
trapezoids(0).rightPoint = s.q.clone
|
||||||
} else {
|
} else {
|
||||||
trapezoids(0).update(t.upperLeft, s.above, trapezoids(2), null)
|
trapezoids(0).update(t.upperLeft, s.above, trapezoids(2), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bottomCross) {
|
if(bottomCross) {
|
||||||
trapezoids(1).lowerRight = trapezoids(2)
|
trapezoids(1).lowerRight = trapezoids(2)
|
||||||
trapezoids(1).rightPoint = s.q
|
trapezoids(1).rightPoint = s.q.clone
|
||||||
} else {
|
} else {
|
||||||
trapezoids(1).update(s.below, t.lowerLeft, null, trapezoids(2))
|
trapezoids(1).update(s.below, t.lowerLeft, null, trapezoids(2))
|
||||||
}
|
}
|
||||||
@ -186,24 +178,24 @@ class TrapezoidalMap {
|
|||||||
// Create an AABB around segments
|
// Create an AABB around segments
|
||||||
def boundingBox(segments: ArrayBuffer[Segment]): Trapezoid = {
|
def boundingBox(segments: ArrayBuffer[Segment]): Trapezoid = {
|
||||||
|
|
||||||
var max = segments(0).p + margin
|
var max = segments(0).p.clone + margin
|
||||||
var min = segments(0).q - margin
|
var min = segments(0).q.clone - margin
|
||||||
|
|
||||||
for(s <- segments) {
|
for(s <- segments) {
|
||||||
if(s.p.x > max.x) max = Point(s.p.x + margin, max.y)
|
if(s.p.clone.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.p.clone.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.clone.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.q.clone.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.clone.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.p.clone.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.clone.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)
|
if(s.q.clone.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 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 bottom = new Segment(Point(min.x, min.y), Point(max.x, min.y))
|
||||||
val left = bottom.p
|
val left = bottom.p.clone
|
||||||
val right = bottom.q
|
val right = bottom.q.clone
|
||||||
|
|
||||||
return new Trapezoid(left, right, top, bottom)
|
return new Trapezoid(left, right, top, bottom)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
val mountain = new MonotoneMountain
|
val mountain = new MonotoneMountain
|
||||||
val k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
|
val k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
|
||||||
val points = s.p :: k ::: List(s.q)
|
val points = s.p :: k ::: List(s.q)
|
||||||
points.foreach(p => mountain += p.clone)
|
points.foreach(p => mountain += p)
|
||||||
mountain.triangulate
|
mountain.triangulate
|
||||||
xMonoPoly += mountain
|
xMonoPoly += mountain
|
||||||
}
|
}
|
||||||
@ -147,9 +147,7 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
segs += new Segment(s.p.clone, s.q.clone)
|
segs += new Segment(s.p.clone, s.q.clone)
|
||||||
// Randomized triangulation improves performance
|
// Randomized triangulation improves performance
|
||||||
// See Seidel's paper, or O'Rourke's book, p. 57
|
// See Seidel's paper, or O'Rourke's book, p. 57
|
||||||
// Turn this off for now because of pointer bug somewhere in DAG?
|
// Turn this off for now because of pointer bug somewhere in DAG / trapezoidal map
|
||||||
// The solution to this bug may be more apparent with a better Trapezoidal Map
|
|
||||||
// data structure... Maybe a modified doubly connected edge list?
|
|
||||||
//Random.shuffle(segs)
|
//Random.shuffle(segs)
|
||||||
segs
|
segs
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user