Skip to content

Commit b305cb6

Browse files
committed
Passengers in the metro car are now actually rendered inside the metro car
1 parent d51c7b5 commit b305cb6

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

PROGRESS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@
6262
- Improved random metro line color generation to prefer hues that are more distinct from already selected line colors.
6363
- Added color-distance helper utilities and tests for hue wrap distance and distinct hue selection behavior.
6464
- Changed station pool generation so new station positions are less likely to spawn far from the current station cluster center.
65+
- Changed metro rendering so passenger icons are displayed inside each metro car in a 2x3 grid that moves with the car.
66+
- Improved metro passenger icon spacing and rotated passenger icon placement to follow metro car orientation.
67+
- Rebalanced metro passenger slot layout to a uniform 3x2 in-car grid to prevent overlap while preserving car-aligned rotation.
68+
- Updated metro passenger slot spacing so row and column icon gaps match the side margins inside the metro car.

src/entity/metro.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import pygame
2+
from math import ceil
3+
14
from config import (
25
metro_accel_time_ms,
36
metro_boarding_time_per_passenger_ms,
@@ -7,10 +10,12 @@
710
metro_passengers_per_row,
811
metro_size,
912
metro_speed_per_ms,
13+
passenger_size,
1014
)
1115
from entity.holder import Holder
1216
from entity.segment import Segment
1317
from entity.station import Station
18+
from geometry.point import Point
1419
from geometry.rect import Rect
1520
from shortuuid import uuid # type: ignore
1621

@@ -38,3 +43,37 @@ def __init__(self) -> None:
3843
self.boarding_progress_ms = 0
3944
self.boarding_time_per_passenger_ms = metro_boarding_time_per_passenger_ms
4045
self.just_arrived_and_stopped = False
46+
47+
def draw(
48+
self,
49+
surface: pygame.surface.Surface,
50+
current_time_ms: int | None = None,
51+
passenger_max_wait_time_ms: int | None = None,
52+
) -> None:
53+
self.shape.draw(surface, self.position)
54+
55+
grid_cols = self.passengers_per_row
56+
grid_rows = ceil(self.capacity / grid_cols)
57+
metro_width = 2 * self.size
58+
metro_height = self.size
59+
passenger_diameter = 2 * passenger_size
60+
x_gap = (metro_width - (grid_cols * passenger_diameter)) / (grid_cols + 1)
61+
y_gap = (metro_height - (grid_rows * passenger_diameter)) / (grid_rows + 1)
62+
x_step = passenger_diameter + x_gap
63+
y_step = passenger_diameter + y_gap
64+
x_start = (-metro_width / 2) + x_gap + passenger_size
65+
y_start = (-metro_height / 2) + y_gap + passenger_size
66+
metro_degrees = getattr(self.shape, "degrees", 0.0)
67+
68+
for idx, passenger in enumerate(self.passengers):
69+
col = idx % grid_cols
70+
row = idx // grid_cols
71+
x_offset = x_start + (col * x_step)
72+
y_offset = y_start + (row * y_step)
73+
rotated_offset = Point(x_offset, y_offset).rotate(metro_degrees)
74+
passenger.position = self.position + rotated_offset
75+
passenger.draw(
76+
surface,
77+
current_time_ms=current_time_ms,
78+
max_wait_time_ms=passenger_max_wait_time_ms,
79+
)

test/test_station.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,60 @@ def test_holder_draw_positions_and_move(self):
8787
self.assertNotIn(passengers[1], station.passengers)
8888
self.assertIn(passengers[1], other_station.passengers)
8989

90+
def test_metro_draw_positions_passengers_in_3x2_grid(self):
91+
metro = Metro()
92+
metro.position = Point(100, 100)
93+
passengers = [Passenger(Circle((0, 0, 0), 1)) for _ in range(6)]
94+
for passenger in passengers:
95+
passenger.draw = MagicMock()
96+
metro.add_passenger(passenger)
97+
98+
metro.draw(self.screen)
99+
100+
grid_cols = metro.passengers_per_row
101+
grid_rows = 2
102+
metro_width = 2 * metro.size
103+
metro_height = metro.size
104+
passenger_diameter = 2 * passenger_size
105+
x_gap = (metro_width - (grid_cols * passenger_diameter)) / (grid_cols + 1)
106+
y_gap = (metro_height - (grid_rows * passenger_diameter)) / (grid_rows + 1)
107+
x_step = passenger_diameter + x_gap
108+
y_step = passenger_diameter + y_gap
109+
x_start = (-metro_width / 2) + x_gap + passenger_size
110+
y_start = (-metro_height / 2) + y_gap + passenger_size
111+
112+
for idx, passenger in enumerate(passengers):
113+
col = idx % grid_cols
114+
row = idx // grid_cols
115+
x_offset = x_start + (col * x_step)
116+
y_offset = y_start + (row * y_step)
117+
expected_position = metro.position + Point(x_offset, y_offset).rotate(0)
118+
self.assertEqual(passenger.position, expected_position)
119+
120+
def test_metro_draw_rotates_passenger_grid_with_metro(self):
121+
metro = Metro()
122+
metro.position = Point(200, 200)
123+
metro.shape.set_degrees(90)
124+
passenger = Passenger(Circle((0, 0, 0), 1))
125+
passenger.draw = MagicMock()
126+
metro.add_passenger(passenger)
127+
128+
metro.draw(self.screen)
129+
130+
grid_cols = metro.passengers_per_row
131+
grid_rows = 2
132+
metro_width = 2 * metro.size
133+
metro_height = metro.size
134+
passenger_diameter = 2 * passenger_size
135+
x_gap = (metro_width - (grid_cols * passenger_diameter)) / (grid_cols + 1)
136+
y_gap = (metro_height - (grid_rows * passenger_diameter)) / (grid_rows + 1)
137+
x_start = (-metro_width / 2) + x_gap + passenger_size
138+
y_start = (-metro_height / 2) + y_gap + passenger_size
139+
x_offset = x_start
140+
y_offset = y_start
141+
expected_position = metro.position + Point(x_offset, y_offset).rotate(90)
142+
self.assertEqual(passenger.position, expected_position)
143+
90144
def test_travel_plan_methods(self):
91145
station = Station(Rect(station_color, station_size, station_size), Point(0, 0))
92146
plan = TravelPlan([Node(station)])

0 commit comments

Comments
 (0)