mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
Start a tool to route through or around an LTN. Just the skeleton reused
from Ungap the Map...
This commit is contained in:
parent
684f9cc4cc
commit
5888536aef
@ -12,6 +12,7 @@ mod browse;
|
|||||||
mod draw_cells;
|
mod draw_cells;
|
||||||
mod rat_run_viewer;
|
mod rat_run_viewer;
|
||||||
mod rat_runs;
|
mod rat_runs;
|
||||||
|
mod route;
|
||||||
mod viewer;
|
mod viewer;
|
||||||
|
|
||||||
pub struct Neighborhood {
|
pub struct Neighborhood {
|
||||||
|
118
game/src/ltn/route.rs
Normal file
118
game/src/ltn/route.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
use geom::{Distance, Polygon};
|
||||||
|
use map_model::NORMAL_LANE_THICKNESS;
|
||||||
|
use sim::{TripEndpoint, TripMode};
|
||||||
|
use widgetry::mapspace::{ObjectID, ToggleZoomed, World};
|
||||||
|
use widgetry::{
|
||||||
|
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, VerticalAlignment,
|
||||||
|
Widget,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app::{App, Transition};
|
||||||
|
use crate::common::{InputWaypoints, WaypointID};
|
||||||
|
|
||||||
|
pub struct RoutePlanner {
|
||||||
|
panel: Panel,
|
||||||
|
waypoints: InputWaypoints,
|
||||||
|
world: World<ID>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
enum ID {
|
||||||
|
MainRoute,
|
||||||
|
Waypoint(WaypointID),
|
||||||
|
}
|
||||||
|
impl ObjectID for ID {}
|
||||||
|
|
||||||
|
impl RoutePlanner {
|
||||||
|
pub fn new_state(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
|
||||||
|
let mut rp = RoutePlanner {
|
||||||
|
panel: Panel::empty(ctx),
|
||||||
|
waypoints: InputWaypoints::new(app),
|
||||||
|
world: World::bounded(app.primary.map.get_bounds()),
|
||||||
|
};
|
||||||
|
rp.update(ctx, app);
|
||||||
|
Box::new(rp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &mut EventCtx, app: &App) {
|
||||||
|
self.panel = Panel::new_builder(Widget::col(vec![
|
||||||
|
ctx.style()
|
||||||
|
.btn_outline
|
||||||
|
.text("Back to editing modal filters")
|
||||||
|
.hotkey(Key::Escape)
|
||||||
|
.build_def(ctx),
|
||||||
|
self.waypoints.get_panel_widget(ctx),
|
||||||
|
]))
|
||||||
|
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||||
|
// Hovering on waypoint cards
|
||||||
|
.ignore_initial_events()
|
||||||
|
.build(ctx);
|
||||||
|
|
||||||
|
let map = &app.primary.map;
|
||||||
|
|
||||||
|
let mut world = World::bounded(map.get_bounds());
|
||||||
|
|
||||||
|
// Actually calculate paths
|
||||||
|
let mut draw_route = ToggleZoomed::builder();
|
||||||
|
let mut hitbox_pieces = Vec::new();
|
||||||
|
for pair in self.waypoints.get_waypoints().windows(2) {
|
||||||
|
if let Some(pl) = TripEndpoint::path_req(pair[0], pair[1], TripMode::Drive, map)
|
||||||
|
.and_then(|req| map.pathfind(req).ok())
|
||||||
|
.and_then(|path| path.trace(map))
|
||||||
|
{
|
||||||
|
let shape = pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS);
|
||||||
|
draw_route
|
||||||
|
.unzoomed
|
||||||
|
.push(Color::RED.alpha(0.8), shape.clone());
|
||||||
|
draw_route.zoomed.push(Color::RED.alpha(0.5), shape.clone());
|
||||||
|
hitbox_pieces.push(shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hitbox_pieces.is_empty() {
|
||||||
|
world
|
||||||
|
.add(ID::MainRoute)
|
||||||
|
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||||
|
.draw(draw_route)
|
||||||
|
.hover_outline(Color::BLACK, Distance::meters(2.0))
|
||||||
|
.build(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.waypoints
|
||||||
|
.rebuild_world(ctx, &mut world, ID::Waypoint, 1);
|
||||||
|
world.initialize_hover(ctx);
|
||||||
|
world.rebuilt_during_drag(&self.world);
|
||||||
|
self.world = world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State<App> for RoutePlanner {
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||||
|
let world_outcome_for_waypoints = match self.world.event(ctx) {
|
||||||
|
x => x.map_id(|id| match id {
|
||||||
|
ID::Waypoint(id) => id,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let panel_outcome = self.panel.event(ctx);
|
||||||
|
if let Outcome::Clicked(ref x) = panel_outcome {
|
||||||
|
if x == "Back to editing modal filters" {
|
||||||
|
return Transition::Pop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.waypoints
|
||||||
|
.event(app, panel_outcome, world_outcome_for_waypoints)
|
||||||
|
{
|
||||||
|
self.update(ctx, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transition::Keep
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||||
|
self.panel.draw(g);
|
||||||
|
self.world.draw(g);
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,11 @@ impl Viewer {
|
|||||||
.text("Browse rat-runs")
|
.text("Browse rat-runs")
|
||||||
.hotkey(Key::R)
|
.hotkey(Key::R)
|
||||||
.build_def(ctx),
|
.build_def(ctx),
|
||||||
|
ctx.style()
|
||||||
|
.btn_outline
|
||||||
|
.text("Pathfind")
|
||||||
|
.hotkey(Key::P)
|
||||||
|
.build_def(ctx),
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
"Draw traffic cells as".text_widget(ctx).centered_vert(),
|
"Draw traffic cells as".text_widget(ctx).centered_vert(),
|
||||||
Toggle::choice(ctx, "draw cells", "areas", "streets", Key::C, true),
|
Toggle::choice(ctx, "draw cells", "areas", "streets", Key::C, true),
|
||||||
@ -92,6 +97,9 @@ impl State<App> for Viewer {
|
|||||||
&self.neighborhood,
|
&self.neighborhood,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
"Pathfind" => {
|
||||||
|
return Transition::Push(super::route::RoutePlanner::new_state(ctx, app));
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
Outcome::Changed(_) => {
|
Outcome::Changed(_) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user