abstreet/game/src/debug/mod.rs

359 lines
12 KiB
Rust
Raw Normal View History

mod associated;
2019-04-26 20:46:41 +03:00
mod color_picker;
2019-04-26 02:35:09 +03:00
mod connected_roads;
mod floodfill;
mod neighborhood_summary;
2019-04-26 02:40:26 +03:00
mod objects;
2019-04-26 02:24:10 +03:00
mod polygons;
2019-06-25 00:48:47 +03:00
mod routes;
2019-04-26 02:02:40 +03:00
use crate::common::CommonState;
use crate::game::{msg, State, Transition, WizardState};
use crate::helpers::ID;
use crate::render::MIN_ZOOM_FOR_DETAIL;
use crate::ui::{ShowLayers, ShowObject, UI};
use abstutil::Timer;
2019-04-26 20:46:41 +03:00
use ezgui::{
hotkey, Color, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, Line,
MenuUnderButton, ModalMenu, Text, Wizard,
2019-04-26 20:46:41 +03:00
};
use geom::Duration;
use std::collections::HashSet;
pub struct DebugMode {
2019-06-22 19:48:42 +03:00
menu: ModalMenu,
general_tools: MenuUnderButton,
common: CommonState,
associated: associated::ShowAssociatedState,
2019-04-26 02:35:09 +03:00
connected_roads: connected_roads::ShowConnectedRoads,
2019-04-26 02:40:26 +03:00
objects: objects::ObjectDebugger,
2019-04-26 08:07:48 +03:00
hidden: HashSet<ID>,
layers: ShowLayers,
search_results: Option<SearchResults>,
neighborhood_summary: neighborhood_summary::NeighborhoodSummary,
2019-06-25 00:48:47 +03:00
all_routes: routes::AllRoutesViewer,
}
impl DebugMode {
pub fn new(ctx: &mut EventCtx, ui: &UI) -> DebugMode {
DebugMode {
2019-06-22 19:48:42 +03:00
menu: ModalMenu::new(
"Debug Mode",
vec![
(hotkey(Key::Num1), "hide buildings"),
(hotkey(Key::Num2), "hide intersections"),
(hotkey(Key::Num3), "hide lanes"),
(hotkey(Key::Num4), "hide areas"),
(hotkey(Key::Num5), "hide extra shapes"),
(hotkey(Key::Num6), "show geometry debug mode"),
(hotkey(Key::Num7), "show labels"),
(hotkey(Key::N), "show neighborhood summaries"),
(hotkey(Key::R), "show route for all agents"),
(None, "show strongly-connected component roads"),
(None, "screenshot everything"),
(hotkey(Key::Slash), "search OSM metadata"),
(hotkey(Key::S), "configure colors"),
],
2019-06-22 19:48:42 +03:00
ctx,
),
general_tools: MenuUnderButton::new(
"assets/ui/hamburger.png",
"General",
vec![
(hotkey(Key::Escape), "return to previous mode"),
(hotkey(Key::F1), "take a screenshot"),
],
2019-10-30 01:45:26 +03:00
0.2,
ctx,
),
common: CommonState::new(ctx),
associated: associated::ShowAssociatedState::Inactive,
2019-04-26 02:35:09 +03:00
connected_roads: connected_roads::ShowConnectedRoads::new(),
2019-04-26 02:40:26 +03:00
objects: objects::ObjectDebugger::new(),
2019-04-26 08:07:48 +03:00
hidden: HashSet::new(),
layers: ShowLayers::new(),
search_results: None,
neighborhood_summary: neighborhood_summary::NeighborhoodSummary::new(
2019-04-29 19:27:43 +03:00
&ui.primary.map,
&ui.primary.draw_map,
ctx.prerender,
&mut Timer::new("set up DebugMode"),
),
2019-06-25 00:48:47 +03:00
all_routes: routes::AllRoutesViewer::Inactive,
}
}
2019-06-22 19:48:42 +03:00
}
2019-06-22 19:48:42 +03:00
impl State for DebugMode {
2019-06-24 03:00:34 +03:00
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
2019-06-22 19:48:42 +03:00
if ctx.redo_mouseover() {
ui.primary.current_selection =
ui.calculate_current_selection(ctx, &ui.primary.sim, self, true);
2019-06-22 19:48:42 +03:00
}
2019-05-02 00:59:20 +03:00
{
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.ids.len()
)));
}
if let routes::AllRoutesViewer::Active(_, ref traces) = self.all_routes {
txt.add(Line(format!("Showing {} routes", traces.len())));
}
self.menu.set_info(ctx, txt);
2019-06-22 19:48:42 +03:00
}
self.menu.event(ctx);
self.general_tools.event(ctx);
2019-06-22 19:48:42 +03:00
ctx.canvas.handle_event(ctx.input);
if let Some(t) = self.common.event(ctx, ui) {
2019-06-24 03:00:34 +03:00
return t;
2019-06-22 19:48:42 +03:00
}
self.associated.event(ui);
2019-05-02 00:59:20 +03:00
if self.general_tools.action("return to previous mode") {
2019-06-24 03:00:34 +03:00
return Transition::Pop;
2019-06-22 19:48:42 +03:00
}
if self.general_tools.action("take a screenshot") {
return Transition::KeepWithMode(EventLoopMode::ScreenCaptureCurrentShot);
}
2019-05-02 00:59:20 +03:00
self.all_routes.event(ui, &mut self.menu, ctx);
2019-06-22 19:48:42 +03:00
match ui.primary.current_selection {
Some(ID::Lane(_)) | Some(ID::Intersection(_)) | Some(ID::ExtraShape(_)) => {
let id = ui.primary.current_selection.clone().unwrap();
2019-06-22 19:48:42 +03:00
if ctx
.input
2019-09-07 23:56:00 +03:00
.contextual_action(Key::H, format!("hide {:?}", id))
2019-06-22 19:48:42 +03:00
{
println!("Hiding {:?}", id);
ui.primary.current_selection = None;
if self.hidden.is_empty() {
self.menu
.push_action(hotkey(Key::H), "unhide everything", ctx);
}
2019-06-22 19:48:42 +03:00
self.hidden.insert(id);
}
}
None => {
if !self.hidden.is_empty() && self.menu.consume_action("unhide everything", ctx) {
2019-06-22 19:48:42 +03:00
self.hidden.clear();
ui.primary.current_selection =
ui.calculate_current_selection(ctx, &ui.primary.sim, self, true);
2019-06-22 19:48:42 +03:00
}
}
_ => {}
}
2019-04-26 02:02:40 +03:00
2019-09-07 23:46:47 +03:00
if let Some(ID::Car(id)) = ui.primary.current_selection {
if ctx
.input
.contextual_action(Key::Backspace, "forcibly kill this car")
{
ui.primary.sim.kill_stuck_car(id, &ui.primary.map);
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
ui.primary.current_selection = None;
} else if ctx
.input
.contextual_action(Key::G, "find front of blockage")
{
return Transition::Push(msg(
"Blockage results",
vec![format!(
"{} is ultimately blocked by {}",
id,
ui.primary.sim.find_blockage_front(id, &ui.primary.map)
)],
));
2019-09-07 23:46:47 +03:00
}
}
2019-06-22 19:48:42 +03:00
self.connected_roads.event(ctx, ui);
self.objects.event(ctx, ui);
self.neighborhood_summary.event(ui, &mut self.menu, ctx);
2019-06-22 19:48:42 +03:00
if let Some(debugger) = polygons::PolygonDebugger::new(ctx, ui) {
2019-06-24 03:00:34 +03:00
return Transition::Push(Box::new(debugger));
2019-06-22 19:48:42 +03:00
}
2019-04-26 02:24:10 +03:00
2019-06-22 19:48:42 +03:00
{
let mut changed = false;
for (label, value) in vec![
("buildings", &mut self.layers.show_buildings),
("intersections", &mut self.layers.show_intersections),
("lanes", &mut self.layers.show_lanes),
("areas", &mut self.layers.show_areas),
("extra shapes", &mut self.layers.show_extra_shapes),
("geometry debug mode", &mut self.layers.geom_debug_mode),
("labels", &mut self.layers.show_labels),
] {
let show = format!("show {}", label);
let hide = format!("hide {}", label);
2019-10-11 23:15:14 +03:00
if *value && self.menu.swap_action(&hide, &show, ctx) {
*value = false;
changed = true;
2019-10-11 23:15:14 +03:00
} else if !*value && self.menu.swap_action(&show, &hide, ctx) {
*value = true;
changed = true;
}
2019-06-22 19:48:42 +03:00
}
2019-04-26 02:24:10 +03:00
2019-06-22 19:48:42 +03:00
if changed {
ui.primary.current_selection =
ui.calculate_current_selection(ctx, &ui.primary.sim, self, true);
2019-06-22 19:48:42 +03:00
}
}
2019-04-26 02:16:21 +03:00
2019-06-22 19:48:42 +03:00
if self.menu.action("screenshot everything") {
let bounds = ui.primary.map.get_bounds();
assert!(bounds.min_x == 0.0 && bounds.min_y == 0.0);
2019-06-24 03:00:34 +03:00
return Transition::KeepWithMode(EventLoopMode::ScreenCaptureEverything {
2019-08-05 02:25:40 +03:00
dir: abstutil::path_pending_screenshots(ui.primary.map.get_name()),
2019-06-24 03:00:34 +03:00
zoom: 3.0,
max_x: bounds.max_x,
max_y: bounds.max_y,
});
2019-06-22 19:48:42 +03:00
}
2019-06-22 19:48:42 +03:00
if self.search_results.is_some() {
2019-10-11 23:15:14 +03:00
if self
.menu
.swap_action("clear OSM search results", "search OSM metadata", ctx)
{
2019-06-22 19:48:42 +03:00
self.search_results = None;
}
2019-10-11 23:15:14 +03:00
} else if self
.menu
.swap_action("search OSM metadata", "clear OSM search results", ctx)
{
return Transition::Push(WizardState::new(Box::new(search_osm)));
2019-06-22 19:48:42 +03:00
} else if self.menu.action("configure colors") {
return Transition::Push(color_picker::ColorChooser::new());
2019-06-22 19:48:42 +03:00
}
2019-08-22 00:27:28 +03:00
if let Some(floodfiller) = floodfill::Floodfiller::new(ctx, ui, &mut self.menu) {
return Transition::Push(floodfiller);
}
2019-06-24 03:00:34 +03:00
Transition::Keep
2019-06-22 19:48:42 +03:00
}
2019-06-07 02:03:22 +03:00
2019-06-22 19:48:42 +03:00
fn draw_default_ui(&self) -> bool {
false
}
2019-06-22 19:48:42 +03:00
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
let mut opts = self.common.draw_options(ui);
2019-08-16 22:38:39 +03:00
opts.label_buildings = self.layers.show_labels;
opts.label_roads = self.layers.show_labels;
2019-06-22 19:48:42 +03:00
opts.geom_debug_mode = self.layers.geom_debug_mode;
for l in &self.connected_roads.lanes {
opts.override_colors.insert(
ID::Lane(*l),
ui.cs.get("something associated with something else"),
);
}
if g.canvas.cam_zoom >= MIN_ZOOM_FOR_DETAIL {
if let Some(ref results) = self.search_results {
for id in &results.ids {
opts.override_colors
.insert(id.clone(), ui.cs.get("search result"));
}
2019-06-22 19:48:42 +03:00
}
}
self.associated
.override_colors(&mut opts.override_colors, ui);
2019-06-22 19:48:42 +03:00
ui.draw(g, opts, &ui.primary.sim, self);
if g.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL {
if let Some(ref results) = self.search_results {
g.redraw(&results.unzoomed);
}
}
2019-06-22 19:48:42 +03:00
self.objects.draw(g, ui);
self.neighborhood_summary.draw(g);
2019-06-25 00:48:47 +03:00
self.all_routes.draw(g, ui);
2019-05-02 00:59:20 +03:00
2019-06-22 19:48:42 +03:00
if !g.is_screencap() {
self.menu.draw(g);
self.general_tools.draw(g);
self.common.draw(g, ui);
}
}
}
impl ShowObject for DebugMode {
fn show(&self, obj: &ID) -> bool {
if self.hidden.contains(obj) {
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
}
}
fn search_osm(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
let filter = wiz.wrap(ctx).input_string("Search for what?")?;
let mut ids = HashSet::new();
let mut batch = GeomBatch::new();
2019-06-22 19:48:42 +03:00
let map = &ui.primary.map;
let color = ui.cs.get_def("search result", Color::RED);
for r in map.all_roads() {
if r.osm_tags
.iter()
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
{
for l in r.all_lanes() {
ids.insert(ID::Lane(l));
2019-06-22 19:48:42 +03:00
}
batch.push(color, r.get_thick_polygon().unwrap());
2019-06-22 19:48:42 +03:00
}
}
for b in map.all_buildings() {
if b.osm_tags
.iter()
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
{
ids.insert(ID::Building(b.id));
batch.push(color, b.polygon.clone());
}
2019-06-22 19:48:42 +03:00
}
let results = SearchResults {
query: filter,
ids,
unzoomed: ctx.prerender.upload(batch),
};
Some(Transition::PopWithData(Box::new(|state, _, _| {
state.downcast_mut::<DebugMode>().unwrap().search_results = Some(results);
})))
2019-06-22 19:48:42 +03:00
}
struct SearchResults {
query: String,
ids: HashSet<ID>,
unzoomed: Drawable,
}