def test_line_interpolation_points(self): arc = Arc(Point(1, 0), 90, 1, 180) expected_points = [ Point(1, 0), Point(math.sqrt(2) / 2, math.sqrt(2) / 2), Point(0, 1), Point(-math.sqrt(2) / 2, math.sqrt(2) / 2), Point(-1, 0) ] self.assertAlmostEqual(arc.line_interpolation_points(math.pi / 4), expected_points)
def test_angular_offset_for_point(self): # with positive angular length arc = Arc(Point(0, 0), 90, 1, 360) self.assertEqual(arc._angular_offset_for_point(Point(0, 0)), 0) self.assertEqual(arc._angular_offset_for_point(Point(-1, 1)), 90) self.assertEqual(arc._angular_offset_for_point(Point(-2, 0)), 180) self.assertEqual(arc._angular_offset_for_point(Point(-1, -1)), 270) # with negative angular length arc = Arc(Point(0, 0), -90, 1, -360) self.assertEqual(arc._angular_offset_for_point(Point(0, 0)), 0) self.assertEqual(arc._angular_offset_for_point(Point(-1, 1)), -270) self.assertEqual(arc._angular_offset_for_point(Point(-2, 0)), -180) self.assertEqual(arc._angular_offset_for_point(Point(-1, -1)), -90)
def test_point_at_offset(self): arc_90_deg = Arc(Point(0, 0), 90, 10, 90) self.assertAlmostEqual(arc_90_deg.point_at_offset(0), Point(0, 0)) self.assertAlmostEqual(arc_90_deg.point_at_offset(7.853981634), Point(-2.92893219, 7.07106781)) self.assertAlmostEqual(arc_90_deg.point_at_offset(15.7079632679), Point(-10, 10)) arc_135_neg_deg = Arc(Point(0, 0), 90, 10, -135) self.assertAlmostEqual(arc_135_neg_deg.point_at_offset(0), Point(0, 0)) self.assertAlmostEqual(arc_135_neg_deg.point_at_offset(15.7079632679), Point(10, 10)) self.assertAlmostEqual(arc_135_neg_deg.point_at_offset(23.5619449019), Point(17.07106781, 7.07106781))
def test_from_points_in_circle(self): # with start_point = end_point circle = Circle(Point(0, 0), 1) point = Point(1, 0) arc = Arc.from_points_in_circle(point, point, circle) self.assertEqual(arc, Arc(point, 90, 1, 0)) # with start point different to end point circle = Circle(Point(5, 5), 1) start_point = Point(6, 5) end_point = Point(5, 6) arc = Arc.from_points_in_circle(start_point, end_point, circle) self.assertEqual(arc, Arc(start_point, 90, 1, 90)) # in the lower half-plane circle = Circle(Point(0, 0), 1) start_point = Point(0, -1) end_point = Point(1, 0) expected_arc = Arc(Point(0, -1), 0, 1, 90) self.assertEqual( Arc.from_points_in_circle(start_point, end_point, circle), expected_arc) # with angular length greater than 180 circle = Circle(Point(0, 0), 1) start_point = Point(1, 0) end_point = Point(0, -1) arc = Arc.from_points_in_circle(start_point, end_point, circle) self.assertEqual(arc, Arc(start_point, 90, 1, 270))
def test_elements_short_S_path(self): a = Point(0, 0) b = Point(50, 0) c = Point(50, 10) d = Point(100, 10) lane = Street.from_control_points([a, b, c, d]).lane_at(0) geometry = lane.path_for(LinesAndArcsGeometry) expected_elements = [ LineSegment(a, Point(45, 0)), Arc(Point(45, 0), 0, 5, 90), Arc(Point(50, 5), 90, 5, -90), LineSegment(Point(55, 10), d)] self.assertAlmostEqual(geometry.elements(), expected_elements)
def test_find_arc_intersection_with_circles_that_do_not_intersect(self): arc1 = Arc(Point(1, 0), 90, 1, 180) arc2 = Arc(Point(10, 10), 180, 2, 270) self.assertEqual(arc1._find_arc_intersection(arc2), []) # with nested circles arc3 = Arc(Point(6, 0), 0, 4, 360) arc4 = Arc(Point(6, 1), 0, 2, 360) self.assertEqual(arc3._find_arc_intersection(arc4), [])
def test_contains_arc(self): # contained arc arc1 = Arc(Point(1, 0), 90, 1, 180) arc2 = Arc(Point(0, 1), 180, 1, 90) self.assertTrue(arc1.contains_arc(arc2)) # with not contained arc arc1 = Arc(Point(1, 0), -90, 1, -180) arc2 = Arc(Point(1, 0), 90, 1, 180) self.assertFalse(arc1.contains_arc(arc2))
def test_extend(self): null_arc = Arc(Point(0, 0), 90, 10, 0) arc_90_deg = null_arc.extend(15.707963268) expected_arc = Arc(Point(0, 0), 90, 10, 90) self.assertArcAlmostEqual(arc_90_deg, expected_arc) negative_arc = Arc(Point(0, 0), 90, 10, -180) extended_deg = negative_arc.extend(10) expected_arc = Arc(Point(0, 0), 90, 10, -122.704220487) self.assertArcAlmostEqual(extended_deg, expected_arc)
def test_heading_at_offset(self): arc = Arc(Point(1, 0), 90, 1, 360) self.assertEqual(arc.heading_at_offset(0), 90) self.assertEqual(arc.heading_at_offset(math.pi / 2), 180) self.assertEqual(arc.heading_at_offset(math.pi), 270) self.assertEqual(arc.heading_at_offset(math.pi * 1.5), 0) self.assertEqual(arc.heading_at_offset(2 * math.pi), 90)
def test_can_be_merged_with(self): arc1 = Arc(Point(1, 0), 90, 1, 90) arc2 = Arc(Point(0, 1), 180, 1, 90) arc3 = Arc(Point(0, 1), 0, 1, -90) arc4 = Arc(Point(-1, 0), 270, 1, 90) self.assertTrue(arc1.can_be_merged_with(arc2)) self.assertFalse(arc1.can_be_merged_with(arc3)) self.assertFalse(arc1.can_be_merged_with(arc4))
def test_find_arc_intersection_with_arcs_that_intersect_at_two_points( self): # with the center of each circle outside the other circle arc1 = Arc(Point(5, 0), 90, 5, 60) arc2 = Arc(Point(8, -1), 180, 5, -90) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [Point(5, 0), Point(3, 4)]) # with the center of one of the circles inside the other circle arc3 = Arc(Point(0, 5), 180, 5, 180) arc4 = Arc(Point(-1, 0), 90, 3, 270) self.assertAlmostEqual(arc3._find_arc_intersection(arc4), [Point(-4, 3), Point(-4, -3)])
def test_circle(self): # counter-clockwise arc = Arc(Point(1, 0), 90, 1, 10) self.assertAlmostEqual(arc.circle(), Circle(Point(0, 0), 1)) # clockwise arc = Arc(Point(1, 0), 90, 1, -45) self.assertAlmostEqual(arc.circle(), Circle(Point(2, 0), 1))
def test_elements_two_non_collinear_segment_path(self): a = Point(0, 0) b = Point(50, 0) c_up = Point(100, 10) c_down = Point(100, -10) lane = Street.from_control_points([a, b, c_up]).lane_at(0) geometry = lane.path_for(LinesAndArcsGeometry) expected_elements = [ LineSegment(a, Point(45.0, 0.0)), Arc(Point(45.0, 0.0), 0.0, 50.49509756, 11.30993247), LineSegment(Point(54.90290337, 0.98058067), c_up)] self.assertAlmostEqual(geometry.elements(), expected_elements) lane = Street.from_control_points([a, b, c_down]).lane_at(0) geometry = lane.path_for(LinesAndArcsGeometry) expected_elements = [ LineSegment(a, Point(45.0, 0.0)), Arc(Point(45.0, 0.0), 0.0, 50.49509756, -11.30993247), LineSegment(Point(54.90290337, -0.98058067), c_down)] self.assertAlmostEqual(geometry.elements(), expected_elements)
def _build_arc(cls, start_point, start_heading, end_point, end_heading): angle_in_degrees = end_heading - start_heading d2 = end_point.squared_distance_to(start_point) cos = math.cos(math.radians(angle_in_degrees)) radius = math.sqrt(d2 / (2 * (1 - cos))) # Keep the angle in the [-180, 180) range if angle_in_degrees >= 180: angle_in_degrees = angle_in_degrees - 360 if angle_in_degrees < -180: angle_in_degrees = angle_in_degrees + 360 return Arc(start_point, start_heading, radius, angle_in_degrees)
def build_curve(data): """ Build a curve object from the passed XML data set """ _arc = Arc() for _k in data: if _k[0] == '{': print('tag set', _k.split('}')[1], data[_k]) _arc.setv(_k.split('}')[1], data[_k]) elif _k[0] == '@': print('attr set') _arc.setv(_k[1:], data[_k]) return _arc
def build_path_and_waypoints(cls, lane, mapped_centers): if lane.road_nodes_count() < 2: raise ValueError( "At least two nodes are required to build a geometry") road_nodes = list(lane.road_nodes()) is_circular = mapped_centers[road_nodes[0]].almost_equal_to( mapped_centers[road_nodes[-1]], 5) path = Path() waypoints = [] previous_node = road_nodes.pop(0) previous_point = mapped_centers[previous_node] nodes_count = len(road_nodes) for index, node in enumerate(road_nodes): point = mapped_centers[node] is_last_node = index + 1 == nodes_count if is_last_node: if is_circular: next_point = mapped_centers[road_nodes[0]] else: next_point = None else: next_point = mapped_centers[road_nodes[index + 1]] if path.is_empty(): previous_element_end_point = previous_point else: previous_element_end_point = path.element_at(-1).end_point() if next_point is None: element = LineSegment(previous_element_end_point, point) path.add_element(element) waypoints.append( cls._new_waypoint(lane, element, previous_node)) waypoints.append( cls._new_waypoint(lane, element, road_nodes[-1], False)) else: previous_vector = point - previous_point next_vector = next_point - point if previous_vector.is_collinear_with(next_vector): element = LineSegment(previous_element_end_point, point) path.add_element(element) waypoints.append( cls._new_waypoint(lane, element, previous_node)) if is_last_node: waypoints.append( cls._new_waypoint(lane, element, road_nodes[0], False)) else: inverted_previous_segment = LineSegment( point, previous_point) real_inverted_previous_segment = LineSegment( point, previous_element_end_point) next_segment = LineSegment(point, next_point) if is_last_node: real_next_segment = LineSegment( point, path.element_at(0).end_point()) delta = min(real_inverted_previous_segment.length(), real_next_segment.length(), 5) else: delta = min(real_inverted_previous_segment.length(), next_segment.length() / 2.0, 5) previous_segment_new_end_point = real_inverted_previous_segment.point_at_offset( delta) next_segment_new_start_point = next_segment.point_at_offset( delta) previous_segment = LineSegment( previous_element_end_point, previous_segment_new_end_point) # Try to avoid small segments if previous_segment.length() < 0.25: # `- 1e-10` to avoid length overflow due to floating point math new_delta = delta + previous_segment.length() - 1e-10 if next_segment.length() > new_delta: previous_segment_new_end_point = real_inverted_previous_segment.point_at_offset( new_delta) next_segment_new_start_point = next_segment.point_at_offset( new_delta) previous_segment = LineSegment( previous_element_end_point, previous_segment_new_end_point) angle_between_vectors = previous_vector.angle(next_vector) d2 = previous_segment_new_end_point.squared_distance_to( next_segment_new_start_point) cos = math.cos(math.radians(angle_between_vectors)) radius = math.sqrt(d2 / (2.0 * (1.0 - cos))) # If there should be no segment, just an arc. Use previous_element_end_point to # avoid rounding errors and make a perfect overlap if previous_segment.length() < 1e-8: if path.not_empty(): heading = path.element_at(-1).end_heading() else: heading = inverted_previous_segment.inverted( ).start_heading() connection_arc = Arc(previous_element_end_point, heading, radius, angle_between_vectors) path.add_element(connection_arc) waypoints.append( cls._new_waypoint(lane, connection_arc, node)) if not connection_arc.end_point().almost_equal_to( next_segment_new_start_point, 3): raise RuntimeError( "Expecting arc end {0} to match next segment entry point {1}" .format(connection_arc.end_point(), next_segment_new_start_point)) else: heading = inverted_previous_segment.inverted( ).start_heading() connection_arc = Arc(previous_segment_new_end_point, heading, radius, angle_between_vectors) path.add_element(previous_segment) waypoints.append( cls._new_waypoint(lane, previous_segment, previous_node)) path.add_element(connection_arc) waypoints.append( cls._new_waypoint(lane, connection_arc, node)) if not connection_arc.end_point().almost_equal_to( next_segment_new_start_point, 3): raise RuntimeError( "Expecting arc end {0} to match next segment entry point {1}" .format(connection_arc.end_point(), next_segment_new_start_point)) if is_last_node: if connection_arc.end_point().distance_to( path.element_at(1).start_point()) < 1e-8: path.remove_first_element() waypoints.pop(0) else: first_element = path.element_at(0) new_first_element = LineSegment( connection_arc.end_point(), first_element.end_point()) path.replace_first_element(new_first_element) waypoints[0] = cls._new_waypoint( lane, new_first_element, lane.road_nodes()[0]) waypoints.append( cls._new_waypoint(lane, connection_arc, road_nodes[0], False)) previous_node = node previous_point = mapped_centers[previous_node] return (path, waypoints)
def test_axis_aligned_arc_end_point(self): # Positive heading and angular length arc_90_deg = Arc(Point(0, 0), 90, 10, 90) self.assertAlmostEqual(arc_90_deg.end_point(), Point(-10, 10)) arc_180_deg = Arc(Point(0, 0), 90, 10, 180) self.assertAlmostEqual(arc_180_deg.end_point(), Point(-20, 0)) arc_270_deg = Arc(Point(0, 0), 90, 10, 270) self.assertAlmostEqual(arc_270_deg.end_point(), Point(-10, -10)) arc_360_deg = Arc(Point(0, 0), 90, 10, 360) self.assertAlmostEqual(arc_360_deg.end_point(), Point(0, 0)) # Positive heading and negative angular length arc_minus_90_deg = Arc(Point(0, 0), 90, 10, -90) self.assertAlmostEqual(arc_minus_90_deg.end_point(), Point(10, 10)) arc_minus_180_deg = Arc(Point(0, 0), 90, 10, -180) self.assertAlmostEqual(arc_minus_180_deg.end_point(), Point(20, 0)) arc_minus_270_deg = Arc(Point(0, 0), 90, 10, -270) self.assertAlmostEqual(arc_minus_270_deg.end_point(), Point(10, -10)) arc_minus_360_deg = Arc(Point(0, 0), 90, 10, -360) self.assertAlmostEqual(arc_minus_360_deg.end_point(), Point(0, 0)) # Negative heading and positive angular length arc_minus_90_deg = Arc(Point(0, 0), -90, 10, 90) self.assertAlmostEqual(arc_minus_90_deg.end_point(), Point(10, -10)) arc_minus_180_deg = Arc(Point(0, 0), -90, 10, 180) self.assertAlmostEqual(arc_minus_180_deg.end_point(), Point(20, 0)) arc_minus_270_deg = Arc(Point(0, 0), -90, 10, 270) self.assertAlmostEqual(arc_minus_270_deg.end_point(), Point(10, 10)) arc_minus_360_deg = Arc(Point(0, 0), -90, 10, 360) self.assertAlmostEqual(arc_minus_360_deg.end_point(), Point(0, 0)) # Negative heading and angular length arc_90_deg = Arc(Point(0, 0), -90, 10, -90) self.assertAlmostEqual(arc_90_deg.end_point(), Point(-10, -10)) arc_180_deg = Arc(Point(0, 0), -90, 10, -180) self.assertAlmostEqual(arc_180_deg.end_point(), Point(-20, 0)) arc_270_deg = Arc(Point(0, 0), -90, 10, -270) self.assertAlmostEqual(arc_270_deg.end_point(), Point(-10, 10)) arc_360_deg = Arc(Point(0, 0), -90, 10, -360) self.assertAlmostEqual(arc_360_deg.end_point(), Point(0, 0))
def test_45_degree_variants_end_point(self): arc_45_deg = Arc(Point(0, 10), 0, 10, 45) self.assertAlmostEqual(arc_45_deg.end_point(), Point(7.07106781, 12.92893218)) arc_135_deg = Arc(Point(0, 0), 90, 10, 135) self.assertAlmostEqual(arc_135_deg.end_point(), Point(-17.07106781, 7.07106781)) arc_225_deg = Arc(Point(0, 0), 315, 10, 225) self.assertAlmostEqual(arc_225_deg.end_point(), Point(7.07106781, 17.07106781)) arc_315_deg = Arc(Point(0, 0), -45, 10, 315) self.assertAlmostEqual(arc_315_deg.end_point(), Point(-2.92893218, 7.07106781))
def test_compute_point_at(self): arc1 = Arc(Point(5, 3), 90, 2, 100) self.assertEqual(arc1._compute_point_at(90), Point(3, 5)) arc2 = Arc(Point(5, 3), 270, 2, -100) self.assertAlmostEqual(arc2._compute_point_at(-90), Point(3, 1))
def test_find_arc_intersection_with_arcs_in_the_same_circle(self): # intersection between and arc and itself arc = Arc(Point(4, 3), 67, 2, 99) self.assertEqual(arc._find_arc_intersection(arc), [arc]) # with no intersection arc1 = Arc(Point(1, 0), 90, 1, 90) arc2 = Arc(Point(-1, 0), 270, 1, 90) self.assertEqual(arc1._find_arc_intersection(arc2), []) # with one point intersection arc1 = Arc(Point(1, 0), 90, 1, 90) arc2 = Arc(Point(0, 1), 180, 1, 90) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [Point(0, 1)]) self.assertAlmostEqual(arc2._find_arc_intersection(arc1), [Point(0, 1)]) # with two points intersection arc1 = Arc(Point(1, 0), 90, 1, 180) arc2 = Arc(Point(1, 0), -90, 1, -180) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [Point(-1, 0), Point(1, 0)]) self.assertAlmostEqual(arc2._find_arc_intersection(arc1), [Point(1, 0), Point(-1, 0)]) # with one arc intersection arc1 = Arc(Point(0, -1), 0, 1, 180) arc2 = Arc(Point(1, 0), 90, 1, 180) expected_arc = Arc(Point(1, 0), 90, 1, 90) self.assertEqual(arc1._find_arc_intersection(arc2), [expected_arc]) self.assertEqual(arc2._find_arc_intersection(arc1), [expected_arc]) # with one arc contained in the other arc1 = Arc(Point(2, 4), -90, 2, 270) arc2 = Arc(Point(6, 4), 270, 2, -90) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [Arc(Point(4, 2), 0, 2, 90)]) self.assertAlmostEqual(arc2._find_arc_intersection(arc1), [Arc(Point(4, 2), 0, 2, 90)]) # with same start point and different end points arc1 = Arc(Point(0, -1), 0, 1, 90) arc2 = Arc(Point(0, -1), 0, 1, 180) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [arc1]) self.assertAlmostEqual(arc2._find_arc_intersection(arc1), [arc1]) # with the same end point and different start points arc1 = Arc(Point(1, 0), 90, 1, 180) arc2 = Arc(Point(0, 1), 180, 1, 90) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [arc2]) self.assertAlmostEqual(arc2._find_arc_intersection(arc1), [arc2]) # with two arcs intersection arc1 = Arc(Point(-1, 0), 270, 1, 270) arc2 = Arc(Point(1, 0), 90, 1, 270) expected_intersection = [ Arc(Point(1, 0), 90, 1, 90), Arc(Point(-1, 0), 270, 1, 90) ] self.assertAlmostEqual(arc1._find_arc_intersection(arc2), expected_intersection) # with and arc in clockwise direction arc1 = Arc(Point(0, 1), 0, 1, -270) arc2 = Arc(Point(1, 0), 90, 1, 270) expected_intersection = [ Arc(Point(1, 0), 90, 1, 90), Arc(Point(-1, 0), 270, 1, 90) ] self.assertAlmostEqual(arc1._find_arc_intersection(arc2), expected_intersection)
def test_offset_for_point(self): arc = Arc(Point(1, 0), 90, 1, 270) self.assertAlmostEqual(arc.offset_for_point(Point(0, 1)), math.pi / 2)
def test_split_into(self): arc = Arc(Point(0, 0), 90, 1, 90) self.assertRaises( ValueError, lambda: arc.split_into([(Point(0, 0), Point(-2, 0))]))
def test_find_arc_intersection_with_circles_that_intersect_but_no_arc_intersection( self): arc1 = Arc(Point(5, 0), 90, 5, 10) arc2 = Arc(Point(0, 3), 0, 5, 10) self.assertEqual(arc1._find_arc_intersection(arc2), [])
def test_end_point_after_counter_clockwise(self): arc = Arc(Point(1, 0), -90, 1, -180).counter_clockwise() self.assertAlmostEqual(arc.end_point(), Point(1, 0))
def test_find_arc_intersection_with_arcs_that_intersect_at_one_point(self): # with positive angular length and circles that intersect at two points arc1 = Arc(Point(5, 0), 90, 5, 360) arc2 = Arc(Point(0, 3), 0, 5, 45) self.assertAlmostEqual(arc1._find_arc_intersection(arc2), [Point(3, 4)]) # with negative angular length and circles that intersect at two points arc3 = Arc(Point(0, 4), 0, 4, -135) arc4 = Arc(Point(4, 0), 180, 4, 360) self.assertEqual(arc3._find_arc_intersection(arc4), [Point(4, 0)]) # with circles that intersect at one point arc5 = Arc(Point(4, 0), 0, 4, 100) arc6 = Arc(Point(8, 4), -90, 2, -30) self.assertEqual(arc5._find_arc_intersection(arc6), [Point(8, 4)])
def test_merge(self): arc1 = Arc(Point(1, 0), 90, 1, 90) arc2 = Arc(Point(0, 1), 180, 1, 90) self.assertAlmostEqual(arc1.merge(arc2), Arc(Point(1, 0), 90, 1, 180)) arc3 = Arc(Point(1, 0), 270, 1, -90) self.assertRaises(ValueError, lambda: arc1.merge(arc3))
def test_null_arc_end_point(self): null_arc = Arc(Point(0, 0), 90, 10, 0) self.assertAlmostEqual(null_arc.end_point(), Point(0, 0))
def test_almost_equal_to(self): arc1 = Arc(Point(1, 0), 90, 1, 180).counter_clockwise() arc2 = Arc(Point(1, 0), -90, 1, -180).counter_clockwise() self.assertFalse(arc1.almost_equal_to(arc2))
def test_points_at_linear_offset(self): # with one point arc = Arc(Point(0, -1), 0, 1, 180) self.assertAlmostEqual(arc.points_at_linear_offset(Point(2, 0), 1), [Point(1, 0)]) # with two points in the arc arc = Arc(Point(0, -1), 0, 1, 180) expected_points = [ Point(1.0 / 2, math.sqrt(3.0 / 4)), Point(1.0 / 2, -math.sqrt(3.0 / 4)) ] self.assertAlmostEqual(arc.points_at_linear_offset(Point(1, 0), 1), expected_points) # with two points in the circle but only one in the arc arc = Arc(Point(0, -1), 0, 1, 90) expected_point = [Point(1.0 / 2, -math.sqrt(3.0 / 4))] self.assertAlmostEqual(arc.points_at_linear_offset(Point(1, 0), 1), expected_point) # with two points in the circle but none in the arc arc = Arc(Point(0, -1), 180, 1, -90) self.assertAlmostEqual(arc.points_at_linear_offset(Point(1, 0), 1), []) # with all the points in the arc at the given offset arc = Arc(Point(0, -1), 0, 1, 180) self.assertAlmostEqual(arc.points_at_linear_offset(Point(0, 0), 1), [arc])
def test_counter_clockwise(self): # for counter-clockwise arc arc1 = Arc(Point(1, 0), 90, 1, 90) self.assertEqual(arc1.counter_clockwise(), arc1) # for clockwise arc with length smaller than 180 degrees arc2 = Arc(Point(1, 0), -90, 1, -90) self.assertAlmostEqual(arc2.counter_clockwise(), Arc(Point(0, -1), 0, 1, 90)) # for clockwise arc with length longer than 180 degrees arc3 = Arc(Point(0, 1), 0, 1, -270) self.assertAlmostEqual(arc3.counter_clockwise(), Arc(Point(-1, 0), 270, 1, 270)) arc4 = Arc(Point(1, 0), -90, 1, -180) self.assertAlmostEqual(arc4.counter_clockwise(), Arc(Point(-1, 0), -90, 1, 180)) arc5 = Arc(Point(1, 0), 90, 1, 180) self.assertAlmostEqual(arc5.counter_clockwise(), arc5) arc6 = Arc(Point(-1, 0), -90, 1, 180) self.assertAlmostEqual(arc6.counter_clockwise(), arc6)