filling out edits manager

This commit is contained in:
Dustin Carlino 2018-10-03 07:47:59 -07:00
parent 53993feebb
commit b1ef405892
5 changed files with 265 additions and 97 deletions

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_cbor;
use serde_json;
use std;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::File;
use std::hash::Hash;
use std::io::{Error, ErrorKind, Read, Write};
@ -101,6 +101,16 @@ pub fn deserialize_multimap<
Ok(map)
}
// Just list all things from a directory, return sorted by name.
pub fn list_all_objects(dir: &str, map_name: &str) -> Vec<String> {
let mut results: BTreeSet<String> = BTreeSet::new();
for entry in std::fs::read_dir(format!("../data/{}/{}/", dir, map_name)).unwrap() {
let name = entry.unwrap().file_name().into_string().unwrap();
results.insert(name);
}
results.into_iter().collect()
}
// Load all serialized things from a directory, return sorted by name.
pub fn load_all_objects<T: DeserializeOwned>(dir: &str, map_name: &str) -> Vec<(String, T)> {
let mut tree: BTreeMap<String, T> = BTreeMap::new();

View File

@ -10,6 +10,6 @@ mod io;
pub use abst_multimap::MultiMap;
pub use clone::Cloneable;
pub use io::{
deserialize_btreemap, deserialize_multimap, load_all_objects, read_binary, read_json,
serialize_btreemap, serialize_multimap, to_json, write_binary, write_json,
deserialize_btreemap, deserialize_multimap, list_all_objects, load_all_objects, read_binary,
read_json, serialize_btreemap, serialize_multimap, to_json, write_binary, write_json,
};

View File

@ -1,22 +1,134 @@
use abstutil;
use control::ControlMap;
use ezgui::{Canvas, GfxCtx, UserInput};
use ezgui::{Canvas, GfxCtx, UserInput, Wizard, WrappedWizard};
use map_model::Map;
use objects::SIM_SETUP;
use piston::input::Key;
use plugins::road_editor::RoadEditor;
use plugins::Colorizer;
use sim::{MapEdits, SimFlags};
// TODO ahh, something needs to remember edits_name.
pub struct EditsManager {
current_flags: SimFlags,
state: State,
}
pub struct EditsManager {}
enum State {
Inactive,
ManageEdits(Wizard),
}
impl EditsManager {
pub fn new() -> EditsManager {
EditsManager {}
pub fn new(current_flags: SimFlags) -> EditsManager {
EditsManager {
current_flags,
state: State::Inactive,
}
}
pub fn event(&mut self, input: &mut UserInput, map: &Map, control_map: &ControlMap) -> bool {
false
pub fn event(
&mut self,
input: &mut UserInput,
map: &Map,
control_map: &ControlMap,
road_editor: &RoadEditor,
new_flags: &mut Option<SimFlags>,
) -> bool {
let mut new_state: Option<State> = None;
match self.state {
State::Inactive => {
if input.unimportant_key_pressed(Key::Q, SIM_SETUP, "manage map edits") {
new_state = Some(State::ManageEdits(Wizard::new()));
}
}
State::ManageEdits(ref mut wizard) => {
if manage_edits(
&mut self.current_flags,
map,
control_map,
road_editor,
new_flags,
wizard.wrap(input),
).is_some()
{
} else if wizard.aborted() {
new_state = Some(State::Inactive);
}
}
}
if let Some(s) = new_state {
self.state = s;
}
match self.state {
State::Inactive => false,
_ => true,
}
}
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {}
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
match self.state {
State::ManageEdits(ref wizard) => {
wizard.draw(g, canvas);
}
_ => {}
}
}
}
impl Colorizer for EditsManager {}
fn manage_edits(
current_flags: &mut SimFlags,
map: &Map,
control_map: &ControlMap,
road_editor: &RoadEditor,
new_flags: &mut Option<SimFlags>,
mut wizard: WrappedWizard,
) -> Option<()> {
// TODO Indicate how many edits are there / if there are any unsaved edits
let load = "Load other map edits";
let save_new = "Save these new map edits";
let save_existing = &format!("Save {}", current_flags.edits_name);
let choices: Vec<&str> = if current_flags.edits_name == "no_edits" {
vec![save_new, load]
} else {
vec![save_existing, load]
};
match wizard.choose_string("Manage map edits", choices)?.as_str() {
x if x == save_new => {
let name = wizard.input_string("Name the map edits")?;
save(&name, map, control_map, road_editor);
// No need to reload everything
current_flags.edits_name = name;
Some(())
}
x if x == save_existing => {
save(&current_flags.edits_name, map, control_map, road_editor);
Some(())
}
x if x == load => {
let map_name = map.get_name().to_string();
let edits = abstutil::list_all_objects("edits", &map_name);
let edit_refs = edits.iter().map(|s| s.as_str()).collect();
let load_name = wizard.choose_string("Load which map edits?", edit_refs)?;
let mut flags = current_flags.clone();
flags.edits_name = load_name;
*new_flags = Some(flags);
Some(())
}
_ => unreachable!(),
}
}
fn save(edits_name: &str, map: &Map, control_map: &ControlMap, road_editor: &RoadEditor) {
abstutil::write_json(
&format!("../data/edits/{}/{}.json", map.get_name(), edits_name),
&MapEdits {
edits_name: edits_name.to_string(),
map_name: map.get_name().to_string(),
road_edits: road_editor.get_edits().clone(),
stop_signs: control_map.get_stop_signs_savestate(),
traffic_signals: control_map.get_traffic_signals_savestate(),
},
).expect("Saving map edits failed");
}

View File

@ -37,7 +37,7 @@ use plugins::warp::WarpState;
use plugins::Colorizer;
use render::{DrawMap, RenderOptions};
use sim;
use sim::{MapEdits, Sim, SimFlags};
use sim::{Sim, SimFlags};
use std::process;
// TODO ideally these would be tuned kind of dynamically based on rendering speed
@ -48,16 +48,22 @@ const MIN_ZOOM_FOR_MOUSEOVER: f64 = 4.0;
// 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, &mut Text) -> bool>>,
plugins: Vec<Box<Fn(PluginCtx) -> bool>>,
// Remember these to support loading a new UIWrapper
flags: SimFlags,
// Remember this to support loading a new UIWrapper
kml: Option<String>,
}
impl GUI for UIWrapper {
fn event(&mut self, input: UserInput, osd: &mut Text) -> EventLoopMode {
self.ui.event(input, osd, &self.plugins)
// If we should start over and load something new, fill this out.
let mut new_flags: Option<SimFlags> = None;
let result = self.ui.event(input, osd, &self.plugins, &mut new_flags);
if let Some(flags) = new_flags {
info!("Reloading everything...");
*self = UIWrapper::new(flags, self.kml.clone());
}
result
}
fn draw(&mut self, g: &mut GfxCtx, osd: Text, window_size: Size) {
@ -122,7 +128,7 @@ impl UIWrapper {
turn_cycler: TurnCyclerState::new(),
draw_neighborhoods: DrawNeighborhoodState::new(),
scenarios: ScenarioManager::new(),
edits_manager: EditsManager::new(),
edits_manager: EditsManager::new(flags),
logs,
active_plugin: None,
@ -152,11 +158,11 @@ impl UIWrapper {
UIWrapper {
ui,
plugins: vec![
Box::new(|ui, input, _osd| {
Box::new(|ctx| {
let layer_changed = {
let mut changed = false;
for layer in ui.toggleable_layers().into_iter() {
if layer.event(input) {
for layer in ctx.ui.toggleable_layers().into_iter() {
if layer.event(ctx.input) {
changed = true;
break;
}
@ -164,92 +170,119 @@ impl UIWrapper {
changed
};
if layer_changed {
ui.current_selection = ui.mouseover_something();
ctx.ui.current_selection = ctx.ui.mouseover_something();
true
} else {
false
}
}),
Box::new(|ui, input, _osd| {
ui.traffic_signal_editor.event(
input,
&ui.map,
&mut ui.control_map,
ui.current_selection,
Box::new(|ctx| {
ctx.ui.traffic_signal_editor.event(
ctx.input,
&ctx.ui.map,
&mut ctx.ui.control_map,
ctx.ui.current_selection,
)
}),
Box::new(|ui, input, _osd| {
ui.stop_sign_editor.event(
input,
&ui.map,
&mut ui.control_map,
ui.current_selection,
Box::new(|ctx| {
ctx.ui.stop_sign_editor.event(
ctx.input,
&ctx.ui.map,
&mut ctx.ui.control_map,
ctx.ui.current_selection,
)
}),
Box::new(|ui, input, _osd| {
ui.road_editor.event(
input,
ui.current_selection,
&mut ui.map,
&mut ui.draw_map,
&ui.control_map,
&mut ui.sim,
Box::new(|ctx| {
ctx.ui.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,
)
}),
Box::new(|ui, input, _osd| ui.search_state.event(input)),
Box::new(|ui, input, _osd| {
ui.warp.event(
input,
&ui.map,
&ui.sim,
&mut ui.canvas,
&mut ui.current_selection,
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,
&mut ctx.ui.canvas,
&mut ctx.ui.current_selection,
)
}),
Box::new(|ui, input, _osd| {
ui.follow.event(
input,
&ui.map,
&ui.sim,
&mut ui.canvas,
ui.current_selection,
Box::new(|ctx| {
ctx.ui.follow.event(
ctx.input,
&ctx.ui.map,
&ctx.ui.sim,
&mut ctx.ui.canvas,
ctx.ui.current_selection,
)
}),
Box::new(|ui, input, _osd| {
ui.show_route.event(input, &ui.sim, ui.current_selection)
Box::new(|ctx| {
ctx.ui
.show_route
.event(ctx.input, &ctx.ui.sim, ctx.ui.current_selection)
}),
Box::new(|ui, input, _osd| {
ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)
Box::new(|ctx| {
ctx.ui
.color_picker
.event(ctx.input, &mut ctx.ui.canvas, &mut ctx.ui.cs)
}),
Box::new(|ui, input, _osd| ui.steepness_viz.event(input)),
Box::new(|ui, input, _osd| ui.osm_classifier.event(input)),
Box::new(|ui, input, _osd| ui.hider.event(input, &mut ui.current_selection)),
Box::new(|ui, input, _osd| {
ui.debug_objects.event(
ui.current_selection,
input,
&ui.map,
&mut ui.sim,
&ui.control_map,
Box::new(|ctx| ctx.ui.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.input,
&ctx.ui.map,
&mut ctx.ui.sim,
&ctx.ui.control_map,
)
}),
Box::new(|ui, input, _osd| {
ui.floodfiller.event(&ui.map, input, ui.current_selection)
Box::new(|ctx| {
ctx.ui
.floodfiller
.event(&ctx.ui.map, ctx.input, ctx.ui.current_selection)
}),
Box::new(|ui, input, _osd| {
ui.geom_validator
.event(input, &mut ui.canvas, &ui.map, &ui.draw_map)
Box::new(|ctx| {
ctx.ui.geom_validator.event(
ctx.input,
&mut ctx.ui.canvas,
&ctx.ui.map,
&ctx.ui.draw_map,
)
}),
Box::new(|ui, input, _osd| ui.turn_cycler.event(input, ui.current_selection)),
Box::new(|ui, input, osd| {
ui.draw_neighborhoods.event(input, &ui.canvas, &ui.map, osd)
Box::new(|ctx| {
ctx.ui
.turn_cycler
.event(ctx.input, ctx.ui.current_selection)
}),
Box::new(|ui, input, _osd| ui.scenarios.event(input, &ui.map, &mut ui.sim)),
Box::new(|ui, input, _osd| ui.edits_manager.event(input, &ui.map, &ui.control_map)),
Box::new(|ui, input, _osd| ui.logs.event(input)),
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.input,
&ctx.ui.map,
&ctx.ui.control_map,
&ctx.ui.road_editor,
ctx.new_flags,
)
}),
Box::new(|ctx| ctx.ui.logs.event(ctx.input)),
],
flags,
kml,
}
}
@ -336,7 +369,8 @@ impl UI {
&mut self,
mut input: UserInput,
osd: &mut Text,
plugins: &Vec<Box<Fn(&mut UI, &mut UserInput, &mut Text) -> bool>>,
plugins: &Vec<Box<Fn(PluginCtx) -> bool>>,
new_flags: &mut Option<SimFlags>,
) -> EventLoopMode {
// First update the camera and handle zoom
let old_zoom = self.canvas.cam_zoom;
@ -362,13 +396,23 @@ impl UI {
// If there's an active plugin, just run it.
if let Some(idx) = self.active_plugin {
if !plugins[idx](self, &mut input, osd) {
if !plugins[idx](PluginCtx {
ui: self,
input: &mut input,
osd,
new_flags,
}) {
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, &mut input, osd) {
if plugin(PluginCtx {
ui: self,
input: &mut input,
osd,
new_flags,
}) {
self.active_plugin = Some(idx);
break;
}
@ -385,18 +429,7 @@ impl UI {
// TODO maybe make state line up with the map, so loading from a new map doesn't break
abstutil::write_json("editor_state", &state).expect("Saving editor_state failed");
abstutil::write_json("color_scheme", &self.cs).expect("Saving color_scheme failed");
// TODO do this from a plugin!
abstutil::write_json(
&format!("../data/edits/{}/ui.json", self.map.get_name()),
&MapEdits {
edits_name: "ui".to_string(),
map_name: self.map.get_name().to_string(),
road_edits: self.road_editor.get_edits().clone(),
stop_signs: self.control_map.get_stop_signs_savestate(),
traffic_signals: self.control_map.get_traffic_signals_savestate(),
},
).expect("Saving map_edits.json failed");
info!("Saved editor_state, color_scheme, and map_edits.json");
info!("Saved editor_state and color_scheme");
process::exit(0);
}
@ -612,3 +645,10 @@ impl ShowTurnIcons for UI {
|| self.traffic_signal_editor.show_turn_icons(id)
}
}
struct PluginCtx<'a> {
ui: &'a mut UI,
input: &'a mut UserInput,
osd: &'a mut Text,
new_flags: &'a mut Option<SimFlags>,
}

View File

@ -2,6 +2,12 @@
set -e
function get_if_needed {
if [ ! -f $2 ]; then
wget $1 -O $2;
fi
}
# Download raw data if needed.
mkdir -p data/input data/maps
# TODO Fill these out: