mvp of visualizing interactive sources/sinks

This commit is contained in:
Dustin Carlino 2019-11-08 13:16:22 -08:00
parent 8fa813f5c2
commit 965037ce77
8 changed files with 130 additions and 76 deletions

View File

@ -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));
}
}

View File

@ -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<IntersectionID>,
}
impl Freeform {
pub fn new(ctx: &EventCtx) -> (ModalMenu, Box<dyn GameplayState>) {
@ -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);
}
}
}
}

View File

@ -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<Transition>;
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);
}
}

View File

@ -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::<SandboxMode>().unwrap();
let freeform = sandbox.gameplay.state.downcast_mut::<Freeform>().unwrap();
freeform.spawn_pts.insert(src);
freeform.spawn_pts.insert(dst);
}));
}
self.menu.event(ctx);

View File

@ -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) {

View File

@ -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,

View File

@ -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<Vec<String>> {
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<Vec<String>> {
pub fn count_trips_involving_border(&self, i: IntersectionID) -> TripCount {
self.trips.count_trips_involving_border(i, self.time)
}

View File

@ -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<Vec<String>> {
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<Vec<String>> {
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<Vec<String>> {
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<T> TripResult<T> {
}
}
}
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<String> {
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
),
]
}
}