mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
folding in road editor
This commit is contained in:
parent
1abb5b630a
commit
138b47512a
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user