Skip to content

Commit

Permalink
Render A/B Street's lanes and traffic simulation on top of Mapbox GL (#…
Browse files Browse the repository at this point in the history
…788)

[rebuild] [release]
  • Loading branch information
dabreegster authored Oct 31, 2021
1 parent 04b54b0 commit 00df96f
Show file tree
Hide file tree
Showing 21 changed files with 751 additions and 91 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"map_model",
"osm_viewer",
"parking_mapper",
"piggyback",
"popdat",
"santa",
"sim",
Expand Down
16 changes: 10 additions & 6 deletions game/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,13 @@ impl App {
}

let mut cache = self.primary.agents.borrow_mut();
cache.draw_unzoomed_agents(g, self);
cache.draw_unzoomed_agents(
g,
&self.primary.map,
&self.primary.sim,
&self.cs,
&self.opts,
);

if let Some(a) = self
.primary
Expand Down Expand Up @@ -419,10 +425,8 @@ impl App {
borrows.extend(bus_stops);

// Expand all of the Traversables into agents, populating the cache if needed.
{
for on in &agents_on {
agents.populate_if_needed(*on, map, &self.primary.sim, &self.cs, prerender);
}
for on in &agents_on {
agents.populate_if_needed(*on, map, &self.primary.sim, &self.cs, prerender);
}

for on in agents_on {
Expand Down Expand Up @@ -666,7 +670,7 @@ impl PerMap {
map,
draw_map,
sim,
agents: RefCell::new(AgentCache::new_state()),
agents: RefCell::new(AgentCache::new()),
current_selection: None,
current_flags: flags,
last_warped_from: None,
Expand Down
2 changes: 1 addition & 1 deletion game/src/sandbox/minimap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl MinimapControls<App> for MinimapController {
}

let mut cache = app.primary.agents.borrow_mut();
cache.draw_unzoomed_agents(g, app);
cache.draw_unzoomed_agents(g, &app.primary.map, &app.primary.sim, &app.cs, &app.opts);
}

fn make_unzoomed_panel(&self, ctx: &mut EventCtx, app: &App) -> Panel {
Expand Down
2 changes: 1 addition & 1 deletion game/src/sandbox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ fn mouseover_unzoomed_agent_circle(ctx: &mut EventCtx, app: &mut App) {
.primary
.agents
.borrow_mut()
.calculate_unzoomed_agents(ctx, app)
.calculate_unzoomed_agents(ctx, &app.primary.map, &app.primary.sim, &app.cs)
.query(
Circle::new(cursor, Distance::meters(3.0))
.get_bounds()
Expand Down
5 changes: 5 additions & 0 deletions geom/src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ impl Duration {
Duration::seconds(mins * 60.0)
}

/// Creates a duration in milliseconds.
pub fn milliseconds(value: f64) -> Duration {
Duration::seconds(value / 1000.0)
}

pub const fn const_seconds(value: f64) -> Duration {
Duration(value)
}
Expand Down
37 changes: 23 additions & 14 deletions map_gui/src/render/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use sim::{AgentID, Sim, UnzoomedAgent, VehicleType};
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Panel, Prerender};

use crate::colors::ColorScheme;
use crate::options::Options;
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.
Expand All @@ -30,7 +30,7 @@ pub struct AgentCache {
}

impl AgentCache {
pub fn new_state() -> AgentCache {
pub fn new() -> AgentCache {
AgentCache {
unzoomed_agents: UnzoomedAgents::new(),
time: None,
Expand Down Expand Up @@ -87,9 +87,11 @@ impl AgentCache {
pub fn calculate_unzoomed_agents<P: AsRef<Prerender>>(
&mut self,
prerender: &mut P,
app: &dyn AppLike,
map: &Map,
sim: &Sim,
cs: &ColorScheme,
) -> &QuadTree<AgentID> {
let now = app.sim().time();
let now = sim.time();
let mut recalc = true;
if let Some((time, ref orig_agents, _, _)) = self.unzoomed {
if now == time && self.unzoomed_agents == orig_agents.clone() {
Expand All @@ -98,10 +100,10 @@ impl AgentCache {
}

if recalc {
let highlighted = app.sim().get_highlighted_people();
let highlighted = sim.get_highlighted_people();

let mut batch = GeomBatch::new();
let mut quadtree = QuadTree::default(app.map().get_bounds().as_bbox());
let mut quadtree = QuadTree::default(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),
Expand All @@ -111,8 +113,8 @@ impl AgentCache {
let ped_circle =
Circle::new(Pt2D::new(0.0, 0.0), unzoomed_agent_radius(None)).to_polygon();

for agent in app.sim().get_unzoomed_agents(app.map()) {
if let Some(mut color) = self.unzoomed_agents.color(&agent, app.cs()) {
for agent in sim.get_unzoomed_agents(map) {
if let Some(mut color) = self.unzoomed_agents.color(&agent, cs) {
// If the sim has highlighted people, then fade all others out.
if highlighted
.as_ref()
Expand Down Expand Up @@ -141,19 +143,26 @@ impl AgentCache {
&self.unzoomed.as_ref().unwrap().2
}

pub fn draw_unzoomed_agents(&mut self, g: &mut GfxCtx, app: &dyn AppLike) {
self.calculate_unzoomed_agents(g, app);
pub fn draw_unzoomed_agents(
&mut self,
g: &mut GfxCtx,
map: &Map,
sim: &Sim,
cs: &ColorScheme,
opts: &Options,
) {
self.calculate_unzoomed_agents(g, map, sim, cs);
g.redraw(&self.unzoomed.as_ref().unwrap().3);

if app.opts().debug_all_agents {
if opts.debug_all_agents {
let mut cnt = 0;
for input in app.sim().get_all_draw_cars(app.map()) {
for input in sim.get_all_draw_cars(map) {
cnt += 1;
draw_vehicle(input, app.map(), app.sim(), g.prerender, app.cs());
draw_vehicle(input, map, sim, g.prerender, cs);
}
println!(
"At {}, debugged {} cars",
app.sim().time(),
sim.time(),
abstutil::prettyprint_usize(cnt)
);
// Pedestrians aren't the ones crashing
Expand Down
2 changes: 1 addition & 1 deletion osm_viewer/src/viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl State<App> for Viewer {
// get_obj must succeed, because we can only click static map elements.
let outline = app
.draw_map
.get_obj(ctx, id, app, &mut map_gui::render::AgentCache::new_state())
.get_obj(ctx, id, app, &mut map_gui::render::AgentCache::new())
.unwrap()
.get_outline(&app.map);
let mut batch = GeomBatch::from(vec![(app.cs.perma_selected_object, outline)]);
Expand Down
25 changes: 25 additions & 0 deletions piggyback/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "piggyback"
version = "0.1.0"
authors = ["Dustin Carlino <[email protected]>"]
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[features]
wasm = ["getrandom/js", "js-sys", "map_gui/wasm", "wasm-bindgen", "web-sys", "widgetry/wasm-backend"]

[dependencies]
abstio = { path = "../abstio" }
abstutil = { path = "../abstutil" }
geom = { path = "../geom" }
getrandom = { version = "0.2.3", optional = true }
js-sys = { version = "0.3.51", optional = true }
log = "0.4.14"
map_gui= { path = "../map_gui" }
map_model = { path = "../map_model" }
sim = { path = "../sim" }
wasm-bindgen = { version = "0.2.70", optional = true }
web-sys = { version = "0.3.47", optional = true }
widgetry = { path = "../widgetry" }
43 changes: 43 additions & 0 deletions piggyback/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# A/B Street + Mapbox demo

This is an example of integrating parts of A/B Street with Mapbox GL. It's a
normal web app using Mapbox, but it includes a layer rendering streets and
moving agents from A/B Street.

The goal is to increase interoperability and meet developers where they're at.
Parts of the A/B Street code-base are intended as
[a platform](https://a-b-street.github.io/docs/tech/map/platform.html) to build
other transportation-related things, using unique features like the detailed
street rendering. But Rust and our unusual UI library are a huge barrier.
Treating A/B Street as a layer that can be added to Mapbox and as a library with
a simple API for controlling a traffic simulation should be an easier start.

Another goal is to take advantage of all the great stuff that exists in the web
ecosystem. Instead of implementing satellite layers, multi-line text entry
(seriously!), and story mapping ourselves, we can just use stuff that's built
aready.

## How to run

You'll need `wasm-pack` and `python3` setup. You'll also need the `data/system/`
directory to contain some maps.

Quick development:
`wasm-pack build --dev --target web -- --features wasm && ./serve_locally.py`

To build the WASM in release mode:
`wasm-pack build --release --target web -- --features wasm && ./serve_locally.py`

Maps can be specified by URL:

- http://localhost:8000/?map=/data/system/us/seattle/maps/arboretum.bin

No deployment instructions yet.

## How it works

The `PiggybackDemo` struct is a thin layer written in Rust to hook up to the
rest of the A/B Street codebase. After beng initialized with a WebGL context and
a map file, it can render streets and agents and control a traffic simulation.
It serves as a public API, exposed via WASM. Then a regular Mapbox GL app treats
it as a library and adds a custom WebGL rendering layer that calls this API.
22 changes: 22 additions & 0 deletions piggyback/bundle_static_files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# This creates a .zip with all of the files needed to serve a copy of the Mapbox demo.

set -x
set -e

wasm-pack build --release --target web -- --features wasm

mkdir mapbox_demo
cp -Rv index.html serve_locally.py pkg mapbox_demo

mkdir -p mapbox_demo/data/system/us/seattle/maps
mkdir -p mapbox_demo/data/system/de/berlin/maps
# Just include a few maps
cp ../data/system/us/seattle/maps/montlake.bin mapbox_demo/data/system/us/seattle/maps
cp ../data/system/de/berlin/maps/neukolln.bin mapbox_demo/data/system/de/berlin/maps

# Uncomment with caution!
# Note this embeds a tiny slice of the data/ directory underneath mapbox_demo.
# The S3 bucket has gzipped map files, but the JS / Rust layers don't handle
# reading both yet.
#aws s3 sync mapbox_demo s3://abstreet/dev/mapbox_demo
1 change: 1 addition & 0 deletions piggyback/data
Loading

0 comments on commit 00df96f

Please sign in to comment.