poly2tri/python/framework/triangulator.pyx

708 lines
23 KiB
Cython
Raw Normal View History

2009-11-12 20:52:23 +01:00
#
# 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.
#
2009-11-12 22:54:10 +01:00
from random import shuffle
2009-11-12 20:52:23 +01:00
###
### 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)
2009-11-12 21:29:29 +01:00
double atan2(double, double)
double floor(double)
2009-11-12 20:52:23 +01:00
double sqrt(double)
2009-11-17 20:21:30 +01:00
class Point:
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
def __cinit__(self, float x, float y):
2009-11-12 20:52:23 +01:00
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)
2009-11-17 20:21:30 +01:00
def cross(self, p):
2009-11-12 20:52:23 +01:00
return self.x * p.y - self.y * p.x
2009-11-17 20:21:30 +01:00
def dot(self, p):
2009-11-12 20:52:23 +01:00
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()
2009-11-17 20:21:30 +01:00
def less(self, p):
2009-11-12 20:52:23 +01:00
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)
2009-11-17 20:21:30 +01:00
cdef class Edge:
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef object above, below
cdef float slope, b
2009-11-12 20:52:23 +01:00
mpoints = []
2009-11-17 20:21:30 +01:00
def __cinit__(self, p, q):
2009-11-12 20:52:23 +01:00
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
2009-11-17 20:21:30 +01:00
cdef bool is_above(self, point):
2009-11-12 20:52:23 +01:00
return (floor(point.y) < floor(self.slope * point.x + self.b))
2009-11-17 20:21:30 +01:00
cdef bool is_below(self, point):
2009-11-12 20:52:23 +01:00
return (floor(point.y) > floor(self.slope * point.x + self.b))
2009-11-17 20:21:30 +01:00
cdef float intersect(self, c, d):
2009-11-12 20:52:23 +01:00
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
2009-11-17 20:21:30 +01:00
cdef float signed_area(self, a, b, c):
2009-11-12 20:52:23 +01:00
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
cdef class Trapezoid:
2009-11-17 20:21:30 +01:00
cdef Edge top, bottom
cdef Trapezoid upper_left, lower_left
cdef Trapezoid upper_right, lower_right
cdef object sink
cdef bool inside
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
def __cinit__(self, left_point, right_point, Edge top, Edge bottom):
2009-11-13 05:04:07 +01:00
self.left_point = left_point
self.right_point = right_point
2009-11-12 20:52:23 +01:00
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
2009-11-13 05:04:07 +01:00
self.sink = None
2009-11-17 20:21:30 +01:00
property inside:
def __get__(self): return self.inside
2009-11-13 05:04:07 +01:00
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
2009-11-17 20:21:30 +01:00
def __set__(self, lp): self.left_point = lp
2009-11-13 05:04:07 +01:00
property right_point:
def __get__(self): return self.right_point
2009-11-17 20:21:30 +01:00
def __set__(self, rp): self.right_point = rp
2009-11-13 05:04:07 +01:00
property sink:
def __get__(self): return self.sink
def __set__(self, object s): self.sink = s
2009-11-12 20:52:23 +01:00
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()
2009-11-17 20:21:30 +01:00
def contains(self, point):
2009-11-13 05:04:07 +01:00
return (point.x > self.left_point.x and point.x < self.right_point.x and
2009-11-12 20:52:23 +01:00
self.top.is_above(point) and self.bottom.is_below(point))
def vertices(self):
cdef list verts = []
2009-11-13 05:04:07 +01:00
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))
2009-11-12 20:52:23 +01:00
return verts
def add_points(self):
2009-11-13 05:04:07 +01:00
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)
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
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
2009-11-12 20:52:23 +01:00
class TrapezoidalMap:
2009-11-13 05:04:07 +01:00
map = []
2009-11-12 20:52:23 +01:00
margin = 50
bcross = None
tcross = None
def clear(self):
self.bcross = None
self.tcross = None
2009-11-17 20:21:30 +01:00
def case1(self, Trapezoid t, Edge e):
2009-11-13 05:04:07 +01:00
trapezoids = []
trapezoids.append(Trapezoid(t.left_point, e.p, t.top, t.bottom))
2009-11-12 20:52:23 +01:00
trapezoids.append(Trapezoid(e.p, e.q, t.top, e))
trapezoids.append(Trapezoid(e.p, e.q, e, t.bottom))
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
2009-11-12 20:52:23 +01:00
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
2009-11-17 20:21:30 +01:00
def case2(self, Trapezoid t, Edge e):
2009-11-13 05:04:07 +01:00
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))
2009-11-12 20:52:23 +01:00
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
2009-11-17 20:21:30 +01:00
def case3(self, Trapezoid t, Edge e):
2009-11-13 05:04:07 +01:00
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 = []
2009-11-12 20:52:23 +01:00
if self.tcross is t.top:
2009-11-13 05:04:07 +01:00
trapezoids.append(t.upper_left)
2009-11-12 20:52:23 +01:00
trapezoids[0].update_right(t.upper_right, None)
2009-11-13 05:04:07 +01:00
trapezoids[0].right_point = rp
2009-11-12 20:52:23 +01:00
else:
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(lp, rp, t.top, e))
2009-11-12 20:52:23 +01:00
trapezoids[0].update_left_right(t.upper_left, e.above, t.upper_right, None)
if self.bcross is t.bottom:
2009-11-13 05:04:07 +01:00
trapezoids.append(t.lower_left)
2009-11-12 20:52:23 +01:00
trapezoids[1].update_right(None, t.lower_right)
2009-11-13 05:04:07 +01:00
trapezoids[1].right_point = rp
2009-11-12 20:52:23 +01:00
else:
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(lp, rp, e, t.bottom))
2009-11-12 20:52:23 +01:00
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
2009-11-17 20:21:30 +01:00
def case4(self, Trapezoid t, Edge e):
2009-11-13 05:04:07 +01:00
lp = e.p if e.p.x == t.left_point.x else t.left_point
trapezoids = []
2009-11-12 20:52:23 +01:00
if self.tcross is t.top:
2009-11-13 05:04:07 +01:00
trapezoids.append(t.upper_left)
trapezoids[0].right_point = e.q
2009-11-12 20:52:23 +01:00
else:
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(lp, e.q, t.top, e))
2009-11-12 20:52:23 +01:00
trapezoids[0].update_left(t.upper_left, e.above)
if self.bcross is t.bottom:
2009-11-13 05:04:07 +01:00
trapezoids.append(t.lower_left)
trapezoids[1].right_point = e.q
2009-11-12 20:52:23 +01:00
else:
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(lp, e.q, e, t.bottom))
2009-11-12 20:52:23 +01:00
trapezoids[1].update_left(e.below, t.lower_left)
2009-11-13 05:04:07 +01:00
trapezoids.append(Trapezoid(e.q, t.right_point, t.top, t.bottom))
2009-11-12 20:52:23 +01:00
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)
2009-11-17 20:21:30 +01:00
cdef class Node:
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef Node left, right
cdef object parent_list
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
def __init__(self, Node left, Node right):
self.parent_list = []
2009-11-12 20:52:23 +01:00
self.left = left
self.right = right
2009-11-17 20:21:30 +01:00
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):
2009-11-12 20:52:23 +01:00
for parent in node.parent_list:
2009-11-13 05:04:07 +01:00
if parent.left is node:
parent.left = self
else:
parent.right = self
2009-11-17 20:21:30 +01:00
self.parent_list += node.parent_list
cdef class Sink(Node):
cdef Trapezoid trapezoid
2009-11-12 20:52:23 +01:00
def __init__(self, trapezoid):
2009-11-13 05:04:07 +01:00
self.trapezoid = trapezoid
2009-11-17 20:21:30 +01:00
super(Sink, self).__init__(None, None)
2009-11-12 20:52:23 +01:00
trapezoid.sink = self
2009-11-17 20:21:30 +01:00
property trapezoid:
def __get__(self): return self.trapezoid
2009-11-12 20:52:23 +01:00
def locate(self, e):
return self
2009-11-17 20:21:30 +01:00
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)
2009-11-12 20:52:23 +01:00
self.point = point
2009-11-17 20:21:30 +01:00
def locate(self, Edge e):
2009-11-13 05:04:07 +01:00
if e.p.x >= self.point.x:
return self.right.locate(e)
return self.left.locate(e)
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef class YNode(Node):
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef Edge edge
def __init__(self, Edge edge, Node lchild, Node rchild):
super(YNode, self).__init__(lchild, rchild)
2009-11-12 20:52:23 +01:00
self.edge = edge
2009-11-17 20:21:30 +01:00
def locate(self, Edge e):
2009-11-13 05:04:07 +01:00
if self.edge.is_above(e.p):
return self.right.locate(e)
elif self.edge.is_below(e.p):
return self.left.locate(e)
2009-11-12 20:52:23 +01:00
else:
2009-11-13 05:04:07 +01:00
if e.slope < self.edge.slope:
return self.right.locate(e)
2009-11-17 20:21:30 +01:00
else:
return self.left.locate(e)
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef class QueryGraph:
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
cdef Node head
2009-11-12 20:52:23 +01:00
2009-11-17 20:21:30 +01:00
def __init__(self, Node head):
2009-11-12 20:52:23 +01:00
self.head = head
2009-11-17 20:21:30 +01:00
def locate(self, Edge e):
2009-11-12 20:52:23 +01:00
return self.head.locate(e).trapezoid
2009-11-17 20:21:30 +01:00
def follow_edge(self, Edge e):
2009-11-12 20:52:23 +01:00
trapezoids = [self.locate(e)]
2009-11-17 20:21:30 +01:00
cdef int j = 0
2009-11-12 20:52:23 +01:00
while(e.q.x > trapezoids[j].right_point.x):
2009-11-17 20:21:30 +01:00
if e.is_above(trapezoids[j].right_point):
2009-11-12 20:52:23 +01:00
trapezoids.append(trapezoids[j].upper_right)
else:
2009-11-17 20:21:30 +01:00
trapezoids.append(trapezoids[j].lower_right)
2009-11-12 20:52:23 +01:00
j += 1
return trapezoids
2009-11-17 20:21:30 +01:00
def replace(self, Sink sink, Node node):
2009-11-12 20:52:23 +01:00
if not sink.parent_list:
self.head = node
else:
node.replace(sink)
2009-11-17 20:21:30 +01:00
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)
2009-11-12 20:52:23 +01:00
self.replace(sink, pNode)
2009-11-17 20:21:30 +01:00
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)
2009-11-12 20:52:23 +01:00
self.replace(sink, pNode)
2009-11-17 20:21:30 +01:00
def case3(self, Sink sink, Edge e, tlist):
yNode = YNode(e, isink(tlist[0]), isink(tlist[1]))
2009-11-12 20:52:23 +01:00
self.replace(sink, yNode)
2009-11-17 20:21:30 +01:00
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]))
2009-11-12 20:52:23 +01:00
self.replace(sink, qNode)
2009-11-12 21:29:29 +01:00
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
2009-11-17 20:21:30 +01:00
self.tail = None
self.head = None
2009-11-12 21:29:29 +01:00
self.positive = False
self.convex_points = []
self.mono_poly = []
self.triangles = []
self.convex_polies = []
2009-11-12 20:52:23 +01:00
2009-11-12 21:29:29 +01:00
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
2009-11-12 21:35:42 +01:00
cdef void remove(self, Point point):
cdef Point next, prev
2009-11-12 21:29:29 +01:00
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()
2009-11-12 21:35:42 +01:00
cdef void triangulate(self):
2009-11-12 21:29:29 +01:00
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")
2009-11-12 21:35:42 +01:00
cdef bool valid(self, Point p):
2009-11-12 21:29:29 +01:00
return p != self.head and p != self.tail and self.is_convex(p)
2009-11-12 21:35:42 +01:00
cdef void gen_mono_poly(self):
cdef Point p = self.head
2009-11-13 05:04:07 +01:00
while(p != None):
2009-11-12 21:29:29 +01:00
self.mono_poly.append(p)
p = p.next
2009-11-12 21:35:42 +01:00
cdef float angle(self, Point p):
2009-11-12 21:29:29 +01:00
cdef Point a = p.next - p
cdef Point b = p.prev - p
return atan2(a.cross(b), a.dot(b))
2009-11-12 21:35:42 +01:00
cdef float angle_sign(self):
cdef Point a = self.head.next - self.head
cdef Point b = self.tail - self.head
2009-11-12 21:29:29 +01:00
return atan2(a.cross(b), a.dot(b)) >= 0
2009-11-12 21:35:42 +01:00
cdef bool is_convex(self, Point p):
2009-11-12 21:29:29 +01:00
if self.positive != (self.angle(p) >= 0): return False
return True