def test_defined_segment_correctly(self) -> None: """ Segment Data Structure Normal Case Test :return: None """ segment: Segment = Segment(Point(0.1, 0.2), Point(0.3, 0.4)) self.assertEqual(segment.p1.x, 0.1) self.assertEqual(segment.p1.y, 0.2) self.assertEqual(segment.p2.x, 0.3) self.assertEqual(segment.p2.y, 0.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 test_intersect_normal(self): s_1: Segment = Segment(Point(0, 0), Point(3, 0)) s_2: Segment = Segment(Point(1, 1), Point(2, -1)) s_3: Segment = Segment(Point(3, 1), Point(3, -1)) s_4: Segment = Segment(Point(3, -2), Point(5, 0)) self.assertEqual(intersect(s_1, s_2), True) self.assertEqual(intersect(s_1, s_3), True) self.assertEqual(intersect(s_1, s_4), False)
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_is_parallel_normal(self): # Vector v_1: Vector = Vector(1.0, 1.0) v_2: Vector = Vector(2.0, 2.0) self.assertEqual(is_parallel(v_1, v_2), True) # Points p_1: Point = Point(1.0, 1.0) p_2: Point = Point(2.0, 2.0) p_3: Point = Point(-1.0, -1.0) p_4: Point = Point(-2.0, -2.0) self.assertEqual(is_parallel(p_1, p_2, p_3, p_4), True) # Segments s_1: Segment = Segment(p_1, p_2) s_2: Segment = Segment(p_3, p_4) self.assertEqual(is_parallel(s_1, s_2), True)
def test_defined_point_correctly(self) -> None: """ Point Data Structure Normal Case Test :return: None """ for i in range(-1, 2): for j in range(-1, 2): point: Point = Point(i * 1.0, j * 1.0) self.assertEqual(point.x, i * 1.0) self.assertEqual(point.y, j * 1.0) if i == -1: self.assertEqual(point < Point(0, 0), True) elif i == 0: self.assertEqual(point < Point(0, 0), j == -1) else: self.assertEqual(point < Point(0, 0), False)
def get_num_of_segment_intersections(segments: List[Segment]) -> int: """ Get Number of Segment Intersections (Manhattan Geometry) :param segments: Segment List :return: Number of Segment Intersections """ n: int = len(segments) k: int = 0 ep: List[EndPoint] = [ EndPoint(Point(i, i), i, EndPointType.BOTTOM) for i in range(2 * n) ] for i in range(n): if segments[i].p1.y == segments[i].p2.y: if segments[i].p1.x > segments[i].p2.x: segments[i].p1, segments[i].p2 = segments[i].p2, segments[i].p1 elif segments[i].p1.y > segments[i].p2.y: segments[i].p1, segments[i].p2 = segments[i].p2, segments[i].p1 if segments[i].p1.y == segments[i].p2.y: ep[k] = EndPoint(Point(segments[i].p1.x, segments[i].p1.y), i, EndPointType.LEFT) k += 1 ep[k] = EndPoint(Point(segments[i].p2.x, segments[i].p2.y), i, EndPointType.RIGHT) k += 1 else: ep[k] = EndPoint(Point(segments[i].p1.x, segments[i].p1.y), i, EndPointType.BOTTOM) k += 1 ep[k] = EndPoint(Point(segments[i].p2.x, segments[i].p2.y), i, EndPointType.TOP) k += 1 ep.sort() bt: List[int] = [] # Binary Tree of Intersect Point x-coordinate value cnt: int = 0 for j in range(2 * n): if ep[j].st == EndPointType.TOP: bt.remove(ep[j].p.x) elif ep[j].st == EndPointType.BOTTOM: bisect.insort(bt, ep[j].p.x) elif ep[j].st == EndPointType.LEFT: left: int = bisect.bisect_left(bt, segments[ep[j].seg].p1.x) right: int = bisect.bisect_right(bt, segments[ep[j].seg].p2.x) cnt += right - left return cnt
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_defined_circle_correctly(self) -> None: """ Circle Data Structure Normal Case Test :return: None """ circle: Circle = Circle(Point(0.5, 0.7), 3.5) self.assertEqual(circle.c.x, 0.5) self.assertEqual(circle.c.y, 0.7) self.assertEqual(circle.r, 3.5)
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 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 test_get_point_relative_position_normal(self): p_0: Point = Point(0, 0) p_1: Point = Point(2, 0) self.assertEqual(get_point_relative_position(p_0, p_1, Point(-1, 1)), PointRelativePosition.COUNTER_CLOCK_WISE) self.assertEqual(get_point_relative_position(p_0, p_1, Point(-1, -1)), PointRelativePosition.CLOCK_WISE) self.assertEqual(get_point_relative_position(p_0, p_1, Point(-1, 0)), PointRelativePosition.ON_LINE_BACK) self.assertEqual(get_point_relative_position(p_0, p_1, Point(3, 0)), PointRelativePosition.ON_LINE_FRONT) self.assertEqual(get_point_relative_position(p_0, p_1, Point(1, 0)), PointRelativePosition.ON_SEGMENT)
def test_point_contained_in_polygon(self): poly: Polygon = Polygon( [Point(0, 0), Point(3, 1), Point(2, 3), Point(0, 3)]) p_1: Point = Point(2, 1) self.assertEqual(point_contained_in_polygon(poly, p_1), PointContainsInPolygon.IN) p_2: Point = Point(0, 2) self.assertEqual(point_contained_in_polygon(poly, p_2), PointContainsInPolygon.ON_EDGE) p_3: Point = Point(3, 2) self.assertEqual(point_contained_in_polygon(poly, p_3), PointContainsInPolygon.OUT)
def get_cross_point(s_1: Segment, s_2: Segment) -> Optional[Point]: """ Calculate Segment Cross Point :param s_1: One Segment :param s_2: The Other Segment :return: Cross Point Coordinate Value if intersects """ if intersect(s_1, s_2): v_base: Vector = Vector((s_2.p2 - s_2.p1).x, (s_2.p2 - s_2.p1).y) v_hypo1: Vector = Vector((s_1.p1 - s_2.p1).x, (s_1.p1 - s_2.p1).y) v_hypo2: Vector = Vector((s_1.p2 - s_2.p1).x, (s_1.p2 - s_2.p1).y) v: Vector = Vector((s_1.p2 - s_1.p1).x, (s_1.p2 - s_1.p1).y) d1: float = abs(v_base.cross(v_hypo1)) d2: float = abs(v_base.cross(v_hypo2)) p: Vector = Vector(s_1.p1.x, s_1.p1.y) + v * (d1 / (d1 + d2)) return Point(p.x, p.y) return None
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 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)
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_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_get_num_of_segment_intersections_normal(self): segments: List[Segment] = [ Segment(Point(2, 2), Point(2, 5)), Segment(Point(1, 3), Point(5, 3)), Segment(Point(4, 1), Point(4, 4)), Segment(Point(5, 2), Point(7, 2)), Segment(Point(6, 1), Point(6, 3)), Segment(Point(6, 5), Point(6, 7)) ] self.assertEqual(3, get_num_of_segment_intersections(segments))