Ejemplo n.º 1
0
class Polygon:
    def __init__(self):
        self.vertices = []
        self.monotones = None
        self.triangles = None
        self.bbox = None
        
        # If this is False, this does not mean it is no convex, but if it is True, it is!
        self.convex = False
    
    def addVertex(self, p):
        self.vertices.append(p)
        self.updateBoundingBox()
        
        if len(self.vertices) == 3:
            self.convex = True
        else:
            self.convex = False
    
    #
    # Draw this polygon
    #
    def draw(self):
        self.__draw__(0)
    
    def __draw__(self, level):
        if len(self.vertices) < 3:
            raise ValueError("This ain't no polygon! A duogon, at best!")
            
        if len(self.vertices) == 3:
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glColor3f (.1, .1, .1)
            glBegin(GL_TRIANGLES)
            glVertex2f(self.vertices[0].x, self.vertices[0].y)
            glVertex2f(self.vertices[1].x, self.vertices[1].y)
            glVertex2f(self.vertices[2].x, self.vertices[2].y)
            glEnd()
            
        if self.monotones and self.triangles:
            raise ValueError("A polygon containing both monotones and triangles? Something went wrong")
            
        if self.monotones:
            for m in self.monotones:
                m.__draw__(level + 1)
                
        if self.triangles:
            for t in self.triangles:
                t.__draw__(level + 1)
         
        if level == 0:
            glColor3f(.9, .9, .9)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glEnable(GL_POLYGON_OFFSET_LINE);
            glPolygonOffset(-1.,-1.);
            self.__drawPolygon__()

    def __drawPolygon__(self):
        glBegin(GL_POLYGON)
        size = len(self.vertices)
        for vertex in range(size):
            glVertex2f(self.vertices[vertex].x, self.vertices[vertex].y)
            glVertex2f(self.vertices[(vertex+1)%size].x, self.vertices[(vertex+1)%size].y)
        glEnd()
    
    # Line segment 1: p + t*r
    # Line segment 2: q + u*s
    @staticmethod
    def __LineSegmentIntersection__(p, r, q, s):
        rCrossS = r.cross(s)
        if rCrossS == 0:
            return -1, -1
        qMinusP = q.subtract(p)
        rhsT = qMinusP.cross(s)
        t = rhsT/rCrossS
        rhsR = qMinusP.cross(r)
        u = rhsR/rCrossS
        
        return t, u
    
    #
    # Check for collisions
    # \return True|False, CollisionVector
    #
    def collisionActor(self, actor):
    
        if not actor.bbox.overlaps(self.bbox):
            return False, Vector(0,0)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        p = Vector(actor.location.x, actor.location.y-actor.height/2)
        r = Vector(0, actor.height-1)

        size = len(self.vertices)
        for vertex in range(size):
            # t = (q - p) x s /(r x s)
            # u = (q - p) x r /(r x s)
            q = self.vertices[vertex]
            qPlusS = self.vertices[(vertex+1)%size]            
            s = qPlusS.subtract(q)
            
            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
            if t >= 0 and t < 1 and u >= 0 and u < 1:
                # return True, p.add(r.multiply(t)).add(r.multiply(0.5))
                return True, r.multiply(t)#.add(Vector(0, -.001))
        return False, Vector(0,0)
              
    #
    # Check if this polygon collides with another polygon
    # \return True|False, CollisionVector
    #
    def collision(self, other):        
        collides, axis, dist = self.__privateCollision__(other)
        
        if collides:
            return True, axis.multiply(dist)
        
        return False, None
            
    def __privateCollision__(self, other):
        if not self.bbox.overlaps(other.bbox):
            return False, None, None
            
        if self.convex:
            minAxis = None
            minDist = sys.float_info.max
            collides, minAxis, minDist = self.__privateCollisionConvex__(other, minAxis, minDist)
            if not collides:
                return False, minAxis, minDist
                
            return other.__privateCollisionConvex__(self, minAxis, minDist)
            
        if self.monotones:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for m in self.monotones:
                newCollides, newAxis, newDist = m.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis
                        
            return collides, axis, dist        
            
        if self.triangles:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for t in self.triangles:
                newCollides, newAxis, newDist = t.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis
                        
            return collides, axis, dist    

            
    # Internal collision method
    def __privateCollisionConvex__(self, other, minAxis, minDist):
        size = len(self.vertices)
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i+1)%size]
            
            perp = Polygon.__perpendicularVector__(self, v1, v2)
            
            minSelf = maxSelf = minOther = maxOther = None
            
            minSelf, maxSelf = self.__projectOnAxis__(perp)
            minOther, maxOther = other.__projectOnAxis__(perp)
            
            dist = Polygon.__getIntervalDistance__(minSelf, maxSelf, minOther, maxOther)
            
            if dist > 0.:
                return False, minAxis, minDist
            elif abs(dist) < minDist:
                minDist = abs(dist)
                minAxis = perp
        return True, minAxis, minDist
            
            
    #
    # Project all vertices of this polygon onto an axis
    # Get the min and max values
    #
    def __projectOnAxis__(self, axis):
        min = max = axis.dot(self.vertices[0])
        
        for i in range(1, len(self.vertices)):
            product = axis.dot(self.vertices[i])
            
            if product < min:
                min = product
            
            if product > max:
                max = product
                
        return min, max
    
    #
    # Get the distance beween two 1-dimensional intervals
    #
    @staticmethod
    def __getIntervalDistance__(min1, max1, min2, max2):
        if min1 < min2:
            return min2 - max1
        else:
            return min1 - max2
            
    #
    # Get the perpendicular vector on a line segment defined by 2 poins
    #
    @staticmethod
    def __perpendicularVector__(self, v1, v2):
        dy = (v1.y - v2.y)
        dx = (v1.x - v2.x)
        d = sqrt(dy*dy + dx*dx)
        if d == 0:
            return Vector(1, 0)
        return Vector(-dy / d, dx / d)
            
    
    def __selfIntersects__(self):
        size = len(self.vertices)
        for vertex1 in range(size):
            p = self.vertices[vertex1]
            pPlusR = self.vertices[(vertex1+1)%size]
            r = pPlusR.subtract(p)
            for vertex2 in range(vertex1, size):
                if vertex1 == vertex2:
                    continue
                q = self.vertices[vertex2]
                qPlusS = self.vertices[(vertex2+1)%size]
                s = qPlusS.subtract(q)
                
                t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    return True
        return False

    # TODO
    def cut(self, other):
        sizeSelf = len(self.vertices)
        sizeOther = len(other.vertices)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        for vertexSelf in range(sizeSelf):
            p = self.vertices[vertexSelf]
            pPlusR = self.vertices[(vertexSelf+1)%sizeSelf]
            r = pPlusR.subtract(p)
            for vertexOther in range(sizeOther):
                # t = (q - p) x s /(r x s)
                # u = (q - p) x r /(r x s)
                q = other.vertices[vertexOther]
                qPlusS = other.vertices[(vertexOther+1)%sizeOther]
                
                s = qPlusS.subtract(q)
                rCrossS = r.cross(s)
                qMinusP = q.subtract(p)
                rhsT = qMinusP.cross(s)
                t = rhsT/rCrossS
                rhsR = qMinusP.cross(r)
                u = rhsR/rCrossS
                
                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    intersect = p.add(r.multiply(t))
                    glColor3f(0.8, 0.8, 0.2)
                    glPointSize(6)
                    intersect.draw()
                    
    #
    # Check if the given point is inside the polygon
    #
    def inside(self, p):
        size = len(self.vertices)
        count = 0
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i+1)%size]
            
            if (v1.y > p.y and v2.y <= p.y) or \
                (v1.y <= p.y and v2.y > p.y):
                            
                div = (v1.x - v2.x)
                if div == 0:
                    if p.x - v1.x > 0:
                        count += 1
                m = (v1.y - v2.y) / div
                if p.x - (v1.x + (p.y - v1.y)/m) > 0:
                    count += 1
        return count%2 == 1
    
    @staticmethod
    def __interiorAngleGreaterThanPi__(v1, v2, v3):
        cross = v1.subtract(v2).cross(v3.subtract(v2))
        if cross < 0:
            return True
        return False
      
    # Doubly Connected edge list: a data structure which can be used in monotone decomposition and triangulation of a polygon
    class DCELHalfEdge:
        def __init__(self, origin):
            self.origin = origin
            self.twin = None
            self.next = None
            self.prev = None
            self.face = None
            self.helper = None
            
        def start(self):
            return self.origin.v
        
        def end(self):
            return self.next.origin.v
            
        def xDistTo(self, v):
            # y - y1 = m(x - x1)
            # x = x1 + (y - y1)/m
            div = (self.start().x - self.end().x)
            if div == 0:
                return v.x - self.start().x
            m = (self.start().y - self.end().y) / div
            
            return v.x - (self.start().x + (v.y - self.start().y)/m)
            
        def mark(self):
            glLineWidth(10)
            glBegin(GL_LINES)
            glVertex2f(self.start().x, self.start().y)
            glVertex2f(self.end().x, self.end().y)
            glEnd()
            glLineWidth(1)
            
    class DCELVertex:
        def __init__(self, v):
            self.v = v
            self.leavingEdge = None
            
    class DCELFace:
        def __init__(self, edge):
            self.edge = edge
            self.visited = False
            
    class DCELNextEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge
            
        def next(self):
            self.current = self.current.next
            if self.current == self.start:
                return None
            return self.current
            
    class DCELLeavingEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge
            
        def next(self):
            self.current = self.current.prev.twin
            if self.current == self.start:
                return None
            return self.current
            

    # Search tree, implemented initially as a flat array (Laziness)
    class Tree:
        def __init__(self):
            self.edges = []
            # self.count = 0
        
        def add(self, edge):
            self.edges.append(edge)
         
        def remove(self, edge):
            self.edges.remove(edge)
            
        def edgeLeftOf(self, v):
            d = sys.float_info.max
            e = None
            for edge in self.edges:
                if (edge.start().y >= v.y and edge.end().y < v.y) or \
                    (edge.start().y < v.y and edge.end().y >= v.y):
                        distToEdge = edge.xDistTo(v)                        
                        if distToEdge > 0 and d > distToEdge:
                            d = distToEdge
                            e = edge
            return e
            
        def draw(self):
            print "hmpf"
            for edge in self.edges:
                glLineWidth(6)
                glBegin(GL_LINES)
                glVertex2f(edge.start().x, edge.start().y)
                glVertex2f(edge.end().x, edge.end().y)
                glEnd()
                glLineWidth(1)
                
    # Is it possible to add a diagonal between two vertices
    def __canAddDiagonal__(self, vi1, vi2):
        size = len(self.vertices)
        p = self.vertices[vi1]
        pPlusR = self.vertices[vi2]
        r = pPlusR.subtract(p)
        for v in range(size):
            # if (vi1==v and vi2==(v+1)%size) or (vi2==v and vi1==(v+1)%size):
                # return False
            q = self.vertices[v]
            qPlusS = self.vertices[(v+1)%size]
            s = qPlusS.subtract(q)
            
            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
            if t > 0 and t < 1 and u > 0 and u < 1:
                return False
                
        midDiagonal = Vector((p.x+pPlusR.x)/2, (p.y+pPlusR.y)/2)
        return self.inside(midDiagonal)
            
       
    # Add a diagonal to the DCEL of this polygon
    def __addDiagonal__(self, vi1, vi2, dcelVertices):
        v1 = dcelVertices[vi1]
        v2 = dcelVertices[vi2]
        
        # Find the common face for v1 and v2
        face = None
        e1 = v1.leavingEdge 
        it1 = Polygon.DCELLeavingEdgeIterator(e1)
        while e1:
            face1 = e1.face
            e2 = v2.leavingEdge
            it2 = Polygon.DCELLeavingEdgeIterator(e2)
            while e2:
                if face1 == e2.face and face1 != None:
                    face = face1
                    break
                e2 = it2.next()
            e1 = it1.next()
        
        # Find the next and previous edges for both diagonals
        prevEdge1 = None
        nextEdge1 = None
        prevEdge2 = None
        nextEdge2 = None

        e = face.edge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            if e.origin == v1:
                prevEdge1 = e.prev
                nextEdge1 = e
            if e.origin == v2:
                prevEdge2 = e.prev
                nextEdge2 = e
            e = it.next()
        
        # Connect 2 new half edges
        diagonal1 = Polygon.DCELHalfEdge(v1)
        diagonal1.prev = prevEdge1
        prevEdge1.next = diagonal1
        diagonal1.next = nextEdge2
        nextEdge2.prev = diagonal1
        
        diagonal2 = Polygon.DCELHalfEdge(v2)
        diagonal2.prev = prevEdge2
        prevEdge2.next = diagonal2
        diagonal2.next = nextEdge1
        nextEdge1.prev = diagonal2

        diagonal1.twin = diagonal2
        diagonal2.twin = diagonal1
        
        # Connect new faces
        face1 = Polygon.DCELFace(diagonal1)
        face2 = Polygon.DCELFace(diagonal2)
        e1 = diagonal1
        it1 = Polygon.DCELNextEdgeIterator(e1)
        while e1:
            e1.face = face1
            e1 = it1.next()
        
        e2 = diagonal2
        it2 = Polygon.DCELNextEdgeIterator(e2)
        while e2:
            e2.face = face2
            e2 = it2.next()

    
    # Construct the Doubly connected edge list
    # \param dcelVertices A list of DCELVertex objects, must be empty
    def __constructDCEL__(self, dcelVertices):
        size = len(self.vertices)
        currentVertex = None
        # Create all DCEL Vertices with a leaving edge
        for i in range(size):
            # Create the current vertex or use the previously stored last one, if this is the last vertex
            v = self.vertices[i]
            currentVertex = Polygon.DCELVertex(v)
            # Create the DCEL Half Edge
            currentEdge = Polygon.DCELHalfEdge(currentVertex)
            # Connect
            currentVertex.leavingEdge = currentEdge
            dcelVertices.append(currentVertex)
        
        # Create all twin half edges
        for i in range(size):
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i+1)%size]
            currentEdge = currentVertex.leavingEdge
            twinEdge = Polygon.DCELHalfEdge(nextVertex)
            currentEdge.twin = twinEdge
            twinEdge.twin = currentEdge
            
        # Connect all edges
        for i in range(size):
            prevVertex = dcelVertices[i-1]
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i+1)%size]
            
            currentEdge = currentVertex.leavingEdge
            currentEdge.prev = prevVertex.leavingEdge
            currentEdge.next = nextVertex.leavingEdge
            
            twinEdge = currentEdge.twin
            twinEdge.prev = nextVertex.leavingEdge.twin
            twinEdge.next = prevVertex.leavingEdge.twin

        # Connect the single face
        startVertex = dcelVertices[0]
        startEdge = startVertex.leavingEdge
        face = Polygon.DCELFace(startEdge)
        
        e = startEdge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            e.face = face
            e = it.next()
        
                
    # Sort the vertices of this polygon according to y coordinate
    def __ySortVertices__(self, sortedVertices):
        size = len(self.vertices)
        for vertex in range(size):
            v = self.vertices[vertex]
            sizeSorted = len(sortedVertices)
            found = False
            for index in range(sizeSorted):
                if v.above(self.vertices[sortedVertices[index]]):
                    sortedVertices.insert(index, vertex)
                    found = True
                    break
            if not found:
                sortedVertices.append(vertex)
    
    @staticmethod
    def __constructPolygonsFromDCEL__(dcelVertices, polygons):
        # Iterator all leaving edges on all vertices to find all faces
        for vertex in dcelVertices:
            e1 = vertex.leavingEdge
            it1 = Polygon.DCELLeavingEdgeIterator(e1)
            polygon = Polygon()
            while e1:
                if e1.face and not e1.face.visited:
                    e1.face.visited = True
                    polygon = Polygon()
                    e2 = e1.face.edge
                    it2 = Polygon.DCELNextEdgeIterator(e2)
                    while e2:
                        polygon.addVertex(e2.origin.v)
                        e2 = it2.next()
                    polygons.append(polygon)
                e1 = it1.next()
            
    def triangulate(self):
        size = len(self.vertices)
        if size <= 3:
            return
        # Monotone decomposition
        # Computation Geometry Algorithms and Applications, page 50
        startVertex = 1
        endVertex = 2
        regularVertex = 3
        mergeVertex = 4
        splitVertex = 5
        
        vertexType = []
        # Determine the type for each vertex
        for vertex in range(size):
            v = self.vertices[vertex]
            vPrev = self.vertices[vertex-1]
            vNext = self.vertices[(vertex+1)%size]
            if v.above(vPrev) and v.above(vNext):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(splitVertex)
                else:
                    vertexType.append(startVertex)
            elif vPrev.above(v) and vNext.above(v):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(mergeVertex)
                else:
                    vertexType.append(endVertex)
            else:
                vertexType.append(regularVertex)

        # glColor3f(1., 1., 1.)
        # glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)
        # if False:
            # self.__privateDraw__()
            # for vertex in range(size):
                # v = self.vertices[vertex]
                # if vertexType[vertex] == startVertex:
                    # glPointSize(12)
                # elif vertexType[vertex] == endVertex:
                    # glPointSize(3)
                # elif vertexType[vertex] == splitVertex:
                    # glPointSize(6)
                # elif vertexType[vertex] == mergeVertex:
                    # glPointSize(9)
                # else:
                    # glPointSize(1)
                
                # glColor3f(1., 2., 2.)
                # glBegin(GL_POINTS)
                # glVertex2f(v.x, v.y)
                # glEnd()
        
        # Construct doubly-connected edge list
        dcelVertices = []
        self.__constructDCEL__(dcelVertices)
        
       
        #
        # TEST CODE : Check DCEL
        #
        # polys = []
        # Polygon.__constructPolygonsFromDCEL__(dcelVertices, polys)
        
        # c = int(time.clock()*2)%(len(polys)+1)
        # if c == len(polys):
            # glColor3f(1., 1., .2)
            # self.__privateDraw__()
            # return
        # glColor3f(.2, .2, 1.)
        # polys[c].__privateDraw__()
        
        #
        # TEST CODE : Check prev, next, twin edges
        #
        # c = int(time.clock()*2)%(len(dcelVertices))
        # v = dcelVertices[c]
        # glPointSize(30)
        # glBegin(GL_POINTS)
        # glVertex2f(v.v.x, v.v.y)
        # glEnd()
        # glPointSize(1)
        # v.leavingEdge.twin.mark()
        # glColor3f(1., 1., .2)
        # v.leavingEdge.twin.next.mark()
        # return
        
        #
        # TEST CODE : Check edgeLeftOf
        #
        # tree = Polygon.Tree()
        # e1 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, 1)))
        # e2 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, -1)))
        # e1.next = e2
        # e2.prev = e1
        # tree.add(e1)
        # tree.edgeLeftOf(Vector(0, 0)).mark()
        # return
                
        # Sort vertices top to bottom
        # Insertion sort
        sortedVertices = []        
        self.__ySortVertices__(sortedVertices)
        
        # Initialize the partition with the edges of the (possibly) not-monotone polygon
        tree = Polygon.Tree()

        # Handle each vertex, from top to bottom the polygon
        # Handling each vertex depends on the type of the vertex
        for vertex in sortedVertices:
            v = self.vertices[vertex]
            if vertexType[vertex] == startVertex:
                ei = dcelVertices[vertex].leavingEdge
                ei.helper = vertex
                # Add e_i to searchtree
                tree.add(ei)
            elif vertexType[vertex] == endVertex or vertexType[vertex] == mergeVertex:
                # Perform actions which are the same of end or merge vertices
                eiMinus1 = dcelVertices[vertex-1].leavingEdge
                # Add a diagonal if helper(e_i+1) is a merge vertex
                if vertexType[eiMinus1.helper] == mergeVertex:
                    self.__addDiagonal__(vertex, eiMinus1.helper, dcelVertices)
                # Delete e_i from searchtree
                tree.remove(eiMinus1)
                # Perform actions which are unique to a merge vertex
                if vertexType[vertex] == mergeVertex:
                    # Find edge directly to the right of vertex
                    ej = tree.edgeLeftOf(v)
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex
            elif vertexType[vertex] == splitVertex:
                # Find edge directly to the left of vertex
                ej = tree.edgeLeftOf(v)
                self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                # Set vertex as new helper
                ej.helper = vertex
                ei = dcelVertices[vertex].leavingEdge
                # Add e_i to tree and set vertex as helper
                ei.helper = vertex
                tree.add(ei)
            elif vertexType[vertex] == regularVertex:
                #If the interior of the polygon lies right of this vertex
                if self.vertices[(vertex+1)%size].y <= v.y and \
                   self.vertices[vertex-1].y > v.y:
                    eiMinus1 = dcelVertices[vertex-1].leavingEdge
                    helper = eiMinus1.helper
                    # Add a diagonal if helper(e_i-1) is a merge vertex
                    if vertexType[helper] == mergeVertex:
                        self.__addDiagonal__(vertex, helper, dcelVertices)
                    # Delete e_i-1 from searchtree
                    tree.remove(eiMinus1)
                    # Add e_i to tree and set vertex as helper
                    ei = dcelVertices[vertex].leavingEdge
                    ei.helper = vertex
                    tree.add(ei)
                else:
                    # Find edge directly to the left of vertex
                    ej = tree.edgeLeftOf(v)
                    if not ej:
                        glPointSize(5)
                        v.draw()
                        glPointSize(1)
                        return
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex

        # Construct the monotones
        self.monotones = []
        Polygon.__constructPolygonsFromDCEL__(dcelVertices, self.monotones)
        
        # print "Monotones: " + str(len(self.monotones))
        # if self.debug == 2:
            # c = int(time.clock()*2)%(len(self.monotones)+1)
            # if c == len(self.monotones):
                # glColor3f(1., 1., .2)
                # self.__privateDraw__()
                # return
            # glColor3f(.2, .2, 1.)
            # self.monotones[c].__privateDraw__()
            # for polygon in self.monotones:
                # polygon.__privateDraw__()
            # return
        
        # for m in self.monotones:
            # glColor3f(1., 1., 1.)
            # m.__privateDraw__()

        for m in self.monotones:
        
            if len(m.vertices) == 3:
                # if self.debug == 1 or self.debug == 3:
                    # m.draw(1., .6, .0)
                continue
                       
            monoDcelVertices = []
            m.__constructDCEL__(monoDcelVertices)

            monotoneSortedVertices = []
            m.__ySortVertices__(monotoneSortedVertices)
          
            stack = []
            stack.append(monotoneSortedVertices[0])
            stack.append(monotoneSortedVertices[1])

            for j in range(2, len(monotoneSortedVertices)-1):
            
                if m.__onSameEdge__(stack[-1], monotoneSortedVertices[j]):
                
                    poppedLast = stack.pop()
                    vStack = stack[-1]
                    
                    while m.__canAddDiagonal__(vStack, monotoneSortedVertices[j]):
                        m.__addDiagonal__(vStack, monotoneSortedVertices[j], monoDcelVertices)
                        poppedLast = stack.pop()
                        if len(stack) == 0:
                            break;
                        vStack = stack[-1]
                    
                    stack.append(poppedLast)
                    stack.append(monotoneSortedVertices[j])
                else:
                    while len(stack) > 0:
                        vi = stack.pop()
                        if len(stack) > 0:
                            m.__addDiagonal__(vi, monotoneSortedVertices[j], monoDcelVertices)
                    stack.append(monotoneSortedVertices[j-1])
                    stack.append(monotoneSortedVertices[j])

            stack.pop()
            while len(stack) > 1:
                m.__addDiagonal__(stack[-1], monotoneSortedVertices[-1], monoDcelVertices)
                stack.pop()

            m.triangles = []
            Polygon.__constructPolygonsFromDCEL__(monoDcelVertices, m.triangles)
            
            # if self.debug == 1:
                # m.triangles[int(time.clock()*2)%len(m.triangles)].draw(1., .2, .2)
            # if self.debug == 3:
                # for t in m.triangles:
                    # t.draw(1., .2, .2)

    # check if two vertices are on the same edge, given their indices
    def __onSameEdge__(self, i1, i2):
        size = len(self.vertices)
        return abs(i1-i2) == 1 or abs(i1-i2) == size-1

    # Apply midpoint displacement to this polygon
    # The midpoint of each line segment will be displaced randomly
    # perpendicular to the line segment, distance between -factor and +factor
    def midpointDisplacement(self, factor):
        seed = random.randint(0, 10000)
        # if factor == 8:
            # seed = 8977
            # seed = 9539
            # seed = 1909
            # seed = 5021
            # seed = 6131 # Triangulation issue
            # seed = 6198 # Triangulation issue
            # seed = 3403 # Triangulation issue
        print factor
        print seed
        random.seed(seed)
        size = len(self.vertices)
        oldVertices = list(self.vertices)
        vertices = []
        for vertex in range(size):
            p1 = self.vertices[vertex]
            p2 = self.vertices[(vertex+1)%size]
            vertices.append(p1)
            p = p1.subtract(p2).perpendicularVector()
            r = factor - random.random()*factor*2
            x = (p1.x + p2.x)/2 + r*p.x
            y = (p1.y + p2.y)/2 + r*p.y
            vertices.append(Vector(x, y))
        self.vertices = vertices
        if self.__selfIntersects__():
            print "uh oh"
            self.vertices = oldVertices
            self.midpointDisplacement(factor/2)
        self.updateBoundingBox()

    def updateBoundingBox(self):
        size = len(self.vertices)
        self.bbox = BoundingBox(self.vertices[0].x,self.vertices[0].y,self.vertices[0].x,self.vertices[0].y)
        for vertex in range(1, size):
            self.bbox.add(self.vertices[vertex].x, self.vertices[vertex].y)
    
    @staticmethod
    def createBoundingBoxPolygon(pMin, pMax):
        p = Polygon()
        p.addVertex(Vector(pMin.x, pMax.y))
        p.addVertex(Vector(pMin.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMax.y))
        return p
