1use std::io::Write;
5
6use anyhow::Result;
7use fs_err::File;
8use serde::{Deserialize, Serialize};
9
10use map_model::osm::RoadRank;
11use map_model::{LaneType, Map};
12use widgetry::tools::ColorScale;
13use widgetry::{Choice, Color, EventCtx, Fill, Style, Texture};
14
15use crate::tools::loading_tips;
16
17#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
29pub enum ColorSchemeChoice {
30 DayMode,
31 NightMode,
32 Textured,
33 ClassicDayMode,
34 LTN,
35}
36
37impl ColorSchemeChoice {
38 pub fn choices() -> Vec<Choice<ColorSchemeChoice>> {
39 vec![
40 Choice::new("day mode", ColorSchemeChoice::DayMode),
41 Choice::new("night mode", ColorSchemeChoice::NightMode),
42 Choice::new("textured", ColorSchemeChoice::Textured),
43 Choice::new("classic", ColorSchemeChoice::ClassicDayMode),
44 Choice::new("LTN", ColorSchemeChoice::LTN),
45 ]
46 }
47
48 pub fn parse(x: &str) -> Result<ColorSchemeChoice> {
49 let mut options = Vec::new();
50 for c in ColorSchemeChoice::choices() {
51 options.push(c.label.clone());
52 if c.label == x {
53 return Ok(c.data);
54 }
55 }
56 bail!(
57 "Invalid --color_scheme={}. Choices: {}",
58 x,
59 options.join(", ")
60 );
61 }
62}
63
64pub struct ColorScheme {
65 scheme: ColorSchemeChoice,
66
67 pub road_outlines: bool,
68 pub road_class_colors: bool,
69 pub show_buildings_in_minimap: bool,
70
71 pub panel_bg: Color,
73 pub inner_panel_bg: Color,
74 pub day_time_slider: Color,
75 pub night_time_slider: Color,
76 pub selected: Color,
77 pub current_object: Color,
78 pub perma_selected_object: Color,
79 pub fade_map_dark: Color,
80 gui_style: Style,
81 pub minimap_cursor_border: Color,
82 pub minimap_cursor_bg: Option<Color>,
83
84 driving_lane: Color,
86 bus_lane: Color,
87 parking_lane: Color,
88 bike_lane: Color,
89 sidewalk: Color,
90 pub sidewalk_lines: Color,
91 pub general_road_marking: Color,
92 road_center_line: Color,
93 pub light_rail_track: Color,
94 pub private_road: Option<Color>,
95 pub unzoomed_highway: Color,
96 pub unzoomed_arterial: Color,
97 pub unzoomed_residential: Color,
98 pub unzoomed_cycleway: Color,
99 pub unzoomed_footway: Color,
100 footway: Color,
101 shared_use: Color,
102
103 pub normal_intersection: Color,
105 pub stop_sign: Color,
106 pub stop_sign_pole: Color,
107 pub signal_protected_turn: Color,
108 pub signal_permitted_turn: Color,
109 pub signal_banned_turn: Color,
110 pub signal_box: Color,
111 pub signal_spinner: Color,
112 pub signal_turn_block_bg: Color,
113
114 pub slowest_intersection: Color,
116 pub slower_intersection: Color,
117 pub slow_intersection: Color,
118
119 pub void_background: Color,
121 pub map_background: Fill,
122 pub unzoomed_interesting_intersection: Color,
123 pub residential_building: Color,
124 pub commercial_building: Color,
125 pub building_outline: Color,
126 pub parking_lot: Color,
127 pub grass: Fill,
128 pub water: Fill,
129 pub study_area: Fill,
130
131 pub unzoomed_car: Color,
133 pub unzoomed_bike: Color,
134 pub unzoomed_bus: Color,
135 pub unzoomed_pedestrian: Color,
136
137 agent_colors: Vec<Color>,
139 pub route: Color,
140 pub turn_arrow: Color,
141 pub brake_light: Color,
142 pub bus_body: Color,
143 pub bus_label: Color,
144 pub train_body: Color,
145 pub ped_head: Color,
146 pub ped_foot: Color,
147 pub ped_preparing_bike_body: Color,
148 pub ped_crowd: Color,
149 pub bike_frame: Color,
150 pub parked_car: Color,
151
152 pub good_to_bad_red: ColorScale,
154 pub good_to_bad_green: ColorScale,
155 pub bus_layer: Color,
156 pub edits_layer: Color,
157
158 pub parking_trip: Color,
160 pub bike_trip: Color,
161 pub bus_trip: Color,
162 pub before_changes: Color,
163 pub after_changes: Color,
164}
165
166impl ColorScheme {
167 pub fn new(ctx: &mut EventCtx, scheme: ColorSchemeChoice) -> ColorScheme {
168 let mut cs = match scheme {
169 ColorSchemeChoice::DayMode => ColorScheme::day_mode(),
170 ColorSchemeChoice::NightMode => ColorScheme::night_mode(),
171 ColorSchemeChoice::Textured => ColorScheme::textured(),
172 ColorSchemeChoice::ClassicDayMode => ColorScheme::classic(),
173 ColorSchemeChoice::LTN => ColorScheme::ltn(),
174 };
175 cs.scheme = scheme;
176 ctx.set_style(cs.gui_style.clone());
177 cs
178 }
179
180 fn classic() -> ColorScheme {
181 let mut cs = Self::light_background(Style::light_bg());
182 cs.scheme = ColorSchemeChoice::ClassicDayMode;
183 cs
184 }
185
186 fn light_background(mut gui_style: Style) -> ColorScheme {
187 gui_style.loading_tips = loading_tips();
188 ColorScheme {
189 scheme: ColorSchemeChoice::DayMode,
190
191 road_outlines: false,
192 road_class_colors: false,
193 show_buildings_in_minimap: true,
194
195 panel_bg: gui_style.panel_bg,
197 inner_panel_bg: gui_style.section_bg,
198 day_time_slider: hex("#F4DA22"),
199 night_time_slider: hex("#12409D"),
200 selected: Color::RED.alpha(0.7),
201 current_object: Color::WHITE,
202 perma_selected_object: Color::BLUE,
203 fade_map_dark: Color::BLACK.alpha(0.6),
204 minimap_cursor_border: Color::BLACK,
205 minimap_cursor_bg: None,
206 gui_style,
207
208 driving_lane: Color::BLACK,
210 bus_lane: Color::rgb(190, 74, 76),
211 parking_lane: Color::grey(0.2),
212 bike_lane: Color::rgb(15, 125, 75),
213 sidewalk: Color::grey(0.8),
214 sidewalk_lines: Color::grey(0.7),
215 general_road_marking: Color::WHITE,
216 road_center_line: Color::YELLOW,
217 light_rail_track: hex("#844204"),
218 private_road: Some(hex("#F0B0C0")),
219 unzoomed_highway: hex("#E892A2"),
220 unzoomed_arterial: hex("#FFC73E"),
221 unzoomed_residential: Color::WHITE,
222 unzoomed_cycleway: hex("#0F7D4B"),
223 unzoomed_footway: hex("#DED68A"),
224 footway: hex("#DED68A"),
226 shared_use: hex("#DED68A"),
227
228 normal_intersection: Color::grey(0.2),
230 stop_sign: Color::RED,
231 stop_sign_pole: Color::grey(0.5),
232 signal_protected_turn: hex("#72CE36"),
233 signal_permitted_turn: hex("#4CA7E9"),
234 signal_banned_turn: hex("#EB3223"),
235 signal_box: Color::grey(0.5),
236 signal_spinner: hex("#F2994A"),
237 signal_turn_block_bg: Color::grey(0.6),
238
239 slowest_intersection: Color::RED,
241 slower_intersection: Color::YELLOW,
242 slow_intersection: Color::GREEN,
243
244 void_background: Color::BLACK,
246 map_background: Color::grey(0.87).into(),
247 unzoomed_interesting_intersection: Color::BLACK,
248 residential_building: hex("#C4C1BC"),
249 commercial_building: hex("#9FABA7"),
250 building_outline: hex("#938E85"),
251 parking_lot: Color::grey(0.7),
252 grass: hex("#94C84A").into(),
253 water: hex("#A4C8EA").into(),
254 study_area: hex("#96830C").into(),
255
256 unzoomed_car: hex("#FE5f55"),
258 unzoomed_bike: hex("#90BE6D"),
259 unzoomed_bus: hex("#FFD166"),
260 unzoomed_pedestrian: hex("#457B9D"),
261
262 agent_colors: vec![
264 hex("#5C45A0"),
265 hex("#3E8BC3"),
266 hex("#E1BA13"),
267 hex("#96322F"),
268 hex("#00A27B"),
269 ],
270 route: Color::ORANGE.alpha(0.5),
271 turn_arrow: hex("#DF8C3D"),
272 brake_light: hex("#FF1300"),
273 bus_body: Color::rgb(50, 133, 117),
274 bus_label: Color::rgb(249, 206, 24),
275 train_body: hex("#42B6E9"),
276 ped_head: Color::rgb(139, 69, 19),
277 ped_foot: Color::BLACK,
278 ped_preparing_bike_body: Color::rgb(255, 0, 144),
279 ped_crowd: Color::rgb_f(0.2, 0.7, 0.7),
280 bike_frame: hex("#AAA9AD"),
281 parked_car: hex("#938E85"),
282
283 good_to_bad_red: ColorScale(vec![hex("#F19A93"), hex("#A32015")]),
285 good_to_bad_green: ColorScale(vec![hex("#BEDB92"), hex("#397A4C")]),
286 bus_layer: hex("#4CA7E9"),
287 edits_layer: hex("#12409D"),
288
289 parking_trip: hex("#4E30A6"),
291 bike_trip: Color::rgb(15, 125, 75),
292 bus_trip: Color::rgb(190, 74, 76),
293 before_changes: Color::BLUE,
294 after_changes: Color::RED,
295 }
296 }
297
298 fn night_mode() -> ColorScheme {
300 let mut cs = ColorScheme::classic();
301 cs.scheme = ColorSchemeChoice::NightMode;
302 cs.gui_style = widgetry::Style::dark_bg();
303
304 cs.void_background = hex("#200A24");
305 cs.map_background = Color::BLACK.into();
306 cs.grass = hex("#243A1F").into();
307 cs.water = hex("#21374E").into();
308 cs.residential_building = hex("#2C422E");
309 cs.commercial_building = hex("#5D5F97");
310
311 cs.driving_lane = hex("#404040");
312 cs.parking_lane = hex("#353535");
313 cs.sidewalk = hex("#6B6B6B");
314 cs.general_road_marking = hex("#B1B1B1");
315 cs.normal_intersection = cs.driving_lane;
316 cs.road_center_line = cs.general_road_marking;
317
318 cs.parking_lot = cs.sidewalk;
319 cs.unzoomed_highway = cs.parking_lane;
320 cs.unzoomed_arterial = cs.sidewalk;
321 cs.unzoomed_residential = cs.driving_lane;
322 cs.unzoomed_interesting_intersection = cs.unzoomed_highway;
323 cs.stop_sign = hex("#A32015");
324 cs.private_road = Some(hex("#9E757F"));
325 cs.study_area = hex("#D9B002").into();
326
327 cs.panel_bg = cs.gui_style.panel_bg;
328 cs.inner_panel_bg = cs.panel_bg.alpha(1.0);
329 cs.minimap_cursor_border = Color::WHITE;
330 cs.minimap_cursor_bg = Some(Color::rgba(238, 112, 46, 0.2));
331
332 cs
333 }
334
335 fn textured() -> ColorScheme {
336 let mut cs = ColorScheme::day_mode();
337 cs.scheme = ColorSchemeChoice::Textured;
338 cs.grass = Texture::GRASS.into();
339 cs.water = Texture::STILL_WATER.into();
340 cs.map_background = Texture::CONCRETE.into();
341 cs
342 }
343
344 fn day_mode() -> ColorScheme {
345 let mut cs = Self::light_background(Style::light_bg());
346 cs.scheme = ColorSchemeChoice::DayMode;
347 cs.road_outlines = true;
348 cs.road_class_colors = true;
349 cs.show_buildings_in_minimap = false;
350
351 cs.map_background = hex("#EEE5C8").into();
352 cs.grass = hex("#BED4A3").into();
353 cs.water = hex("#6384D6").into();
354
355 cs.sidewalk = hex("#A9A9A9");
356 cs.sidewalk_lines = hex("#989898");
357
358 cs.unzoomed_arterial = hex("#F6A483");
359
360 cs.residential_building = hex("#C5D2E5");
361 cs.commercial_building = hex("#99AECC");
362
363 cs
364 }
365
366 fn ltn() -> ColorScheme {
367 let mut cs = ColorScheme::day_mode();
368 cs.scheme = ColorSchemeChoice::LTN;
369 cs.private_road = None;
370 cs.fade_map_dark = Color::BLACK.alpha(0.3);
371
372 cs.map_background = hex("#F6F6F4").into();
374 cs.water = hex("#c7d7d9").into();
376 cs.grass = hex("#ddebe4").into();
378 cs.unzoomed_highway = Color::BLACK;
381 cs.unzoomed_arterial = Color::BLACK;
382 cs.unzoomed_residential = Color::WHITE;
383 cs.unzoomed_cycleway = Color::CLEAR;
384 cs.unzoomed_footway = Color::CLEAR;
385 cs.light_rail_track = Color::CLEAR;
386
387 cs.parking_lot = Color::BLACK.alpha(0.2);
389 cs.residential_building = Color::BLACK.alpha(0.3);
390 cs.commercial_building = Color::BLACK.alpha(0.5);
391
392 cs.gui_style.panel_bg = Color::WHITE;
393 cs.panel_bg = cs.gui_style.panel_bg;
394
395 cs
396 }
397}
398
399impl ColorScheme {
400 pub fn rotating_color_plot(&self, idx: usize) -> Color {
401 modulo_color(
402 &[
403 Color::RED,
404 Color::BLUE,
405 Color::GREEN,
406 Color::PURPLE,
407 Color::BLACK,
408 ],
409 idx,
410 )
411 }
412
413 pub fn rotating_color_agents(&self, idx: usize) -> Color {
414 modulo_color(&self.agent_colors, idx)
415 }
416
417 pub fn unzoomed_road_surface(&self, rank: RoadRank) -> Color {
418 match rank {
419 RoadRank::Highway => self.unzoomed_highway,
420 RoadRank::Arterial => self.unzoomed_arterial,
421 RoadRank::Local => self.unzoomed_residential,
422 }
423 }
424
425 pub fn zoomed_road_surface(&self, lane: LaneType, rank: RoadRank) -> Color {
426 let main_asphalt = if self.road_class_colors {
427 match rank {
428 RoadRank::Highway => Color::grey(0.3),
429 RoadRank::Arterial => Color::grey(0.4),
430 RoadRank::Local => Color::grey(0.5),
431 }
432 } else {
433 self.driving_lane
434 };
435 let parking_asphalt = if self.road_class_colors {
436 main_asphalt
437 } else {
438 self.parking_lane
439 };
440
441 match lane {
442 LaneType::Driving => main_asphalt,
443 LaneType::Bus => self.bus_lane,
444 LaneType::Parking => parking_asphalt,
445 LaneType::Sidewalk | LaneType::Shoulder => self.sidewalk,
446 LaneType::Biking => self.bike_lane,
447 LaneType::SharedLeftTurn => main_asphalt,
448 LaneType::Construction => parking_asphalt,
449 LaneType::LightRail => unreachable!(),
450 LaneType::Buffer(_) => main_asphalt,
451 LaneType::Footway => self.footway,
452 LaneType::SharedUse => self.shared_use,
453 }
454 }
455 pub fn zoomed_intersection_surface(&self, rank: RoadRank) -> Color {
456 if self.road_class_colors {
457 self.zoomed_road_surface(LaneType::Driving, rank)
458 } else {
459 self.normal_intersection
460 }
461 }
462
463 pub fn curb(&self, rank: RoadRank) -> Color {
464 match rank {
466 RoadRank::Highway => Color::grey(0.2),
467 RoadRank::Arterial => Color::grey(0.3),
468 RoadRank::Local => Color::grey(0.4),
469 }
470 }
471
472 pub fn road_center_line(&self, map: &Map) -> Color {
473 if map.get_name().city.country == "gb" {
476 self.general_road_marking
477 } else {
478 self.road_center_line
479 }
480 }
481
482 pub fn export(&self, path: &str) -> Result<()> {
485 let mut f = File::create(path)?;
486 writeln!(f, "unzoomed_highway {}", self.unzoomed_highway.as_hex())?;
487 writeln!(f, "unzoomed_arterial {}", self.unzoomed_arterial.as_hex())?;
488 writeln!(
489 f,
490 "unzoomed_residential {}",
491 self.unzoomed_residential.as_hex()
492 )?;
493 writeln!(f, "unzoomed_cycleway {}", self.unzoomed_cycleway.as_hex())?;
494 writeln!(f, "unzoomed_footway {}", self.unzoomed_footway.as_hex())?;
495 writeln!(
496 f,
497 "residential_building {}",
498 self.residential_building.as_hex()
499 )?;
500 writeln!(
501 f,
502 "commercial_building {}",
503 self.commercial_building.as_hex()
504 )?;
505 if let Fill::Color(c) = self.grass {
506 writeln!(f, "grass {}", c.as_hex())?;
507 }
508 if let Fill::Color(c) = self.water {
509 writeln!(f, "water {}", c.as_hex())?;
510 }
511 Ok(())
512 }
513
514 pub fn import(&mut self, path: &str) -> Result<()> {
515 let raw = String::from_utf8(abstio::slurp_file(path)?)?;
516 let mut colors = Vec::new();
517 for line in raw.split('\n') {
518 if line.is_empty() {
519 continue;
520 }
521 let mut parts = line.split(' ');
522 parts.next();
523 colors.push(Color::hex(parts.next().unwrap()));
524 }
525
526 self.unzoomed_highway = colors[0];
527 self.unzoomed_arterial = colors[1];
528 self.unzoomed_residential = colors[2];
529 self.unzoomed_cycleway = colors[3];
530 self.unzoomed_footway = colors[4];
531 self.residential_building = colors[5];
532 self.commercial_building = colors[6];
533 self.grass = Fill::Color(colors[7]);
534 self.water = Fill::Color(colors[8]);
535
536 Ok(())
537 }
538}
539
540fn modulo_color(colors: &[Color], idx: usize) -> Color {
541 colors[idx % colors.len()]
542}
543
544fn hex(x: &str) -> Color {
546 Color::hex(x)
547}