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