This commit is contained in:
zzzzrrr 2009-07-28 21:01:41 -04:00
parent f75a0a2974
commit 3ededbd3d0
6 changed files with 115 additions and 16 deletions

View File

@ -28,14 +28,75 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package org.poly2tri.cdt
import scala.collection.mutable.ArrayBuffer
import shapes.{Segment, Point}
import utils.Util
/** /**
* Sweep-line, Constrained Delauney Triangulation * Sweep-line, Constrained Delauney Triangulation
* See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', * See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
* International Journal of Geographical Information Science,22:4,449 462 * International Journal of Geographical Information Science,22:4,449 462
*/ */
package org.poly2tri.cdt class CDT(segments: ArrayBuffer[Segment]) {
class CDT {
// The point list
val points = init
// The triangle mesh
val mesh = new Mesh
// Sweep points; build mesh
sweep
// Finalize triangulation
finalization
// Initialize and sort point list
private def init: List[Point] = {
var xmax, xmin = 0f
var ymax, ymin = 0f
val pts = new ArrayBuffer[Point]
for(i <- 0 until segments.size) {
val p = segments(i).p
val q = segments(i).q
if(p.x > xmax) xmax = p.x
if(q.x > xmax) xmax = q.x
if(p.x < xmin) xmin = p.x
if(q.x < xmin) xmin = q.x
if(p.y > ymax) ymax = p.x
if(q.y > ymax) ymax = q.x
if(p.y < ymin) ymin = p.x
if(q.y < ymin) ymin = q.x
pts += shearTransform(p)
pts += shearTransform(q)
}
if(pts.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.
Util.insertSort((p1: Point, p2: Point) => p1 > p2)(pts).toList
else
// Merge sort: O(n log n)
Util.msort((p1: Point, p2: Point) => p1 > p2)(pts.toList)
}
// Implement sweep-line paradigm
private def sweep {
}
private def finalization {
}
// Prevents any two distinct endpoints from lying on a common horizontal 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, point.y + point.x * 0.0001f)
} }

View File

@ -31,7 +31,9 @@
package org.poly2tri.cdt package org.poly2tri.cdt
import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet
import shapes.Point
class Mesh { class Mesh {
val map = HashSet.empty[Triangle] val map = HashSet.empty[Triangle]

View File

@ -158,7 +158,7 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
if(s.mPoints.size < 10) if(s.mPoints.size < 10)
// Insertion sort is one of the fastest algorithms for sorting arrays containing // Insertion sort is one of the fastest algorithms for sorting arrays containing
// fewer than ten elements, or for lists that are already mostly sorted. // fewer than ten elements, or for lists that are already mostly sorted.
k = Util.insertSort(s.mPoints).toList k = Util.insertSort((p1: Point, p2: Point) => p1 < p2)(s.mPoints).toList
else else
k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList) k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)

View File

@ -30,10 +30,18 @@
*/ */
package org.poly2tri.shapes package org.poly2tri.shapes
object Event extends Enumeration {
val point, edge = Value
}
case class Point(val x: Float, val y: Float) { case class Point(val x: Float, val y: Float) {
// Pointers to next and previous points in Monontone Mountain // Pointers to next and previous points in Monontone Mountain
var next, prev: Point = null var next, prev: Point = null
// The setment this point belongs to
var segment: Segment = null
// Point type for CDT
var eventType: Event.Value = _
@inline def -(p: Point) = Point(x - p.x, y - p.y) @inline def -(p: Point) = Point(x - p.x, y - p.y)
@inline def +(p: Point) = Point(x + p.x, y + p.y) @inline def +(p: Point) = Point(x + p.x, y + p.y)
@ -45,7 +53,10 @@ case class Point(val x: Float, val y: Float) {
@inline def dot(p: Point) = x * p.x + y * p.y @inline def dot(p: Point) = x * p.x + y * p.y
@inline def length = Math.sqrt(x * x + y * y).toFloat @inline def length = Math.sqrt(x * x + y * y).toFloat
@inline def normalize = this / length @inline def normalize = this / length
@inline def <(p: Point) = (x < p.x) // Sort along x axis
@inline def <(p: Point) = (x < p.x)
// Sort along y axis
@inline def >(p: Point) = (y < p.y)
@inline def !(p: Point) = !(p.x == x && p.y == y) @inline def !(p: Point) = !(p.x == x && p.y == y)
@inline override def clone = Point(x, y) @inline override def clone = Point(x, y)
} }

View File

@ -35,11 +35,13 @@ import scala.collection.mutable.{ArrayBuffer}
// Represents a simple polygon's edge // Represents a simple polygon's edge
class Segment(var p: Point, var q: Point) { class Segment(var p: Point, var q: Point) {
p.segment = this
q.segment = this
// Pointers used for building trapezoidal map // Pointers used for building trapezoidal map
var above, below: Trapezoid = null var above, below: Trapezoid = null
// Montone mountain points // Montone mountain points
val mPoints = new ArrayBuffer[Point] val mPoints = new ArrayBuffer[Point]
// Equation of a line: y = m*x + b // Equation of a line: y = m*x + b
// Slope of the line (m) // Slope of the line (m)
val slope = (q.y - p.y)/(q.x - p.x) val slope = (q.y - p.y)/(q.x - p.x)
@ -50,5 +52,28 @@ class Segment(var p: Point, var q: Point) {
def > (point: Point) = (Math.floor(point.y) < Math.floor(slope * point.x + b)) def > (point: Point) = (Math.floor(point.y) < Math.floor(slope * point.x + b))
// Determines if this segment lies below the given point // Determines if this segment lies below the given point
def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b)) def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b))
// Assign point type for CDT
if(p.y > q.y)
pEdge
else if (p.y < q.y)
pPoint
else
if(p.x < q.x)
pPoint
else if (p.x > q.x)
pEdge
else
throw new Exception("Invalid segment")
private def pPoint {
p.eventType = Event.point
q.eventType = Event.edge
}
private def pEdge {
p.eventType = Event.edge
q.eventType = Event.point
}
} }

View File

@ -19,19 +19,19 @@ object Util {
else merge(msort(less)(xs take n), msort(less)(xs drop n)) else merge(msort(less)(xs take n), msort(less)(xs drop n))
} }
def insertSort(list:ArrayBuffer[Point]) = { def insertSort[A](less: (A, A) => Boolean)(xs: ArrayBuffer[A]): ArrayBuffer[A] = {
var j = 1 var j = 1
while(j < list.size){ while(j < xs.size){
val key = list(j) val key = xs(j)
var i = j-1 var i = j-1
while(i>=0 && list(i).x > key.x){ while(i >= 0 && less(key, xs(i)) ){
list(i+1) = list(i) xs(i+1) = xs(i)
i=i-1 i -= 1
} }
list(i+1)=key xs(i+1)=key
j=j+1 j += 1
} }
list xs
} }
} }