mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
select a path of roads
This commit is contained in:
parent
f3b396a7e9
commit
db7c276e91
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1006,6 +1006,7 @@ dependencies = [
|
|||||||
"kml 0.1.0",
|
"kml 0.1.0",
|
||||||
"map_model 0.1.0",
|
"map_model 0.1.0",
|
||||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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)",
|
"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)",
|
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -23,6 +23,7 @@ instant = "0.1.2"
|
|||||||
kml = { path = "../kml" }
|
kml = { path = "../kml" }
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
map_model = { path = "../map_model" }
|
map_model = { path = "../map_model" }
|
||||||
|
petgraph = "0.5.0"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
rand_xorshift = "0.2.0"
|
rand_xorshift = "0.2.0"
|
||||||
serde = "1.0.98"
|
serde = "1.0.98"
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use crate::app::{App, ShowEverything};
|
use crate::app::{App, ShowEverything};
|
||||||
|
use crate::common::CommonState;
|
||||||
use crate::game::{State, Transition};
|
use crate::game::{State, Transition};
|
||||||
use crate::helpers::ID;
|
use crate::helpers::ID;
|
||||||
use ezgui::{
|
use ezgui::{
|
||||||
hotkey, Btn, Composite, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, TextExt,
|
hotkey, Btn, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||||
VerticalAlignment, Widget,
|
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;
|
use sim::DontDrawAgents;
|
||||||
|
|
||||||
// TODO For now, individual turns can't be manipulated. Banning turns could be useful, but I'm not
|
// 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 {
|
pub struct BulkSelect {
|
||||||
composite: Composite,
|
composite: Composite,
|
||||||
i1: Option<IntersectionID>,
|
i1: Option<IntersectionID>,
|
||||||
|
preview_path: Option<(IntersectionID, Vec<RoadID>, Drawable)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BulkSelect {
|
impl BulkSelect {
|
||||||
@ -22,7 +26,9 @@ impl BulkSelect {
|
|||||||
composite: Composite::new(
|
composite: Composite::new(
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
Line("Edit many roads").small_heading().draw(ctx),
|
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")
|
Btn::text_fg("Quit")
|
||||||
.build_def(ctx, hotkey(Key::Escape))
|
.build_def(ctx, hotkey(Key::Escape))
|
||||||
.margin_above(10),
|
.margin_above(10),
|
||||||
@ -33,6 +39,7 @@ impl BulkSelect {
|
|||||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||||
.build(ctx),
|
.build(ctx),
|
||||||
i1: None,
|
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) {
|
match self.composite.event(ctx) {
|
||||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||||
"Quit" => {
|
"Quit" => {
|
||||||
@ -68,5 +127,32 @@ impl State for BulkSelect {
|
|||||||
|
|
||||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||||
self.composite.draw(g);
|
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<Vec<RoadID>> {
|
||||||
|
let mut graph: UnGraphMap<IntersectionID, RoadID> = 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(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -168,3 +168,9 @@ impl std::iter::Sum for Distance {
|
|||||||
sum
|
sum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Distance {
|
||||||
|
fn default() -> Distance {
|
||||||
|
Distance::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user