folding in road editor

This commit is contained in:
Dustin Carlino 2019-04-23 17:26:10 -07:00
parent 1abb5b630a
commit 138b47512a
5 changed files with 112 additions and 121 deletions

View File

@ -1,10 +1,10 @@
use crate::game::{GameState, Mode};
use crate::objects::DrawCtx;
use crate::objects::{DrawCtx, ID};
use crate::plugins::{apply_map_edits, load_edits, PluginCtx};
use crate::render::{RenderOptions, Renderable, MIN_ZOOM_FOR_DETAIL};
use abstutil::Timer;
use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Wizard, WrappedWizard, GUI};
use map_model::Map;
use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, Wizard, WrappedWizard, GUI};
use map_model::{Lane, LaneType, Map, Road};
pub enum EditMode {
ViewingDiffs,
@ -13,13 +13,16 @@ pub enum EditMode {
}
impl EditMode {
pub fn event(state: &mut GameState, ctx: EventCtx) -> EventLoopMode {
let edits = state.ui.state.primary.map.get_edits();
pub fn event(state: &mut GameState, mut ctx: EventCtx) -> EventLoopMode {
ctx.canvas.handle_event(ctx.input);
// TODO Display info/hints on more lines.
ctx.input.set_mode_with_prompt(
"Map Edit Mode",
format!("Map Edit Mode for {}", edits.describe()),
format!(
"Map Edit Mode for {}",
state.ui.state.primary.map.get_edits().describe()
),
&ctx.canvas,
);
// TODO Clicking this works, but the key doesn't
@ -37,6 +40,42 @@ impl EditMode {
} else if ctx.input.modal_action("load different edits") {
state.mode = Mode::Edit(EditMode::Loading(Wizard::new()));
}
// TODO Reset when transitioning in/out of this state? Or maybe we just don't draw
// the effects of it. Or eventually, the Option<ID> itself will live in here
// directly.
// TODO Only mouseover lanes and intersections?
state.ui.handle_mouseover(&mut ctx);
if let Some(ID::Lane(id)) = state.ui.state.primary.current_selection {
let lane = state.ui.state.primary.map.get_l(id);
let road = state.ui.state.primary.map.get_r(lane.parent);
if lane.lane_type != LaneType::Sidewalk {
if let Some(new_type) = next_valid_type(road, lane) {
if ctx
.input
.contextual_action(Key::Space, &format!("toggle to {:?}", new_type))
{
let mut new_edits = state.ui.state.primary.map.get_edits().clone();
new_edits.lane_overrides.insert(lane.id, new_type);
apply_map_edits(
&mut PluginCtx {
primary: &mut state.ui.state.primary,
secondary: &mut None,
canvas: ctx.canvas,
cs: &mut state.ui.state.cs,
prerender: ctx.prerender,
input: ctx.input,
hints: &mut state.ui.hints,
recalculate_current_selection: &mut false,
},
new_edits,
);
}
}
}
}
}
Mode::Edit(EditMode::Saving(ref mut wizard)) => {
if save_edits(
@ -76,7 +115,6 @@ impl EditMode {
_ => unreachable!(),
}
ctx.canvas.handle_event(ctx.input);
EventLoopMode::InputOnly
}
@ -166,3 +204,60 @@ fn save_edits(mut wizard: WrappedWizard, map: &mut Map) -> Option<()> {
}
Some(())
}
// For lane editing
fn next_valid_type(r: &Road, l: &Lane) -> Option<LaneType> {
let mut new_type = next_type(l.lane_type);
while new_type != l.lane_type {
if can_change_lane_type(r, l, new_type) {
return Some(new_type);
}
new_type = next_type(new_type);
}
None
}
fn next_type(lt: LaneType) -> LaneType {
match lt {
LaneType::Driving => LaneType::Parking,
LaneType::Parking => LaneType::Biking,
LaneType::Biking => LaneType::Bus,
LaneType::Bus => LaneType::Driving,
LaneType::Sidewalk => unreachable!(),
}
}
fn can_change_lane_type(r: &Road, l: &Lane, lt: LaneType) -> bool {
let (fwds, idx) = r.dir_and_offset(l.id);
// Only one parking lane per side.
if lt == LaneType::Parking {
let has_parking = if fwds {
r.get_lane_types().0
} else {
r.get_lane_types().1
}
.contains(&LaneType::Parking);
if has_parking {
return false;
}
}
// Two adjacent bike lanes is unnecessary.
if lt == LaneType::Biking {
let types = if fwds {
r.get_lane_types().0
} else {
r.get_lane_types().1
};
if (idx != 0 && types[idx - 1] == LaneType::Biking)
|| types.get(idx + 1) == Some(&LaneType::Biking)
{
return false;
}
}
true
}

View File

@ -1,7 +1,6 @@
pub mod a_b_tests;
pub mod color_picker;
pub mod draw_neighborhoods;
pub mod road_editor;
pub mod scenarios;
pub mod stop_sign_editor;
pub mod traffic_signal_editor;

View File

@ -1,103 +0,0 @@
use crate::objects::ID;
use crate::plugins::{apply_map_edits, BlockingPlugin, PluginCtx};
use ezgui::Key;
use map_model::{Lane, LaneType, Road};
pub struct RoadEditor {}
impl RoadEditor {
pub fn new(ctx: &mut PluginCtx) -> Option<RoadEditor> {
if ctx.primary.current_selection.is_none()
&& ctx.primary.sim.is_empty()
&& ctx.input.action_chosen("edit roads")
{
return Some(RoadEditor {});
}
None
}
}
impl BlockingPlugin for RoadEditor {
fn blocking_event(&mut self, ctx: &mut PluginCtx) -> bool {
ctx.input.set_mode("Road Editor", &ctx.canvas);
if ctx.input.modal_action("quit") {
return false;
} else if let Some(ID::Lane(id)) = ctx.primary.current_selection {
let lane = ctx.primary.map.get_l(id);
let road = ctx.primary.map.get_r(lane.parent);
if lane.lane_type == LaneType::Sidewalk {
return true;
}
if let Some(new_type) = next_valid_type(road, lane) {
if ctx
.input
.contextual_action(Key::Space, &format!("toggle to {:?}", new_type))
{
let mut edits = ctx.primary.map.get_edits().clone();
edits.lane_overrides.insert(lane.id, new_type);
apply_map_edits(ctx, edits);
}
}
}
true
}
}
fn next_valid_type(r: &Road, l: &Lane) -> Option<LaneType> {
let mut new_type = next_type(l.lane_type);
while new_type != l.lane_type {
if can_change_lane_type(r, l, new_type) {
return Some(new_type);
}
new_type = next_type(new_type);
}
None
}
fn next_type(lt: LaneType) -> LaneType {
match lt {
LaneType::Driving => LaneType::Parking,
LaneType::Parking => LaneType::Biking,
LaneType::Biking => LaneType::Bus,
LaneType::Bus => LaneType::Driving,
LaneType::Sidewalk => unreachable!(),
}
}
fn can_change_lane_type(r: &Road, l: &Lane, lt: LaneType) -> bool {
let (fwds, idx) = r.dir_and_offset(l.id);
// Only one parking lane per side.
if lt == LaneType::Parking {
let has_parking = if fwds {
r.get_lane_types().0
} else {
r.get_lane_types().1
}
.contains(&LaneType::Parking);
if has_parking {
return false;
}
}
// Two adjacent bike lanes is unnecessary.
if lt == LaneType::Biking {
let types = if fwds {
r.get_lane_types().0
} else {
r.get_lane_types().1
};
if (idx != 0 && types[idx - 1] == LaneType::Biking)
|| types.get(idx + 1) == Some(&LaneType::Biking)
{
return false;
}
}
true
}

View File

@ -222,8 +222,6 @@ impl UIState {
edit::draw_neighborhoods::DrawNeighborhoodState::new(&mut ctx)
{
self.exclusive_blocking_plugin = Some(Box::new(p));
} else if let Some(p) = edit::road_editor::RoadEditor::new(&mut ctx) {
self.exclusive_blocking_plugin = Some(Box::new(p));
} else if let Some(p) = edit::scenarios::ScenarioManager::new(&mut ctx) {
self.exclusive_blocking_plugin = Some(Box::new(p));
} else if let Some(p) = edit::stop_sign_editor::StopSignEditor::new(&mut ctx) {

View File

@ -55,7 +55,6 @@ impl GUI for UI {
(Some(Key::B), "manage A/B tests"),
(None, "configure colors"),
(Some(Key::N), "manage neighborhoods"),
(Some(Key::E), "edit roads"),
(Some(Key::W), "manage scenarios"),
],
),
@ -117,7 +116,6 @@ impl GUI for UI {
(Key::Enter, "quit"),
],
),
ModalMenu::new("Road Editor", vec![(Key::Enter, "quit")]),
ModalMenu::new(
"Color Picker",
vec![(Key::Backspace, "revert"), (Key::Enter, "finalize")],
@ -407,12 +405,7 @@ impl UI {
ctx.canvas.handle_event(ctx.input);
// Always handle mouseover
if !ctx.canvas.is_dragging() && ctx.input.get_moved_mouse().is_some() {
self.state.primary.current_selection = self.mouseover_something(&ctx);
}
if ctx.input.window_lost_cursor() {
self.state.primary.current_selection = None;
}
self.handle_mouseover(&mut ctx);
let mut recalculate_current_selection = false;
self.state.event(
@ -452,6 +445,15 @@ impl UI {
)
}
pub fn handle_mouseover(&mut self, ctx: &mut EventCtx) {
if !ctx.canvas.is_dragging() && ctx.input.get_moved_mouse().is_some() {
self.state.primary.current_selection = self.mouseover_something(&ctx);
}
if ctx.input.window_lost_cursor() {
self.state.primary.current_selection = None;
}
}
fn mouseover_something(&self, ctx: &EventCtx) -> Option<ID> {
// Unzoomed mode. Ignore when debugging areas.
if ctx.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL