mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-12-02 10:13:29 +01:00
ported Scala classes
This commit is contained in:
parent
8635340839
commit
1c75a2dc1c
5
python/bc.sh
Normal file
5
python/bc.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
touch framework/framework.pyx
|
||||||
|
rm framework/*.c
|
||||||
|
rm -rf build
|
||||||
|
python setup.py build_ext -i
|
138
python/framework/framework.pyx
Normal file
138
python/framework/framework.pyx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
##
|
||||||
|
## GL convenience layer
|
||||||
|
##
|
||||||
|
from math import pi as PI
|
||||||
|
|
||||||
|
from gl cimport *
|
||||||
|
|
||||||
|
#from triangulator import Point
|
||||||
|
|
||||||
|
include "triangulator.pyx"
|
||||||
|
|
||||||
|
cdef extern from 'math.h':
|
||||||
|
double cos(double)
|
||||||
|
double sin(double)
|
||||||
|
|
||||||
|
SEGMENTS = 25
|
||||||
|
INCREMENT = 2.0 * PI / SEGMENTS
|
||||||
|
|
||||||
|
def init_gl(width, height):
|
||||||
|
#glEnable(GL_LINE_SMOOTH)
|
||||||
|
glEnable(GL_BLEND)
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||||
|
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST)
|
||||||
|
|
||||||
|
def reset_zoom(float zoom, center, size):
|
||||||
|
|
||||||
|
zinv = 1.0 / zoom
|
||||||
|
left = -size[0] * zinv
|
||||||
|
right = size[0] * zinv
|
||||||
|
bottom = -size[1] * zinv
|
||||||
|
top = size[1] * zinv
|
||||||
|
|
||||||
|
# Reset viewport
|
||||||
|
glLoadIdentity()
|
||||||
|
glMatrixMode(GL_PROJECTION)
|
||||||
|
glLoadIdentity()
|
||||||
|
|
||||||
|
# Reset ortho view
|
||||||
|
glOrtho(left, right, bottom, top, 1, -1)
|
||||||
|
glTranslatef(-center[0], -center[1], 0)
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
glDisable(GL_DEPTH_TEST)
|
||||||
|
glLoadIdentity()
|
||||||
|
|
||||||
|
# Clear the screen
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
|
def draw_polygon(verts, color):
|
||||||
|
r, g, b = color
|
||||||
|
glColor3f(r, g, b)
|
||||||
|
glBegin(GL_LINE_LOOP)
|
||||||
|
for v in verts:
|
||||||
|
glVertex2f(v[0], v[1])
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
##
|
||||||
|
## Game engine / main loop / UI
|
||||||
|
##
|
||||||
|
from glfw cimport *
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
cdef extern from 'math.h':
|
||||||
|
double cos(double)
|
||||||
|
double sin(double)
|
||||||
|
double sqrt(double)
|
||||||
|
|
||||||
|
# Keyboard callback wrapper
|
||||||
|
kbd_callback_method = None
|
||||||
|
|
||||||
|
cdef extern void __stdcall kbd_callback(int id, int state):
|
||||||
|
kbd_callback_method(id, state)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Game:
|
||||||
|
|
||||||
|
title = "Poly2Tri"
|
||||||
|
|
||||||
|
def __init__(self, window_width, window_height):
|
||||||
|
|
||||||
|
p1 = Point(12, 10)
|
||||||
|
p2 = Point(50, 47)
|
||||||
|
print p1.cross(p2)
|
||||||
|
|
||||||
|
glfwInit()
|
||||||
|
|
||||||
|
# 16 bit color, no depth, alpha or stencil buffers, windowed
|
||||||
|
if not glfwOpenWindow(window_width, window_height, 8, 8, 8, 8, 24, 0, GLFW_WINDOW):
|
||||||
|
glfwTerminate()
|
||||||
|
raise SystemError('Unable to create GLFW window')
|
||||||
|
|
||||||
|
glfwEnable(GLFW_STICKY_KEYS)
|
||||||
|
glfwSwapInterval(1) #VSync on
|
||||||
|
|
||||||
|
def register_kbd_callback(self, f):
|
||||||
|
global kbd_callback_method
|
||||||
|
glfwSetKeyCallback(kbd_callback)
|
||||||
|
kbd_callback_method = f
|
||||||
|
|
||||||
|
def main_loop(self):
|
||||||
|
|
||||||
|
frame_count = 1
|
||||||
|
start_time = glfwGetTime()
|
||||||
|
|
||||||
|
running = True
|
||||||
|
while running:
|
||||||
|
|
||||||
|
current_time = glfwGetTime()
|
||||||
|
|
||||||
|
#Calculate and display FPS (frames per second)
|
||||||
|
if (current_time - start_time) > 1 or frame_count == 0:
|
||||||
|
frame_rate = frame_count / (current_time - start_time)
|
||||||
|
t = self.title + " (%d FPS)" % frame_rate
|
||||||
|
glfwSetWindowTitle(t)
|
||||||
|
start_time = current_time
|
||||||
|
frame_count = 0
|
||||||
|
|
||||||
|
frame_count = frame_count + 1
|
||||||
|
|
||||||
|
# Check if the ESC key was pressed or the window was closed
|
||||||
|
running = ((not glfwGetKey(GLFW_KEY_ESC))
|
||||||
|
and glfwGetWindowParam(GLFW_OPENED))
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
self.render()
|
||||||
|
|
||||||
|
glfwSwapBuffers()
|
||||||
|
|
||||||
|
|
||||||
|
glfwTerminate()
|
||||||
|
|
||||||
|
property window_title:
|
||||||
|
def __set__(self, title): self.title = title
|
||||||
|
|
||||||
|
property time:
|
||||||
|
def __get__(self): return glfwGetTime()
|
||||||
|
def __set__(self, t): glfwSetTime(t)
|
496
python/framework/triangulator.pyx
Normal file
496
python/framework/triangulator.pyx
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
#
|
||||||
|
# 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 math import floor
|
||||||
|
|
||||||
|
###
|
||||||
|
### 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)
|
||||||
|
double sqrt(double)
|
||||||
|
|
||||||
|
cdef list merge_sort(list 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:])
|
||||||
|
#do merge here
|
||||||
|
p1,p2,p = 0,0,0
|
||||||
|
while p1<len(lleft) and p2<len(lright):
|
||||||
|
if lleft[p1][0] < lright[p2][0]:
|
||||||
|
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
|
||||||
|
|
||||||
|
cdef class Point:
|
||||||
|
|
||||||
|
cdef float x, y
|
||||||
|
|
||||||
|
next = None
|
||||||
|
prev = None
|
||||||
|
edge = None
|
||||||
|
edges = []
|
||||||
|
|
||||||
|
property x:
|
||||||
|
def __get__(self): return self.x
|
||||||
|
|
||||||
|
property y:
|
||||||
|
def __get__(self): return self.y
|
||||||
|
|
||||||
|
def __init__(self, float x, float y):
|
||||||
|
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)
|
||||||
|
|
||||||
|
def cross(self, Point p):
|
||||||
|
return self.x * p.y - self.y * p.x
|
||||||
|
|
||||||
|
def dot(self, Point 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, Point p):
|
||||||
|
return self.x < p.x
|
||||||
|
|
||||||
|
'''
|
||||||
|
# Sort along y axis
|
||||||
|
def greater(self, p):
|
||||||
|
if y < p.y:
|
||||||
|
return True
|
||||||
|
elif y > p.y:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if x < p.x:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
'''
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
cdef class Edge:
|
||||||
|
|
||||||
|
cdef Point p, q
|
||||||
|
cdef bool above, below
|
||||||
|
cdef float slope, b
|
||||||
|
|
||||||
|
mpoints = []
|
||||||
|
|
||||||
|
def __init__(self, Point p, Point 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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
cdef bool is_above(self, Point point):
|
||||||
|
return (floor(point.y) < floor(self.slope * point.x + self.b))
|
||||||
|
cdef bool is_below(self, Point point):
|
||||||
|
return (floor(point.y) > floor(self.slope * point.x + self.b))
|
||||||
|
|
||||||
|
cdef float intersect(self, Point c, Point d):
|
||||||
|
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
|
||||||
|
|
||||||
|
cdef float signed_area(self, Point a, Point b, Point c):
|
||||||
|
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
|
||||||
|
|
||||||
|
cdef Point line_intersect(Edge e, float x):
|
||||||
|
cdef float y = e.slope * x + e.b
|
||||||
|
return Point(x, y)
|
||||||
|
|
||||||
|
cdef class Trapezoid:
|
||||||
|
|
||||||
|
cdef:
|
||||||
|
Point lpoint, rpoint
|
||||||
|
Edge top, bottom
|
||||||
|
Trapezoid upper_left, lower_left
|
||||||
|
Trapezoid upper_right, lower_right
|
||||||
|
bool inside
|
||||||
|
|
||||||
|
sink = None
|
||||||
|
|
||||||
|
def __init__(self, Point lpoint, Point rpoint, Edge top, Edge bottom):
|
||||||
|
self.lpoint = lpoint
|
||||||
|
self.rpoint = rpoint
|
||||||
|
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
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
def contains(self, Point point):
|
||||||
|
return (point.x > self.lpoint.x and point.x < self.rpoint.x and
|
||||||
|
self.top.is_above(point) and self.bottom.is_below(point))
|
||||||
|
|
||||||
|
def vertices(self):
|
||||||
|
cdef list verts = []
|
||||||
|
verts.append(line_intersect(self.top, self.lpoint.x))
|
||||||
|
verts.append(line_intersect(self.bottom, self.lpoint.x))
|
||||||
|
verts.append(line_intersect(self.bottom, self.rpoint.x))
|
||||||
|
verts.append(line_intersect(self.top, self.rpoint.x))
|
||||||
|
return verts
|
||||||
|
|
||||||
|
def add_points(self):
|
||||||
|
if self.lpoint != self.bottom.p:
|
||||||
|
self.bottom.mpoints.append(self.lpoint.clone)
|
||||||
|
if self.rpoint != self.bottom.q:
|
||||||
|
self.bottom.mpoints.append(self.rpoint.clone)
|
||||||
|
if self.lpoint != self.top.p:
|
||||||
|
self.top.mpoints.append(self.lpoint.clone)
|
||||||
|
if self.rpoint != self.top.q:
|
||||||
|
self.top.mpoints.append(self.rpoint.clone)
|
||||||
|
|
||||||
|
class TrapezoidalMap:
|
||||||
|
|
||||||
|
map = {}
|
||||||
|
margin = 50
|
||||||
|
bcross = None
|
||||||
|
tcross = None
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.bcross = None
|
||||||
|
self.tcross = None
|
||||||
|
|
||||||
|
def case1(self, t, e):
|
||||||
|
trapezoids = [None, None, None, None]
|
||||||
|
trapezoids.append(Trapezoid(t.lpoint, 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.rpoint, 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.rpoint.x else t.rpoint
|
||||||
|
trapezoids = [None, None, None]
|
||||||
|
trapezoids.append(Trapezoid(t.lpoint, 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.lpoint.x else t.lpoint
|
||||||
|
rp = e.q if e.q.x == t.rpoint.x else t.rpoint
|
||||||
|
trapezoids = [None, None]
|
||||||
|
if self.tcross is t.top:
|
||||||
|
trapezoids[0] = t.upper_left
|
||||||
|
trapezoids[0].update_right(t.upper_right, None)
|
||||||
|
trapezoids[0].rpoint = rp
|
||||||
|
else:
|
||||||
|
trapezoids[0] = 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[1] = t.lower_left
|
||||||
|
trapezoids[1].update_right(None, t.lower_right)
|
||||||
|
trapezoids[1].rpoint = rp
|
||||||
|
else:
|
||||||
|
trapezoids[1] = 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.lpoint.x else t.lpoint
|
||||||
|
trapezoids = [None, None, None]
|
||||||
|
if self.tcross is t.top:
|
||||||
|
trapezoids[0] = t.upper_left
|
||||||
|
trapezoids[0].rpoint = e.q
|
||||||
|
else:
|
||||||
|
trapezoids[0] = Trapezoid(lp, e.q, t.top, e)
|
||||||
|
trapezoids[0].update_left(t.upper_left, e.above)
|
||||||
|
if self.bcross is t.bottom:
|
||||||
|
trapezoids[1] = t.lower_left
|
||||||
|
trapezoids[1].rpoint = e.q
|
||||||
|
else:
|
||||||
|
trapezoids[1] = Trapezoid(lp, e.q, e, t.bottom)
|
||||||
|
trapezoids[1].update_left(e.below, t.lower_left)
|
||||||
|
trapezoids[2] = Trapezoid(e.q, t.rpoint, 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 = bottom.p
|
||||||
|
right = top.q
|
||||||
|
return Trapezoid(left, right, top, bottom)
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
|
||||||
|
parent_list = []
|
||||||
|
|
||||||
|
def __init__(self, left, right):
|
||||||
|
self.left = left
|
||||||
|
self.right = right
|
||||||
|
if left is not None:
|
||||||
|
left.parent_list.append(self)
|
||||||
|
if right is not None:
|
||||||
|
right.parent_list.append(self)
|
||||||
|
|
||||||
|
def replace(self, node):
|
||||||
|
for parent in node.parent_list:
|
||||||
|
if parent.left is node:
|
||||||
|
parent.left = self
|
||||||
|
else:
|
||||||
|
parent.right = self
|
||||||
|
self.parent_list.append(parent)
|
||||||
|
|
||||||
|
class Sink(Node):
|
||||||
|
|
||||||
|
def __new__(cls, trapezoid):
|
||||||
|
if trapezoid.sink is not None:
|
||||||
|
return trapezoid.sink
|
||||||
|
else:
|
||||||
|
return Sink(trapezoid)
|
||||||
|
|
||||||
|
def __init__(self, trapezoid):
|
||||||
|
Node.__init__(self, None, None)
|
||||||
|
trapezoid.sink = self
|
||||||
|
|
||||||
|
def locate(self, e):
|
||||||
|
return self
|
||||||
|
|
||||||
|
class XNode(Node):
|
||||||
|
|
||||||
|
def __init__(self, point, lchild, rchild):
|
||||||
|
Node.__init__(self, lchild, rchild)
|
||||||
|
self.point = point
|
||||||
|
self.lchild = lchild
|
||||||
|
self.rchild = rchild
|
||||||
|
|
||||||
|
def locate(self, e):
|
||||||
|
if e.p.x >= self.point.x:
|
||||||
|
return self.right.locate(e)
|
||||||
|
else:
|
||||||
|
return self.left.locate(e)
|
||||||
|
|
||||||
|
class YNode(Node):
|
||||||
|
|
||||||
|
def __init__(self, edge, lchild, rchild):
|
||||||
|
Node.__init__(self, lchild, rchild)
|
||||||
|
self.edge = edge
|
||||||
|
self.lchild = lchild
|
||||||
|
self.rchild = rchild
|
||||||
|
|
||||||
|
def locate(self, e):
|
||||||
|
if self.edge.is_above(e.p):
|
||||||
|
return self.right.locate(e)
|
||||||
|
elif self.edge.is_below(e.p):
|
||||||
|
return self.left.locate(e)
|
||||||
|
else:
|
||||||
|
if e.slope < self.edge.slope:
|
||||||
|
return self.right.locate(e)
|
||||||
|
else:
|
||||||
|
return self.left.locate(e)
|
||||||
|
|
||||||
|
class QueryGraph:
|
||||||
|
|
||||||
|
head = None
|
||||||
|
|
||||||
|
def __init__(self, head):
|
||||||
|
self.head = head
|
||||||
|
|
||||||
|
def locate(self, e):
|
||||||
|
return self.head.locate(e).trapezoid
|
||||||
|
|
||||||
|
def follow_segment(self, e):
|
||||||
|
trapezoids = [self.locate(e)]
|
||||||
|
j = 0
|
||||||
|
while(e.q.x > trapezoids[j].right_point.x):
|
||||||
|
if e > trapezoids[j].right_point:
|
||||||
|
trapezoids.append(trapezoids[j].upper_right)
|
||||||
|
else:
|
||||||
|
trapezoids .append(trapezoids[j].lower_right)
|
||||||
|
j += 1
|
||||||
|
return trapezoids
|
||||||
|
|
||||||
|
def replace(self, sink, node):
|
||||||
|
if not sink.parent_list:
|
||||||
|
self.head = node
|
||||||
|
else:
|
||||||
|
node.replace(sink)
|
||||||
|
|
||||||
|
def case1(self, sink, e, tlist):
|
||||||
|
yNode = YNode(e, Sink(tlist[1]), Sink(tlist[2]))
|
||||||
|
qNode = XNode(e.q, yNode, Sink(tlist[3]))
|
||||||
|
pNode = XNode(e.p, Sink(tlist[0]), qNode)
|
||||||
|
self.replace(sink, pNode)
|
||||||
|
|
||||||
|
def case2(self, sink, e, tlist):
|
||||||
|
yNode = YNode(e, Sink(tlist[1]), Sink(tlist[2]))
|
||||||
|
pNode = XNode(e.p, Sink(tlist[0]), yNode)
|
||||||
|
self.replace(sink, pNode)
|
||||||
|
|
||||||
|
def case3(self, sink, e, tlist):
|
||||||
|
yNode = YNode(e, Sink(tlist[0]), Sink(tlist[1]))
|
||||||
|
self.replace(sink, yNode)
|
||||||
|
|
||||||
|
def case4(self, sink, e, tlist):
|
||||||
|
yNode = YNode(e, Sink(tlist[0]), Sink(tlist[1]))
|
||||||
|
qNode = XNode(e.q, yNode, Sink(tlist[2]))
|
||||||
|
self.replace(sink, qNode)
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
import "triangulator.pyx"
|
|
@ -1,647 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this 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 this 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 math import floor
|
|
||||||
|
|
||||||
###
|
|
||||||
### Based on Raimund Seidel's paper "A simple and fast incremental randomized
|
|
||||||
### algorithm for computing trapezoidal decompositions and for triangulating polygons"
|
|
||||||
### (Ported from poly2tri)
|
|
||||||
|
|
||||||
class Triangulator(object) {
|
|
||||||
|
|
||||||
def __init__(points):
|
|
||||||
|
|
||||||
# Convex polygon list
|
|
||||||
self.polygons = []
|
|
||||||
# Order and randomize the Edges
|
|
||||||
self.EdgeList = initEdges()
|
|
||||||
|
|
||||||
# Initialize trapezoidal map and query structure
|
|
||||||
self.trapezoidalMap = new TrapezoidalMap
|
|
||||||
self.bounding_box = trapezoidalMap.bounding_box(EdgeList)
|
|
||||||
self.queryGraph = QueryGraph(Sink.init(bounding_box))
|
|
||||||
self.xMonoPoly = []
|
|
||||||
|
|
||||||
# The trapezoidal map
|
|
||||||
self.trapezoidMap = trapezoidalMap.map
|
|
||||||
# Trapezoid decomposition list
|
|
||||||
self.trapezoids = []
|
|
||||||
|
|
||||||
self.process()
|
|
||||||
|
|
||||||
// Build the trapezoidal map and query graph
|
|
||||||
def process(self):
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while(i < len(EdgeList)):
|
|
||||||
|
|
||||||
s = EdgeList(i)
|
|
||||||
traps = queryGraph.followEdge(s)
|
|
||||||
|
|
||||||
// Remove trapezoids from trapezoidal Map
|
|
||||||
for j in range(len(traps)):
|
|
||||||
trapezoidalMap.map -= traps(j)
|
|
||||||
|
|
||||||
for j in range(len(traps)):
|
|
||||||
t = traps(j)
|
|
||||||
tList = []
|
|
||||||
containsP = t.contains(s.p)
|
|
||||||
containsQ = t.contains(s.q)
|
|
||||||
if containsP and containsQ:
|
|
||||||
// Case 1
|
|
||||||
tList = trapezoidalMap.case1(t,s)
|
|
||||||
queryGraph.case1(t.sink, s, tList)
|
|
||||||
elif containsP and !containsQ:
|
|
||||||
// Case 2
|
|
||||||
tList = trapezoidalMap.case2(t,s)
|
|
||||||
queryGraph.case2(t.sink, s, tList)
|
|
||||||
elif !containsP and !containsQ:
|
|
||||||
// Case 3
|
|
||||||
tList = trapezoidalMap.case3(t, s)
|
|
||||||
queryGraph.case3(t.sink, s, tList)
|
|
||||||
else:
|
|
||||||
// Case 4
|
|
||||||
tList = trapezoidalMap.case4(t, s)
|
|
||||||
queryGraph.case4(t.sink, s, tList)
|
|
||||||
|
|
||||||
// Add new trapezoids to map
|
|
||||||
for k in range(len(tList)):
|
|
||||||
trapezoidalMap.map += tList[k]
|
|
||||||
|
|
||||||
trapezoidalMap.clear
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
for t in trapezoidalMap.map
|
|
||||||
markOutside(t)
|
|
||||||
|
|
||||||
for t in trapezoidalMap.map
|
|
||||||
if t.inside:
|
|
||||||
trapezoids.append(t)
|
|
||||||
t.addPoints()
|
|
||||||
|
|
||||||
createMountains()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monotone polygons - these are monotone mountains
|
|
||||||
def monoPolies(self):
|
|
||||||
polies = []
|
|
||||||
for i in range(len(self.xMonoPoly)):
|
|
||||||
polies.append(self.xMonoPoly(i).monoPoly)
|
|
||||||
return polies
|
|
||||||
|
|
||||||
|
|
||||||
// Build a list of x-monotone mountains
|
|
||||||
private def createMountains {
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
while(i < EdgeList.size) {
|
|
||||||
|
|
||||||
val s = EdgeList(i)
|
|
||||||
|
|
||||||
if(s.mPoints.size > 0) {
|
|
||||||
|
|
||||||
mountain = MonotoneMountain()
|
|
||||||
|
|
||||||
|
|
||||||
if len(s.mPoints) < 10:
|
|
||||||
k = insertSort((p1: Point, p2: Point) => p1 < p2)(s.mPoints).toList
|
|
||||||
else
|
|
||||||
k = msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
|
|
||||||
|
|
||||||
points = s.p :: k ::: List(s.q)
|
|
||||||
|
|
||||||
for p in points:
|
|
||||||
mountain.add(p)
|
|
||||||
|
|
||||||
mountain.process()
|
|
||||||
|
|
||||||
// Extract the triangles into a single list
|
|
||||||
j = 0
|
|
||||||
while(j < mountain.triangles.size) {
|
|
||||||
polygons += mountain.triangles(j)
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
xMonoPoly += mountain
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the outside trapezoids surrounding the polygon
|
|
||||||
private def markOutside(t: Trapezoid) {
|
|
||||||
if(t.top == bounding_box.top || t.bottom == bounding_box.bottom) {
|
|
||||||
t trimNeighbors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Edges and connect end points; update edge event pointer
|
|
||||||
private def initEdges: ArrayBuffer[Edge] = {
|
|
||||||
var Edges = List[Edge]()
|
|
||||||
for(i <- 0 until points.size-1)
|
|
||||||
Edges = new Edge(points(i), points(i+1)) :: Edges
|
|
||||||
Edges = new Edge(points.first, points.last) :: Edges
|
|
||||||
orderEdges(Edges)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def orderEdges(Edges: List[Edge]) = {
|
|
||||||
|
|
||||||
// Ignore vertical Edges!
|
|
||||||
val segs = new ArrayBuffer[Edge]
|
|
||||||
for(s <- Edges) {
|
|
||||||
val p = shearTransform(s.p)
|
|
||||||
val q = shearTransform(s.q)
|
|
||||||
// Point p must be to the left of point q
|
|
||||||
if(p.x > q.x) {
|
|
||||||
segs += new Edge(q, p)
|
|
||||||
} else if(p.x < q.x) {
|
|
||||||
segs += new Edge(p, q)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Randomized triangulation improves performance
|
|
||||||
// See Seidel's paper, or O'Rourke's book, p. 57
|
|
||||||
Random.shuffle(segs)
|
|
||||||
segs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevents any two distinct endpoints from lying on a common vertical 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 + 0.0001f * point.y, point.y)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doubly linked list
|
|
||||||
class MonotoneMountain {
|
|
||||||
|
|
||||||
var tail, head: Point = None
|
|
||||||
var size = 0
|
|
||||||
|
|
||||||
private val convexPoints = new ArrayBuffer[Point]
|
|
||||||
// Monotone mountain points
|
|
||||||
val monoPoly = new ArrayBuffer[Point]
|
|
||||||
// Triangles that constitute the mountain
|
|
||||||
val triangles = new ArrayBuffer[Array[Point]]
|
|
||||||
// Convex polygons that constitute the mountain
|
|
||||||
val convexPolies = new ArrayBuffer[Array[Point]]
|
|
||||||
// Used to track which side of the line we are on
|
|
||||||
private var positive = false
|
|
||||||
// Almost Pi!
|
|
||||||
private val PI_SLOP = 3.1
|
|
||||||
|
|
||||||
// Append a point to the list
|
|
||||||
def +=(point: Point) {
|
|
||||||
size match {
|
|
||||||
case 0 =>
|
|
||||||
head = point
|
|
||||||
size += 1
|
|
||||||
case 1 =>
|
|
||||||
// Keep repeat points out of the list
|
|
||||||
if(point ! head) {
|
|
||||||
tail = point
|
|
||||||
tail.prev = head
|
|
||||||
head.next = tail
|
|
||||||
size += 1
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
// Keep repeat points out of the list
|
|
||||||
if(point ! tail) {
|
|
||||||
tail.next = point
|
|
||||||
point.prev = tail
|
|
||||||
tail = point
|
|
||||||
size += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a point from the list
|
|
||||||
def remove(point: Point) {
|
|
||||||
val next = point.next
|
|
||||||
val prev = point.prev
|
|
||||||
point.prev.next = next
|
|
||||||
point.next.prev = prev
|
|
||||||
size -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partition a x-monotone mountain into triangles O(n)
|
|
||||||
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
|
|
||||||
def process {
|
|
||||||
|
|
||||||
// Establish the proper sign
|
|
||||||
positive = angleSign
|
|
||||||
// create monotone polygon - for dubug purposes
|
|
||||||
genMonoPoly
|
|
||||||
|
|
||||||
// Initialize internal angles at each nonbase vertex
|
|
||||||
// Link strictly convex vertices into a list, ignore reflex vertices
|
|
||||||
var p = head.next
|
|
||||||
while(p != tail) {
|
|
||||||
val a = angle(p)
|
|
||||||
// If the point is almost colinear with it's neighbor, remove it!
|
|
||||||
if(a >= PI_SLOP || a <= -PI_SLOP)
|
|
||||||
remove(p)
|
|
||||||
else
|
|
||||||
if(convex(p)) convexPoints += p
|
|
||||||
p = p.next
|
|
||||||
}
|
|
||||||
|
|
||||||
triangulate
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private def triangulate {
|
|
||||||
|
|
||||||
while(!convexPoints.isEmpty) {
|
|
||||||
|
|
||||||
val ear = convexPoints.remove(0)
|
|
||||||
val a = ear.prev
|
|
||||||
val b = ear
|
|
||||||
val c = ear.next
|
|
||||||
val triangle = Array(a, b, c)
|
|
||||||
|
|
||||||
triangles += triangle
|
|
||||||
|
|
||||||
// Remove ear, update angles and convex list
|
|
||||||
remove(ear)
|
|
||||||
if(valid(a)) convexPoints += a
|
|
||||||
if(valid(c)) convexPoints += c
|
|
||||||
}
|
|
||||||
assert(size <= 3, "Triangulation bug, please report")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private def valid(p: Point) = (p != head && p != tail && convex(p))
|
|
||||||
|
|
||||||
// Create the monotone polygon
|
|
||||||
private def genMonoPoly {
|
|
||||||
var p = head
|
|
||||||
while(p != None) {
|
|
||||||
monoPoly += p
|
|
||||||
p = p.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def angle(p: Point) = {
|
|
||||||
val a = (p.next - p)
|
|
||||||
val b = (p.prev - p)
|
|
||||||
Math.atan2(a cross b, a dot b)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def angleSign = {
|
|
||||||
val a = (head.next - head)
|
|
||||||
val b = (tail - head)
|
|
||||||
(Math.atan2(a cross b, a dot b) >= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines if the inslide angle is convex or reflex
|
|
||||||
private def convex(p: Point) = {
|
|
||||||
if(positive != (angle(p) >= 0)) false
|
|
||||||
else true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Node for a Directed Acyclic graph (DAG)
|
|
||||||
class Node(object):
|
|
||||||
|
|
||||||
def __init__(self, left, right):
|
|
||||||
self.left = left
|
|
||||||
self.right = right
|
|
||||||
if left is not None:
|
|
||||||
left.parentList.append(self)
|
|
||||||
if right is not None:
|
|
||||||
right.parentList.append(self)
|
|
||||||
parentList = []
|
|
||||||
|
|
||||||
def replace(self, node):
|
|
||||||
for parent in node.parentList:
|
|
||||||
if(parent.left == node):
|
|
||||||
parent.left = self
|
|
||||||
else:
|
|
||||||
parent.right = self
|
|
||||||
parentList.append(parent)
|
|
||||||
|
|
||||||
# Directed Acyclic graph (DAG)
|
|
||||||
# See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
|
|
||||||
|
|
||||||
class QueryGraph(var head: Node) {
|
|
||||||
|
|
||||||
def locate(s: Edge) = head.locate(s).trapezoid
|
|
||||||
|
|
||||||
def followEdge(s: Edge) = {
|
|
||||||
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
|
||||||
trapezoids += locate(s)
|
|
||||||
var j = 0
|
|
||||||
while(s.q.x > trapezoids(j).rightPoint.x) {
|
|
||||||
if(s > trapezoids(j).rightPoint) {
|
|
||||||
trapezoids += trapezoids(j).upperRight
|
|
||||||
} else {
|
|
||||||
trapezoids += trapezoids(j).lowerRight
|
|
||||||
}
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
trapezoids
|
|
||||||
}
|
|
||||||
|
|
||||||
def replace(sink: Sink, node: Node) {
|
|
||||||
if(sink.parentList.size == 0) {
|
|
||||||
head = node
|
|
||||||
} else {
|
|
||||||
node replace sink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def case1(sink: Sink, s: Edge, tList: Array[Trapezoid]) {
|
|
||||||
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
|
||||||
val qNode = new XNode(s.q, yNode, Sink.init(tList(3)))
|
|
||||||
val pNode = new XNode(s.p, Sink.init(tList(0)), qNode)
|
|
||||||
replace(sink, pNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
def case2(sink: Sink, s: Edge, tList: Array[Trapezoid]) {
|
|
||||||
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
|
||||||
val pNode = new XNode(s.p, Sink.init(tList(0)), yNode)
|
|
||||||
replace(sink, pNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
def case3(sink: Sink, s: Edge, tList: Array[Trapezoid]) {
|
|
||||||
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
|
||||||
replace(sink, yNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
def case4(sink: Sink, s: Edge, tList: Array[Trapezoid]) {
|
|
||||||
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
|
||||||
val qNode = new XNode(s.q, yNode, Sink.init(tList(2)))
|
|
||||||
replace(sink, qNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Sink(Node):
|
|
||||||
|
|
||||||
def __new__(cls, trapezoid):
|
|
||||||
if trapezoid.sink is not None:
|
|
||||||
return trapezoid.sink
|
|
||||||
else
|
|
||||||
return Sink(trapezoid)
|
|
||||||
|
|
||||||
def __init__(self, trapezoid):
|
|
||||||
Node.__init__(self, None, None)
|
|
||||||
trapezoid.sink = self
|
|
||||||
|
|
||||||
def locate(e):
|
|
||||||
return self
|
|
||||||
|
|
||||||
class TrapezoidalMap(object):
|
|
||||||
|
|
||||||
map = {}
|
|
||||||
margin = 50
|
|
||||||
bcross = None
|
|
||||||
tcross = None
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self.bcross = None
|
|
||||||
self.tcross = None
|
|
||||||
|
|
||||||
def case1(self, t, e):
|
|
||||||
trapezoids = (None, None, None, None)
|
|
||||||
trapezoids.append(Trapezoid(t.leftPoint, 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.rightPoint, t.top, t.bottom))
|
|
||||||
trapezoids[0].updateLeft(t.upperLeft, t.lowerLeft)
|
|
||||||
trapezoids[1].updateLeftRight(trapezoids[0], None, trapezoids[3], None)
|
|
||||||
trapezoids[2].updateLeftRight(None, trapezoids[0], None, trapezoids[3])
|
|
||||||
trapezoids[3].updateRight(t.upperRight, t.lowerRight)
|
|
||||||
return trapezoids
|
|
||||||
|
|
||||||
def case2(self, t, e):
|
|
||||||
val rp = e.q if e.q.x == t.rightPoint.x else t.rightPoint
|
|
||||||
trapezoids = (None, None, None)
|
|
||||||
trapezoids.append(Trapezoid(t.leftPoint, 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].updateLeft(t.upperLeft, t.lowerLeft)
|
|
||||||
trapezoids[1].updateLeftRight(trapezoids[0], None, t.upperRight, None)
|
|
||||||
trapezoids[2].updateLeftRight(None, trapezoids[0], None, t.lowerRight)
|
|
||||||
self.bcross = t.bottom
|
|
||||||
self.tcross = t.top
|
|
||||||
e.above = trapezoids[1]
|
|
||||||
e.below = trapezoids[2]
|
|
||||||
return trapezoids
|
|
||||||
|
|
||||||
def case3(self, t, e):
|
|
||||||
lp = s.p if s.p.x == t.leftPoint.x else t.leftPoint
|
|
||||||
rp = s.q if s.q.x == t.rightPoint.x else t.rightPoint
|
|
||||||
trapezoids = (None, None)
|
|
||||||
if self.tcross is t.top:
|
|
||||||
trapezoids[0] = t.upperLeft
|
|
||||||
trapezoids[0].updateRight(t.upperRight, None)
|
|
||||||
trapezoids[0].rightPoint = rp
|
|
||||||
else:
|
|
||||||
trapezoids[0] = Trapezoid(lp, rp, t.top, s)
|
|
||||||
trapezoids[0].updateLeftRight(t.upperLeft, s.above, t.upperRight, None)
|
|
||||||
if self.bcross is t.bottom:
|
|
||||||
trapezoids[1] = t.lowerLeft
|
|
||||||
trapezoids[1].updateRight(None, t.lowerRight)
|
|
||||||
trapezoids[1].rightPoint = rp
|
|
||||||
else:
|
|
||||||
trapezoids[1] = Trapezoid(lp, rp, s, t.bottom)
|
|
||||||
trapezoids[1].updateLeftRight(s.below, t.lowerLeft, None, t.lowerRight)
|
|
||||||
self.bcross = t.bottom
|
|
||||||
self.tcross = t.top
|
|
||||||
s.above = trapezoids[0]
|
|
||||||
s.below = trapezoids[1]
|
|
||||||
return trapezoids
|
|
||||||
|
|
||||||
def case4(self, t, e):
|
|
||||||
lp = s.p if s.p.x == t.leftPoint.x else t.leftPoint
|
|
||||||
trapezoids = (None, None, None)
|
|
||||||
if self.tcross is t.top:
|
|
||||||
trapezoids[0] = t.upperLeft
|
|
||||||
trapezoids[0].rightPoint = s.q
|
|
||||||
else:
|
|
||||||
trapezoids[0] = Trapezoid(lp, s.q, t.top, s)
|
|
||||||
trapezoids[0].updateLeft(t.upperLeft, s.above)
|
|
||||||
if self.bcross is t.bottom:
|
|
||||||
trapezoids[1] = t.lowerLeft
|
|
||||||
trapezoids[1].rightPoint = s.q
|
|
||||||
else:
|
|
||||||
trapezoids[1] = Trapezoid(lp, s.q, s, t.bottom)
|
|
||||||
trapezoids[1].updateLeft(s.below, t.lowerLeft)
|
|
||||||
trapezoids[2] = Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
|
||||||
trapezoids[2].updateLeftRight(trapezoids[0], trapezoids[1], t.upperRight, t.lowerRight)
|
|
||||||
|
|
||||||
return trapezoids
|
|
||||||
|
|
||||||
def bounding_box(self, edges):
|
|
||||||
max = edges[0].p + margin
|
|
||||||
min = edges[0].q - margin
|
|
||||||
for s in edges:
|
|
||||||
if s.p.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.q.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.p.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.q.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)
|
|
||||||
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)
|
|
||||||
|
|
||||||
class XNode(Node):
|
|
||||||
|
|
||||||
def __init__(self, point, lchild, rchild):
|
|
||||||
Node.__init__(self, lChild, rChild)
|
|
||||||
self.point = point
|
|
||||||
self.lchild = lchild
|
|
||||||
self.rchild = rchils
|
|
||||||
|
|
||||||
def locate(self, e):
|
|
||||||
if e.p.x >= self.point.x:
|
|
||||||
return self.right.locate(e)
|
|
||||||
else:
|
|
||||||
return self.left.locate(e)
|
|
||||||
|
|
||||||
class YNode(Node):
|
|
||||||
|
|
||||||
def __init__(self, edge, lchild, rchild):
|
|
||||||
Node.__init__(self, lChild, rChild)
|
|
||||||
self.edge = edge
|
|
||||||
self.lchild = lchild
|
|
||||||
self.rchile = rchild
|
|
||||||
|
|
||||||
def locate(self, e):
|
|
||||||
if edge > e.p:
|
|
||||||
return self.right.locate(e)
|
|
||||||
elif edge < e.p:
|
|
||||||
return self.left.locate(e)
|
|
||||||
else:
|
|
||||||
if e.slope < self.edge.slope:
|
|
||||||
return self.right.locate(e)
|
|
||||||
else:
|
|
||||||
return self.left.locate(e)
|
|
||||||
|
|
||||||
cdef class Point(object):
|
|
||||||
|
|
||||||
def __init__(self, x, y):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
next = None
|
|
||||||
prev = None
|
|
||||||
Edge = None
|
|
||||||
edges = []
|
|
||||||
|
|
||||||
cdef __sub__(self, Point p):
|
|
||||||
return Point(self.x - p.x, self.y - p.y)
|
|
||||||
|
|
||||||
cdef __sub__(self, float f):
|
|
||||||
return Point(self.x - f, self.y - f)
|
|
||||||
|
|
||||||
cdef __add__(self, Point p):
|
|
||||||
return Point(self.x + p.x, self.y + p.y)
|
|
||||||
|
|
||||||
cdef __add__(self, float f):
|
|
||||||
return Point(self.x + f, self.y + f)
|
|
||||||
|
|
||||||
cdef __mul__(self, float f):
|
|
||||||
return Point(self.x * f, self.y * f)
|
|
||||||
|
|
||||||
cdef __div__(self, float a):
|
|
||||||
return Point(self.x / a, self.y / a)
|
|
||||||
|
|
||||||
cdef cross(self, Point p):
|
|
||||||
return self.x * p.y - self.y * p.x
|
|
||||||
|
|
||||||
cdef dot(self, Point p):
|
|
||||||
return self.x * p.x + self.y * p.y
|
|
||||||
|
|
||||||
cdef length(self):
|
|
||||||
return math.sqrt(self.x * self.x + self.y * self.y)
|
|
||||||
|
|
||||||
cdef normalize(self):
|
|
||||||
return self / length
|
|
||||||
|
|
||||||
cdef __lt__(self, Point p):
|
|
||||||
return self.x < p.x
|
|
||||||
|
|
||||||
# Sort along y axis
|
|
||||||
cdef >(p: Point):
|
|
||||||
if y < p.y:
|
|
||||||
return True
|
|
||||||
elif y > p.y:
|
|
||||||
return False
|
|
||||||
else {
|
|
||||||
if x < p.x:
|
|
||||||
return True
|
|
||||||
else
|
|
||||||
return False
|
|
||||||
|
|
||||||
cdef !(p: Point) = !(p.x == x && p.y == y)
|
|
||||||
cdef clone = Point(x, y)
|
|
||||||
|
|
||||||
|
|
||||||
// Represents a simple polygon's edge
|
|
||||||
// TODO: Rename this class to Edge?
|
|
||||||
class Edge(object):
|
|
||||||
|
|
||||||
def __init__(self, p, q):
|
|
||||||
self.p = p
|
|
||||||
self.q = q
|
|
||||||
self.above, self.below = None
|
|
||||||
mPoints = []
|
|
||||||
self.slope = (q.y - p.y)/(q.x - p.x)
|
|
||||||
self.b = p.y - (p.x * self.slope)
|
|
||||||
|
|
||||||
def __gt__(self, point):
|
|
||||||
return (floor(point.y) < floor(slope * point.x + b))
|
|
||||||
def __lt__(self, point):
|
|
||||||
return (floor(point.y) > floor(slope * point.x + b))
|
|
||||||
|
|
||||||
def intersect(self, c, d):
|
|
||||||
a = self.p
|
|
||||||
b = self.q
|
|
||||||
a1 = _signed_area(a, b, d)
|
|
||||||
a2 = _signed_area(a, b, c)
|
|
||||||
if a1 != 0 and a2 != 0 and a1 * a2 < 0:
|
|
||||||
a3 = _signed_area(c, d, a)
|
|
||||||
a4 = a3 + a2 - a1
|
|
||||||
if a3 * a4 < 0:
|
|
||||||
t = a3 / (a3 - a4)
|
|
||||||
return a + ((b - a) * t)
|
|
||||||
|
|
||||||
def _signed_area(self, a, b, c):
|
|
||||||
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
|
|
2
python/p2t.sh
Normal file
2
python/p2t.sh
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
python -O poly2tri.py
|
20
python/poly2tri.py
Normal file
20
python/poly2tri.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env python2.6
|
||||||
|
from framework import Game
|
||||||
|
|
||||||
|
class Poly2Tri(Game):
|
||||||
|
|
||||||
|
#Screen size
|
||||||
|
screen_size = 800.0, 600.0
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(Poly2Tri, self).__init__(*self.screen_size)
|
||||||
|
self.main_loop()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
demo = Poly2Tri()
|
Loading…
Reference in New Issue
Block a user