From db7c276e913ce89220fedda16fde59da7da7cd32 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sat, 9 May 2020 10:34:52 -0700 Subject: [PATCH] select a path of roads --- Cargo.lock | 1 + game/Cargo.toml | 1 + game/src/edit/bulk.rs | 94 +++++++++++++++++++++++++++++++++++++++++-- geom/src/distance.rs | 6 +++ 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 363090e8c2..018f5aeaa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,6 +1006,7 @@ dependencies = [ "kml 0.1.0", "map_model 0.1.0", "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/game/Cargo.toml b/game/Cargo.toml index a65ab8f2b3..a8cd003654 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -23,6 +23,7 @@ instant = "0.1.2" kml = { path = "../kml" } maplit = "1.0.2" map_model = { path = "../map_model" } +petgraph = "0.5.0" rand = "0.7.0" rand_xorshift = "0.2.0" serde = "1.0.98" diff --git a/game/src/edit/bulk.rs b/game/src/edit/bulk.rs index 66559c6413..54aec1c59e 100644 --- a/game/src/edit/bulk.rs +++ b/game/src/edit/bulk.rs @@ -1,11 +1,14 @@ use crate::app::{App, ShowEverything}; +use crate::common::CommonState; use crate::game::{State, Transition}; use crate::helpers::ID; use ezgui::{ - hotkey, Btn, Composite, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, TextExt, - VerticalAlignment, Widget, + hotkey, Btn, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, + Line, Outcome, TextExt, VerticalAlignment, Widget, }; -use map_model::IntersectionID; +use geom::Distance; +use map_model::{IntersectionID, Map, RoadID}; +use petgraph::graphmap::UnGraphMap; use sim::DontDrawAgents; // TODO For now, individual turns can't be manipulated. Banning turns could be useful, but I'm not @@ -13,6 +16,7 @@ use sim::DontDrawAgents; pub struct BulkSelect { composite: Composite, i1: Option, + preview_path: Option<(IntersectionID, Vec, Drawable)>, } impl BulkSelect { @@ -22,7 +26,9 @@ impl BulkSelect { composite: Composite::new( Widget::col(vec![ Line("Edit many roads").small_heading().draw(ctx), - "Click one intersection to start".draw_text(ctx), + "Click one intersection to start" + .draw_text(ctx) + .named("instructions"), Btn::text_fg("Quit") .build_def(ctx, hotkey(Key::Escape)) .margin_above(10), @@ -33,6 +39,7 @@ impl BulkSelect { .aligned(HorizontalAlignment::Center, VerticalAlignment::Top) .build(ctx), i1: None, + preview_path: None, }) } } @@ -54,6 +61,58 @@ impl State for BulkSelect { } } + if let Some(ID::Intersection(i)) = app.primary.current_selection { + if self.i1.is_none() && app.per_obj.left_click(ctx, "start here") { + self.i1 = Some(i); + self.composite.replace( + ctx, + "instructions", + "Click a second intersection to edit this path".draw_text(ctx), + ); + } + } + + if let Some(i1) = self.i1 { + if let Some(ID::Intersection(i2)) = app.primary.current_selection { + if self + .preview_path + .as_ref() + .map(|(i, _, _)| *i != i2) + .unwrap_or(true) + { + let mut batch = GeomBatch::new(); + let roads = if let Some(roads) = pathfind(&app.primary.map, i1, i2) { + for r in &roads { + batch.push( + Color::RED, + app.primary + .map + .get_r(*r) + .get_thick_polygon(&app.primary.map) + .unwrap(), + ); + } + roads + } else { + Vec::new() + }; + self.preview_path = Some((i2, roads, ctx.upload(batch))); + } + + if self + .preview_path + .as_ref() + .map(|(_, roads, _)| !roads.is_empty()) + .unwrap_or(false) + && app.per_obj.left_click(ctx, "end here") + { + println!("do it"); + } + } else { + self.preview_path = None; + } + } + match self.composite.event(ctx) { Some(Outcome::Clicked(x)) => match x.as_ref() { "Quit" => { @@ -68,5 +127,32 @@ impl State for BulkSelect { fn draw(&self, g: &mut GfxCtx, app: &App) { self.composite.draw(g); + if let Some(i) = self.i1 { + g.draw_polygon(Color::GREEN, &app.primary.map.get_i(i).polygon); + } + if let Some((_, _, ref p)) = self.preview_path { + g.redraw(p); + } + CommonState::draw_osd(g, app, &None); } } + +// Simple search along undirected roads +fn pathfind(map: &Map, i1: IntersectionID, i2: IntersectionID) -> Option> { + let mut graph: UnGraphMap = UnGraphMap::new(); + for r in map.all_roads() { + graph.add_edge(r.src_i, r.dst_i, r.id); + } + let (_, path) = petgraph::algo::astar( + &graph, + i1, + |i| i == i2, + |(_, _, r)| map.get_r(*r).center_pts.length(), + |_| Distance::ZERO, + )?; + Some( + path.windows(2) + .map(|pair| *graph.edge_weight(pair[0], pair[1]).unwrap()) + .collect(), + ) +} diff --git a/geom/src/distance.rs b/geom/src/distance.rs index 13b7dfb912..c8b7da8d1d 100644 --- a/geom/src/distance.rs +++ b/geom/src/distance.rs @@ -168,3 +168,9 @@ impl std::iter::Sum for Distance { sum } } + +impl Default for Distance { + fn default() -> Distance { + Distance::ZERO + } +}