2019-04-26 02:02:40 +03:00
|
|
|
mod chokepoints;
|
2019-04-26 20:46:41 +03:00
|
|
|
mod color_picker;
|
2019-04-26 02:35:09 +03:00
|
|
|
mod connected_roads;
|
2019-04-27 23:58:36 +03:00
|
|
|
mod neighborhood_summary;
|
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
|
|
|
|
2019-04-29 02:29:19 +03:00
|
|
|
use crate::common::CommonState;
|
2019-05-29 23:28:51 +03:00
|
|
|
use crate::edit::EditMode;
|
2019-04-26 01:50:16 +03:00
|
|
|
use crate::game::{GameState, Mode};
|
2019-04-29 19:56:01 +03:00
|
|
|
use crate::helpers::ID;
|
2019-04-29 23:55:59 +03:00
|
|
|
use crate::render::DrawOptions;
|
2019-05-29 23:28:51 +03:00
|
|
|
use crate::sandbox::SandboxMode;
|
2019-04-27 23:58:36 +03:00
|
|
|
use crate::ui::{ShowLayers, ShowObject, UI};
|
2019-05-18 01:52:08 +03:00
|
|
|
use abstutil::wraparound_get;
|
2019-04-27 23:58:36 +03:00
|
|
|
use abstutil::Timer;
|
2019-05-17 23:27:30 +03:00
|
|
|
use clipping::CPolygon;
|
2019-04-26 20:46:41 +03:00
|
|
|
use ezgui::{
|
2019-05-29 23:28:51 +03:00
|
|
|
hotkey, lctrl, Color, EventCtx, EventLoopMode, GfxCtx, InputResult, Key, ModalMenu,
|
|
|
|
ScrollingMenu, Text, TextBox, Wizard,
|
2019-04-26 20:46:41 +03:00
|
|
|
};
|
2019-05-18 01:52:08 +03:00
|
|
|
use geom::{Distance, PolyLine, Polygon, Pt2D};
|
2019-05-17 23:27:30 +03:00
|
|
|
use map_model::{IntersectionID, Map, RoadID};
|
2019-04-29 23:55:59 +03:00
|
|
|
use std::collections::HashSet;
|
2019-04-26 01:50:16 +03:00
|
|
|
|
|
|
|
pub struct DebugMode {
|
|
|
|
state: State,
|
2019-04-29 02:29:19 +03:00
|
|
|
common: CommonState,
|
2019-04-26 02:02:40 +03:00
|
|
|
chokepoints: Option<chokepoints::ChokepointsFinder>,
|
2019-04-26 02:16:21 +03:00
|
|
|
show_original_roads: HashSet<RoadID>,
|
2019-05-17 23:27:30 +03:00
|
|
|
intersection_geom: HashSet<IntersectionID>,
|
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>,
|
2019-04-26 08:19:54 +03:00
|
|
|
layers: ShowLayers,
|
2019-04-26 20:21:05 +03:00
|
|
|
search_results: Option<(String, HashSet<ID>)>,
|
2019-04-27 23:58:36 +03:00
|
|
|
neighborhood_summary: neighborhood_summary::NeighborhoodSummary,
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
enum State {
|
2019-05-02 01:58:11 +03:00
|
|
|
Exploring(ModalMenu),
|
2019-04-26 02:24:10 +03:00
|
|
|
Polygons(polygons::PolygonDebugger),
|
2019-04-26 20:21:05 +03:00
|
|
|
SearchOSM(TextBox),
|
2019-04-26 20:46:41 +03:00
|
|
|
Colors(color_picker::ColorPicker),
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DebugMode {
|
2019-04-27 23:58:36 +03:00
|
|
|
pub fn new(ctx: &mut EventCtx, ui: &UI) -> DebugMode {
|
2019-04-26 01:50:16 +03:00
|
|
|
DebugMode {
|
2019-05-02 00:59:20 +03:00
|
|
|
state: DebugMode::exploring_state(ctx),
|
2019-04-29 02:29:19 +03:00
|
|
|
common: CommonState::new(),
|
2019-04-26 02:02:40 +03:00
|
|
|
chokepoints: None,
|
2019-04-26 02:16:21 +03:00
|
|
|
show_original_roads: HashSet::new(),
|
2019-05-17 23:27:30 +03:00
|
|
|
intersection_geom: HashSet::new(),
|
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(),
|
2019-04-26 08:19:54 +03:00
|
|
|
layers: ShowLayers::new(),
|
2019-04-26 20:21:05 +03:00
|
|
|
search_results: None,
|
2019-04-27 23:58:36 +03:00
|
|
|
neighborhood_summary: neighborhood_summary::NeighborhoodSummary::new(
|
2019-04-29 19:27:43 +03:00
|
|
|
&ui.primary.map,
|
|
|
|
&ui.primary.draw_map,
|
2019-04-27 23:58:36 +03:00
|
|
|
ctx.prerender,
|
|
|
|
&mut Timer::new("set up DebugMode"),
|
|
|
|
),
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-02 00:59:20 +03:00
|
|
|
fn exploring_state(ctx: &EventCtx) -> State {
|
2019-05-02 01:58:11 +03:00
|
|
|
State::Exploring(ModalMenu::new(
|
2019-05-02 00:59:20 +03:00
|
|
|
"Debug Mode",
|
|
|
|
vec![
|
2019-05-04 00:33:16 +03:00
|
|
|
vec![
|
2019-05-29 23:28:51 +03:00
|
|
|
(hotkey(Key::Escape), "quit"),
|
|
|
|
(hotkey(Key::C), "show/hide chokepoints"),
|
|
|
|
(hotkey(Key::O), "clear original roads shown"),
|
|
|
|
(hotkey(Key::G), "clear intersection geometry"),
|
|
|
|
(hotkey(Key::H), "unhide everything"),
|
|
|
|
(hotkey(Key::Num1), "show/hide buildings"),
|
|
|
|
(hotkey(Key::Num2), "show/hide intersections"),
|
|
|
|
(hotkey(Key::Num3), "show/hide lanes"),
|
|
|
|
(hotkey(Key::Num4), "show/hide areas"),
|
|
|
|
(hotkey(Key::Num5), "show/hide extra shapes"),
|
|
|
|
(hotkey(Key::Num6), "show/hide geometry debug mode"),
|
2019-05-04 00:33:16 +03:00
|
|
|
(None, "screenshot everything"),
|
2019-05-29 23:28:51 +03:00
|
|
|
(hotkey(Key::Slash), "search OSM metadata"),
|
|
|
|
(hotkey(Key::M), "clear OSM search results"),
|
|
|
|
(hotkey(Key::S), "configure colors"),
|
|
|
|
(hotkey(Key::N), "show/hide neighborhood summaries"),
|
|
|
|
(lctrl(Key::S), "sandbox mode"),
|
|
|
|
(lctrl(Key::E), "edit mode"),
|
2019-05-04 00:33:16 +03:00
|
|
|
],
|
|
|
|
CommonState::modal_menu_entries(),
|
|
|
|
]
|
|
|
|
.concat(),
|
2019-05-02 00:59:20 +03:00
|
|
|
ctx,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2019-04-26 01:50:16 +03:00
|
|
|
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
|
|
|
match state.mode {
|
|
|
|
Mode::Debug(ref mut mode) => {
|
2019-05-02 00:59:20 +03:00
|
|
|
// TODO Argh, bad hack! Can't do it below because menu is borrowed and ShowObject
|
|
|
|
// is implemented on the entirety of DebugMode. :(
|
|
|
|
if let State::Exploring(_) = mode.state {
|
|
|
|
state.ui.primary.current_selection =
|
|
|
|
state
|
|
|
|
.ui
|
2019-05-11 02:21:21 +03:00
|
|
|
.handle_mouseover(ctx, &state.ui.primary.sim, mode, true);
|
2019-05-02 00:59:20 +03:00
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
|
2019-05-02 00:59:20 +03:00
|
|
|
match mode.state {
|
|
|
|
State::Exploring(ref mut menu) => {
|
2019-05-01 21:42:14 +03:00
|
|
|
let mut txt = Text::prompt("Debug Mode");
|
2019-04-26 02:24:10 +03:00
|
|
|
if mode.chokepoints.is_some() {
|
|
|
|
txt.add_line("Showing chokepoints".to_string());
|
|
|
|
}
|
|
|
|
if !mode.show_original_roads.is_empty() {
|
|
|
|
txt.add_line(format!(
|
|
|
|
"Showing {} original roads",
|
|
|
|
mode.show_original_roads.len()
|
|
|
|
));
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
if !mode.intersection_geom.is_empty() {
|
|
|
|
txt.add_line(format!(
|
|
|
|
"Showing {} attempts at intersection geometry",
|
|
|
|
mode.intersection_geom.len()
|
|
|
|
));
|
|
|
|
}
|
2019-04-26 08:07:48 +03:00
|
|
|
if !mode.hidden.is_empty() {
|
|
|
|
txt.add_line(format!("Hiding {} things", mode.hidden.len()));
|
|
|
|
}
|
2019-04-26 20:21:05 +03:00
|
|
|
if let Some((ref search, ref results)) = mode.search_results {
|
|
|
|
txt.add_line(format!(
|
|
|
|
"Search for {} has {} results",
|
|
|
|
search,
|
|
|
|
results.len()
|
|
|
|
));
|
|
|
|
}
|
2019-04-27 23:58:36 +03:00
|
|
|
if mode.neighborhood_summary.active {
|
|
|
|
txt.add_line("Showing neighborhood summaries".to_string());
|
|
|
|
}
|
2019-05-02 05:41:36 +03:00
|
|
|
menu.handle_event(ctx, Some(txt));
|
2019-05-02 00:59:20 +03:00
|
|
|
|
|
|
|
ctx.canvas.handle_event(ctx.input);
|
2019-05-04 20:08:45 +03:00
|
|
|
if let Some(evmode) = mode.common.event(ctx, &mut state.ui, menu) {
|
2019-05-02 00:59:20 +03:00
|
|
|
return evmode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if menu.action("quit") {
|
2019-04-26 02:24:10 +03:00
|
|
|
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
|
|
|
return EventLoopMode::InputOnly;
|
|
|
|
}
|
2019-05-29 23:28:51 +03:00
|
|
|
if menu.action("sandbox mode") {
|
|
|
|
state.mode = Mode::Sandbox(SandboxMode::new(ctx));
|
|
|
|
return EventLoopMode::InputOnly;
|
|
|
|
}
|
|
|
|
if menu.action("edit mode") {
|
|
|
|
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
|
|
|
return EventLoopMode::InputOnly;
|
|
|
|
}
|
2019-04-26 02:02:40 +03:00
|
|
|
|
2019-05-02 00:59:20 +03:00
|
|
|
if menu.action("show/hide chokepoints") {
|
2019-04-26 02:24:10 +03:00
|
|
|
if mode.chokepoints.is_some() {
|
|
|
|
mode.chokepoints = None;
|
|
|
|
} else {
|
|
|
|
// TODO Nothing will actually exist. ;)
|
|
|
|
mode.chokepoints = Some(chokepoints::ChokepointsFinder::new(
|
2019-04-29 19:27:43 +03:00
|
|
|
&state.ui.primary.sim,
|
2019-04-26 02:24:10 +03:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !mode.show_original_roads.is_empty() {
|
2019-05-02 00:59:20 +03:00
|
|
|
if menu.action("clear original roads shown") {
|
2019-04-26 02:24:10 +03:00
|
|
|
mode.show_original_roads.clear();
|
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
if !mode.intersection_geom.is_empty()
|
|
|
|
&& state.ui.primary.current_selection.is_none()
|
|
|
|
{
|
|
|
|
if menu.action("clear intersection geometry") {
|
|
|
|
mode.intersection_geom.clear();
|
|
|
|
}
|
|
|
|
}
|
2019-04-29 19:27:43 +03:00
|
|
|
match state.ui.primary.current_selection {
|
2019-04-26 08:07:48 +03:00
|
|
|
Some(ID::Lane(_))
|
|
|
|
| Some(ID::Intersection(_))
|
|
|
|
| Some(ID::ExtraShape(_)) => {
|
2019-04-29 19:27:43 +03:00
|
|
|
let id = state.ui.primary.current_selection.unwrap();
|
2019-04-26 08:07:48 +03:00
|
|
|
if ctx
|
|
|
|
.input
|
|
|
|
.contextual_action(Key::H, &format!("hide {:?}", id))
|
|
|
|
{
|
|
|
|
println!("Hiding {:?}", id);
|
|
|
|
//*ctx.recalculate_current_selection = true;
|
2019-04-29 19:27:43 +03:00
|
|
|
state.ui.primary.current_selection = None;
|
2019-04-26 08:07:48 +03:00
|
|
|
mode.hidden.insert(id);
|
|
|
|
}
|
|
|
|
}
|
2019-05-04 00:50:56 +03:00
|
|
|
None => {
|
|
|
|
if !mode.hidden.is_empty() && menu.action("unhide everything") {
|
|
|
|
mode.hidden.clear();
|
|
|
|
// TODO recalculate current_selection
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 08:07:48 +03:00
|
|
|
_ => {}
|
|
|
|
}
|
2019-04-26 02:24:10 +03:00
|
|
|
|
2019-04-29 19:27:43 +03:00
|
|
|
if let Some(ID::Lane(l)) = state.ui.primary.current_selection {
|
|
|
|
let id = state.ui.primary.map.get_l(l).parent;
|
2019-05-17 23:27:30 +03:00
|
|
|
if !mode.show_original_roads.contains(&id)
|
|
|
|
&& ctx.input.contextual_action(
|
|
|
|
Key::V,
|
|
|
|
&format!("show original geometry of {}", id),
|
|
|
|
)
|
|
|
|
{
|
2019-04-26 02:24:10 +03:00
|
|
|
mode.show_original_roads.insert(id);
|
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
if let Some(ID::Intersection(i)) = state.ui.primary.current_selection {
|
|
|
|
if !mode.intersection_geom.contains(&i)
|
|
|
|
&& ctx.input.contextual_action(
|
|
|
|
Key::G,
|
|
|
|
&format!("recalculate intersection geometry of {}", i),
|
|
|
|
)
|
|
|
|
{
|
|
|
|
mode.intersection_geom.insert(i);
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:35:09 +03:00
|
|
|
mode.connected_roads.event(ctx, &state.ui);
|
2019-04-26 02:40:26 +03:00
|
|
|
mode.objects.event(ctx, &state.ui);
|
2019-05-02 00:59:20 +03:00
|
|
|
mode.neighborhood_summary.event(&state.ui, menu);
|
2019-04-26 02:24:10 +03:00
|
|
|
|
|
|
|
if let Some(debugger) = polygons::PolygonDebugger::new(ctx, &state.ui) {
|
|
|
|
mode.state = State::Polygons(debugger);
|
2019-05-02 00:59:20 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2019-04-26 02:24:10 +03:00
|
|
|
}
|
2019-04-26 02:16:21 +03:00
|
|
|
|
2019-04-26 08:19:54 +03:00
|
|
|
// TODO recalc current selection...
|
2019-05-02 00:59:20 +03:00
|
|
|
if menu.action("show/hide buildings") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.show_buildings = !mode.layers.show_buildings;
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("show/hide intersections") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.show_intersections = !mode.layers.show_intersections;
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("show/hide lanes") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.show_lanes = !mode.layers.show_lanes;
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("show/hide areas") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.show_areas = !mode.layers.show_areas;
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("show/hide extra shapes") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.show_extra_shapes = !mode.layers.show_extra_shapes;
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("show/hide geometry debug mode") {
|
2019-04-26 08:19:54 +03:00
|
|
|
mode.layers.geom_debug_mode = !mode.layers.geom_debug_mode;
|
|
|
|
}
|
|
|
|
|
2019-05-02 00:59:20 +03:00
|
|
|
if menu.action("screenshot everything") {
|
2019-04-29 19:27:43 +03:00
|
|
|
let bounds = state.ui.primary.map.get_bounds();
|
2019-04-26 20:15:25 +03:00
|
|
|
assert!(bounds.min_x == 0.0 && bounds.min_y == 0.0);
|
|
|
|
return EventLoopMode::ScreenCaptureEverything {
|
|
|
|
dir: format!(
|
|
|
|
"../data/screenshots/pending_{}",
|
2019-04-29 19:27:43 +03:00
|
|
|
state.ui.primary.map.get_name()
|
2019-04-26 20:15:25 +03:00
|
|
|
),
|
|
|
|
zoom: 3.0,
|
|
|
|
max_x: bounds.max_x,
|
|
|
|
max_y: bounds.max_y,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-04-26 20:21:05 +03:00
|
|
|
if mode.search_results.is_some() {
|
2019-05-02 00:59:20 +03:00
|
|
|
if menu.action("clear OSM search results") {
|
2019-04-26 20:21:05 +03:00
|
|
|
mode.search_results = None;
|
|
|
|
}
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("search OSM metadata") {
|
2019-04-26 20:21:05 +03:00
|
|
|
mode.state = State::SearchOSM(TextBox::new("Search for what?", None));
|
2019-05-02 00:59:20 +03:00
|
|
|
} else if menu.action("configure colors") {
|
2019-04-26 20:46:41 +03:00
|
|
|
mode.state = State::Colors(color_picker::ColorPicker::Choosing(
|
|
|
|
ScrollingMenu::new(
|
|
|
|
"Pick a color to change",
|
2019-04-29 19:27:43 +03:00
|
|
|
state.ui.cs.color_names(),
|
2019-04-26 20:46:41 +03:00
|
|
|
),
|
|
|
|
));
|
2019-04-26 20:21:05 +03:00
|
|
|
}
|
|
|
|
|
2019-04-26 02:24:10 +03:00
|
|
|
EventLoopMode::InputOnly
|
|
|
|
}
|
|
|
|
State::Polygons(ref mut debugger) => {
|
|
|
|
if debugger.event(ctx) {
|
2019-05-02 00:59:20 +03:00
|
|
|
mode.state = DebugMode::exploring_state(ctx);
|
2019-04-26 02:24:10 +03:00
|
|
|
}
|
|
|
|
EventLoopMode::InputOnly
|
2019-04-26 02:16:21 +03:00
|
|
|
}
|
2019-04-26 20:21:05 +03:00
|
|
|
State::SearchOSM(ref mut tb) => {
|
|
|
|
match tb.event(&mut ctx.input) {
|
|
|
|
InputResult::Canceled => {
|
2019-05-02 00:59:20 +03:00
|
|
|
mode.state = DebugMode::exploring_state(ctx);
|
2019-04-26 20:21:05 +03:00
|
|
|
}
|
|
|
|
InputResult::Done(filter, _) => {
|
2019-05-02 00:59:20 +03:00
|
|
|
mode.state = DebugMode::exploring_state(ctx);
|
2019-04-26 20:21:05 +03:00
|
|
|
|
|
|
|
let mut ids = HashSet::new();
|
2019-04-29 19:27:43 +03:00
|
|
|
let map = &state.ui.primary.map;
|
2019-04-26 20:21:05 +03:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mode.search_results = Some((filter, ids));
|
|
|
|
}
|
|
|
|
InputResult::StillActive => {}
|
|
|
|
}
|
|
|
|
EventLoopMode::InputOnly
|
|
|
|
}
|
2019-04-26 20:46:41 +03:00
|
|
|
State::Colors(ref mut picker) => {
|
|
|
|
if picker.event(ctx, &mut state.ui) {
|
2019-05-02 00:59:20 +03:00
|
|
|
mode.state = DebugMode::exploring_state(ctx);
|
2019-04-26 20:46:41 +03:00
|
|
|
}
|
|
|
|
EventLoopMode::InputOnly
|
|
|
|
}
|
2019-04-26 02:16:21 +03:00
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
|
|
|
match state.mode {
|
|
|
|
Mode::Debug(ref mode) => match mode.state {
|
2019-05-02 00:59:20 +03:00
|
|
|
State::Exploring(ref menu) => {
|
2019-04-29 23:55:59 +03:00
|
|
|
let mut opts = mode.common.draw_options(&state.ui);
|
2019-04-30 00:31:12 +03:00
|
|
|
opts.geom_debug_mode = mode.layers.geom_debug_mode;
|
2019-04-26 02:02:40 +03:00
|
|
|
if let Some(ref chokepoints) = mode.chokepoints {
|
2019-04-29 19:27:43 +03:00
|
|
|
let color = state.ui.cs.get_def("chokepoint", Color::RED);
|
2019-04-26 02:02:40 +03:00
|
|
|
for l in &chokepoints.lanes {
|
2019-04-29 23:55:59 +03:00
|
|
|
opts.override_colors.insert(ID::Lane(*l), color);
|
2019-04-26 02:02:40 +03:00
|
|
|
}
|
|
|
|
for i in &chokepoints.intersections {
|
2019-04-29 23:55:59 +03:00
|
|
|
opts.override_colors.insert(ID::Intersection(*i), color);
|
2019-04-26 02:02:40 +03:00
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:35:09 +03:00
|
|
|
for l in &mode.connected_roads.lanes {
|
2019-04-29 23:55:59 +03:00
|
|
|
opts.override_colors.insert(
|
2019-04-26 02:35:09 +03:00
|
|
|
ID::Lane(*l),
|
2019-04-29 19:27:43 +03:00
|
|
|
state.ui.cs.get("something associated with something else"),
|
2019-04-26 02:35:09 +03:00
|
|
|
);
|
|
|
|
}
|
2019-04-26 20:21:05 +03:00
|
|
|
if let Some((_, ref results)) = mode.search_results {
|
|
|
|
for id in results {
|
2019-04-29 23:55:59 +03:00
|
|
|
opts.override_colors
|
2019-04-29 19:27:43 +03:00
|
|
|
.insert(*id, state.ui.cs.get_def("search result", Color::RED));
|
2019-04-26 20:21:05 +03:00
|
|
|
}
|
|
|
|
}
|
2019-04-29 23:55:59 +03:00
|
|
|
state.ui.draw(g, opts, &state.ui.primary.sim, mode);
|
2019-04-29 03:11:46 +03:00
|
|
|
mode.common.draw(g, &state.ui);
|
2019-04-26 02:16:21 +03:00
|
|
|
|
|
|
|
for id in &mode.show_original_roads {
|
2019-04-29 19:27:43 +03:00
|
|
|
let r = state.ui.primary.map.get_r(*id);
|
2019-04-26 02:16:21 +03:00
|
|
|
if let Some(pair) = r.get_center_for_side(true) {
|
|
|
|
let (pl, width) = pair.unwrap();
|
|
|
|
g.draw_polygon(
|
|
|
|
state
|
|
|
|
.ui
|
|
|
|
.cs
|
|
|
|
.get_def("original road forwards", Color::RED.alpha(0.5)),
|
|
|
|
&pl.make_polygons(width),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if let Some(pair) = r.get_center_for_side(false) {
|
|
|
|
let (pl, width) = pair.unwrap();
|
|
|
|
g.draw_polygon(
|
|
|
|
state
|
|
|
|
.ui
|
|
|
|
.cs
|
|
|
|
.get_def("original road backwards", Color::BLUE.alpha(0.5)),
|
|
|
|
&pl.make_polygons(width),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
for id in &mode.intersection_geom {
|
|
|
|
recalc_intersection_geom(*id, &state.ui.primary.map, g);
|
|
|
|
}
|
2019-04-26 02:40:26 +03:00
|
|
|
|
|
|
|
mode.objects.draw(g, &state.ui);
|
2019-04-27 23:58:36 +03:00
|
|
|
mode.neighborhood_summary.draw(g);
|
2019-05-02 00:59:20 +03:00
|
|
|
|
2019-05-02 02:11:22 +03:00
|
|
|
if !g.is_screencap() {
|
|
|
|
menu.draw(g);
|
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
}
|
2019-04-26 02:24:10 +03:00
|
|
|
State::Polygons(ref debugger) => {
|
2019-04-30 00:31:12 +03:00
|
|
|
let mut opts = DrawOptions::new();
|
|
|
|
opts.geom_debug_mode = mode.layers.geom_debug_mode;
|
|
|
|
state.ui.draw(g, opts, &state.ui.primary.sim, mode);
|
2019-04-26 02:24:10 +03:00
|
|
|
debugger.draw(g, &state.ui);
|
|
|
|
}
|
2019-04-26 20:21:05 +03:00
|
|
|
State::SearchOSM(ref tb) => {
|
2019-04-30 00:31:12 +03:00
|
|
|
let mut opts = DrawOptions::new();
|
|
|
|
opts.geom_debug_mode = mode.layers.geom_debug_mode;
|
|
|
|
state.ui.draw(g, opts, &state.ui.primary.sim, mode);
|
2019-04-26 20:21:05 +03:00
|
|
|
tb.draw(g);
|
|
|
|
}
|
2019-04-26 20:46:41 +03:00
|
|
|
State::Colors(ref picker) => {
|
2019-04-30 00:31:12 +03:00
|
|
|
let mut opts = DrawOptions::new();
|
|
|
|
opts.geom_debug_mode = mode.layers.geom_debug_mode;
|
|
|
|
state.ui.draw(g, opts, &state.ui.primary.sim, mode);
|
2019-04-26 20:46:41 +03:00
|
|
|
picker.draw(g);
|
|
|
|
}
|
2019-04-26 01:50:16 +03:00
|
|
|
},
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 02:56:38 +03:00
|
|
|
|
|
|
|
impl ShowObject for DebugMode {
|
|
|
|
fn show(&self, obj: ID) -> bool {
|
2019-04-26 08:19:54 +03:00
|
|
|
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
|
2019-04-26 02:56:38 +03:00
|
|
|
}
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
|
|
|
|
fn recalc_intersection_geom(id: IntersectionID, map: &Map, g: &mut GfxCtx) {
|
|
|
|
let mut all_polys = Vec::new();
|
|
|
|
|
2019-05-18 21:45:09 +03:00
|
|
|
if false {
|
2019-05-18 01:52:08 +03:00
|
|
|
// Get road center lines sorted by angle into the intersection, to find adjacent roads.
|
|
|
|
// TODO Maybe do this by directed roads instead. Otherwise the corner is too big? But not
|
|
|
|
// sure what the intersections will look like yet.
|
|
|
|
let mut road_centers: Vec<(PolyLine, Distance)> = map
|
|
|
|
.get_i(id)
|
|
|
|
.roads
|
|
|
|
.iter()
|
|
|
|
.map(|r| {
|
|
|
|
let road = map.get_r(*r);
|
|
|
|
let (pl, width) = road.get_thick_polyline(true).unwrap();
|
|
|
|
if road.dst_i == id {
|
|
|
|
(pl, width)
|
|
|
|
} else {
|
|
|
|
(pl.reversed(), width)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
// TODO Right?
|
|
|
|
let common_pt = map.get_i(id).point;
|
|
|
|
// TODO Brittle because of f64->i64 and for short last lines
|
|
|
|
road_centers.sort_by_key(|(pl, _)| {
|
|
|
|
pl.last_line()
|
|
|
|
.pt1()
|
|
|
|
.angle_to(common_pt)
|
|
|
|
.normalized_degrees() as i64
|
|
|
|
});
|
|
|
|
|
|
|
|
for idx in 0..road_centers.len() as isize {
|
|
|
|
// pl1 to pl2 moves clockwise
|
|
|
|
let (pl1, width1) = wraparound_get(&road_centers, idx);
|
|
|
|
let (pl2, width2) = wraparound_get(&road_centers, idx + 1);
|
|
|
|
|
2019-05-28 17:47:04 +03:00
|
|
|
let glued = pl1.clone().extend(pl2.reversed());
|
2019-05-18 01:52:08 +03:00
|
|
|
let max_width = (*width1).max(*width2);
|
|
|
|
let poly = Polygon::new(&glued.to_thick_boundary_pts(max_width));
|
|
|
|
g.draw_polygon(Color::RED.alpha(0.4), &poly);
|
|
|
|
all_polys.push(poly);
|
|
|
|
//break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if false {
|
|
|
|
if let Some(p) = intersection_many(&all_polys) {
|
|
|
|
g.draw_polygon(Color::GREEN.alpha(0.4), &p);
|
2019-05-17 23:27:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-18 01:52:08 +03:00
|
|
|
|
2019-05-18 21:45:09 +03:00
|
|
|
if true {
|
2019-05-18 01:52:08 +03:00
|
|
|
for r in &map.get_i(id).roads {
|
2019-05-18 21:45:09 +03:00
|
|
|
let road = map.get_r(*r);
|
|
|
|
let (orig_pl, width) = road.get_thick_polyline(true).unwrap();
|
|
|
|
let dir_pl = if road.dst_i == id {
|
|
|
|
orig_pl
|
|
|
|
} else {
|
|
|
|
orig_pl.reversed()
|
|
|
|
};
|
|
|
|
// Extend the last line by, say, the length of the original road.
|
|
|
|
let last_pt = dir_pl
|
|
|
|
.last_line()
|
|
|
|
.pt2()
|
|
|
|
.project_away(dir_pl.length(), dir_pl.last_line().angle());
|
|
|
|
let mut pts = dir_pl.points().clone();
|
|
|
|
pts.pop();
|
|
|
|
pts.push(last_pt);
|
|
|
|
|
2019-05-18 01:52:08 +03:00
|
|
|
// This is different than pl.make_polygons(width) because of the order of the points!!!
|
2019-05-18 21:45:09 +03:00
|
|
|
let poly = Polygon::new(&PolyLine::new(pts).to_thick_boundary_pts(width));
|
|
|
|
//g.draw_polygon(Color::RED.alpha(0.4), &poly);
|
2019-05-18 01:52:08 +03:00
|
|
|
all_polys.push(poly);
|
|
|
|
}
|
2019-05-18 21:45:09 +03:00
|
|
|
|
|
|
|
if let Some(p) = intersection_many(&all_polys) {
|
|
|
|
g.draw_polygon(Color::GREEN.alpha(0.4), &p);
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
}
|
2019-05-18 01:52:08 +03:00
|
|
|
|
2019-05-17 23:27:30 +03:00
|
|
|
if false {
|
2019-05-18 01:52:08 +03:00
|
|
|
let mut all_pieces = Vec::new();
|
|
|
|
for (idx1, p1) in all_polys.iter().enumerate() {
|
|
|
|
for (idx2, p2) in all_polys.iter().enumerate() {
|
|
|
|
if idx1 != idx2 {
|
|
|
|
all_pieces.extend(intersection(p1, p2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for p in &all_pieces {
|
|
|
|
g.draw_polygon(Color::BLUE.alpha(0.4), &p);
|
|
|
|
}
|
|
|
|
if false {
|
|
|
|
if let Some(final_poly) = union(&all_pieces) {
|
|
|
|
g.draw_polygon(Color::GREEN.alpha(0.4), &final_poly);
|
|
|
|
}
|
2019-05-17 23:27:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn poly_to_cpoly(poly: &Polygon) -> CPolygon {
|
|
|
|
let mut pts: Vec<[f64; 2]> = poly.points().iter().map(|pt| [pt.x(), pt.y()]).collect();
|
|
|
|
if pts[0] == *pts.last().unwrap() {
|
|
|
|
pts.pop();
|
|
|
|
}
|
|
|
|
CPolygon::from_vec(&pts)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cpoly_to_poly(raw_pts: Vec<[f64; 2]>) -> Polygon {
|
|
|
|
let mut pts: Vec<Pt2D> = raw_pts
|
|
|
|
.into_iter()
|
|
|
|
.map(|pt| Pt2D::new(pt[0], pt[1]))
|
|
|
|
.collect();
|
|
|
|
if pts[0] != *pts.last().unwrap() {
|
|
|
|
pts.push(pts[0]);
|
|
|
|
}
|
|
|
|
Polygon::new(&pts)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn intersection(p1: &Polygon, p2: &Polygon) -> Vec<Polygon> {
|
|
|
|
let mut cp1 = poly_to_cpoly(p1);
|
|
|
|
let mut cp2 = poly_to_cpoly(p2);
|
|
|
|
cp1.intersection(&mut cp2)
|
|
|
|
.into_iter()
|
2019-05-29 22:55:34 +03:00
|
|
|
.map(cpoly_to_poly)
|
2019-05-17 23:27:30 +03:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn union(polys: &Vec<Polygon>) -> Option<Polygon> {
|
|
|
|
let mut result = poly_to_cpoly(&polys[0]);
|
|
|
|
for p in polys.iter().skip(1) {
|
|
|
|
let output = result.union(&mut poly_to_cpoly(p));
|
|
|
|
if output.len() != 1 {
|
|
|
|
println!("Argh, got {} pieces from union", output.len());
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
result = CPolygon::from_vec(&output[0]);
|
|
|
|
}
|
|
|
|
Some(cpoly_to_poly(result.points()))
|
|
|
|
}
|
2019-05-18 01:52:08 +03:00
|
|
|
|
|
|
|
fn intersection_many(polys: &Vec<Polygon>) -> Option<Polygon> {
|
|
|
|
let mut result = poly_to_cpoly(&polys[0]);
|
|
|
|
for p in polys.iter().skip(1) {
|
|
|
|
let output = result.intersection(&mut poly_to_cpoly(p));
|
|
|
|
if output.len() != 1 {
|
|
|
|
println!("Argh, got {} pieces from intersection", output.len());
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
result = CPolygon::from_vec(&output[0]);
|
|
|
|
}
|
|
|
|
Some(cpoly_to_poly(result.points()))
|
|
|
|
}
|