Skip to content

Commit

Permalink
Store Movements per Intersection, instead of constantly calculating t…
Browse files Browse the repository at this point in the history
…hem! #746

Regenerating everything...
  • Loading branch information
dabreegster committed Sep 6, 2021
1 parent 7f86fe5 commit 5efd3a9
Show file tree
Hide file tree
Showing 28 changed files with 544 additions and 539 deletions.
8 changes: 5 additions & 3 deletions game/src/edit/traffic_signals/edits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl ChangeDuration {
signal: &ControlTrafficSignal,
idx: usize,
) -> Box<dyn State<App>> {
let i = app.primary.map.get_i(signal.id);
let panel = Panel::new_builder(Widget::col(vec![
Widget::row(vec![
Line("How long should this stage last?")
Expand All @@ -36,7 +37,7 @@ impl ChangeDuration {
Spinner::widget(
ctx,
"duration",
(signal.get_min_crossing_time(idx), Duration::minutes(5)),
(signal.get_min_crossing_time(idx, i), Duration::minutes(5)),
signal.stages[idx].stage_type.simple_duration(),
Duration::seconds(1.0),
),
Expand Down Expand Up @@ -178,7 +179,8 @@ pub fn edit_entire_signal(
let has_sidewalks = app
.primary
.map
.get_turns_in_intersection(i)
.get_i(i)
.turns
.iter()
.any(|t| t.between_sidewalks());

Expand Down Expand Up @@ -241,7 +243,7 @@ pub fn edit_entire_signal(
Transition::Pop,
Transition::ModifyState(Box::new(move |state, ctx, app| {
let mut new_signal = app.primary.map.get_traffic_signal(i).clone();
if new_signal.convert_to_ped_scramble() {
if new_signal.convert_to_ped_scramble(app.primary.map.get_i(i)) {
let editor = state.downcast_mut::<TrafficSignalEditor>().unwrap();
editor.add_new_edit(ctx, app, 0, |ts| {
*ts = new_signal.clone();
Expand Down
25 changes: 12 additions & 13 deletions game/src/edit/traffic_signals/gmns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub fn import_all(ctx: &mut EventCtx, app: &mut App, path: &str) -> Box<dyn Stat
for i in all_signals {
timer.next();
match import(&app.primary.map, i, path)
.and_then(|signal| signal.validate().map(|_| signal))
.and_then(|signal| signal.validate(app.primary.map.get_i(i)).map(|_| signal))
{
Ok(signal) => {
info!("Success at {}", i);
Expand Down Expand Up @@ -251,10 +251,12 @@ impl Snapper {
Ok(Snapper {
roads_incoming,
roads_outgoing,
movements: ControlTrafficSignal::new(map, i)
movements: map
.get_i(i)
.movements
.into_iter()
.iter()
.filter(|(id, _)| !id.crosswalk)
.map(|(k, v)| (*k, v.clone()))
.collect(),
})
}
Expand Down Expand Up @@ -340,33 +342,32 @@ fn add_crosswalks(signal: &mut ControlTrafficSignal, map: &Map) {
TurnType::Left
};

let i = map.get_i(signal.id);
let mut crosswalks: Vec<MovementID> = Vec::new();
for id in signal.movements.keys() {
for id in i.movements.keys() {
if id.crosswalk {
crosswalks.push(*id);
}
}
// Temporary for the borrow checker
let movements = std::mem::take(&mut signal.movements);

// We could try to look for straight turns parallel to the crosswalk, but... just brute-force
// it
for stage in &mut signal.stages {
crosswalks.retain(|id| {
if stage.could_be_protected(*id, &movements) {
stage.edit_movement(&movements[id], TurnPriority::Protected);
if stage.could_be_protected(*id, i) {
stage.edit_movement(&i.movements[id], TurnPriority::Protected);
false
} else {
// There may be conflicting right turns that we can downgrade. Try that.
let mut stage_copy = stage.clone();
for maybe_right_turn in stage.protected_movements.clone() {
if movements[&maybe_right_turn].turn_type == downgrade_type {
if i.movements[&maybe_right_turn].turn_type == downgrade_type {
stage.protected_movements.remove(&maybe_right_turn);
stage.yield_movements.insert(maybe_right_turn);
}
}
if stage_copy.could_be_protected(*id, &movements) {
stage_copy.edit_movement(&movements[id], TurnPriority::Protected);
if stage_copy.could_be_protected(*id, i) {
stage_copy.edit_movement(&i.movements[id], TurnPriority::Protected);
*stage = stage_copy;
false
} else {
Expand All @@ -375,6 +376,4 @@ fn add_crosswalks(signal: &mut ControlTrafficSignal, map: &Map) {
}
});
}

signal.movements = movements;
}
22 changes: 15 additions & 7 deletions game/src/edit/traffic_signals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ impl TrafficSignalEditor {
movements.push(m);
}
traffic_signal::draw_stage_number(
app,
ctx.prerender,
*i,
app.primary.map.get_i(*i),
self.current_stage,
&mut batch,
);
Expand All @@ -181,7 +180,10 @@ impl TrafficSignalEditor {
// We may have imported the signal configuration without validating it.
fn validate_all_members(&self, app: &App) -> Result<()> {
for i in &self.members {
app.primary.map.get_traffic_signal(*i).validate()?;
app.primary
.map
.get_traffic_signal(*i)
.validate(app.primary.map.get_i(*i))?;
}
Ok(())
}
Expand Down Expand Up @@ -403,11 +405,12 @@ impl State<App> for TrafficSignalEditor {
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
for m in &self.movements {
let signal = app.primary.map.get_traffic_signal(m.id.parent);
let i = app.primary.map.get_i(signal.id);
if m.hitbox.contains_pt(pt) {
let stage = &signal.stages[self.current_stage];
let next_priority = match stage.get_priority_of_movement(m.id) {
TurnPriority::Banned => {
if stage.could_be_protected(m.id, &signal.movements) {
if stage.could_be_protected(m.id, i) {
Some(TurnPriority::Protected)
} else if m.id.crosswalk {
None
Expand Down Expand Up @@ -468,10 +471,10 @@ impl State<App> for TrafficSignalEditor {
),
) {
let idx = self.current_stage;
let signal = signal.clone();
let movement = app.primary.map.get_i(id.parent).movements[&id].clone();
self.add_new_edit(ctx, app, idx, |ts| {
if ts.id == id.parent {
ts.stages[idx].edit_movement(&signal.movements[&id], pri);
ts.stages[idx].edit_movement(&movement, pri);
}
});
return Transition::KeepWithMouseover;
Expand Down Expand Up @@ -833,7 +836,12 @@ impl BundleEdits {
fn check_for_missing_turns(app: &App, members: &BTreeSet<IntersectionID>) -> Option<BundleEdits> {
let mut all_missing = BTreeSet::new();
for i in members {
all_missing.extend(app.primary.map.get_traffic_signal(*i).missing_turns());
all_missing.extend(
app.primary
.map
.get_traffic_signal(*i)
.missing_turns(app.primary.map.get_i(*i)),
);
}
if all_missing.is_empty() {
return None;
Expand Down
2 changes: 1 addition & 1 deletion game/src/info/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ fn current_demand_body(ctx: &mut EventCtx, app: &App, id: IntersectionID) -> Wid
let mut rows = vec![];
let mut total_demand = 0;
let mut demand_per_movement: Vec<(&PolyLine, usize)> = Vec::new();
for m in app.primary.map.get_traffic_signal(id).movements.values() {
for m in app.primary.map.get_i(id).movements.values() {
let demand = app
.primary
.sim
Expand Down
2 changes: 1 addition & 1 deletion game/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ fn setup_app(ctx: &mut EventCtx, mut setup: Setup) -> (App, Vec<Box<dyn State<Ap
// Use this low-level API, since the secondary map file probably isn't in the usual
// directory structure
let mut map: Map = abstio::read_binary(path.clone(), &mut timer);
map.map_loaded_directly();
map.map_loaded_directly(&mut timer);
let sim = Sim::new(&map, setup.flags.sim_flags.opts.clone());
let mut per_map = crate::app::PerMap::map_loaded(
map,
Expand Down
17 changes: 9 additions & 8 deletions game/src/sandbox/dashboards/traffic_signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use abstutil::{prettyprint_usize, Counter, Timer};
use geom::{ArrowCap, Distance, Duration, Polygon, Time};
use map_gui::render::DrawOptions;
use map_gui::ID;
use map_model::{ControlTrafficSignal, IntersectionID, MovementID, PathStep, TurnType};
use map_model::{Intersection, IntersectionID, MovementID, PathStep, TurnType};
use sim::TripEndpoint;
use widgetry::{
Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
Expand Down Expand Up @@ -75,10 +75,11 @@ impl State<App> for TrafficSignalDemand {
self.selected = None;
app.recalculate_current_selection(ctx);
if let Some(ID::Intersection(i)) = app.primary.current_selection.take() {
if let Some(ts) = app.primary.map.maybe_get_traffic_signal(i) {
let i = app.primary.map.get_i(i);
if i.is_traffic_signal() {
// If we're mousing over something, the cursor is on the map.
let pt = ctx.canvas.get_cursor_in_map_space().unwrap();
for (arrow, count) in self.all_demand[&i].make_arrows(ts, self.hour) {
for (arrow, count) in self.all_demand[&i.id].make_arrows(i, self.hour) {
if arrow.contains_pt(pt) {
let mut batch = GeomBatch::new();
batch.push(Color::hex("#EE702E"), arrow.clone());
Expand All @@ -88,7 +89,7 @@ impl State<App> for TrafficSignalDemand {
let txt = Text::from(format!(
"{} / {}",
prettyprint_usize(count),
self.all_demand[&i].count(self.hour).sum()
self.all_demand[&i.id].count(self.hour).sum()
));
self.selected = Some((ctx.upload(batch), txt));
break;
Expand Down Expand Up @@ -198,7 +199,7 @@ impl Demand {
if let Some(demand) = all_demand.get_mut(&t.parent) {
demand
.raw
.push((now, map.get_traffic_signal(t.parent).turn_to_movement(*t)));
.push((now, map.get_i(t.parent).turn_to_movement(*t).0));
}
}
}
Expand All @@ -219,14 +220,14 @@ impl Demand {
cnt
}

fn make_arrows(&self, ts: &ControlTrafficSignal, hour: Time) -> Vec<(Polygon, usize)> {
fn make_arrows(&self, i: &Intersection, hour: Time) -> Vec<(Polygon, usize)> {
let cnt = self.count(hour);
let total_demand = cnt.sum() as f64;

let mut arrows = Vec::new();
for (m, demand) in cnt.consume() {
let percent = (demand as f64) / total_demand;
let arrow = ts.movements[&m]
let arrow = i.movements[&m]
.geom
.make_arrow(percent * Distance::meters(3.0), ArrowCap::Triangle);
arrows.push((arrow, demand));
Expand All @@ -243,7 +244,7 @@ impl Demand {
let mut batch = GeomBatch::new();
for (i, demand) in all_demand {
let mut outlines = Vec::new();
for (arrow, _) in demand.make_arrows(app.primary.map.get_traffic_signal(*i), hour) {
for (arrow, _) in demand.make_arrows(app.primary.map.get_i(*i), hour) {
if let Ok(p) = arrow.to_outline(Distance::meters(0.1)) {
outlines.push(p);
}
Expand Down
30 changes: 13 additions & 17 deletions headless/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,23 +208,21 @@ fn handle_command(
Ok(format!("{} has been updated", id))
}
"/traffic-signals/get-delays" => {
let i = IntersectionID(get("id")?.parse::<usize>()?);
let i = map.get_i(IntersectionID(get("id")?.parse::<usize>()?));
let t1 = Time::parse(get("t1")?)?;
let t2 = Time::parse(get("t2")?)?;
let ts = if let Some(ts) = map.maybe_get_traffic_signal(i) {
ts
} else {
bail!("{} isn't a traffic signal", i);
};
let movements: Vec<&MovementID> = ts.movements.keys().collect();
if !i.is_traffic_signal() {
bail!("{} isn't a traffic signal", i.id);
}
let movements: Vec<&MovementID> = i.movements.keys().collect();

let mut delays = Delays {
per_direction: BTreeMap::new(),
};
for m in ts.movements.keys() {
for m in i.movements.keys() {
delays.per_direction.insert(*m, Vec::new());
}
if let Some(list) = sim.get_analytics().intersection_delays.get(&i) {
if let Some(list) = sim.get_analytics().intersection_delays.get(&i.id) {
for (idx, t, dt, _) in list {
if *t >= t1 && *t <= t2 {
delays
Expand All @@ -238,23 +236,21 @@ fn handle_command(
Ok(abstutil::to_json(&delays))
}
"/traffic-signals/get-cumulative-thruput" => {
let i = IntersectionID(get("id")?.parse::<usize>()?);
let ts = if let Some(ts) = map.maybe_get_traffic_signal(i) {
ts
} else {
bail!("{} isn't a traffic signal", i);
};
let i = map.get_i(IntersectionID(get("id")?.parse::<usize>()?));
if !i.is_traffic_signal() {
bail!("{} isn't a traffic signal", i.id);
}

let mut thruput = Throughput {
per_direction: BTreeMap::new(),
};
for (idx, m) in ts.movements.keys().enumerate() {
for (idx, m) in i.movements.keys().enumerate() {
thruput.per_direction.insert(
*m,
sim.get_analytics()
.traffic_signal_thruput
.total_for(CompressedMovementID {
i,
i: i.id,
idx: u8::try_from(idx).unwrap(),
}),
);
Expand Down
2 changes: 1 addition & 1 deletion importer/src/bin/json_to_binary_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fn main() {
let mut args = CmdArgs::new();
// TODO This can't handle the output of dump_map! What?!
let mut map: Map = abstio::read_json(args.required("--input"), &mut Timer::throwaway());
map.map_loaded_directly();
map.map_loaded_directly(&mut Timer::throwaway());
abstio::write_binary(args.required("--output"), &map);
args.done();
}
2 changes: 1 addition & 1 deletion map_gui/src/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl MapLoader {
match map {
Ok(mut map) => {
// Kind of a hack. We can't generically call Map::new with the FileLoader.
map.map_loaded_directly();
map.map_loaded_directly(timer);

app.map_switched(ctx, map, timer);

Expand Down
6 changes: 3 additions & 3 deletions map_gui/src/render/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl DrawIntersection {
default_geom.extend(app.cs().curb(rank), calculate_corner_curbs(i, map));
}

for turn in map.get_turns_in_intersection(i.id) {
for turn in &i.turns {
// Avoid double-rendering
if turn.turn_type == TurnType::Crosswalk
&& !turn.other_crosswalk_ids.iter().any(|id| *id < turn.id)
Expand Down Expand Up @@ -265,7 +265,7 @@ pub fn calculate_corners(i: &Intersection, map: &Map) -> Vec<Polygon> {

let mut corners = Vec::new();

for turn in map.get_turns_in_intersection(i.id) {
for turn in &i.turns {
if turn.turn_type == TurnType::SharedSidewalkCorner {
// Avoid double-rendering
if map.get_l(turn.id.src).dst_i != i.id {
Expand Down Expand Up @@ -332,7 +332,7 @@ fn calculate_corner_curbs(i: &Intersection, map: &Map) -> Vec<Polygon> {
let thickness = Distance::meters(0.2);
let shift = |width| (width - thickness) / 2.0;

for turn in map.get_turns_in_intersection(i.id) {
for turn in &i.turns {
if turn.turn_type == TurnType::SharedSidewalkCorner {
// Avoid double-rendering
if map.get_l(turn.id.src).dst_i != i.id {
Expand Down
Loading

0 comments on commit 5efd3a9

Please sign in to comment.