def test_get_distance_ss_normal(self):
     s_0: Segment = Segment(Point(0, 0), Point(2, 2))
     s_1: Segment = Segment(Point(-3, -3), Point(-1, -1))
     s_2: Segment = Segment(Point(0, -3), Point(1, -2))
     s_3: Segment = Segment(Point(0, 2), Point(2, 0))
     self.assertEqual(equals(get_distance_ss(s_0, s_1), math.sqrt(2)), True)
     self.assertEqual(equals(get_distance_ss(s_0, s_2), math.sqrt(5)), True)
     self.assertEqual(equals(get_distance_ss(s_0, s_3), 0), True)
 def test_get_cross_point_normal(self):
     s_1: Segment = Segment(Point(0, 0), Point(1, 1))
     s_2: Segment = Segment(Point(0, 1), Point(1, 0))
     s_3: Segment = Segment(Point(1, 0), Point(0, 1))
     s_4: Segment = Segment(Point(0, 1), Point(1, 2))
     self.assertEqual(equals(get_cross_point(s_1, s_2).x, 0.5), True)
     self.assertEqual(equals(get_cross_point(s_1, s_2).y, 0.5), True)
     self.assertEqual(equals(get_cross_point(s_1, s_3).x, 0.5), True)
     self.assertEqual(equals(get_cross_point(s_1, s_3).y, 0.5), True)
     self.assertIsNone(get_cross_point(s_1, s_4))
 def test_get_distance_sp_normal(self):
     p_l: Point = Point(-1, -1)
     p_r: Point = Point(1, 1)
     s: Segment = Segment(p_l, p_r)
     p_1: Point = Point(-2, -2)
     p_2: Point = Point(0, 1)
     p_3: Point = Point(2, 2)
     self.assertEqual(equals(get_distance_sp(s, p_1), (p_1 - p_l).abs()),
                      True)
     self.assertEqual(equals(get_distance_sp(s, p_2), 1.0 / math.sqrt(2)),
                      True)
     self.assertEqual(equals(get_distance_sp(s, p_3), (p_3 - p_r).abs()),
                      True)
def is_parallel(*args) -> Optional[bool]:
    """
    Parallel Judgement
    :param args: Two Vectors or Four Points or Two Segments
    :return: Optional[bool]
    """
    len_args: int = len(args)
    if len_args == 2:
        if isinstance(args[0], Vector):
            v_1: Vector = args[0]
            v_2: Vector = args[1]
        elif isinstance(args[0], Segment):
            s_1: Segment = args[0]
            s_2: Segment = args[1]
            v_1 = Vector((s_1.p1 - s_1.p2).x, (s_1.p1 - s_1.p2).y)
            v_2 = Vector((s_2.p1 - s_2.p2).x, (s_2.p1 - s_2.p2).y)
        else:
            raise TypeError("Error: Argument Type Error")
    elif len_args == 4:
        if isinstance(args[0], Point):
            p_1: Point = args[0]
            p_2: Point = args[1]
            p_3: Point = args[2]
            p_4: Point = args[3]
            v_1 = Vector((p_1 - p_2).x, (p_1 - p_2).y)
            v_2 = Vector((p_3 - p_4).x, (p_3 - p_4).y)
        else:
            raise TypeError("Error: Argument Type Error")
    else:
        raise TypeError("Error: Argument Error")
    return equals(v_1.cross(v_2), 0.0)
