2020-01-22 21:24:52 +03:00
|
|
|
use crate::common::Overlays;
|
2019-04-30 00:40:48 +03:00
|
|
|
use crate::helpers::{ColorScheme, ID};
|
2019-12-12 02:04:32 +03:00
|
|
|
use crate::obj_actions::PerObjectActions;
|
2019-12-03 21:39:16 +03:00
|
|
|
use crate::options::Options;
|
2019-02-17 01:26:29 +03:00
|
|
|
use crate::render::{
|
2020-01-24 23:59:34 +03:00
|
|
|
AgentCache, AgentColorScheme, DrawCtx, DrawMap, DrawOptions, Renderable, MIN_ZOOM_FOR_DETAIL,
|
2019-02-17 01:26:29 +03:00
|
|
|
};
|
2020-02-03 21:21:13 +03:00
|
|
|
use crate::sandbox::TutorialState;
|
2019-06-07 20:59:13 +03:00
|
|
|
use abstutil::{MeasureMemory, Timer};
|
2019-12-05 20:37:05 +03:00
|
|
|
use ezgui::{Color, EventCtx, GfxCtx, Prerender, TextureType};
|
2019-09-05 00:26:55 +03:00
|
|
|
use geom::{Bounds, Circle, Distance, Pt2D};
|
2019-05-11 02:21:21 +03:00
|
|
|
use map_model::{Map, Traversable};
|
2019-06-21 01:15:14 +03:00
|
|
|
use rand::seq::SliceRandom;
|
2019-12-22 21:14:19 +03:00
|
|
|
use sim::{Analytics, GetDrawAgents, Sim, SimFlags};
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2019-04-22 03:30:04 +03:00
|
|
|
pub struct UI {
|
2019-04-29 19:27:43 +03:00
|
|
|
pub primary: PerMapUI,
|
2019-06-24 02:45:58 +03:00
|
|
|
// Invariant: This is Some(...) iff we're in A/B test mode or a sub-state.
|
|
|
|
pub secondary: Option<PerMapUI>,
|
2019-12-22 21:14:19 +03:00
|
|
|
// Only exists in some gameplay modes. Must be carefully reset otherwise.
|
|
|
|
prebaked: Option<Analytics>,
|
2019-04-29 19:27:43 +03:00
|
|
|
pub cs: ColorScheme,
|
2019-12-18 02:50:09 +03:00
|
|
|
// TODO This is a bit weird to keep here; it's controlled almost entirely by the minimap panel.
|
|
|
|
// It has no meaning in edit mode.
|
2019-08-19 01:11:05 +03:00
|
|
|
pub agent_cs: AgentColorScheme,
|
2019-12-03 21:39:16 +03:00
|
|
|
pub opts: Options,
|
2019-12-12 02:04:32 +03:00
|
|
|
|
|
|
|
pub per_obj: PerObjectActions,
|
2020-01-22 21:24:52 +03:00
|
|
|
pub overlay: Overlays,
|
2020-02-03 21:21:13 +03:00
|
|
|
|
|
|
|
// Static data that lasts the entire session. Use sparingly.
|
|
|
|
pub session: SessionState,
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2019-04-22 03:30:04 +03:00
|
|
|
impl UI {
|
2019-12-03 21:56:47 +03:00
|
|
|
pub fn new(flags: Flags, opts: Options, ctx: &mut EventCtx, splash: bool) -> UI {
|
|
|
|
let cs = ColorScheme::load(opts.color_scheme.clone());
|
2019-12-05 03:36:01 +03:00
|
|
|
let primary = ctx.loading_screen("load map", |ctx, mut timer| {
|
2019-12-05 20:37:05 +03:00
|
|
|
ctx.set_textures(
|
2019-09-11 03:04:33 +03:00
|
|
|
vec![
|
2019-12-05 20:37:05 +03:00
|
|
|
("assets/pregame/challenges.png", TextureType::Stretch),
|
|
|
|
("assets/pregame/logo.png", TextureType::Stretch),
|
|
|
|
],
|
|
|
|
&mut timer,
|
|
|
|
);
|
2019-09-11 03:04:33 +03:00
|
|
|
|
2019-12-05 03:36:01 +03:00
|
|
|
PerMapUI::new(flags, &cs, ctx, &mut timer)
|
2019-06-07 20:59:13 +03:00
|
|
|
});
|
2019-06-21 01:15:14 +03:00
|
|
|
|
|
|
|
let mut rng = primary.current_flags.sim_flags.make_rng();
|
|
|
|
let rand_focus_pt = primary
|
|
|
|
.map
|
|
|
|
.all_buildings()
|
|
|
|
.choose(&mut rng)
|
|
|
|
.and_then(|b| ID::Building(b.id).canonical_point(&primary))
|
|
|
|
.or_else(|| {
|
|
|
|
primary
|
|
|
|
.map
|
|
|
|
.all_lanes()
|
|
|
|
.choose(&mut rng)
|
|
|
|
.and_then(|l| ID::Lane(l.id).canonical_point(&primary))
|
|
|
|
})
|
|
|
|
.expect("Can't get canonical_point of a random building or lane");
|
2020-01-14 01:39:02 +03:00
|
|
|
let bounds = primary.map.get_bounds();
|
|
|
|
ctx.canvas.map_dims = (bounds.width(), bounds.height());
|
2019-06-21 01:15:14 +03:00
|
|
|
|
|
|
|
if splash {
|
|
|
|
ctx.canvas.center_on_map_pt(rand_focus_pt);
|
|
|
|
} else {
|
2019-10-27 02:16:32 +03:00
|
|
|
if !ctx.canvas.load_camera_state(primary.map.get_name()) {
|
|
|
|
println!("Couldn't load camera state, just focusing on an arbitrary building");
|
|
|
|
ctx.canvas.center_on_map_pt(rand_focus_pt);
|
2019-06-21 01:15:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 02:45:58 +03:00
|
|
|
UI {
|
|
|
|
primary,
|
|
|
|
secondary: None,
|
2019-12-22 21:14:19 +03:00
|
|
|
prebaked: None,
|
2019-12-20 01:52:54 +03:00
|
|
|
agent_cs: AgentColorScheme::default(&cs),
|
2019-12-17 01:50:21 +03:00
|
|
|
cs,
|
2019-12-03 21:56:47 +03:00
|
|
|
opts,
|
2019-12-12 02:04:32 +03:00
|
|
|
per_obj: PerObjectActions::new(),
|
2020-01-22 21:24:52 +03:00
|
|
|
overlay: Overlays::Inactive,
|
2020-02-03 21:21:13 +03:00
|
|
|
session: SessionState::empty(),
|
2019-06-24 02:45:58 +03:00
|
|
|
}
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
2018-10-21 22:18:25 +03:00
|
|
|
|
2020-01-21 22:50:56 +03:00
|
|
|
pub fn has_prebaked(&self) -> bool {
|
|
|
|
self.prebaked.is_some()
|
|
|
|
}
|
2019-12-22 21:14:19 +03:00
|
|
|
pub fn prebaked(&self) -> &Analytics {
|
|
|
|
self.prebaked.as_ref().unwrap()
|
|
|
|
}
|
|
|
|
pub fn set_prebaked(&mut self, prebaked: Option<Analytics>) {
|
|
|
|
self.prebaked = prebaked;
|
|
|
|
}
|
|
|
|
|
2019-12-05 02:12:18 +03:00
|
|
|
pub fn switch_map(&mut self, ctx: &mut EventCtx, load: String) {
|
2019-11-19 00:51:11 +03:00
|
|
|
ctx.canvas.save_camera_state(self.primary.map.get_name());
|
|
|
|
let mut flags = self.primary.current_flags.clone();
|
2019-12-05 02:12:18 +03:00
|
|
|
flags.sim_flags.load = load;
|
2020-02-03 21:21:13 +03:00
|
|
|
let session = std::mem::replace(&mut self.session, SessionState::empty());
|
2019-12-03 21:56:47 +03:00
|
|
|
*self = UI::new(flags, self.opts.clone(), ctx, false);
|
2020-02-03 21:21:13 +03:00
|
|
|
self.session = session;
|
2019-11-19 00:51:11 +03:00
|
|
|
}
|
|
|
|
|
2019-10-17 23:44:14 +03:00
|
|
|
pub fn draw_ctx(&self) -> DrawCtx<'_> {
|
2019-10-14 04:44:19 +03:00
|
|
|
DrawCtx {
|
|
|
|
cs: &self.cs,
|
|
|
|
map: &self.primary.map,
|
|
|
|
draw_map: &self.primary.draw_map,
|
|
|
|
sim: &self.primary.sim,
|
2019-12-03 21:39:16 +03:00
|
|
|
opts: &self.opts,
|
2019-10-14 04:44:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-29 23:55:59 +03:00
|
|
|
pub fn draw(
|
2019-04-24 21:16:50 +03:00
|
|
|
&self,
|
|
|
|
g: &mut GfxCtx,
|
2019-04-29 23:55:59 +03:00
|
|
|
opts: DrawOptions,
|
2019-08-23 00:19:35 +03:00
|
|
|
source: &dyn GetDrawAgents,
|
|
|
|
show_objs: &dyn ShowObject,
|
2019-04-24 21:16:50 +03:00
|
|
|
) {
|
2019-10-14 04:44:19 +03:00
|
|
|
let ctx = self.draw_ctx();
|
2019-04-24 21:16:50 +03:00
|
|
|
let mut sample_intersection: Option<String> = None;
|
|
|
|
|
2019-04-29 19:27:43 +03:00
|
|
|
g.clear(self.cs.get_def("true background", Color::BLACK));
|
|
|
|
g.redraw(&self.primary.draw_map.boundary_polygon);
|
2019-04-24 21:16:50 +03:00
|
|
|
|
|
|
|
if g.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL && !g.is_screencap() {
|
|
|
|
// Unzoomed mode
|
2019-04-26 08:19:54 +03:00
|
|
|
let layers = show_objs.layers();
|
|
|
|
if layers.show_areas {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_areas);
|
2019-04-24 21:16:50 +03:00
|
|
|
}
|
2019-04-26 08:19:54 +03:00
|
|
|
if layers.show_lanes {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_thick_roads);
|
2019-04-24 21:16:50 +03:00
|
|
|
}
|
2019-04-26 08:19:54 +03:00
|
|
|
if layers.show_intersections {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_unzoomed_intersections);
|
2019-04-24 21:16:50 +03:00
|
|
|
}
|
2019-04-26 08:19:54 +03:00
|
|
|
if layers.show_buildings {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_buildings);
|
2019-04-24 21:16:50 +03:00
|
|
|
}
|
|
|
|
|
2019-05-10 00:00:07 +03:00
|
|
|
if layers.show_extra_shapes {
|
|
|
|
for es in &self.primary.draw_map.extra_shapes {
|
2019-08-15 01:09:54 +03:00
|
|
|
if show_objs.show(&es.get_id()) {
|
2019-05-10 00:00:07 +03:00
|
|
|
es.draw(g, &opts, &ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Still show area/extra shape selection when zoomed out.
|
2019-04-29 19:27:43 +03:00
|
|
|
if let Some(ID::Area(id)) = self.primary.current_selection {
|
2019-04-27 22:02:32 +03:00
|
|
|
g.draw_polygon(
|
2019-04-29 19:27:43 +03:00
|
|
|
self.cs.get("selected"),
|
2019-05-12 01:00:46 +03:00
|
|
|
&ctx.draw_map.get_a(id).get_outline(&ctx.map),
|
2019-04-27 22:02:32 +03:00
|
|
|
);
|
2019-05-10 00:00:07 +03:00
|
|
|
} else if let Some(ID::ExtraShape(id)) = self.primary.current_selection {
|
|
|
|
g.draw_polygon(
|
|
|
|
self.cs.get("selected"),
|
2019-05-12 01:00:46 +03:00
|
|
|
&ctx.draw_map.get_es(id).get_outline(&ctx.map),
|
2019-05-10 00:00:07 +03:00
|
|
|
);
|
2019-04-24 21:16:50 +03:00
|
|
|
}
|
|
|
|
|
2019-08-19 00:41:56 +03:00
|
|
|
let mut cache = self.primary.draw_map.agents.borrow_mut();
|
2019-12-17 05:46:03 +03:00
|
|
|
cache.draw_unzoomed_agents(
|
|
|
|
source,
|
|
|
|
&self.primary.map,
|
2019-12-20 01:52:54 +03:00
|
|
|
&self.agent_cs,
|
2019-12-17 05:46:03 +03:00
|
|
|
g,
|
|
|
|
g.canvas.cam_zoom,
|
2019-12-17 05:51:29 +03:00
|
|
|
Distance::meters(10.0),
|
2019-12-17 05:46:03 +03:00
|
|
|
);
|
2019-04-24 21:16:50 +03:00
|
|
|
} else {
|
2019-04-29 19:27:43 +03:00
|
|
|
let mut cache = self.primary.draw_map.agents.borrow_mut();
|
2019-04-24 21:16:50 +03:00
|
|
|
let objects = self.get_renderables_back_to_front(
|
|
|
|
g.get_screen_bounds(),
|
|
|
|
&g.prerender,
|
|
|
|
&mut cache,
|
2019-04-26 01:23:45 +03:00
|
|
|
source,
|
2019-04-26 02:56:38 +03:00
|
|
|
show_objs,
|
2019-04-24 21:16:50 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut drawn_all_buildings = false;
|
|
|
|
let mut drawn_all_areas = false;
|
|
|
|
|
|
|
|
for obj in objects {
|
2019-08-16 22:38:39 +03:00
|
|
|
obj.draw(g, &opts, &ctx);
|
|
|
|
|
2019-04-24 21:16:50 +03:00
|
|
|
match obj.get_id() {
|
2019-08-19 22:02:47 +03:00
|
|
|
ID::Building(_) => {
|
2019-04-24 21:16:50 +03:00
|
|
|
if !drawn_all_buildings {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_buildings);
|
2019-04-24 21:16:50 +03:00
|
|
|
drawn_all_buildings = true;
|
|
|
|
}
|
2019-08-16 22:38:39 +03:00
|
|
|
}
|
2019-04-24 21:16:50 +03:00
|
|
|
ID::Area(_) => {
|
|
|
|
if !drawn_all_areas {
|
2019-04-29 19:27:43 +03:00
|
|
|
g.redraw(&self.primary.draw_map.draw_all_areas);
|
2019-04-24 21:16:50 +03:00
|
|
|
drawn_all_areas = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
};
|
|
|
|
|
2019-04-29 19:27:43 +03:00
|
|
|
if self.primary.current_selection == Some(obj.get_id()) {
|
2019-04-24 21:16:50 +03:00
|
|
|
g.draw_polygon(
|
2019-05-12 01:15:03 +03:00
|
|
|
self.cs.get_def("selected", Color::RED.alpha(0.7)),
|
2019-05-12 01:00:46 +03:00
|
|
|
&obj.get_outline(&ctx.map),
|
2019-04-24 21:16:50 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.is_screencap() && sample_intersection.is_none() {
|
|
|
|
if let ID::Intersection(id) = obj.get_id() {
|
|
|
|
sample_intersection = Some(format!("_i{}", id.0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(i) = sample_intersection {
|
|
|
|
g.set_screencap_naming_hint(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 23:26:00 +03:00
|
|
|
// Assumes some defaults.
|
|
|
|
pub fn recalculate_current_selection(&mut self, ctx: &EventCtx) {
|
|
|
|
self.primary.current_selection =
|
|
|
|
self.calculate_current_selection(ctx, &self.primary.sim, &ShowEverything::new(), false);
|
|
|
|
}
|
|
|
|
|
2019-04-26 01:23:45 +03:00
|
|
|
// Because we have to sometimes borrow part of self for GetDrawAgents, this just returns the
|
|
|
|
// Option<ID> that the caller should assign. When this monolithic UI nonsense is dismantled,
|
|
|
|
// this weirdness goes away.
|
2019-06-24 23:26:00 +03:00
|
|
|
pub fn calculate_current_selection(
|
2019-04-26 01:23:45 +03:00
|
|
|
&self,
|
2019-06-06 22:12:03 +03:00
|
|
|
ctx: &EventCtx,
|
2019-08-23 00:19:35 +03:00
|
|
|
source: &dyn GetDrawAgents,
|
|
|
|
show_objs: &dyn ShowObject,
|
2019-05-20 01:01:40 +03:00
|
|
|
debug_mode: bool,
|
2019-04-26 01:23:45 +03:00
|
|
|
) -> Option<ID> {
|
2019-06-06 22:12:03 +03:00
|
|
|
// Unzoomed mode. Ignore when debugging areas and extra shapes.
|
|
|
|
if ctx.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL && !debug_mode {
|
|
|
|
return None;
|
|
|
|
}
|
2019-04-30 00:44:43 +03:00
|
|
|
|
2019-06-06 22:12:03 +03:00
|
|
|
let pt = ctx.canvas.get_cursor_in_map_space()?;
|
|
|
|
|
|
|
|
let mut cache = self.primary.draw_map.agents.borrow_mut();
|
|
|
|
let mut objects = self.get_renderables_back_to_front(
|
|
|
|
Circle::new(pt, Distance::meters(3.0)).get_bounds(),
|
|
|
|
ctx.prerender,
|
|
|
|
&mut cache,
|
|
|
|
source,
|
|
|
|
show_objs,
|
|
|
|
);
|
|
|
|
objects.reverse();
|
|
|
|
|
|
|
|
for obj in objects {
|
|
|
|
// In unzoomed mode, can only mouseover areas
|
|
|
|
match obj.get_id() {
|
|
|
|
ID::Area(_) | ID::ExtraShape(_) => {
|
|
|
|
if !debug_mode {
|
2019-05-16 00:27:44 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2019-06-06 22:12:03 +03:00
|
|
|
// Never mouseover these
|
|
|
|
ID::Road(_) => {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
if ctx.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL {
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-16 00:27:44 +03:00
|
|
|
}
|
2019-04-30 00:44:43 +03:00
|
|
|
}
|
2019-06-06 22:12:03 +03:00
|
|
|
if obj.contains_pt(pt, &self.primary.map) {
|
|
|
|
return Some(obj.get_id());
|
|
|
|
}
|
2019-04-24 03:26:10 +03:00
|
|
|
}
|
2019-06-06 22:12:03 +03:00
|
|
|
None
|
2019-04-24 03:26:10 +03:00
|
|
|
}
|
|
|
|
|
2019-02-02 23:49:17 +03:00
|
|
|
// TODO This could probably belong to DrawMap again, but it's annoying to plumb things that
|
|
|
|
// State does, like show_icons_for() and show().
|
2019-02-02 23:35:50 +03:00
|
|
|
fn get_renderables_back_to_front<'a>(
|
|
|
|
&'a self,
|
2019-02-02 09:41:32 +03:00
|
|
|
bounds: Bounds,
|
|
|
|
prerender: &Prerender,
|
2019-02-02 23:35:50 +03:00
|
|
|
agents: &'a mut AgentCache,
|
2019-08-23 00:19:35 +03:00
|
|
|
source: &dyn GetDrawAgents,
|
|
|
|
show_objs: &dyn ShowObject,
|
2019-06-12 19:42:34 +03:00
|
|
|
) -> Vec<&'a (dyn Renderable + 'a)> {
|
2019-04-29 19:27:43 +03:00
|
|
|
let map = &self.primary.map;
|
|
|
|
let draw_map = &self.primary.draw_map;
|
2019-02-02 09:41:32 +03:00
|
|
|
|
2019-06-12 19:42:34 +03:00
|
|
|
let mut areas: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut lanes: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut roads: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut intersections: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut buildings: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut extra_shapes: Vec<&dyn Renderable> = Vec::new();
|
|
|
|
let mut bus_stops: Vec<&dyn Renderable> = Vec::new();
|
2019-02-02 09:41:32 +03:00
|
|
|
let mut agents_on: Vec<Traversable> = Vec::new();
|
|
|
|
|
|
|
|
for id in draw_map.get_matching_objects(bounds) {
|
2019-08-15 01:09:54 +03:00
|
|
|
if !show_objs.show(&id) {
|
2019-02-02 09:41:32 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
match id {
|
2019-06-12 19:42:34 +03:00
|
|
|
ID::Area(id) => areas.push(draw_map.get_a(id)),
|
2019-02-02 09:41:32 +03:00
|
|
|
ID::Lane(id) => {
|
2019-06-12 19:42:34 +03:00
|
|
|
lanes.push(draw_map.get_l(id));
|
2019-05-11 02:21:21 +03:00
|
|
|
agents_on.push(Traversable::Lane(id));
|
|
|
|
for bs in &map.get_l(id).bus_stops {
|
2019-06-12 19:42:34 +03:00
|
|
|
bus_stops.push(draw_map.get_bs(*bs));
|
2019-02-02 09:41:32 +03:00
|
|
|
}
|
|
|
|
}
|
2019-02-12 19:26:23 +03:00
|
|
|
ID::Road(id) => {
|
2019-06-12 19:42:34 +03:00
|
|
|
roads.push(draw_map.get_r(id));
|
2019-02-12 19:26:23 +03:00
|
|
|
}
|
2019-02-02 09:41:32 +03:00
|
|
|
ID::Intersection(id) => {
|
2019-06-12 19:42:34 +03:00
|
|
|
intersections.push(draw_map.get_i(id));
|
2019-05-12 02:14:21 +03:00
|
|
|
for t in &map.get_i(id).turns {
|
|
|
|
agents_on.push(Traversable::Turn(*t));
|
|
|
|
}
|
2019-02-02 09:41:32 +03:00
|
|
|
}
|
|
|
|
// TODO front paths will get drawn over buildings, depending on quadtree order.
|
|
|
|
// probably just need to make them go around other buildings instead of having
|
|
|
|
// two passes through buildings.
|
2019-06-12 19:42:34 +03:00
|
|
|
ID::Building(id) => buildings.push(draw_map.get_b(id)),
|
|
|
|
ID::ExtraShape(id) => extra_shapes.push(draw_map.get_es(id)),
|
2019-02-02 09:41:32 +03:00
|
|
|
|
2020-01-17 21:53:09 +03:00
|
|
|
ID::BusStop(_)
|
|
|
|
| ID::Turn(_)
|
|
|
|
| ID::Car(_)
|
|
|
|
| ID::Pedestrian(_)
|
|
|
|
| ID::PedCrowd(_)
|
|
|
|
| ID::Trip(_) => panic!("{:?} shouldn't be in the quadtree", id),
|
2019-02-02 09:41:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// From background to foreground Z-order
|
2019-06-12 19:42:34 +03:00
|
|
|
let mut borrows: Vec<&dyn Renderable> = Vec::new();
|
2019-02-10 06:28:26 +03:00
|
|
|
borrows.extend(areas);
|
2019-02-02 09:41:32 +03:00
|
|
|
borrows.extend(lanes);
|
2019-02-12 19:26:23 +03:00
|
|
|
borrows.extend(roads);
|
2019-02-02 09:41:32 +03:00
|
|
|
borrows.extend(intersections);
|
|
|
|
borrows.extend(buildings);
|
|
|
|
borrows.extend(extra_shapes);
|
|
|
|
borrows.extend(bus_stops);
|
|
|
|
|
2019-02-02 23:35:50 +03:00
|
|
|
// Expand all of the Traversables into agents, populating the cache if needed.
|
2019-02-02 09:41:32 +03:00
|
|
|
{
|
|
|
|
for on in &agents_on {
|
2020-01-24 23:59:34 +03:00
|
|
|
agents.populate_if_needed(*on, map, source, &self.cs, prerender);
|
2019-02-02 09:41:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-02 23:35:50 +03:00
|
|
|
for on in agents_on {
|
|
|
|
for obj in agents.get(on) {
|
|
|
|
borrows.push(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a stable sort.
|
2019-11-19 00:51:11 +03:00
|
|
|
borrows.sort_by_key(|x| x.get_zorder());
|
2019-02-02 23:35:50 +03:00
|
|
|
|
|
|
|
borrows
|
2019-02-02 09:41:32 +03:00
|
|
|
}
|
2018-06-22 21:01:44 +03:00
|
|
|
}
|
2019-03-28 06:54:04 +03:00
|
|
|
|
2019-04-26 08:19:54 +03:00
|
|
|
pub struct ShowLayers {
|
|
|
|
pub show_buildings: bool,
|
|
|
|
pub show_intersections: bool,
|
|
|
|
pub show_lanes: bool,
|
|
|
|
pub show_areas: bool,
|
|
|
|
pub show_extra_shapes: bool,
|
2019-08-16 22:38:39 +03:00
|
|
|
pub show_labels: bool,
|
2019-04-26 08:19:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ShowLayers {
|
|
|
|
pub fn new() -> ShowLayers {
|
|
|
|
ShowLayers {
|
|
|
|
show_buildings: true,
|
|
|
|
show_intersections: true,
|
|
|
|
show_lanes: true,
|
|
|
|
show_areas: true,
|
|
|
|
show_extra_shapes: true,
|
2019-08-16 22:38:39 +03:00
|
|
|
show_labels: false,
|
2019-04-26 08:19:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 02:56:38 +03:00
|
|
|
pub trait ShowObject {
|
2019-08-15 01:09:54 +03:00
|
|
|
fn show(&self, obj: &ID) -> bool;
|
2019-04-26 08:19:54 +03:00
|
|
|
fn layers(&self) -> &ShowLayers;
|
2019-04-26 02:56:38 +03:00
|
|
|
}
|
|
|
|
|
2019-04-26 08:19:54 +03:00
|
|
|
pub struct ShowEverything {
|
|
|
|
layers: ShowLayers,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShowEverything {
|
|
|
|
pub fn new() -> ShowEverything {
|
|
|
|
ShowEverything {
|
|
|
|
layers: ShowLayers::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:56:38 +03:00
|
|
|
|
|
|
|
impl ShowObject for ShowEverything {
|
2019-08-15 01:09:54 +03:00
|
|
|
fn show(&self, _: &ID) -> bool {
|
2019-04-26 02:56:38 +03:00
|
|
|
true
|
|
|
|
}
|
2019-04-26 08:19:54 +03:00
|
|
|
|
|
|
|
fn layers(&self) -> &ShowLayers {
|
|
|
|
&self.layers
|
|
|
|
}
|
2019-04-26 02:56:38 +03:00
|
|
|
}
|
2019-04-29 19:27:43 +03:00
|
|
|
|
2019-09-19 03:29:34 +03:00
|
|
|
#[derive(Clone)]
|
2019-04-29 19:27:43 +03:00
|
|
|
pub struct Flags {
|
|
|
|
pub sim_flags: SimFlags,
|
|
|
|
pub kml: Option<String>,
|
2019-09-19 03:29:34 +03:00
|
|
|
pub draw_lane_markings: bool,
|
|
|
|
// Number of agents to generate when requested. If unspecified, trips to/from borders will be
|
|
|
|
// included.
|
2019-05-09 20:45:45 +03:00
|
|
|
pub num_agents: Option<usize>,
|
2019-04-29 19:27:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// All of the state that's bound to a specific map+edit has to live here.
|
|
|
|
pub struct PerMapUI {
|
|
|
|
pub map: Map,
|
|
|
|
pub draw_map: DrawMap,
|
|
|
|
pub sim: Sim,
|
|
|
|
|
|
|
|
pub current_selection: Option<ID>,
|
|
|
|
pub current_flags: Flags,
|
2019-09-05 00:26:55 +03:00
|
|
|
pub last_warped_from: Option<(Pt2D, f64)>,
|
2019-04-29 19:27:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PerMapUI {
|
2019-06-07 20:59:13 +03:00
|
|
|
pub fn new(flags: Flags, cs: &ColorScheme, ctx: &mut EventCtx, timer: &mut Timer) -> PerMapUI {
|
|
|
|
let mut mem = MeasureMemory::new();
|
2019-08-16 23:08:53 +03:00
|
|
|
let (map, sim, _) = flags.sim_flags.load(timer);
|
2019-06-07 20:59:13 +03:00
|
|
|
mem.reset("Map and Sim", timer);
|
|
|
|
|
|
|
|
timer.start("draw_map");
|
2019-09-11 02:41:35 +03:00
|
|
|
let draw_map = DrawMap::new(&map, &flags, cs, ctx, timer);
|
2019-06-07 20:59:13 +03:00
|
|
|
timer.stop("draw_map");
|
|
|
|
mem.reset("DrawMap", timer);
|
2019-04-29 19:27:43 +03:00
|
|
|
|
|
|
|
PerMapUI {
|
|
|
|
map,
|
|
|
|
draw_map,
|
|
|
|
sim,
|
|
|
|
current_selection: None,
|
|
|
|
current_flags: flags.clone(),
|
2019-09-05 00:26:55 +03:00
|
|
|
last_warped_from: None,
|
2019-04-29 19:27:43 +03:00
|
|
|
}
|
|
|
|
}
|
2019-05-04 19:38:42 +03:00
|
|
|
|
2019-12-25 01:34:50 +03:00
|
|
|
// Returns whatever was there
|
|
|
|
pub fn clear_sim(&mut self) -> Sim {
|
|
|
|
std::mem::replace(
|
|
|
|
&mut self.sim,
|
|
|
|
Sim::new(
|
|
|
|
&self.map,
|
|
|
|
self.current_flags.sim_flags.opts.clone(),
|
|
|
|
&mut Timer::new("reset simulation"),
|
|
|
|
),
|
|
|
|
)
|
2019-05-04 19:38:42 +03:00
|
|
|
}
|
2019-04-29 19:27:43 +03:00
|
|
|
}
|
2020-02-03 21:21:13 +03:00
|
|
|
|
|
|
|
pub struct SessionState {
|
|
|
|
pub tutorial: Option<TutorialState>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SessionState {
|
|
|
|
pub fn empty() -> SessionState {
|
|
|
|
SessionState { tutorial: None }
|
|
|
|
}
|
|
|
|
}
|