Ejemplo n.º 2
0
class ACmap:
    def __init__(self, file, options):
        execfile(file)

        if not hasattr(self, 'map_data'):
            raise MdsError.ExecError, "After loading map data from '%s' no map_data has been defined." % file

        print "Read %d points from '%s'" % (len(self.map_data.keys()), file)

        self.file = file
        self.options = options

        # Under some circumstances we need to maintain a bounding box
        if options['bounding box compute'] or options['grid compute'] or options['notches compute'] or options['dots compute']:
            self.bb = BoundingBox(options)
        else:
            self.bb = None
            
        cgoData = {} # A collection of independent CGO groups.
        types = [] # The point types we have seen (usually this will just be 'strains' and 'antisera').
        titerCoords = [] # A list of xyz-tuples, being the coords that will be used to make the titer surface (if one is being made).

        pointsOrder = self.map_data.keys()
        pointsOrder.sort(lambda a,b: cmp(self.map_data[a]['transparency'], self.map_data[b]['transparency']))
        for pointName in pointsOrder:
            point = self.map_data[pointName]
            
            type = point['type']

            # Get the defaults for this point type (i.e., strain, serum).
            for option in ('color', 'shape'):
                if option not in point:
                    point[option] = options[type + ' ' + option]
                    
            pointCoords = Coord(point['coords'])
            pointColor = point['color']
            pointRadius = point.get('radius', options[type + ' radius'])
            pointAlpha = point.get('transparency', None)
            if pointAlpha is not None:
                pointAlpha = 1.0 - pointAlpha # convert transparency to Alpha
				
            # Show the point (strain or serum) shape.
            if options[type + ' compute']:
                if type not in types:
                    types.append(type)
                if type not in cgoData:
                    cgoData[type] = independentPymolCgoGroup(options['cgo prefix'] + options[type + ' cgo name'])

                if pointAlpha is not None:
                    cgoData[type].addAlpha(pointAlpha)
                cgoData[type].setColor(pointColor)
                
                if point['shape'] == 'sphere':
                    cgoData[type].addSphere(pointCoords, pointRadius)
                    if self.bb:
                        self.bb.add(pointCoords, pointRadius)
                elif point['shape'] == 'cube':
                    sideLength = point.get('side length', options[type + ' side length'])
                    cgoData[type].addCube(pointCoords, sideLength)
                    if self.bb:
                        self.bb.add(pointCoords, sideLength / 2.0)
                else:
                    raise MdsError.PointError, "Point '%s' in file '%s' has an unrecognized shape option (%s)." % (pointName, file, point['shape'])
            # cgoData[type].addAlpha(1.0) # reset transparency

            # Show the point (strain or serum) name.
            if options[type + ' names compute']:
                cgoKey = type + ' names'
                if cgoKey not in types:
                    types.append(cgoKey)
                if cgoKey not in cgoData:
                    cgoData[cgoKey] = independentPymolCgoGroup(options['cgo prefix'] + options[cgoKey + ' cgo name'])
                pointNameCoords = pointCoords + options[cgoKey + ' offset']
                cgoData[cgoKey].addWiretext(pointName, pointNameCoords, options[cgoKey + ' color'], options[cgoKey + ' text width'])
                if self.bb:
                    # TODO: this is broken - we don't know the extent of the name.
                    self.bb.add(pointNameCoords)

            # Add the titer line.
            pointTiter = point.get('titer')
            
            if pointTiter is not None:
                # The titer coords (which we compute in any case, as we may use them later in surface computations)
                # are this point's coords, with the titer added to the Z coord.
                titerEndCoords = pointCoords + [0, 0, pointTiter]
                titerCoords.append(titerEndCoords)
                if options['titer compute']:
                    if 'titer' not in cgoData:
                        cgoData['titer'] = independentPymolCgoGroup(options['cgo prefix'] + options['titer cgo name'])
                    cgoData['titer'].setLineWidth(options['titer line width'])
                    cgoData['titer'].addLine(pointCoords, titerEndCoords)
                    if self.bb:
                        self.bb.add(titerEndCoords)

            # Add the Procrustes name, sphere and line, if any
            pointProcrustesCoords = point.get('procrustes coords')
            
            if pointProcrustesCoords is not None:
                if options['procrustes names compute']:
                    if 'procrustes name' in point:
                        pointProcrustesName = point['procrustes names']
                    else:
                        pointProcrustesName = pointName
                    if 'procrustes names' not in cgoData:
                        cgoData['procrustes names'] = independentPymolCgoGroup(options['cgo prefix'] + options['procrustes names cgo name'])
                    procrustesNameCoords = pointProcrustesCoords + options['procrustes names offset']
                    cgoData['procrustes names'].addWiretext(pointProcrustesName, procrustesNameCoords,
                                                            options['procrustes names color'], options['procrustes names text width'])
                    if self.bb:
                        # TODO: this is broken - we don't know the extent of the name.
                        self.bb.add(procrustesNameCoords)

                if options['procrustes spheres compute']:
                    if 'procrustes spheres' not in cgoData:
                        cgoData['procrustes spheres'] = independentPymolCgoGroup(options['cgo prefix'] + options['procrustes spheres cgo name'])
                    cgoData['procrustes spheres'].setColor(options['procrustes spheres color'])
                    cgoData['procrustes spheres'].addSphere(pointProcrustesCoords, options['procrustes spheres radius'])
                    if self.bb:
                        self.bb.add(pointProcrustesCoords, options['procrustes spheres radius'])

                if options['procrustes lines compute']:
                    if 'procrustes lines' not in cgoData:
                        cgoData['procrustes lines'] = independentPymolCgoGroup(options['cgo prefix'] + options['procrustes lines cgo name'])
                    cgoData['procrustes lines'].setColor(options['procrustes lines color'])
                    cgoData['procrustes lines'].setLineWidth(options['procrustes lines width'])
                    cgoData['procrustes lines'].addLine(pointCoords, pointProcrustesCoords)
                    if self.bb:
                        self.bb.add(pointProcrustesCoords)

            # Add the blobs around the point.
            blobs = point.get('blobs')
            
            if blobs is not None:
                cgoKey = type + ' blobs'
                if options[cgoKey + ' compute']:
                    if cgoKey not in cgoData:
                        cgoData[cgoKey] = independentPymolCgoGroup(options['cgo prefix'] + options[cgoKey + ' cgo name'])
                    blobStyle = options[cgoKey + ' style']
                    blobZ = 0.0 # How much to add to the z component of each blob
                    blobZInc = options[cgoKey + ' z inc']
                    for blob in blobs:
                        blobZ += blobZInc
                        if blobStyle == 'triangleFan':
                            # The first blob vertex is at the center (i.e., where the strain/serum is).
                            cgoData[cgoKey].startTriangleFan()
                            alpha = blob.get('transparency', None)
                            if alpha is not None:
                                cgoData[cgoKey].addAlpha(alpha)
                            coords = Coord(pointCoords)
                            coords += (0.0, 0.0, blobZ)
                            cgoData[cgoKey].setColor(pointColor)
                            cgoData[cgoKey].addVertex(coords)
                            for vertex in blob['points']:
                                coords = Coord(vertex['coord'])
                                coords += (0.0, 0.0, blobZ)
                                cgoData[cgoKey].setColor(vertex['color'])
                                cgoData[cgoKey].addVertex(coords)
                            # Add the first point in the fan again (in order to close it).
                            vertex = blob['points'][0]
                            coords = Coord(vertex['coord'])
                            coords += (0.0, 0.0, blobZ)
                            cgoData[cgoKey].setColor(vertex['color'])
                            cgoData[cgoKey].addVertex(coords)
                            # Finish the fan.
                            cgoData[cgoKey].endTriangleFan()
                        elif blobStyle == 'contours':
                            cgoData[cgoKey].startLineLoop()
                            cgoData[cgoKey].setLineWidth(options[cgoKey + ' contours line width'])
                            alpha = blob.get('transparency', None)
                            if alpha is not None:
                                cgoData[cgoKey].addAlpha(alpha)
                            for vertex in blob['points']:
                                coords = Coord(vertex['coord'])
                                coords += (0.0, 0.0, blobZ)
                                cgoData[cgoKey].setColor(vertex['color'])
                                cgoData[cgoKey].addVertex(coords)
                            # Finish the loop.
                            cgoData[cgoKey].endLineLoop()
                        elif blobStyle == 'dots':
                            alpha = blob.get('transparency', None)
                            radius = options[cgoKey + ' dots radius']
                            if alpha is not None:
                                cgoData[cgoKey].addAlpha(alpha)
                            for vertex in blob['points']:
                                coords = Coord(vertex['coord'])
                                coords += (0.0, 0.0, blobZ)
                                cgoData[cgoKey].setColor(vertex['color'])
                                cgoData[cgoKey].addSphere(coords, radius)
                        else:
                            raise Exception, "Unrecognized %s blob style (%s)." % (type, blobStyle)

            # Do error lines, prediction lines, connection lines, etc.
            for lineType in ('error lines', 'prediction lines', 'connection lines'):
                lines = point.get(lineType)

                if lines and options[lineType + ' compute']:
                    if lineType not in cgoData:
                        cgoData[lineType] = independentPymolCgoGroup(options['cgo prefix'] + options[lineType + ' cgo name'])
                    for errorSpec in lines:
                        cgoData[lineType].setColor(errorSpec['color'])
                        cgoData[lineType].setLineWidth(options['error lines width'])
                        cgoData[lineType].addLine(pointCoords, errorSpec['coord'])

                        
        # All points have now been seen.
                        
        if options['surface compute']:
            gridData = None
            if options['surface method'] == 'precomputed':
                gridData = options['surface precomputed data']
            elif titerCoords:
                if options['surface method'] == 'gnuplot':
                    from Gnuplot import grid
                    # Use a bounding box to get the limits on the titer data.
                    bb = BoundingBox(options)
                    for point in titerCoords:
                        bb.add(point)
                    rows, cols = bb.computeGridRowsCols(options['surface gnuplot density'])
                    gridData = grid(options['surface raw data'], rows, cols, options['surface gnuplot norm'])
                    # print "coords: %s\nrows %s\ncols %s" % (str(titerCoords), rows, cols)
                elif options['surface method'] == 'r':
                    from R import rGrid
                    # Use R to generate a surface.
                    rows, cols = options['surface rows'], options['surface cols']
                    if options['surface vaccine'] and options['surface vaccine'] in self.map_data:
                        focus = self.map_data[options['surface vaccine']]['coords'][0:2]
                    else:
                        # We weren't give an vaccine, just use the coords from the
                        # first of the titer values and issue a warning.
                        print >>sys.stderr, "Surface vaccine not specified (or not found)! Using coords of first point with a titer for the R fitting focal position. Pass a 'surface vaccine' option to change this."
                        focus = titerCoords[0][0:2]
                    gridData = rGrid(titerCoords, options['surface r fit'], focus, rows, cols)
                else:
                    raise MdsError.NoSuchMethod, "Unknown surface method option: '%s'." % options['surface method']
        
            # print "rows, cols = %d, %d" % (rows, cols)
            # print "gridded data is: %s" % str(gridData)

            # The points come back from gnuplot with Y changing first (increasing)
            # and then X changing (decreasing).
            #
            # So if you think of the typical X,Y plane, the order of points from gnuplot
            # is start at the bottom right, go up to max y, then step left & return to
            # bottom, and continue until you reach x min, and move up to the top left point.
            #
            # Data that is given to us in options['surface gridded data'] is expected to
            # follow the same format.
            #
            # With that understanding, we know how to get triangles, quadrilaterals, etc.

            if gridData:
                # Add surface spheres.
                if options['surface spheres compute']:
                    radius = options['surface spheres radius']
                    cgoData['surface spheres'] = independentPymolCgoGroup(options['cgo prefix'] + options['surface spheres cgo name'])
                    cgoData['surface spheres'].setColor(options['surface spheres color'])
                    for point in gridData:
                        # print "grid point = %s" % str(point)
                        cgoData['surface spheres'].addSphere(point, radius)
                        if self.bb:
                            self.bb.add(point, radius)

                # Add the triangle mesh.
                if options['surface triangles compute']:
                    cgoData['surface triangles'] = independentPymolCgoGroup(options['cgo prefix'] + options['surface triangles cgo name'])
                    cgoData['surface triangles'].setColor(options['surface triangles color'])
                    for col in xrange(cols - 1):
                        cgoData['surface triangles'].startTriangleStrip()
                        # Add the bottom right and bottom left vertices of the initial triangle.
                        cgoData['surface triangles'].addVertex(gridData[col * rows])
                        cgoData['surface triangles'].addVertex(gridData[(col + 1) * rows])

                        for row in xrange(1, rows):
                            # Add the right and left vertices of the next row up.
                            cgoData['surface triangles'].addVertex(gridData[col * rows + row])
                            cgoData['surface triangles'].addVertex(gridData[(col + 1) * rows + row])

                        cgoData['surface triangles'].endStrip()

                # Add the quadrilateral mesh.
                if options['surface quads compute']:
                    cgoData['surface quads'] = independentPymolCgoGroup(options['cgo prefix'] + options['surface quads cgo name'])
                    cgoData['surface quads'].setColor(options['surface quads color'])
                    for col in xrange(cols):
                        cgoData['surface quads'].startLineStrip()
                        for row in xrange(rows):
                            cgoData['surface quads'].addVertex(gridData[col * rows + row])
                        cgoData['surface quads'].endStrip()
                    for row in xrange(rows):
                        cgoData['surface quads'].startLineStrip()
                        for col in xrange(cols):
                            cgoData['surface quads'].addVertex(gridData[col * rows + row])
                        cgoData['surface quads'].endStrip()

                # Add the titer plane (note that this only happens if we're computing a surface & there's surface data).
                if options['titer plane compute']:
                    cgoData['titer plane'] = TiterPlane(options['titer plane titer'],
                                                        options['cgo prefix'] + options['titer plane cgo name'],
                                                        options['titer plane color'],
                                                        options['titer plane line width'],
                                                        options['titer plane x min'],
                                                        options['titer plane x max'],
                                                        options['titer plane y min'],
                                                        options['titer plane y max'],
                                                        options['titer plane titer']).cgo()

                
        # Deal with bounding box related options.
        if self.bb:
            for what, func in (('bounding box', self.bb.computeBoundingBox),
                               ('grid', self.bb.computeGrid),
                               ('notches', self.bb.computeNotches),
                               ('dots', self.bb.computeDots)):
                if options[what + ' compute']:
                    cgoData[what] = func()

        # Load all the cgo sets (sorted by cgo name, so they appear alphabetically down the GUI rhs).
        def cgoCmp(a, b):
            return cmp(cgoData[a].name(), cgoData[b].name())
        
        cgoSets = cgoData.keys()
        cgoSets.sort(cgoCmp)
        for cgo in cgoSets:
            cgoData[cgo].load()

        # Set transparency.
        for cgo in cgoData:
            if cgo + ' transparency' in options:
                cgoData[cgo].setTransparency(options[cgo + ' transparency'])

        # Hide those that should be initially hidden.

        # TODO: make sure we really need to do this in two phases now that the type names
        #       have had the extra space removed from their cgoData keys.

        # 1) the types we found (e.g., strains, antisera), and their names.
        #
        # NOTE: we remove these cgo sets from cgoData at this point. We're done with them,
        # and it makes the subsequent CGO group hiding code a little simpler.
        for type in types:
            if options[type + ' hidden']:
                cgoData[type].hide()
            cgoData.pop(type)

        # 2) the other cgo sets created above.
        for cgo in cgoData:
            if options[cgo + ' compute'] and options[cgo + ' hidden']:
                cgoData[cgo].hide()

    # Unused
    def boundingBox(self):
        return self.bb
