mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-03 03:47:23 +03:00
Broad strokes for fleshless folks
This commit is contained in:
parent
1585eed66f
commit
78f37e68e9
191
game/src/ungap/edit.rs
Normal file
191
game/src/ungap/edit.rs
Normal file
@ -0,0 +1,191 @@
|
||||
use abstutil::Tags;
|
||||
use map_gui::tools::PopupMsg;
|
||||
use map_model::{BufferType, Direction, EditCmd, EditRoad, LaneSpec, LaneType};
|
||||
use widgetry::{
|
||||
Choice, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, Transition};
|
||||
use crate::common::RoadSelector;
|
||||
use crate::edit::apply_map_edits;
|
||||
|
||||
pub struct QuickEdit {
|
||||
top_panel: Panel,
|
||||
selector: RoadSelector,
|
||||
unzoomed_layer: Drawable,
|
||||
}
|
||||
|
||||
impl QuickEdit {
|
||||
pub fn new_state(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
|
||||
let selector =
|
||||
RoadSelector::new(ctx, app, app.primary.map.get_edits().changed_roads.clone());
|
||||
let top_panel = make_top_panel(ctx, &selector);
|
||||
Box::new(QuickEdit {
|
||||
top_panel,
|
||||
selector,
|
||||
unzoomed_layer: crate::ungap::make_unzoomed_layer(ctx, app),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl State<App> for QuickEdit {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.top_panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
"Back" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
"Add bike lanes" => {
|
||||
let messages = make_quick_changes(
|
||||
ctx,
|
||||
app,
|
||||
&self.selector,
|
||||
self.top_panel.dropdown_value("buffer type"),
|
||||
);
|
||||
return Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
Transition::Replace(crate::ungap::ExploreMap::new_state(ctx, app)),
|
||||
Transition::Push(PopupMsg::new_state(ctx, "Changes made", messages)),
|
||||
]);
|
||||
}
|
||||
x => {
|
||||
if self.selector.event(ctx, app, Some(x)) {
|
||||
let new_controls = self.selector.make_controls(ctx);
|
||||
self.top_panel.replace(ctx, "selector", new_controls);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if self.selector.event(ctx, app, None) {
|
||||
let new_controls = self.selector.make_controls(ctx);
|
||||
self.top_panel.replace(ctx, "selector", new_controls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
self.top_panel.draw(g);
|
||||
self.selector.draw(g, app, true);
|
||||
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
|
||||
g.redraw(&self.unzoomed_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_top_panel(ctx: &mut EventCtx, selector: &RoadSelector) -> Panel {
|
||||
Panel::new_builder(Widget::col(vec![
|
||||
Line("Draw your ideal bike network")
|
||||
.small_heading()
|
||||
.into_widget(ctx),
|
||||
selector.make_controls(ctx).named("selector"),
|
||||
// TODO This should be the simplified "edit mode."
|
||||
// - Click a road segment to edit in detail
|
||||
// - A way to quickly sketch new additions
|
||||
// - All the file management -- load, save as, share
|
||||
// - Summarize number of new segments and distance? Or does that belong in the read-only
|
||||
// view?
|
||||
Widget::row(vec![
|
||||
"Protect the new bike lanes?"
|
||||
.text_widget(ctx)
|
||||
.centered_vert(),
|
||||
Widget::dropdown(
|
||||
ctx,
|
||||
"buffer type",
|
||||
Some(BufferType::FlexPosts),
|
||||
vec![
|
||||
// TODO Width / cost summary?
|
||||
Choice::new("diagonal stripes", Some(BufferType::Stripes)),
|
||||
Choice::new("flex posts", Some(BufferType::FlexPosts)),
|
||||
Choice::new("planters", Some(BufferType::Planters)),
|
||||
// Omit the others for now
|
||||
Choice::new("no -- just paint", None),
|
||||
],
|
||||
),
|
||||
]),
|
||||
Widget::custom_row(vec![
|
||||
ctx.style()
|
||||
.btn_solid_primary
|
||||
.text("Add bike lanes")
|
||||
.hotkey(Key::Enter)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_solid_destructive
|
||||
.text("Back")
|
||||
.hotkey(Key::Escape)
|
||||
.build_def(ctx),
|
||||
])
|
||||
.evenly_spaced(),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||
.build(ctx)
|
||||
}
|
||||
|
||||
fn make_quick_changes(
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
selector: &RoadSelector,
|
||||
buffer_type: Option<BufferType>,
|
||||
) -> Vec<String> {
|
||||
// TODO Erasing changes
|
||||
|
||||
let mut edits = app.primary.map.get_edits().clone();
|
||||
let already_modified_roads = edits.changed_roads.clone();
|
||||
let mut num_changes = 0;
|
||||
for r in &selector.roads {
|
||||
if already_modified_roads.contains(r) {
|
||||
continue;
|
||||
}
|
||||
let r = *r;
|
||||
let old = app.primary.map.get_r_edit(r);
|
||||
let mut new = old.clone();
|
||||
maybe_add_bike_lanes(&mut new, buffer_type);
|
||||
if old != new {
|
||||
num_changes += 1;
|
||||
edits.commands.push(EditCmd::ChangeRoad { r, old, new });
|
||||
}
|
||||
}
|
||||
apply_map_edits(ctx, app, edits);
|
||||
|
||||
vec![format!("Changed {} segments", num_changes)]
|
||||
}
|
||||
|
||||
// TODO Unit test me
|
||||
fn maybe_add_bike_lanes(r: &mut EditRoad, buffer_type: Option<BufferType>) {
|
||||
// Super rough first heuristic -- replace parking on each side.
|
||||
let dummy_tags = Tags::empty();
|
||||
|
||||
let mut lanes_ltr = Vec::new();
|
||||
for spec in r.lanes_ltr.drain(..) {
|
||||
if spec.lt != LaneType::Parking {
|
||||
lanes_ltr.push(spec);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(buffer) = buffer_type {
|
||||
// Put the buffer on the proper side
|
||||
let replacements = if spec.dir == Direction::Fwd {
|
||||
[LaneType::Buffer(buffer), LaneType::Biking]
|
||||
} else {
|
||||
[LaneType::Biking, LaneType::Buffer(buffer)]
|
||||
};
|
||||
for lt in replacements {
|
||||
lanes_ltr.push(LaneSpec {
|
||||
lt,
|
||||
dir: spec.dir,
|
||||
width: LaneSpec::typical_lane_widths(lt, &dummy_tags)[0].0,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
lanes_ltr.push(LaneSpec {
|
||||
lt: LaneType::Biking,
|
||||
dir: spec.dir,
|
||||
width: LaneSpec::typical_lane_widths(LaneType::Biking, &dummy_tags)[0].0,
|
||||
});
|
||||
}
|
||||
}
|
||||
r.lanes_ltr = lanes_ltr;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
mod edit;
|
||||
|
||||
use geom::{Circle, Distance, Pt2D};
|
||||
use map_gui::tools::{nice_map_name, CityPicker, PopupMsg};
|
||||
use map_gui::ID;
|
||||
@ -93,7 +95,7 @@ impl State<App> for ExploreMap {
|
||||
return Transition::Push(PopupMsg::new_state(ctx, "TODO", vec!["TODO"]));
|
||||
}
|
||||
"Edit network" => {
|
||||
return Transition::Push(PopupMsg::new_state(ctx, "TODO", vec!["TODO"]));
|
||||
return Transition::Push(edit::QuickEdit::new_state(ctx, app));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -192,6 +194,7 @@ fn make_top_panel(ctx: &mut EventCtx) -> Panel {
|
||||
ctx.style()
|
||||
.btn_solid_primary
|
||||
.icon_text("system/assets/tools/pencil.svg", "Edit network")
|
||||
.hotkey(lctrl(Key::E))
|
||||
.build_def(ctx)
|
||||
.centered_vert(),
|
||||
]))
|
||||
@ -310,7 +313,7 @@ fn magnifying_glass(batch: GeomBatch) -> GeomBatch {
|
||||
new_batch
|
||||
}
|
||||
|
||||
fn make_unzoomed_layer(ctx: &mut EventCtx, app: &App) -> Drawable {
|
||||
pub fn make_unzoomed_layer(ctx: &mut EventCtx, app: &App) -> Drawable {
|
||||
let mut batch = GeomBatch::new();
|
||||
for r in app.primary.map.all_roads() {
|
||||
if r.is_cycleway() {
|
||||
|
Loading…
Reference in New Issue
Block a user