diff --git a/docs/design.md b/docs/design.md index 1f5b614d78..500849308c 100644 --- a/docs/design.md +++ b/docs/design.md @@ -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) - bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow - - 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. + = 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. - then rethink colors, with simplified single plugin = then finally try out a unified quadtree! diff --git a/editor/src/plugins/follow.rs b/editor/src/plugins/follow.rs index 1d48e086b7..978f47fe57 100644 --- a/editor/src/plugins/follow.rs +++ b/editor/src/plugins/follow.rs @@ -45,7 +45,7 @@ impl FollowState { // instead FollowState::FollowingCar(id) => { 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()); input.key_pressed(Key::Return, "stop following") } else { diff --git a/editor/src/plugins/warp.rs b/editor/src/plugins/warp.rs index edb53b4d85..dac547c09e 100644 --- a/editor/src/plugins/warp.rs +++ b/editor/src/plugins/warp.rs @@ -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) { println!("Warping to {}", id); *selected = Some(ID::Car(id)); - c.focus_pt() + c.front } else { println!("{} doesn't exist", id); return; diff --git a/sim/src/draw_car.rs b/editor/src/render/car.rs similarity index 53% rename from sim/src/draw_car.rs rename to editor/src/render/car.rs index d43e204b86..40afe1c98b 100644 --- a/sim/src/draw_car.rs +++ b/editor/src/render/car.rs @@ -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 ezgui::GfxCtx; -use geom::{Angle, Polygon, Pt2D}; +use geom::{Polygon, Pt2D}; use graphics; -use kinematics::Vehicle; -use map_model::{geometry, Map, TurnID}; -use {CarID, Distance}; +use graphics::types::Color; +use map_model::{geometry, Map}; +use render::{get_bbox, Renderable}; +use sim::{CarID, DrawCarInput}; const CAR_WIDTH: f64 = 2.0; -// TODO should this live in editor/render? pub struct DrawCar { pub id: CarID, body_polygon: Polygon, window_polygons: Vec, // TODO ideally, draw the turn icon inside the car quad. how can we do that easily? turn_arrow: Option<[f64; 4]>, - front_pt: Pt2D, // 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 // taken @@ -25,43 +24,39 @@ pub struct DrawCar { } impl DrawCar { - pub fn new( - id: CarID, - vehicle: &Vehicle, - waiting_for_turn: Option, - map: &Map, - front: Pt2D, - angle: Angle, - stopping_dist: Distance, - ) -> DrawCar { - let turn_arrow = if let Some(t) = waiting_for_turn { + pub fn new(input: DrawCarInput, map: &Map) -> DrawCar { + let turn_arrow = if let Some(t) = input.waiting_for_turn { let angle = map.get_t(t).line.angle(); - let arrow_pt = front.project_away(vehicle.length.value_unsafe / 2.0, angle.opposite()); - Some([arrow_pt.x(), arrow_pt.y(), front.x(), front.y()]) + let arrow_pt = input + .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 { 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 } else { - let arrow_pt = front.project_away(stopping_dist.value_unsafe, angle); - Some([front.x(), front.y(), arrow_pt.x(), arrow_pt.y()]) + let arrow_pt = input + .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_thickness = 0.3; DrawCar { - id: id, + id: input.id, turn_arrow, // TODO the rounded corners from graphics::Line::new_round look kind of cool though body_polygon: geometry::thick_line_from_angle( CAR_WIDTH, - vehicle.length.value_unsafe, - front, + input.vehicle_length.value_unsafe, + input.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 // or something. @@ -69,30 +64,44 @@ impl DrawCar { geometry::thick_line_from_angle( front_window_thickness, CAR_WIDTH - 2.0 * front_window_length_gap, - front.project_away(1.0, angle.opposite()).project_away( - CAR_WIDTH / 2.0 - front_window_length_gap, - angle.rotate_degs(-90.0), - ), - angle.rotate_degs(90.0), + input + .front + .project_away(1.0, input.angle.opposite()) + .project_away( + 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( front_window_thickness * 0.8, CAR_WIDTH - 2.0 * front_window_length_gap, - front - .project_away(vehicle.length.value_unsafe - 1.0, angle.opposite()) + input + .front + .project_away( + input.vehicle_length.value_unsafe - 1.0, + input.angle.opposite(), + ) .project_away( 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, } } +} - 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); for p in &self.window_polygons { 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) } - pub fn focus_pt(&self) -> Pt2D { - self.front_pt + fn tooltip_lines(&self, _map: &Map) -> Vec { + vec![self.id.to_string()] } } diff --git a/editor/src/render/mod.rs b/editor/src/render/mod.rs index bb69488a3b..b1806e0372 100644 --- a/editor/src/render/mod.rs +++ b/editor/src/render/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 mod building; +mod car; mod extra_shape; mod intersection; mod lane; @@ -15,6 +16,7 @@ use ezgui::GfxCtx; use geom::{Bounds, Pt2D}; use graphics::types::Color; use map_model::{geometry, Map}; +pub use render::car::DrawCar; pub use render::lane::DrawLane; pub use render::map::DrawMap; pub use render::pedestrian::DrawPedestrian; diff --git a/editor/src/ui.rs b/editor/src/ui.rs index b547d3d2fe..1ddef172bc 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -337,7 +337,8 @@ impl UI { Vec::new() }; 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) { return Some(ID::Car(c.id)); } @@ -361,7 +362,8 @@ impl UI { 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) { return Some(ID::Car(c.id)); } @@ -655,8 +657,9 @@ impl UI { .get_t(*t) .draw(g, self.color_turn_icon(*t), &self.cs); } - for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) { - c.draw(g, self.color_car(c.id)); + for c in self.sim.get_draw_cars_on_turn(*t, &self.map).into_iter() { + 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() { let p = render::DrawPedestrian::new(p, &self.map); @@ -689,8 +692,9 @@ impl UI { } for l in &lanes_onscreen { - for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) { - c.draw(g, self.color_car(c.id)); + for c in self.sim.get_draw_cars_on_lane(l.id, &self.map).into_iter() { + 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() { let p = render::DrawPedestrian::new(p, &self.map); diff --git a/sim/src/driving.rs b/sim/src/driving.rs index 28b4718bec..e873fbdd71 100644 --- a/sim/src/driving.rs +++ b/sim/src/driving.rs @@ -1,7 +1,6 @@ use abstutil; use abstutil::{deserialize_btreemap, serialize_btreemap}; use dimensioned::si; -use draw_car::DrawCar; use failure::{Error, ResultExt}; use geom::EPSILON_DIST; use intersections::{IntersectionSimState, Request}; @@ -19,8 +18,8 @@ use std::collections::BTreeMap; use transit::TransitSimState; use view::{AgentView, WorldView}; use { - Acceleration, AgentID, CarID, CarState, Distance, Event, InvariantViolated, On, ParkedCar, - ParkingSpot, Speed, Tick, Time, + Acceleration, AgentID, CarID, CarState, Distance, DrawCarInput, Event, InvariantViolated, On, + ParkedCar, ParkingSpot, Speed, Tick, Time, }; const TIME_TO_PARK_OR_DEPART: Time = si::Second { @@ -397,7 +396,7 @@ impl SimQueue { map: &Map, time: Tick, properties: &BTreeMap, - ) -> Vec { + ) -> Vec { let mut results = Vec::new(); for (_, id) in &self.cars_queue { results.push(sim.get_draw_car(*id, time, map, properties).unwrap()) @@ -767,7 +766,7 @@ impl DrivingSimState { time: Tick, map: &Map, properties: &BTreeMap, - ) -> Option { + ) -> Option { let c = self.cars.get(&id)?; let (base_pos, angle) = c.on.dist_along(c.dist_along, map); let vehicle = &properties[&id]; @@ -789,15 +788,14 @@ impl DrivingSimState { base_pos }; - Some(DrawCar::new( - c.id, - vehicle, - c.waiting_for.and_then(|on| on.maybe_turn()), - map, - pos, + Some(DrawCarInput { + id: c.id, + vehicle_length: vehicle.length, + waiting_for_turn: c.waiting_for.and_then(|on| on.maybe_turn()), + front: pos, angle, stopping_dist, - )) + }) } pub fn get_draw_cars_on_lane( @@ -806,7 +804,7 @@ impl DrivingSimState { time: Tick, map: &Map, properties: &BTreeMap, - ) -> Vec { + ) -> Vec { self.lanes[lane.0].get_draw_cars(self, map, time, properties) } @@ -816,7 +814,7 @@ impl DrivingSimState { time: Tick, map: &Map, properties: &BTreeMap, - ) -> Vec { + ) -> Vec { if let Some(queue) = self.turns.get(&turn) { return queue.get_draw_cars(self, map, time, properties); } diff --git a/sim/src/lib.rs b/sim/src/lib.rs index ef696a2b03..b3851400c1 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -27,7 +27,6 @@ extern crate serde; #[macro_use] extern crate serde_derive; -mod draw_car; mod driving; mod events; mod helpers; @@ -352,3 +351,12 @@ pub struct DrawPedestrianInput { pub pos: Pt2D, pub waiting_for_turn: Option, } + +pub struct DrawCarInput { + pub id: CarID, + pub vehicle_length: Distance, + pub waiting_for_turn: Option, + pub front: Pt2D, + pub angle: Angle, + pub stopping_dist: Distance, +} diff --git a/sim/src/parking.rs b/sim/src/parking.rs index 965b682773..b5e6be23eb 100644 --- a/sim/src/parking.rs +++ b/sim/src/parking.rs @@ -1,12 +1,11 @@ use dimensioned::si; -use draw_car::DrawCar; use geom::{Angle, Pt2D}; use kinematics::Vehicle; use map_model; use map_model::{Lane, LaneID, LaneType, Map}; use std::collections::BTreeMap; use std::iter; -use {CarID, Distance, ParkedCar, ParkingSpot}; +use {CarID, Distance, DrawCarInput, ParkedCar, ParkingSpot}; #[derive(Serialize, Deserialize, PartialEq, Eq)] pub struct ParkingSimState { @@ -75,24 +74,20 @@ impl ParkingSimState { pub fn get_draw_cars( &self, id: LaneID, - map: &Map, properties: &BTreeMap, - ) -> Vec { - self.lanes[id.0].get_draw_cars(map, properties) + ) -> Vec { + self.lanes[id.0].get_draw_cars(properties) } pub fn get_draw_car( &self, id: CarID, - map: &Map, properties: &BTreeMap, - ) -> Option { + ) -> Option { // TODO this is so horrendously slow :D for l in &self.lanes { if l.occupants.contains(&Some(id)) { - return l.get_draw_cars(map, properties) - .into_iter() - .find(|c| c.id == id); + return l.get_draw_cars(properties).into_iter().find(|c| c.id == id); } } None @@ -196,7 +191,7 @@ impl ParkingLane { self.occupants[idx] = None; } - fn get_draw_cars(&self, map: &Map, properties: &BTreeMap) -> Vec { + fn get_draw_cars(&self, properties: &BTreeMap) -> Vec { self.occupants .iter() .enumerate() @@ -204,15 +199,14 @@ impl ParkingLane { maybe_id.and_then(|id| { let vehicle = &properties[&id]; let (front, angle) = self.spots[idx].front_of_car(vehicle); - Some(DrawCar::new( - id, - vehicle, - None, - map, - front, - angle, - 0.0 * si::M, - )) + Some(DrawCarInput { + id: id, + vehicle_length: vehicle.length, + waiting_for_turn: None, + front: front, + angle: angle, + stopping_dist: 0.0 * si::M, + }) }) }) .collect() diff --git a/sim/src/sim.rs b/sim/src/sim.rs index 73a731b05d..b74e0ea6a5 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -3,7 +3,6 @@ use abstutil; use control::ControlMap; use dimensioned::si; -use draw_car::DrawCar; use driving::DrivingSimState; use failure::Error; use instrument::capture_backtrace; @@ -22,7 +21,10 @@ use transit::TransitSimState; use trips::TripManager; use view::WorldView; 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)] #[derivative(PartialEq, Eq)] @@ -224,13 +226,10 @@ impl Sim { self.driving_state.get_car_state(c) } - pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option { + pub fn get_draw_car(&self, id: CarID, map: &Map) -> Option { self.driving_state .get_draw_car(id, self.time, map, &self.car_properties) - .or_else(|| { - self.parking_state - .get_draw_car(id, map, &self.car_properties) - }) + .or_else(|| self.parking_state.get_draw_car(id, &self.car_properties)) } pub fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option { @@ -238,20 +237,19 @@ impl Sim { } // TODO maybe just DrawAgent instead? should caller care? - pub fn get_draw_cars_on_lane(&self, l: LaneID, map: &Map) -> Vec { + pub fn get_draw_cars_on_lane(&self, l: LaneID, map: &Map) -> Vec { match map.get_l(l).lane_type { LaneType::Driving => { self.driving_state .get_draw_cars_on_lane(l, self.time, map, &self.car_properties) } - LaneType::Parking => self.parking_state - .get_draw_cars(l, map, &self.car_properties), + LaneType::Parking => self.parking_state.get_draw_cars(l, &self.car_properties), LaneType::Sidewalk => Vec::new(), LaneType::Biking => Vec::new(), } } - pub fn get_draw_cars_on_turn(&self, t: TurnID, map: &Map) -> Vec { + pub fn get_draw_cars_on_turn(&self, t: TurnID, map: &Map) -> Vec { self.driving_state .get_draw_cars_on_turn(t, self.time, map, &self.car_properties) }