def point_contained_in_polygon(poly: Polygon,
                               p: Point) -> PointContainsInPolygon:
    """
    Calculate Is Point Contained in Polygon
    :param poly: Polygon
    :param p: Point
    :return: PointContainsInPolygon
    """
    n: int = len(poly.p_i)  # Number of Nodes of Polygon
    count_intersect: int = 0
    for i in range(n):
        a: Point = poly.p_i[i] - p
        b: Point = poly.p_i[(i + 1) % n] - p
        v_1: Vector = Vector(a.x, a.y)
        v_2: Vector = Vector(b.x, b.y)
        if v_1.dot(v_2) < 0 and equals(v_1.cross(v_2), 0):
            return PointContainsInPolygon.ON_EDGE
        if a.y > b.y:
            a, b = b, a
            v_1 = Vector(a.x, a.y)
            v_2 = Vector(b.x, b.y)
        if a.y < EPS < b.y and v_1.cross(v_2) > EPS:
            count_intersect += 1
    return PointContainsInPolygon.IN \
        if count_intersect % 2 == 1 else PointContainsInPolygon.OUT
 def test_get_convex_hull_normal(self):
     poly: Polygon = Polygon([
         Point(2, 1),
         Point(0, 0),
         Point(1, 2),
         Point(2, 2),
         Point(4, 2),
         Point(1, 3),
         Point(3, 3)
     ])
     res: Polygon = get_convex_hull(poly)
     self.assertEqual(5, len(res.p_i))
     x_expected: List[float] = [0, 2, 4, 3, 1]
     y_expected: List[float] = [0, 1, 2, 3, 3]
     for i in range(5):
         self.assertTrue(equals(res.p_i[i].x, x_expected[i]))
         self.assertTrue(equals(res.p_i[i].y, y_expected[i]))
 def test_get_cross_point_line_and_circle(self):
     c_0: Circle = Circle(Point(1, 1), 2)
     l_1: Line = Line(Point(0, -2), Point(1, -2))
     l_2: Line = Line(Point(0, -1), Point(1, -1))
     l_3: Line = Line(Point(0, 0), Point(1, 0))
     self.assertIsNone(get_cross_points_circle_and_line(c_0, l_1))
     self.assertEqual(len(get_cross_points_circle_and_line(c_0, l_2)), 1)
     self.assertTrue(
         equals(get_cross_points_circle_and_line(c_0, l_2)[0].x, 1.0))
     self.assertTrue(
         equals(get_cross_points_circle_and_line(c_0, l_2)[0].y, -1.0))
     self.assertEqual(len(get_cross_points_circle_and_line(c_0, l_3)), 2)
     self.assertTrue(
         equals(
             get_cross_points_circle_and_line(c_0, l_3)[0].x,
             1 + math.sqrt(3)))
     self.assertTrue(
         equals(
             get_cross_points_circle_and_line(c_0, l_3)[1].x,
             1 - math.sqrt(3)))
def get_common_points_circle_and_circle(c_1: Circle,
                                        c_2: Circle) -> Optional[List[Point]]:
    """
    Calculate Two Circle Common Points
    :param c_1: One Circle
    :param c_2: The Other Circle
    :return: None: Not Intersect or Duplicate, List[Point] : Contacts or Intersects
    """
    d: float = (c_1.c - c_2.c).abs()  # Distance Between Circle Centers
    res: List[Point] = []
    if c_1.c == c_2.c and equals(c_1.r, c_2.r) \
            or c_1.r + c_2.r < d or abs(c_1.r - c_2.r) > d:
        # Completely Duplicates or Not Contacts and Intersects
        return None
    if equals(c_1.r + c_2.r, d) or equals(abs(c_1.r - c_2.r), d):
        # Contacts Each Other
        c_larger: Circle = c_1 if c_1.r >= c_2.r else c_2
        c_smaller: Circle = c_1 if c_1.r < c_2.r else c_2
        v_c1_c2: Vector = Vector((c_smaller.c - c_larger.c).x,
                                 (c_smaller.c - c_larger.c).y)
        x_c: Vector = Vector(c_larger.c.x,
                             c_larger.c.y) + v_c1_c2 * (c_larger.r / d)
        res.append(Point(x_c.x, x_c.y))
    if abs(c_1.r - c_2.r) < d < c_1.r + c_2.r:
        # Intersects
        t: float = (math.pi / 2.0 if c_1.c.y < c_2.c.y else -math.pi / 2.0)\
            if c_1.c.x == c_2.c.x \
            else math.atan(abs((c_2.c - c_1.c).y / (c_2.c - c_1.c).x))
        alpha: float = \
            math.acos((c_1.r * c_1.r + d * d - c_2.r * c_2.r) / (2 * c_1.r * d))
        v_1: Vector = Vector(c_1.c.x, c_1.c.y) + \
            Vector(c_1.r * math.cos(t + alpha), c_1.r * math.sin(t + alpha))
        v_2: Vector = Vector(c_1.c.x, c_1.c.y) + \
            Vector(c_1.r * math.cos(t - alpha), c_1.r * math.sin(t - alpha))
        res.append(Point(v_1.x, v_1.y))
        res.append(Point(v_2.x, v_2.y))
    return res
