santa/
buildings.rs

1use std::collections::{HashMap, HashSet};
2
3use map_model::{AmenityType, BuildingID, BuildingType};
4use widgetry::{Color, Drawable, EventCtx, GeomBatch, Line, Text};
5
6use crate::App;
7
8pub struct Buildings {
9    // Every building in the map is here, to simplify lookup logic.
10    pub buildings: HashMap<BuildingID, BldgState>,
11    // This an unchanging base layer that can get covered up by drawing on top of it. Maybe we
12    // could even replace the one in DrawMap.
13    pub draw_all: Drawable,
14    pub total_housing_units: usize,
15    pub upzones: HashSet<BuildingID>,
16}
17
18#[derive(Clone)]
19pub enum BldgState {
20    // Score
21    Undelivered(usize),
22    Store,
23    Done,
24    // Not a relevant building
25    Ignore,
26}
27
28impl Buildings {
29    pub fn new(ctx: &mut EventCtx, app: &App, upzones: HashSet<BuildingID>) -> Buildings {
30        let colors = &app.session.colors;
31
32        let mut buildings = HashMap::new();
33        let mut total_housing_units = 0;
34        let mut batch = GeomBatch::new();
35        for b in app.map.all_buildings() {
36            if upzones.contains(&b.id) {
37                buildings.insert(b.id, BldgState::Store);
38                batch.push(colors.store, b.polygon.clone());
39                batch.append(
40                    Text::from("Upzoned")
41                        .render_autocropped(ctx)
42                        .scale(0.1)
43                        .centered_on(b.label_center),
44                );
45                continue;
46            }
47
48            if let BuildingType::Residential {
49                num_housing_units, ..
50            } = b.bldg_type
51            {
52                // There are some unused commercial buildings around!
53                if num_housing_units > 0 {
54                    buildings.insert(b.id, BldgState::Undelivered(num_housing_units));
55                    total_housing_units += num_housing_units;
56
57                    let color = if num_housing_units > 5 {
58                        colors.apartment
59                    } else {
60                        colors.house
61                    };
62                    batch.push(color, b.polygon.clone());
63                    // Call out non-single family homes
64                    if num_housing_units > 1 {
65                        batch.append(
66                            Text::from(Line(num_housing_units.to_string()).fg(Color::BLACK))
67                                .render_autocropped(ctx)
68                                .scale(0.2)
69                                .centered_on(b.label_center),
70                        );
71                    }
72                    continue;
73                }
74            } else if let Some(amenity) = b.amenities.iter().find(|a| {
75                if let Some(at) = AmenityType::categorize(&a.amenity_type) {
76                    at == AmenityType::Bar
77                        || at == AmenityType::ConvenienceStore
78                        || at == AmenityType::Food
79                        || at == AmenityType::Supermarket
80                } else {
81                    false
82                }
83            }) {
84                buildings.insert(b.id, BldgState::Store);
85                batch.push(colors.store, b.polygon.clone());
86                batch.append(
87                    Text::from(amenity.names.get(app.opts.language.as_ref()))
88                        .render_autocropped(ctx)
89                        .scale(0.1)
90                        .centered_on(b.label_center),
91                );
92                continue;
93            }
94
95            // If it's not a residence or store, just blank it out.
96            buildings.insert(b.id, BldgState::Ignore);
97            batch.push(colors.visited, b.polygon.clone());
98        }
99
100        Buildings {
101            buildings,
102            draw_all: ctx.upload(batch),
103            total_housing_units,
104            upzones,
105        }
106    }
107
108    pub fn all_stores(&self) -> Vec<BuildingID> {
109        let mut stores = Vec::new();
110        for (b, state) in &self.buildings {
111            if let BldgState::Store = state {
112                stores.push(*b);
113            }
114        }
115        stores
116    }
117
118    pub fn draw_done_houses(&self, ctx: &mut EventCtx, app: &App) -> Drawable {
119        let mut batch = GeomBatch::new();
120        for (b, state) in &self.buildings {
121            if let BldgState::Done = state {
122                batch.push(
123                    app.session.colors.visited,
124                    app.map.get_b(*b).polygon.clone(),
125                );
126            }
127        }
128        ctx.upload(batch)
129    }
130}