map_gui/render/
parking_lot.rs1use std::cell::RefCell;
2
3use geom::{Bounds, Distance, PolyLine, Pt2D, Tessellation};
4use map_model::{
5 osm, LaneType, Map, ParkingLot, ParkingLotID, NORMAL_LANE_THICKNESS, PARKING_LOT_SPOT_LENGTH,
6};
7use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, RewriteColor};
8
9use crate::colors::ColorScheme;
10use crate::options::Options;
11use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS};
12use crate::{AppLike, ID};
13
14pub struct DrawParkingLot {
15 pub id: ParkingLotID,
16 draw: RefCell<Option<Drawable>>,
17}
18
19impl DrawParkingLot {
20 pub fn new(
21 ctx: &EventCtx,
22 lot: &ParkingLot,
23 cs: &ColorScheme,
24 opts: &Options,
25 unzoomed_batch: &mut GeomBatch,
26 ) -> DrawParkingLot {
27 unzoomed_batch.push(cs.parking_lot, lot.polygon.clone());
28 for aisle in &lot.aisles {
29 let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
30 unzoomed_batch.push(
31 cs.unzoomed_road_surface(osm::RoadRank::Local),
32 PolyLine::unchecked_new(aisle.clone()).thicken_tessellation(aisle_thickness),
33 );
34 }
35 unzoomed_batch.append(
36 GeomBatch::load_svg(ctx, "system/assets/map/parking.svg")
37 .scale(0.05)
38 .centered_on(lot.polygon.polylabel())
39 .color(if opts.simplify_basemap {
40 RewriteColor::Change(Color::hex("#204A87"), cs.parking_lot)
41 } else {
42 RewriteColor::NoOp
43 }),
44 );
45
46 DrawParkingLot {
47 id: lot.id,
48 draw: RefCell::new(None),
49 }
50 }
51
52 pub fn render(&self, app: &dyn AppLike) -> GeomBatch {
53 let lot = app.map().get_pl(self.id);
54
55 let orig_line = &lot.sidewalk_line;
58 let front_path_line = orig_line
59 .slice(
60 Distance::ZERO,
61 orig_line.length() - app.map().get_l(lot.sidewalk_pos.lane()).width / 2.0,
62 )
63 .unwrap_or_else(|_| orig_line.clone());
64
65 let mut batch = GeomBatch::new();
66 let rank = app.map().get_parent(lot.sidewalk_pos.lane()).get_rank();
69 batch.push(
70 app.cs().zoomed_road_surface(LaneType::Sidewalk, rank),
71 front_path_line.make_polygons(NORMAL_LANE_THICKNESS),
72 );
73 batch.push(app.cs().parking_lot, lot.polygon.clone());
74 for aisle in &lot.aisles {
75 let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
76 batch.push(
77 app.cs()
78 .zoomed_road_surface(LaneType::Driving, osm::RoadRank::Local),
79 PolyLine::unchecked_new(aisle.clone()).thicken_tessellation(aisle_thickness),
80 );
81 }
82 let width = NORMAL_LANE_THICKNESS;
83 let height = PARKING_LOT_SPOT_LENGTH;
84 for (pt, angle) in &lot.spots {
85 let left = pt.project_away(width / 2.0, angle.rotate_degs(90.0));
86 let right = pt.project_away(width / 2.0, angle.rotate_degs(-90.0));
87
88 batch.push(
89 app.cs().general_road_marking,
90 PolyLine::must_new(vec![
91 left.project_away(height, *angle),
92 left,
93 right,
94 right.project_away(height, *angle),
95 ])
96 .make_polygons(Distance::meters(0.25)),
97 );
98 }
99
100 batch
101 }
102
103 pub fn clear_rendering(&self) {
104 *self.draw.borrow_mut() = None;
105 }
106}
107
108impl Renderable for DrawParkingLot {
109 fn get_id(&self) -> ID {
110 ID::ParkingLot(self.id)
111 }
112
113 fn draw(&self, g: &mut GfxCtx, app: &dyn AppLike, _: &DrawOptions) {
114 let mut draw = self.draw.borrow_mut();
115 if draw.is_none() {
116 *draw = Some(g.upload(self.render(app)));
117 }
118 g.redraw(draw.as_ref().unwrap());
119 }
120
121 fn get_zorder(&self) -> isize {
122 0
123 }
124
125 fn get_outline(&self, map: &Map) -> Tessellation {
126 map.get_pl(self.id).polygon.to_outline(OUTLINE_THICKNESS)
127 }
128
129 fn get_bounds(&self, map: &Map) -> Bounds {
130 map.get_pl(self.id).polygon.get_bounds()
131 }
132
133 fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool {
134 map.get_pl(self.id).polygon.contains_pt(pt)
135 }
136}