1
1
"""
2
- Collision detection algorithms for basic geometric shapes.
3
- Supports collision detection between:
4
- - Circles
5
- - Rectangles (Axis-Aligned Bounding Boxes)
6
- - Circle and Rectangle
2
+ This is a Python implementation for collision detection between geometric shapes.
3
+ The implementation supports detecting intersections between basic shapes like circles
4
+ and rectangles in 2D space.
5
+
6
+ Question :-
7
+ Given two geometric shapes and their positions in 2D space, determine if they intersect
8
+ or overlap with each other. The shapes can be:
9
+ - Circles (defined by center point and radius)
10
+ - Rectangles (defined by center point and dimensions)
11
+
12
+ The implementation uses Axis-Aligned Bounding Box (AABB) technique for efficient
13
+ rectangle collision detection.
7
14
8
15
Example:
9
- >>> from geometry import Circle, Rectangle
10
- >>> circle1 = Circle(5) # circle with radius 5
11
- >>> circle2 = Circle(3) # circle with radius 3
12
- >>> detect_circle_collision(circle1, circle2, (0, 0), (7, 0))
13
- True # circles overlap at x=7 (distance less than sum of radii 5+3=8)
16
+ >>> detector = CollisionDetector()
17
+ >>> # Test circle-circle collision
18
+ >>> circle1, circle2 = Circle(5), Circle(3)
19
+ >>> detector.detect_circle_collision(circle1, circle2, (0, 0), (7, 0))
20
+ True # circles overlap as distance (7) < sum of radii (8)
21
+ >>> detector.detect_circle_collision(circle1, circle2, (0, 0), (9, 0))
22
+ False # circles don't overlap as distance (9) > sum of radii (8)
23
+ >>> # Test rectangle-rectangle collision
24
+ >>> rect1, rect2 = Rectangle(4, 6), Rectangle(2, 2)
25
+ >>> detector.detect_aabb_collision(rect1, rect2, (0, 0), (1, 1))
26
+ True # rectangles overlap
27
+ >>> detector.detect_aabb_collision(rect1, rect2, (0, 0), (5, 5))
28
+ False # rectangles don't overlap
29
+ >>> # Test circle-rectangle collision
30
+ >>> circle, rect = Circle(2), Rectangle(4, 4)
31
+ >>> detector.detect_circle_rectangle_collision(circle, rect, (0, 0), (3, 0))
32
+ True # shapes overlap as circle edge reaches rectangle
33
+ >>> detector.detect_circle_rectangle_collision(circle, rect, (0, 0), (5, 0))
34
+ False # shapes don't overlap
14
35
"""
36
+
15
37
from __future__ import annotations
16
38
17
39
import math
@@ -28,26 +50,15 @@ class AABB:
28
50
"""
29
51
Axis-Aligned Bounding Box representation of a rectangle.
30
52
Stores the minimum and maximum coordinates of the box.
31
-
32
- >>> box = AABB.from_rectangle(Rectangle(2, 3), (0, 0))
33
- >>> box.min_x, box.min_y, box.max_x, box.max_y
34
- (-1.0, -1.5, 1.0, 1.5)
35
53
"""
36
-
37
54
min_x : float
38
55
min_y : float
39
56
max_x : float
40
57
max_y : float
41
58
42
59
@classmethod
43
60
def from_rectangle (cls , rect : Rectangle , center : Point ) -> AABB :
44
- """
45
- Create an AABB from a Rectangle and its center point.
46
-
47
- >>> box = AABB.from_rectangle(Rectangle(4, 6), (1, 2))
48
- >>> box.min_x, box.min_y, box.max_x, box.max_y
49
- (-1.0, -1.0, 3.0, 5.0)
50
- """
61
+ """Convert a Rectangle at given center point to AABB representation."""
51
62
half_width = rect .short_side .length / 2
52
63
half_height = rect .long_side .length / 2
53
64
return cls (
@@ -58,72 +69,114 @@ def from_rectangle(cls, rect: Rectangle, center: Point) -> AABB:
58
69
)
59
70
60
71
61
- def detect_circle_collision (circle1 : Circle , circle2 : Circle , pos1 : Point , pos2 : Point ) -> bool :
62
- """
63
- Detect collision between two circles at given positions.
64
- Returns True if circles overlap or touch, False otherwise.
65
-
66
- >>> detect_circle_collision(Circle(5), Circle(3), (0, 0), (7, 0))
67
- True
68
- >>> detect_circle_collision(Circle(5), Circle(3), (0, 0), (9, 0))
69
- False
70
- >>> detect_circle_collision(Circle(5), Circle(3), (0, 0), (8, 0)) # touching
71
- True
72
- """
73
- dx = pos2 [0 ] - pos1 [0 ]
74
- dy = pos2 [1 ] - pos1 [1 ]
75
- distance = math .sqrt (dx * dx + dy * dy )
76
- return distance <= (circle1 .radius + circle2 .radius ) # Changed < to <=
77
-
78
-
79
- def detect_aabb_collision (rect1 : Rectangle , rect2 : Rectangle , pos1 : Point , pos2 : Point ) -> bool :
72
+ class CollisionDetector :
80
73
"""
81
- Detect collision between two rectangles using AABB method.
82
- Returns True if rectangles overlap, False otherwise.
83
-
84
- >>> detect_aabb_collision(Rectangle(2, 3), Rectangle(2, 2), (0, 0), (1, 1))
85
- True
86
- >>> detect_aabb_collision(Rectangle(2, 3), Rectangle(2, 2), (0, 0), (3, 3))
87
- False
74
+ A class that provides methods for detecting collisions between different geometric shapes.
75
+ Supports collision detection between:
76
+ - Circle to Circle
77
+ - Rectangle to Rectangle (using AABB)
78
+ - Circle to Rectangle
88
79
"""
89
- box1 = AABB .from_rectangle (rect1 , pos1 )
90
- box2 = AABB .from_rectangle (rect2 , pos2 )
91
80
92
- return (
93
- box1 .min_x <= box2 .max_x
94
- and box1 .max_x >= box2 .min_x
95
- and box1 .min_y <= box2 .max_y
96
- and box1 .max_y >= box2 .min_y
97
- )
98
-
99
-
100
- def detect_circle_rectangle_collision (
101
- circle : Circle , rect : Rectangle , circle_pos : Point , rect_pos : Point
102
- ) -> bool :
103
- """
104
- Detect collision between a circle and a rectangle.
105
- Returns True if shapes overlap, False otherwise.
81
+ @staticmethod
82
+ def detect_circle_collision (circle1 : Circle , circle2 : Circle , pos1 : Point , pos2 : Point ) -> bool :
83
+ """
84
+ Detect collision between two circles at given positions.
85
+ Returns True if circles overlap or touch, False otherwise.
86
+ """
87
+ # Calculate distance between circle centers using Pythagorean theorem
88
+ dx = pos2 [0 ] - pos1 [0 ]
89
+ dy = pos2 [1 ] - pos1 [1 ]
90
+ distance = math .sqrt (dx * dx + dy * dy )
91
+
92
+ # Circles collide if distance is less than or equal to sum of radii
93
+ return distance <= (circle1 .radius + circle2 .radius )
94
+
95
+ @staticmethod
96
+ def detect_aabb_collision (rect1 : Rectangle , rect2 : Rectangle , pos1 : Point , pos2 : Point ) -> bool :
97
+ """
98
+ Detect collision between two rectangles using AABB method.
99
+ Returns True if rectangles overlap, False otherwise.
100
+ """
101
+ # Convert rectangles to AABB representation
102
+ box1 = AABB .from_rectangle (rect1 , pos1 )
103
+ box2 = AABB .from_rectangle (rect2 , pos2 )
104
+
105
+ # Check for overlap in both x and y axes
106
+ return (
107
+ box1 .min_x <= box2 .max_x
108
+ and box1 .max_x >= box2 .min_x
109
+ and box1 .min_y <= box2 .max_y
110
+ and box1 .max_y >= box2 .min_y
111
+ )
106
112
107
- >>> detect_circle_rectangle_collision(Circle(2), Rectangle(4, 4), (0, 0), (3, 0))
108
- True
109
- >>> detect_circle_rectangle_collision(Circle(2), Rectangle(4, 4), (0, 0), (5, 0))
110
- False
111
- """
112
- box = AABB .from_rectangle (rect , rect_pos )
113
+ @staticmethod
114
+ def detect_circle_rectangle_collision (
115
+ circle : Circle , rect : Rectangle , circle_pos : Point , rect_pos : Point
116
+ ) -> bool :
117
+ """
118
+ Detect collision between a circle and a rectangle.
119
+ Returns True if shapes overlap, False otherwise.
120
+ """
121
+ # Convert rectangle to AABB
122
+ box = AABB .from_rectangle (rect , rect_pos )
113
123
114
- # Find the closest point on the rectangle to the circle's center
115
- closest_x = max (box .min_x , min (circle_pos [0 ], box .max_x ))
116
- closest_y = max (box .min_y , min (circle_pos [1 ], box .max_y ))
124
+ # Find the closest point on rectangle to circle center
125
+ closest_x = max (box .min_x , min (circle_pos [0 ], box .max_x ))
126
+ closest_y = max (box .min_y , min (circle_pos [1 ], box .max_y ))
117
127
118
- # Calculate distance between the closest point and circle center
119
- dx = circle_pos [0 ] - closest_x
120
- dy = circle_pos [1 ] - closest_y
121
- distance = math .sqrt (dx * dx + dy * dy )
128
+ # Calculate distance between closest point and circle center
129
+ dx = circle_pos [0 ] - closest_x
130
+ dy = circle_pos [1 ] - closest_y
131
+ distance = math .sqrt (dx * dx + dy * dy )
122
132
123
- return distance < circle .radius
133
+ # Collision occurs if distance is less than circle radius
134
+ return distance < circle .radius
124
135
125
136
126
137
if __name__ == "__main__" :
138
+ # Run doctest examples
127
139
import doctest
128
-
129
140
doctest .testmod ()
141
+
142
+ # Additional test cases
143
+ detector = CollisionDetector ()
144
+
145
+ # Test circle-circle collision
146
+ print ("\n Testing circle-circle collision:" )
147
+ circle1 , circle2 = Circle (5 ), Circle (3 )
148
+ test_cases = [
149
+ ((0 , 0 ), (7 , 0 ), True , "Overlapping circles" ),
150
+ ((0 , 0 ), (8 , 0 ), True , "Touching circles" ),
151
+ ((0 , 0 ), (9 , 0 ), False , "Non-overlapping circles" ),
152
+ ((0 , 0 ), (5 , 5 ), True , "Diagonal overlap" ),
153
+ ]
154
+ for pos1 , pos2 , expected , desc in test_cases :
155
+ result = detector .detect_circle_collision (circle1 , circle2 , pos1 , pos2 )
156
+ print (f"{ desc } : { '✓' if result == expected else '✗' } " )
157
+
158
+ # Test rectangle-rectangle collision
159
+ print ("\n Testing rectangle-rectangle collision:" )
160
+ rect1 , rect2 = Rectangle (4 , 6 ), Rectangle (2 , 2 )
161
+ test_cases = [
162
+ ((0 , 0 ), (1 , 1 ), True , "Overlapping rectangles" ),
163
+ ((0 , 0 ), (3 , 0 ), True , "Touching rectangles" ),
164
+ ((0 , 0 ), (5 , 5 ), False , "Non-overlapping rectangles" ),
165
+ ((0 , 0 ), (2 , 2 ), True , "Partial overlap" ),
166
+ ]
167
+ for pos1 , pos2 , expected , desc in test_cases :
168
+ result = detector .detect_aabb_collision (rect1 , rect2 , pos1 , pos2 )
169
+ print (f"{ desc } : { '✓' if result == expected else '✗' } " )
170
+
171
+ # Test circle-rectangle collision
172
+ print ("\n Testing circle-rectangle collision:" )
173
+ circle , rect = Circle (2 ), Rectangle (4 , 4 )
174
+ test_cases = [
175
+ ((0 , 0 ), (3 , 0 ), True , "Circle overlapping rectangle edge" ),
176
+ ((0 , 0 ), (0 , 0 ), True , "Circle inside rectangle" ),
177
+ ((0 , 0 ), (5 , 0 ), False , "No collision" ),
178
+ ((0 , 0 ), (3 , 3 ), True , "Corner overlap" ),
179
+ ]
180
+ for circle_pos , rect_pos , expected , desc in test_cases :
181
+ result = detector .detect_circle_rectangle_collision (circle , rect , circle_pos , rect_pos )
182
+ print (f"{ desc } : { '✓' if result == expected else '✗' } " )
0 commit comments