2020-10-06 06:26:11 +03:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2021-01-02 21:28:00 +03:00
|
|
|
use abstio::MapName;
|
|
|
|
use abstutil::{Parallelism, Tags, Timer};
|
2020-11-13 01:21:08 +03:00
|
|
|
use geom::{Distance, Pt2D};
|
2020-11-23 21:35:08 +03:00
|
|
|
use map_gui::load::MapLoader;
|
2020-11-23 20:27:07 +03:00
|
|
|
use map_gui::options::OptionsPanel;
|
2021-01-01 23:46:25 +03:00
|
|
|
use map_gui::render::{calculate_corners, DrawMap, DrawOptions};
|
2020-11-24 04:56:56 +03:00
|
|
|
use map_gui::tools::{ChooseSomething, PopupMsg, PromptInput};
|
2020-12-27 01:38:43 +03:00
|
|
|
use map_gui::ID;
|
2020-12-01 02:15:13 +03:00
|
|
|
use map_model::{osm, ControlTrafficSignal, IntersectionID, NORMAL_LANE_THICKNESS};
|
|
|
|
use sim::Sim;
|
2020-08-27 20:37:04 +03:00
|
|
|
use widgetry::{
|
2020-12-27 01:38:43 +03:00
|
|
|
lctrl, Btn, Cached, Checkbox, Choice, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch,
|
2021-01-06 21:52:22 +03:00
|
|
|
GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, ScreenDims, State, Text, UpdateType,
|
2020-12-27 01:38:43 +03:00
|
|
|
VerticalAlignment, Widget,
|
2020-08-27 20:37:04 +03:00
|
|
|
};
|
2019-04-26 01:50:16 +03:00
|
|
|
|
2020-11-24 04:56:56 +03:00
|
|
|
use crate::app::{App, ShowLayers, ShowObject, Transition};
|
2020-11-24 10:39:55 +03:00
|
|
|
use crate::common::{tool_panel, CommonState};
|
|
|
|
use crate::info::ContextualActions;
|
2020-10-06 06:26:11 +03:00
|
|
|
use crate::sandbox::GameplayMode;
|
|
|
|
|
2020-11-21 01:19:19 +03:00
|
|
|
mod blocked_by;
|
2020-10-05 02:09:20 +03:00
|
|
|
mod floodfill;
|
|
|
|
mod objects;
|
|
|
|
pub mod path_counter;
|
|
|
|
mod polygons;
|
|
|
|
pub mod shared_row;
|
2020-10-23 02:29:42 +03:00
|
|
|
pub mod streetmix;
|
2020-10-05 02:09:20 +03:00
|
|
|
|
2019-04-26 01:50:16 +03:00
|
|
|
pub struct DebugMode {
|
2020-08-27 22:12:44 +03:00
|
|
|
panel: Panel,
|
2019-04-29 02:29:19 +03:00
|
|
|
common: CommonState,
|
2020-08-27 22:12:44 +03:00
|
|
|
tool_panel: Panel,
|
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
|
|
|
|
2020-12-01 02:15:13 +03:00
|
|
|
highlighted_agents: Cached<IntersectionID, Drawable>,
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DebugMode {
|
2020-10-23 03:34:59 +03:00
|
|
|
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
2020-09-14 20:28:25 +03:00
|
|
|
Box::new(DebugMode {
|
2020-08-27 22:12:44 +03:00
|
|
|
panel: Panel::new(Widget::col(vec![
|
2020-07-03 23:01:54 +03:00
|
|
|
Widget::row(vec![
|
|
|
|
Line("Debug Mode").small_heading().draw(ctx),
|
2020-10-28 22:04:36 +03:00
|
|
|
Btn::close(ctx),
|
2020-07-03 23:01:54 +03:00
|
|
|
]),
|
|
|
|
Text::new().draw(ctx).named("current info"),
|
2020-09-18 19:26:14 +03:00
|
|
|
Checkbox::switch(ctx, "show buildings", Key::Num1, true),
|
|
|
|
Checkbox::switch(ctx, "show intersections", Key::Num2, true),
|
|
|
|
Checkbox::switch(ctx, "show lanes", Key::Num3, true),
|
|
|
|
Checkbox::switch(ctx, "show areas", Key::Num4, true),
|
|
|
|
Checkbox::switch(ctx, "show labels", Key::Num5, false),
|
|
|
|
Checkbox::switch(ctx, "show route for all agents", Key::R, false),
|
|
|
|
Widget::col(vec![
|
|
|
|
Btn::text_fg("unhide everything").build_def(ctx, lctrl(Key::H)),
|
2021-01-06 21:52:22 +03:00
|
|
|
Btn::text_fg("screenshot everything (for leaflet)").build_def(ctx, None),
|
2020-09-18 19:26:14 +03:00
|
|
|
Btn::text_fg("screenshot all of the everything").build_def(ctx, None),
|
|
|
|
Btn::text_fg("search OSM metadata").build_def(ctx, Key::Slash),
|
|
|
|
Btn::text_fg("clear OSM search results").build_def(ctx, lctrl(Key::Slash)),
|
|
|
|
Btn::text_fg("save sim state").build_def(ctx, Key::O),
|
|
|
|
Btn::text_fg("load previous sim state").build_def(ctx, Key::Y),
|
|
|
|
Btn::text_fg("load next sim state").build_def(ctx, Key::U),
|
|
|
|
Btn::text_fg("pick a savestate to load").build_def(ctx, None),
|
|
|
|
Btn::text_fg("find bad traffic signals").build_def(ctx, None),
|
|
|
|
Btn::text_fg("find degenerate roads").build_def(ctx, None),
|
|
|
|
Btn::text_fg("find large intersections").build_def(ctx, None),
|
2020-11-16 22:51:24 +03:00
|
|
|
Btn::text_fg("sim internal stats").build_def(ctx, None),
|
2020-11-21 01:19:19 +03:00
|
|
|
Btn::text_fg("blocked-by graph").build_def(ctx, Key::B),
|
2021-01-01 23:46:25 +03:00
|
|
|
Btn::text_fg("render to GeoJSON").build_def(ctx, Key::G),
|
2020-09-18 19:26:14 +03:00
|
|
|
]),
|
2020-08-04 22:00:07 +03:00
|
|
|
Text::from_all(vec![
|
|
|
|
Line("Hold "),
|
2020-09-19 20:18:23 +03:00
|
|
|
Key::LeftControl.txt(ctx),
|
2020-08-04 22:00:07 +03:00
|
|
|
Line(" to show position"),
|
|
|
|
])
|
|
|
|
.draw(ctx),
|
2020-07-03 23:01:54 +03:00
|
|
|
]))
|
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-07-03 23:01:54 +03:00
|
|
|
tool_panel: tool_panel(ctx),
|
2020-11-23 00:24:08 +03:00
|
|
|
objects: objects::ObjectDebugger,
|
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-12-01 02:15:13 +03:00
|
|
|
highlighted_agents: Cached::new(),
|
2020-09-14 20:28:25 +03:00
|
|
|
})
|
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-11-10 22:49:55 +03:00
|
|
|
self.panel.replace(ctx, "current info", txt.draw(ctx));
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
|
2020-10-23 03:34:59 +03:00
|
|
|
impl State<App> 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-10-04 02:37:56 +03:00
|
|
|
app.primary.current_selection = app.mouseover_debug_mode(ctx, self);
|
2019-06-22 19:48:42 +03:00
|
|
|
}
|
2019-05-02 00:59:20 +03:00
|
|
|
|
2020-08-27 22:12:44 +03:00
|
|
|
match self.panel.event(ctx) {
|
2020-08-03 23:42:59 +03:00
|
|
|
Outcome::Clicked(x) => match x.as_ref() {
|
2020-06-04 07:52:54 +03:00
|
|
|
"close" => {
|
2020-02-28 00:22:57 +03:00
|
|
|
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-11-25 02:38:20 +03:00
|
|
|
match prev_state
|
|
|
|
.clone()
|
|
|
|
.and_then(|path| Sim::load_savestate(path, &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
|
|
|
|
}
|
2020-08-07 23:52:27 +03:00
|
|
|
None => Some(Transition::Push(PopupMsg::new(
|
|
|
|
ctx,
|
2020-02-28 00:22:57 +03:00
|
|
|
"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-11-25 02:38:20 +03:00
|
|
|
match next_state
|
|
|
|
.clone()
|
|
|
|
.and_then(|path| Sim::load_savestate(path, &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
|
|
|
|
}
|
2020-08-07 23:52:27 +03:00
|
|
|
None => Some(Transition::Push(PopupMsg::new(
|
|
|
|
ctx,
|
2020-02-28 00:22:57 +03:00
|
|
|
"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" => {
|
2020-08-06 20:21:01 +03:00
|
|
|
return Transition::Push(ChooseSomething::new(
|
|
|
|
ctx,
|
|
|
|
"Load which savestate?",
|
2021-01-02 21:28:00 +03:00
|
|
|
Choice::strings(abstio::list_all_objects(app.primary.sim.save_dir())),
|
2020-08-06 20:21:01 +03:00
|
|
|
Box::new(|ss, ctx, app| {
|
|
|
|
// TODO Oh no, we have to do path construction here :(
|
|
|
|
let ss_path = format!("{}/{}.bin", app.primary.sim.save_dir(), ss);
|
|
|
|
|
|
|
|
ctx.loading_screen("load savestate", |ctx, mut timer| {
|
2020-11-25 02:38:20 +03:00
|
|
|
app.primary.sim = Sim::load_savestate(ss_path, &mut timer)
|
|
|
|
.expect("Can't load savestate");
|
2020-08-06 20:21:01 +03:00
|
|
|
app.recalculate_current_selection(ctx);
|
|
|
|
});
|
|
|
|
Transition::Pop
|
|
|
|
}),
|
|
|
|
));
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
|
|
|
"unhide everything" => {
|
2019-06-22 19:48:42 +03:00
|
|
|
self.hidden.clear();
|
2020-10-04 02:37:56 +03:00
|
|
|
app.primary.current_selection = app.mouseover_debug_mode(ctx, self);
|
2020-02-28 00:22:57 +03:00
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
|
|
|
"search OSM metadata" => {
|
2020-08-07 23:33:58 +03:00
|
|
|
return Transition::Push(PromptInput::new(
|
|
|
|
ctx,
|
|
|
|
"Search for what?",
|
|
|
|
Box::new(search_osm),
|
|
|
|
));
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
|
|
|
"clear OSM search results" => {
|
|
|
|
self.search_results = None;
|
|
|
|
self.reset_info(ctx);
|
|
|
|
}
|
2021-01-06 21:52:22 +03:00
|
|
|
"screenshot everything (for leaflet)" => {
|
2021-01-07 03:33:53 +03:00
|
|
|
export_for_leaflet(ctx, app);
|
2020-07-02 03:47:30 +03:00
|
|
|
return Transition::Keep;
|
2020-02-28 00:22:57 +03:00
|
|
|
}
|
2020-08-26 19:41:03 +03:00
|
|
|
"screenshot all of the everything" => {
|
2020-10-10 01:07:35 +03:00
|
|
|
return Transition::Push(ScreenshotTest::new(
|
|
|
|
ctx,
|
2020-10-10 01:57:20 +03:00
|
|
|
app,
|
2020-10-10 01:07:35 +03:00
|
|
|
vec![
|
2020-11-05 04:26:32 +03:00
|
|
|
MapName::seattle("downtown"),
|
2020-11-06 02:24:02 +03:00
|
|
|
MapName::new("krakow", "center"),
|
2020-11-05 04:26:32 +03:00
|
|
|
MapName::seattle("lakeslice"),
|
|
|
|
MapName::seattle("montlake"),
|
|
|
|
MapName::new("london", "southbank"),
|
|
|
|
MapName::seattle("udistrict"),
|
2020-08-26 19:41:03 +03:00
|
|
|
],
|
2020-10-10 01:07:35 +03:00
|
|
|
));
|
2020-08-26 19:41:03 +03:00
|
|
|
}
|
2020-06-19 23:07:42 +03:00
|
|
|
"find bad traffic signals" => {
|
|
|
|
find_bad_signals(app);
|
|
|
|
}
|
2020-07-08 07:39:42 +03:00
|
|
|
"find degenerate roads" => {
|
|
|
|
find_degenerate_roads(app);
|
|
|
|
}
|
2020-08-04 04:12:18 +03:00
|
|
|
"find large intersections" => {
|
|
|
|
find_large_intersections(app);
|
|
|
|
}
|
2020-11-16 22:51:24 +03:00
|
|
|
"sim internal stats" => {
|
|
|
|
return Transition::Push(PopupMsg::new(
|
|
|
|
ctx,
|
|
|
|
"Simulation internal stats",
|
|
|
|
app.primary.sim.describe_internal_stats(),
|
|
|
|
));
|
|
|
|
}
|
2020-11-21 01:19:19 +03:00
|
|
|
"blocked-by graph" => {
|
|
|
|
return Transition::Push(blocked_by::Viewer::new(ctx, app));
|
|
|
|
}
|
2021-01-01 23:46:25 +03:00
|
|
|
"render to GeoJSON" => {
|
2021-01-02 02:33:11 +03:00
|
|
|
// TODO Loading screen doesn't actually display anything because of the rules
|
|
|
|
// around hiding the first few draws
|
|
|
|
ctx.loading_screen("render to GeoJSON", |ctx, timer| {
|
|
|
|
timer.start("render");
|
|
|
|
let batch = DrawMap::zoomed_batch(ctx, app);
|
|
|
|
let features = batch.to_geojson(Some(app.primary.map.get_gps_bounds()));
|
|
|
|
let geojson = geojson::GeoJson::from(geojson::FeatureCollection {
|
|
|
|
bbox: None,
|
|
|
|
features,
|
|
|
|
foreign_members: None,
|
|
|
|
});
|
2021-01-02 21:28:00 +03:00
|
|
|
abstio::write_json("rendered_map.json".to_string(), &geojson);
|
2021-01-02 02:33:11 +03:00
|
|
|
timer.stop("render");
|
2021-01-01 23:46:25 +03:00
|
|
|
});
|
|
|
|
}
|
2020-02-28 00:22:57 +03:00
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
2020-08-04 00:27:44 +03:00
|
|
|
Outcome::Changed => {
|
|
|
|
// TODO We should really recalculate current_selection when these change. Meh.
|
2020-08-27 22:12:44 +03:00
|
|
|
self.layers.show_buildings = self.panel.is_checked("show buildings");
|
|
|
|
self.layers.show_intersections = self.panel.is_checked("show intersections");
|
|
|
|
self.layers.show_lanes = self.panel.is_checked("show lanes");
|
|
|
|
self.layers.show_areas = self.panel.is_checked("show areas");
|
|
|
|
self.layers.show_labels = self.panel.is_checked("show labels");
|
|
|
|
if self.panel.is_checked("show route for all agents") {
|
2020-08-04 00:27:44 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-03-19 05:17:54 +03:00
|
|
|
}
|
2020-08-04 00:27:44 +03:00
|
|
|
_ => {}
|
2020-03-19 05:17:54 +03:00
|
|
|
}
|
2019-04-26 02:02:40 +03:00
|
|
|
|
2020-12-01 02:15:13 +03:00
|
|
|
self.highlighted_agents.update(
|
|
|
|
match app.primary.current_selection {
|
|
|
|
Some(ID::Intersection(i)) => Some(i),
|
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
|key| {
|
|
|
|
let mut batch = GeomBatch::new();
|
|
|
|
for (a, _) in app.primary.sim.get_accepted_agents(key) {
|
|
|
|
if let Some(obj) = app.primary.draw_map.get_obj(
|
|
|
|
ctx,
|
|
|
|
ID::from_agent(a),
|
|
|
|
app,
|
|
|
|
&mut app.primary.agents.borrow_mut(),
|
|
|
|
) {
|
|
|
|
batch.push(Color::PURPLE, obj.get_outline(&app.primary.map));
|
|
|
|
} else {
|
|
|
|
warn!(
|
|
|
|
"{} is accepted at or blocked by by {:?}, but no longer exists",
|
|
|
|
a, key
|
|
|
|
);
|
2020-04-29 22:34:09 +03:00
|
|
|
}
|
2020-02-18 19:40:05 +03:00
|
|
|
}
|
2020-12-01 02:15:13 +03:00
|
|
|
ctx.upload(batch)
|
|
|
|
},
|
|
|
|
);
|
2020-02-18 19:40:05 +03:00
|
|
|
|
2020-03-30 20:15:53 +03:00
|
|
|
if let Some(t) = self.common.event(ctx, app, &mut Actions {}) {
|
2019-12-12 02:25:43 +03:00
|
|
|
return t;
|
|
|
|
}
|
2020-08-04 22:34:58 +03:00
|
|
|
match self.tool_panel.event(ctx) {
|
|
|
|
Outcome::Clicked(x) => match x.as_ref() {
|
2019-12-16 21:10:01 +03:00
|
|
|
"back" => Transition::Pop,
|
2020-08-04 22:34:58 +03:00
|
|
|
"settings" => Transition::Push(OptionsPanel::new(ctx, app)),
|
2019-12-16 21:10:01 +03:00
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
2020-08-04 22:34:58 +03:00
|
|
|
_ => Transition::Keep,
|
2019-12-16 21:10:01 +03:00
|
|
|
}
|
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;
|
2020-10-12 19:52:05 +03:00
|
|
|
app.draw(g, opts, 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-12-01 02:15:13 +03:00
|
|
|
if let Some(draw) = self.highlighted_agents.value() {
|
2020-02-18 19:40:05 +03:00
|
|
|
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-08-27 22:12:44 +03:00
|
|
|
self.panel.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::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-08-07 23:33:58 +03:00
|
|
|
fn search_osm(filter: String, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
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;
|
2020-08-24 19:01:27 +03:00
|
|
|
let color = Color::RED.alpha(0.8);
|
2019-08-07 23:28:46 +03:00
|
|
|
for r in map.all_roads() {
|
|
|
|
if r.osm_tags
|
2020-07-23 17:43:50 +03:00
|
|
|
.inner()
|
2019-08-07 23:28:46 +03:00
|
|
|
.iter()
|
|
|
|
.any(|(k, v)| format!("{} = {}", k, v).contains(&filter))
|
|
|
|
{
|
2020-01-25 00:38:15 +03:00
|
|
|
num_matches += 1;
|
2020-07-08 01:52:51 +03:00
|
|
|
batch.push(color, r.get_thick_polygon(map));
|
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
|
2020-07-23 17:43:50 +03:00
|
|
|
.inner()
|
2019-11-14 21:54:11 +03:00
|
|
|
.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-09-07 20:49:20 +03:00
|
|
|
Transition::Multi(vec![
|
|
|
|
Transition::Pop,
|
|
|
|
Transition::ModifyState(Box::new(|state, ctx, _| {
|
|
|
|
let mut mode = state.downcast_mut::<DebugMode>().unwrap();
|
|
|
|
mode.search_results = Some(results);
|
|
|
|
mode.reset_info(ctx);
|
|
|
|
})),
|
|
|
|
])
|
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-19 00:41:34 +03:00
|
|
|
fn calc_all_routes(ctx: &EventCtx, app: &mut App) -> (usize, Drawable) {
|
2020-06-25 19:20:54 +03:00
|
|
|
let agents = app.primary.sim.active_agents();
|
2020-03-19 00:41:34 +03:00
|
|
|
let mut batch = GeomBatch::new();
|
|
|
|
let mut cnt = 0;
|
|
|
|
let sim = &app.primary.sim;
|
|
|
|
let map = &app.primary.map;
|
2020-07-23 19:15:25 +03:00
|
|
|
for maybe_trace in Timer::new("calculate all routes").parallelize(
|
|
|
|
"route to geometry",
|
|
|
|
Parallelism::Fastest,
|
|
|
|
agents,
|
|
|
|
|id| {
|
2020-12-17 03:09:11 +03:00
|
|
|
sim.trace_route(id, map)
|
2020-03-19 00:41:34 +03:00
|
|
|
.map(|trace| trace.make_polygons(NORMAL_LANE_THICKNESS))
|
2020-07-23 19:15:25 +03:00
|
|
|
},
|
|
|
|
) {
|
2020-03-19 00:41:34 +03:00
|
|
|
if let Some(t) = maybe_trace {
|
|
|
|
cnt += 1;
|
2020-04-04 20:59:51 +03:00
|
|
|
batch.push(app.cs.route, t);
|
2020-03-19 00:41:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
(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()));
|
2020-11-12 23:53:41 +03:00
|
|
|
actions.push((
|
|
|
|
Key::B,
|
|
|
|
"trace the block to the left of this road".to_string(),
|
|
|
|
));
|
2020-03-29 03:09:36 +03:00
|
|
|
}
|
2020-07-15 07:02:33 +03:00
|
|
|
ID::Intersection(i) => {
|
2020-03-29 03:09:36 +03:00
|
|
|
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()));
|
2020-07-15 07:02:33 +03:00
|
|
|
if app.primary.map.get_i(i).roads.len() == 2 {
|
|
|
|
actions.push((Key::C, "collapse degenerate road?".to_string()));
|
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
}
|
|
|
|
ID::Car(_) => {
|
2020-09-07 22:44:41 +03:00
|
|
|
actions.push((Key::Backspace, "forcibly delete this car".to_string()));
|
2020-03-29 03:09:36 +03:00
|
|
|
}
|
|
|
|
ID::Area(_) => {
|
|
|
|
actions.push((Key::X, "debug area geometry".to_string()));
|
|
|
|
actions.push((Key::F2, "debug area triangles".to_string()));
|
|
|
|
}
|
2020-06-27 03:02:14 +03:00
|
|
|
ID::ParkingLot(_) => {
|
|
|
|
actions.push((Key::H, "hide this".to_string()));
|
2020-07-16 23:22:05 +03:00
|
|
|
}
|
|
|
|
ID::BusStop(_) => {
|
|
|
|
actions.push((Key::H, "hide this".to_string()));
|
2020-06-27 03:02:14 +03:00
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
actions
|
2020-03-29 00:55:54 +03:00
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
|
2020-03-30 20:15:53 +03:00
|
|
|
fn execute(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut EventCtx,
|
|
|
|
app: &mut App,
|
|
|
|
id: ID,
|
|
|
|
action: String,
|
|
|
|
close_info: &mut bool,
|
|
|
|
) -> Transition {
|
2020-03-29 03:09:36 +03:00
|
|
|
match (id, action.as_ref()) {
|
2020-09-07 20:49:20 +03:00
|
|
|
(id, "hide this") => Transition::ModifyState(Box::new(|state, ctx, app| {
|
2020-03-29 03:09:36 +03:00
|
|
|
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") => {
|
2020-03-30 20:15:53 +03:00
|
|
|
*close_info = false;
|
2020-05-16 21:58:55 +03:00
|
|
|
objects::ObjectDebugger::dump_debug(id, &app.primary.map, &app.primary.sim);
|
2020-03-29 03:09:36 +03:00
|
|
|
Transition::Keep
|
|
|
|
}
|
2020-09-07 22:44:41 +03:00
|
|
|
(ID::Car(c), "forcibly delete this car") => {
|
|
|
|
app.primary.sim.delete_car(c, &app.primary.map);
|
2020-06-10 00:29:35 +03:00
|
|
|
app.primary
|
|
|
|
.sim
|
|
|
|
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
2020-03-29 03:09:36 +03:00
|
|
|
app.primary.current_selection = None;
|
|
|
|
Transition::Keep
|
|
|
|
}
|
|
|
|
(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",
|
2020-07-03 03:12:08 +03:00
|
|
|
calculate_corners(app.primary.map.get_i(i), &app.primary.map)
|
|
|
|
.into_iter()
|
|
|
|
.map(|poly| polygons::Item::Polygon(poly))
|
|
|
|
.collect(),
|
2020-03-29 03:09:36 +03:00
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
2020-07-15 07:02:33 +03:00
|
|
|
(ID::Intersection(i), "collapse degenerate road?") => {
|
|
|
|
let i = app.primary.map.get_i(i);
|
|
|
|
let (r1, r2) = {
|
|
|
|
let mut iter = i.roads.iter();
|
|
|
|
(*iter.next().unwrap(), *iter.next().unwrap())
|
|
|
|
};
|
|
|
|
diff_tags(
|
|
|
|
&app.primary.map.get_r(r1).osm_tags,
|
|
|
|
&app.primary.map.get_r(r2).osm_tags,
|
|
|
|
);
|
|
|
|
Transition::Keep
|
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
(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,
|
|
|
|
))
|
|
|
|
}
|
2020-11-12 23:53:41 +03:00
|
|
|
(ID::Lane(l), "trace the block to the left of this road") => {
|
|
|
|
Transition::ModifyState(Box::new(move |state, ctx, app| {
|
|
|
|
let mut mode = state.downcast_mut::<DebugMode>().unwrap();
|
|
|
|
// Just abuse this to display the results
|
2020-11-13 01:21:08 +03:00
|
|
|
mode.search_results = app
|
|
|
|
.primary
|
|
|
|
.map
|
|
|
|
.get_l(l)
|
|
|
|
.trace_around_block(&app.primary.map)
|
|
|
|
.map(|(poly, _)| SearchResults {
|
|
|
|
query: format!("block around {}", l),
|
|
|
|
num_matches: 0,
|
|
|
|
draw: ctx.upload(GeomBatch::from(vec![(Color::RED, poly)])),
|
|
|
|
});
|
2020-11-12 23:53:41 +03:00
|
|
|
mode.reset_info(ctx);
|
|
|
|
}))
|
|
|
|
}
|
2020-03-29 03:09:36 +03:00
|
|
|
(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
|
|
|
}
|
2020-04-02 01:50:11 +03:00
|
|
|
|
|
|
|
fn is_paused(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
2020-08-24 19:01:27 +03:00
|
|
|
|
|
|
|
fn gameplay_mode(&self) -> GameplayMode {
|
|
|
|
// Hack so info panels can be opened in DebugMode
|
2020-11-05 04:26:32 +03:00
|
|
|
GameplayMode::FixTrafficSignals
|
2020-08-24 19:01:27 +03:00
|
|
|
}
|
2020-03-29 00:55:54 +03:00
|
|
|
}
|
2020-06-19 23:07:42 +03:00
|
|
|
|
|
|
|
fn find_bad_signals(app: &App) {
|
|
|
|
println!("Bad traffic signals:");
|
|
|
|
for i in app.primary.map.all_intersections() {
|
|
|
|
if i.is_traffic_signal() {
|
2020-06-21 21:38:34 +03:00
|
|
|
let first = &ControlTrafficSignal::get_possible_policies(
|
|
|
|
&app.primary.map,
|
|
|
|
i.id,
|
|
|
|
&mut Timer::throwaway(),
|
|
|
|
)[0]
|
|
|
|
.0;
|
2021-01-04 22:18:17 +03:00
|
|
|
if first == "stage per road" || first == "arbitrary assignment" {
|
2020-06-19 23:07:42 +03:00
|
|
|
println!("- {}", i.id);
|
|
|
|
ControlTrafficSignal::brute_force(&app.primary.map, i.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 07:39:42 +03:00
|
|
|
|
|
|
|
fn find_degenerate_roads(app: &App) {
|
|
|
|
let map = &app.primary.map;
|
|
|
|
for i in map.all_intersections() {
|
|
|
|
if i.roads.len() != 2 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if i.turns.iter().any(|t| map.get_t(*t).between_sidewalks()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let (r1, r2) = {
|
|
|
|
let mut iter = i.roads.iter();
|
|
|
|
(*iter.next().unwrap(), *iter.next().unwrap())
|
|
|
|
};
|
|
|
|
let r1 = map.get_r(r1);
|
|
|
|
let r2 = map.get_r(r2);
|
|
|
|
if r1.zorder != r2.zorder {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if r1
|
2020-08-25 01:39:17 +03:00
|
|
|
.lanes_ltr()
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_, dir, lt)| (dir, lt))
|
2020-07-08 07:39:42 +03:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
!= r2
|
2020-08-25 01:39:17 +03:00
|
|
|
.lanes_ltr()
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_, dir, lt)| (dir, lt))
|
2020-07-08 07:39:42 +03:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("Maybe merge {}", i.id);
|
|
|
|
diff_tags(&r1.osm_tags, &r2.osm_tags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 17:43:50 +03:00
|
|
|
fn diff_tags(t1: &Tags, t2: &Tags) {
|
|
|
|
for (k, v1) in t1.inner() {
|
2020-07-08 07:39:42 +03:00
|
|
|
if k == osm::OSM_WAY_ID {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let v2 = t2.get(k).cloned().unwrap_or_else(String::new);
|
|
|
|
if v1 != &v2 {
|
|
|
|
println!("- {} = \"{}\" vs \"{}\"", k, v1, v2);
|
|
|
|
}
|
|
|
|
}
|
2020-07-23 17:43:50 +03:00
|
|
|
for (k, v2) in t2.inner() {
|
2020-07-08 07:39:42 +03:00
|
|
|
if !t1.contains_key(k) {
|
|
|
|
println!("- {} = \"\" vs \"{}\"", k, v2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-04 04:12:18 +03:00
|
|
|
|
|
|
|
fn find_large_intersections(app: &App) {
|
|
|
|
let mut seen = HashSet::new();
|
|
|
|
for t in app.primary.map.all_turns().values() {
|
|
|
|
if !seen.contains(&t.id.parent) && t.geom.length() > Distance::meters(50.0) {
|
|
|
|
println!("{} has a long turn", t.id.parent);
|
|
|
|
seen.insert(t.id.parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-26 19:41:03 +03:00
|
|
|
|
2020-08-27 20:37:04 +03:00
|
|
|
// Because of the slightly odd control flow needed to ask widgetry to ScreenCaptureEverything, a
|
2020-08-26 19:41:03 +03:00
|
|
|
// separate state is the easiest way to automatically screenshot multiple maps.
|
|
|
|
struct ScreenshotTest {
|
2020-11-05 04:26:32 +03:00
|
|
|
todo_maps: Vec<MapName>,
|
2020-10-10 01:07:35 +03:00
|
|
|
screenshot_done: bool,
|
2021-01-07 03:33:53 +03:00
|
|
|
orig_min_zoom: f64,
|
2020-10-10 01:07:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ScreenshotTest {
|
2021-01-07 03:33:53 +03:00
|
|
|
fn new(ctx: &mut EventCtx, app: &mut App, mut todo_maps: Vec<MapName>) -> Box<dyn State<App>> {
|
|
|
|
let orig_min_zoom = app.opts.min_zoom_for_detail;
|
|
|
|
app.opts.min_zoom_for_detail = 0.0;
|
2020-10-10 01:07:35 +03:00
|
|
|
MapLoader::new(
|
|
|
|
ctx,
|
2020-10-10 01:57:20 +03:00
|
|
|
app,
|
2020-11-05 04:26:32 +03:00
|
|
|
todo_maps.pop().unwrap(),
|
2020-10-10 01:07:35 +03:00
|
|
|
Box::new(move |_, _| {
|
|
|
|
Transition::Replace(Box::new(ScreenshotTest {
|
2020-10-14 23:36:08 +03:00
|
|
|
todo_maps,
|
2020-10-10 01:07:35 +03:00
|
|
|
screenshot_done: false,
|
2021-01-07 03:33:53 +03:00
|
|
|
orig_min_zoom,
|
2020-10-10 01:07:35 +03:00
|
|
|
}))
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
}
|
2020-08-26 19:41:03 +03:00
|
|
|
}
|
|
|
|
|
2020-10-23 03:34:59 +03:00
|
|
|
impl State<App> for ScreenshotTest {
|
2020-08-26 19:41:03 +03:00
|
|
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
2020-10-10 01:07:35 +03:00
|
|
|
if self.screenshot_done {
|
|
|
|
if self.todo_maps.is_empty() {
|
2021-01-07 03:33:53 +03:00
|
|
|
app.opts.min_zoom_for_detail = self.orig_min_zoom;
|
2020-10-10 01:07:35 +03:00
|
|
|
Transition::Pop
|
|
|
|
} else {
|
2020-10-10 01:57:20 +03:00
|
|
|
Transition::Replace(ScreenshotTest::new(
|
|
|
|
ctx,
|
|
|
|
app,
|
|
|
|
self.todo_maps.drain(..).collect(),
|
|
|
|
))
|
2020-10-10 01:07:35 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.screenshot_done = true;
|
2021-01-07 03:33:53 +03:00
|
|
|
let name = app.primary.map.get_name();
|
|
|
|
ctx.request_update(UpdateType::ScreenCaptureEverything {
|
|
|
|
dir: format!("screenshots/{}/{}", name.city, name.map),
|
|
|
|
zoom: 3.0,
|
|
|
|
dims: ctx.canvas.get_window_dims(),
|
|
|
|
leaflet_naming: false,
|
|
|
|
});
|
2020-08-26 19:41:03 +03:00
|
|
|
// TODO Sometimes this still gets stuck and needs a mouse wiggle for input event?
|
|
|
|
Transition::Keep
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn draw(&self, _: &mut GfxCtx, _: &App) {}
|
|
|
|
}
|
|
|
|
|
2021-01-07 03:33:53 +03:00
|
|
|
fn export_for_leaflet(ctx: &mut EventCtx, app: &App) {
|
2020-11-06 01:42:40 +03:00
|
|
|
let name = app.primary.map.get_name();
|
2021-01-07 03:33:53 +03:00
|
|
|
let bounds = app.primary.map.get_bounds();
|
|
|
|
let map_length = bounds.width().max(bounds.height());
|
|
|
|
|
|
|
|
// At zoom level N, the entire map fits into (N + 1) * (N + 1) tiles
|
|
|
|
for zoom_level in 0..=25 {
|
|
|
|
let num_tiles = zoom_level + 1;
|
|
|
|
// How do we fit the entire map_length into this many tiles?
|
|
|
|
let zoom = 256.0 * (num_tiles as f64) / map_length;
|
|
|
|
ctx.request_update(UpdateType::ScreenCaptureEverything {
|
|
|
|
dir: format!("screenshots/{}/{}/{}", name.city, name.map, zoom_level),
|
|
|
|
zoom,
|
|
|
|
dims: ScreenDims::new(256.0, 256.0),
|
|
|
|
leaflet_naming: true,
|
|
|
|
});
|
|
|
|
}
|
2020-08-26 19:41:03 +03:00
|
|
|
}
|