2019-08-20 23:32:27 +03:00
|
|
|
mod floodfill;
|
2019-04-26 02:40:26 +03:00
|
|
|
mod objects;
|
2019-04-26 02:24:10 +03:00
|
|
|
mod polygons;
|
2019-04-26 02:02:40 +03:00
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
use crate::app::{App, ShowLayers, ShowObject};
|
2020-02-28 00:22:57 +03:00
|
|
|
use crate::colors;
|
2020-03-29 00:55:54 +03:00
|
|
|
use crate::common::{tool_panel, CommonState, ContextualActions};
|
2020-02-04 23:52:47 +03:00
|
|
|
use crate::game::{msg, DrawBaselayer, State, Transition, WizardState};
|
2019-04-29 19:56:01 +03:00
|
|
|
use crate::helpers::ID;
|
2020-01-22 21:41:58 +03:00
|
|
|
use crate::managed::{WrappedComposite, WrappedOutcome};
|
2020-03-29 03:09:36 +03:00
|
|
|
use crate::render::{calculate_corners, DrawOptions};
|
2020-03-19 00:41:34 +03:00
|
|
|
use abstutil::Timer;
|
2019-04-26 20:46:41 +03:00
|
|
|
use ezgui::{
|
2020-03-21 01:22:45 +03:00
|
|
|
hotkey, lctrl, Btn, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx,
|
2020-03-21 09:41:07 +03:00
|
|
|
HorizontalAlignment, Key, Line, Outcome, Text, VerticalAlignment, Widget, Wizard,
|
2019-04-26 20:46:41 +03:00
|
|
|
};
|
2020-03-29 03:09:36 +03:00
|
|
|
use geom::{Duration, Pt2D};
|
2020-03-19 00:41:34 +03:00
|
|
|
use map_model::{IntersectionID, NORMAL_LANE_THICKNESS};
|
2020-03-21 23:10:50 +03:00
|
|
|
use sim::{Sim, TripID};
|
2019-04-29 23:55:59 +03:00
|
|
|
use std::collections::HashSet;
|
2019-04-26 01:50:16 +03:00
|
|
|
|
|
|
|
pub struct DebugMode {
|
2020-02-28 00:22:57 +03:00
|
|
|
composite: Composite,
|
2019-04-29 02:29:19 +03:00
|
|
|
common: CommonState,
|
2020-01-22 21:41:58 +03:00
|
|
|
tool_panel: WrappedComposite,
|
2019-04-26 02:40:26 +03:00
|
|
|
objects: objects::ObjectDebugger,
|
2019-04-26 08:07:48 +03:00
|
|
|
hidden: HashSet<ID>,
|
2019-04-26 08:19:54 +03:00
|
|
|
layers: ShowLayers,
|
2019-06-27 00:38:16 +03:00
|
|
|
search_results: Option<SearchResults>,
|
2020-03-19 00:41:34 +03:00
|
|
|
all_routes: Option<(usize, Drawable)>,
|
2020-02-18 19:40:05 +03:00
|
|
|
|
|
|
|
highlighted_agents: Option<(IntersectionID, Drawable)>,
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DebugMode {
|
2019-12-04 02:08:46 +03:00
|
|
|
pub fn new(ctx: &mut EventCtx) -> DebugMode {
|
2019-04-26 01:50:16 +03:00
|
|
|
DebugMode {
|
2020-02-28 00:22:57 +03:00
|
|
|
composite: Composite::new(
|
2020-03-21 09:41:07 +03:00
|
|
|
Widget::col(vec![
|
|
|
|
Widget::row(vec![
|
2020-03-27 01:42:37 +03:00
|
|
|
Line("Debug Mode").small_heading().draw(ctx),
|
2020-03-21 01:22:45 +03:00
|
|
|
Btn::text_fg("X")
|
|
|
|
.build_def(ctx, hotkey(Key::Escape))
|
|
|
|
.align_right(),
|
2020-02-28 00:22:57 +03:00
|
|
|
]),
|
2020-03-14 09:06:52 +03:00
|
|
|
Text::new().draw(ctx).named("current info"),
|
2020-03-21 09:41:07 +03:00
|
|
|
Widget::checkbox(ctx, "show buildings", hotkey(Key::Num1), true),
|
|
|
|
Widget::checkbox(ctx, "show intersections", hotkey(Key::Num2), true),
|
|
|
|
Widget::checkbox(ctx, "show lanes", hotkey(Key::Num3), true),
|
|
|
|
Widget::checkbox(ctx, "show areas", hotkey(Key::Num4), true),
|
|
|
|
Widget::checkbox(ctx, "show extra shapes", hotkey(Key::Num5), true),
|
|
|
|
Widget::checkbox(ctx, "show labels", hotkey(Key::Num6), false),
|
|
|
|
Widget::checkbox(ctx, "show route for all agents", hotkey(Key::R), false),
|
|
|
|
Widget::col(
|
2020-02-28 00:22:57 +03:00
|
|
|
vec![
|
|
|
|
(lctrl(Key::H), "unhide everything"),
|
|
|
|
(None, "screenshot everything"),
|
|
|
|
(hotkey(Key::Slash), "search OSM metadata"),
|
|
|
|
(lctrl(Key::Slash), "clear OSM search results"),
|
|
|
|
(hotkey(Key::O), "save sim state"),
|
|
|
|
(hotkey(Key::Y), "load previous sim state"),
|
|
|
|
(hotkey(Key::U), "load next sim state"),
|
|
|
|
(None, "pick a savestate to load"),
|
|
|
|
]
|
|
|
|
.into_iter()
|
2020-03-21 01:22:45 +03:00
|
|
|
.map(|(key, action)| Btn::text_fg(action).build_def(ctx, key))
|
2020-02-28 00:22:57 +03:00
|
|
|
.collect(),
|
2020-03-19 00:41:34 +03:00
|
|
|
),
|
2020-02-28 00:22:57 +03:00
|
|
|
])
|
|
|
|
.padding(10)
|
|
|
|
.bg(colors::PANEL_BG),
|
|
|
|
)
|
2020-03-19 00:41:34 +03:00
|
|
|
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
2020-02-28 00:22:57 +03:00
|
|
|
.build(ctx),
|
2019-12-16 21:10:01 +03:00
|
|
|
common: CommonState::new(),
|
2020-01-20 02:20:19 +03:00
|
|
|
tool_panel: tool_panel(ctx),
|
2019-04-26 02:40:26 +03:00
|
|
|
objects: objects::ObjectDebugger::new(),
|
2019-04-26 08:07:48 +03:00
|
|
|
hidden: HashSet::new(),
|
2019-04-26 08:19:54 +03:00
|
|
|
layers: ShowLayers::new(),
|
2019-04-26 20:21:05 +03:00
|
|
|
search_results: None,
|
2020-03-19 00:41:34 +03:00
|
|
|
all_routes: None,
|
2020-02-18 19:40:05 +03:00
|
|
|
highlighted_agents: None,
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 00:22:57 +03:00
|
|
|
|
|
|
|
fn reset_info(&mut self, ctx: &mut EventCtx) {
|
|
|
|
let mut txt = Text::new();
|
|
|
|
if !self.hidden.is_empty() {
|
|
|
|
txt.add(Line(format!("Hiding {} things", self.hidden.len())));
|
|
|
|
}
|
|
|
|
if let Some(ref results) = self.search_results {
|
|
|
|
txt.add(Line(format!(
|
|
|
|
"Search for {} has {} results",
|
|
|
|
results.query, results.num_matches
|
|
|
|
)));
|
|
|
|
}
|
2020-03-19 00:41:34 +03:00
|
|
|
if let Some((n, _)) = self.all_routes {
|
|
|
|
txt.add(Line(format!(
|
|
|
|
"Showing {} routes",
|
|
|
|
abstutil::prettyprint_usize(n)
|
|
|
|
)));
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
2020-03-14 09:06:52 +03:00
|
|
|
self.composite
|
|
|
|
.replace(ctx, "current info", txt.draw(ctx).named("current info"));
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
|
2019-06-22 19:48:42 +03:00
|
|
|
impl State for DebugMode {
|
2020-03-02 20:51:20 +03:00
|
|
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
2020-02-28 00:22:57 +03:00
|
|
|
ctx.canvas_movement();
|
|
|
|
|
2019-06-22 19:48:42 +03:00
|
|
|
if ctx.redo_mouseover() {
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.current_selection =
|
|
|
|
app.calculate_current_selection(ctx, &app.primary.sim, self, true, false);
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-05-02 00:59:20 +03:00
|
|
|
|
2020-02-28 00:22:57 +03:00
|
|
|
match self.composite.event(ctx) {
|
|
|
|
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
|
|
|
"X" => {
|
|
|
|
return Transition::Pop;
|
2019-12-07 20:38:06 +03:00
|
|
|
}
|
2020-02-28 00:22:57 +03:00
|
|
|
"save sim state" => {
|
|
|
|
ctx.loading_screen("savestate", |_, timer| {
|
|
|
|
timer.start("save sim state");
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.sim.save();
|
2020-02-28 00:22:57 +03:00
|
|
|
timer.stop("save sim state");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
"load previous sim state" => {
|
|
|
|
if let Some(t) =
|
|
|
|
ctx.loading_screen("load previous savestate", |ctx, mut timer| {
|
2020-03-02 20:51:20 +03:00
|
|
|
let prev_state = app
|
2020-02-28 00:22:57 +03:00
|
|
|
.primary
|
|
|
|
.sim
|
2020-03-02 20:51:20 +03:00
|
|
|
.find_previous_savestate(app.primary.sim.time());
|
2020-02-28 00:22:57 +03:00
|
|
|
match prev_state.clone().and_then(|path| {
|
2020-03-02 20:51:20 +03:00
|
|
|
Sim::load_savestate(path, &app.primary.map, &mut timer).ok()
|
2020-02-28 00:22:57 +03:00
|
|
|
}) {
|
|
|
|
Some(new_sim) => {
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.sim = new_sim;
|
|
|
|
app.recalculate_current_selection(ctx);
|
2020-02-28 00:22:57 +03:00
|
|
|
None
|
|
|
|
}
|
|
|
|
None => Some(Transition::Push(msg(
|
|
|
|
"Error",
|
|
|
|
vec![format!(
|
|
|
|
"Couldn't load previous savestate {:?}",
|
|
|
|
prev_state
|
|
|
|
)],
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
{
|
|
|
|
return t;
|
2019-12-07 20:38:06 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 00:22:57 +03:00
|
|
|
"load next sim state" => {
|
|
|
|
if let Some(t) = ctx.loading_screen("load next savestate", |ctx, mut timer| {
|
2020-03-02 20:51:20 +03:00
|
|
|
let next_state =
|
|
|
|
app.primary.sim.find_next_savestate(app.primary.sim.time());
|
2020-02-28 00:22:57 +03:00
|
|
|
match next_state.clone().and_then(|path| {
|
2020-03-02 20:51:20 +03:00
|
|
|
Sim::load_savestate(path, &app.primary.map, &mut timer).ok()
|
2020-02-28 00:22:57 +03:00
|
|
|
}) {
|
|
|
|
Some(new_sim) => {
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.sim = new_sim;
|
|
|
|
app.recalculate_current_selection(ctx);
|
2020-02-28 00:22:57 +03:00
|
|
|
None
|
|
|
|
}
|
|
|
|
None => Some(Transition::Push(msg(
|
|
|
|
"Error",
|
|
|
|
vec![format!("Couldn't load next savestate {:?}", next_state)],
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
return t;
|
2019-10-11 23:09:32 +03:00
|
|
|
}
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2020-02-28 00:22:57 +03:00
|
|
|
"pick a savestate to load" => {
|
|
|
|
return Transition::Push(WizardState::new(Box::new(load_savestate)));
|
|
|
|
}
|
|
|
|
"unhide everything" => {
|
2019-06-22 19:48:42 +03:00
|
|
|
self.hidden.clear();
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.current_selection =
|
|
|
|
app.calculate_current_selection(ctx, &app.primary.sim, self, true, false);
|
2020-02-28 00:22:57 +03:00
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
|
|
|
"search OSM metadata" => {
|
|
|
|
return Transition::Push(WizardState::new(Box::new(search_osm)));
|
|
|
|
}
|
|
|
|
"clear OSM search results" => {
|
|
|
|
self.search_results = None;
|
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
|
|
|
"screenshot everything" => {
|
2020-03-02 20:51:20 +03:00
|
|
|
let bounds = app.primary.map.get_bounds();
|
2020-02-28 00:22:57 +03:00
|
|
|
assert!(bounds.min_x == 0.0 && bounds.min_y == 0.0);
|
|
|
|
return Transition::KeepWithMode(EventLoopMode::ScreenCaptureEverything {
|
2020-03-02 20:51:20 +03:00
|
|
|
dir: abstutil::path_pending_screenshots(app.primary.map.get_name()),
|
2020-02-28 00:22:57 +03:00
|
|
|
zoom: 3.0,
|
|
|
|
max_x: bounds.max_x,
|
|
|
|
max_y: bounds.max_y,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
None => {}
|
|
|
|
}
|
2020-03-19 00:41:34 +03:00
|
|
|
// TODO We should really recalculate current_selection when these change. Meh.
|
|
|
|
self.layers.show_buildings = self.composite.is_checked("show buildings");
|
|
|
|
self.layers.show_intersections = self.composite.is_checked("show intersections");
|
|
|
|
self.layers.show_lanes = self.composite.is_checked("show lanes");
|
|
|
|
self.layers.show_areas = self.composite.is_checked("show areas");
|
|
|
|
self.layers.show_extra_shapes = self.composite.is_checked("show extra shapes");
|
|
|
|
self.layers.show_labels = self.composite.is_checked("show labels");
|
2020-03-19 05:17:54 +03:00
|
|
|
if self.composite.is_checked("show route for all agents") {
|
|
|
|
if self.all_routes.is_none() {
|
|
|
|
self.all_routes = Some(calc_all_routes(ctx, app));
|
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if self.all_routes.is_some() {
|
|
|
|
self.all_routes = None;
|
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:02:40 +03:00
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
if let Some(ID::Intersection(id)) = app.primary.current_selection {
|
2020-02-18 19:40:05 +03:00
|
|
|
if self
|
|
|
|
.highlighted_agents
|
|
|
|
.as_ref()
|
|
|
|
.map(|(i, _)| id != *i)
|
|
|
|
.unwrap_or(true)
|
|
|
|
{
|
|
|
|
let mut batch = GeomBatch::new();
|
2020-03-02 20:51:20 +03:00
|
|
|
for a in app.primary.sim.get_accepted_agents(id) {
|
2020-02-18 19:40:05 +03:00
|
|
|
batch.push(
|
2020-03-02 20:51:20 +03:00
|
|
|
app.cs.get("something associated with something else"),
|
|
|
|
app.primary
|
2020-02-18 19:40:05 +03:00
|
|
|
.draw_map
|
|
|
|
.get_obj(
|
|
|
|
ID::from_agent(a),
|
2020-03-02 20:51:20 +03:00
|
|
|
app,
|
|
|
|
&mut app.primary.draw_map.agents.borrow_mut(),
|
2020-02-18 19:40:05 +03:00
|
|
|
ctx.prerender,
|
|
|
|
)
|
|
|
|
.unwrap()
|
2020-03-02 20:51:20 +03:00
|
|
|
.get_outline(&app.primary.map),
|
2020-02-18 19:40:05 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
self.highlighted_agents = Some((id, ctx.upload(batch)));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.highlighted_agents = None;
|
|
|
|
}
|
|
|
|
|
2020-03-29 03:48:50 +03:00
|
|
|
self.objects.event(ctx);
|
2019-06-22 19:48:42 +03:00
|
|
|
|
2020-03-29 00:55:54 +03:00
|
|
|
if let Some(t) = self.common.event(ctx, app, None, &mut Actions {}) {
|
2019-12-12 02:25:43 +03:00
|
|
|
return t;
|
|
|
|
}
|
2020-03-02 20:51:20 +03:00
|
|
|
match self.tool_panel.event(ctx, app) {
|
2020-01-22 21:41:58 +03:00
|
|
|
Some(WrappedOutcome::Transition(t)) => t,
|
|
|
|
Some(WrappedOutcome::Clicked(x)) => match x.as_ref() {
|
2019-12-16 21:10:01 +03:00
|
|
|
"back" => Transition::Pop,
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
None => Transition::Keep,
|
|
|
|
}
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-06-07 02:03:22 +03:00
|
|
|
|
2020-02-04 23:52:47 +03:00
|
|
|
fn draw_baselayer(&self) -> DrawBaselayer {
|
|
|
|
DrawBaselayer::Custom
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
2020-03-08 21:27:00 +03:00
|
|
|
let mut opts = DrawOptions::new();
|
2019-08-16 22:38:39 +03:00
|
|
|
opts.label_buildings = self.layers.show_labels;
|
|
|
|
opts.label_roads = self.layers.show_labels;
|
2020-03-02 20:51:20 +03:00
|
|
|
app.draw(g, opts, &app.primary.sim, self);
|
2019-06-27 00:38:16 +03:00
|
|
|
|
2020-01-25 00:38:15 +03:00
|
|
|
if let Some(ref results) = self.search_results {
|
|
|
|
g.redraw(&results.draw);
|
2019-06-27 00:38:16 +03:00
|
|
|
}
|
2020-02-18 19:40:05 +03:00
|
|
|
if let Some((_, ref draw)) = self.highlighted_agents {
|
|
|
|
g.redraw(draw);
|
|
|
|
}
|
2019-06-27 00:38:16 +03:00
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
self.objects.draw(g, app);
|
2020-03-19 00:41:34 +03:00
|
|
|
if let Some((_, ref draw)) = self.all_routes {
|
|
|
|
g.redraw(draw);
|
|
|
|
}
|
2019-05-02 00:59:20 +03:00
|
|
|
|
2019-06-22 19:48:42 +03:00
|
|
|
if !g.is_screencap() {
|
2020-02-28 00:22:57 +03:00
|
|
|
self.composite.draw(g);
|
2020-03-02 20:51:20 +03:00
|
|
|
self.common.draw(g, app);
|
2019-12-16 21:10:01 +03:00
|
|
|
self.tool_panel.draw(g);
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:56:38 +03:00
|
|
|
|
|
|
|
impl ShowObject for DebugMode {
|
2019-08-15 01:09:54 +03:00
|
|
|
fn show(&self, obj: &ID) -> bool {
|
|
|
|
if self.hidden.contains(obj) {
|
2019-04-26 08:19:54 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
match obj {
|
|
|
|
ID::Road(_) | ID::Lane(_) => self.layers.show_lanes,
|
|
|
|
ID::Building(_) => self.layers.show_buildings,
|
|
|
|
ID::Intersection(_) => self.layers.show_intersections,
|
|
|
|
ID::ExtraShape(_) => self.layers.show_extra_shapes,
|
|
|
|
ID::Area(_) => self.layers.show_areas,
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn layers(&self) -> &ShowLayers {
|
|
|
|
&self.layers
|
2019-04-26 02:56:38 +03:00
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
fn search_osm(wiz: &mut Wizard, ctx: &mut EventCtx, app: &mut App) -> Option<Transition> {
|
2019-08-07 23:28:46 +03:00
|
|
|
let filter = wiz.wrap(ctx).input_string("Search for what?")?;
|
2020-01-25 00:38:15 +03:00
|
|
|
let mut num_matches = 0;
|
2019-08-07 23:28:46 +03:00
|
|
|
let mut batch = GeomBatch::new();
|
2019-06-22 19:48:42 +03:00
|
|
|
|
2020-01-25 00:38:15 +03:00
|
|
|
// TODO Case insensitive
|
2020-03-02 20:51:20 +03:00
|
|
|
let map = &app.primary.map;
|
|
|
|
let color = app.cs.get_def("search result", Color::RED);
|
2019-08-07 23:28:46 +03:00
|
|
|
for r in map.all_roads() {
|
|
|
|
if r.osm_tags
|
|
|
|
.iter()
|
|
|
|
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
|
|
|
|
{
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches += 1;
|
2020-01-29 04:26:14 +03:00
|
|
|
batch.push(color, r.get_thick_polygon(map).unwrap());
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:28:46 +03:00
|
|
|
for b in map.all_buildings() {
|
|
|
|
if b.osm_tags
|
|
|
|
.iter()
|
|
|
|
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
|
2020-01-29 02:57:45 +03:00
|
|
|
|| b.amenities
|
|
|
|
.iter()
|
|
|
|
.any(|(n, a)| n.contains(&filter) || a.contains(&filter))
|
2019-08-07 23:28:46 +03:00
|
|
|
{
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches += 1;
|
2019-08-07 23:28:46 +03:00
|
|
|
batch.push(color, b.polygon.clone());
|
|
|
|
}
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-11-14 21:54:11 +03:00
|
|
|
for a in map.all_areas() {
|
|
|
|
if a.osm_tags
|
|
|
|
.iter()
|
|
|
|
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
|
|
|
|
{
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches += 1;
|
2019-11-14 21:54:11 +03:00
|
|
|
batch.push(color, a.polygon.clone());
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:28:46 +03:00
|
|
|
|
|
|
|
let results = SearchResults {
|
|
|
|
query: filter,
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches,
|
|
|
|
draw: batch.upload(ctx),
|
2019-08-07 23:28:46 +03:00
|
|
|
};
|
|
|
|
|
2020-02-28 00:22:57 +03:00
|
|
|
Some(Transition::PopWithData(Box::new(|state, _, ctx| {
|
|
|
|
let mut mode = state.downcast_mut::<DebugMode>().unwrap();
|
|
|
|
mode.search_results = Some(results);
|
|
|
|
mode.reset_info(ctx);
|
2019-08-07 23:28:46 +03:00
|
|
|
})))
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-06-27 00:38:16 +03:00
|
|
|
|
|
|
|
struct SearchResults {
|
|
|
|
query: String,
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches: usize,
|
|
|
|
draw: Drawable,
|
2019-06-27 00:38:16 +03:00
|
|
|
}
|
2019-12-07 20:38:06 +03:00
|
|
|
|
2020-03-02 20:51:20 +03:00
|
|
|
fn load_savestate(wiz: &mut Wizard, ctx: &mut EventCtx, app: &mut App) -> Option<Transition> {
|
2019-12-07 20:38:06 +03:00
|
|
|
let ss = wiz.wrap(ctx).choose_string("Load which savestate?", || {
|
2020-03-02 20:51:20 +03:00
|
|
|
abstutil::list_all_objects(app.primary.sim.save_dir())
|
2019-12-07 20:38:06 +03:00
|
|
|
})?;
|
|
|
|
// TODO Oh no, we have to do path construction here :(
|
2020-03-02 20:51:20 +03:00
|
|
|
let ss_path = format!("{}/{}.bin", app.primary.sim.save_dir(), ss);
|
2019-12-07 20:38:06 +03:00
|
|
|
|
|
|
|
ctx.loading_screen("load savestate", |ctx, mut timer| {
|
2020-03-02 20:51:20 +03:00
|
|
|
app.primary.sim = Sim::load_savestate(ss_path, &app.primary.map, &mut timer)
|
2019-12-18 05:08:59 +03:00
|
|
|
.expect("Can't load savestate");
|
2020-03-02 20:51:20 +03:00
|
|
|
app.recalculate_current_selection(ctx);
|
2019-12-07 20:38:06 +03:00
|
|
|
});
|
|
|
|
Some(Transition::Pop)
|
|
|
|
}
|
2020-03-19 00:41:34 +03:00
|
|
|
|
|
|
|
fn calc_all_routes(ctx: &EventCtx, app: &mut App) -> (usize, Drawable) {
|
|
|
|
let trips: Vec<TripID> = app
|
|
|
|
.primary
|
|
|
|
.sim
|
|
|
|
.get_trip_positions(&app.primary.map)
|
|
|
|
.canonical_pt_per_trip
|
|
|
|
.keys()
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
let mut batch = GeomBatch::new();
|
|
|
|
let mut cnt = 0;
|
|
|
|
let sim = &app.primary.sim;
|
|
|
|
let map = &app.primary.map;
|
|
|
|
for maybe_trace in
|
|
|
|
Timer::new("calculate all routes").parallelize("route to geometry", trips, |trip| {
|
|
|
|
sim.trip_to_agent(trip)
|
|
|
|
.ok()
|
|
|
|
.and_then(|agent| sim.trace_route(agent, map, None))
|
|
|
|
.map(|trace| trace.make_polygons(NORMAL_LANE_THICKNESS))
|
|
|
|
})
|
|
|
|
{
|
|
|
|
if let Some(t) = maybe_trace {
|
|
|
|
cnt += 1;
|
|
|
|
batch.push(app.cs.get("route"), t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(cnt, ctx.upload(batch))
|
|
|
|
}
|
2020-03-29 00:55:54 +03:00
|
|
|
|
|
|
|
struct Actions;
|
|
|
|
impl ContextualActions for Actions {
|
|
|
|
fn actions(&self, app: &App, id: ID) -> Vec<(Key, String)> {
|
2020-03-29 03:09:36 +03:00
|
|
|
let mut actions = vec![(Key::D, "debug".to_string())];
|
|
|
|
match id {
|
|
|
|
ID::Lane(l) => {
|
|
|
|
actions.push((Key::H, "hide this".to_string()));
|
|
|
|
if app.primary.map.get_l(l).lane_type.supports_any_movement() {
|
|
|
|
actions.push((Key::F, "floodfill from this lane".to_string()));
|
|
|
|
actions.push((Key::S, "show strongly-connected components".to_string()));
|
|
|
|
}
|
|
|
|
actions.push((Key::X, "debug lane geometry".to_string()));
|
|
|
|
actions.push((Key::F2, "debug lane triangles geometry".to_string()));
|
|
|
|
}
|
|
|
|
ID::Intersection(_) => {
|
|
|
|
actions.push((Key::H, "hide this".to_string()));
|
|
|
|
actions.push((Key::X, "debug intersection geometry".to_string()));
|
|
|
|
actions.push((Key::F2, "debug sidewalk corners".to_string()));
|
|
|
|
}
|
|
|
|
ID::ExtraShape(_) => {
|
|
|
|
actions.push((Key::H, "hide this".to_string()));
|
|
|
|
}
|
|
|
|
ID::Car(_) => {
|
|
|
|
actions.push((Key::Backspace, "forcibly kill this car".to_string()));
|
|
|
|
actions.push((Key::G, "find front of blockage".to_string()));
|
|
|
|
}
|
|
|
|
ID::Area(_) => {
|
|
|
|
actions.push((Key::X, "debug area geometry".to_string()));
|
|
|
|
actions.push((Key::F2, "debug area triangles".to_string()));
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
actions
|
2020-03-29 00:55:54 +03:00
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
|
2020-03-29 00:55:54 +03:00
|
|
|
fn execute(&mut self, ctx: &mut EventCtx, app: &mut App, id: ID, action: String) -> Transition {
|
2020-03-29 03:09:36 +03:00
|
|
|
match (id, action.as_ref()) {
|
|
|
|
(id, "hide this") => Transition::KeepWithData(Box::new(|state, app, ctx| {
|
|
|
|
let mode = state.downcast_mut::<DebugMode>().unwrap();
|
|
|
|
println!("Hiding {:?}", id);
|
|
|
|
app.primary.current_selection = None;
|
|
|
|
mode.hidden.insert(id);
|
|
|
|
mode.reset_info(ctx);
|
|
|
|
})),
|
|
|
|
(id, "debug") => {
|
|
|
|
objects::ObjectDebugger::dump_debug(
|
|
|
|
id,
|
|
|
|
&app.primary.map,
|
|
|
|
&app.primary.sim,
|
|
|
|
&app.primary.draw_map,
|
|
|
|
);
|
|
|
|
Transition::Keep
|
|
|
|
}
|
|
|
|
(ID::Car(c), "forcibly kill this car") => {
|
|
|
|
app.primary.sim.kill_stuck_car(c, &app.primary.map);
|
|
|
|
app.primary
|
|
|
|
.sim
|
|
|
|
.normal_step(&app.primary.map, Duration::seconds(0.1));
|
|
|
|
app.primary.current_selection = None;
|
|
|
|
Transition::Keep
|
|
|
|
}
|
|
|
|
(ID::Car(c), "find front of blockage") => Transition::Push(msg(
|
|
|
|
"Blockage results",
|
|
|
|
vec![format!(
|
|
|
|
"{} is ultimately blocked by {}",
|
|
|
|
c,
|
|
|
|
app.primary.sim.find_blockage_front(c, &app.primary.map)
|
|
|
|
)],
|
|
|
|
)),
|
|
|
|
(ID::Lane(l), "floodfill from this lane") => {
|
|
|
|
Transition::Push(floodfill::Floodfiller::floodfill(ctx, app, l))
|
|
|
|
}
|
|
|
|
(ID::Lane(l), "show strongly-connected components") => {
|
|
|
|
Transition::Push(floodfill::Floodfiller::scc(ctx, app, l))
|
|
|
|
}
|
|
|
|
(ID::Intersection(i), "debug intersection geometry") => {
|
|
|
|
let pts = app.primary.map.get_i(i).polygon.points();
|
|
|
|
let mut pts_without_last = pts.clone();
|
|
|
|
pts_without_last.pop();
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"point",
|
|
|
|
pts.iter().map(|pt| polygons::Item::Point(*pt)).collect(),
|
|
|
|
Some(Pt2D::center(&pts_without_last)),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
(ID::Intersection(i), "debug sidewalk corners") => {
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"corner",
|
|
|
|
calculate_corners(
|
|
|
|
app.primary.map.get_i(i),
|
|
|
|
&app.primary.map,
|
|
|
|
&mut Timer::new("calculate corners"),
|
|
|
|
)
|
|
|
|
.into_iter()
|
|
|
|
.map(|poly| polygons::Item::Polygon(poly))
|
|
|
|
.collect(),
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
(ID::Lane(l), "debug lane geometry") => {
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"point",
|
|
|
|
app.primary
|
|
|
|
.map
|
|
|
|
.get_l(l)
|
|
|
|
.lane_center_pts
|
|
|
|
.points()
|
|
|
|
.iter()
|
|
|
|
.map(|pt| polygons::Item::Point(*pt))
|
|
|
|
.collect(),
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
(ID::Lane(l), "debug lane triangles geometry") => {
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"triangle",
|
|
|
|
app.primary
|
|
|
|
.draw_map
|
|
|
|
.get_l(l)
|
|
|
|
.polygon
|
|
|
|
.triangles()
|
|
|
|
.into_iter()
|
|
|
|
.map(|tri| polygons::Item::Triangle(tri))
|
|
|
|
.collect(),
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
(ID::Area(a), "debug area geometry") => {
|
|
|
|
let pts = &app.primary.map.get_a(a).polygon.points();
|
|
|
|
let center = if pts[0] == *pts.last().unwrap() {
|
|
|
|
// TODO The center looks really wrong for Volunteer Park and others, but I
|
|
|
|
// think it's because they have many points along some edges.
|
|
|
|
Pt2D::center(&pts.iter().skip(1).cloned().collect())
|
|
|
|
} else {
|
|
|
|
Pt2D::center(pts)
|
|
|
|
};
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"point",
|
|
|
|
pts.iter().map(|pt| polygons::Item::Point(*pt)).collect(),
|
|
|
|
Some(center),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
(ID::Area(a), "debug area triangles") => {
|
|
|
|
Transition::Push(polygons::PolygonDebugger::new(
|
|
|
|
ctx,
|
|
|
|
"triangle",
|
|
|
|
app.primary
|
|
|
|
.map
|
|
|
|
.get_a(a)
|
|
|
|
.polygon
|
|
|
|
.triangles()
|
|
|
|
.into_iter()
|
|
|
|
.map(|tri| polygons::Item::Triangle(tri))
|
|
|
|
.collect(),
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2020-03-29 00:55:54 +03:00
|
|
|
}
|
|
|
|
}
|