zorder for cars/peds too... had to rework get_objects_onscreen

This commit is contained in:
Dustin Carlino 2019-01-21 14:04:07 -08:00
parent 1e7b8f2dfc
commit 6dee096a0e
9 changed files with 99 additions and 63 deletions

View File

@ -22,9 +22,6 @@
- model U-turns
- zorder for bridges/tunnels
- apply to cars/peds too; figure out statics/dynamics plumbing
- degenerate-2's should only have one crosswalk
- then make them thinner

View File

@ -4,8 +4,6 @@
- interactively spawn a car/ped somewhere to test this easily
- pathfinding or trace or something is wrong for walking; the last line sometimes has the wrong distance
- driving OR walking goal could also be border. actually need this to spawn cars on tunnels reasonably
- then back to zorder for cars/peds
- try showing traffic signals by little boxes at the end of lanes
- red circle means right turn on red OK, red right arrow means nope, green means normal turns ok, green arrow means protected left, crosswalk hand or stick figure

View File

@ -8,6 +8,7 @@ use map_model::{BusStop, BusStopID, Map, LANE_THICKNESS};
pub struct DrawBusStop {
pub id: BusStopID,
polygon: Polygon,
zorder: isize,
}
impl DrawBusStop {
@ -31,6 +32,7 @@ impl DrawBusStop {
DrawBusStop {
id: stop.id,
polygon,
zorder: map.get_parent(lane.id).get_zorder(),
}
}
}
@ -57,4 +59,8 @@ impl Renderable for DrawBusStop {
fn contains_pt(&self, pt: Pt2D) -> bool {
self.polygon.contains_pt(pt)
}
fn get_zorder(&self) -> isize {
self.zorder
}
}

View File

