mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-26 15:26:12 +01:00
added fast robust predicates
This commit is contained in:
parent
b3ff213b50
commit
91c722558a
@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
touch framework/framework.pyx
|
||||
rm framework/*.c
|
||||
rm -rf build
|
||||
python setup.py build_ext -i
|
@ -5,6 +5,7 @@ from math import pi as PI
|
||||
|
||||
from gl cimport *
|
||||
include "polydecomp.pxi"
|
||||
include "seidel.pxi"
|
||||
|
||||
cdef extern from 'math.h':
|
||||
double cos(double)
|
||||
|
@ -4,15 +4,18 @@
|
||||
##
|
||||
from sys import float_info
|
||||
|
||||
def makeCCW(list poly):
|
||||
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
|
||||
br = i
|
||||
# reverse poly if clockwise
|
||||
if not left(at(poly, br - 1), at(poly, br), at(poly, br + 1)):
|
||||
poly.reverse()
|
||||
poly.reverse()
|
||||
|
||||
cpdef list decompose_poly(list poly, list polys):
|
||||
|
||||
@ -48,8 +51,8 @@ cpdef list decompose_poly(list poly, list polys):
|
||||
|
||||
# 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]) / 2
|
||||
p[1] = (lowerInt[1] + upperInt[1]) / 2
|
||||
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])
|
||||
@ -99,26 +102,19 @@ cpdef list decompose_poly(list poly, list 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 list i = []
|
||||
cdef float a1, b1, c1, a2, b2, c2, det
|
||||
a1 = p2[1] - p1[1]
|
||||
b1 = p1[0] - p2[0]
|
||||
c1 = a1 * p1[0] + b1 * p1[1]
|
||||
a2 = q2[1] - q1[1]
|
||||
b2 = q1[0] - q2[0]
|
||||
c2 = a2 * q1[0] + b2 * q1[1]
|
||||
det = a1 * b2 - a2 * b1
|
||||
if not eq(det, 0):
|
||||
# lines are not parallel
|
||||
i.append((b2 * c1 - b1 * c2) / det)
|
||||
i.append((a1 * c2 - a2 * c1) / det)
|
||||
return i
|
||||
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
|
||||
@ -130,16 +126,28 @@ 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):
|
||||
return area(a, b, c) > 0
|
||||
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):
|
||||
return area(a, b, c) >= 0
|
||||
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):
|
||||
return area(a, b, c) < 0
|
||||
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):
|
||||
return area(a, b, c) <= 0
|
||||
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]
|
||||
|
4262
python/framework/predicates.c
Normal file
4262
python/framework/predicates.c
Normal file
File diff suppressed because it is too large
Load Diff
4
python/framework/predicates.h
Normal file
4
python/framework/predicates.h
Normal file
@ -0,0 +1,4 @@
|
||||
double orient2d(pa, pb, pc);
|
||||
double *pa;
|
||||
double *pb;
|
||||
double *pc;
|
636
python/framework/seidel.pxi
Normal file
636
python/framework/seidel.pxi
Normal file
@ -0,0 +1,636 @@
|
||||
#
|
||||
# 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)
|
||||
|
||||
##
|
||||
## NOTE Rounding accuracy significantly effects numerical robustness!!!
|
||||
##
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
def __init__(self, poly_line):
|
||||
assert len(poly_line) > 3, "Number of points must be > 3"
|
||||
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
|
@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python2.6
|
||||
from framework import Game, draw_polygon, reset_zoom, draw_line, decompose_poly, makeCCW
|
||||
|
||||
from seidel import Triangulator
|
||||
from framework import Game, draw_polygon, reset_zoom, draw_line, decompose_poly, make_ccw, Triangulator
|
||||
|
||||
class Poly2Tri(Game):
|
||||
|
||||
@ -21,7 +19,7 @@ class Poly2Tri(Game):
|
||||
[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],
|
||||
@ -35,7 +33,79 @@ class Poly2Tri(Game):
|
||||
[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)
|
||||
|
||||
@ -56,22 +126,13 @@ class Poly2Tri(Game):
|
||||
|
||||
for p in self.dude:
|
||||
p[0] -= 75
|
||||
|
||||
makeCCW(self.dude)
|
||||
self.dude_poly = []
|
||||
t1 = self.time
|
||||
decompose_poly(self.dude, self.dude_poly)
|
||||
dt = (self.time - t1) * 1000.0
|
||||
print "time (ms) = %f , num polies = %d" % (dt, len(self.dude_poly))
|
||||
|
||||
spam = []
|
||||
makeCCW(self.test)
|
||||
clean = []
|
||||
for t in self.test:
|
||||
x = round(t[0], 0)
|
||||
y = round(t[1], 0)
|
||||
clean.append([x,y])
|
||||
decompose_poly(clean, spam)
|
||||
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()
|
||||
|
||||
@ -79,16 +140,16 @@ class Poly2Tri(Game):
|
||||
pass
|
||||
|
||||
def render(self):
|
||||
reset_zoom(2.2, (300, 450), self.screen_size)
|
||||
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.dude_poly:
|
||||
for p in self.decomp_poly:
|
||||
draw_polygon(p, red)
|
||||
|
||||
|
||||
'''
|
||||
for t in self.trapezoids:
|
||||
verts = self.trapezoids[t].vertices()
|
||||
@ -99,8 +160,6 @@ class Poly2Tri(Game):
|
||||
p1 = e.p.x, e.p.y
|
||||
p2 = e.q.x, e.q.y
|
||||
draw_line(p1, p2, green)
|
||||
|
||||
#draw_polygon(self.points, green)
|
||||
|
||||
|
||||
def load_points(self, file_name):
|
||||
@ -113,9 +172,6 @@ class Poly2Tri(Game):
|
||||
break
|
||||
points.append((float(s[0]), float(s[1])))
|
||||
return points
|
||||
|
||||
spam = (544.80998999999997, 579.86046999999996), (544.80998999999997, 450.57477), (594.09569999999997, 450.57477), (643.38142000000005, 450.57477), (643.38142000000005, 525.26486999999997), (643.38142000000005, 599.95487000000003), (603.67391999999995, 654.55056999999999), (563.96655999999996, 709.14621999999997), (554.38819999999998, 709.14621999999997), (544.80998999999997, 709.14621999999997)
|
||||
eggs = [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]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo = Poly2Tri()
|
@ -9,7 +9,7 @@ from Cython.Distutils import build_ext
|
||||
|
||||
version = '0.1'
|
||||
|
||||
sourcefiles = ['framework/framework.pyx']
|
||||
sourcefiles = ['framework/framework.pyx', 'framework/predicates.c']
|
||||
|
||||
# Platform-dependent submodules
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user