# Ryan Martin
# macid: abcd123
# student number: #######
# February 23, 2012
#
#
# This is a solution for Major Assignment 5 (Lab 11) on Thursday.

class ConvexPolygon:

    def __init__(self, vertices):
        self.vertices = vertices

    def getVertices(self):
        #The [:] notation makes a copy, so we avoid aliasing issues
        return self.vertices[:]

    def getN(self):
        return len(self.vertices)

    def setVertex(self, i, x, y):
        self.vertices[i][0] = float(x)
        self.vertices[i][1] = float(y)

    def isPointInside(self, x, y):
        v = self.getVertices()

        #Start by checking the line connecting the last and first points 
        first_alpha = (y - v[-1][1])*(v[0][0] - v[-1][0]) - (x - v[-1][0])*(v[0][1] - v[-1][1])

        #Zero means that the point is on an edge
        if first_alpha == 0:
            return False

        #Continue around the polygon, clockwise
        for i in range(len(v)-1):
            nxt_alpha = (y - v[i][1])*(v[i+1][0] - v[i][0]) - (x - v[i][0])*(v[i+1][1] - v[i][1])

            #Point is outside if the signs don't match for all points
            if nxt_alpha == 0 or nxt_alpha/abs(nxt_alpha) != first_alpha/abs(first_alpha):
                return False 

        #All points match
        return True

def polygonCollision(a, b):
    v = a.getVertices()
    for i in range(a.getN()):
        if b.isPointInside(v[i][0],v[i][1]):
            return True
    
    v = b.getVertices()
    for i in range(b.getN()):
        if a.isPointInside(v[i][0],v[i][1]):
            return True

    return False

'''
Design Question

There are many ways to implement isPointInside. 
This solution uses the following method:

1. Check the first value of alpha at i = -1.
   If alpha == 0, return False immediately.
2. Iterate through the remaining points:
    ->if any alpha == 0, return False immediately.
    ->if any sign(alpha) != sign(first_alpha),
      return False immediately.
3. If the loop exits successfully, return True.

Where sign(alpha) = alpha/abs(alpha)

'''
##Note: This format is used by our automated marking system.
##You will see that, for inputs, the function name is
##given with the corresponding inputs to that function
##listed right after it. AutoMarker uses the data that you see here
##Format: [funName, [arg1, arg2, ...]]
##OR: 
##[ConvexPolygon, [initArg1, initArg2, ...], classFunName, [arg1, arg2, ...]]
##
##If you need to pass an instance of a class, use a tuple:
##('clsName', [arg1, arg2])
##and the class will be created at runtime.
##
##
##If the expected output for a function is an instance of a testingClass,
##each get method of testingClass and the actual output class will be compared.

'''
Testing Plan


class Class_State_Change: #equivalent to a basic ConvexPolygon class
	def __init__(self, v):
		self.v = v
	
	def getVertices(self):
		return self.v
       
v = [[0.75,3.5],[2.5,2.75],[2.25,-0.5],[-0.75,-1.5],[-0.25,2.25]]
v2 = [[]]

V02 = {'test1': 
       {'inputs': ['ConvexPolygon', [v], 'getN', []],
       'outputs': [5]},
        'test2': 
       {'inputs': ['ConvexPolygon', [v], 'setVertex', [1,2.0,3.0]],
       'outputs': [Class_State_Change([[0.75,3.5],[2.0,3.0],[2.25,-0.5],[-0.75,-1.5],[-0.25,2.25]])]},
        'test3': 
       {'inputs': ['ConvexPolygon', [v2], 'setVertex', [0,1.0,3.0]],
       'outputs': [IndexError: list assignment index out of range]},
       'test4':
       {'inputs': ['ConvexPolygon', [v], 'isPointInside', [0.25,-0.5]],
       'outputs': [True]},
       'test5':
       {'inputs': ['ConvexPolygon', [v], 'isPointInside', [-0.75,-0.25]],
       'outputs': [False]},
       'test6':
       {'inputs': ['polygonCollision', [('ConvexPolygon', [v]), ('ConvexPolygon', [[[0.5,4.0],[2.5,3.5],[0.75,2.5]]])]],
       'outputs': [True]},
       'test7':
       {'inputs': ['polygonCollision', [('ConvexPolygon', [v]), ('ConvexPolygon', [[[-0.5,-4.0],[-2.5,-3.5],[-0.75,-2.5]]])]],
       'outputs': [False]}}

'''

