mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-26 07:52:05 +03:00
start a tool to understand popular destinations. remove dot map,
superceded by live population map
This commit is contained in:
parent
9ce3061e17
commit
3145326207
@ -93,6 +93,10 @@ impl<T: Ord + PartialEq + Clone> Counter<T> {
|
|||||||
list.into_iter().map(|(t, _)| t).collect()
|
list.into_iter().map(|(t, _)| t).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn max(&self) -> usize {
|
||||||
|
*self.map.values().max().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compare(mut self, mut other: Counter<T>) -> Vec<(T, usize, usize)> {
|
pub fn compare(mut self, mut other: Counter<T>) -> Vec<(T, usize, usize)> {
|
||||||
for key in self.map.keys() {
|
for key in self.map.keys() {
|
||||||
other.map.entry(key.clone()).or_insert(0);
|
other.map.entry(key.clone()).or_insert(0);
|
||||||
@ -106,6 +110,9 @@ impl<T: Ord + PartialEq + Clone> Counter<T> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow(&self) -> &BTreeMap<T, usize> {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
pub fn consume(self) -> BTreeMap<T, usize> {
|
pub fn consume(self) -> BTreeMap<T, usize> {
|
||||||
self.map
|
self.map
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ first make sure your .osm has been clipped:
|
|||||||
[the instructions](dev.md#building-map-data). You'll need Rust, osmconvert,
|
[the instructions](dev.md#building-map-data). You'll need Rust, osmconvert,
|
||||||
gdal, etc.
|
gdal, etc.
|
||||||
|
|
||||||
2. Use http://geojson.io/ to draw a polygon around the region you want to
|
2. Use [geojson.io](http://geojson.io/) to draw a polygon around the region you
|
||||||
simulate.
|
want to simulate.
|
||||||
|
|
||||||
3. Create a new directory: `mkdir -p data/input/your_city/polygons`
|
3. Create a new directory: `mkdir -p data/input/your_city/polygons`
|
||||||
|
|
||||||
|
@ -278,6 +278,7 @@ impl App {
|
|||||||
&ShowEverything::new(),
|
&ShowEverything::new(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,10 +292,11 @@ impl App {
|
|||||||
show_objs: &dyn ShowObject,
|
show_objs: &dyn ShowObject,
|
||||||
debug_mode: bool,
|
debug_mode: bool,
|
||||||
unzoomed_roads_and_intersections: bool,
|
unzoomed_roads_and_intersections: bool,
|
||||||
|
unzoomed_buildings: bool,
|
||||||
) -> Option<ID> {
|
) -> Option<ID> {
|
||||||
// Unzoomed mode. Ignore when debugging areas and extra shapes.
|
// Unzoomed mode. Ignore when debugging areas and extra shapes.
|
||||||
if ctx.canvas.cam_zoom < self.opts.min_zoom_for_detail
|
if ctx.canvas.cam_zoom < self.opts.min_zoom_for_detail
|
||||||
&& !(debug_mode || unzoomed_roads_and_intersections)
|
&& !(debug_mode || unzoomed_roads_and_intersections || unzoomed_buildings)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -332,6 +334,11 @@ impl App {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ID::Building(_) => {
|
||||||
|
if ctx.canvas.cam_zoom < self.opts.min_zoom_for_detail && !unzoomed_buildings {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if ctx.canvas.cam_zoom < self.opts.min_zoom_for_detail {
|
if ctx.canvas.cam_zoom < self.opts.min_zoom_for_detail {
|
||||||
continue;
|
continue;
|
||||||
|
@ -110,7 +110,7 @@ impl State for DebugMode {
|
|||||||
|
|
||||||
if ctx.redo_mouseover() {
|
if ctx.redo_mouseover() {
|
||||||
app.primary.current_selection =
|
app.primary.current_selection =
|
||||||
app.calculate_current_selection(ctx, &app.primary.sim, self, true, false);
|
app.calculate_current_selection(ctx, &app.primary.sim, self, true, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.composite.event(ctx) {
|
match self.composite.event(ctx) {
|
||||||
@ -179,8 +179,14 @@ impl State for DebugMode {
|
|||||||
}
|
}
|
||||||
"unhide everything" => {
|
"unhide everything" => {
|
||||||
self.hidden.clear();
|
self.hidden.clear();
|
||||||
app.primary.current_selection =
|
app.primary.current_selection = app.calculate_current_selection(
|
||||||
app.calculate_current_selection(ctx, &app.primary.sim, self, true, false);
|
ctx,
|
||||||
|
&app.primary.sim,
|
||||||
|
self,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
self.reset_info(ctx);
|
self.reset_info(ctx);
|
||||||
}
|
}
|
||||||
"search OSM metadata" => {
|
"search OSM metadata" => {
|
||||||
|
@ -28,7 +28,7 @@ struct Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockMap {
|
impl BlockMap {
|
||||||
pub fn new(ctx: &mut EventCtx, app: &App, scenario: Scenario) -> BlockMap {
|
pub fn new(ctx: &mut EventCtx, app: &App, scenario: Scenario) -> Box<dyn State> {
|
||||||
let mut bldg_to_block = HashMap::new();
|
let mut bldg_to_block = HashMap::new();
|
||||||
let mut blocks = Vec::new();
|
let mut blocks = Vec::new();
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ impl BlockMap {
|
|||||||
all_blocks.push(Color::YELLOW.alpha(0.5), block.shape.clone());
|
all_blocks.push(Color::YELLOW.alpha(0.5), block.shape.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockMap {
|
Box::new(BlockMap {
|
||||||
bldg_to_block,
|
bldg_to_block,
|
||||||
blocks,
|
blocks,
|
||||||
scenario,
|
scenario,
|
||||||
@ -89,7 +89,7 @@ impl BlockMap {
|
|||||||
)
|
)
|
||||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||||
.build(ctx),
|
.build(ctx),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_per_block(&self, base: &Block, from: bool, map: &Map) -> Vec<(&Block, usize)> {
|
fn count_per_block(&self, base: &Block, from: bool, map: &Map) -> Vec<(&Block, usize)> {
|
||||||
|
152
game/src/devtools/destinations.rs
Normal file
152
game/src/devtools/destinations.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use crate::app::{App, ShowEverything};
|
||||||
|
use crate::common::{make_heatmap, HeatmapOptions};
|
||||||
|
use crate::game::{State, Transition};
|
||||||
|
use crate::helpers::ID;
|
||||||
|
use abstutil::Counter;
|
||||||
|
use ezgui::{
|
||||||
|
hotkey, Btn, Checkbox, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||||
|
HorizontalAlignment, Key, Line, Outcome, Text, VerticalAlignment, Widget,
|
||||||
|
};
|
||||||
|
use map_model::BuildingID;
|
||||||
|
use sim::{DontDrawAgents, Scenario, TripEndpoint};
|
||||||
|
|
||||||
|
pub struct PopularDestinations {
|
||||||
|
per_bldg: Counter<BuildingID>,
|
||||||
|
composite: Composite,
|
||||||
|
opts: Option<HeatmapOptions>,
|
||||||
|
draw: Drawable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopularDestinations {
|
||||||
|
pub fn new(ctx: &mut EventCtx, app: &App, scenario: &Scenario) -> Box<dyn State> {
|
||||||
|
let mut per_bldg = Counter::new();
|
||||||
|
for p in &scenario.people {
|
||||||
|
for trip in &p.trips {
|
||||||
|
if let TripEndpoint::Bldg(b) = trip.trip.end(&app.primary.map) {
|
||||||
|
per_bldg.inc(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopularDestinations::make(ctx, app, per_bldg, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make(
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
app: &App,
|
||||||
|
per_bldg: Counter<BuildingID>,
|
||||||
|
opts: Option<HeatmapOptions>,
|
||||||
|
) -> Box<dyn State> {
|
||||||
|
let map = &app.primary.map;
|
||||||
|
let mut batch = GeomBatch::new();
|
||||||
|
let controls = if let Some(ref o) = opts {
|
||||||
|
let mut pts = Vec::new();
|
||||||
|
for (b, cnt) in per_bldg.borrow() {
|
||||||
|
let pt = map.get_b(*b).label_center;
|
||||||
|
for _ in 0..*cnt {
|
||||||
|
pts.push(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO Er, the heatmap actually looks terrible.
|
||||||
|
Widget::col(o.to_controls(ctx, make_heatmap(&mut batch, map.get_bounds(), pts, o)))
|
||||||
|
} else {
|
||||||
|
let max = per_bldg.max();
|
||||||
|
let gradient = colorous::REDS;
|
||||||
|
for (b, cnt) in per_bldg.borrow() {
|
||||||
|
let c = gradient.eval_rational(*cnt, max);
|
||||||
|
batch.push(
|
||||||
|
Color::rgb(c.r as usize, c.g as usize, c.b as usize),
|
||||||
|
map.get_b(*b).polygon.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget::nothing()
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(PopularDestinations {
|
||||||
|
per_bldg,
|
||||||
|
draw: ctx.upload(batch),
|
||||||
|
composite: Composite::new(
|
||||||
|
Widget::col(vec![
|
||||||
|
Widget::row(vec![
|
||||||
|
Line("Most popular destinations")
|
||||||
|
.small_heading()
|
||||||
|
.draw(ctx)
|
||||||
|
.margin_right(10),
|
||||||
|
Btn::text_fg("X")
|
||||||
|
.build_def(ctx, hotkey(Key::Escape))
|
||||||
|
.align_right(),
|
||||||
|
]),
|
||||||
|
Checkbox::text(ctx, "Show heatmap", None, opts.is_some()),
|
||||||
|
controls,
|
||||||
|
])
|
||||||
|
.padding(10)
|
||||||
|
.bg(app.cs.panel_bg),
|
||||||
|
)
|
||||||
|
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||||
|
.build(ctx),
|
||||||
|
opts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State for PopularDestinations {
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||||
|
ctx.canvas_movement();
|
||||||
|
if ctx.redo_mouseover() {
|
||||||
|
app.primary.current_selection = app.calculate_current_selection(
|
||||||
|
ctx,
|
||||||
|
&DontDrawAgents {},
|
||||||
|
&ShowEverything::new(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
if let Some(ID::Building(_)) = app.primary.current_selection {
|
||||||
|
} else {
|
||||||
|
app.primary.current_selection = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.composite.event(ctx) {
|
||||||
|
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||||
|
"X" => {
|
||||||
|
return Transition::Pop;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let opts = if self.composite.is_checked("Show heatmap") {
|
||||||
|
Some(HeatmapOptions::from_controls(&self.composite))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if self.opts != opts {
|
||||||
|
return Transition::Replace(PopularDestinations::make(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.per_bldg.clone(),
|
||||||
|
opts,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition::Keep
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||||
|
g.redraw(&self.draw);
|
||||||
|
self.composite.draw(g);
|
||||||
|
|
||||||
|
if let Some(ID::Building(b)) = app.primary.current_selection {
|
||||||
|
let mut txt = Text::new();
|
||||||
|
txt.add(Line(format!(
|
||||||
|
"{} trips to here",
|
||||||
|
abstutil::prettyprint_usize(self.per_bldg.get(b))
|
||||||
|
)));
|
||||||
|
for (name, amenity) in &app.primary.map.get_b(b).amenities {
|
||||||
|
txt.add(Line(format!("- {} ({})", name, amenity)));
|
||||||
|
}
|
||||||
|
g.draw_mouse_tooltip(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -210,6 +210,7 @@ impl State for ParkingMapper {
|
|||||||
&ShowEverything::new(),
|
&ShowEverything::new(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
) {
|
) {
|
||||||
Some(ID::Road(r)) => Some(r),
|
Some(ID::Road(r)) => Some(r),
|
||||||
Some(ID::Lane(l)) => Some(map.get_l(l).parent),
|
Some(ID::Lane(l)) => Some(map.get_l(l).parent),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod blocks;
|
mod blocks;
|
||||||
|
mod destinations;
|
||||||
pub mod mapping;
|
pub mod mapping;
|
||||||
mod polygon;
|
mod polygon;
|
||||||
mod scenario;
|
mod scenario;
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::common::{tool_panel, Colorer, CommonState, ContextualActions, Warping};
|
use crate::common::{tool_panel, Colorer, CommonState, ContextualActions, Warping};
|
||||||
use crate::devtools::blocks::BlockMap;
|
use crate::devtools::blocks::BlockMap;
|
||||||
|
use crate::devtools::destinations::PopularDestinations;
|
||||||
use crate::game::{State, Transition, WizardState};
|
use crate::game::{State, Transition, WizardState};
|
||||||
use crate::helpers::ID;
|
use crate::helpers::ID;
|
||||||
use crate::managed::{WrappedComposite, WrappedOutcome};
|
use crate::managed::{WrappedComposite, WrappedOutcome};
|
||||||
use abstutil::{prettyprint_usize, Counter, MultiMap};
|
use abstutil::{prettyprint_usize, Counter, MultiMap};
|
||||||
use ezgui::{
|
use ezgui::{
|
||||||
hotkey, lctrl, Btn, Choice, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
|
hotkey, lctrl, Choice, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line,
|
||||||
HorizontalAlignment, Key, Line, Outcome, Slider, Text, VerticalAlignment, Widget,
|
Outcome, Text,
|
||||||
};
|
};
|
||||||
use geom::{Distance, Line, PolyLine, Polygon};
|
use geom::{Distance, PolyLine};
|
||||||
use map_model::{BuildingID, IntersectionID, Map};
|
use map_model::{BuildingID, IntersectionID, Map};
|
||||||
use sim::{
|
use sim::{
|
||||||
DrivingGoal, IndividTrip, PersonSpec, Scenario, SidewalkPOI, SidewalkSpot, SpawnTrip,
|
DrivingGoal, IndividTrip, PersonSpec, Scenario, SidewalkPOI, SidewalkSpot, SpawnTrip,
|
||||||
@ -104,7 +105,7 @@ impl ScenarioManager {
|
|||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
(hotkey(Key::B), "block map"),
|
(hotkey(Key::B), "block map"),
|
||||||
(hotkey(Key::D), "dot map"),
|
(hotkey(Key::D), "popular destinations"),
|
||||||
(lctrl(Key::P), "stop showing paths"),
|
(lctrl(Key::P), "stop showing paths"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -128,15 +129,11 @@ impl State for ScenarioManager {
|
|||||||
"X" => {
|
"X" => {
|
||||||
return Transition::Pop;
|
return Transition::Pop;
|
||||||
}
|
}
|
||||||
"dot map" => {
|
|
||||||
return Transition::Push(Box::new(DotMap::new(ctx, app, &self.scenario)));
|
|
||||||
}
|
|
||||||
"block map" => {
|
"block map" => {
|
||||||
return Transition::Push(Box::new(BlockMap::new(
|
return Transition::Push(BlockMap::new(ctx, app, self.scenario.clone()));
|
||||||
ctx,
|
}
|
||||||
app,
|
"popular destinations" => {
|
||||||
self.scenario.clone(),
|
return Transition::Push(PopularDestinations::new(ctx, app, &self.scenario));
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
// TODO Inactivate this sometimes
|
// TODO Inactivate this sometimes
|
||||||
"stop showing paths" => {
|
"stop showing paths" => {
|
||||||
@ -465,109 +462,6 @@ fn show_demand(
|
|||||||
batch.upload(ctx)
|
batch.upload(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DotMap {
|
|
||||||
composite: Composite,
|
|
||||||
|
|
||||||
lines: Vec<Line>,
|
|
||||||
draw: Option<(f64, Drawable)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DotMap {
|
|
||||||
fn new(ctx: &mut EventCtx, app: &App, scenario: &Scenario) -> DotMap {
|
|
||||||
let map = &app.primary.map;
|
|
||||||
let lines = scenario
|
|
||||||
.people
|
|
||||||
.iter()
|
|
||||||
.flat_map(|p| {
|
|
||||||
p.trips.iter().filter_map(|trip| {
|
|
||||||
let (start, end) = match &trip.trip {
|
|
||||||
SpawnTrip::VehicleAppearing { start, goal, .. } => {
|
|
||||||
(start.pt(map), goal.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::FromBorder { dr, goal, .. } => {
|
|
||||||
(map.get_i(dr.src_i(map)).polygon.center(), goal.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::UsingParkedCar(b, goal) => {
|
|
||||||
(map.get_b(*b).polygon.center(), goal.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::UsingBike(start, goal) => {
|
|
||||||
(start.sidewalk_pos.pt(map), goal.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::JustWalking(start, goal) => {
|
|
||||||
(start.sidewalk_pos.pt(map), goal.sidewalk_pos.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::UsingTransit(start, goal, _, _, _) => {
|
|
||||||
(start.sidewalk_pos.pt(map), goal.sidewalk_pos.pt(map))
|
|
||||||
}
|
|
||||||
SpawnTrip::Remote { .. } => unimplemented!(),
|
|
||||||
};
|
|
||||||
Line::maybe_new(start, end)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
DotMap {
|
|
||||||
composite: Composite::new(
|
|
||||||
Widget::col(vec![
|
|
||||||
Widget::row(vec![
|
|
||||||
Line("Dot map of all trips").small_heading().draw(ctx),
|
|
||||||
Btn::text_fg("X")
|
|
||||||
.build_def(ctx, hotkey(Key::Escape))
|
|
||||||
.align_right(),
|
|
||||||
]),
|
|
||||||
Slider::horizontal(ctx, 150.0, 25.0, 0.0).named("time slider"),
|
|
||||||
])
|
|
||||||
.padding(10)
|
|
||||||
.bg(app.cs.panel_bg),
|
|
||||||
)
|
|
||||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
|
||||||
.build(ctx),
|
|
||||||
|
|
||||||
lines,
|
|
||||||
draw: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State for DotMap {
|
|
||||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
|
||||||
ctx.canvas_movement();
|
|
||||||
|
|
||||||
match self.composite.event(ctx) {
|
|
||||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
|
||||||
"X" => {
|
|
||||||
return Transition::Pop;
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pct = self.composite.slider("time slider").get_percent();
|
|
||||||
|
|
||||||
if self.draw.as_ref().map(|(p, _)| pct != *p).unwrap_or(true) {
|
|
||||||
let mut batch = GeomBatch::new();
|
|
||||||
let radius = Distance::meters(5.0);
|
|
||||||
for l in &self.lines {
|
|
||||||
// Circles are too expensive. :P
|
|
||||||
batch.push(
|
|
||||||
Color::RED,
|
|
||||||
Polygon::rectangle_centered(l.percent_along(pct), radius, radius),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.draw = Some((pct, batch.upload(ctx)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Transition::Keep
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
|
||||||
if let Some((_, ref d)) = self.draw {
|
|
||||||
g.redraw(d);
|
|
||||||
}
|
|
||||||
self.composite.draw(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Actions<'a> {
|
struct Actions<'a> {
|
||||||
demand: &'a mut Option<Drawable>,
|
demand: &'a mut Option<Drawable>,
|
||||||
scenario: &'a Scenario,
|
scenario: &'a Scenario,
|
||||||
|
@ -58,6 +58,7 @@ impl State for BulkSelect {
|
|||||||
&ShowEverything::new(),
|
&ShowEverything::new(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
if let Some(ID::Intersection(_)) = app.primary.current_selection {
|
if let Some(ID::Intersection(_)) = app.primary.current_selection {
|
||||||
} else {
|
} else {
|
||||||
@ -296,6 +297,7 @@ impl State for PaintSelect {
|
|||||||
&ShowEverything::new(),
|
&ShowEverything::new(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
if let Some(ID::Road(_)) = app.primary.current_selection {
|
if let Some(ID::Road(_)) = app.primary.current_selection {
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,6 +100,7 @@ impl State for EditMode {
|
|||||||
&ShowEverything::new(),
|
&ShowEverything::new(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
if let Some(ID::Lane(l)) = app.primary.current_selection {
|
if let Some(ID::Lane(l)) = app.primary.current_selection {
|
||||||
if !can_edit_lane(&self.mode, l, app) {
|
if !can_edit_lane(&self.mode, l, app) {
|
||||||
|
@ -47,7 +47,7 @@ pub fn info(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID
|
|||||||
txt.add(Line(format!("{} amenities:", b.amenities.len())));
|
txt.add(Line(format!("{} amenities:", b.amenities.len())));
|
||||||
}
|
}
|
||||||
for (name, amenity) in &b.amenities {
|
for (name, amenity) in &b.amenities {
|
||||||
txt.add(Line(format!("- {} (a {})", name, amenity)));
|
txt.add(Line(format!("- {} ({})", name, amenity)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +132,7 @@ pub fn main_menu(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
|||||||
txt
|
txt
|
||||||
})
|
})
|
||||||
.build_def(ctx, hotkey(Key::M)),
|
.build_def(ctx, hotkey(Key::M)),
|
||||||
if app.opts.dev {
|
Btn::text_bg2("Internal Dev Tools").build_def(ctx, hotkey(Key::D)),
|
||||||
Btn::text_bg2("Internal Dev Tools").build_def(ctx, hotkey(Key::D))
|
|
||||||
} else {
|
|
||||||
Widget::nothing()
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
.centered(),
|
.centered(),
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
@ -146,7 +142,7 @@ pub fn main_menu(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
|||||||
.centered(),
|
.centered(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut c = WrappedComposite::new(
|
let c = WrappedComposite::new(
|
||||||
Composite::new(Widget::col(col).evenly_spaced())
|
Composite::new(Widget::col(col).evenly_spaced())
|
||||||
.exact_size_percent(90, 85)
|
.exact_size_percent(90, 85)
|
||||||
.build(ctx),
|
.build(ctx),
|
||||||
@ -219,13 +215,11 @@ pub fn main_menu(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
|||||||
crate::devtools::mapping::ParkingMapper::new(ctx, app, true, BTreeMap::new()),
|
crate::devtools::mapping::ParkingMapper::new(ctx, app, true, BTreeMap::new()),
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
if app.opts.dev {
|
.cb(
|
||||||
c = c.cb(
|
|
||||||
"Internal Dev Tools",
|
"Internal Dev Tools",
|
||||||
Box::new(|ctx, app| Some(Transition::Push(DevToolsMode::new(ctx, app)))),
|
Box::new(|ctx, app| Some(Transition::Push(DevToolsMode::new(ctx, app)))),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
ManagedGUIState::fullscreen(c)
|
ManagedGUIState::fullscreen(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user