def hasCollided(poly1, poly2, maxDist=None):
    """
    Checks for a collision between two convex 2D polygons using separating axis theorem (SAT)
    :param poly1, poly2: The two polygons described as lists of points as tuples
        Example: [(x1, y1), (x2, y2), (x3, y3)]
        Note: The points list must go in sequence around the polygon
    :param maxDist: The maximum distance between any two points of any two polygons that can be touching
        If this is left off the optimization check that uses it will be skipped
    :return: The boolean result
    """
    def edgeVector(point1, point2):
        """
        :return: A vector going from point1 to point2
        """
        return point2[0] - point1[0], point2[1] - point1[1]

    def polyToEdges(poly):
        """
        Runs edgeVector() on each point paired with the point after it in the poly
        :return: A list of the edges of the poly as vectors
        """
        return [edgeVector(poly[i], poly[(i + 1) % len(poly)]) for i in range(len(poly))]

    def orthogonal(vector):
        """
        :return: A new vector which is orthogonal to the given vector
        """
        return vector[1], - vector[0]

    def dotProduct(vector1, vector2):
        """
        :return: The dot (or scalar) product of the two vectors
        """
        return vector1[0] * vector2[0] + vector1[1] * vector2[1]

    def project(poly, axis):
        """
        :return: A vector showing how much of the poly lies along the axis
        """
        dots = [dotProduct(point, axis) for point in poly]
        return min(dots), max(dots)

    def overlap(projection1, projection2):
        """
        :return: Boolean indicating if the two projections overlap
        """
        return min(projection1) <= max(projection2) and min(projection2) <= max(projection1)

    def runSAT(poly1, poly2):
        """
        :return: The boolean result of running separating axis theorem on the two polys
        """
        edges = polyToEdges(poly1) + polyToEdges(poly2)
        axes = [orthogonal(edge) for edge in edges]

        for axis in axes:
            overlapping = overlap(project(poly1, axis), project(poly2, axis))

            if not overlapping:
                # The polys don't overlap on this axis so they can't be touching
                return False

        # The polys overlap on all axes so they must be touching
        return True

    # Do an optimization check using the maxDist
    if maxDist is not None:
        if (poly1[1][0] - poly2[0][0]) ** 2 + (poly1[1][1] - poly2[0][1]) ** 2 <= maxDist ** 2:
            # Collision is possible so run SAT on the polys
            return runSAT(poly1, poly2)
        else:
            return False
    else:
        # No maxDist so run SAT on the polys
        return runSAT(poly1, poly2)