2018-06-04 03:47:46 +03:00
|
|
|
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
2018-03-13 18:04:21 +03:00
|
|
|
|
|
|
|
// TODO this should just be a way to handle interactions between plugins
|
|
|
|
|
2018-06-22 21:01:44 +03:00
|
|
|
use abstutil;
|
2018-06-21 20:03:51 +03:00
|
|
|
use colors::{ColorScheme, Colors};
|
2018-05-11 02:12:16 +03:00
|
|
|
use control::ControlMap;
|
2018-06-22 21:01:44 +03:00
|
|
|
use control::{ModifiedStopSign, ModifiedTrafficSignal};
|
2018-09-11 03:15:47 +03:00
|
|
|
use ezgui::{shift_color, Canvas, EventLoopMode, GfxCtx, ToggleableLayer, UserInput, GUI};
|
2018-09-08 23:01:36 +03:00
|
|
|
use flame;
|
2018-08-09 20:49:20 +03:00
|
|
|
use geom::Pt2D;
|
2018-03-13 18:04:21 +03:00
|
|
|
use graphics::types::Color;
|
2018-08-10 23:28:34 +03:00
|
|
|
use kml;
|
2018-06-25 19:11:25 +03:00
|
|
|
use map_model;
|
2018-08-27 23:03:35 +03:00
|
|
|
use map_model::IntersectionID;
|
2018-09-13 20:50:59 +03:00
|
|
|
use objects::ID;
|
2018-04-11 03:38:26 +03:00
|
|
|
use piston::input::{Key, MouseCursorEvent};
|
2018-03-13 18:04:21 +03:00
|
|
|
use piston::window::Size;
|
|
|
|
use plugins::classification::OsmClassifier;
|
2018-06-21 04:30:38 +03:00
|
|
|
use plugins::color_picker::ColorPicker;
|
2018-09-13 19:59:49 +03:00
|
|
|
use plugins::debug_objects::DebugObjectsState;
|
2018-03-13 18:04:21 +03:00
|
|
|
use plugins::floodfill::Floodfiller;
|
2018-08-29 21:11:51 +03:00
|
|
|
use plugins::follow::FollowState;
|
2018-07-06 01:37:35 +03:00
|
|
|
use plugins::geom_validation::Validator;
|
2018-09-13 19:43:04 +03:00
|
|
|
use plugins::hider::Hider;
|
2018-07-24 22:58:55 +03:00
|
|
|
use plugins::road_editor::RoadEditor;
|
2018-03-13 18:04:21 +03:00
|
|
|
use plugins::search::SearchState;
|
2018-08-31 01:15:37 +03:00
|
|
|
use plugins::show_route::ShowRouteState;
|
2018-04-11 03:38:26 +03:00
|
|
|
use plugins::sim_controls::SimController;
|
2018-03-13 18:04:21 +03:00
|
|
|
use plugins::steep::SteepnessVisualizer;
|
|
|
|
use plugins::stop_sign_editor::StopSignEditor;
|
|
|
|
use plugins::traffic_signal_editor::TrafficSignalEditor;
|
|
|
|
use plugins::turn_colors::TurnColors;
|
2018-09-13 02:05:35 +03:00
|
|
|
use plugins::turn_cycler::TurnCyclerState;
|
2018-06-26 18:44:58 +03:00
|
|
|
use plugins::warp::WarpState;
|
2018-03-13 18:04:21 +03:00
|
|
|
use render;
|
2018-09-11 23:12:48 +03:00
|
|
|
use render::Renderable;
|
2018-08-27 23:03:35 +03:00
|
|
|
use sim;
|
2018-09-13 03:39:53 +03:00
|
|
|
use sim::{AgentID, CarID, CarState, PedestrianID, Sim};
|
2018-08-31 01:15:37 +03:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2018-03-13 18:04:21 +03:00
|
|
|
use std::process;
|
|
|
|
|
|
|
|
// TODO ideally these would be tuned kind of dynamically based on rendering speed
|
2018-07-24 05:31:15 +03:00
|
|
|
const MIN_ZOOM_FOR_LANES: f64 = 0.15;
|
2018-03-13 18:04:21 +03:00
|
|
|
const MIN_ZOOM_FOR_PARCELS: f64 = 1.0;
|
|
|
|
const MIN_ZOOM_FOR_MOUSEOVER: f64 = 1.0;
|
2018-07-24 05:31:15 +03:00
|
|
|
const MIN_ZOOM_FOR_LANE_MARKERS: f64 = 5.0;
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
// Necessary so we can iterate over and run the plugins, which mutably borrow UI.
|
|
|
|
pub struct UIWrapper {
|
|
|
|
ui: UI,
|
|
|
|
plugins: Vec<Box<Fn(&mut UI, &mut UserInput) -> bool>>,
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
impl GUI for UIWrapper {
|
|
|
|
fn event(&mut self, input: &mut UserInput) -> EventLoopMode {
|
|
|
|
self.ui.event(input, &self.plugins)
|
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
fn draw(&mut self, g: &mut GfxCtx, input: UserInput, window_size: Size) {
|
|
|
|
// Since self is mut here, we can set window_size on the canvas, but then let the real
|
|
|
|
// draw() be immutable.
|
|
|
|
self.ui.canvas.start_drawing(g, window_size);
|
|
|
|
self.ui.draw(g, input);
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
impl UIWrapper {
|
|
|
|
// nit: lots of this logic could live in UI, if it mattered
|
2018-08-04 20:11:52 +03:00
|
|
|
pub fn new(
|
2018-08-27 23:03:35 +03:00
|
|
|
load: String,
|
2018-08-27 20:18:08 +03:00
|
|
|
scenario_name: String,
|
2018-08-04 20:11:52 +03:00
|
|
|
rng_seed: Option<u8>,
|
2018-08-10 23:28:34 +03:00
|
|
|
kml: Option<String>,
|
2018-09-13 08:43:58 +03:00
|
|
|
) -> UIWrapper {
|
2018-09-08 23:08:38 +03:00
|
|
|
flame::start("setup");
|
2018-09-01 01:03:15 +03:00
|
|
|
let (map, edits, control_map, sim) = sim::load(
|
2018-08-27 23:29:00 +03:00
|
|
|
load,
|
|
|
|
scenario_name,
|
|
|
|
rng_seed,
|
|
|
|
Some(sim::Tick::from_seconds(30)),
|
|
|
|
);
|
2018-08-10 23:28:34 +03:00
|
|
|
|
|
|
|
let extra_shapes = if let Some(path) = kml {
|
|
|
|
kml::load(&path, &map.get_gps_bounds()).expect("Couldn't load extra KML shapes")
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
|
|
|
|
2018-09-08 23:01:36 +03:00
|
|
|
flame::start("draw_map");
|
2018-08-24 20:51:29 +03:00
|
|
|
let (draw_map, center_pt) = render::DrawMap::new(&map, &control_map, extra_shapes);
|
2018-09-08 23:01:36 +03:00
|
|
|
flame::end("draw_map");
|
|
|
|
|
2018-09-08 23:08:38 +03:00
|
|
|
flame::end("setup");
|
2018-09-08 23:01:36 +03:00
|
|
|
flame::dump_stdout();
|
2018-03-13 18:04:21 +03:00
|
|
|
|
|
|
|
let steepness_viz = SteepnessVisualizer::new(&map);
|
|
|
|
let turn_colors = TurnColors::new(&control_map);
|
|
|
|
|
|
|
|
let mut ui = UI {
|
2018-09-13 03:39:53 +03:00
|
|
|
// TODO organize this by section
|
2018-03-13 18:04:21 +03:00
|
|
|
map,
|
|
|
|
draw_map,
|
|
|
|
control_map,
|
2018-09-13 03:39:53 +03:00
|
|
|
sim,
|
|
|
|
|
2018-03-13 18:04:21 +03:00
|
|
|
steepness_viz,
|
|
|
|
turn_colors,
|
2018-09-13 03:39:53 +03:00
|
|
|
sim_ctrl: SimController::new(),
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-08-15 00:06:27 +03:00
|
|
|
show_lanes: ToggleableLayer::new("lanes", Key::D3, Some(MIN_ZOOM_FOR_LANES)),
|
|
|
|
show_buildings: ToggleableLayer::new("buildings", Key::D1, Some(0.0)),
|
2018-03-13 18:04:21 +03:00
|
|
|
show_intersections: ToggleableLayer::new(
|
|
|
|
"intersections",
|
|
|
|
Key::D2,
|
2018-07-24 05:31:15 +03:00
|
|
|
Some(MIN_ZOOM_FOR_LANES),
|
2018-03-13 18:04:21 +03:00
|
|
|
),
|
2018-08-15 00:06:27 +03:00
|
|
|
show_parcels: ToggleableLayer::new("parcels", Key::D4, Some(MIN_ZOOM_FOR_PARCELS)),
|
2018-08-10 23:28:34 +03:00
|
|
|
show_extra_shapes: ToggleableLayer::new(
|
|
|
|
"extra KML shapes",
|
|
|
|
Key::D7,
|
|
|
|
Some(MIN_ZOOM_FOR_LANES),
|
|
|
|
),
|
2018-08-24 20:51:29 +03:00
|
|
|
show_all_turn_icons: ToggleableLayer::new("turn icons", Key::D9, None),
|
2018-08-15 00:06:27 +03:00
|
|
|
debug_mode: ToggleableLayer::new("debug mode", Key::G, None),
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 20:50:59 +03:00
|
|
|
current_selection: None,
|
|
|
|
|
2018-06-29 21:53:55 +03:00
|
|
|
hider: Hider::new(),
|
2018-09-13 19:59:49 +03:00
|
|
|
debug_objects: DebugObjectsState::new(),
|
2018-03-13 18:04:21 +03:00
|
|
|
current_search_state: SearchState::Empty,
|
2018-06-26 18:44:58 +03:00
|
|
|
warp: WarpState::Empty,
|
2018-08-29 21:11:51 +03:00
|
|
|
follow: FollowState::Empty,
|
2018-08-31 01:15:37 +03:00
|
|
|
show_route: ShowRouteState::Empty,
|
2018-07-06 23:34:24 +03:00
|
|
|
floodfiller: Floodfiller::new(),
|
2018-06-22 20:26:36 +03:00
|
|
|
osm_classifier: OsmClassifier::new(),
|
2018-07-06 23:45:05 +03:00
|
|
|
traffic_signal_editor: TrafficSignalEditor::new(),
|
2018-07-06 23:49:03 +03:00
|
|
|
stop_sign_editor: StopSignEditor::new(),
|
2018-07-24 22:58:55 +03:00
|
|
|
road_editor: RoadEditor::new(edits),
|
2018-06-21 04:30:38 +03:00
|
|
|
color_picker: ColorPicker::new(),
|
2018-07-06 23:56:00 +03:00
|
|
|
geom_validator: Validator::new(),
|
2018-09-13 02:05:35 +03:00
|
|
|
turn_cycler: TurnCyclerState::new(),
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 19:21:41 +03:00
|
|
|
active_plugin: None,
|
|
|
|
|
2018-09-10 03:10:34 +03:00
|
|
|
canvas: Canvas::new(),
|
2018-06-21 20:42:30 +03:00
|
|
|
cs: ColorScheme::load("color_scheme").unwrap(),
|
2018-03-13 18:04:21 +03:00
|
|
|
};
|
|
|
|
|
2018-06-22 21:01:44 +03:00
|
|
|
match abstutil::read_json::<EditorState>("editor_state") {
|
2018-09-09 23:21:53 +03:00
|
|
|
Ok(ref state) if *ui.map.get_name() == state.map_name => {
|
2018-03-13 18:04:21 +03:00
|
|
|
println!("Loaded previous editor_state");
|
|
|
|
ui.canvas.cam_x = state.cam_x;
|
|
|
|
ui.canvas.cam_y = state.cam_y;
|
|
|
|
ui.canvas.cam_zoom = state.cam_zoom;
|
2018-04-11 03:38:26 +03:00
|
|
|
ui.control_map
|
|
|
|
.load_savestate(&state.traffic_signals, &state.stop_signs);
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
2018-09-09 23:21:53 +03:00
|
|
|
_ => {
|
|
|
|
println!("Couldn't load editor_state or it's for a different map, so just centering initial view");
|
2018-07-06 20:02:01 +03:00
|
|
|
ui.canvas.center_on_map_pt(center_pt.x(), center_pt.y());
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let new_zoom = ui.canvas.cam_zoom;
|
2018-09-13 20:36:58 +03:00
|
|
|
for layer in ui.toggleable_layers().into_iter() {
|
|
|
|
layer.handle_zoom(-1.0, new_zoom);
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
UIWrapper {
|
|
|
|
ui,
|
|
|
|
plugins: vec![
|
|
|
|
Box::new(|ui, input| {
|
|
|
|
ui.traffic_signal_editor.event(
|
|
|
|
input,
|
|
|
|
&ui.map,
|
|
|
|
&mut ui.control_map,
|
2018-09-13 20:50:59 +03:00
|
|
|
ui.current_selection,
|
2018-09-13 08:43:58 +03:00
|
|
|
)
|
|
|
|
}),
|
|
|
|
Box::new(|ui, input| {
|
|
|
|
ui.stop_sign_editor.event(
|
|
|
|
input,
|
|
|
|
&ui.map,
|
|
|
|
&mut ui.control_map,
|
2018-09-13 20:50:59 +03:00
|
|
|
ui.current_selection,
|
2018-09-13 08:43:58 +03:00
|
|
|
)
|
|
|
|
}),
|
|
|
|
Box::new(|ui, input| {
|
|
|
|
ui.road_editor.event(
|
|
|
|
input,
|
2018-09-13 20:50:59 +03:00
|
|
|
ui.current_selection,
|
2018-09-13 08:43:58 +03:00
|
|
|
&mut ui.map,
|
|
|
|
&mut ui.draw_map,
|
|
|
|
&ui.control_map,
|
|
|
|
&mut ui.sim,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
Box::new(|ui, input| ui.current_search_state.event(input)),
|
|
|
|
Box::new(|ui, input| {
|
|
|
|
ui.warp.event(
|
|
|
|
input,
|
|
|
|
&ui.map,
|
|
|
|
&ui.sim,
|
|
|
|
&mut ui.canvas,
|
2018-09-13 20:50:59 +03:00
|
|
|
&mut ui.current_selection,
|
2018-09-13 08:43:58 +03:00
|
|
|
)
|
|
|
|
}),
|
|
|
|
Box::new(|ui, input| ui.follow.event(input, &ui.map, &ui.sim, &mut ui.canvas)),
|
|
|
|
Box::new(|ui, input| ui.show_route.event(input, &ui.sim)),
|
2018-09-13 19:26:04 +03:00
|
|
|
Box::new(|ui, input| ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)),
|
|
|
|
Box::new(|ui, input| ui.steepness_viz.event(input)),
|
|
|
|
Box::new(|ui, input| ui.osm_classifier.event(input)),
|
2018-09-13 20:50:59 +03:00
|
|
|
Box::new(|ui, input| ui.hider.event(input, &mut ui.current_selection)),
|
2018-09-13 19:59:49 +03:00
|
|
|
Box::new(|ui, input| {
|
2018-09-13 20:50:59 +03:00
|
|
|
ui.debug_objects.event(
|
|
|
|
ui.current_selection,
|
|
|
|
input,
|
|
|
|
&ui.map,
|
|
|
|
&mut ui.sim,
|
|
|
|
&ui.control_map,
|
|
|
|
)
|
2018-09-13 19:59:49 +03:00
|
|
|
}),
|
2018-09-13 08:43:58 +03:00
|
|
|
Box::new(|ui, input| ui.floodfiller.event(&ui.map, input)),
|
2018-09-13 19:21:41 +03:00
|
|
|
Box::new(|ui, input| ui.geom_validator.event(input, &mut ui.canvas, &ui.map)),
|
2018-09-13 20:50:59 +03:00
|
|
|
Box::new(|ui, input| ui.turn_cycler.event(input, ui.current_selection)),
|
2018-09-13 08:43:58 +03:00
|
|
|
],
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct UI {
|
|
|
|
map: map_model::Map,
|
|
|
|
draw_map: render::DrawMap,
|
|
|
|
control_map: ControlMap,
|
|
|
|
sim: Sim,
|
|
|
|
|
|
|
|
show_lanes: ToggleableLayer,
|
|
|
|
show_buildings: ToggleableLayer,
|
|
|
|
show_intersections: ToggleableLayer,
|
|
|
|
show_parcels: ToggleableLayer,
|
|
|
|
show_extra_shapes: ToggleableLayer,
|
|
|
|
show_all_turn_icons: ToggleableLayer,
|
|
|
|
debug_mode: ToggleableLayer,
|
|
|
|
|
2018-09-13 20:50:59 +03:00
|
|
|
current_selection: Option<ID>,
|
2018-09-13 08:43:58 +03:00
|
|
|
|
|
|
|
hider: Hider,
|
2018-09-13 19:59:49 +03:00
|
|
|
debug_objects: DebugObjectsState,
|
2018-09-13 08:43:58 +03:00
|
|
|
current_search_state: SearchState,
|
|
|
|
warp: WarpState,
|
|
|
|
follow: FollowState,
|
|
|
|
show_route: ShowRouteState,
|
|
|
|
floodfiller: Floodfiller,
|
|
|
|
steepness_viz: SteepnessVisualizer,
|
|
|
|
osm_classifier: OsmClassifier,
|
|
|
|
traffic_signal_editor: TrafficSignalEditor,
|
|
|
|
stop_sign_editor: StopSignEditor,
|
|
|
|
road_editor: RoadEditor,
|
|
|
|
sim_ctrl: SimController,
|
|
|
|
color_picker: ColorPicker,
|
|
|
|
geom_validator: Validator,
|
|
|
|
turn_cycler: TurnCyclerState,
|
|
|
|
|
2018-09-13 19:21:41 +03:00
|
|
|
// An index into UIWrapper.plugins.
|
|
|
|
active_plugin: Option<usize>,
|
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
// Not really a plugin; it doesn't react to anything.
|
|
|
|
turn_colors: TurnColors,
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
canvas: Canvas,
|
|
|
|
// TODO maybe never pass this to other places? Always resolve colors here?
|
|
|
|
cs: ColorScheme,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UI {
|
2018-09-13 20:36:58 +03:00
|
|
|
fn toggleable_layers(&mut self) -> Vec<&mut ToggleableLayer> {
|
|
|
|
vec![
|
|
|
|
&mut self.show_lanes,
|
|
|
|
&mut self.show_buildings,
|
|
|
|
&mut self.show_intersections,
|
|
|
|
&mut self.show_parcels,
|
|
|
|
&mut self.show_extra_shapes,
|
|
|
|
&mut self.show_all_turn_icons,
|
|
|
|
&mut self.debug_mode,
|
|
|
|
]
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
|
2018-07-06 20:02:01 +03:00
|
|
|
fn mouseover_something(&self) -> Option<ID> {
|
2018-06-23 19:01:53 +03:00
|
|
|
let (x, y) = self.canvas.get_cursor_in_map_space();
|
2018-08-09 20:49:20 +03:00
|
|
|
let pt = Pt2D::new(x, y);
|
2018-06-23 19:01:53 +03:00
|
|
|
|
2018-07-06 20:02:01 +03:00
|
|
|
let screen_bbox = self.canvas.get_screen_bbox();
|
2018-06-23 19:01:53 +03:00
|
|
|
|
2018-08-24 00:37:13 +03:00
|
|
|
if self.show_extra_shapes.is_enabled() {
|
|
|
|
for s in &self.draw_map
|
|
|
|
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
|
|
|
if s.contains_pt(pt) {
|
|
|
|
return Some(ID::ExtraShape(s.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 05:31:15 +03:00
|
|
|
let lanes_onscreen = if self.show_lanes.is_enabled() {
|
|
|
|
self.draw_map.get_loads_onscreen(screen_bbox, &self.hider)
|
2018-06-23 19:01:53 +03:00
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
2018-07-24 05:31:15 +03:00
|
|
|
for l in &lanes_onscreen {
|
2018-09-13 03:39:53 +03:00
|
|
|
for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) {
|
2018-08-09 20:49:20 +03:00
|
|
|
if c.contains_pt(pt) {
|
2018-06-23 19:01:53 +03:00
|
|
|
return Some(ID::Car(c.id));
|
|
|
|
}
|
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
for p in &self.sim.get_draw_peds_on_lane(l.id, &self.map) {
|
2018-08-09 20:49:20 +03:00
|
|
|
if p.contains_pt(pt) {
|
2018-07-12 00:01:12 +03:00
|
|
|
return Some(ID::Pedestrian(p.id));
|
|
|
|
}
|
|
|
|
}
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.show_intersections.is_enabled() {
|
2018-06-29 21:53:55 +03:00
|
|
|
for i in &self.draw_map
|
|
|
|
.get_intersections_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
2018-08-06 00:17:18 +03:00
|
|
|
let show_icons = self.show_icons_for(i.id);
|
|
|
|
|
2018-06-23 19:01:53 +03:00
|
|
|
for t in &self.map.get_i(i.id).turns {
|
2018-08-09 20:49:20 +03:00
|
|
|
if show_icons && self.draw_map.get_t(*t).contains_pt(pt) {
|
2018-08-06 00:17:18 +03:00
|
|
|
return Some(ID::Turn(*t));
|
|
|
|
}
|
|
|
|
|
2018-09-13 03:39:53 +03:00
|
|
|
for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) {
|
2018-08-09 20:49:20 +03:00
|
|
|
if c.contains_pt(pt) {
|
2018-06-23 19:01:53 +03:00
|
|
|
return Some(ID::Car(c.id));
|
|
|
|
}
|
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
for p in &self.sim.get_draw_peds_on_turn(*t, &self.map) {
|
2018-08-09 20:49:20 +03:00
|
|
|
if p.contains_pt(pt) {
|
2018-07-12 00:01:12 +03:00
|
|
|
return Some(ID::Pedestrian(p.id));
|
|
|
|
}
|
|
|
|
}
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
|
2018-08-09 20:49:20 +03:00
|
|
|
if i.contains_pt(pt) {
|
2018-06-23 19:01:53 +03:00
|
|
|
return Some(ID::Intersection(i.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 05:31:15 +03:00
|
|
|
if self.show_lanes.is_enabled() {
|
|
|
|
for l in &lanes_onscreen {
|
2018-08-09 20:49:20 +03:00
|
|
|
if l.contains_pt(pt) {
|
2018-07-24 05:31:15 +03:00
|
|
|
return Some(ID::Lane(l.id));
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.show_buildings.is_enabled() {
|
2018-06-29 21:53:55 +03:00
|
|
|
for b in &self.draw_map
|
|
|
|
.get_buildings_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
2018-08-09 20:49:20 +03:00
|
|
|
if b.contains_pt(pt) {
|
2018-06-23 19:01:53 +03:00
|
|
|
return Some(ID::Building(b.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2018-07-24 05:31:15 +03:00
|
|
|
fn color_lane(&self, id: map_model::LaneID) -> Color {
|
|
|
|
let l = self.map.get_l(id);
|
|
|
|
let mut default = match l.lane_type {
|
2018-06-23 19:01:53 +03:00
|
|
|
map_model::LaneType::Driving => self.cs.get(Colors::Road),
|
|
|
|
map_model::LaneType::Parking => self.cs.get(Colors::Parking),
|
|
|
|
map_model::LaneType::Sidewalk => self.cs.get(Colors::Sidewalk),
|
2018-07-23 20:50:01 +03:00
|
|
|
map_model::LaneType::Biking => self.cs.get(Colors::Biking),
|
2018-06-23 19:01:53 +03:00
|
|
|
};
|
2018-07-24 05:31:15 +03:00
|
|
|
if l.probably_broken {
|
2018-06-29 20:55:55 +03:00
|
|
|
default = self.cs.get(Colors::Broken);
|
|
|
|
}
|
2018-06-23 19:01:53 +03:00
|
|
|
|
|
|
|
// TODO This evaluates all the color methods, which may be expensive. But the option
|
|
|
|
// chaining is harder to read. :(
|
|
|
|
vec![
|
2018-09-13 20:50:59 +03:00
|
|
|
self.color_for_selected(ID::Lane(l.id)),
|
2018-08-31 01:15:37 +03:00
|
|
|
self.show_route.color_l(l.id, &self.cs),
|
2018-07-24 18:05:25 +03:00
|
|
|
self.current_search_state.color_l(l, &self.map, &self.cs),
|
2018-07-24 05:31:15 +03:00
|
|
|
self.floodfiller.color_l(l, &self.cs),
|
|
|
|
self.steepness_viz.color_l(&self.map, l),
|
2018-07-24 18:05:25 +03:00
|
|
|
self.osm_classifier.color_l(l, &self.map, &self.cs),
|
2018-06-23 19:01:53 +03:00
|
|
|
].iter()
|
|
|
|
.filter_map(|c| *c)
|
|
|
|
.next()
|
|
|
|
.unwrap_or(default)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn color_intersection(&self, id: map_model::IntersectionID) -> Color {
|
|
|
|
let i = self.map.get_i(id);
|
2018-07-04 23:27:04 +03:00
|
|
|
let changed = if let Some(s) = self.control_map.traffic_signals.get(&i.id) {
|
|
|
|
s.changed()
|
2018-06-23 19:01:53 +03:00
|
|
|
} else if let Some(s) = self.control_map.stop_signs.get(&i.id) {
|
2018-07-04 23:27:04 +03:00
|
|
|
s.changed()
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
let default_color = if changed {
|
|
|
|
self.cs.get(Colors::ChangedIntersection)
|
2018-06-23 19:01:53 +03:00
|
|
|
} else {
|
2018-07-04 23:27:04 +03:00
|
|
|
self.cs.get(Colors::UnchangedIntersection)
|
2018-06-23 19:01:53 +03:00
|
|
|
};
|
|
|
|
|
2018-09-13 20:50:59 +03:00
|
|
|
self.color_for_selected(ID::Intersection(i.id))
|
2018-06-23 19:01:53 +03:00
|
|
|
.unwrap_or(default_color)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn color_turn_icon(&self, id: map_model::TurnID) -> Color {
|
|
|
|
let t = self.map.get_t(id);
|
|
|
|
// TODO traffic signal selection logic maybe moves here
|
2018-09-13 20:50:59 +03:00
|
|
|
self.color_for_selected(ID::Turn(t.id)).unwrap_or_else(|| {
|
|
|
|
self.stop_sign_editor
|
|
|
|
.color_t(t, &self.control_map, &self.cs)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
self.traffic_signal_editor
|
|
|
|
.color_t(t, &self.map, &self.control_map, &self.cs)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
self.turn_colors
|
|
|
|
.color_t(t)
|
|
|
|
.unwrap_or(self.cs.get(Colors::TurnIconInactive))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn color_building(&self, id: map_model::BuildingID) -> Color {
|
|
|
|
let b = self.map.get_b(id);
|
|
|
|
vec![
|
2018-09-13 20:50:59 +03:00
|
|
|
self.color_for_selected(ID::Building(b.id)),
|
2018-06-23 19:01:53 +03:00
|
|
|
self.current_search_state.color_b(b, &self.cs),
|
|
|
|
self.osm_classifier.color_b(b, &self.cs),
|
|
|
|
].iter()
|
|
|
|
.filter_map(|c| *c)
|
|
|
|
.next()
|
|
|
|
.unwrap_or(self.cs.get(Colors::Building))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns (boundary, fill) color
|
2018-09-11 23:12:48 +03:00
|
|
|
fn color_parcel(&self, id: map_model::ParcelID) -> Color {
|
2018-08-06 21:33:06 +03:00
|
|
|
const COLORS: [Color; 14] = [
|
|
|
|
// TODO these are awful choices
|
|
|
|
[1.0, 1.0, 0.0, 1.0],
|
|
|
|
[1.0, 0.0, 1.0, 1.0],
|
|
|
|
[0.0, 1.0, 1.0, 1.0],
|
|
|
|
[0.5, 0.2, 0.7, 1.0],
|
|
|
|
[0.5, 0.5, 0.0, 0.5],
|
|
|
|
[0.5, 0.0, 0.5, 0.5],
|
|
|
|
[0.0, 0.5, 0.5, 0.5],
|
|
|
|
[0.0, 0.0, 0.5, 0.5],
|
|
|
|
[0.3, 0.2, 0.5, 0.5],
|
|
|
|
[0.4, 0.2, 0.5, 0.5],
|
|
|
|
[0.5, 0.2, 0.5, 0.5],
|
|
|
|
[0.6, 0.2, 0.5, 0.5],
|
|
|
|
[0.7, 0.2, 0.5, 0.5],
|
|
|
|
[0.8, 0.2, 0.5, 0.5],
|
|
|
|
];
|
|
|
|
let p = self.map.get_p(id);
|
2018-09-11 23:12:48 +03:00
|
|
|
/*(
|
2018-06-23 19:01:53 +03:00
|
|
|
self.cs.get(Colors::ParcelBoundary),
|
2018-08-06 21:33:06 +03:00
|
|
|
COLORS[p.block % COLORS.len()],
|
2018-09-11 23:12:48 +03:00
|
|
|
)*/
|
|
|
|
COLORS[p.block % COLORS.len()]
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn color_car(&self, id: CarID) -> Color {
|
2018-09-13 20:50:59 +03:00
|
|
|
if let Some(c) = self.color_for_selected(ID::Car(id)) {
|
2018-06-23 19:01:53 +03:00
|
|
|
return c;
|
|
|
|
}
|
2018-08-29 23:40:07 +03:00
|
|
|
// TODO if it's a bus, color it differently -- but how? :\
|
2018-09-13 03:39:53 +03:00
|
|
|
match self.sim.get_car_state(id) {
|
2018-09-11 03:15:47 +03:00
|
|
|
CarState::Debug => shift_color(self.cs.get(Colors::DebugCar), id.0),
|
|
|
|
CarState::Moving => shift_color(self.cs.get(Colors::MovingCar), id.0),
|
|
|
|
CarState::Stuck => shift_color(self.cs.get(Colors::StuckCar), id.0),
|
|
|
|
CarState::Parked => shift_color(self.cs.get(Colors::ParkedCar), id.0),
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
}
|
2018-07-12 00:01:12 +03:00
|
|
|
|
2018-07-31 23:51:20 +03:00
|
|
|
fn color_ped(&self, id: PedestrianID) -> Color {
|
2018-09-13 20:50:59 +03:00
|
|
|
if let Some(c) = self.color_for_selected(ID::Pedestrian(id)) {
|
2018-07-31 23:51:20 +03:00
|
|
|
return c;
|
|
|
|
}
|
2018-09-11 03:15:47 +03:00
|
|
|
shift_color(self.cs.get(Colors::Pedestrian), id.0)
|
2018-07-12 00:01:12 +03:00
|
|
|
}
|
2018-08-06 00:17:18 +03:00
|
|
|
|
2018-09-13 20:50:59 +03:00
|
|
|
fn color_for_selected(&self, id: ID) -> Option<Color> {
|
|
|
|
if Some(id) == self.current_selection {
|
|
|
|
Some(self.cs.get(Colors::Selected))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 00:17:18 +03:00
|
|
|
fn show_icons_for(&self, id: IntersectionID) -> bool {
|
2018-08-24 20:51:29 +03:00
|
|
|
self.show_all_turn_icons.is_enabled()
|
|
|
|
|| self.stop_sign_editor.show_turn_icons(id)
|
|
|
|
|| self.traffic_signal_editor.show_turn_icons(id)
|
2018-08-06 00:17:18 +03:00
|
|
|
}
|
2018-06-23 19:01:53 +03:00
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
fn event(
|
|
|
|
&mut self,
|
|
|
|
input: &mut UserInput,
|
|
|
|
plugins: &Vec<Box<Fn(&mut UI, &mut UserInput) -> bool>>,
|
|
|
|
) -> EventLoopMode {
|
2018-07-06 21:23:53 +03:00
|
|
|
// First update the camera and handle zoom
|
|
|
|
let old_zoom = self.canvas.cam_zoom;
|
|
|
|
self.canvas.handle_event(input.use_event_directly());
|
|
|
|
let new_zoom = self.canvas.cam_zoom;
|
2018-09-13 20:36:58 +03:00
|
|
|
for layer in self.toggleable_layers().into_iter() {
|
|
|
|
layer.handle_zoom(old_zoom, new_zoom);
|
|
|
|
}
|
2018-07-06 21:23:53 +03:00
|
|
|
|
|
|
|
// Always handle mouseover
|
|
|
|
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
|
2018-09-13 20:50:59 +03:00
|
|
|
self.current_selection = None;
|
2018-07-06 21:23:53 +03:00
|
|
|
}
|
2018-07-09 22:30:59 +03:00
|
|
|
if !self.canvas.is_dragging()
|
|
|
|
&& input.use_event_directly().mouse_cursor_args().is_some()
|
2018-07-06 21:23:53 +03:00
|
|
|
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
|
|
|
|
{
|
2018-09-13 20:50:59 +03:00
|
|
|
self.current_selection = self.mouseover_something();
|
2018-07-06 21:23:53 +03:00
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 19:43:04 +03:00
|
|
|
// TODO Normally we'd return InputOnly here if there was an active plugin, but actually, we
|
|
|
|
// want some keys to always be pressable (sim controller stuff, quitting the game?)
|
|
|
|
|
2018-09-13 19:21:41 +03:00
|
|
|
// If there's an active plugin, just run it.
|
|
|
|
if let Some(idx) = self.active_plugin {
|
|
|
|
if !plugins[idx](self, input) {
|
|
|
|
self.active_plugin = None;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Run each plugin, short-circuiting if the plugin claimed it was active.
|
|
|
|
for (idx, plugin) in plugins.iter().enumerate() {
|
|
|
|
if plugin(self, input) {
|
|
|
|
self.active_plugin = Some(idx);
|
|
|
|
break;
|
|
|
|
}
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-13 20:36:58 +03:00
|
|
|
let layer_changed = {
|
|
|
|
let mut changed = false;
|
|
|
|
for layer in self.toggleable_layers().into_iter() {
|
|
|
|
if layer.event(input) {
|
|
|
|
changed = true;
|
|
|
|
break;
|
2018-07-07 00:30:34 +03:00
|
|
|
}
|
2018-08-24 20:51:29 +03:00
|
|
|
}
|
2018-09-13 20:36:58 +03:00
|
|
|
changed
|
|
|
|
};
|
|
|
|
if layer_changed {
|
2018-09-13 20:50:59 +03:00
|
|
|
self.current_selection = self.mouseover_something();
|
|
|
|
// TODO is it even necessary to update this here? shouldnt all plugins potentilly need
|
|
|
|
// to know? dont we need to also do this when warp/follow/other things potentially move
|
|
|
|
// stuff?
|
|
|
|
self.debug_objects.event(
|
|
|
|
self.current_selection,
|
|
|
|
input,
|
|
|
|
&self.map,
|
|
|
|
&mut self.sim,
|
|
|
|
&self.control_map,
|
|
|
|
);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-10 23:28:34 +03:00
|
|
|
}
|
2018-07-07 00:48:38 +03:00
|
|
|
|
|
|
|
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
|
|
|
|
self.geom_validator = Validator::start(&self.draw_map);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-07-07 00:48:38 +03:00
|
|
|
}
|
2018-07-28 18:42:04 +03:00
|
|
|
if input.unimportant_key_pressed(Key::S, "Seed the map with agents") {
|
2018-09-13 03:39:53 +03:00
|
|
|
self.sim.small_spawn(&self.map);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-07-06 21:23:53 +03:00
|
|
|
}
|
|
|
|
|
2018-09-13 20:50:59 +03:00
|
|
|
match self.current_selection {
|
|
|
|
Some(ID::Car(id)) => {
|
2018-07-06 21:23:53 +03:00
|
|
|
// TODO not sure if we should debug like this (pushing the bit down to all the
|
|
|
|
// layers representing an entity) or by using some scary global mutable singleton
|
2018-07-26 23:56:23 +03:00
|
|
|
if input.unimportant_key_pressed(Key::D, "debug") {
|
2018-09-13 03:39:53 +03:00
|
|
|
self.sim.toggle_debug(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-07-06 21:23:53 +03:00
|
|
|
}
|
2018-08-15 05:45:45 +03:00
|
|
|
if input.key_pressed(Key::A, "start this parked car") {
|
2018-09-13 03:39:53 +03:00
|
|
|
self.sim.start_parked_car(&self.map, id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-15 05:45:45 +03:00
|
|
|
}
|
2018-08-29 21:11:51 +03:00
|
|
|
if input.key_pressed(Key::F, "follow this car") {
|
|
|
|
self.follow = FollowState::FollowingCar(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-29 21:11:51 +03:00
|
|
|
}
|
2018-08-31 01:15:37 +03:00
|
|
|
if input.key_pressed(Key::R, "show this car's route") {
|
|
|
|
self.show_route = ShowRouteState::Active(AgentID::Car(id), HashSet::new());
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-31 01:15:37 +03:00
|
|
|
}
|
2018-08-29 21:11:51 +03:00
|
|
|
}
|
2018-09-13 20:50:59 +03:00
|
|
|
Some(ID::Pedestrian(id)) => {
|
2018-08-29 21:11:51 +03:00
|
|
|
if input.key_pressed(Key::F, "follow this pedestrian") {
|
|
|
|
self.follow = FollowState::FollowingPedestrian(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-29 21:11:51 +03:00
|
|
|
}
|
2018-08-31 01:15:37 +03:00
|
|
|
if input.key_pressed(Key::R, "show this pedestrian's route") {
|
|
|
|
self.show_route =
|
|
|
|
ShowRouteState::Active(AgentID::Pedestrian(id), HashSet::new());
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-08-31 01:15:37 +03:00
|
|
|
}
|
2018-07-06 21:23:53 +03:00
|
|
|
}
|
2018-09-13 20:50:59 +03:00
|
|
|
Some(ID::Lane(id)) => {
|
2018-07-26 23:56:23 +03:00
|
|
|
if input.key_pressed(Key::F, "start floodfilling from this lane") {
|
2018-07-06 23:34:24 +03:00
|
|
|
self.floodfiller = Floodfiller::start(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
|
2018-08-15 07:19:53 +03:00
|
|
|
if self.map.get_l(id).is_sidewalk()
|
|
|
|
&& input.key_pressed(Key::A, "spawn a pedestrian here")
|
|
|
|
{
|
2018-09-13 03:39:53 +03:00
|
|
|
self.sim.spawn_pedestrian(&self.map, id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
2018-09-13 20:50:59 +03:00
|
|
|
Some(ID::Intersection(id)) => {
|
2018-07-06 21:23:53 +03:00
|
|
|
if self.control_map.traffic_signals.contains_key(&id) {
|
2018-07-26 23:56:23 +03:00
|
|
|
if input.key_pressed(Key::E, &format!("edit traffic signal for {:?}", id)) {
|
2018-07-06 23:45:05 +03:00
|
|
|
self.traffic_signal_editor = TrafficSignalEditor::start(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
2018-07-06 21:23:53 +03:00
|
|
|
if self.control_map.stop_signs.contains_key(&id) {
|
2018-07-26 23:56:23 +03:00
|
|
|
if input.key_pressed(Key::E, &format!("edit stop sign for {:?}", id)) {
|
2018-07-06 23:49:03 +03:00
|
|
|
self.stop_sign_editor = StopSignEditor::start(id);
|
2018-09-10 03:10:34 +03:00
|
|
|
return EventLoopMode::InputOnly;
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2018-07-26 23:56:23 +03:00
|
|
|
if input.unimportant_key_pressed(Key::Escape, "quit") {
|
2018-06-22 21:01:44 +03:00
|
|
|
let state = EditorState {
|
2018-09-09 23:21:53 +03:00
|
|
|
map_name: self.map.get_name().clone(),
|
2018-03-13 18:04:21 +03:00
|
|
|
cam_x: self.canvas.cam_x,
|
|
|
|
cam_y: self.canvas.cam_y,
|
|
|
|
cam_zoom: self.canvas.cam_zoom,
|
|
|
|
traffic_signals: self.control_map.get_traffic_signals_savestate(),
|
|
|
|
stop_signs: self.control_map.get_stop_signs_savestate(),
|
|
|
|
};
|
|
|
|
// TODO maybe make state line up with the map, so loading from a new map doesn't break
|
2018-06-22 21:01:44 +03:00
|
|
|
abstutil::write_json("editor_state", &state).expect("Saving editor_state failed");
|
|
|
|
abstutil::write_json("color_scheme", &self.cs).expect("Saving color_scheme failed");
|
2018-07-24 22:58:55 +03:00
|
|
|
abstutil::write_json("road_edits.json", self.road_editor.get_edits())
|
|
|
|
.expect("Saving road_edits.json failed");
|
|
|
|
println!("Saved editor_state, color_scheme, and road_edits.json");
|
2018-03-13 18:04:21 +03:00
|
|
|
process::exit(0);
|
|
|
|
}
|
|
|
|
|
2018-07-06 21:23:53 +03:00
|
|
|
// Sim controller plugin is kind of always active? If nothing else ran, let it use keys.
|
2018-09-13 19:26:04 +03:00
|
|
|
self.sim_ctrl
|
2018-09-13 03:39:53 +03:00
|
|
|
.event(input, &self.map, &self.control_map, &mut self.sim)
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
|
2018-09-13 08:43:58 +03:00
|
|
|
fn draw(&self, g: &mut GfxCtx, input: UserInput) {
|
2018-07-06 20:33:03 +03:00
|
|
|
g.clear(self.cs.get(Colors::Background));
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-07-06 20:02:01 +03:00
|
|
|
let screen_bbox = self.canvas.get_screen_bbox();
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-06-26 21:19:58 +03:00
|
|
|
if self.show_parcels.is_enabled() {
|
|
|
|
for p in &self.draw_map.get_parcels_onscreen(screen_bbox) {
|
2018-09-11 23:12:48 +03:00
|
|
|
p.draw(g, self.color_parcel(p.id), &self.cs);
|
2018-06-26 21:19:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 05:31:15 +03:00
|
|
|
let lanes_onscreen = if self.show_lanes.is_enabled() {
|
|
|
|
self.draw_map.get_loads_onscreen(screen_bbox, &self.hider)
|
2018-04-11 03:38:26 +03:00
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
2018-07-24 05:31:15 +03:00
|
|
|
for l in &lanes_onscreen {
|
2018-09-11 23:12:48 +03:00
|
|
|
l.draw(g, self.color_lane(l.id), &self.cs);
|
2018-07-24 05:31:15 +03:00
|
|
|
if self.canvas.cam_zoom >= MIN_ZOOM_FOR_LANE_MARKERS {
|
2018-09-08 01:13:06 +03:00
|
|
|
l.draw_detail(g, &self.cs);
|
2018-04-11 03:38:26 +03:00
|
|
|
}
|
|
|
|
if self.debug_mode.is_enabled() {
|
2018-09-08 01:13:06 +03:00
|
|
|
l.draw_debug(g, &self.canvas, &self.cs, self.map.get_l(l.id));
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.show_intersections.is_enabled() {
|
2018-06-29 21:53:55 +03:00
|
|
|
for i in &self.draw_map
|
|
|
|
.get_intersections_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
2018-06-29 19:30:31 +03:00
|
|
|
i.draw(g, self.color_intersection(i.id), &self.cs);
|
2018-08-06 00:17:18 +03:00
|
|
|
let show_icons = self.show_icons_for(i.id);
|
2018-07-28 21:21:46 +03:00
|
|
|
for t in &self.map.get_i(i.id).turns {
|
2018-08-06 00:17:18 +03:00
|
|
|
if show_icons {
|
|
|
|
self.draw_map
|
|
|
|
.get_t(*t)
|
2018-09-11 23:12:48 +03:00
|
|
|
.draw(g, self.color_turn_icon(*t), &self.cs);
|
2018-08-06 00:17:18 +03:00
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) {
|
2018-07-28 21:21:46 +03:00
|
|
|
c.draw(g, self.color_car(c.id));
|
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
for p in &self.sim.get_draw_peds_on_turn(*t, &self.map) {
|
2018-07-28 21:21:46 +03:00
|
|
|
p.draw(g, self.color_ped(p.id));
|
|
|
|
}
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 21:25:57 +03:00
|
|
|
// Building paths overlap sidewalks and also other buildings, so do separate layers
|
|
|
|
// TODO but pedestrians will then walk through buildings; the front paths should ideally
|
|
|
|
// zig-zag around if possible
|
2018-03-13 18:04:21 +03:00
|
|
|
if self.show_buildings.is_enabled() {
|
2018-09-05 21:25:57 +03:00
|
|
|
for b in &self.draw_map
|
|
|
|
.get_buildings_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
|
|
|
b.draw_front_path(g, self.cs.get(Colors::BuildingPath));
|
|
|
|
}
|
|
|
|
|
2018-06-29 21:53:55 +03:00
|
|
|
for b in &self.draw_map
|
|
|
|
.get_buildings_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
2018-06-26 20:44:14 +03:00
|
|
|
b.draw(
|
|
|
|
g,
|
|
|
|
self.color_building(b.id),
|
2018-09-11 23:12:48 +03:00
|
|
|
&self.cs,
|
|
|
|
//self.cs.get(Colors::BuildingBoundary),
|
2018-06-26 20:44:14 +03:00
|
|
|
);
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-31 06:45:23 +03:00
|
|
|
for l in &lanes_onscreen {
|
2018-09-13 03:39:53 +03:00
|
|
|
for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) {
|
2018-07-31 06:45:23 +03:00
|
|
|
c.draw(g, self.color_car(c.id));
|
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
for p in &self.sim.get_draw_peds_on_lane(l.id, &self.map) {
|
2018-07-31 06:45:23 +03:00
|
|
|
p.draw(g, self.color_ped(p.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-10 23:28:34 +03:00
|
|
|
if self.show_extra_shapes.is_enabled() {
|
|
|
|
for s in &self.draw_map
|
|
|
|
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
|
|
|
|
{
|
|
|
|
// TODO no separate color method?
|
|
|
|
s.draw(
|
|
|
|
g,
|
2018-09-13 20:50:59 +03:00
|
|
|
self.color_for_selected(ID::ExtraShape(s.id))
|
2018-08-10 23:28:34 +03:00
|
|
|
.unwrap_or(self.cs.get(Colors::ExtraShape)),
|
2018-09-11 23:12:48 +03:00
|
|
|
&self.cs,
|
2018-08-10 23:28:34 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-13 02:26:24 +03:00
|
|
|
self.turn_cycler.draw(
|
2018-03-13 18:04:21 +03:00
|
|
|
&self.map,
|
|
|
|
&self.draw_map,
|
|
|
|
&self.control_map,
|
2018-09-13 03:39:53 +03:00
|
|
|
&self.sim,
|
2018-06-21 20:03:51 +03:00
|
|
|
&self.cs,
|
2018-03-13 18:04:21 +03:00
|
|
|
g,
|
|
|
|
);
|
2018-09-13 19:59:49 +03:00
|
|
|
self.debug_objects
|
2018-09-13 03:39:53 +03:00
|
|
|
.draw(&self.map, &self.canvas, &self.draw_map, &self.sim, g);
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-06-21 22:54:07 +03:00
|
|
|
self.color_picker.draw(&self.canvas, g);
|
2018-06-21 04:30:38 +03:00
|
|
|
|
2018-09-13 03:39:53 +03:00
|
|
|
let mut osd_lines = self.sim_ctrl.get_osd_lines(&self.sim);
|
2018-05-17 05:01:06 +03:00
|
|
|
let action_lines = input.get_possible_actions();
|
|
|
|
if !action_lines.is_empty() {
|
|
|
|
osd_lines.push(String::from(""));
|
|
|
|
osd_lines.extend(action_lines);
|
|
|
|
}
|
2018-05-17 03:09:38 +03:00
|
|
|
let search_lines = self.current_search_state.get_osd_lines();
|
|
|
|
if !search_lines.is_empty() {
|
|
|
|
osd_lines.push(String::from(""));
|
|
|
|
osd_lines.extend(search_lines);
|
|
|
|
}
|
2018-06-26 18:44:58 +03:00
|
|
|
let warp_lines = self.warp.get_osd_lines();
|
|
|
|
if !warp_lines.is_empty() {
|
|
|
|
osd_lines.push(String::from(""));
|
|
|
|
osd_lines.extend(warp_lines);
|
|
|
|
}
|
2018-05-17 03:09:38 +03:00
|
|
|
self.canvas.draw_osd_notification(g, &osd_lines);
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
}
|
2018-06-22 21:01:44 +03:00
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct EditorState {
|
2018-09-09 23:21:53 +03:00
|
|
|
pub map_name: String,
|
2018-06-22 21:01:44 +03:00
|
|
|
pub cam_x: f64,
|
|
|
|
pub cam_y: f64,
|
|
|
|
pub cam_zoom: f64,
|
|
|
|
|
|
|
|
pub traffic_signals: HashMap<IntersectionID, ModifiedTrafficSignal>,
|
|
|
|
pub stop_signs: HashMap<IntersectionID, ModifiedStopSign>,
|
|
|
|
}
|