map_gui/render/
parking_lot.rs

1use 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        // Trim the front path line away from the sidewalk's center line, so that it doesn't
56        // overlap. For now, this cleanup is visual; it doesn't belong in the map_model layer.
57        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        // TODO This isn't getting clipped to the parking lot boundary properly, so just stick
67        // this on the lowest order for now.
68        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}