game/render/
agents.rs

1use std::borrow::Borrow;
2use std::collections::HashMap;
3
4use geom::{Circle, Pt2D, QuadTree, Time};
5use map_gui::colors::ColorScheme;
6use map_gui::options::Options;
7use map_model::{Map, Traversable};
8use sim::{AgentID, Sim, UnzoomedAgent, VehicleType};
9use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Panel, Prerender};
10
11use crate::render::{
12    draw_vehicle, unzoomed_agent_radius, DrawPedCrowd, DrawPedestrian, GameRenderable,
13};
14
15pub struct AgentCache {
16    /// This is controlled almost entirely by the minimap panel. It has no meaning in edit mode.
17    pub unzoomed_agents: UnzoomedAgents,
18
19    // This time applies to agents_per_on. unzoomed has its own possibly separate Time!
20    time: Option<Time>,
21    agents_per_on: HashMap<Traversable, Vec<Box<dyn GameRenderable>>>,
22    // when either of (time, unzoomed agent filters) change, recalculate (a quadtree of all agents,
23    // draw all agents)
24    unzoomed: Option<(Time, UnzoomedAgents, QuadTree<AgentID>, Drawable)>,
25}
26
27impl AgentCache {
28    pub fn new() -> AgentCache {
29        AgentCache {
30            unzoomed_agents: UnzoomedAgents::new(),
31            time: None,
32            agents_per_on: HashMap::new(),
33            unzoomed: None,
34        }
35    }
36
37    pub fn get(&self, on: Traversable) -> Vec<&dyn GameRenderable> {
38        self.agents_per_on[&on]
39            .iter()
40            .map(|obj| obj.borrow())
41            .collect()
42    }
43
44    pub fn populate_if_needed(
45        &mut self,
46        on: Traversable,
47        map: &Map,
48        sim: &Sim,
49        cs: &ColorScheme,
50        prerender: &Prerender,
51    ) {
52        let now = sim.time();
53        if Some(now) == self.time && self.agents_per_on.contains_key(&on) {
54            return;
55        }
56        let step_count = sim.step_count();
57
58        let mut list: Vec<Box<dyn GameRenderable>> = Vec::new();
59        for c in sim.get_draw_cars(on, map).into_iter() {
60            list.push(draw_vehicle(c, map, sim, prerender, cs));
61        }
62        let (loners, crowds) = sim.get_draw_peds(on, map);
63        for p in loners {
64            list.push(Box::new(DrawPedestrian::new(
65                p, step_count, map, sim, prerender, cs,
66            )));
67        }
68        for c in crowds {
69            list.push(Box::new(DrawPedCrowd::new(c, map, prerender, cs)));
70        }
71
72        if Some(now) != self.time {
73            self.agents_per_on.clear();
74            self.time = Some(now);
75        }
76
77        self.agents_per_on.insert(on, list);
78    }
79
80    /// If the sim time has changed or the unzoomed agent filters have been modified, recalculate
81    /// the quadtree and drawable for all unzoomed agents.
82    pub fn calculate_unzoomed_agents<P: AsRef<Prerender>>(
83        &mut self,
84        prerender: &mut P,
85        map: &Map,
86        sim: &Sim,
87        cs: &ColorScheme,
88    ) -> &QuadTree<AgentID> {
89        let now = sim.time();
90        let mut recalc = true;
91        if let Some((time, ref orig_agents, _, _)) = self.unzoomed {
92            if now == time && self.unzoomed_agents == orig_agents.clone() {
93                recalc = false;
94            }
95        }
96
97        if recalc {
98            let highlighted = sim.get_highlighted_people();
99
100            let mut batch = GeomBatch::new();
101            let mut quadtree = QuadTree::builder();
102            // It's quite silly to produce triangles for the same circle over and over again. ;)
103            let car_circle = Circle::new(
104                Pt2D::new(0.0, 0.0),
105                unzoomed_agent_radius(Some(VehicleType::Car)),
106            )
107            .to_polygon();
108            let ped_circle =
109                Circle::new(Pt2D::new(0.0, 0.0), unzoomed_agent_radius(None)).to_polygon();
110
111            for agent in sim.get_unzoomed_agents(map) {
112                if let Some(mut color) = self.unzoomed_agents.color(&agent, cs) {
113                    // If the sim has highlighted people, then fade all others out.
114                    if highlighted
115                        .as_ref()
116                        .and_then(|h| agent.person.as_ref().map(|p| !h.contains(p)))
117                        .unwrap_or(false)
118                    {
119                        // TODO Tune. How's this look at night?
120                        color = color.tint(0.5);
121                    }
122
123                    let circle = if agent.id.to_vehicle_type().is_some() {
124                        car_circle.translate(agent.pos.x(), agent.pos.y())
125                    } else {
126                        ped_circle.translate(agent.pos.x(), agent.pos.y())
127                    };
128                    quadtree.add_with_box(agent.id, circle.get_bounds());
129                    batch.push(color, circle);
130                }
131            }
132
133            let draw = prerender.as_ref().upload(batch);
134
135            self.unzoomed = Some((now, self.unzoomed_agents.clone(), quadtree.build(), draw));
136        }
137
138        &self.unzoomed.as_ref().unwrap().2
139    }
140
141    pub fn draw_unzoomed_agents(
142        &mut self,
143        g: &mut GfxCtx,
144        map: &Map,
145        sim: &Sim,
146        cs: &ColorScheme,
147        opts: &Options,
148    ) {
149        self.calculate_unzoomed_agents(g, map, sim, cs);
150        g.redraw(&self.unzoomed.as_ref().unwrap().3);
151
152        if opts.debug_all_agents {
153            let mut cnt = 0;
154            for input in sim.get_all_draw_cars(map) {
155                cnt += 1;
156                draw_vehicle(input, map, sim, g.prerender, cs);
157            }
158            println!(
159                "At {}, debugged {} cars",
160                sim.time(),
161                abstutil::prettyprint_usize(cnt)
162            );
163            // Pedestrians aren't the ones crashing
164        }
165    }
166}
167
168#[derive(PartialEq, Clone)]
169pub struct UnzoomedAgents {
170    cars: bool,
171    bikes: bool,
172    buses_and_trains: bool,
173    peds: bool,
174}
175
176impl UnzoomedAgents {
177    pub fn new() -> UnzoomedAgents {
178        UnzoomedAgents {
179            cars: true,
180            bikes: true,
181            buses_and_trains: true,
182            peds: true,
183        }
184    }
185
186    pub fn cars(&self) -> bool {
187        self.cars
188    }
189    pub fn bikes(&self) -> bool {
190        self.bikes
191    }
192    pub fn buses_and_trains(&self) -> bool {
193        self.buses_and_trains
194    }
195    pub fn peds(&self) -> bool {
196        self.peds
197    }
198
199    fn color(&self, agent: &UnzoomedAgent, color_scheme: &ColorScheme) -> Option<Color> {
200        match agent.id.to_vehicle_type() {
201            Some(VehicleType::Car) => {
202                if self.cars {
203                    Some(color_scheme.unzoomed_car)
204                } else {
205                    None
206                }
207            }
208            Some(VehicleType::Bike) => {
209                if self.bikes {
210                    Some(color_scheme.unzoomed_bike)
211                } else {
212                    None
213                }
214            }
215            Some(VehicleType::Bus) | Some(VehicleType::Train) => {
216                if self.buses_and_trains {
217                    Some(color_scheme.unzoomed_bus)
218                } else {
219                    None
220                }
221            }
222            None => {
223                if self.peds {
224                    Some(color_scheme.unzoomed_pedestrian)
225                } else {
226                    None
227                }
228            }
229        }
230    }
231
232    pub fn update(&mut self, panel: &Panel) {
233        self.cars = panel.is_checked("Car");
234        self.bikes = panel.is_checked("Bike");
235        self.buses_and_trains = panel.is_checked("Bus");
236        self.peds = panel.is_checked("Walk");
237    }
238}