11"""
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.
714
815Example:
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
1435"""
36+
1537from __future__ import annotations
1638
1739import math
@@ -28,26 +50,15 @@ class AABB:
2850 """
2951 Axis-Aligned Bounding Box representation of a rectangle.
3052 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)
3553 """
36-
3754 min_x : float
3855 min_y : float
3956 max_x : float
4057 max_y : float
4158
4259 @classmethod
4360 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."""
5162 half_width = rect .short_side .length / 2
5263 half_height = rect .long_side .length / 2
5364 return cls (
@@ -58,72 +69,114 @@ def from_rectangle(cls, rect: Rectangle, center: Point) -> AABB:
5869 )
5970
6071
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 :
8073 """
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
8879 """
89- box1 = AABB .from_rectangle (rect1 , pos1 )
90- box2 = AABB .from_rectangle (rect2 , pos2 )
9180
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+ )
106112
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 )
113123
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 ))
117127
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 )
122132
123- return distance < circle .radius
133+ # Collision occurs if distance is less than circle radius
134+ return distance < circle .radius
124135
125136
126137if __name__ == "__main__" :
138+ # Run doctest examples
127139 import doctest
128-
129140 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