@ -21,6 +21,7 @@ pub struct DrawCar {
// TODO maybe also draw lookahead buffer to know what the car is considering
stopping_buffer: Option<Polygon>,
state: CarState,
zorder: isize,
}
impl DrawCar {
@ -49,6 +50,7 @@ impl DrawCar {
right_blinker_on,
stopping_buffer,
state: input.state,
zorder: input.on.get_zorder(map),
};
}
@ -118,6 +120,7 @@ impl DrawCar {
right_blinker_on,
stopping_buffer,
state: input.state,
zorder: input.on.get_zorder(map),
}
}
}
@ -202,6 +205,10 @@ impl Renderable for DrawCar {
fn contains_pt(&self, pt: Pt2D) -> bool {
self.body_polygon.contains_pt(pt)
}
fn get_zorder(&self) -> isize {
self.zorder
}
}
fn thick_line_from_angle(thickness: f64, line_length: f64, pt: Pt2D, angle: Angle) -> Polygon {

View File

@ -19,8 +19,15 @@ use map_model::{
RoadID, Traversable, Turn, TurnID, LANE_THICKNESS,
};
use sim::GetDrawAgents;
use std::borrow::Borrow;
use std::collections::HashMap;
#[derive(PartialEq)]
pub enum RenderOrder {
BackToFront,
FrontToBack,
}
pub struct DrawMap {
pub lanes: Vec<DrawLane>,
pub intersections: Vec<DrawIntersection>,
@ -210,7 +217,6 @@ impl DrawMap {
&self.areas[id.0]
}
// A greatly simplified form of get_objects_onscreen
pub fn get_matching_lanes(&self, bounds: Bounds) -> Vec<LaneID> {
let mut results: Vec<LaneID> = Vec::new();
for &(id, _, _) in &self.quadtree.query(bounds.as_bbox()) {
@ -221,25 +227,21 @@ impl DrawMap {
results
}
// Returns in back-to-front order
// The second pair is ephemeral objects (cars, pedestrians) that we can't borrow --
// conveniently they're the front-most layer, so the caller doesn't have to do anything strange
// to merge. TODO no longer true with zorder
// TODO alternatively, we could return IDs in order, then the caller could turn around and call
// a getter... except they still have to deal with DrawCar and DrawPedestrian not being
// borrowable. Could move contains_pt and draw calls here directly, but that might be weird?
// But maybe not.
pub fn get_objects_onscreen<T: ShowObjects>(
// False from the callback means abort
pub fn handle_objects_onscreen<T: ShowObjects, F: FnMut(Box<&Renderable>) -> bool>(
&self,
screen_bounds: Bounds,
map: &Map,
sim: &GetDrawAgents,
show_objs: &T,
) -> (Vec<Box<&Renderable>>, Vec<Box<Renderable>>) {
order: RenderOrder,
mut callback: F,
) {
// From background to foreground Z-order
let mut areas: Vec<Box<&Renderable>> = Vec::new();
let mut parcels: Vec<Box<&Renderable>> = Vec::new();
let mut lanes_and_intersections: Vec<Box<&Renderable>> = Vec::new();
let mut lanes: Vec<Box<&Renderable>> = Vec::new();
let mut intersections: Vec<Box<&Renderable>> = Vec::new();
let mut buildings: Vec<Box<&Renderable>> = Vec::new();
let mut extra_shapes: Vec<Box<&Renderable>> = Vec::new();
let mut bus_stops: Vec<Box<&Renderable>> = Vec::new();
@ -254,7 +256,7 @@ impl DrawMap {
ID::Area(id) => areas.push(Box::new(self.get_a(*id))),
ID::Parcel(id) => parcels.push(Box::new(self.get_p(*id))),
ID::Lane(id) => {
lanes_and_intersections.push(Box::new(self.get_l(*id)));
lanes.push(Box::new(self.get_l(*id)));
if !show_objs.show_icons_for(map.get_l(*id).dst_i) {
for c in sim.get_draw_cars(Traversable::Lane(*id), map).into_iter() {
cars.push(draw_vehicle(c, map));
@ -265,7 +267,7 @@ impl DrawMap {
}
}
ID::Intersection(id) => {
lanes_and_intersections.push(Box::new(self.get_i(*id)));
intersections.push(Box::new(self.get_i(*id)));
for t in &map.get_i(*id).turns {
if show_objs.show_icons_for(*id) {
turn_icons.push(Box::new(self.get_t(*t)));
@ -296,18 +298,30 @@ impl DrawMap {
let mut borrows: Vec<Box<&Renderable>> = Vec::new();
borrows.extend(areas);
borrows.extend(parcels);
// This is a stable sort.
lanes_and_intersections.sort_by_key(|r| r.get_zorder());
borrows.extend(lanes_and_intersections);
borrows.extend(lanes);
borrows.extend(intersections);
borrows.extend(buildings);
borrows.extend(extra_shapes);
borrows.extend(bus_stops);
borrows.extend(turn_icons);
for c in &cars {
borrows.push(Box::new(c.borrow()));
}
for p in &peds {
borrows.push(Box::new(p.borrow()));
}
let mut returns: Vec<Box<Renderable>> = Vec::new();
returns.extend(cars);
returns.extend(peds);
// This is a stable sort.
borrows.sort_by_key(|r| r.get_zorder());
(borrows, returns)
if order == RenderOrder::FrontToBack {
borrows.reverse();
}
for r in borrows {
if !callback(r) {
break;
}
}
}
}

View File

@ -18,7 +18,7 @@ use crate::render::car::DrawCar;
pub use crate::render::extra_shape::ExtraShapeID;
pub use crate::render::intersection::{draw_signal_cycle, draw_signal_diagram};
pub use crate::render::lane::DrawLane;
pub use crate::render::map::DrawMap;
pub use crate::render::map::{DrawMap, RenderOrder};
pub use crate::render::pedestrian::DrawPedestrian;
pub use crate::render::turn::{DrawCrosswalk, DrawTurn};
use ezgui::{Color, GfxCtx};

View File

@ -12,6 +12,7 @@ pub struct DrawPedestrian {
circle: Circle,
turn_arrow: Option<Line>,
preparing_bike: bool,
zorder: isize,
}
impl DrawPedestrian {
@ -30,6 +31,7 @@ impl DrawPedestrian {
circle: Circle::new(input.pos, RADIUS),
turn_arrow,
preparing_bike: input.preparing_bike,
zorder: input.on.get_zorder(map),
}
}
}
@ -70,4 +72,8 @@ impl Renderable for DrawPedestrian {
fn contains_pt(&self, pt: Pt2D) -> bool {
self.circle.contains_pt(pt)
}
fn get_zorder(&self) -> isize {
self.zorder
}
}

View File

@ -2,7 +2,7 @@ use crate::colors::ColorScheme;
use abstutil;
//use cpuprofiler;
use crate::objects::{Ctx, RenderingHints, ID};
use crate::render::{RenderOptions, Renderable};
use crate::render::{RenderOptions, RenderOrder, Renderable};
use crate::state::UIState;
use ezgui::{
Canvas, Color, EventLoopMode, Folder, GfxCtx, Key, ModalMenu, Text, TopMenu, UserInput,
@ -12,7 +12,6 @@ use kml;
use map_model::{BuildingID, LaneID};
use serde_derive::{Deserialize, Serialize};
use sim::GetDrawAgents;
use std::borrow::Borrow;
use std::collections::HashSet;
use std::process;
@ -265,11 +264,7 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
};
let mut sample_intersection: Option<String> = None;
let (statics, dynamics) = self.get_objects_onscreen();
for obj in statics
.into_iter()
.chain(dynamics.iter().map(|obj| Box::new(obj.borrow())))
{
self.handle_objects_onscreen(RenderOrder::BackToFront, |obj| {
let opts = RenderOptions {
color: self.state.get_state().color_obj(obj.get_id(), &ctx),
debug_mode: self.state.get_state().layers.debug_mode.is_enabled(),
@ -284,7 +279,9 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
sample_intersection = Some(format!("_i{}", id.0));
}
}
}
true
});
if !screencap {
self.state.draw(g, &ctx);
@ -356,7 +353,35 @@ impl<S: UIState> UI<S> {
ui
}
fn get_objects_onscreen(&self) -> (Vec<Box<&Renderable>>, Vec<Box<Renderable>>) {
fn mouseover_something(&self) -> Option<ID> {
let pt = self.canvas.get_cursor_in_map_space()?;
let mut id: Option<ID> = None;
self.handle_objects_onscreen(RenderOrder::FrontToBack, |obj| {
// Don't mouseover parcels.
// TODO Might get fancier rules in the future, so we can't mouseover irrelevant things
// in intersection editor mode, for example.
match obj.get_id() {
ID::Parcel(_) => {}
_ => {
if obj.contains_pt(pt) {
id = Some(obj.get_id());
return false;
}
}
};
true
});
id
}
fn handle_objects_onscreen<F: FnMut(Box<&Renderable>) -> bool>(
&self,
order: RenderOrder,
callback: F,
) {
let state = self.state.get_state();
let draw_agent_source: &GetDrawAgents = {
@ -368,40 +393,16 @@ impl<S: UIState> UI<S> {
}
};
state.primary.draw_map.get_objects_onscreen(
state.primary.draw_map.handle_objects_onscreen(
self.canvas.get_screen_bounds(),
&state.primary.map,
draw_agent_source,
state,
order,
callback,
)
}
fn mouseover_something(&self) -> Option<ID> {
let pt = self.canvas.get_cursor_in_map_space()?;
let (statics, dynamics) = self.get_objects_onscreen();
// Check front-to-back
for obj in dynamics
.iter()
.map(|obj| Box::new(obj.borrow()))
.chain(statics.into_iter().rev())
{
// Don't mouseover parcels.
// TODO Might get fancier rules in the future, so we can't mouseover irrelevant things
// in intersection editor mode, for example.
match obj.get_id() {
ID::Parcel(_) => {}
_ => {
if obj.contains_pt(pt) {
return Some(obj.get_id());
}
}
};
}
None
}
fn save_editor_state(&self) {
let state = EditorState {
map_name: self.state.get_state().primary.map.get_name().clone(),

View File

@ -115,4 +115,11 @@ impl Traversable {
Traversable::Turn(id) => map.get_parent(id.dst).get_speed_limit(),
}
}
pub fn get_zorder(&self, map: &Map) -> isize {
match *self {
Traversable::Lane(id) => map.get_parent(id).get_zorder(),
Traversable::Turn(id) => map.get_i(id.parent).get_zorder(map),
}
}
}