mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 03:41:09 +03:00
grouping UI state that's bound to a certain map+edits
This commit is contained in:
parent
af2d49debb
commit
2aa47bbd51
@ -278,6 +278,16 @@ Alright, I think this is the sequence of things to do:
|
||||
2) make it possible to completely reload UI and everything from scratch, from a plugin. rationale: it'd be nice to switch maps from inside the editor anyway. not necessary, but useful.
|
||||
3) make road edits propogate correctly, and somehow have a strategy for ensuring nothing is forgotten. impl today is VERY incomplete.
|
||||
|
||||
Thinking about this again now that we need two copies of everything to be alive at a time and switch between them...
|
||||
|
||||
- very tied together: map, control map, draw map, sim
|
||||
- current selection is UI state that has to refresh when changing maps
|
||||
- which plugins have state tied to the map?
|
||||
- have a method on UI to switch map+edits? no, dont want to reload all this stuff every time...
|
||||
- bundle that state together, including the plugins!
|
||||
- make the existing 'load new edits' thing use this new mechanism
|
||||
- then go back to managing a second sim...
|
||||
|
||||
## Rendering a map differently
|
||||
|
||||
For "Project Halloween", I want to draw the map model in a very different
|
||||
@ -298,3 +308,48 @@ So, try adding the quadtree for roads and buildings (diff quadtrees or one
|
||||
unified? hmm) and see what looks common. Remember we could use quadtrees in map
|
||||
model construction for building/sidewalk pruning, but there's the awkwardness
|
||||
of quadtrees _kind of_ being a UI concept.
|
||||
|
||||
## Side-by-side
|
||||
|
||||
What should this feature do? Is it easier to watch two maps side-by-side moving
|
||||
in lockstep, with the same camera? Or would a ghostly trace overlay on one map
|
||||
be easier to understand? The use cases:
|
||||
|
||||
- Glancing at a vague overview of how the two runs are doing. Watching graphs
|
||||
side-by-side might even be more useful here. But for a zoomed out view,
|
||||
side-by-side with reasonably clear pockets of color (weather model style,
|
||||
almost) seems nice.
|
||||
- Detailed inspection of a fixed area. Side-by-side view with full detail seems
|
||||
nice.
|
||||
- Detailed inspection of a specific agent, following it. Side-by-side would
|
||||
have to trace it in both canvases.
|
||||
- Looking for differences... what are these? For a single agent, wanting to
|
||||
know are they farther along their journey at some point in time? That could
|
||||
be visualized nicely with a red or green thick route in front or behind them.
|
||||
Maybe they're ahead of the baseline by some amount, or behind it. This could
|
||||
use relative score or relative distance to goal or something. Would it trace
|
||||
ahead by pure distance or by anticipated distance in a given time?
|
||||
|
||||
The side-by-side canvas seems incredibly difficult -- have to revamp everything
|
||||
to dispatch mouse events, maybe synchronize cameras, other plugins
|
||||
arbitrarily... No way.
|
||||
|
||||
Let's start with two concrete things:
|
||||
|
||||
- Start running an A/B test sets up a second optional simulation in the UI.
|
||||
Some keys can toggle between showing one of the two, for now. Stepping will
|
||||
step BOTH of them. Have to adjust the OSD and other sim control things.
|
||||
Possibly even sim control should own the second sim?
|
||||
- Q: how to prevent scenario instatiation or adding new agents while an
|
||||
A/B test is in progress? need plugin groups / modes!
|
||||
- road editing during an A/B test is _definitely_ not k
|
||||
- argh!! wait, we need a different map, since we need different edits!
|
||||
- that means also a different control map
|
||||
- should the Sim own map and control_map to make it clear they're tied? I think so!
|
||||
- practically all of the UI touches the map...
|
||||
- wait wait draw map also needs to be duplicated then.
|
||||
- we're back to the problem of loading map edits
|
||||
- Make a visual trace abstraction to show something in front of or behind an
|
||||
agent. It follows bends in the road, crosses intersections, etc. Could be
|
||||
used for lookahead debugging right now, and this relative ghost comparison
|
||||
thing next.
|
||||
|
@ -8,7 +8,7 @@ use objects::{Ctx, ID};
|
||||
use piston::input::Key;
|
||||
use plugins::Colorizer;
|
||||
use render::DrawMap;
|
||||
use sim::Sim;
|
||||
use sim::Tick;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TurnCyclerState {
|
||||
@ -68,7 +68,7 @@ impl TurnCyclerState {
|
||||
map: &Map,
|
||||
draw_map: &DrawMap,
|
||||
control_map: &ControlMap,
|
||||
sim: &Sim,
|
||||
time: Tick,
|
||||
cs: &ColorScheme,
|
||||
g: &mut GfxCtx,
|
||||
) {
|
||||
@ -92,7 +92,7 @@ impl TurnCyclerState {
|
||||
}
|
||||
TurnCyclerState::Intersection(id) => {
|
||||
if let Some(signal) = control_map.traffic_signals.get(&id) {
|
||||
let (cycle, _) = signal.current_cycle_and_remaining_time(sim.time.as_time());
|
||||
let (cycle, _) = signal.current_cycle_and_remaining_time(time.as_time());
|
||||
for t in &cycle.turns {
|
||||
draw_map.get_t(*t).draw_full(g, cs.get(Colors::Turn));
|
||||
}
|
||||
|
@ -35,16 +35,13 @@ pub struct DrawMap {
|
||||
pub bus_stops: HashMap<BusStopID, DrawBusStop>,
|
||||
pub areas: Vec<DrawArea>,
|
||||
|
||||
pub center_pt: Pt2D,
|
||||
|
||||
quadtree: QuadTree<ID>,
|
||||
}
|
||||
|
||||
impl DrawMap {
|
||||
// Also returns the center of the map in map-space
|
||||
pub fn new(
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
raw_extra_shapes: Vec<ExtraShape>,
|
||||
) -> (DrawMap, Pt2D) {
|
||||
pub fn new(map: &Map, control_map: &ControlMap, raw_extra_shapes: Vec<ExtraShape>) -> DrawMap {
|
||||
let mut lanes: Vec<DrawLane> = Vec::new();
|
||||
for l in map.all_lanes() {
|
||||
lanes.push(DrawLane::new(l, map, control_map));
|
||||
@ -120,21 +117,20 @@ impl DrawMap {
|
||||
quadtree.insert_with_box(obj.get_id(), get_bbox(obj.get_bounds()));
|
||||
}
|
||||
|
||||
(
|
||||
DrawMap {
|
||||
lanes,
|
||||
intersections,
|
||||
turns,
|
||||
buildings,
|
||||
parcels,
|
||||
extra_shapes,
|
||||
bus_stops,
|
||||
areas,
|
||||
DrawMap {
|
||||
lanes,
|
||||
intersections,
|
||||
turns,
|
||||
buildings,
|
||||
parcels,
|
||||
extra_shapes,
|
||||
bus_stops,
|
||||
areas,
|
||||
|
||||
quadtree,
|
||||
},
|
||||
Pt2D::new(max_screen_pt.x() / 2.0, max_screen_pt.y() / 2.0),
|
||||
)
|
||||
center_pt: Pt2D::new(max_screen_pt.x() / 2.0, max_screen_pt.y() / 2.0),
|
||||
|
||||
quadtree,
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_turn_to_lane_offset(result: &mut HashMap<TurnID, usize>, l: &Lane, map: &Map) {
|
||||
|
357
editor/src/ui.rs
357
editor/src/ui.rs
@ -10,8 +10,7 @@ use ezgui::{
|
||||
};
|
||||
use flame;
|
||||
use kml;
|
||||
use map_model;
|
||||
use map_model::IntersectionID;
|
||||
use map_model::{IntersectionID, Map};
|
||||
use objects::{Ctx, DEBUG_LAYERS, ID, ROOT_MENU};
|
||||
use piston::input::Key;
|
||||
use plugins::a_b_tests::ABTestManager;
|
||||
@ -82,55 +81,16 @@ impl UIWrapper {
|
||||
// Do this first, so anything logged by sim::load isn't lost.
|
||||
let logs = DisplayLogs::new();
|
||||
|
||||
flame::start("setup");
|
||||
let (map, control_map, sim) = sim::load(flags.clone(), Some(sim::Tick::from_seconds(30)));
|
||||
let extra_shapes = if let Some(path) = kml.clone() {
|
||||
kml::load(&path, &map.get_gps_bounds()).expect("Couldn't load extra KML shapes")
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
flame::start("draw_map");
|
||||
let (draw_map, center_pt) = DrawMap::new(&map, &control_map, extra_shapes);
|
||||
flame::end("draw_map");
|
||||
|
||||
flame::end("setup");
|
||||
flame::dump_stdout();
|
||||
|
||||
let steepness_viz = SteepnessVisualizer::new(&map);
|
||||
let road_editor = RoadEditor::new(map.get_road_edits().clone());
|
||||
|
||||
let mut ui = UI {
|
||||
// TODO organize this by section
|
||||
map,
|
||||
draw_map,
|
||||
control_map,
|
||||
sim,
|
||||
|
||||
steepness_viz,
|
||||
road_editor,
|
||||
sim_ctrl: SimController::new(),
|
||||
primary: PerMapUI::new(flags, kml.clone()),
|
||||
secondary: None,
|
||||
|
||||
layers: ToggleableLayers::new(),
|
||||
|
||||
current_selection: None,
|
||||
|
||||
hider: Hider::new(),
|
||||
debug_objects: DebugObjectsState::new(),
|
||||
search_state: SearchState::Empty,
|
||||
warp: WarpState::Empty,
|
||||
follow: FollowState::Empty,
|
||||
show_route: ShowRouteState::Empty,
|
||||
floodfiller: Floodfiller::new(),
|
||||
osm_classifier: OsmClassifier::new(),
|
||||
traffic_signal_editor: TrafficSignalEditor::new(),
|
||||
stop_sign_editor: StopSignEditor::new(),
|
||||
sim_ctrl: SimController::new(),
|
||||
color_picker: ColorPicker::new(),
|
||||
geom_validator: Validator::new(),
|
||||
turn_cycler: TurnCyclerState::new(),
|
||||
draw_neighborhoods: DrawNeighborhoodState::new(),
|
||||
scenarios: ScenarioManager::new(),
|
||||
edits_manager: EditsManager::new(flags),
|
||||
ab_test_manager: ABTestManager::new(),
|
||||
logs,
|
||||
|
||||
@ -141,7 +101,7 @@ impl UIWrapper {
|
||||
};
|
||||
|
||||
match abstutil::read_json::<EditorState>("editor_state") {
|
||||
Ok(ref state) if *ui.map.get_name() == state.map_name => {
|
||||
Ok(ref state) if ui.primary.map.get_name().to_string() == state.map_name => {
|
||||
info!("Loaded previous editor_state");
|
||||
ui.canvas.cam_x = state.cam_x;
|
||||
ui.canvas.cam_y = state.cam_y;
|
||||
@ -149,7 +109,7 @@ impl UIWrapper {
|
||||
}
|
||||
_ => {
|
||||
warn!("Couldn't load editor_state or it's for a different map, so just centering initial view");
|
||||
ui.canvas.center_on_map_pt(center_pt);
|
||||
ui.canvas.center_on_map_pt(ui.primary.draw_map.center_pt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,117 +133,132 @@ impl UIWrapper {
|
||||
changed
|
||||
};
|
||||
if layer_changed {
|
||||
ctx.ui.current_selection = ctx.ui.mouseover_something();
|
||||
ctx.ui.primary.current_selection = ctx.ui.mouseover_something();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.traffic_signal_editor.event(
|
||||
ctx.ui.primary.traffic_signal_editor.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.control_map,
|
||||
ctx.ui.current_selection,
|
||||
&ctx.ui.primary.map,
|
||||
&mut ctx.ui.primary.control_map,
|
||||
ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.stop_sign_editor.event(
|
||||
ctx.ui.primary.stop_sign_editor.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.control_map,
|
||||
ctx.ui.current_selection,
|
||||
&ctx.ui.primary.map,
|
||||
&mut ctx.ui.primary.control_map,
|
||||
ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.road_editor.event(
|
||||
ctx.ui.primary.road_editor.event(
|
||||
ctx.input,
|
||||
ctx.ui.current_selection,
|
||||
&mut ctx.ui.map,
|
||||
&mut ctx.ui.draw_map,
|
||||
&ctx.ui.control_map,
|
||||
&mut ctx.ui.sim,
|
||||
ctx.ui.primary.current_selection,
|
||||
&mut ctx.ui.primary.map,
|
||||
&mut ctx.ui.primary.draw_map,
|
||||
&ctx.ui.primary.control_map,
|
||||
&mut ctx.ui.primary.sim,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| ctx.ui.search_state.event(ctx.input)),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.warp.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.sim,
|
||||
&ctx.ui.primary.map,
|
||||
&ctx.ui.primary.sim,
|
||||
&mut ctx.ui.canvas,
|
||||
&mut ctx.ui.current_selection,
|
||||
&mut ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.follow.event(
|
||||
ctx.ui.primary.follow.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.sim,
|
||||
&ctx.ui.primary.map,
|
||||
&ctx.ui.primary.sim,
|
||||
&mut ctx.ui.canvas,
|
||||
ctx.ui.current_selection,
|
||||
ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.show_route
|
||||
.event(ctx.input, &ctx.ui.sim, ctx.ui.current_selection)
|
||||
ctx.ui.primary.show_route.event(
|
||||
ctx.input,
|
||||
&ctx.ui.primary.sim,
|
||||
ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.color_picker
|
||||
.event(ctx.input, &mut ctx.ui.canvas, &mut ctx.ui.cs)
|
||||
}),
|
||||
Box::new(|ctx| ctx.ui.steepness_viz.event(ctx.input)),
|
||||
Box::new(|ctx| ctx.ui.primary.steepness_viz.event(ctx.input)),
|
||||
Box::new(|ctx| ctx.ui.osm_classifier.event(ctx.input)),
|
||||
Box::new(|ctx| ctx.ui.hider.event(ctx.input, &mut ctx.ui.current_selection)),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.debug_objects.event(
|
||||
ctx.ui.current_selection,
|
||||
ctx.ui
|
||||
.primary
|
||||
.hider
|
||||
.event(ctx.input, &mut ctx.ui.primary.current_selection)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.primary.debug_objects.event(
|
||||
ctx.ui.primary.current_selection,
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.sim,
|
||||
&ctx.ui.control_map,
|
||||
&ctx.ui.primary.map,
|
||||
&mut ctx.ui.primary.sim,
|
||||
&ctx.ui.primary.control_map,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.floodfiller
|
||||
.event(&ctx.ui.map, ctx.input, ctx.ui.current_selection)
|
||||
ctx.ui.primary.floodfiller.event(
|
||||
&ctx.ui.primary.map,
|
||||
ctx.input,
|
||||
ctx.ui.primary.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.geom_validator.event(
|
||||
ctx.ui.primary.geom_validator.event(
|
||||
ctx.input,
|
||||
&mut ctx.ui.canvas,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.draw_map,
|
||||
&ctx.ui.primary.map,
|
||||
&ctx.ui.primary.draw_map,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.primary
|
||||
.turn_cycler
|
||||
.event(ctx.input, ctx.ui.current_selection)
|
||||
.event(ctx.input, ctx.ui.primary.current_selection)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.draw_neighborhoods
|
||||
.event(ctx.input, &ctx.ui.canvas, &ctx.ui.map, ctx.osd)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.scenarios
|
||||
.event(ctx.input, &ctx.ui.map, &mut ctx.ui.sim)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.edits_manager.event(
|
||||
ctx.ui.primary.draw_neighborhoods.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.control_map,
|
||||
&ctx.ui.road_editor,
|
||||
&ctx.ui.canvas,
|
||||
&ctx.ui.primary.map,
|
||||
ctx.osd,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.primary.scenarios.event(
|
||||
ctx.input,
|
||||
&ctx.ui.primary.map,
|
||||
&mut ctx.ui.primary.sim,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.primary.edits_manager.event(
|
||||
ctx.input,
|
||||
&ctx.ui.primary.map,
|
||||
&ctx.ui.primary.control_map,
|
||||
&ctx.ui.primary.road_editor,
|
||||
ctx.new_flags,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| ctx.ui.ab_test_manager.event(ctx.input, &ctx.ui.map)),
|
||||
Box::new(|ctx| ctx.ui.ab_test_manager.event(ctx.input, &ctx.ui.primary.map)),
|
||||
Box::new(|ctx| ctx.ui.logs.event(ctx.input)),
|
||||
],
|
||||
|
||||
@ -292,35 +267,92 @@ impl UIWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
struct UI {
|
||||
map: map_model::Map,
|
||||
// All of the state that's bound to a specific map+edit has to live here.
|
||||
// TODO How can we arrange the code so that we statically know that we don't pass anything from UI
|
||||
// to something in PerMapUI?
|
||||
struct PerMapUI {
|
||||
map: Map,
|
||||
draw_map: DrawMap,
|
||||
control_map: ControlMap,
|
||||
sim: Sim,
|
||||
|
||||
layers: ToggleableLayers,
|
||||
|
||||
current_selection: Option<ID>,
|
||||
|
||||
// Anything that holds onto any kind of ID has to live here!
|
||||
hider: Hider,
|
||||
debug_objects: DebugObjectsState,
|
||||
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,
|
||||
draw_neighborhoods: DrawNeighborhoodState,
|
||||
scenarios: ScenarioManager,
|
||||
edits_manager: EditsManager,
|
||||
}
|
||||
|
||||
impl PerMapUI {
|
||||
fn new(flags: SimFlags, kml: Option<String>) -> PerMapUI {
|
||||
flame::start("setup");
|
||||
let (map, control_map, sim) = sim::load(flags.clone(), Some(sim::Tick::from_seconds(30)));
|
||||
let extra_shapes = if let Some(path) = kml.clone() {
|
||||
kml::load(&path, &map.get_gps_bounds()).expect("Couldn't load extra KML shapes")
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
flame::start("draw_map");
|
||||
let draw_map = DrawMap::new(&map, &control_map, extra_shapes);
|
||||
flame::end("draw_map");
|
||||
|
||||
flame::end("setup");
|
||||
flame::dump_stdout();
|
||||
|
||||
let steepness_viz = SteepnessVisualizer::new(&map);
|
||||
let road_editor = RoadEditor::new(map.get_road_edits().clone());
|
||||
|
||||
PerMapUI {
|
||||
map,
|
||||
draw_map,
|
||||
control_map,
|
||||
sim,
|
||||
|
||||
current_selection: None,
|
||||
|
||||
hider: Hider::new(),
|
||||
debug_objects: DebugObjectsState::new(),
|
||||
follow: FollowState::Empty,
|
||||
show_route: ShowRouteState::Empty,
|
||||
floodfiller: Floodfiller::new(),
|
||||
steepness_viz,
|
||||
traffic_signal_editor: TrafficSignalEditor::new(),
|
||||
stop_sign_editor: StopSignEditor::new(),
|
||||
road_editor,
|
||||
geom_validator: Validator::new(),
|
||||
turn_cycler: TurnCyclerState::new(),
|
||||
draw_neighborhoods: DrawNeighborhoodState::new(),
|
||||
scenarios: ScenarioManager::new(),
|
||||
edits_manager: EditsManager::new(flags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UI {
|
||||
primary: PerMapUI,
|
||||
// When running an A/B test, this is populated too.
|
||||
secondary: Option<PerMapUI>,
|
||||
|
||||
layers: ToggleableLayers,
|
||||
search_state: SearchState,
|
||||
warp: WarpState,
|
||||
osm_classifier: OsmClassifier,
|
||||
// TODO This one has per-sim state right now, but soon will understand how to handle two sims.
|
||||
sim_ctrl: SimController,
|
||||
color_picker: ColorPicker,
|
||||
ab_test_manager: ABTestManager,
|
||||
logs: DisplayLogs,
|
||||
|
||||
@ -347,11 +379,11 @@ impl UI {
|
||||
fn mouseover_something(&self) -> Option<ID> {
|
||||
let pt = self.canvas.get_cursor_in_map_space();
|
||||
|
||||
let (statics, dynamics) = self.draw_map.get_objects_onscreen(
|
||||
let (statics, dynamics) = self.primary.draw_map.get_objects_onscreen(
|
||||
self.canvas.get_screen_bbox(),
|
||||
&self.hider,
|
||||
&self.map,
|
||||
&self.sim,
|
||||
&self.primary.hider,
|
||||
&self.primary.map,
|
||||
&self.primary.sim,
|
||||
&self.layers,
|
||||
self,
|
||||
);
|
||||
@ -387,13 +419,13 @@ impl UI {
|
||||
|
||||
// Always handle mouseover
|
||||
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
|
||||
self.current_selection = None;
|
||||
self.primary.current_selection = None;
|
||||
}
|
||||
if !self.canvas.is_dragging()
|
||||
&& input.get_moved_mouse().is_some()
|
||||
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
|
||||
{
|
||||
self.current_selection = self.mouseover_something();
|
||||
self.primary.current_selection = self.mouseover_something();
|
||||
}
|
||||
|
||||
// TODO Normally we'd return InputOnly here if there was an active plugin, but actually, we
|
||||
@ -426,7 +458,7 @@ impl UI {
|
||||
|
||||
if input.unimportant_key_pressed(Key::Escape, ROOT_MENU, "quit") {
|
||||
let state = EditorState {
|
||||
map_name: self.map.get_name().clone(),
|
||||
map_name: self.primary.map.get_name().clone(),
|
||||
cam_x: self.canvas.cam_x,
|
||||
cam_y: self.canvas.cam_y,
|
||||
cam_zoom: self.canvas.cam_zoom,
|
||||
@ -441,10 +473,10 @@ impl UI {
|
||||
// Sim controller plugin is kind of always active? If nothing else ran, let it use keys.
|
||||
let result = self.sim_ctrl.event(
|
||||
&mut input,
|
||||
&self.map,
|
||||
&self.control_map,
|
||||
&mut self.sim,
|
||||
self.current_selection,
|
||||
&self.primary.map,
|
||||
&self.primary.control_map,
|
||||
&mut self.primary.sim,
|
||||
self.primary.current_selection,
|
||||
osd,
|
||||
);
|
||||
input.populate_osd(osd);
|
||||
@ -454,11 +486,11 @@ impl UI {
|
||||
fn draw(&self, g: &mut GfxCtx, osd: Text) {
|
||||
g.clear(self.cs.get(Colors::Background));
|
||||
|
||||
let (statics, dynamics) = self.draw_map.get_objects_onscreen(
|
||||
let (statics, dynamics) = self.primary.draw_map.get_objects_onscreen(
|
||||
self.canvas.get_screen_bbox(),
|
||||
&self.hider,
|
||||
&self.map,
|
||||
&self.sim,
|
||||
&self.primary.hider,
|
||||
&self.primary.map,
|
||||
&self.primary.sim,
|
||||
&self.layers,
|
||||
self,
|
||||
);
|
||||
@ -473,10 +505,10 @@ impl UI {
|
||||
opts,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
map: &self.map,
|
||||
control_map: &self.control_map,
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
canvas: &self.canvas,
|
||||
sim: &self.sim,
|
||||
sim: &self.primary.sim,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -491,29 +523,34 @@ impl UI {
|
||||
opts,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
map: &self.map,
|
||||
control_map: &self.control_map,
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
canvas: &self.canvas,
|
||||
sim: &self.sim,
|
||||
sim: &self.primary.sim,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// TODO Only if active?
|
||||
self.turn_cycler.draw(
|
||||
&self.map,
|
||||
&self.draw_map,
|
||||
&self.control_map,
|
||||
&self.sim,
|
||||
self.primary.turn_cycler.draw(
|
||||
&self.primary.map,
|
||||
&self.primary.draw_map,
|
||||
&self.primary.control_map,
|
||||
self.primary.sim.time,
|
||||
&self.cs,
|
||||
g,
|
||||
);
|
||||
self.debug_objects
|
||||
.draw(&self.map, &self.canvas, &self.draw_map, &self.sim, g);
|
||||
self.primary.debug_objects.draw(
|
||||
&self.primary.map,
|
||||
&self.canvas,
|
||||
&self.primary.draw_map,
|
||||
&self.primary.sim,
|
||||
g,
|
||||
);
|
||||
self.color_picker.draw(&self.canvas, g);
|
||||
self.draw_neighborhoods.draw(g, &self.canvas);
|
||||
self.scenarios.draw(g, &self.canvas);
|
||||
self.edits_manager.draw(g, &self.canvas);
|
||||
self.primary.draw_neighborhoods.draw(g, &self.canvas);
|
||||
self.primary.scenarios.draw(g, &self.canvas);
|
||||
self.primary.edits_manager.draw(g, &self.canvas);
|
||||
self.ab_test_manager.draw(g, &self.canvas);
|
||||
self.logs.draw(g, &self.canvas);
|
||||
self.search_state.draw(g, &self.canvas);
|
||||
@ -524,7 +561,7 @@ impl UI {
|
||||
}
|
||||
|
||||
fn color_obj(&self, id: ID) -> Option<Color> {
|
||||
if Some(id) == self.current_selection {
|
||||
if Some(id) == self.primary.current_selection {
|
||||
return Some(self.cs.get(Colors::Selected));
|
||||
}
|
||||
|
||||
@ -533,10 +570,10 @@ impl UI {
|
||||
id,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
map: &self.map,
|
||||
control_map: &self.control_map,
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
canvas: &self.canvas,
|
||||
sim: &self.sim,
|
||||
sim: &self.primary.sim,
|
||||
},
|
||||
) {
|
||||
return Some(c);
|
||||
@ -553,24 +590,24 @@ impl UI {
|
||||
match idx {
|
||||
// The first plugin is all the ToggleableLayers, which doesn't implement Colorizer.
|
||||
0 => None,
|
||||
1 => Some(Box::new(&self.traffic_signal_editor)),
|
||||
2 => Some(Box::new(&self.stop_sign_editor)),
|
||||
3 => Some(Box::new(&self.road_editor)),
|
||||
1 => Some(Box::new(&self.primary.traffic_signal_editor)),
|
||||
2 => Some(Box::new(&self.primary.stop_sign_editor)),
|
||||
3 => Some(Box::new(&self.primary.road_editor)),
|
||||
4 => Some(Box::new(&self.search_state)),
|
||||
5 => Some(Box::new(&self.warp)),
|
||||
6 => Some(Box::new(&self.follow)),
|
||||
7 => Some(Box::new(&self.show_route)),
|
||||
6 => Some(Box::new(&self.primary.follow)),
|
||||
7 => Some(Box::new(&self.primary.show_route)),
|
||||
8 => Some(Box::new(&self.color_picker)),
|
||||
9 => Some(Box::new(&self.steepness_viz)),
|
||||
9 => Some(Box::new(&self.primary.steepness_viz)),
|
||||
10 => Some(Box::new(&self.osm_classifier)),
|
||||
11 => Some(Box::new(&self.hider)),
|
||||
12 => Some(Box::new(&self.debug_objects)),
|
||||
13 => Some(Box::new(&self.floodfiller)),
|
||||
14 => Some(Box::new(&self.geom_validator)),
|
||||
15 => Some(Box::new(&self.turn_cycler)),
|
||||
16 => Some(Box::new(&self.draw_neighborhoods)),
|
||||
17 => Some(Box::new(&self.scenarios)),
|
||||
18 => Some(Box::new(&self.edits_manager)),
|
||||
11 => Some(Box::new(&self.primary.hider)),
|
||||
12 => Some(Box::new(&self.primary.debug_objects)),
|
||||
13 => Some(Box::new(&self.primary.floodfiller)),
|
||||
14 => Some(Box::new(&self.primary.geom_validator)),
|
||||
15 => Some(Box::new(&self.primary.turn_cycler)),
|
||||
16 => Some(Box::new(&self.primary.draw_neighborhoods)),
|
||||
17 => Some(Box::new(&self.primary.scenarios)),
|
||||
18 => Some(Box::new(&self.primary.edits_manager)),
|
||||
19 => Some(Box::new(&self.ab_test_manager)),
|
||||
20 => Some(Box::new(&self.logs)),
|
||||
_ => panic!("Active plugin {} is too high", idx),
|
||||
@ -648,8 +685,8 @@ pub trait ShowTurnIcons {
|
||||
impl ShowTurnIcons for UI {
|
||||
fn show_icons_for(&self, id: IntersectionID) -> bool {
|
||||
self.layers.show_all_turn_icons.is_enabled()
|
||||
|| self.stop_sign_editor.show_turn_icons(id)
|
||||
|| self.traffic_signal_editor.show_turn_icons(id)
|
||||
|| self.primary.stop_sign_editor.show_turn_icons(id)
|
||||
|| self.primary.traffic_signal_editor.show_turn_icons(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user