From 965037ce77e9dd358ba6ce22028c4f744ab1fcde Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 8 Nov 2019 13:16:22 -0800 Subject: [PATCH] mvp of visualizing interactive sources/sinks --- game/src/common/info.rs | 10 +- game/src/sandbox/gameplay/freeform.rs | 35 +++++- game/src/sandbox/gameplay/mod.rs | 5 +- game/src/sandbox/{ => gameplay}/spawner.rs | 20 ++-- game/src/sandbox/mod.rs | 3 +- sim/src/lib.rs | 2 +- sim/src/sim.rs | 10 +- sim/src/trips.rs | 121 ++++++++++++--------- 8 files changed, 130 insertions(+), 76 deletions(-) rename game/src/sandbox/{ => gameplay}/spawner.rs (96%) diff --git a/game/src/common/info.rs b/game/src/common/info.rs index eef4caf384..683e8caa01 100644 --- a/game/src/common/info.rs +++ b/game/src/common/info.rs @@ -139,9 +139,10 @@ fn info_for(id: ID, ui: &UI, ctx: &EventCtx) -> Text { txt.add(Line(format!("{} turning", accepted.len()))); } - if let Some(lines) = sim.count_trips_involving_border(id) { + let cnt = sim.count_trips_involving_border(id); + if cnt.nonzero() { txt.add(Line("")); - for line in lines { + for line in cnt.describe() { txt.add(Line(line)); } } @@ -170,9 +171,10 @@ fn info_for(id: ID, ui: &UI, ctx: &EventCtx) -> Text { txt.add(Line("")); } - if let Some(lines) = sim.count_trips_involving_bldg(id) { + let cnt = sim.count_trips_involving_bldg(id); + if cnt.nonzero() { txt.add(Line("")); - for line in lines { + for line in cnt.describe() { txt.add(Line(line)); } } diff --git a/game/src/sandbox/gameplay/freeform.rs b/game/src/sandbox/gameplay/freeform.rs index 2f52d2c950..c565628334 100644 --- a/game/src/sandbox/gameplay/freeform.rs +++ b/game/src/sandbox/gameplay/freeform.rs @@ -1,13 +1,18 @@ use crate::game::{msg, Transition, WizardState}; -use crate::sandbox::gameplay::{change_scenario, load_map, GameplayState}; +use crate::helpers::ID; +use crate::sandbox::gameplay::{change_scenario, load_map, spawner, GameplayState}; use crate::sandbox::overlays::Overlays; -use crate::sandbox::spawner; use crate::ui::UI; -use ezgui::{hotkey, lctrl, EventCtx, Key, ModalMenu}; +use ezgui::{hotkey, lctrl, Color, EventCtx, GfxCtx, Key, Line, ModalMenu, Text}; +use map_model::IntersectionID; use sim::Analytics; +use std::collections::BTreeSet; // TODO Maybe remember what things were spawned, offer to replay this later -pub struct Freeform; +pub struct Freeform { + // TODO Clean these up later when done? + pub spawn_pts: BTreeSet, +} impl Freeform { pub fn new(ctx: &EventCtx) -> (ModalMenu, Box) { @@ -21,7 +26,9 @@ impl Freeform { ], ctx, ), - Box::new(Freeform), + Box::new(Freeform { + spawn_pts: BTreeSet::new(), + }), ) } } @@ -55,4 +62,22 @@ impl GameplayState for Freeform { } None } + + fn draw(&self, g: &mut GfxCtx, ui: &UI) { + // TODO Overriding draw options would be ideal, but... + for i in &self.spawn_pts { + g.draw_polygon(Color::GREEN.alpha(0.8), &ui.primary.map.get_i(*i).polygon); + } + + if let Some(ID::Intersection(i)) = ui.primary.current_selection { + if self.spawn_pts.contains(&i) { + let cnt = ui.primary.sim.count_trips_involving_border(i); + let mut txt = Text::new(); + for line in cnt.describe() { + txt.add(Line(line)); + } + g.draw_mouse_tooltip(&txt); + } + } + } } diff --git a/game/src/sandbox/gameplay/mod.rs b/game/src/sandbox/gameplay/mod.rs index 14d9133352..33b3ffc313 100644 --- a/game/src/sandbox/gameplay/mod.rs +++ b/game/src/sandbox/gameplay/mod.rs @@ -3,6 +3,7 @@ mod faster_trips; mod freeform; mod optimize_bus; mod play_scenario; +mod spawner; use crate::game::Transition; use crate::render::AgentColorScheme; @@ -42,6 +43,7 @@ pub trait GameplayState: downcast_rs::Downcast { menu: &mut ModalMenu, analytics: &Analytics, ) -> Option; + fn draw(&self, _: &mut GfxCtx, _: &UI) {} } downcast_rs::impl_downcast!(GameplayState); @@ -133,8 +135,9 @@ impl GameplayRunner { .event(ctx, ui, overlays, &mut self.menu, &self.prebaked) } - pub fn draw(&self, g: &mut GfxCtx) { + pub fn draw(&self, g: &mut GfxCtx, ui: &UI) { self.menu.draw(g); + self.state.draw(g, ui); } } diff --git a/game/src/sandbox/spawner.rs b/game/src/sandbox/gameplay/spawner.rs similarity index 96% rename from game/src/sandbox/spawner.rs rename to game/src/sandbox/gameplay/spawner.rs index 1d45681483..638029cb93 100644 --- a/game/src/sandbox/spawner.rs +++ b/game/src/sandbox/gameplay/spawner.rs @@ -2,6 +2,8 @@ use crate::common::CommonState; use crate::game::{msg, State, Transition, WizardState}; use crate::helpers::ID; use crate::render::DrawOptions; +use crate::sandbox::gameplay::freeform::Freeform; +use crate::sandbox::SandboxMode; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; use ezgui::{hotkey, EventCtx, GfxCtx, Key, ModalMenu}; @@ -463,14 +465,16 @@ impl State for SpawnManyAgents { // PopWithData is a weird pattern; we should have a resume() handler that handles the // context if let Some((count, duration)) = self.schedule { - create_swarm( - ui, - self.from, - self.maybe_goal.take().unwrap().0, - count, - duration, - ); - return Transition::Pop; + let dst_l = self.maybe_goal.take().unwrap().0; + create_swarm(ui, self.from, dst_l, count, duration); + let src = ui.primary.map.get_l(self.from).src_i; + let dst = ui.primary.map.get_l(dst_l).dst_i; + return Transition::PopWithData(Box::new(move |state, _, _| { + let sandbox = state.downcast_mut::().unwrap(); + let freeform = sandbox.gameplay.state.downcast_mut::().unwrap(); + freeform.spawn_pts.insert(src); + freeform.spawn_pts.insert(dst); + })); } self.menu.event(ctx); diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index 7e7310b036..dbefb46b80 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -2,7 +2,6 @@ mod bus_explorer; mod gameplay; mod overlays; mod score; -mod spawner; use crate::common::{time_controls, AgentTools, CommonState, SpeedControls}; use crate::debug::DebugMode; @@ -322,7 +321,7 @@ impl State for SandboxMode { self.info_tools.draw(g); self.general_tools.draw(g); self.save_tools.draw(g); - self.gameplay.draw(g); + self.gameplay.draw(g, ui); } fn on_suspend(&mut self, _: &mut EventCtx, _: &mut UI) { diff --git a/sim/src/lib.rs b/sim/src/lib.rs index b343b2edd4..a8346e8152 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -22,8 +22,8 @@ pub(crate) use self::router::{ActionAtEnd, Router}; pub(crate) use self::scheduler::{Command, Scheduler}; pub use self::sim::{Sim, SimOptions}; pub(crate) use self::transit::TransitSimState; -pub use self::trips::TripResult; pub use self::trips::{FinishedTrips, TripEnd, TripMode, TripStart, TripStatus}; +pub use self::trips::{TripCount, TripResult}; pub(crate) use self::trips::{TripLeg, TripManager}; pub use crate::render::{ AgentMetadata, CarStatus, DontDrawAgents, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, diff --git a/sim/src/sim.rs b/sim/src/sim.rs index 18ff0f6240..0a4a3dfd2a 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -2,9 +2,9 @@ use crate::{ AgentID, AgentMetadata, Analytics, CarID, Command, CreateCar, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, DrivingGoal, DrivingSimState, Event, FinishedTrips, GetDrawAgents, IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, - SidewalkPOI, SidewalkSpot, TransitSimState, TripID, TripLeg, TripManager, TripPositions, - TripResult, TripSpawner, TripSpec, TripStart, TripStatus, UnzoomedAgent, VehicleSpec, - VehicleType, WalkingSimState, BUS_LENGTH, + SidewalkPOI, SidewalkSpot, TransitSimState, TripCount, TripID, TripLeg, TripManager, + TripPositions, TripResult, TripSpawner, TripSpec, TripStart, TripStatus, UnzoomedAgent, + VehicleSpec, VehicleType, WalkingSimState, BUS_LENGTH, }; use abstutil::{elapsed_seconds, Timer}; use derivative::Derivative; @@ -759,10 +759,10 @@ impl Sim { self.trips.get_finished_trips() } - pub fn count_trips_involving_bldg(&self, b: BuildingID) -> Option> { + pub fn count_trips_involving_bldg(&self, b: BuildingID) -> TripCount { self.trips.count_trips_involving_bldg(b, self.time) } - pub fn count_trips_involving_border(&self, i: IntersectionID) -> Option> { + pub fn count_trips_involving_border(&self, i: IntersectionID) -> TripCount { self.trips.count_trips_involving_border(i, self.time) } diff --git a/sim/src/trips.rs b/sim/src/trips.rs index 1f8b1cb523..a3431c5200 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -530,74 +530,49 @@ impl TripManager { } // TODO Refactor after wrangling the TripStart/TripEnd mess - pub fn count_trips_involving_bldg(&self, b: BuildingID, now: Duration) -> Option> { + pub fn count_trips_involving_bldg(&self, b: BuildingID, now: Duration) -> TripCount { self.count_trips(TripStart::Bldg(b), TripEnd::Bldg(b), now) } - pub fn count_trips_involving_border( - &self, - i: IntersectionID, - now: Duration, - ) -> Option> { + pub fn count_trips_involving_border(&self, i: IntersectionID, now: Duration) -> TripCount { self.count_trips(TripStart::Border(i), TripEnd::Border(i), now) } - fn count_trips(&self, start: TripStart, end: TripEnd, now: Duration) -> Option> { - let mut from_aborted = 0; - let mut from_in_progress = 0; - let mut from_completed = 0; - let mut from_unstarted = 0; - let mut to_aborted = 0; - let mut to_in_progress = 0; - let mut to_completed = 0; - let mut to_unstarted = 0; - - let mut any = false; + fn count_trips(&self, start: TripStart, end: TripEnd, now: Duration) -> TripCount { + let mut cnt = TripCount { + from_aborted: 0, + from_in_progress: 0, + from_completed: 0, + from_unstarted: 0, + to_aborted: 0, + to_in_progress: 0, + to_completed: 0, + to_unstarted: 0, + }; for trip in &self.trips { if trip.start == start { - any = true; if trip.aborted { - from_aborted += 1; + cnt.from_aborted += 1; } else if trip.finished_at.is_some() { - from_completed += 1; + cnt.from_completed += 1; } else if now >= trip.spawned_at { - from_in_progress += 1; + cnt.from_in_progress += 1; } else { - from_unstarted += 1; + cnt.from_unstarted += 1; } - } else if trip.end == end { - any = true; + } + // One trip might could towards both! + if trip.end == end { if trip.aborted { - to_aborted += 1; + cnt.to_aborted += 1; } else if trip.finished_at.is_some() { - to_completed += 1; + cnt.to_completed += 1; } else if now >= trip.spawned_at { - to_in_progress += 1; + cnt.to_in_progress += 1; } else { - to_unstarted += 1; + cnt.to_unstarted += 1; } } } - if !any { - return None; - } - - Some(vec![ - format!( - "Aborted trips: {} from here, {} to here", - from_aborted, to_aborted - ), - format!( - "Finished trips: {} from here, {} to here", - from_completed, to_completed - ), - format!( - "In-progress trips: {} from here, {} to here", - from_in_progress, to_in_progress - ), - format!( - "Future trips: {} from here, {} to here", - from_unstarted, to_unstarted - ), - ]) + cnt } } @@ -781,3 +756,49 @@ impl TripResult { } } } + +pub struct TripCount { + pub from_aborted: usize, + pub from_in_progress: usize, + pub from_completed: usize, + pub from_unstarted: usize, + pub to_aborted: usize, + pub to_in_progress: usize, + pub to_completed: usize, + pub to_unstarted: usize, +} + +impl TripCount { + pub fn nonzero(&self) -> bool { + self.from_aborted + + self.from_in_progress + + self.from_completed + + self.from_unstarted + + self.to_aborted + + self.to_in_progress + + self.to_completed + + self.to_unstarted + > 0 + } + + pub fn describe(&self) -> Vec { + vec![ + format!( + "Aborted trips: {} from here, {} to here", + self.from_aborted, self.to_aborted + ), + format!( + "Finished trips: {} from here, {} to here", + self.from_completed, self.to_completed + ), + format!( + "In-progress trips: {} from here, {} to here", + self.from_in_progress, self.to_in_progress + ), + format!( + "Future trips: {} from here, {} to here", + self.from_unstarted, self.to_unstarted + ), + ] + } +}