mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 01:15:12 +03:00
Split the controls experiment into a separate binary, get it to draw a
map way more simply than the game
This commit is contained in:
parent
558eb6f716
commit
ab2f6fefec
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -705,6 +705,15 @@ dependencies = [
|
||||
"num-traits 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "experiment"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"geom",
|
||||
"map_gui",
|
||||
"widgetry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
|
@ -4,6 +4,7 @@ members = [
|
||||
"abstutil",
|
||||
"collisions",
|
||||
"convert_osm",
|
||||
"experiment",
|
||||
"game",
|
||||
"geom",
|
||||
"headless",
|
||||
|
@ -139,6 +139,7 @@ Other:
|
||||
- `collisions`: an experimental data format for real-world collision data
|
||||
- `traffic_seitan`: a bug-finding tool that randomly generates live map edits
|
||||
- `tests`: integration tests
|
||||
- `experiment`: ...
|
||||
|
||||
## Code conventions
|
||||
|
||||
|
13
experiment/Cargo.toml
Normal file
13
experiment/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "experiment"
|
||||
version = "0.1.0"
|
||||
authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["widgetry/native-backend"]
|
||||
|
||||
[dependencies]
|
||||
geom = { path = "../geom" }
|
||||
map_gui = { path = "../map_gui" }
|
||||
widgetry = { path = "../widgetry" }
|
@ -1,21 +1,19 @@
|
||||
use geom::{Angle, Circle, Distance, Pt2D, Speed};
|
||||
use map_gui::SimpleApp;
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
UpdateType, VerticalAlignment, Widget,
|
||||
Transition, UpdateType, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::Transition;
|
||||
|
||||
pub struct Experiment {
|
||||
pub struct Game {
|
||||
panel: Panel,
|
||||
controls: Box<dyn Controller>,
|
||||
sleigh: Pt2D,
|
||||
}
|
||||
|
||||
impl Experiment {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
Box::new(Experiment {
|
||||
impl Game {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<SimpleApp>> {
|
||||
Box::new(Game {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Line("Experiment").small_heading().draw(ctx),
|
||||
@ -31,8 +29,8 @@ impl Experiment {
|
||||
}
|
||||
}
|
||||
|
||||
impl State<App> for Experiment {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
impl State<SimpleApp> for Game {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut SimpleApp) -> Transition<SimpleApp> {
|
||||
let (dx, dy) = self.controls.displacement(ctx);
|
||||
self.sleigh = self.sleigh.offset(dx, dy);
|
||||
ctx.canvas.center_on_map_pt(self.sleigh);
|
||||
@ -58,7 +56,7 @@ impl State<App> for Experiment {
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
|
||||
self.panel.draw(g);
|
||||
|
||||
g.draw_polygon(
|
9
experiment/src/main.rs
Normal file
9
experiment/src/main.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod game;
|
||||
|
||||
fn main() {
|
||||
widgetry::run(widgetry::Settings::new("experiment"), |ctx| {
|
||||
ctx.canvas.cam_zoom = 10.0; // TODO
|
||||
let app = map_gui::SimpleApp::new(ctx);
|
||||
(app, vec![game::Game::new(ctx)])
|
||||
});
|
||||
}
|
@ -6,6 +6,9 @@ use rand::seq::SliceRandom;
|
||||
|
||||
use abstutil::{MapName, Timer};
|
||||
use geom::{Bounds, Circle, Distance, Duration, Pt2D, Time};
|
||||
use map_gui::colors::ColorScheme;
|
||||
use map_gui::options::Options;
|
||||
use map_gui::render::{unzoomed_agent_radius, AgentCache, DrawMap, DrawOptions, Renderable};
|
||||
use map_model::{IntersectionID, Map, Traversable};
|
||||
use sim::{AgentID, Analytics, Scenario, Sim, SimCallback, SimFlags};
|
||||
use widgetry::{Canvas, EventCtx, GfxCtx, Prerender, SharedAppState};
|
||||
@ -15,11 +18,6 @@ use crate::edit::apply_map_edits;
|
||||
use crate::helpers::ID;
|
||||
use crate::layer::Layer;
|
||||
use crate::sandbox::{GameplayMode, TutorialState};
|
||||
use map_gui::colors::{ColorScheme, ColorSchemeChoice};
|
||||
use map_gui::options::Options;
|
||||
use map_gui::render::{
|
||||
unzoomed_agent_radius, AgentCache, DrawMap, DrawOptions, Renderable, UnzoomedAgents,
|
||||
};
|
||||
|
||||
/// The top-level data that lasts through the entire game, no matter what state the game is in.
|
||||
pub struct App {
|
||||
@ -454,6 +452,10 @@ impl map_gui::AppLike for App {
|
||||
&self.cs
|
||||
}
|
||||
#[inline]
|
||||
fn mut_cs(&mut self) -> &mut ColorScheme {
|
||||
&mut self.cs
|
||||
}
|
||||
#[inline]
|
||||
fn draw_map(&self) -> &DrawMap {
|
||||
&self.primary.draw_map
|
||||
}
|
||||
@ -469,24 +471,6 @@ impl map_gui::AppLike for App {
|
||||
fn mut_opts(&mut self) -> &mut Options {
|
||||
&mut self.opts
|
||||
}
|
||||
#[inline]
|
||||
fn unzoomed_agents(&self) -> &UnzoomedAgents {
|
||||
&self.unzoomed_agents
|
||||
}
|
||||
fn change_color_scheme(&mut self, ctx: &mut EventCtx, cs: ColorSchemeChoice) -> bool {
|
||||
if self.opts.color_scheme == cs {
|
||||
return false;
|
||||
}
|
||||
self.opts.color_scheme = cs;
|
||||
self.cs = ColorScheme::new(ctx, self.opts.color_scheme);
|
||||
|
||||
ctx.loading_screen("rerendering map colors", |ctx, timer| {
|
||||
self.primary.draw_map =
|
||||
DrawMap::new(&self.primary.map, &self.opts, &self.cs, ctx, timer);
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShowLayers {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use abstutil::{prettyprint_usize, MapName};
|
||||
use geom::{Distance, Percent, Polygon, Pt2D};
|
||||
use map_gui::render::DrawArea;
|
||||
use map_model::City;
|
||||
use widgetry::{
|
||||
Autocomplete, Btn, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome,
|
||||
@ -10,7 +11,6 @@ use crate::app::App;
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{grey_out_map, nice_map_name, open_browser};
|
||||
use crate::load::MapLoader;
|
||||
use map_gui::render::DrawArea;
|
||||
|
||||
pub struct CityPicker {
|
||||
panel: Panel,
|
||||
|
@ -2,6 +2,8 @@ use std::collections::HashSet;
|
||||
|
||||
use abstutil::{MapName, Parallelism, Tags, Timer};
|
||||
use geom::{Distance, Pt2D};
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::{calculate_corners, DrawOptions};
|
||||
use map_model::{osm, ControlTrafficSignal, NORMAL_LANE_THICKNESS};
|
||||
use sim::{AgentID, Sim};
|
||||
use widgetry::{
|
||||
@ -16,8 +18,6 @@ use crate::game::{ChooseSomething, PopupMsg, PromptInput, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::load::MapLoader;
|
||||
use crate::sandbox::GameplayMode;
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::{calculate_corners, DrawOptions};
|
||||
|
||||
mod blocked_by;
|
||||
mod floodfill;
|
||||
|
@ -7,6 +7,7 @@ use aabb_quadtree::QuadTree;
|
||||
use abstutil::{prettyprint_usize, Parallelism, Timer};
|
||||
use geom::{Circle, Distance, PolyLine, Polygon, Pt2D, Ring};
|
||||
use kml::{ExtraShape, ExtraShapes};
|
||||
use map_gui::colors::ColorScheme;
|
||||
use map_model::BuildingID;
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
@ -15,7 +16,6 @@ use widgetry::{
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{ChooseSomething, PopupMsg, Transition};
|
||||
use map_gui::colors::ColorScheme;
|
||||
|
||||
pub struct ViewKML {
|
||||
panel: Panel,
|
||||
|
@ -14,7 +14,6 @@ use crate::game::{ChooseSomething, Transition};
|
||||
use crate::helpers::nice_map_name;
|
||||
|
||||
mod collisions;
|
||||
pub mod controls;
|
||||
mod destinations;
|
||||
pub mod fifteen_min;
|
||||
mod kml;
|
||||
|
@ -2,6 +2,8 @@ use std::collections::BTreeSet;
|
||||
|
||||
use abstutil::{prettyprint_usize, Counter};
|
||||
use geom::ArrowCap;
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
use map_model::osm;
|
||||
use widgetry::{
|
||||
lctrl, Btn, Checkbox, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
@ -14,8 +16,6 @@ use crate::common::{CityPicker, Minimap, Navigator};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{nice_map_name, open_browser, ID};
|
||||
use crate::sandbox::TurnExplorer;
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
|
||||
pub struct Viewer {
|
||||
top_panel: Panel,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use geom::{Distance, LonLat, PolyLine, Polygon, Pt2D, Ring};
|
||||
use map_gui::render::DrawOptions;
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, RewriteColor, State, Text, VerticalAlignment,
|
||||
@ -10,7 +11,6 @@ use widgetry::{
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::CommonState;
|
||||
use crate::game::{ChooseSomething, PromptInput, Transition};
|
||||
use map_gui::render::DrawOptions;
|
||||
|
||||
// TODO This is a really great example of things that widgetry ought to make easier. Maybe a radio
|
||||
// button-ish thing to start?
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use geom::ArrowCap;
|
||||
use map_gui::render::{DrawOptions, DrawUberTurnGroup, BIG_ARROW_THICKNESS};
|
||||
use map_model::{IntersectionCluster, IntersectionID};
|
||||
use widgetry::{
|
||||
Btn, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Outcome, Panel,
|
||||
@ -9,7 +10,6 @@ use widgetry::{
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::game::Transition;
|
||||
use map_gui::render::{DrawOptions, DrawUberTurnGroup, BIG_ARROW_THICKNESS};
|
||||
|
||||
pub struct ClusterTrafficSignalEditor {
|
||||
panel: Panel,
|
||||
|
@ -1,3 +1,4 @@
|
||||
use map_gui::render::Renderable;
|
||||
use map_model::{EditCmd, LaneID, LaneType, Map};
|
||||
use widgetry::{
|
||||
Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
@ -13,7 +14,6 @@ use crate::edit::{
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::sandbox::GameplayMode;
|
||||
use map_gui::render::Renderable;
|
||||
|
||||
pub struct LaneEditor {
|
||||
l: LaneID,
|
||||
|
@ -4,6 +4,8 @@ use maplit::btreeset;
|
||||
|
||||
use abstutil::{prettyprint_usize, Timer};
|
||||
use geom::Speed;
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::DrawMap;
|
||||
use map_model::{EditCmd, IntersectionID, LaneID, LaneType, MapEdits};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Menu,
|
||||
@ -22,8 +24,6 @@ use crate::debug::DebugMode;
|
||||
use crate::game::{ChooseSomething, PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::sandbox::{GameplayMode, SandboxMode, TimeWarpScreen};
|
||||
use map_gui::options::OptionsPanel;
|
||||
use map_gui::render::DrawMap;
|
||||
|
||||
mod bulk;
|
||||
mod cluster_traffic_signals;
|
||||
|
@ -4,6 +4,7 @@ use maplit::btreeset;
|
||||
|
||||
use abstutil::Timer;
|
||||
use geom::Polygon;
|
||||
use map_gui::render::DrawIntersection;
|
||||
use map_model::{
|
||||
ControlStopSign, ControlTrafficSignal, EditCmd, EditIntersection, IntersectionID, RoadID,
|
||||
};
|
||||
@ -17,7 +18,6 @@ use crate::common::CommonState;
|
||||
use crate::edit::{apply_map_edits, check_sidewalk_connectivity, TrafficSignalEditor};
|
||||
use crate::game::Transition;
|
||||
use crate::sandbox::GameplayMode;
|
||||
use map_gui::render::DrawIntersection;
|
||||
|
||||
// TODO For now, individual turns can't be manipulated. Banning turns could be useful, but I'm not
|
||||
// sure what to do about the player orphaning a section of the map.
|
||||
|
@ -2,6 +2,8 @@ use std::collections::{BTreeSet, VecDeque};
|
||||
|
||||
use abstutil::Timer;
|
||||
use geom::{Distance, Duration, Line, Polygon, Pt2D};
|
||||
use map_gui::options::TrafficSignalStyle;
|
||||
use map_gui::render::{traffic_signal, DrawMovement, DrawOptions};
|
||||
use map_model::{
|
||||
ControlTrafficSignal, EditCmd, EditIntersection, IntersectionID, MovementID, PhaseType, Stage,
|
||||
TurnPriority,
|
||||
@ -17,8 +19,6 @@ use crate::common::{CommonState, Warping};
|
||||
use crate::edit::{apply_map_edits, ConfirmDiscard};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::sandbox::GameplayMode;
|
||||
use map_gui::options::TrafficSignalStyle;
|
||||
use map_gui::render::{traffic_signal, DrawMovement, DrawOptions};
|
||||
|
||||
mod edits;
|
||||
mod offsets;
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use geom::{Angle, Circle, Distance, Speed, Time};
|
||||
use map_gui::render::DrawPedestrian;
|
||||
use map_model::{BuildingID, LaneID, OffstreetParking, Traversable, SIDEWALK_THICKNESS};
|
||||
use sim::{DrawPedestrianInput, PedestrianID, PersonID, TripMode, TripResult};
|
||||
use widgetry::{Btn, Color, EventCtx, Line, Text, TextExt, Widget};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::info::{header_btns, make_table, make_tabs, Details, Tab};
|
||||
use map_gui::render::DrawPedestrian;
|
||||
|
||||
pub fn info(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID) -> Vec<Widget> {
|
||||
let mut rows = header(ctx, app, details, id, Tab::BldgInfo(id));
|
||||
|
@ -2,6 +2,8 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{ArrowCap, Distance, Duration, PolyLine, Polygon, Time};
|
||||
use map_gui::options::TrafficSignalStyle;
|
||||
use map_gui::render::traffic_signal::draw_signal_stage;
|
||||
use map_model::{IntersectionID, IntersectionType, PhaseType};
|
||||
use sim::AgentType;
|
||||
use widgetry::{
|
||||
@ -12,8 +14,6 @@ use widgetry::{
|
||||
use crate::app::App;
|
||||
use crate::helpers::color_for_agent_type;
|
||||
use crate::info::{header_btns, make_tabs, throughput, DataOptions, Details, Tab};
|
||||
use map_gui::options::TrafficSignalStyle;
|
||||
use map_gui::render::traffic_signal::draw_signal_stage;
|
||||
|
||||
pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: IntersectionID) -> Vec<Widget> {
|
||||
let mut rows = header(ctx, app, details, id, Tab::IntersectionInfo(id));
|
||||
|
@ -2,6 +2,7 @@ use std::collections::BTreeSet;
|
||||
|
||||
use abstutil::{prettyprint_usize, Counter, Parallelism};
|
||||
use geom::{Circle, Distance, Duration, Pt2D, Time};
|
||||
use map_gui::render::unzoomed_agent_radius;
|
||||
use map_model::{
|
||||
BuildingID, Map, OffstreetParking, ParkingLotID, PathConstraints, PathRequest, RoadID,
|
||||
};
|
||||
@ -14,7 +15,6 @@ use widgetry::{
|
||||
use crate::app::App;
|
||||
use crate::common::{ColorLegend, ColorNetwork};
|
||||
use crate::layer::{Layer, LayerOutcome};
|
||||
use map_gui::render::unzoomed_agent_radius;
|
||||
|
||||
pub struct Occupancy {
|
||||
time: Time,
|
||||
|
@ -4,6 +4,7 @@ use maplit::btreeset;
|
||||
|
||||
use abstutil::{prettyprint_usize, Counter};
|
||||
use geom::{Circle, Distance, Duration, Polygon, Pt2D, Time};
|
||||
use map_gui::render::unzoomed_agent_radius;
|
||||
use map_model::{IntersectionID, Map, Traversable};
|
||||
use sim::VehicleType;
|
||||
use widgetry::{
|
||||
@ -15,7 +16,6 @@ use crate::app::App;
|
||||
use crate::common::{ColorLegend, ColorNetwork, DivergingScale};
|
||||
use crate::helpers::ID;
|
||||
use crate::layer::{Layer, LayerOutcome};
|
||||
use map_gui::render::unzoomed_agent_radius;
|
||||
|
||||
pub struct Backpressure {
|
||||
time: Time,
|
||||
|
@ -2,13 +2,13 @@
|
||||
extern crate log;
|
||||
|
||||
use abstutil::CmdArgs;
|
||||
use map_gui::options::Options;
|
||||
use sim::SimFlags;
|
||||
use widgetry::{EventCtx, State};
|
||||
|
||||
use crate::app::{App, Flags};
|
||||
use crate::pregame::TitleScreen;
|
||||
use crate::sandbox::{GameplayMode, SandboxMode};
|
||||
use map_gui::options::Options;
|
||||
|
||||
mod app;
|
||||
mod challenges;
|
||||
@ -111,7 +111,6 @@ pub fn main(mut args: CmdArgs) {
|
||||
let start_with_edits = args.optional("--edits");
|
||||
let osm_viewer = args.enabled("--osm");
|
||||
let fifteen_min = args.enabled("--15min");
|
||||
let experiment = args.enabled("--experiment");
|
||||
|
||||
args.done();
|
||||
|
||||
@ -124,7 +123,6 @@ pub fn main(mut args: CmdArgs) {
|
||||
mode,
|
||||
osm_viewer,
|
||||
fifteen_min,
|
||||
experiment,
|
||||
)
|
||||
});
|
||||
}
|
||||
@ -137,14 +135,12 @@ fn setup_app(
|
||||
maybe_mode: Option<GameplayMode>,
|
||||
osm_viewer: bool,
|
||||
fifteen_min: bool,
|
||||
experiment: bool,
|
||||
) -> (App, Vec<Box<dyn State<App>>>) {
|
||||
let title = !opts.dev
|
||||
&& !flags.sim_flags.load.contains("player/save")
|
||||
&& !flags.sim_flags.load.contains("/scenarios/")
|
||||
&& !osm_viewer
|
||||
&& !fifteen_min
|
||||
&& !experiment
|
||||
&& maybe_mode.is_none();
|
||||
let mut app = App::new(flags, opts, ctx, title);
|
||||
|
||||
@ -188,8 +184,6 @@ fn setup_app(
|
||||
vec![crate::devtools::fifteen_min::Viewer::random_start(
|
||||
ctx, &app,
|
||||
)]
|
||||
} else if experiment {
|
||||
vec![crate::devtools::controls::Experiment::new(ctx)]
|
||||
} else {
|
||||
let mode = maybe_mode
|
||||
.unwrap_or_else(|| GameplayMode::Freeform(app.primary.map.get_name().clone()));
|
||||
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use abstutil::{prettyprint_usize, Counter, Parallelism, Timer};
|
||||
use geom::{ArrowCap, Distance, Duration, Polygon, Time};
|
||||
use map_gui::render::DrawOptions;
|
||||
use map_model::{ControlTrafficSignal, IntersectionID, MovementID, PathStep, TurnType};
|
||||
use sim::TripEndpoint;
|
||||
use widgetry::{
|
||||
@ -13,7 +14,6 @@ use crate::app::{App, ShowEverything};
|
||||
use crate::common::CommonState;
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use map_gui::render::DrawOptions;
|
||||
|
||||
pub struct TrafficSignalDemand {
|
||||
panel: Panel,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use geom::{ArrowCap, Distance, Time};
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
use map_model::{IntersectionID, LaneID, TurnType};
|
||||
use sim::AgentID;
|
||||
use widgetry::{
|
||||
@ -12,7 +13,6 @@ use crate::app::{App, ShowEverything};
|
||||
use crate::common::{ColorLegend, CommonState};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
|
||||
/// Draws a preview of the path for the agent under the mouse cursor.
|
||||
pub struct RoutePreview {
|
||||
|
@ -2,6 +2,7 @@ use instant::Instant;
|
||||
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{Duration, Polygon, Pt2D, Ring, Time};
|
||||
use map_gui::render::DrawOptions;
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Choice, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome,
|
||||
Panel, Slider, State, Text, UpdateType, Widget,
|
||||
@ -12,7 +13,6 @@ use crate::common::Warping;
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::sandbox::{GameplayMode, SandboxMode};
|
||||
use map_gui::render::DrawOptions;
|
||||
|
||||
// TODO Text entry would be great
|
||||
pub struct JumpToTime {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use geom::ArrowCap;
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
use map_model::{IntersectionCluster, IntersectionID, PathConstraints};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
@ -13,7 +14,6 @@ use crate::common::CommonState;
|
||||
use crate::edit::ClusterTrafficSignalEditor;
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::ID;
|
||||
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
|
||||
pub struct UberTurnPicker {
|
||||
members: BTreeSet<IntersectionID>,
|
||||
|
@ -11,4 +11,4 @@ colorous = "1.0.3"
|
||||
geom = { path = "../geom" }
|
||||
map_model = { path = "../map_model" }
|
||||
sim = { path = "../sim" }
|
||||
widgetry = { path = "../widgetry", default-features=false }
|
||||
widgetry = { path = "../widgetry" }
|
||||
|
@ -1,6 +1,16 @@
|
||||
//! Several distinct tools/applications all share the same general structure for their shared GUI
|
||||
//! state, based around drawing and interacting with a Map.
|
||||
|
||||
use colors::{ColorScheme, ColorSchemeChoice};
|
||||
use map_model::Map;
|
||||
use options::Options;
|
||||
use render::DrawMap;
|
||||
use sim::Sim;
|
||||
use widgetry::{EventCtx, GfxCtx, SharedAppState};
|
||||
|
||||
use crate::helpers::ID;
|
||||
use crate::render::DrawOptions;
|
||||
|
||||
pub mod colors;
|
||||
pub mod common;
|
||||
pub mod helpers;
|
||||
@ -11,18 +21,144 @@ pub mod render;
|
||||
/// have any common widgetry States... although maybe we can instead organize the common state into
|
||||
/// a struct, and make the trait we pass around just be a getter/setter for this shared struct?
|
||||
pub trait AppLike {
|
||||
fn map(&self) -> &map_model::Map;
|
||||
fn sim(&self) -> &sim::Sim;
|
||||
fn cs(&self) -> &colors::ColorScheme;
|
||||
fn draw_map(&self) -> &render::DrawMap;
|
||||
fn mut_draw_map(&mut self) -> &mut render::DrawMap;
|
||||
fn opts(&self) -> &options::Options;
|
||||
fn mut_opts(&mut self) -> &mut options::Options;
|
||||
fn unzoomed_agents(&self) -> &render::UnzoomedAgents;
|
||||
fn map(&self) -> ⤅
|
||||
fn sim(&self) -> &Sim;
|
||||
fn cs(&self) -> &ColorScheme;
|
||||
fn mut_cs(&mut self) -> &mut ColorScheme;
|
||||
fn draw_map(&self) -> &DrawMap;
|
||||
fn mut_draw_map(&mut self) -> &mut DrawMap;
|
||||
fn opts(&self) -> &Options;
|
||||
fn mut_opts(&mut self) -> &mut Options;
|
||||
|
||||
/// Change the color scheme. Idempotent. Return true if there was a change.
|
||||
fn change_color_scheme(
|
||||
&mut self,
|
||||
ctx: &mut widgetry::EventCtx,
|
||||
cs: colors::ColorSchemeChoice,
|
||||
) -> bool;
|
||||
fn change_color_scheme(&mut self, ctx: &mut EventCtx, cs: ColorSchemeChoice) -> bool {
|
||||
if self.opts().color_scheme == cs {
|
||||
return false;
|
||||
}
|
||||
self.mut_opts().color_scheme = cs;
|
||||
*self.mut_cs() = ColorScheme::new(ctx, self.opts().color_scheme);
|
||||
|
||||
ctx.loading_screen("rerendering map colors", |ctx, timer| {
|
||||
*self.mut_draw_map() = DrawMap::new(self.map(), self.opts(), self.cs(), ctx, timer);
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple app state that just renders a map. Deliberately not sharing the more complicated
|
||||
/// implementation from the game crate; that handles way more stuff other apps don't need, like
|
||||
/// agents.
|
||||
pub struct SimpleApp {
|
||||
pub map: Map,
|
||||
pub draw_map: DrawMap,
|
||||
pub cs: ColorScheme,
|
||||
pub opts: Options,
|
||||
}
|
||||
|
||||
impl SimpleApp {
|
||||
pub fn new(ctx: &mut EventCtx) -> SimpleApp {
|
||||
ctx.loading_screen("load map", |ctx, mut timer| {
|
||||
let opts = Options::default();
|
||||
let cs = ColorScheme::new(ctx, opts.color_scheme);
|
||||
let map = Map::new(abstutil::MapName::seattle("montlake").path(), &mut timer);
|
||||
let draw_map = DrawMap::new(&map, &opts, &cs, ctx, timer);
|
||||
// TODO Should we refactor the whole camera state / initial focusing thing?
|
||||
SimpleApp {
|
||||
map,
|
||||
draw_map,
|
||||
cs,
|
||||
opts,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_unzoomed(&self, g: &mut GfxCtx) {
|
||||
g.redraw(&self.draw_map.draw_all_areas);
|
||||
g.redraw(&self.draw_map.draw_all_unzoomed_parking_lots);
|
||||
g.redraw(&self.draw_map.draw_all_unzoomed_roads_and_intersections);
|
||||
g.redraw(&self.draw_map.draw_all_buildings);
|
||||
// Not the building paths
|
||||
}
|
||||
|
||||
fn draw_zoomed(&self, g: &mut GfxCtx, opts: DrawOptions) {
|
||||
let objects = self
|
||||
.draw_map
|
||||
.get_renderables_back_to_front(g.get_screen_bounds(), &self.map);
|
||||
|
||||
let mut drawn_all_buildings = false;
|
||||
let mut drawn_all_areas = false;
|
||||
|
||||
for obj in objects {
|
||||
obj.draw(g, self, &opts);
|
||||
|
||||
match obj.get_id() {
|
||||
ID::Building(_) => {
|
||||
if !drawn_all_buildings {
|
||||
if opts.show_building_paths {
|
||||
g.redraw(&self.draw_map.draw_all_building_paths);
|
||||
}
|
||||
g.redraw(&self.draw_map.draw_all_buildings);
|
||||
g.redraw(&self.draw_map.draw_all_building_outlines);
|
||||
drawn_all_buildings = true;
|
||||
}
|
||||
}
|
||||
ID::Area(_) => {
|
||||
if !drawn_all_areas {
|
||||
g.redraw(&self.draw_map.draw_all_areas);
|
||||
drawn_all_areas = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppLike for SimpleApp {
|
||||
#[inline]
|
||||
fn map(&self) -> &Map {
|
||||
&self.map
|
||||
}
|
||||
#[inline]
|
||||
fn sim(&self) -> &Sim {
|
||||
unreachable!()
|
||||
}
|
||||
#[inline]
|
||||
fn cs(&self) -> &ColorScheme {
|
||||
&self.cs
|
||||
}
|
||||
#[inline]
|
||||
fn mut_cs(&mut self) -> &mut ColorScheme {
|
||||
&mut self.cs
|
||||
}
|
||||
#[inline]
|
||||
fn draw_map(&self) -> &DrawMap {
|
||||
&self.draw_map
|
||||
}
|
||||
#[inline]
|
||||
fn mut_draw_map(&mut self) -> &mut DrawMap {
|
||||
&mut self.draw_map
|
||||
}
|
||||
#[inline]
|
||||
fn opts(&self) -> &Options {
|
||||
&self.opts
|
||||
}
|
||||
#[inline]
|
||||
fn mut_opts(&mut self) -> &mut Options {
|
||||
&mut self.opts
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedAppState for SimpleApp {
|
||||
fn draw_default(&self, g: &mut GfxCtx) {
|
||||
g.clear(self.cs.void_background);
|
||||
g.redraw(&self.draw_map.boundary_polygon);
|
||||
|
||||
if g.canvas.cam_zoom < self.opts.min_zoom_for_detail {
|
||||
self.draw_unzoomed(g);
|
||||
} else {
|
||||
self.draw_zoomed(g, DrawOptions::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ use map_model::{Map, Traversable};
|
||||
use sim::{AgentID, Sim, UnzoomedAgent, VehicleType};
|
||||
use widgetry::{Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Panel, Prerender, Widget};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::colors::ColorScheme;
|
||||
use crate::render::{
|
||||
draw_vehicle, unzoomed_agent_radius, DrawPedCrowd, DrawPedestrian, Renderable,
|
||||
};
|
||||
use crate::AppLike;
|
||||
|
||||
pub struct AgentCache {
|
||||
/// This is controlled almost entirely by the minimap panel. It has no meaning in edit mode.
|
||||
@ -84,9 +84,9 @@ impl AgentCache {
|
||||
pub fn calculate_unzoomed_agents<P: AsRef<Prerender>>(
|
||||
&mut self,
|
||||
prerender: &mut P,
|
||||
app: &App,
|
||||
app: &dyn AppLike,
|
||||
) -> &QuadTree<AgentID> {
|
||||
let now = app.primary.sim.time();
|
||||
let now = app.sim().time();
|
||||
let mut recalc = true;
|
||||
if let Some((time, ref orig_agents, _, _)) = self.unzoomed {
|
||||
if now == time && self.unzoomed_agents == orig_agents.clone() {
|
||||
@ -96,7 +96,7 @@ impl AgentCache {
|
||||
|
||||
if recalc {
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut quadtree = QuadTree::default(app.primary.map.get_bounds().as_bbox());
|
||||
let mut quadtree = QuadTree::default(app.map().get_bounds().as_bbox());
|
||||
// It's quite silly to produce triangles for the same circle over and over again. ;)
|
||||
let car_circle = Circle::new(
|
||||
Pt2D::new(0.0, 0.0),
|
||||
@ -106,7 +106,7 @@ impl AgentCache {
|
||||
let ped_circle =
|
||||
Circle::new(Pt2D::new(0.0, 0.0), unzoomed_agent_radius(None)).to_polygon();
|
||||
|
||||
for agent in app.primary.sim.get_unzoomed_agents(&app.primary.map) {
|
||||
for agent in app.sim().get_unzoomed_agents(app.map()) {
|
||||
if let Some(color) = self.unzoomed_agents.color(&agent) {
|
||||
let circle = if agent.id.to_vehicle_type().is_some() {
|
||||
car_circle.translate(agent.pos.x(), agent.pos.y())
|
||||
@ -126,19 +126,19 @@ impl AgentCache {
|
||||
&self.unzoomed.as_ref().unwrap().2
|
||||
}
|
||||
|
||||
pub fn draw_unzoomed_agents(&mut self, g: &mut GfxCtx, app: &App) {
|
||||
pub fn draw_unzoomed_agents(&mut self, g: &mut GfxCtx, app: &dyn AppLike) {
|
||||
self.calculate_unzoomed_agents(g, app);
|
||||
g.redraw(&self.unzoomed.as_ref().unwrap().3);
|
||||
|
||||
if app.opts.debug_all_agents {
|
||||
if app.opts().debug_all_agents {
|
||||
let mut cnt = 0;
|
||||
for input in app.primary.sim.get_all_draw_cars(&app.primary.map) {
|
||||
for input in app.sim().get_all_draw_cars(app.map()) {
|
||||
cnt += 1;
|
||||
draw_vehicle(input, &app.primary.map, g.prerender, &app.cs);
|
||||
draw_vehicle(input, app.map(), g.prerender, app.cs());
|
||||
}
|
||||
println!(
|
||||
"At {}, debugged {} cars",
|
||||
app.primary.sim.time(),
|
||||
app.sim().time(),
|
||||
abstutil::prettyprint_usize(cnt)
|
||||
);
|
||||
// Pedestrians aren't the ones crashing
|
@ -313,7 +313,7 @@ impl DrawMap {
|
||||
agents.get(on).into_iter().find(|r| r.get_id() == id)
|
||||
}
|
||||
|
||||
// Unsorted, unexpanded, raw result.
|
||||
/// Unsorted, unexpanded, raw result.
|
||||
pub fn get_matching_objects(&self, bounds: Bounds) -> Vec<ID> {
|
||||
let mut results: Vec<ID> = Vec::new();
|
||||
for &(id, _, _) in &self.quadtree.query(bounds.as_bbox()) {
|
||||
@ -321,4 +321,58 @@ impl DrawMap {
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// A simple variation of the one in game that shows all layers, ignores dynamic agents.
|
||||
pub fn get_renderables_back_to_front(&self, bounds: Bounds, map: &Map) -> Vec<&dyn Renderable> {
|
||||
let mut areas: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut parking_lots: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut lanes: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut roads: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut intersections: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut buildings: Vec<&dyn Renderable> = Vec::new();
|
||||
let mut bus_stops: Vec<&dyn Renderable> = Vec::new();
|
||||
|
||||
for id in self.get_matching_objects(bounds) {
|
||||
match id {
|
||||
ID::Area(id) => areas.push(self.get_a(id)),
|
||||
ID::Lane(id) => {
|
||||
lanes.push(self.get_l(id));
|
||||
for bs in &map.get_l(id).bus_stops {
|
||||
bus_stops.push(self.get_bs(*bs));
|
||||
}
|
||||
}
|
||||
ID::Road(id) => {
|
||||
roads.push(self.get_r(id));
|
||||
}
|
||||
ID::Intersection(id) => {
|
||||
intersections.push(self.get_i(id));
|
||||
}
|
||||
ID::Building(id) => buildings.push(self.get_b(id)),
|
||||
ID::ParkingLot(id) => {
|
||||
parking_lots.push(self.get_pl(id));
|
||||
}
|
||||
|
||||
ID::BusStop(_) | ID::Car(_) | ID::Pedestrian(_) | ID::PedCrowd(_) => {
|
||||
panic!("{:?} shouldn't be in the quadtree", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From background to foreground Z-order
|
||||
let mut borrows: Vec<&dyn Renderable> = Vec::new();
|
||||
borrows.extend(areas);
|
||||
borrows.extend(parking_lots);
|
||||
borrows.extend(lanes);
|
||||
borrows.extend(roads);
|
||||
borrows.extend(intersections);
|
||||
borrows.extend(buildings);
|
||||
borrows.extend(bus_stops);
|
||||
|
||||
//borrows.retain(|x| x.get_zorder() <= self.primary.show_zorder);
|
||||
|
||||
// This is a stable sort.
|
||||
borrows.sort_by_key(|x| x.get_zorder());
|
||||
|
||||
borrows
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user