|
7 | 7 | from pygame.geometry import Circle |
8 | 8 |
|
9 | 9 |
|
| 10 | +def float_range(a, b, step): |
| 11 | + result = [] |
| 12 | + current_value = a |
| 13 | + while current_value < b: |
| 14 | + result.append(current_value) |
| 15 | + current_value += step |
| 16 | + return result |
| 17 | + |
| 18 | + |
10 | 19 | class CircleTypeTest(unittest.TestCase): |
11 | 20 | def testConstruction_invalid_type(self): |
12 | 21 | """Checks whether passing wrong types to the constructor |
@@ -910,6 +919,185 @@ class CircleSub(Circle): |
910 | 919 | self.assertEqual(type(c.move_ip(1, 1)), type(None)) |
911 | 920 | self.assertEqual(type(cs.move_ip(1, 1)), type(None)) |
912 | 921 |
|
| 922 | + def test_meth_rotate_ip_invalid_argnum(self): |
| 923 | + """Ensures that the rotate_ip method correctly deals with invalid numbers of arguments.""" |
| 924 | + c = Circle(0, 0, 1) |
| 925 | + |
| 926 | + with self.assertRaises(TypeError): |
| 927 | + c.rotate_ip() |
| 928 | + |
| 929 | + invalid_args = [ |
| 930 | + (1, (2, 2), 2), |
| 931 | + (1, (2, 2), 2, 2), |
| 932 | + (1, (2, 2), 2, 2, 2), |
| 933 | + (1, (2, 2), 2, 2, 2, 2), |
| 934 | + (1, (2, 2), 2, 2, 2, 2, 2), |
| 935 | + (1, (2, 2), 2, 2, 2, 2, 2, 2), |
| 936 | + ] |
| 937 | + |
| 938 | + for args in invalid_args: |
| 939 | + with self.assertRaises(TypeError): |
| 940 | + c.rotate_ip(*args) |
| 941 | + |
| 942 | + def test_meth_rotate_ip_invalid_argtype(self): |
| 943 | + """Ensures that the rotate_ip method correctly deals with invalid argument types.""" |
| 944 | + c = Circle(0, 0, 1) |
| 945 | + |
| 946 | + invalid_args = [ |
| 947 | + ("a",), # angle str |
| 948 | + (None,), # angle str |
| 949 | + ((1, 2)), # angle tuple |
| 950 | + ([1, 2]), # angle list |
| 951 | + (1, "a"), # origin str |
| 952 | + (1, None), # origin None |
| 953 | + (1, True), # origin True |
| 954 | + (1, False), # origin False |
| 955 | + (1, (1, 2, 3)), # origin tuple |
| 956 | + (1, [1, 2, 3]), # origin list |
| 957 | + (1, (1, "a")), # origin str |
| 958 | + (1, ("a", 1)), # origin str |
| 959 | + (1, (1, None)), # origin None |
| 960 | + (1, (None, 1)), # origin None |
| 961 | + (1, (1, (1, 2))), # origin tuple |
| 962 | + (1, (1, [1, 2])), # origin list |
| 963 | + ] |
| 964 | + |
| 965 | + for value in invalid_args: |
| 966 | + with self.assertRaises(TypeError): |
| 967 | + c.rotate_ip(*value) |
| 968 | + |
| 969 | + def test_meth_rotate_ip_return(self): |
| 970 | + """Ensures that the rotate_ip method always returns None.""" |
| 971 | + c = Circle(0, 0, 1) |
| 972 | + |
| 973 | + for angle in float_range(-360, 360, 1): |
| 974 | + self.assertIsNone(c.rotate_ip(angle)) |
| 975 | + self.assertIsInstance(c.rotate_ip(angle), type(None)) |
| 976 | + |
| 977 | + def test_meth_rotate_invalid_argnum(self): |
| 978 | + """Ensures that the rotate method correctly deals with invalid numbers of arguments.""" |
| 979 | + c = Circle(0, 0, 1) |
| 980 | + |
| 981 | + with self.assertRaises(TypeError): |
| 982 | + c.rotate() |
| 983 | + |
| 984 | + invalid_args = [ |
| 985 | + (1, (2, 2), 2), |
| 986 | + (1, (2, 2), 2, 2), |
| 987 | + (1, (2, 2), 2, 2, 2), |
| 988 | + (1, (2, 2), 2, 2, 2, 2), |
| 989 | + (1, (2, 2), 2, 2, 2, 2, 2), |
| 990 | + (1, (2, 2), 2, 2, 2, 2, 2, 2), |
| 991 | + ] |
| 992 | + |
| 993 | + for args in invalid_args: |
| 994 | + with self.assertRaises(TypeError): |
| 995 | + c.rotate(*args) |
| 996 | + |
| 997 | + def test_meth_rotate_invalid_argtype(self): |
| 998 | + """Ensures that the rotate method correctly deals with invalid argument types.""" |
| 999 | + c = Circle(0, 0, 1) |
| 1000 | + |
| 1001 | + invalid_args = [ |
| 1002 | + ("a",), # angle str |
| 1003 | + (None,), # angle str |
| 1004 | + ((1, 2)), # angle tuple |
| 1005 | + ([1, 2]), # angle list |
| 1006 | + (1, "a"), # origin str |
| 1007 | + (1, None), # origin None |
| 1008 | + (1, True), # origin True |
| 1009 | + (1, False), # origin False |
| 1010 | + (1, (1, 2, 3)), # origin tuple |
| 1011 | + (1, [1, 2, 3]), # origin list |
| 1012 | + (1, (1, "a")), # origin str |
| 1013 | + (1, ("a", 1)), # origin str |
| 1014 | + (1, (1, None)), # origin None |
| 1015 | + (1, (None, 1)), # origin None |
| 1016 | + (1, (1, (1, 2))), # origin tuple |
| 1017 | + (1, (1, [1, 2])), # origin list |
| 1018 | + ] |
| 1019 | + |
| 1020 | + for value in invalid_args: |
| 1021 | + with self.assertRaises(TypeError): |
| 1022 | + c.rotate(*value) |
| 1023 | + |
| 1024 | + def test_meth_rotate_return(self): |
| 1025 | + """Ensures that the rotate method always returns a Circle.""" |
| 1026 | + c = Circle(0, 0, 1) |
| 1027 | + |
| 1028 | + class CircleSubclass(Circle): |
| 1029 | + pass |
| 1030 | + |
| 1031 | + cs = CircleSubclass(0, 0, 1) |
| 1032 | + |
| 1033 | + for angle in float_range(-360, 360, 1): |
| 1034 | + self.assertIsInstance(c.rotate(angle), Circle) |
| 1035 | + self.assertIsInstance(cs.rotate(angle), CircleSubclass) |
| 1036 | + |
| 1037 | + def test_meth_rotate(self): |
| 1038 | + """Ensures the Circle.rotate() method rotates the Circle correctly.""" |
| 1039 | + |
| 1040 | + def rotate_circle(circle: Circle, angle, center): |
| 1041 | + def rotate_point(x, y, rang, cx, cy): |
| 1042 | + x -= cx |
| 1043 | + y -= cy |
| 1044 | + x_new = x * math.cos(rang) - y * math.sin(rang) |
| 1045 | + y_new = x * math.sin(rang) + y * math.cos(rang) |
| 1046 | + return x_new + cx, y_new + cy |
| 1047 | + |
| 1048 | + angle = math.radians(angle) |
| 1049 | + cx, cy = center if center is not None else circle.center |
| 1050 | + x, y = rotate_point(circle.x, circle.y, angle, cx, cy) |
| 1051 | + return Circle(x, y, circle.r) |
| 1052 | + |
| 1053 | + def assert_approx_equal(circle1, circle2, eps=1e-12): |
| 1054 | + self.assertAlmostEqual(circle1.x, circle2.x, delta=eps) |
| 1055 | + self.assertAlmostEqual(circle1.y, circle2.y, delta=eps) |
| 1056 | + self.assertAlmostEqual(circle1.r, circle2.r, delta=eps) |
| 1057 | + |
| 1058 | + c = Circle(0, 0, 1) |
| 1059 | + angles = float_range(-360, 360, 0.5) |
| 1060 | + centers = [(a, b) for a in range(-10, 10) for b in range(-10, 10)] |
| 1061 | + for angle in angles: |
| 1062 | + assert_approx_equal(c.rotate(angle), rotate_circle(c, angle, None)) |
| 1063 | + for center in centers: |
| 1064 | + assert_approx_equal( |
| 1065 | + c.rotate(angle, center), rotate_circle(c, angle, center) |
| 1066 | + ) |
| 1067 | + |
| 1068 | + def test_meth_rotate_ip(self): |
| 1069 | + """Ensures the Circle.rotate_ip() method rotates the Circle correctly.""" |
| 1070 | + |
| 1071 | + def rotate_circle(circle: Circle, angle, center): |
| 1072 | + def rotate_point(x, y, rang, cx, cy): |
| 1073 | + x -= cx |
| 1074 | + y -= cy |
| 1075 | + x_new = x * math.cos(rang) - y * math.sin(rang) |
| 1076 | + y_new = x * math.sin(rang) + y * math.cos(rang) |
| 1077 | + return x_new + cx, y_new + cy |
| 1078 | + |
| 1079 | + angle = math.radians(angle) |
| 1080 | + cx, cy = center if center is not None else circle.center |
| 1081 | + x, y = rotate_point(circle.x, circle.y, angle, cx, cy) |
| 1082 | + circle.x = x |
| 1083 | + circle.y = y |
| 1084 | + return circle |
| 1085 | + |
| 1086 | + def assert_approx_equal(circle1, circle2, eps=1e-12): |
| 1087 | + self.assertAlmostEqual(circle1.x, circle2.x, delta=eps) |
| 1088 | + self.assertAlmostEqual(circle1.y, circle2.y, delta=eps) |
| 1089 | + self.assertAlmostEqual(circle1.r, circle2.r, delta=eps) |
| 1090 | + |
| 1091 | + c = Circle(0, 0, 1) |
| 1092 | + angles = float_range(-360, 360, 0.5) |
| 1093 | + centers = [(a, b) for a in range(-10, 10) for b in range(-10, 10)] |
| 1094 | + for angle in angles: |
| 1095 | + c.rotate_ip(angle) |
| 1096 | + assert_approx_equal(c, rotate_circle(c, angle, None)) |
| 1097 | + for center in centers: |
| 1098 | + c.rotate_ip(angle, center) |
| 1099 | + assert_approx_equal(c, rotate_circle(c, angle, center)) |
| 1100 | + |
913 | 1101 |
|
914 | 1102 | if __name__ == "__main__": |
915 | 1103 | unittest.main() |
0 commit comments