Skip to content

Commit 9c3b102

Browse files
committed
Move tests into main script following project convention
- Remove separate test file - Add comprehensive doctests - Add example usage in main block - Follow project's testing convention
1 parent e46e3c7 commit 9c3b102

File tree

2 files changed

+132
-162
lines changed

2 files changed

+132
-162
lines changed

Diff for: geometry/collision_detection.py

+132-79
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
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
815
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
1435
"""
36+
1537
from __future__ import annotations
1638

1739
import 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

126137
if __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("\nTesting 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("\nTesting 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("\nTesting 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 '✗'}")

Diff for: geometry/test_collision_detection.py

-83
This file was deleted.

0 commit comments

Comments
 (0)