mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-27 15:03:20 +03:00
Edit modal filters from within the pathfinding view too.
Slow implementation, but at least it works
This commit is contained in:
parent
fbc847f48c
commit
f7f23465c1
@ -15,10 +15,10 @@ pub use browse::BrowseNeighborhoods;
|
||||
mod browse;
|
||||
mod connectivity;
|
||||
mod draw_cells;
|
||||
mod pathfinding;
|
||||
mod per_neighborhood;
|
||||
mod rat_run_viewer;
|
||||
mod rat_runs;
|
||||
mod route;
|
||||
mod select_boundary;
|
||||
|
||||
pub struct Neighborhood {
|
||||
|
@ -6,7 +6,7 @@ use widgetry::{
|
||||
Color, EventCtx, GfxCtx, Line, Outcome, Panel, RoundedF64, Spinner, State, Text, Widget,
|
||||
};
|
||||
|
||||
use super::per_neighborhood::{Tab, TakeNeighborhood};
|
||||
use super::per_neighborhood::{FilterableObj, Tab, TakeNeighborhood};
|
||||
use super::Neighborhood;
|
||||
use crate::app::{App, Transition};
|
||||
use crate::common::{cmp_dist, cmp_duration, InputWaypoints, WaypointID};
|
||||
@ -14,7 +14,7 @@ use crate::common::{cmp_dist, cmp_duration, InputWaypoints, WaypointID};
|
||||
pub struct RoutePlanner {
|
||||
panel: Panel,
|
||||
waypoints: InputWaypoints,
|
||||
world: World<ID>,
|
||||
world: World<Obj>,
|
||||
|
||||
neighborhood: Neighborhood,
|
||||
}
|
||||
@ -26,12 +26,13 @@ impl TakeNeighborhood for RoutePlanner {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum ID {
|
||||
enum Obj {
|
||||
RouteAfterFilters,
|
||||
RouteBeforeFilters,
|
||||
Waypoint(WaypointID),
|
||||
Filterable(FilterableObj),
|
||||
}
|
||||
impl ObjectID for ID {}
|
||||
impl ObjectID for Obj {}
|
||||
|
||||
impl RoutePlanner {
|
||||
pub fn new_state(
|
||||
@ -42,9 +43,10 @@ impl RoutePlanner {
|
||||
let mut rp = RoutePlanner {
|
||||
panel: Panel::empty(ctx),
|
||||
waypoints: InputWaypoints::new(app),
|
||||
world: World::bounded(app.primary.map.get_bounds()),
|
||||
world: World::unbounded(),
|
||||
neighborhood,
|
||||
};
|
||||
|
||||
rp.update(ctx, app);
|
||||
Box::new(rp)
|
||||
}
|
||||
@ -75,17 +77,30 @@ impl RoutePlanner {
|
||||
|
||||
let mut world = self.calculate_paths(ctx, app);
|
||||
self.waypoints
|
||||
.rebuild_world(ctx, &mut world, ID::Waypoint, 2);
|
||||
.rebuild_world(ctx, &mut world, Obj::Waypoint, 3);
|
||||
world.initialize_hover(ctx);
|
||||
world.rebuilt_during_drag(&self.world);
|
||||
self.world = world;
|
||||
}
|
||||
|
||||
/// Also has the side effect of changing a note in the panel
|
||||
fn calculate_paths(&mut self, ctx: &mut EventCtx, app: &App) -> World<ID> {
|
||||
fn calculate_paths(&mut self, ctx: &mut EventCtx, app: &App) -> World<Obj> {
|
||||
let map = &app.primary.map;
|
||||
let mut world = World::bounded(map.get_bounds());
|
||||
|
||||
// TODO It's expensive to do this as we constantly drag the route!
|
||||
super::per_neighborhood::populate_world(
|
||||
ctx,
|
||||
app,
|
||||
&self.neighborhood,
|
||||
&mut world,
|
||||
Obj::Filterable,
|
||||
// TODO Put these on top of the routes, so we can click and filter roads part of the
|
||||
// route. We lose the tooltip though; probably should put that in the panel instead
|
||||
// anyway.
|
||||
2,
|
||||
);
|
||||
|
||||
// First the route respecting the filters
|
||||
let (total_time_after, total_dist_after) = {
|
||||
let mut params = map.routing_params().clone();
|
||||
@ -123,7 +138,7 @@ impl RoutePlanner {
|
||||
txt.add_line(Line(format!("Distance: {}", total_dist)));
|
||||
|
||||
world
|
||||
.add(ID::RouteAfterFilters)
|
||||
.add(Obj::RouteAfterFilters)
|
||||
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||
.zorder(0)
|
||||
.draw(draw_route)
|
||||
@ -167,7 +182,7 @@ impl RoutePlanner {
|
||||
let mut txt = Text::new();
|
||||
// If these two stats are the same, assume the two paths are equivalent
|
||||
if total_time == total_time_after && total_dist == total_dist_after {
|
||||
world.delete(ID::RouteAfterFilters);
|
||||
world.delete(Obj::RouteAfterFilters);
|
||||
txt.add_line(Line(
|
||||
"The route is the same before/after the new modal filters",
|
||||
));
|
||||
@ -206,7 +221,7 @@ impl RoutePlanner {
|
||||
}
|
||||
|
||||
world
|
||||
.add(ID::RouteBeforeFilters)
|
||||
.add(Obj::RouteBeforeFilters)
|
||||
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||
// If the two routes partly overlap, put the "before" on top, since it has
|
||||
// the comparison stats.
|
||||
@ -224,8 +239,25 @@ impl RoutePlanner {
|
||||
|
||||
impl State<App> for RoutePlanner {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
let world_outcome_for_waypoints = self.world.event(ctx).map_id(|id| match id {
|
||||
ID::Waypoint(id) => id,
|
||||
let world_outcome = self.world.event(ctx);
|
||||
// TODO map_id can only extract one case. Do a bit of a hack to handle filter managament
|
||||
// first.
|
||||
if let Some(outcome) = world_outcome.clone().maybe_map_id(|id| match id {
|
||||
Obj::Filterable(id) => Some(id),
|
||||
_ => None,
|
||||
}) {
|
||||
if super::per_neighborhood::handle_world_outcome(ctx, app, outcome) {
|
||||
// Recalculate the neighborhood
|
||||
self.neighborhood =
|
||||
Neighborhood::new(ctx, app, self.neighborhood.orig_perimeter.clone());
|
||||
self.update(ctx, app);
|
||||
return Transition::Keep;
|
||||
}
|
||||
// Fall through. Clicking free space and other ID-less outcomes will match here, but we
|
||||
// don't want them to.
|
||||
}
|
||||
let world_outcome_for_waypoints = world_outcome.map_id(|id| match id {
|
||||
Obj::Waypoint(id) => id,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
|
||||
@ -239,7 +271,7 @@ impl State<App> for RoutePlanner {
|
||||
// Recompute paths
|
||||
let mut world = self.calculate_paths(ctx, app);
|
||||
self.waypoints
|
||||
.rebuild_world(ctx, &mut world, ID::Waypoint, 2);
|
||||
.rebuild_world(ctx, &mut world, Obj::Waypoint, 2);
|
||||
world.initialize_hover(ctx);
|
||||
world.rebuilt_during_drag(&self.world);
|
||||
self.world = world;
|
@ -89,7 +89,7 @@ impl Tab {
|
||||
})),
|
||||
"Pathfinding" => Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<T>().ok().unwrap();
|
||||
vec![super::route::RoutePlanner::new_state(
|
||||
vec![super::pathfinding::RoutePlanner::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.take_neighborhood(),
|
||||
|
@ -31,6 +31,7 @@ pub struct World<ID: ObjectID> {
|
||||
}
|
||||
|
||||
/// The result of a `World` handling an event
|
||||
#[derive(Clone)]
|
||||
pub enum WorldOutcome<ID: ObjectID> {
|
||||
/// A left click occurred while not hovering on any object
|
||||
ClickedFreeSpace(Pt2D),
|
||||
@ -55,22 +56,27 @@ impl<I: ObjectID> WorldOutcome<I> {
|
||||
/// component owns a World that contains a few different types of objects, some of which are
|
||||
/// managed by another component that only cares about its IDs.
|
||||
pub fn map_id<O: ObjectID, F: Fn(I) -> O>(self, f: F) -> WorldOutcome<O> {
|
||||
self.maybe_map_id(|id| Some(f(id))).unwrap()
|
||||
}
|
||||
|
||||
/// Like `map_id`, but the transformation may fail.
|
||||
pub fn maybe_map_id<O: ObjectID, F: Fn(I) -> Option<O>>(self, f: F) -> Option<WorldOutcome<O>> {
|
||||
match self {
|
||||
WorldOutcome::ClickedFreeSpace(pt) => WorldOutcome::ClickedFreeSpace(pt),
|
||||
WorldOutcome::ClickedFreeSpace(pt) => Some(WorldOutcome::ClickedFreeSpace(pt)),
|
||||
WorldOutcome::Dragging {
|
||||
obj,
|
||||
dx,
|
||||
dy,
|
||||
cursor,
|
||||
} => WorldOutcome::Dragging {
|
||||
obj: f(obj),
|
||||
} => Some(WorldOutcome::Dragging {
|
||||
obj: f(obj)?,
|
||||
dx,
|
||||
dy,
|
||||
cursor,
|
||||
},
|
||||
WorldOutcome::Keypress(action, id) => WorldOutcome::Keypress(action, f(id)),
|
||||
WorldOutcome::ClickedObject(id) => WorldOutcome::ClickedObject(f(id)),
|
||||
WorldOutcome::Nothing => WorldOutcome::Nothing,
|
||||
}),
|
||||
WorldOutcome::Keypress(action, id) => Some(WorldOutcome::Keypress(action, f(id)?)),
|
||||
WorldOutcome::ClickedObject(id) => Some(WorldOutcome::ClickedObject(f(id)?)),
|
||||
WorldOutcome::Nothing => Some(WorldOutcome::Nothing),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user