def get_cross_points_circle_and_line(circle: Circle,
                                     line: Line) -> Optional[List[Point]]:
    """
    Calculate Line and Circle Cross Point Coordinate Values
    :param circle: Circle
    :param line: Line
    :return: Cross Point or Contact Point
    """
    res: List[Point] = []
    d: float = get_distance_lp(line, circle.c)
    if circle.r - d > EPS:
        p: Point = project(line, circle.c)
        e: float = math.sqrt(math.pow(circle.r, 2.0) - math.pow(d, 2.0))
        v_base: Vector = Vector((line.p2 - line.p1).x, (line.p2 - line.p1).y)
        x_1: Vector = Vector(p.x, p.y) + v_base * e / v_base.abs()
        x_2: Vector = Vector(p.x, p.y) - v_base * e / v_base.abs()
        res.append(Point(x_1.x, x_1.y))
        res.append(Point(x_2.x, x_2.y))
        return res
    elif equals(circle.r, d):
        res.append(project(line, circle.c))
        return res
    return None
 def test_get_distance_lp_normal(self):
     l: Line = Line(Point(0, 0), Point(1, 1))
     p: Point = Point(0.5, 0)
     self.assertEqual(equals(get_distance_lp(l, p), 0.5 / math.sqrt(2)),
                      True)
 def test_get_distance_normal(self):
     p_1: Point = Point(0, 0)
     p_2: Point = Point(1, 1)
     self.assertEqual(equals(get_distance(p_1, p_2), math.sqrt(2.0)), True)
 def test_reflect_normal(self):
     seg: Segment = Segment(Point(0, 0), Point(3, 4))
     p: Point = Point(2, 5)
     self.assertEqual(equals(reflect(seg, p).x, 4.24), True)
     self.assertEqual(equals(reflect(seg, p).y, 3.32), True)
 def test_project_normal(self):
     seg: Segment = Segment(Point(0, 0), Point(3, 4))
     p: Point = Point(2, 5)
     self.assertEqual(equals(project(seg, p).x, 3.12), True)
     self.assertEqual(equals(project(seg, p).y, 4.16), True)
 def test_get_common_points_circle_and_circle_normal(self):
     c_1: Circle = Circle(Point(0, 0), 2.0)
     c_2: Circle = Circle(Point(2.0, 0), 2.0)
     # Intersects Case
     res: List[Point] = get_common_points_circle_and_circle(c_1, c_2)
     self.assertEqual(len(res), 2)
     self.assertTrue(equals(res[0].x, 2.0 * 1.0 / 2.0))
     self.assertTrue(equals(res[0].y, 2.0 * math.sqrt(3.0) / 2.0))
     self.assertTrue(equals(res[1].x, 2.0 * 1.0 / 2.0))
     self.assertTrue(equals(res[1].y, -2.0 * math.sqrt(3.0) / 2.0))
     c_2_1: Circle = Circle(Point(0, 2.0), 2.0)
     res = get_common_points_circle_and_circle(c_1, c_2_1)
     self.assertEqual(len(res), 2)
     self.assertTrue(equals(res[0].x, -2.0 * math.sqrt(3.0) / 2.0))
     self.assertTrue(equals(res[0].y, 2.0 * 1.0 / 2.0))
     self.assertTrue(equals(res[1].x, 2.0 * math.sqrt(3.0) / 2.0))
     self.assertTrue(equals(res[1].y, 2.0 * 1.0 / 2.0))
     c_2_2: Circle = Circle(Point(2.0, 2.0), 2.0)
     res = get_common_points_circle_and_circle(c_1, c_2_2)
     self.assertEqual(len(res), 2)
     self.assertTrue(equals(res[0].x, 0))
     self.assertTrue(equals(res[0].y, 2.0))
     self.assertTrue(equals(res[1].x, 2.0))
     self.assertTrue(equals(res[1].y, 0))
     # Contacts Case
     c_3: Circle = Circle(Point(4.0, 0), 2.0)
     res = get_common_points_circle_and_circle(c_1, c_3)
     self.assertEqual(len(res), 1)
     self.assertTrue(equals(res[0].x, 2.0))
     self.assertTrue(equals(res[0].y, 0))
     # Not Intersects and Contacts Case
     c_4: Circle = Circle(Point(5.0, 0), 2.0)
     res = get_common_points_circle_and_circle(c_1, c_4)
     self.assertIsNone(res)
     c_5: Circle = Circle(Point(1.0, 0), 0.5)
     res = get_common_points_circle_and_circle(c_1, c_5)
     self.assertIsNone(res)
     # Completely Duplicates Case
     res = get_common_points_circle_and_circle(c_1, c_1)
     self.assertIsNone(res)