def test_lower_tangent_case3(self):
        point_list_a = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        polygon_a = Polygon(point_list_a)
        point_list_b = [(4, 1), (3, 3), (5, 2), (4, 4)]
        polygon_b = Polygon(point_list_b)

        # After polygons are created
        mock_tangent_polygon_calculator = TangentPolygonCalculator()
        mock_tangent_polygon_calculator['a'] = polygon_a
        mock_tangent_polygon_calculator['b'] = polygon_b

        expected = [Point(0, -1), Point(4, 1)]
        self.assertCountEqual(
            expected, mock_tangent_polygon_calculator.lower_tangent('a', 'b'))
    def test_upper_tangent_case2(self):
        point_list_a = [(3, 1), (4, 2), (3, 3), (2, 2)]
        polygon_a = Polygon(point_list_a)
        point_list_b = [(0, 1), (0, 2), (-1, 1), (-1, 2)]
        polygon_b = Polygon(point_list_b)

        # After polygons are created
        mock_tangent_polygon_calculator = TangentPolygonCalculator()
        mock_tangent_polygon_calculator['a'] = polygon_a
        mock_tangent_polygon_calculator['b'] = polygon_b

        expected = [Point(-1, 2), Point(3, 3)]
        self.assertCountEqual(
            expected, mock_tangent_polygon_calculator.upper_tangent('a', 'b'))
    def convex_hull(self):
        # Find midpoint
        self.set_midpoint()
        left = [
            point for point in self.point_list if point.x <= self.midpoint.x
        ]
        right = [
            point for point in self.point_list if point.x > self.midpoint.x
        ]

        # Divide and conquer
        # Left and right convex hull can be recursively found
        # Here Jarvis Algorithm is directly applied
        left_convex_hull = ConvexHullJarvis(left).convex_hull()
        right_convex_hull = ConvexHullJarvis(right).convex_hull()

        # Find tangents by using tangent polygon calculator
        left_polygon = Polygon(left_convex_hull)
        right_polygon = Polygon(right_convex_hull)
        tangent_finder = TangentPolygonCalculator()
        tangent_finder[1] = left_polygon
        tangent_finder[2] = right_polygon
        left_upper_idx, right_upper_idx = tangent_finder.upper_tangent(
            1, 2, idx=True)
        left_lower_idx, right_lower_idx = tangent_finder.lower_tangent(
            1, 2, idx=True)

        # Need to do a collinear check else the convex hull will have extra collinear points
        left_upper_idx, right_upper_idx, left_lower_idx, right_lower_idx = self.collinear_check(
            left_polygon, right_polygon, left_upper_idx, right_upper_idx,
            left_lower_idx, right_lower_idx)

        # Merge
        res = []
        left_idx = left_upper_idx
        # Turn counterclockwise to include the left outer convex hull points until the lower index is reached
        while left_idx != left_lower_idx:
            res.append(left_polygon.point_list[left_idx])
            left_idx = (left_idx + 1) % len(left_polygon)
        res.append(left_polygon.point_list[left_idx])

        right_idx = right_upper_idx
        # Turn clockwise to include the right outer convex hull points until the lower index is reached
        while right_idx != right_lower_idx:
            res.append(right_polygon.point_list[right_idx])
            right_idx = (len(right_polygon) + right_idx -
                         1) % len(right_polygon)
        res.append(right_polygon.point_list[right_idx])

        # Use polygon to sort
        sort_poly = Polygon(res)
        sort_poly.sort()
        return sort_poly.point_list