making DrawCar implement renderable

This commit is contained in:
Dustin Carlino 2018-09-13 22:16:48 -07:00
parent b3d2a5c5be
commit 5303087fa2
10 changed files with 114 additions and 97 deletions

View File

@ -981,8 +981,8 @@ so it feels like we implicitly have a big enum of active plugin, with each of th
- deal with overlapping keys that still kinda happen (sim ctrl, escape game) - deal with overlapping keys that still kinda happen (sim ctrl, escape game)
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow - bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
- make draw car/ped implement renderable. = make draw car/ped implement renderable.
- move Draw* to editor crate, and have an intermediate thing exposed from sim and do the translation in get_blah_onscreen. = move Draw* to editor crate, and have an intermediate thing exposed from sim and do the translation in get_blah_onscreen.
- then rethink colors, with simplified single plugin - then rethink colors, with simplified single plugin
= then finally try out a unified quadtree! = then finally try out a unified quadtree!

View File

@ -45,7 +45,7 @@ impl FollowState {
// instead // instead
FollowState::FollowingCar(id) => { FollowState::FollowingCar(id) => {
if let Some(c) = sim.get_draw_car(*id, map) { if let Some(c) = sim.get_draw_car(*id, map) {
let pt = c.focus_pt(); let pt = c.front;
canvas.center_on_map_pt(pt.x(), pt.y()); canvas.center_on_map_pt(pt.x(), pt.y());
input.key_pressed(Key::Return, "stop following") input.key_pressed(Key::Return, "stop following")
} else { } else {

View File

@ -133,7 +133,7 @@ fn warp(line: String, map: &Map, sim: &Sim, canvas: &mut Canvas, selected: &mut
if let Some(c) = sim.get_draw_car(id, map) { if let Some(c) = sim.get_draw_car(id, map) {
println!("Warping to {}", id); println!("Warping to {}", id);
*selected = Some(ID::Car(id)); *selected = Some(ID::Car(id));
c.focus_pt() c.front
} else { } else {
println!("{} doesn't exist", id); println!("{} doesn't exist", id);
return; return;

View File

@ -1,23 +1,22 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 use aabb_quadtree::geom::Rect;
use colors::ColorScheme;
use dimensioned::si; use dimensioned::si;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom::{Angle, Polygon, Pt2D}; use geom::{Polygon, Pt2D};
use graphics; use graphics;
use kinematics::Vehicle; use graphics::types::Color;
use map_model::{geometry, Map, TurnID}; use map_model::{geometry, Map};
use {CarID, Distance}; use render::{get_bbox, Renderable};
use sim::{CarID, DrawCarInput};
const CAR_WIDTH: f64 = 2.0; const CAR_WIDTH: f64 = 2.0;
// TODO should this live in editor/render?
pub struct DrawCar { pub struct DrawCar {
pub id: CarID, pub id: CarID,
body_polygon: Polygon, body_polygon: Polygon,
window_polygons: Vec<Polygon>, window_polygons: Vec<Polygon>,
// TODO ideally, draw the turn icon inside the car quad. how can we do that easily? // TODO ideally, draw the turn icon inside the car quad. how can we do that easily?
turn_arrow: Option<[f64; 4]>, turn_arrow: Option<[f64; 4]>,
front_pt: Pt2D,
// TODO maybe also draw lookahead buffer to know what the car is considering // TODO maybe also draw lookahead buffer to know what the car is considering
// TODO it would be really neat to project the stopping buffer onto the actual route that'll be // TODO it would be really neat to project the stopping buffer onto the actual route that'll be
// taken // taken
@ -25,43 +24,39 @@ pub struct DrawCar {
} }
impl DrawCar { impl DrawCar {
pub fn new( pub fn new(input: DrawCarInput, map: &Map) -> DrawCar {
id: CarID, let turn_arrow = if let Some(t) = input.waiting_for_turn {
vehicle: &Vehicle,
waiting_for_turn: Option<TurnID>,
map: &Map,
front: Pt2D,
angle: Angle,
stopping_dist: Distance,
) -> DrawCar {
let turn_arrow = if let Some(t) = waiting_for_turn {
let angle = map.get_t(t).line.angle(); let angle = map.get_t(t).line.angle();
let arrow_pt = front.project_away(vehicle.length.value_unsafe / 2.0, angle.opposite()); let arrow_pt = input
Some([arrow_pt.x(), arrow_pt.y(), front.x(), front.y()]) .front
.project_away(input.vehicle_length.value_unsafe / 2.0, angle.opposite());
Some([arrow_pt.x(), arrow_pt.y(), input.front.x(), input.front.y()])
} else { } else {
None None
}; };
let stopping_buffer_arrow = if stopping_dist == 0.0 * si::M { let stopping_buffer_arrow = if input.stopping_dist == 0.0 * si::M {
None None
} else { } else {
let arrow_pt = front.project_away(stopping_dist.value_unsafe, angle); let arrow_pt = input
Some([front.x(), front.y(), arrow_pt.x(), arrow_pt.y()]) .front
.project_away(input.stopping_dist.value_unsafe, input.angle);
Some([input.front.x(), input.front.y(), arrow_pt.x(), arrow_pt.y()])
}; };
let front_window_length_gap = 0.2; let front_window_length_gap = 0.2;
let front_window_thickness = 0.3; let front_window_thickness = 0.3;
DrawCar { DrawCar {
id: id, id: input.id,
turn_arrow, turn_arrow,
// TODO the rounded corners from graphics::Line::new_round look kind of cool though // TODO the rounded corners from graphics::Line::new_round look kind of cool though
body_polygon: geometry::thick_line_from_angle( body_polygon: geometry::thick_line_from_angle(
CAR_WIDTH, CAR_WIDTH,
vehicle.length.value_unsafe, input.vehicle_length.value_unsafe,
front, input.front,
// find the back of the car relative to the front // find the back of the car relative to the front
angle.opposite(), input.angle.opposite(),
), ),
// TODO it's way too hard to understand and tune this. just wait and stick in sprites // TODO it's way too hard to understand and tune this. just wait and stick in sprites
// or something. // or something.
@ -69,30 +64,44 @@ impl DrawCar {
geometry::thick_line_from_angle( geometry::thick_line_from_angle(
front_window_thickness, front_window_thickness,
CAR_WIDTH - 2.0 * front_window_length_gap, CAR_WIDTH - 2.0 * front_window_length_gap,
front.project_away(1.0, angle.opposite()).project_away( input
CAR_WIDTH / 2.0 - front_window_length_gap, .front
angle.rotate_degs(-90.0), .project_away(1.0, input.angle.opposite())
), .project_away(
angle.rotate_degs(90.0), CAR_WIDTH / 2.0 - front_window_length_gap,
input.angle.rotate_degs(-90.0),
),
input.angle.rotate_degs(90.0),
), ),
geometry::thick_line_from_angle( geometry::thick_line_from_angle(
front_window_thickness * 0.8, front_window_thickness * 0.8,
CAR_WIDTH - 2.0 * front_window_length_gap, CAR_WIDTH - 2.0 * front_window_length_gap,
front input
.project_away(vehicle.length.value_unsafe - 1.0, angle.opposite()) .front
.project_away(
input.vehicle_length.value_unsafe - 1.0,
input.angle.opposite(),
)
.project_away( .project_away(
CAR_WIDTH / 2.0 - front_window_length_gap, CAR_WIDTH / 2.0 - front_window_length_gap,
angle.rotate_degs(-90.0), input.angle.rotate_degs(-90.0),
), ),
angle.rotate_degs(90.0), input.angle.rotate_degs(90.0),
), ),
], ],
front_pt: front,
stopping_buffer_arrow, stopping_buffer_arrow,
} }
} }
}
pub fn draw(&self, g: &mut GfxCtx, color: graphics::types::Color) { impl Renderable for DrawCar {
type ID = CarID;
fn get_id(&self) -> CarID {
self.id
}
fn draw(&self, g: &mut GfxCtx, color: Color, _cs: &ColorScheme) {
g.draw_polygon(color, &self.body_polygon); g.draw_polygon(color, &self.body_polygon);
for p in &self.window_polygons { for p in &self.window_polygons {
g.draw_polygon([0.0, 0.0, 0.0, 1.0], p); g.draw_polygon([0.0, 0.0, 0.0, 1.0], p);
@ -116,11 +125,15 @@ impl DrawCar {
} }
} }
pub fn contains_pt(&self, pt: Pt2D) -> bool { fn get_bbox(&self) -> Rect {
get_bbox(&self.body_polygon.get_bounds())
}
fn contains_pt(&self, pt: Pt2D) -> bool {
self.body_polygon.contains_pt(pt) self.body_polygon.contains_pt(pt)
} }
pub fn focus_pt(&self) -> Pt2D { fn tooltip_lines(&self, _map: &Map) -> Vec<String> {
self.front_pt vec![self.id.to_string()]
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 // Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
mod building; mod building;
mod car;
mod extra_shape; mod extra_shape;
mod intersection; mod intersection;
mod lane; mod lane;
@ -15,6 +16,7 @@ use ezgui::GfxCtx;
use geom::{Bounds, Pt2D}; use geom::{Bounds, Pt2D};
use graphics::types::Color; use graphics::types::Color;
use map_model::{geometry, Map}; use map_model::{geometry, Map};
pub use render::car::DrawCar;
pub use render::lane::DrawLane; pub use render::lane::DrawLane;
pub use render::map::DrawMap; pub use render::map::DrawMap;
pub use render::pedestrian::DrawPedestrian; pub use render::pedestrian::DrawPedestrian;

View File

@ -337,7 +337,8 @@ impl UI {
Vec::new() Vec::new()
}; };
for l in &lanes_onscreen { for l in &lanes_onscreen {
for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) { for c in self.sim.get_draw_cars_on_lane(l.id, &self.map).into_iter() {
let c = render::DrawCar::new(c, &self.map);
if c.contains_pt(pt) { if c.contains_pt(pt) {
return Some(ID::Car(c.id)); return Some(ID::Car(c.id));
} }
@ -361,7 +362,8 @@ impl UI {
return Some(ID::Turn(*t)); return Some(ID::Turn(*t));
} }
for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) { for c in self.sim.get_draw_cars_on_turn(*t, &self.map).into_iter() {
let c = render::DrawCar::new(c, &self.map);
if c.contains_pt(pt) { if c.contains_pt(pt) {
return Some(ID::Car(c.id)); return Some(ID::Car(c.id));
} }
@ -655,8 +657,9 @@ impl UI {
.get_t(*t) .get_t(*t)
.draw(g, self.color_turn_icon(*t), &self.cs); .draw(g, self.color_turn_icon(*t), &self.cs);
} }
for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) { for c in self.sim.get_draw_cars_on_turn(*t, &self.map).into_iter() {
c.draw(g, self.color_car(c.id)); let c = render::DrawCar::new(c, &self.map);
c.draw(g, self.color_car(c.id), &self.cs);
} }
for p in self.sim.get_draw_peds_on_turn(*t, &self.map).into_iter() { for p in self.sim.get_draw_peds_on_turn(*t, &self.map).into_iter() {
let p = render::DrawPedestrian::new(p, &self.map); let p = render::DrawPedestrian::new(p, &self.map);
@ -689,8 +692,9 @@ impl UI {
} }
for l in &lanes_onscreen { for l in &lanes_onscreen {
for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) { for c in self.sim.get_draw_cars_on_lane(l.id, &self.map).into_iter() {
c.draw(g, self.color_car(c.id)); let c = render::DrawCar::new(c, &self.map);
c.draw(g, self.color_car(c.id), &self.cs);
} }
for p in self.sim.get_draw_peds_on_lane(l.id, &self.map).into_iter() { for p in self.sim.get_draw_peds_on_lane(l.id, &self.map).into_iter() {
let p = render::DrawPedestrian::new(p, &self.map); let p = render::DrawPedestrian::new(p, &self.map);

View File

@ -1,7 +1,6 @@
use abstutil; use abstutil;
use abstutil::{deserialize_btreemap, serialize_btreemap}; use abstutil::{deserialize_btreemap, serialize_btreemap};
use dimensioned::si; use dimensioned::si;
use draw_car::DrawCar;
use failure::{Error, ResultExt}; use failure::{Error, ResultExt};
use geom::EPSILON_DIST; use geom::EPSILON_DIST;
use intersections::{IntersectionSimState, Request}; use intersections::{IntersectionSimState, Request};
@ -19,8 +18,8 @@ use std::collections::BTreeMap;
use transit::TransitSimState; use transit::TransitSimState;
use view::{AgentView, WorldView}; use view::{AgentView, WorldView};
use { use {
Acceleration, AgentID, CarID, CarState, Distance, Event, InvariantViolated, On, ParkedCar, Acceleration, AgentID, CarID, CarState, Distance, DrawCarInput, Event, InvariantViolated, On,
ParkingSpot, Speed, Tick, Time, ParkedCar, ParkingSpot, Speed, Tick, Time,
}; };
const TIME_TO_PARK_OR_DEPART: Time = si::Second { const TIME_TO_PARK_OR_DEPART: Time = si::Second {
@ -397,7 +396,7 @@ impl SimQueue {
map: &Map, map: &Map,
time: Tick, time: Tick,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Vec<DrawCar> { ) -> Vec<DrawCarInput> {
let mut results = Vec::new(); let mut results = Vec::new();
for (_, id) in &self.cars_queue { for (_, id) in &self.cars_queue {
results.push(sim.get_draw_car(*id, time, map, properties).unwrap()) results.push(sim.get_draw_car(*id, time, map, properties).unwrap())
@ -767,7 +766,7 @@ impl DrivingSimState {
time: Tick, time: Tick,
map: &Map, map: &Map,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Option<DrawCar> { ) -> Option<DrawCarInput> {
let c = self.cars.get(&id)?; let c = self.cars.get(&id)?;
let (base_pos, angle) = c.on.dist_along(c.dist_along, map); let (base_pos, angle) = c.on.dist_along(c.dist_along, map);
let vehicle = &properties[&id]; let vehicle = &properties[&id];
@ -789,15 +788,14 @@ impl DrivingSimState {
base_pos base_pos
}; };
Some(DrawCar::new( Some(DrawCarInput {
c.id, id: c.id,
vehicle, vehicle_length: vehicle.length,
c.waiting_for.and_then(|on| on.maybe_turn()), waiting_for_turn: c.waiting_for.and_then(|on| on.maybe_turn()),
map, front: pos,
pos,
angle, angle,
stopping_dist, stopping_dist,
)) })
} }
pub fn get_draw_cars_on_lane( pub fn get_draw_cars_on_lane(
@ -806,7 +804,7 @@ impl DrivingSimState {
time: Tick, time: Tick,
map: &Map, map: &Map,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Vec<DrawCar> { ) -> Vec<DrawCarInput> {
self.lanes[lane.0].get_draw_cars(self, map, time, properties) self.lanes[lane.0].get_draw_cars(self, map, time, properties)
} }
@ -816,7 +814,7 @@ impl DrivingSimState {
time: Tick, time: Tick,
map: &Map, map: &Map,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Vec<DrawCar> { ) -> Vec<DrawCarInput> {
if let Some(queue) = self.turns.get(&turn) { if let Some(queue) = self.turns.get(&turn) {
return queue.get_draw_cars(self, map, time, properties); return queue.get_draw_cars(self, map, time, properties);
} }

View File

@ -27,7 +27,6 @@ extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
mod draw_car;
mod driving; mod driving;
mod events; mod events;
mod helpers; mod helpers;
@ -352,3 +351,12 @@ pub struct DrawPedestrianInput {
pub pos: Pt2D, pub pos: Pt2D,
pub waiting_for_turn: Option<TurnID>, pub waiting_for_turn: Option<TurnID>,
} }
pub struct DrawCarInput {
pub id: CarID,
pub vehicle_length: Distance,
pub waiting_for_turn: Option<TurnID>,
pub front: Pt2D,
pub angle: Angle,
pub stopping_dist: Distance,
}

View File

@ -1,12 +1,11 @@
use dimensioned::si; use dimensioned::si;
use draw_car::DrawCar;
use geom::{Angle, Pt2D}; use geom::{Angle, Pt2D};
use kinematics::Vehicle; use kinematics::Vehicle;
use map_model; use map_model;
use map_model::{Lane, LaneID, LaneType, Map}; use map_model::{Lane, LaneID, LaneType, Map};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::iter; use std::iter;
use {CarID, Distance, ParkedCar, ParkingSpot}; use {CarID, Distance, DrawCarInput, ParkedCar, ParkingSpot};
#[derive(Serialize, Deserialize, PartialEq, Eq)] #[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct ParkingSimState { pub struct ParkingSimState {
@ -75,24 +74,20 @@ impl ParkingSimState {
pub fn get_draw_cars( pub fn get_draw_cars(
&self, &self,
id: LaneID, id: LaneID,
map: &Map,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Vec<DrawCar> { ) -> Vec<DrawCarInput> {
self.lanes[id.0].get_draw_cars(map, properties) self.lanes[id.0].get_draw_cars(properties)
} }
pub fn get_draw_car( pub fn get_draw_car(
&self, &self,
id: CarID, id: CarID,
map: &Map,
properties: &BTreeMap<CarID, Vehicle>, properties: &BTreeMap<CarID, Vehicle>,
) -> Option<DrawCar> { ) -> Option<DrawCarInput> {
// TODO this is so horrendously slow :D // TODO this is so horrendously slow :D
for l in &self.lanes { for l in &self.lanes {
if l.occupants.contains(&Some(id)) { if l.occupants.contains(&Some(id)) {
return l.get_draw_cars(map, properties) return l.get_draw_cars(properties).into_iter().find(|c| c.id == id);
.into_iter()
.find(|c| c.id == id);
} }
} }
None None
@ -196,7 +191,7 @@ impl ParkingLane {
self.occupants[idx] = None; self.occupants[idx] = None;
} }
fn get_draw_cars(&self, map: &Map, properties: &BTreeMap<CarID, Vehicle>) -> Vec<DrawCar> { fn get_draw_cars(&self, properties: &BTreeMap<CarID, Vehicle>) -> Vec<DrawCarInput> {
self.occupants self.occupants
.iter() .iter()
.enumerate() .enumerate()
@ -204,15 +199,14 @@ impl ParkingLane {
maybe_id.and_then(|id| { maybe_id.and_then(|id| {
let vehicle = &properties[&id]; let vehicle = &properties[&id];
let (front, angle) = self.spots[idx].front_of_car(vehicle); let (front, angle) = self.spots[idx].front_of_car(vehicle);
Some(DrawCar::new( Some(DrawCarInput {
id, id: id,
vehicle, vehicle_length: vehicle.length,
None, waiting_for_turn: None,
map, front: front,
front, angle: angle,
angle, stopping_dist: 0.0 * si::M,
0.0 * si::M, })
))
}) })
}) })
.collect() .collect()

View File

@ -3,7 +3,6 @@
use abstutil; use abstutil;
use control::ControlMap; use control::ControlMap;
use dimensioned::si; use dimensioned::si;
use draw_car::DrawCar;
use driving::DrivingSimState; use driving::DrivingSimState;
use failure::Error; use failure::Error;
use instrument::capture_backtrace; use instrument::capture_backtrace;
@ -22,7 +21,10 @@ use transit::TransitSimState;
use trips::TripManager; use trips::TripManager;
use view::WorldView; use view::WorldView;
use walking::WalkingSimState; use walking::WalkingSimState;
use {AgentID, CarID, CarState, DrawPedestrianInput, Event, PedestrianID, Tick, TIMESTEP}; use {
AgentID, CarID, CarState, DrawCarInput, DrawPedestrianInput, Event, PedestrianID, Tick,
TIMESTEP,
};
#[derive(Serialize, Deserialize, Derivative)] #[derive(Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Eq)] #[derivative(PartialEq, Eq)]
@ -224,13 +226,10 @@ impl Sim {
self.driving_state.get_car_state(c) self.driving_state.get_car_state(c)
} }
pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCar> { pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCarInput> {
self.driving_state self.driving_state
.get_draw_car(id, self.time, map, &self.car_properties) .get_draw_car(id, self.time, map, &self.car_properties)
.or_else(|| { .or_else(|| self.parking_state.get_draw_car(id, &self.car_properties))
self.parking_state
.get_draw_car(id, map, &self.car_properties)
})
} }
pub fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option<DrawPedestrianInput> { pub fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option<DrawPedestrianInput> {
@ -238,20 +237,19 @@ impl Sim {
} }
// TODO maybe just DrawAgent instead? should caller care? // TODO maybe just DrawAgent instead? should caller care?
pub fn get_draw_cars_on_lane(&self, l: LaneID, map: &Map) -> Vec<DrawCar> { pub fn get_draw_cars_on_lane(&self, l: LaneID, map: &Map) -> Vec<DrawCarInput> {
match map.get_l(l).lane_type { match map.get_l(l).lane_type {
LaneType::Driving => { LaneType::Driving => {
self.driving_state self.driving_state
.get_draw_cars_on_lane(l, self.time, map, &self.car_properties) .get_draw_cars_on_lane(l, self.time, map, &self.car_properties)
} }
LaneType::Parking => self.parking_state LaneType::Parking => self.parking_state.get_draw_cars(l, &self.car_properties),
.get_draw_cars(l, map, &self.car_properties),
LaneType::Sidewalk => Vec::new(), LaneType::Sidewalk => Vec::new(),
LaneType::Biking => Vec::new(), LaneType::Biking => Vec::new(),
} }
} }
pub fn get_draw_cars_on_turn(&self, t: TurnID, map: &Map) -> Vec<DrawCar> { pub fn get_draw_cars_on_turn(&self, t: TurnID, map: &Map) -> Vec<DrawCarInput> {
self.driving_state self.driving_state
.get_draw_cars_on_turn(t, self.time, map, &self.car_properties) .get_draw_cars_on_turn(t, self.time, map, &self.car_properties)
} }