1 use crate::app::App; 2 use crate::colors::ColorScheme; 3 use crate::helpers::ID; 4 use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS}; 5 use geom::{Distance, PolyLine, Polygon, Pt2D}; 6 use map_model::{Map, ParkingLot, ParkingLotID, NORMAL_LANE_THICKNESS, PARKING_LOT_SPOT_LENGTH}; 7 use std::cell::RefCell; 8 use widgetry::{Drawable, EventCtx, GeomBatch, GfxCtx}; 9 10 pub struct DrawParkingLot { 11 pub id: ParkingLotID, 12 draw: RefCell<Option<Drawable>>, 13 } 14 15 impl DrawParkingLot { new( ctx: &EventCtx, lot: &ParkingLot, cs: &ColorScheme, unzoomed_batch: &mut GeomBatch, ) -> DrawParkingLot16 pub fn new( 17 ctx: &EventCtx, 18 lot: &ParkingLot, 19 cs: &ColorScheme, 20 unzoomed_batch: &mut GeomBatch, 21 ) -> DrawParkingLot { 22 unzoomed_batch.push(cs.parking_lot, lot.polygon.clone()); 23 for aisle in &lot.aisles { 24 let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0; 25 unzoomed_batch.push( 26 cs.unzoomed_residential, 27 PolyLine::unchecked_new(aisle.clone()).make_polygons(aisle_thickness), 28 ); 29 } 30 unzoomed_batch.append( 31 GeomBatch::load_svg(ctx.prerender, "system/assets/map/parking.svg") 32 .scale(0.05) 33 .centered_on(lot.polygon.polylabel()), 34 ); 35 36 DrawParkingLot { 37 id: lot.id, 38 draw: RefCell::new(None), 39 } 40 } 41 } 42 43 impl Renderable for DrawParkingLot { get_id(&self) -> ID44 fn get_id(&self) -> ID { 45 ID::ParkingLot(self.id) 46 } 47 draw(&self, g: &mut GfxCtx, app: &App, _: &DrawOptions)48 fn draw(&self, g: &mut GfxCtx, app: &App, _: &DrawOptions) { 49 let mut draw = self.draw.borrow_mut(); 50 if draw.is_none() { 51 let lot = app.primary.map.get_pl(self.id); 52 53 // Trim the front path line away from the sidewalk's center line, so that it doesn't 54 // overlap. For now, this cleanup is visual; it doesn't belong in the map_model layer. 55 let orig_line = &lot.sidewalk_line; 56 let front_path_line = orig_line 57 .slice( 58 Distance::ZERO, 59 orig_line.length() - app.primary.map.get_l(lot.sidewalk_pos.lane()).width / 2.0, 60 ) 61 .unwrap_or_else(|| orig_line.clone()); 62 63 let mut batch = GeomBatch::new(); 64 // TODO This isn't getting clipped to the parking lot boundary properly, so just stick 65 // this on the lowest order for now. 66 batch.push( 67 app.cs.sidewalk, 68 front_path_line.make_polygons(NORMAL_LANE_THICKNESS), 69 ); 70 batch.push(app.cs.parking_lot, lot.polygon.clone()); 71 for aisle in &lot.aisles { 72 let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0; 73 batch.push( 74 app.cs.driving_lane, 75 PolyLine::unchecked_new(aisle.clone()).make_polygons(aisle_thickness), 76 ); 77 } 78 let width = NORMAL_LANE_THICKNESS; 79 let height = PARKING_LOT_SPOT_LENGTH; 80 for (pt, angle) in &lot.spots { 81 let left = pt.project_away(width / 2.0, angle.rotate_degs(90.0)); 82 let right = pt.project_away(width / 2.0, angle.rotate_degs(-90.0)); 83 84 batch.push( 85 app.cs.general_road_marking, 86 PolyLine::must_new(vec![ 87 left.project_away(height, *angle), 88 left, 89 right, 90 right.project_away(height, *angle), 91 ]) 92 .make_polygons(Distance::meters(0.25)), 93 ); 94 } 95 96 *draw = Some(g.upload(batch)); 97 } 98 g.redraw(draw.as_ref().unwrap()); 99 } 100 get_zorder(&self) -> isize101 fn get_zorder(&self) -> isize { 102 0 103 } 104 get_outline(&self, map: &Map) -> Polygon105 fn get_outline(&self, map: &Map) -> Polygon { 106 let pl = map.get_pl(self.id); 107 if let Ok(p) = pl.polygon.to_outline(OUTLINE_THICKNESS) { 108 p 109 } else { 110 pl.polygon.clone() 111 } 112 } 113 contains_pt(&self, pt: Pt2D, map: &Map) -> bool114 fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { 115 map.get_pl(self.id).polygon.contains_pt(pt) 116 } 117 } 118