Ejemplo n.º 3
0
class Polygon:
    def __init__(self):
        self.vertices = []
        self.monotones = None
        self.triangles = None
        self.bbox = None

        # If this is False, this does not mean it is no convex, but if it is True, it is!
        self.convex = False

    def addVertex(self, p):
        self.vertices.append(p)
        self.updateBoundingBox()

        if len(self.vertices) == 3:
            self.convex = True
        else:
            self.convex = False

    #
    # Draw this polygon
    #
    def draw(self):
        self.__draw__(0)

    def __draw__(self, level):
        if len(self.vertices) < 3:
            raise ValueError("This ain't no polygon! A duogon, at best!")

        if len(self.vertices) == 3:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
            glColor3f(.1, .1, .1)
            glBegin(GL_TRIANGLES)
            glVertex2f(self.vertices[0].x, self.vertices[0].y)
            glVertex2f(self.vertices[1].x, self.vertices[1].y)
            glVertex2f(self.vertices[2].x, self.vertices[2].y)
            glEnd()

        if self.monotones and self.triangles:
            raise ValueError(
                "A polygon containing both monotones and triangles? Something went wrong"
            )

        if self.monotones:
            for m in self.monotones:
                m.__draw__(level + 1)

        if self.triangles:
            for t in self.triangles:
                t.__draw__(level + 1)

        if level == 0:
            glColor3f(.9, .9, .9)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
            glEnable(GL_POLYGON_OFFSET_LINE)
            glPolygonOffset(-1., -1.)
            self.__drawPolygon__()

    def __drawPolygon__(self):
        glBegin(GL_POLYGON)
        size = len(self.vertices)
        for vertex in range(size):
            glVertex2f(self.vertices[vertex].x, self.vertices[vertex].y)
            glVertex2f(self.vertices[(vertex + 1) % size].x,
                       self.vertices[(vertex + 1) % size].y)
        glEnd()

    # Line segment 1: p + t*r
    # Line segment 2: q + u*s
    @staticmethod
    def __LineSegmentIntersection__(p, r, q, s):
        rCrossS = r.cross(s)
        if rCrossS == 0:
            return -1, -1
        qMinusP = q.subtract(p)
        rhsT = qMinusP.cross(s)
        t = rhsT / rCrossS
        rhsR = qMinusP.cross(r)
        u = rhsR / rCrossS

        return t, u

    #
    # Check for collisions
    # \return True|False, CollisionVector
    #
    def collisionActor(self, actor):

        if not actor.bbox.overlaps(self.bbox):
            return False, Vector(0, 0)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        p = Vector(actor.location.x, actor.location.y - actor.height / 2)
        r = Vector(0, actor.height - 1)

        size = len(self.vertices)
        for vertex in range(size):
            # t = (q - p) x s /(r x s)
            # u = (q - p) x r /(r x s)
            q = self.vertices[vertex]
            qPlusS = self.vertices[(vertex + 1) % size]
            s = qPlusS.subtract(q)

            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)

            if t >= 0 and t < 1 and u >= 0 and u < 1:
                # return True, p.add(r.multiply(t)).add(r.multiply(0.5))
                return True, r.multiply(t)  #.add(Vector(0, -.001))
        return False, Vector(0, 0)

    #
    # Check if this polygon collides with another polygon
    # \return True|False, CollisionVector
    #
    def collision(self, other):
        collides, axis, dist = self.__privateCollision__(other)

        if collides:
            return True, axis.multiply(dist)

        return False, None

    def __privateCollision__(self, other):
        if not self.bbox.overlaps(other.bbox):
            return False, None, None

        if self.convex:
            minAxis = None
            minDist = sys.float_info.max
            collides, minAxis, minDist = self.__privateCollisionConvex__(
                other, minAxis, minDist)
            if not collides:
                return False, minAxis, minDist

            return other.__privateCollisionConvex__(self, minAxis, minDist)

        if self.monotones:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for m in self.monotones:
                newCollides, newAxis, newDist = m.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis

            return collides, axis, dist

        if self.triangles:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for t in self.triangles:
                newCollides, newAxis, newDist = t.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis

            return collides, axis, dist

    # Internal collision method
    def __privateCollisionConvex__(self, other, minAxis, minDist):
        size = len(self.vertices)
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i + 1) % size]

            perp = Polygon.__perpendicularVector__(self, v1, v2)

            minSelf = maxSelf = minOther = maxOther = None

            minSelf, maxSelf = self.__projectOnAxis__(perp)
            minOther, maxOther = other.__projectOnAxis__(perp)

            dist = Polygon.__getIntervalDistance__(minSelf, maxSelf, minOther,
                                                   maxOther)

            if dist > 0.:
                return False, minAxis, minDist
            elif abs(dist) < minDist:
                minDist = abs(dist)
                minAxis = perp
        return True, minAxis, minDist

    #
    # Project all vertices of this polygon onto an axis
    # Get the min and max values
    #
    def __projectOnAxis__(self, axis):
        min = max = axis.dot(self.vertices[0])

        for i in range(1, len(self.vertices)):
            product = axis.dot(self.vertices[i])

            if product < min:
                min = product

            if product > max:
                max = product

        return min, max

    #
    # Get the distance beween two 1-dimensional intervals
    #
    @staticmethod
    def __getIntervalDistance__(min1, max1, min2, max2):
        if min1 < min2:
            return min2 - max1
        else:
            return min1 - max2

    #
    # Get the perpendicular vector on a line segment defined by 2 poins
    #
    @staticmethod
    def __perpendicularVector__(self, v1, v2):
        dy = (v1.y - v2.y)
        dx = (v1.x - v2.x)
        d = sqrt(dy * dy + dx * dx)
        if d == 0:
            return Vector(1, 0)
        return Vector(-dy / d, dx / d)

    def __selfIntersects__(self):
        size = len(self.vertices)
        for vertex1 in range(size):
            p = self.vertices[vertex1]
            pPlusR = self.vertices[(vertex1 + 1) % size]
            r = pPlusR.subtract(p)
            for vertex2 in range(vertex1, size):
                if vertex1 == vertex2:
                    continue
                q = self.vertices[vertex2]
                qPlusS = self.vertices[(vertex2 + 1) % size]
                s = qPlusS.subtract(q)

                t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)

                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    return True
        return False

    # TODO
    def cut(self, other):
        sizeSelf = len(self.vertices)
        sizeOther = len(other.vertices)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        for vertexSelf in range(sizeSelf):
            p = self.vertices[vertexSelf]
            pPlusR = self.vertices[(vertexSelf + 1) % sizeSelf]
            r = pPlusR.subtract(p)
            for vertexOther in range(sizeOther):
                # t = (q - p) x s /(r x s)
                # u = (q - p) x r /(r x s)
                q = other.vertices[vertexOther]
                qPlusS = other.vertices[(vertexOther + 1) % sizeOther]

                s = qPlusS.subtract(q)
                rCrossS = r.cross(s)
                qMinusP = q.subtract(p)
                rhsT = qMinusP.cross(s)
                t = rhsT / rCrossS
                rhsR = qMinusP.cross(r)
                u = rhsR / rCrossS

                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    intersect = p.add(r.multiply(t))
                    glColor3f(0.8, 0.8, 0.2)
                    glPointSize(6)
                    intersect.draw()

    #
    # Check if the given point is inside the polygon
    #
    def inside(self, p):
        size = len(self.vertices)
        count = 0
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i + 1) % size]

            if (v1.y > p.y and v2.y <= p.y) or \
                (v1.y <= p.y and v2.y > p.y):

                div = (v1.x - v2.x)
                if div == 0:
                    if p.x - v1.x > 0:
                        count += 1
                m = (v1.y - v2.y) / div
                if p.x - (v1.x + (p.y - v1.y) / m) > 0:
                    count += 1
        return count % 2 == 1

    @staticmethod
    def __interiorAngleGreaterThanPi__(v1, v2, v3):
        cross = v1.subtract(v2).cross(v3.subtract(v2))
        if cross < 0:
            return True
        return False

    # Doubly Connected edge list: a data structure which can be used in monotone decomposition and triangulation of a polygon
    class DCELHalfEdge:
        def __init__(self, origin):
            self.origin = origin
            self.twin = None
            self.next = None
            self.prev = None
            self.face = None
            self.helper = None

        def start(self):
            return self.origin.v

        def end(self):
            return self.next.origin.v

        def xDistTo(self, v):
            # y - y1 = m(x - x1)
            # x = x1 + (y - y1)/m
            div = (self.start().x - self.end().x)
            if div == 0:
                return v.x - self.start().x
            m = (self.start().y - self.end().y) / div

            return v.x - (self.start().x + (v.y - self.start().y) / m)

        def mark(self):
            glLineWidth(10)
            glBegin(GL_LINES)
            glVertex2f(self.start().x, self.start().y)
            glVertex2f(self.end().x, self.end().y)
            glEnd()
            glLineWidth(1)

    class DCELVertex:
        def __init__(self, v):
            self.v = v
            self.leavingEdge = None

    class DCELFace:
        def __init__(self, edge):
            self.edge = edge
            self.visited = False

    class DCELNextEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge

        def next(self):
            self.current = self.current.next
            if self.current == self.start:
                return None
            return self.current

    class DCELLeavingEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge

        def next(self):
            self.current = self.current.prev.twin
            if self.current == self.start:
                return None
            return self.current

    # Search tree, implemented initially as a flat array (Laziness)
    class Tree:
        def __init__(self):
            self.edges = []
            # self.count = 0

        def add(self, edge):
            self.edges.append(edge)

        def remove(self, edge):
            self.edges.remove(edge)

        def edgeLeftOf(self, v):
            d = sys.float_info.max
            e = None
            for edge in self.edges:
                if (edge.start().y >= v.y and edge.end().y < v.y) or \
                    (edge.start().y < v.y and edge.end().y >= v.y):
                    distToEdge = edge.xDistTo(v)
                    if distToEdge > 0 and d > distToEdge:
                        d = distToEdge
                        e = edge
            return e

        def draw(self):
            print "hmpf"
            for edge in self.edges:
                glLineWidth(6)
                glBegin(GL_LINES)
                glVertex2f(edge.start().x, edge.start().y)
                glVertex2f(edge.end().x, edge.end().y)
                glEnd()
                glLineWidth(1)

    # Is it possible to add a diagonal between two vertices
    def __canAddDiagonal__(self, vi1, vi2):
        size = len(self.vertices)
        p = self.vertices[vi1]
        pPlusR = self.vertices[vi2]
        r = pPlusR.subtract(p)
        for v in range(size):
            # if (vi1==v and vi2==(v+1)%size) or (vi2==v and vi1==(v+1)%size):
            # return False
            q = self.vertices[v]
            qPlusS = self.vertices[(v + 1) % size]
            s = qPlusS.subtract(q)

            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)

            if t > 0 and t < 1 and u > 0 and u < 1:
                return False

        midDiagonal = Vector((p.x + pPlusR.x) / 2, (p.y + pPlusR.y) / 2)
        return self.inside(midDiagonal)

    # Add a diagonal to the DCEL of this polygon
    def __addDiagonal__(self, vi1, vi2, dcelVertices):
        v1 = dcelVertices[vi1]
        v2 = dcelVertices[vi2]

        # Find the common face for v1 and v2
        face = None
        e1 = v1.leavingEdge
        it1 = Polygon.DCELLeavingEdgeIterator(e1)
        while e1:
            face1 = e1.face
            e2 = v2.leavingEdge
            it2 = Polygon.DCELLeavingEdgeIterator(e2)
            while e2:
                if face1 == e2.face and face1 != None:
                    face = face1
                    break
                e2 = it2.next()
            e1 = it1.next()

        # Find the next and previous edges for both diagonals
        prevEdge1 = None
        nextEdge1 = None
        prevEdge2 = None
        nextEdge2 = None

        e = face.edge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            if e.origin == v1:
                prevEdge1 = e.prev
                nextEdge1 = e
            if e.origin == v2:
                prevEdge2 = e.prev
                nextEdge2 = e
            e = it.next()

        # Connect 2 new half edges
        diagonal1 = Polygon.DCELHalfEdge(v1)
        diagonal1.prev = prevEdge1
        prevEdge1.next = diagonal1
        diagonal1.next = nextEdge2
        nextEdge2.prev = diagonal1

        diagonal2 = Polygon.DCELHalfEdge(v2)
        diagonal2.prev = prevEdge2
        prevEdge2.next = diagonal2
        diagonal2.next = nextEdge1
        nextEdge1.prev = diagonal2

        diagonal1.twin = diagonal2
        diagonal2.twin = diagonal1

        # Connect new faces
        face1 = Polygon.DCELFace(diagonal1)
        face2 = Polygon.DCELFace(diagonal2)
        e1 = diagonal1
        it1 = Polygon.DCELNextEdgeIterator(e1)
        while e1:
            e1.face = face1
            e1 = it1.next()

        e2 = diagonal2
        it2 = Polygon.DCELNextEdgeIterator(e2)
        while e2:
            e2.face = face2
            e2 = it2.next()

    # Construct the Doubly connected edge list
    # \param dcelVertices A list of DCELVertex objects, must be empty
    def __constructDCEL__(self, dcelVertices):
        size = len(self.vertices)
        currentVertex = None
        # Create all DCEL Vertices with a leaving edge
        for i in range(size):
            # Create the current vertex or use the previously stored last one, if this is the last vertex
            v = self.vertices[i]
            currentVertex = Polygon.DCELVertex(v)
            # Create the DCEL Half Edge
            currentEdge = Polygon.DCELHalfEdge(currentVertex)
            # Connect
            currentVertex.leavingEdge = currentEdge
            dcelVertices.append(currentVertex)

        # Create all twin half edges
        for i in range(size):
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i + 1) % size]
            currentEdge = currentVertex.leavingEdge
            twinEdge = Polygon.DCELHalfEdge(nextVertex)
            currentEdge.twin = twinEdge
            twinEdge.twin = currentEdge

        # Connect all edges
        for i in range(size):
            prevVertex = dcelVertices[i - 1]
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i + 1) % size]

            currentEdge = currentVertex.leavingEdge
            currentEdge.prev = prevVertex.leavingEdge
            currentEdge.next = nextVertex.leavingEdge

            twinEdge = currentEdge.twin
            twinEdge.prev = nextVertex.leavingEdge.twin
            twinEdge.next = prevVertex.leavingEdge.twin

        # Connect the single face
        startVertex = dcelVertices[0]
        startEdge = startVertex.leavingEdge
        face = Polygon.DCELFace(startEdge)

        e = startEdge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            e.face = face
            e = it.next()

    # Sort the vertices of this polygon according to y coordinate
    def __ySortVertices__(self, sortedVertices):
        size = len(self.vertices)
        for vertex in range(size):
            v = self.vertices[vertex]
            sizeSorted = len(sortedVertices)
            found = False
            for index in range(sizeSorted):
                if v.above(self.vertices[sortedVertices[index]]):
                    sortedVertices.insert(index, vertex)
                    found = True
                    break
            if not found:
                sortedVertices.append(vertex)

    @staticmethod
    def __constructPolygonsFromDCEL__(dcelVertices, polygons):
        # Iterator all leaving edges on all vertices to find all faces
        for vertex in dcelVertices:
            e1 = vertex.leavingEdge
            it1 = Polygon.DCELLeavingEdgeIterator(e1)
            polygon = Polygon()
            while e1:
                if e1.face and not e1.face.visited:
                    e1.face.visited = True
                    polygon = Polygon()
                    e2 = e1.face.edge
                    it2 = Polygon.DCELNextEdgeIterator(e2)
                    while e2:
                        polygon.addVertex(e2.origin.v)
                        e2 = it2.next()
                    polygons.append(polygon)
                e1 = it1.next()

    def triangulate(self):
        size = len(self.vertices)
        if size <= 3:
            return
        # Monotone decomposition
        # Computation Geometry Algorithms and Applications, page 50
        startVertex = 1
        endVertex = 2
        regularVertex = 3
        mergeVertex = 4
        splitVertex = 5

        vertexType = []
        # Determine the type for each vertex
        for vertex in range(size):
            v = self.vertices[vertex]
            vPrev = self.vertices[vertex - 1]
            vNext = self.vertices[(vertex + 1) % size]
            if v.above(vPrev) and v.above(vNext):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(splitVertex)
                else:
                    vertexType.append(startVertex)
            elif vPrev.above(v) and vNext.above(v):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(mergeVertex)
                else:
                    vertexType.append(endVertex)
            else:
                vertexType.append(regularVertex)

        # glColor3f(1., 1., 1.)
        # glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)
        # if False:
        # self.__privateDraw__()
        # for vertex in range(size):
        # v = self.vertices[vertex]
        # if vertexType[vertex] == startVertex:
        # glPointSize(12)
        # elif vertexType[vertex] == endVertex:
        # glPointSize(3)
        # elif vertexType[vertex] == splitVertex:
        # glPointSize(6)
        # elif vertexType[vertex] == mergeVertex:
        # glPointSize(9)
        # else:
        # glPointSize(1)

        # glColor3f(1., 2., 2.)
        # glBegin(GL_POINTS)
        # glVertex2f(v.x, v.y)
        # glEnd()

        # Construct doubly-connected edge list
        dcelVertices = []
        self.__constructDCEL__(dcelVertices)

        #
        # TEST CODE : Check DCEL
        #
        # polys = []
        # Polygon.__constructPolygonsFromDCEL__(dcelVertices, polys)

        # c = int(time.clock()*2)%(len(polys)+1)
        # if c == len(polys):
        # glColor3f(1., 1., .2)
        # self.__privateDraw__()
        # return
        # glColor3f(.2, .2, 1.)
        # polys[c].__privateDraw__()

        #
        # TEST CODE : Check prev, next, twin edges
        #
        # c = int(time.clock()*2)%(len(dcelVertices))
        # v = dcelVertices[c]
        # glPointSize(30)
        # glBegin(GL_POINTS)
        # glVertex2f(v.v.x, v.v.y)
        # glEnd()
        # glPointSize(1)
        # v.leavingEdge.twin.mark()
        # glColor3f(1., 1., .2)
        # v.leavingEdge.twin.next.mark()
        # return

        #
        # TEST CODE : Check edgeLeftOf
        #
        # tree = Polygon.Tree()
        # e1 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, 1)))
        # e2 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, -1)))
        # e1.next = e2
        # e2.prev = e1
        # tree.add(e1)
        # tree.edgeLeftOf(Vector(0, 0)).mark()
        # return

        # Sort vertices top to bottom
        # Insertion sort
        sortedVertices = []
        self.__ySortVertices__(sortedVertices)

        # Initialize the partition with the edges of the (possibly) not-monotone polygon
        tree = Polygon.Tree()

        # Handle each vertex, from top to bottom the polygon
        # Handling each vertex depends on the type of the vertex
        for vertex in sortedVertices:
            v = self.vertices[vertex]
            if vertexType[vertex] == startVertex:
                ei = dcelVertices[vertex].leavingEdge
                ei.helper = vertex
                # Add e_i to searchtree
                tree.add(ei)
            elif vertexType[vertex] == endVertex or vertexType[
                    vertex] == mergeVertex:
                # Perform actions which are the same of end or merge vertices
                eiMinus1 = dcelVertices[vertex - 1].leavingEdge
                # Add a diagonal if helper(e_i+1) is a merge vertex
                if vertexType[eiMinus1.helper] == mergeVertex:
                    self.__addDiagonal__(vertex, eiMinus1.helper, dcelVertices)
                # Delete e_i from searchtree
                tree.remove(eiMinus1)
                # Perform actions which are unique to a merge vertex
                if vertexType[vertex] == mergeVertex:
                    # Find edge directly to the right of vertex
                    ej = tree.edgeLeftOf(v)
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex
            elif vertexType[vertex] == splitVertex:
                # Find edge directly to the left of vertex
                ej = tree.edgeLeftOf(v)
                self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                # Set vertex as new helper
                ej.helper = vertex
                ei = dcelVertices[vertex].leavingEdge
                # Add e_i to tree and set vertex as helper
                ei.helper = vertex
                tree.add(ei)
            elif vertexType[vertex] == regularVertex:
                #If the interior of the polygon lies right of this vertex
                if self.vertices[(vertex+1)%size].y <= v.y and \
                   self.vertices[vertex-1].y > v.y:
                    eiMinus1 = dcelVertices[vertex - 1].leavingEdge
                    helper = eiMinus1.helper
                    # Add a diagonal if helper(e_i-1) is a merge vertex
                    if vertexType[helper] == mergeVertex:
                        self.__addDiagonal__(vertex, helper, dcelVertices)
                    # Delete e_i-1 from searchtree
                    tree.remove(eiMinus1)
                    # Add e_i to tree and set vertex as helper
                    ei = dcelVertices[vertex].leavingEdge
                    ei.helper = vertex
                    tree.add(ei)
                else:
                    # Find edge directly to the left of vertex
                    ej = tree.edgeLeftOf(v)
                    if not ej:
                        glPointSize(5)
                        v.draw()
                        glPointSize(1)
                        return
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex

        # Construct the monotones
        self.monotones = []
        Polygon.__constructPolygonsFromDCEL__(dcelVertices, self.monotones)

        # print "Monotones: " + str(len(self.monotones))
        # if self.debug == 2:
        # c = int(time.clock()*2)%(len(self.monotones)+1)
        # if c == len(self.monotones):
        # glColor3f(1., 1., .2)
        # self.__privateDraw__()
        # return
        # glColor3f(.2, .2, 1.)
        # self.monotones[c].__privateDraw__()
        # for polygon in self.monotones:
        # polygon.__privateDraw__()
        # return

        # for m in self.monotones:
        # glColor3f(1., 1., 1.)
        # m.__privateDraw__()

        for m in self.monotones:

            if len(m.vertices) == 3:
                # if self.debug == 1 or self.debug == 3:
                # m.draw(1., .6, .0)
                continue

            monoDcelVertices = []
            m.__constructDCEL__(monoDcelVertices)

            monotoneSortedVertices = []
            m.__ySortVertices__(monotoneSortedVertices)

            stack = []
            stack.append(monotoneSortedVertices[0])
            stack.append(monotoneSortedVertices[1])

            for j in range(2, len(monotoneSortedVertices) - 1):

                if m.__onSameEdge__(stack[-1], monotoneSortedVertices[j]):

                    poppedLast = stack.pop()
                    vStack = stack[-1]

                    while m.__canAddDiagonal__(vStack,
                                               monotoneSortedVertices[j]):
                        m.__addDiagonal__(vStack, monotoneSortedVertices[j],
                                          monoDcelVertices)
                        poppedLast = stack.pop()
                        if len(stack) == 0:
                            break
                        vStack = stack[-1]

                    stack.append(poppedLast)
                    stack.append(monotoneSortedVertices[j])
                else:
                    while len(stack) > 0:
                        vi = stack.pop()
                        if len(stack) > 0:
                            m.__addDiagonal__(vi, monotoneSortedVertices[j],
                                              monoDcelVertices)
                    stack.append(monotoneSortedVertices[j - 1])
                    stack.append(monotoneSortedVertices[j])

            stack.pop()
            while len(stack) > 1:
                m.__addDiagonal__(stack[-1], monotoneSortedVertices[-1],
                                  monoDcelVertices)
                stack.pop()

            m.triangles = []
            Polygon.__constructPolygonsFromDCEL__(monoDcelVertices,
                                                  m.triangles)

            # if self.debug == 1:
            # m.triangles[int(time.clock()*2)%len(m.triangles)].draw(1., .2, .2)
            # if self.debug == 3:
            # for t in m.triangles:
            # t.draw(1., .2, .2)

    # check if two vertices are on the same edge, given their indices
    def __onSameEdge__(self, i1, i2):
        size = len(self.vertices)
        return abs(i1 - i2) == 1 or abs(i1 - i2) == size - 1

    # Apply midpoint displacement to this polygon
    # The midpoint of each line segment will be displaced randomly
    # perpendicular to the line segment, distance between -factor and +factor
    def midpointDisplacement(self, factor):
        seed = random.randint(0, 10000)
        # if factor == 8:
        # seed = 8977
        # seed = 9539
        # seed = 1909
        # seed = 5021
        # seed = 6131 # Triangulation issue
        # seed = 6198 # Triangulation issue
        # seed = 3403 # Triangulation issue
        print factor
        print seed
        random.seed(seed)
        size = len(self.vertices)
        oldVertices = list(self.vertices)
        vertices = []
        for vertex in range(size):
            p1 = self.vertices[vertex]
            p2 = self.vertices[(vertex + 1) % size]
            vertices.append(p1)
            p = p1.subtract(p2).perpendicularVector()
            r = factor - random.random() * factor * 2
            x = (p1.x + p2.x) / 2 + r * p.x
            y = (p1.y + p2.y) / 2 + r * p.y
            vertices.append(Vector(x, y))
        self.vertices = vertices
        if self.__selfIntersects__():
            print "uh oh"
            self.vertices = oldVertices
            self.midpointDisplacement(factor / 2)
        self.updateBoundingBox()

    def updateBoundingBox(self):
        size = len(self.vertices)
        self.bbox = BoundingBox(self.vertices[0].x, self.vertices[0].y,
                                self.vertices[0].x, self.vertices[0].y)
        for vertex in range(1, size):
            self.bbox.add(self.vertices[vertex].x, self.vertices[vertex].y)

    @staticmethod
    def createBoundingBoxPolygon(pMin, pMax):
        p = Polygon()
        p.addVertex(Vector(pMin.x, pMax.y))
        p.addVertex(Vector(pMin.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMax.y))
        return p