diff --git a/python/framework/framework.pyx b/python/framework/framework.pyx index 6dc1846..878237f 100644 --- a/python/framework/framework.pyx +++ b/python/framework/framework.pyx @@ -4,6 +4,7 @@ from math import pi as PI from gl cimport * +include "polydecomp.pxi" cdef extern from 'math.h': double cos(double) diff --git a/python/framework/polydecomp.pxi b/python/framework/polydecomp.pxi new file mode 100644 index 0000000..3653b98 --- /dev/null +++ b/python/framework/polydecomp.pxi @@ -0,0 +1,150 @@ +## +## Ported from PolyDeomp by Mark Bayazit +## http://mnbayazit.com/406/credit +## +from sys import float_info + +def makeCCW(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 + # reverse poly if clockwise + if not left(at(poly, br - 1), at(poly, br), at(poly, br + 1)): + poly.reverse() + +cpdef list decompose_poly(list poly, list polys): + + cdef list upperInt = [], lowerInt = [], p = [], closestVert = [] + cdef float upperDist, lowerDist, d, closestDist + cdef int upper_index, lower_index, closest_index + cdef list lower_poly = [], upper_poly = [] + + for i from 0 <= i < len(poly): + if is_reflex(poly, i): + upperDist = lowerDist = float_info.max + for j from 0 <= j < len(poly): + if left(at(poly, i - 1), at(poly, i), at(poly, j)) and rightOn(at(poly, i - 1), at(poly, i), at(poly, j - 1)): + # if line intersects with an edge + # find the point of intersection + p = intersection(at(poly, i - 1), at(poly, i), at(poly, j), at(poly, j - 1)) + if right(at(poly, i + 1), at(poly, i), p): + # make sure it's inside the poly + d = sqdist(poly[i], p) + if d < lowerDist: + # keep only the closest intersection + lowerDist = d + lowerInt = p + lower_index = j + if left(at(poly, i + 1), at(poly, i), at(poly, j + 1)) and rightOn(at(poly, i + 1), at(poly, i), at(poly, j)): + p = intersection(at(poly, i + 1), at(poly, i), at(poly, j), at(poly, j + 1)) + if left(at(poly, i - 1), at(poly, i), p): + d = sqdist(poly[i], p) + if d < upperDist: + upperDist = d + upperInt = p + upper_index = j + + # 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 + + if i < upper_index: + lower_poly.extend(poly[i:upper_index+1]) + lower_poly.append(p) + upper_poly.append(p) + if lower_index != 0: + upper_poly.extend(poly[lower_index:]) + upper_poly.extend(poly[:i+1]) + else: + if i != 0: + lower_poly.extend(poly[i:]) + lower_poly.extend(poly[:upper_index+1]) + lower_poly.append(p) + upper_poly.append(p) + upper_poly.extend(poly[lower_index:i+1]) + else: + + # connect to the closest point within the triangle + + if lower_index > upper_index: + upper_index += len(poly) + + closestDist = float_info.max + for j from lower_index <= j <= upper_index: + if leftOn(at(poly, i - 1), at(poly, i), at(poly, j)) and rightOn(at(poly, i + 1), at(poly, i), at(poly, j)): + d = sqdist(at(poly, i), at(poly, j)) + if d < closestDist: + closestDist = d + closestVert = at(poly, j) + closest_index = j % len(poly) + + if i < closest_index: + lower_poly.extend(poly[i:closest_index+1]) + if closest_index != 0: + upper_poly.extend(poly[closest_index:]) + upper_poly.extend(poly[:i+1]) + else: + if i != 0: + lower_poly.extend(poly[i:]) + lower_poly.extend(poly[:closest_index+1]) + upper_poly.extend(poly[closest_index:i+1]) + + # solve smallest poly first + if len(lower_poly) < len(upper_poly): + decompose_poly(lower_poly, polys) + decompose_poly(upper_poly, 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 bool eq(float a, float b): + return abs(a - b) <= 1e-8 + +cdef list at(list v, int i): + return v[i%len(v)] + +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 bool leftOn(list a, list b, list c): + return area(a, b, c) >= 0 + +cdef bool right(list a, list b, list c): + return area(a, b, c) < 0 + +cdef bool rightOn(list a, list b, list c): + return area(a, b, c) <= 0 + +cdef float sqdist(list a, list b): + cdef float dx = b[0] - a[0] + cdef float dy = b[1] - a[1] + return dx * dx + dy * dy + +cdef bool is_reflex(list poly, int i): + return right(at(poly, i - 1), at(poly, i), at(poly, i + 1)) \ No newline at end of file diff --git a/python/poly2tri.py b/python/poly2tri.py index 47d5c9c..3b56584 100644 --- a/python/poly2tri.py +++ b/python/poly2tri.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2.6 -from framework import Game, draw_polygon, reset_zoom, draw_line +from framework import Game, draw_polygon, reset_zoom, draw_line, decompose_poly, makeCCW from seidel import Triangulator @@ -7,11 +7,26 @@ class Poly2Tri(Game): screen_size = 800.0, 600.0 + dude = [[174.50415,494.59368],[215.21844,478.87939],[207.36129,458.87939],[203.07558,441.02225],[203.07558,418.1651], + [210.93272,394.59367],[224.50415,373.1651],[241.64701,358.1651],[257.36129,354.59367],[275.93272,356.73653], + [293.07558,359.59367],[309.50415,377.45082],[322.36129,398.1651],[331.64701,421.73653],[335.21844,437.45082], + [356.64701,428.52225],[356.1113,428.34367],[356.1113,428.34367],[368.78987,419.59368],[349.50415,384.59367], + [323.78987,359.59367],[290.93272,343.87939],[267.36129,341.02225],[264.50415,331.02225],[264.50415,321.02225], + [268.78987,306.02225],[285.93272,286.02225],[295.21844,270.30796],[303.78987,254.59367],[306.64701,213.87939], + [320,202.36218],[265,202.36218],[286.64701,217.45082],[293.78987,241.02225],[285,257.36218],[270.93272,271.73653], + [254.50415,266.02225],[250.93272,248.1651],[256.64701,233.1651],[256.64701,221.02225],[245.93272,215.30796], + [238.78987,216.73653],[233.78987,232.45082],[232.36129,249.59367],[243.07558,257.09367],[242.89701,270.30796], + [235.93272,279.95082],[222.36129,293.1651],[205.21844,300.6651],[185,297.36218],[170,242.36218],[175,327.36218], + [185,322.36218],[195,317.36218],[230.75415,301.02225],[235.39701,312.45082],[240.57558,323.52225], + [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]] + def __init__(self): super(Poly2Tri, self).__init__(*self.screen_size) # Load point set - file_name = "../data/star.dat" + file_name = "../data/dude.dat" self.points = self.load_points(file_name) # Triangulate @@ -24,20 +39,33 @@ class Poly2Tri(Game): self.trapezoids = seidel.trapezoidal_map.map self.edges = seidel.edge_list print "time (ms) = %f , num triangles = %d" % (dt, len(self.triangles)) + + 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)) + self.main_loop() def update(self): pass def render(self): - reset_zoom(2.1, (400, 100), self.screen_size) + reset_zoom(2.1, (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: + draw_polygon(p, red) + ''' for t in self.trapezoids: verts = self.trapezoids[t].vertices()