mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
the great color scheme refactor!
This commit is contained in:
parent
45bd7ee128
commit
898e036c7e
@ -370,3 +370,22 @@ Let's start with two concrete things:
|
||||
- sometimes call draw()
|
||||
|
||||
alright, the boilerplate is all gone! \o/ I'm happy now.
|
||||
|
||||
## Colors
|
||||
|
||||
It's too tedious to make new colors, so I find myself hacking in temporary
|
||||
hardcoded values. Declaring something far from its use sucks, because there's
|
||||
no context. So how about this for an alternative:
|
||||
|
||||
- allow either [0.0, 1.0] or [0, 255] formats. helper rgb(r, g, b) implicit 1.0 alpha and rgba(r, g, b, a)
|
||||
- cs.get("name", default)
|
||||
- the colorscheme object lazily accumulates the list of colors, so the color picker plugin still works
|
||||
- but some are in plugins that dont get called often
|
||||
- what gets serialized? just marked changes from the color picker? yeah, and
|
||||
those overrides are loaded in at first. colorscheme obj has a set() fxn that
|
||||
remembers it's changed from default in code.
|
||||
- can still later support different colorschemes with different json files.
|
||||
- shared colors?
|
||||
- should those be defined in one of the places arbitrarily, that we know will be invoked early?
|
||||
- could a macro help with registration?
|
||||
- get() being mutable means we have to use RefCell or propogate mutability in lots of sad places
|
||||
|
@ -27,5 +27,3 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
sim = { path = "../sim" }
|
||||
structopt = "0.2"
|
||||
strum = "0.9.0"
|
||||
strum_macros = "0.9.0"
|
||||
|
@ -1,322 +1,3 @@
|
||||
{
|
||||
"map": {
|
||||
"Background": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Debug": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"BrightDebug": [
|
||||
0.8,
|
||||
0.1,
|
||||
0.1,
|
||||
1.0
|
||||
],
|
||||
"Broken": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.565,
|
||||
1.0
|
||||
],
|
||||
"Road": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"DrivingLaneMarking": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Parking": [
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
1.0
|
||||
],
|
||||
"ParkingMarking": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Sidewalk": [
|
||||
0.8,
|
||||
0.8,
|
||||
0.8,
|
||||
1.0
|
||||
],
|
||||
"SidewalkMarking": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
1.0
|
||||
],
|
||||
"Crosswalk": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"StopSignMarking": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Biking": [
|
||||
0.059,
|
||||
0.494,
|
||||
0.294,
|
||||
1.0
|
||||
],
|
||||
"BusStopMarking": [
|
||||
0.867,
|
||||
0.627,
|
||||
0.867,
|
||||
0.8
|
||||
],
|
||||
"UnchangedIntersection": [
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
1.0
|
||||
],
|
||||
"ChangedIntersection": [
|
||||
0.8,
|
||||
0.6,
|
||||
0.6,
|
||||
1.0
|
||||
],
|
||||
"Selected": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Turn": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"ConflictingTurn": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.5
|
||||
],
|
||||
"Building": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
0.8
|
||||
],
|
||||
"BuildingPath": [
|
||||
0.6,
|
||||
0.6,
|
||||
0.6,
|
||||
1.0
|
||||
],
|
||||
"BuildingBoundary": [
|
||||
0.0,
|
||||
0.395,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"ParcelBoundary": [
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
1.0
|
||||
],
|
||||
"RoadOrientation": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"SearchResult": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Visited": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Queued": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"NextQueued": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"TurnIconCircle": [
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
1.0
|
||||
],
|
||||
"TurnIconInactive": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
1.0
|
||||
],
|
||||
"ExtraShape": [
|
||||
0.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"MatchClassification": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"DontMatchClassification": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.1
|
||||
],
|
||||
"TurnIrrelevant": [
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
1.0
|
||||
],
|
||||
"SignalEditorTurnInCurrentCycle": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"SignalEditorTurnCompatibleWithCurrentCycle": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.2
|
||||
],
|
||||
"SignalEditorTurnConflictsWithCurrentCycle": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"PriorityTurn": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"YieldTurn": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"StopTurn": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"DebugCar": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.8
|
||||
],
|
||||
"MovingCar": [
|
||||
0.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"StuckCar": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"ParkedCar": [
|
||||
0.7062618,
|
||||
0.91600245,
|
||||
0.29857337,
|
||||
1.0
|
||||
],
|
||||
"Pedestrian": [
|
||||
0.2,
|
||||
0.7,
|
||||
0.7,
|
||||
1.0
|
||||
],
|
||||
"TrafficSignalBox": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"TrafficSignalGreen": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"TrafficSignalYellow": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"TrafficSignalRed": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"StopSignBackground": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"ParkArea": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"SwampArea": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.6,
|
||||
1.0
|
||||
],
|
||||
"WaterArea": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
"map": {}
|
||||
}
|
@ -1,122 +1,75 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use abstutil;
|
||||
use ezgui::Color;
|
||||
use rand;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io::Error;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
ToString,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Clone,
|
||||
Copy,
|
||||
)]
|
||||
pub enum Colors {
|
||||
Background,
|
||||
Debug,
|
||||
BrightDebug,
|
||||
Broken,
|
||||
Road,
|
||||
DrivingLaneMarking,
|
||||
Parking,
|
||||
ParkingMarking,
|
||||
Sidewalk,
|
||||
SidewalkMarking,
|
||||
Crosswalk,
|
||||
StopSignMarking,
|
||||
Biking,
|
||||
BusStopMarking,
|
||||
pub struct ColorScheme {
|
||||
// Filled out by lazy calls to get()
|
||||
map: HashMap<String, Color>,
|
||||
|
||||
UnchangedIntersection,
|
||||
ChangedIntersection,
|
||||
|
||||
Selected,
|
||||
Turn,
|
||||
ConflictingTurn,
|
||||
Building,
|
||||
BuildingPath,
|
||||
BuildingBoundary,
|
||||
ParcelBoundary,
|
||||
RoadOrientation,
|
||||
SearchResult,
|
||||
Visited,
|
||||
Queued,
|
||||
NextQueued,
|
||||
TurnIconCircle,
|
||||
TurnIconInactive,
|
||||
ExtraShape,
|
||||
|
||||
MatchClassification,
|
||||
DontMatchClassification,
|
||||
|
||||
TurnIrrelevant,
|
||||
SignalEditorTurnInCurrentCycle,
|
||||
SignalEditorTurnCompatibleWithCurrentCycle,
|
||||
SignalEditorTurnConflictsWithCurrentCycle,
|
||||
|
||||
PriorityTurn,
|
||||
YieldTurn,
|
||||
StopTurn,
|
||||
|
||||
DebugCar,
|
||||
MovingCar,
|
||||
StuckCar,
|
||||
ParkedCar,
|
||||
|
||||
Pedestrian,
|
||||
|
||||
TrafficSignalBox,
|
||||
TrafficSignalGreen,
|
||||
TrafficSignalYellow,
|
||||
TrafficSignalRed,
|
||||
|
||||
StopSignBackground,
|
||||
|
||||
ParkArea,
|
||||
SwampArea,
|
||||
WaterArea,
|
||||
// A subset of map
|
||||
modified: ModifiedColors,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ColorScheme {
|
||||
map: BTreeMap<Colors, Color>,
|
||||
struct ModifiedColors {
|
||||
map: BTreeMap<String, Color>,
|
||||
}
|
||||
|
||||
impl ColorScheme {
|
||||
pub fn load(path: &str) -> Result<ColorScheme, Error> {
|
||||
let mut scheme: ColorScheme = abstutil::read_json(path)?;
|
||||
|
||||
for color in Colors::iter() {
|
||||
if !scheme.map.contains_key(&color) {
|
||||
warn!(
|
||||
"No color for {:?} defined, initializing with a random one",
|
||||
color
|
||||
);
|
||||
scheme
|
||||
.map
|
||||
.insert(color, [rand::random(), rand::random(), rand::random(), 1.0]);
|
||||
}
|
||||
pub fn load() -> Result<ColorScheme, Error> {
|
||||
let modified: ModifiedColors = abstutil::read_json("color_scheme")?;
|
||||
let mut map: HashMap<String, Color> = HashMap::new();
|
||||
for (name, c) in &modified.map {
|
||||
map.insert(name.clone(), *c);
|
||||
}
|
||||
|
||||
Ok(scheme)
|
||||
Ok(ColorScheme { map, modified })
|
||||
}
|
||||
|
||||
pub fn get(&self, c: Colors) -> Color {
|
||||
// TODO make sure this isn't slow; maybe back this with an array
|
||||
*self.map.get(&c).unwrap()
|
||||
pub fn save(&self) {
|
||||
abstutil::write_json("color_scheme", &self.modified).expect("Saving color_scheme failed");
|
||||
}
|
||||
|
||||
pub fn set(&mut self, c: Colors, value: Color) {
|
||||
self.map.insert(c, value);
|
||||
pub fn get(&mut self, name: &str, default: Color) -> Color {
|
||||
if let Some(existing) = self.map.get(name) {
|
||||
if default != *existing && !self.modified.map.contains_key(name) {
|
||||
panic!(
|
||||
"Two colors defined for {}: {} and {}",
|
||||
name, existing, default
|
||||
);
|
||||
}
|
||||
return *existing;
|
||||
}
|
||||
|
||||
self.map.insert(name.to_string(), default);
|
||||
default
|
||||
}
|
||||
|
||||
// Just for the color picker plugin, that's why the funky return value
|
||||
pub fn color_names(&self) -> Vec<(String, ())> {
|
||||
let mut names: Vec<(String, ())> = self.map.keys().map(|n| (n.clone(), ())).collect();
|
||||
names.sort();
|
||||
names
|
||||
}
|
||||
|
||||
pub fn override_color(&mut self, name: &str, value: Color) {
|
||||
self.modified.map.insert(name.to_string(), value);
|
||||
self.map.insert(name.to_string(), value);
|
||||
}
|
||||
|
||||
pub fn get_modified(&self, name: &str) -> Option<Color> {
|
||||
self.modified.map.get(name).map(|c| *c)
|
||||
}
|
||||
|
||||
pub fn reset_modified(&mut self, name: &str, orig: Option<Color>) {
|
||||
if let Some(c) = orig {
|
||||
self.modified.map.insert(name.to_string(), c);
|
||||
self.map.insert(name.to_string(), c);
|
||||
} else {
|
||||
self.modified.map.remove(name);
|
||||
// Just, uh, wait for the default to be populated next time. :P
|
||||
self.map.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,6 @@ extern crate serde_derive;
|
||||
extern crate sim;
|
||||
#[macro_use]
|
||||
extern crate structopt;
|
||||
extern crate strum;
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -103,7 +103,7 @@ impl ID {
|
||||
|
||||
// For plugins and rendering. Not sure what module this should live in, here seems fine.
|
||||
pub struct Ctx<'a> {
|
||||
pub cs: &'a ColorScheme,
|
||||
pub cs: &'a mut ColorScheme,
|
||||
pub map: &'a Map,
|
||||
pub control_map: &'a ControlMap,
|
||||
pub draw_map: &'a DrawMap,
|
||||
|
@ -1,3 +1,4 @@
|
||||
use colors::ColorScheme;
|
||||
use ezgui::{GfxCtx, LogScroller, Wizard, WrappedWizard};
|
||||
use map_model::Map;
|
||||
use objects::{Ctx, SIM_SETUP};
|
||||
@ -45,7 +46,7 @@ impl Plugin for ABTestManager {
|
||||
ABTestManager::ManageABTest(test, ref mut scroller) => {
|
||||
if ctx.input.key_pressed(Key::R, "run this A/B test") {
|
||||
let ((new_primary, new_primary_plugins), new_secondary) =
|
||||
launch_test(test, ctx.kml, &ctx.primary.current_flags);
|
||||
launch_test(test, ctx.kml, &ctx.primary.current_flags, ctx.cs);
|
||||
*ctx.primary = new_primary;
|
||||
*ctx.new_primary_plugins = Some(new_primary_plugins);
|
||||
*ctx.secondary = Some(new_secondary);
|
||||
@ -103,6 +104,7 @@ fn launch_test(
|
||||
test: &ABTest,
|
||||
kml: &Option<String>,
|
||||
current_flags: &SimFlags,
|
||||
cs: &mut ColorScheme,
|
||||
) -> ((PerMapUI, PluginsPerMap), (PerMapUI, PluginsPerMap)) {
|
||||
info!("Launching A/B test {}...", test.test_name);
|
||||
let load = format!(
|
||||
@ -123,6 +125,7 @@ fn launch_test(
|
||||
edits_name: test.edits1_name.clone(),
|
||||
},
|
||||
kml,
|
||||
cs,
|
||||
);
|
||||
let secondary = PerMapUI::new(
|
||||
SimFlags {
|
||||
@ -132,6 +135,7 @@ fn launch_test(
|
||||
edits_name: test.edits2_name.clone(),
|
||||
},
|
||||
kml,
|
||||
cs,
|
||||
);
|
||||
// That's all! The scenario will be instantiated.
|
||||
(primary, secondary)
|
||||
|
@ -1,4 +1,3 @@
|
||||
use colors::Colors;
|
||||
use counter::Counter;
|
||||
use dimensioned::si;
|
||||
use ezgui::Color;
|
||||
@ -55,13 +54,12 @@ impl Plugin for ChokepointsFinder {
|
||||
}
|
||||
|
||||
fn color_for(&self, obj: ID, ctx: Ctx) -> Option<Color> {
|
||||
let color = ctx.cs.get("chokepoint", Color::RED);
|
||||
match self {
|
||||
ChokepointsFinder::Inactive => None,
|
||||
ChokepointsFinder::Active(lanes, intersections) => match obj {
|
||||
ID::Lane(l) if lanes.contains(&l) => Some(ctx.cs.get(Colors::MatchClassification)),
|
||||
ID::Intersection(i) if intersections.contains(&i) => {
|
||||
Some(ctx.cs.get(Colors::MatchClassification))
|
||||
}
|
||||
ID::Lane(l) if lanes.contains(&l) => Some(color),
|
||||
ID::Intersection(i) if intersections.contains(&i) => Some(color),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::Color;
|
||||
use objects::{Ctx, DEBUG_EXTRA, ID};
|
||||
use piston::input::Key;
|
||||
@ -41,13 +40,16 @@ impl Plugin for OsmClassifier {
|
||||
Some(hwy) => hwy == "primary" || hwy == "secondary" || hwy == "tertiary",
|
||||
None => false,
|
||||
} {
|
||||
Some(ctx.cs.get(Colors::MatchClassification))
|
||||
Some(ctx.cs.get("matches OSM classification", Color::GREEN))
|
||||
} else {
|
||||
Some(ctx.cs.get(Colors::DontMatchClassification))
|
||||
Some(ctx.cs.get(
|
||||
"doesn't match OSM classification",
|
||||
Color::rgba(0, 0, 0, 0.1),
|
||||
))
|
||||
}
|
||||
}
|
||||
ID::Building(b) => if ctx.map.get_b(b).osm_tags.contains_key("addr:housenumber") {
|
||||
Some(ctx.cs.get(Colors::MatchClassification))
|
||||
Some(ctx.cs.get("matches OSM classification", Color::GREEN))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
@ -1,12 +1,9 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::{Canvas, Color, GfxCtx, InputResult, Menu};
|
||||
use objects::{Ctx, SETTINGS};
|
||||
use piston::input::Key;
|
||||
use plugins::{Plugin, PluginCtx};
|
||||
use std::string::ToString;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
// TODO assumes minimum screen size
|
||||
const WIDTH: u32 = 255;
|
||||
@ -16,9 +13,9 @@ const TILE_DIMS: u32 = 2;
|
||||
// TODO parts of this should be in ezgui
|
||||
pub enum ColorPicker {
|
||||
Inactive,
|
||||
Choosing(Menu<Colors>),
|
||||
// Remember the original color, in case we revert
|
||||
PickingColor(Colors, Color),
|
||||
Choosing(Menu<()>),
|
||||
// Remember the original modified color in case we revert.
|
||||
ChangingColor(String, Option<Color>),
|
||||
}
|
||||
|
||||
impl ColorPicker {
|
||||
@ -37,7 +34,7 @@ impl Plugin for ColorPicker {
|
||||
if input.unimportant_key_pressed(Key::D8, SETTINGS, "configure colors") {
|
||||
new_state = Some(ColorPicker::Choosing(Menu::new(
|
||||
"Pick a color to change",
|
||||
Colors::iter().map(|c| (c.to_string(), c)).collect(),
|
||||
cs.color_names(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@ -47,21 +44,25 @@ impl Plugin for ColorPicker {
|
||||
new_state = Some(ColorPicker::Inactive);
|
||||
}
|
||||
InputResult::StillActive => {}
|
||||
InputResult::Done(_, color) => {
|
||||
new_state = Some(ColorPicker::PickingColor(color, cs.get(color)));
|
||||
InputResult::Done(name, _) => {
|
||||
new_state = Some(ColorPicker::ChangingColor(
|
||||
name.clone(),
|
||||
cs.get_modified(&name),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
ColorPicker::PickingColor(c, orig_color) => {
|
||||
ColorPicker::ChangingColor(name, orig) => {
|
||||
if input.key_pressed(
|
||||
Key::Escape,
|
||||
&format!("stop configuring color for {:?} and revert", c),
|
||||
&format!("stop changing color for {} and revert", name),
|
||||
) {
|
||||
cs.set(*c, *orig_color);
|
||||
cs.reset_modified(name, *orig);
|
||||
new_state = Some(ColorPicker::Inactive);
|
||||
} else if input.key_pressed(Key::Return, &format!("finalize new color for {:?}", c))
|
||||
} else if input
|
||||
.key_pressed(Key::Return, &format!("finalize new color for {}", name))
|
||||
{
|
||||
info!("Setting color for {:?}", c);
|
||||
info!("Setting color for {}", name);
|
||||
new_state = Some(ColorPicker::Inactive);
|
||||
}
|
||||
|
||||
@ -71,7 +72,7 @@ impl Plugin for ColorPicker {
|
||||
let x = (m_x - (start_x as f64)) / (TILE_DIMS as f64) / 255.0;
|
||||
let y = (m_y - (start_y as f64)) / (TILE_DIMS as f64) / 255.0;
|
||||
if x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0 {
|
||||
cs.set(*c, get_color(x as f32, y as f32));
|
||||
cs.override_color(name, get_color(x as f32, y as f32));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,7 +92,7 @@ impl Plugin for ColorPicker {
|
||||
ColorPicker::Choosing(menu) => {
|
||||
menu.draw(g, ctx.canvas);
|
||||
}
|
||||
ColorPicker::PickingColor(_, _) => {
|
||||
ColorPicker::ChangingColor(_, _) => {
|
||||
let (start_x, start_y) = get_screen_offset(ctx.canvas);
|
||||
|
||||
for x in 0..WIDTH {
|
||||
@ -123,5 +124,5 @@ fn get_screen_offset(canvas: &Canvas) -> (u32, u32) {
|
||||
fn get_color(x: f32, y: f32) -> Color {
|
||||
assert!(x >= 0.0 && x <= 1.0);
|
||||
assert!(y >= 0.0 && y <= 1.0);
|
||||
[x, y, (x + y) / 2.0, 1.0]
|
||||
Color::rgb_f(x, y, (x + y) / 2.0)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::Line;
|
||||
use map_model::LANE_THICKNESS;
|
||||
use objects::Ctx;
|
||||
@ -74,11 +74,14 @@ impl Plugin for DiffAllState {
|
||||
active
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _ctx: Ctx) {
|
||||
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
if let DiffAllState::Active(ref lines) = self {
|
||||
for line in lines {
|
||||
// TODO move constants
|
||||
g.draw_line([1.0, 1.0, 0.0, 1.0], LANE_THICKNESS, line);
|
||||
g.draw_line(
|
||||
ctx.cs.get("diff agents line", Color::YELLOW),
|
||||
LANE_THICKNESS,
|
||||
line,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use dimensioned::si;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::Line;
|
||||
use map_model::{Trace, LANE_THICKNESS};
|
||||
use objects::Ctx;
|
||||
@ -98,7 +98,7 @@ impl Plugin for DiffWorldsState {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _ctx: Ctx) {
|
||||
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
if let DiffWorldsState::Active {
|
||||
line,
|
||||
primary_route,
|
||||
@ -107,18 +107,23 @@ impl Plugin for DiffWorldsState {
|
||||
} = self
|
||||
{
|
||||
if let Some(l) = line {
|
||||
// TODO move constants
|
||||
g.draw_line([1.0, 1.0, 0.0, 1.0], LANE_THICKNESS, l);
|
||||
g.draw_line(
|
||||
ctx.cs.get("diff agents line", Color::YELLOW),
|
||||
LANE_THICKNESS,
|
||||
l,
|
||||
);
|
||||
}
|
||||
if let Some(t) = primary_route {
|
||||
g.draw_polygon(
|
||||
[1.0, 0.0, 0.0, 0.5],
|
||||
ctx.cs
|
||||
.get("primary agent route", Color::rgba(255, 0, 0, 0.5)),
|
||||
&t.get_polyline().make_polygons_blindly(LANE_THICKNESS),
|
||||
);
|
||||
}
|
||||
if let Some(t) = secondary_route {
|
||||
g.draw_polygon(
|
||||
[0.0, 0.0, 1.0, 0.5],
|
||||
ctx.cs
|
||||
.get("secondary agent route", Color::rgba(0, 0, 255, 0.5)),
|
||||
&t.get_polyline().make_polygons_blindly(LANE_THICKNESS),
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ezgui::{GfxCtx, Wizard, WrappedWizard};
|
||||
use ezgui::{Color, GfxCtx, Wizard, WrappedWizard};
|
||||
use geom::{Circle, Line, Polygon};
|
||||
use map_model::Map;
|
||||
use objects::{Ctx, EDIT_MAP};
|
||||
@ -94,12 +94,6 @@ impl Plugin for DrawNeighborhoodState {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
// TODO add colorscheme entries
|
||||
let red = [1.0, 0.0, 0.0, 1.0];
|
||||
let green = [0.0, 1.0, 0.0, 1.0];
|
||||
let blue = [0.0, 0.0, 1.0, 0.6];
|
||||
let cyan = [0.0, 1.0, 1.0, 1.0];
|
||||
|
||||
let (pts, current_idx) = match self {
|
||||
DrawNeighborhoodState::Inactive => {
|
||||
return;
|
||||
@ -118,19 +112,36 @@ impl Plugin for DrawNeighborhoodState {
|
||||
};
|
||||
|
||||
if pts.len() == 2 {
|
||||
g.draw_line(red, POINT_RADIUS / 2.0, &Line::new(pts[0], pts[1]));
|
||||
g.draw_line(
|
||||
ctx.cs.get("neighborhood point", Color::RED),
|
||||
POINT_RADIUS / 2.0,
|
||||
&Line::new(pts[0], pts[1]),
|
||||
);
|
||||
}
|
||||
if pts.len() >= 3 {
|
||||
g.draw_polygon(blue, &Polygon::new(pts));
|
||||
g.draw_polygon(
|
||||
ctx.cs
|
||||
.get("neighborhood polygon", Color::rgba(0, 0, 255, 0.6)),
|
||||
&Polygon::new(pts),
|
||||
);
|
||||
}
|
||||
for pt in pts {
|
||||
g.draw_circle(red, &Circle::new(*pt, POINT_RADIUS));
|
||||
g.draw_circle(
|
||||
ctx.cs.get("neighborhood point", Color::RED),
|
||||
&Circle::new(*pt, POINT_RADIUS),
|
||||
);
|
||||
}
|
||||
if let Some(last) = pts.last() {
|
||||
g.draw_circle(green, &Circle::new(*last, POINT_RADIUS));
|
||||
g.draw_circle(
|
||||
ctx.cs.get("neighborhood last placed point", Color::GREEN),
|
||||
&Circle::new(*last, POINT_RADIUS),
|
||||
);
|
||||
}
|
||||
if let Some(idx) = current_idx {
|
||||
g.draw_circle(cyan, &Circle::new(pts[idx], POINT_RADIUS));
|
||||
g.draw_circle(
|
||||
ctx.cs.get("neighborhood point to move", Color::CYAN),
|
||||
&Circle::new(pts[idx], POINT_RADIUS),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::Color;
|
||||
use map_model::{LaneID, Map};
|
||||
use objects::{Ctx, ID};
|
||||
@ -87,14 +86,14 @@ impl Plugin for Floodfiller {
|
||||
match (self, obj) {
|
||||
(Floodfiller::Active { visited, queue }, ID::Lane(l)) => {
|
||||
if visited.contains(&l) {
|
||||
return Some(ctx.cs.get(Colors::Visited));
|
||||
return Some(ctx.cs.get("visited in floodfill", Color::BLUE));
|
||||
}
|
||||
if !queue.is_empty() && *queue.front().unwrap() == l {
|
||||
return Some(ctx.cs.get(Colors::NextQueued));
|
||||
return Some(ctx.cs.get("next to visit in floodfill", Color::GREEN));
|
||||
}
|
||||
// TODO linear search shouldnt suck too much for interactive mode
|
||||
if queue.contains(&l) {
|
||||
return Some(ctx.cs.get(Colors::Queued));
|
||||
return Some(ctx.cs.get("queued in floodfill", Color::RED));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use colors::ColorScheme;
|
||||
use control::ControlMap;
|
||||
use ezgui::{GfxCtx, Wizard, WrappedWizard};
|
||||
use map_model::Map;
|
||||
@ -39,6 +40,7 @@ impl Plugin for EditsManager {
|
||||
&ctx.primary.control_map,
|
||||
ctx.kml,
|
||||
&mut new_primary,
|
||||
ctx.cs,
|
||||
wizard.wrap(ctx.input),
|
||||
).is_some()
|
||||
{
|
||||
@ -77,6 +79,7 @@ fn manage_edits(
|
||||
control_map: &ControlMap,
|
||||
kml: &Option<String>,
|
||||
new_primary: &mut Option<(PerMapUI, PluginsPerMap)>,
|
||||
cs: &mut ColorScheme,
|
||||
mut wizard: WrappedWizard,
|
||||
) -> Option<()> {
|
||||
// TODO Indicate how many edits are there / if there are any unsaved edits
|
||||
@ -122,7 +125,7 @@ fn manage_edits(
|
||||
flags.edits_name = load_name;
|
||||
|
||||
info!("Reloading everything...");
|
||||
*new_primary = Some(PerMapUI::new(flags, kml));
|
||||
*new_primary = Some(PerMapUI::new(flags, kml, cs));
|
||||
Some(())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -136,7 +136,7 @@ impl Region {
|
||||
|
||||
const COLORS: [Color; 3] = [
|
||||
// TODO these are awful choices
|
||||
[1.0, 0.0, 0.0, 0.8],
|
||||
[0.0, 1.0, 0.0, 0.8],
|
||||
[0.0, 0.0, 1.0, 0.8],
|
||||
Color([1.0, 0.0, 0.0, 0.8]),
|
||||
Color([0.0, 1.0, 0.0, 0.8]),
|
||||
Color([0.0, 0.0, 1.0, 0.8]),
|
||||
];
|
||||
|
@ -89,7 +89,7 @@ impl Plugin for RoadEditor {
|
||||
// TODO Pretty sure control map needs to recalculate based on the new turns
|
||||
let old_type = map.get_l(id).lane_type;
|
||||
map.edit_lane_type(id, new_type);
|
||||
draw_map.edit_lane_type(id, map, control_map);
|
||||
draw_map.edit_lane_type(id, map, control_map, ctx.cs);
|
||||
sim.edit_lane_type(id, old_type, map);
|
||||
|
||||
// Add turns back
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ezgui::{GfxCtx, LogScroller, Wizard, WrappedWizard};
|
||||
use ezgui::{Color, GfxCtx, LogScroller, Wizard, WrappedWizard};
|
||||
use geom::Polygon;
|
||||
use map_model::Map;
|
||||
use objects::{Ctx, SIM_SETUP};
|
||||
@ -88,7 +88,11 @@ impl Plugin for ScenarioManager {
|
||||
}
|
||||
ScenarioManager::EditScenario(_, wizard) => {
|
||||
if let Some(neighborhood) = wizard.current_menu_choice::<Neighborhood>() {
|
||||
g.draw_polygon([0.0, 0.0, 1.0, 0.6], &Polygon::new(&neighborhood.points));
|
||||
g.draw_polygon(
|
||||
ctx.cs
|
||||
.get("neighborhood polygon", Color::rgba(0, 0, 255, 0.6)),
|
||||
&Polygon::new(&neighborhood.points),
|
||||
);
|
||||
}
|
||||
wizard.draw(g, ctx.canvas);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::{ColorScheme, Colors};
|
||||
use colors::ColorScheme;
|
||||
use ezgui::{Color, GfxCtx, InputResult, TextBox};
|
||||
use objects::{Ctx, DEBUG_EXTRA, ID};
|
||||
use piston::input::Key;
|
||||
@ -14,11 +14,15 @@ pub enum SearchState {
|
||||
}
|
||||
|
||||
impl SearchState {
|
||||
fn choose_color(&self, osm_tags: &BTreeMap<String, String>, cs: &ColorScheme) -> Option<Color> {
|
||||
fn choose_color(
|
||||
&self,
|
||||
osm_tags: &BTreeMap<String, String>,
|
||||
cs: &mut ColorScheme,
|
||||
) -> Option<Color> {
|
||||
if let SearchState::FilterOSM(filter) = self {
|
||||
for (k, v) in osm_tags {
|
||||
if format!("{}={}", k, v).contains(filter) {
|
||||
return Some(cs.get(Colors::SearchResult));
|
||||
return Some(cs.get("search result", Color::RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use colors::Colors;
|
||||
use ezgui::Color;
|
||||
use map_model::BuildingID;
|
||||
use objects::{Ctx, ID};
|
||||
@ -65,16 +64,16 @@ impl Plugin for ShowOwnerState {
|
||||
}
|
||||
|
||||
fn color_for(&self, obj: ID, ctx: Ctx) -> Option<Color> {
|
||||
let color = ctx.cs.get("car/building owner", Color::PURPLE);
|
||||
match (self, obj) {
|
||||
(ShowOwnerState::BuildingSelected(_, cars), ID::Car(id)) => {
|
||||
if cars.contains(&id) {
|
||||
// TODO really got lazy defining colors
|
||||
return Some(ctx.cs.get(Colors::SearchResult));
|
||||
return Some(color);
|
||||
}
|
||||
}
|
||||
(ShowOwnerState::CarSelected(_, Some(id1)), ID::Building(id2)) => {
|
||||
if *id1 == id2 {
|
||||
return Some(ctx.cs.get(Colors::SearchResult));
|
||||
return Some(color);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use colors::Colors;
|
||||
use dimensioned::si;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use map_model::{Trace, LANE_THICKNESS};
|
||||
use objects::Ctx;
|
||||
use piston::input::Key;
|
||||
@ -70,7 +69,7 @@ impl Plugin for ShowRouteState {
|
||||
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
if let ShowRouteState::Active(_, trace) = self {
|
||||
g.draw_polygon(
|
||||
ctx.cs.get(Colors::Queued),
|
||||
ctx.cs.get("route", Color::rgba(255, 0, 0, 0.8)),
|
||||
&trace.get_polyline().make_polygons_blindly(LANE_THICKNESS),
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{Canvas, EventLoopMode, GfxCtx, Text, UserInput, TOP_RIGHT};
|
||||
use ezgui::{Canvas, Color, EventLoopMode, GfxCtx, Text, UserInput, TOP_RIGHT};
|
||||
use objects::SIM;
|
||||
use piston::input::Key;
|
||||
use sim::{Benchmark, ScoreSummary, TIMESTEP};
|
||||
@ -180,8 +180,8 @@ impl SimController {
|
||||
fn summarize(txt: &mut Text, summary: ScoreSummary) {
|
||||
txt.add_styled_line(
|
||||
"Walking".to_string(),
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
Some([1.0, 0.0, 0.0, 0.8]),
|
||||
Color::BLACK,
|
||||
Some(Color::rgba(255, 0, 0, 0.8)),
|
||||
);
|
||||
txt.add_line(format!(
|
||||
" {}/{} trips done",
|
||||
@ -192,8 +192,8 @@ fn summarize(txt: &mut Text, summary: ScoreSummary) {
|
||||
|
||||
txt.add_styled_line(
|
||||
"Driving".to_string(),
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
Some([0.0, 0.0, 1.0, 0.8]),
|
||||
Color::BLACK,
|
||||
Some(Color::rgba(0, 0, 255, 0.8)),
|
||||
);
|
||||
txt.add_line(format!(
|
||||
" {}/{} trips done",
|
||||
|
@ -63,7 +63,7 @@ impl Plugin for SteepnessVisualizer {
|
||||
ID::Lane(l) => {
|
||||
let normalized = (self.get_delta(ctx.map, ctx.map.get_l(l)) - self.min_difference)
|
||||
/ (self.max_difference - self.min_difference);
|
||||
Some([normalized as f32, 0.0, 0.0, 1.0])
|
||||
Some(Color::rgb_f(normalized as f32, 0.0, 0.0))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use control::stop_signs::TurnPriority;
|
||||
use ezgui::Color;
|
||||
use map_model::IntersectionID;
|
||||
@ -105,12 +104,14 @@ impl Plugin for StopSignEditor {
|
||||
match (self, obj) {
|
||||
(StopSignEditor::Active(i), ID::Turn(t)) => {
|
||||
if t.parent != *i {
|
||||
return Some(ctx.cs.get(Colors::TurnIrrelevant));
|
||||
return Some(ctx.cs.get("irrelevant turn", Color::grey(0.3)));
|
||||
}
|
||||
match ctx.control_map.stop_signs[i].get_priority(t) {
|
||||
TurnPriority::Priority => Some(ctx.cs.get(Colors::PriorityTurn)),
|
||||
TurnPriority::Yield => Some(ctx.cs.get(Colors::YieldTurn)),
|
||||
TurnPriority::Stop => Some(ctx.cs.get(Colors::StopTurn)),
|
||||
TurnPriority::Priority => {
|
||||
Some(ctx.cs.get("priority stop sign turn", Color::GREEN))
|
||||
}
|
||||
TurnPriority::Yield => Some(ctx.cs.get("yield stop sign turn", Color::YELLOW)),
|
||||
TurnPriority::Stop => Some(ctx.cs.get("stop turn", Color::RED)),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
// TODO how to edit cycle time?
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::Color;
|
||||
use map_model::IntersectionID;
|
||||
use objects::{Ctx, ID};
|
||||
@ -117,23 +116,20 @@ impl Plugin for TrafficSignalEditor {
|
||||
match (self, obj) {
|
||||
(TrafficSignalEditor::Active { i, current_cycle }, ID::Turn(t)) => {
|
||||
if t.parent != *i {
|
||||
return Some(ctx.cs.get(Colors::TurnIrrelevant));
|
||||
return Some(ctx.cs.get("irrelevant turn", Color::grey(0.3)));
|
||||
}
|
||||
|
||||
let cycle = &ctx.control_map.traffic_signals[&i].cycles[*current_cycle];
|
||||
|
||||
if cycle.contains(t) {
|
||||
Some(ctx.cs.get(Colors::SignalEditorTurnInCurrentCycle))
|
||||
Some(ctx.cs.get("turn in current cycle", Color::GREEN))
|
||||
} else if !cycle.conflicts_with(t, ctx.map) {
|
||||
Some(
|
||||
ctx.cs
|
||||
.get(Colors::SignalEditorTurnCompatibleWithCurrentCycle),
|
||||
)
|
||||
Some(ctx.cs.get(
|
||||
"turn could be in current cycle",
|
||||
Color::rgba(0, 255, 0, 0.2),
|
||||
))
|
||||
} else {
|
||||
Some(
|
||||
ctx.cs
|
||||
.get(Colors::SignalEditorTurnConflictsWithCurrentCycle),
|
||||
)
|
||||
Some(ctx.cs.get("turn conflicts with current cycle", Color::RED))
|
||||
}
|
||||
// TODO maybe something to indicate unused in any cycle so far
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use map_model::{IntersectionID, LaneID};
|
||||
use objects::{Ctx, ID};
|
||||
@ -65,6 +64,8 @@ impl Plugin for TurnCyclerState {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
let color = ctx.cs.get("current selected turn", Color::RED);
|
||||
|
||||
match self {
|
||||
TurnCyclerState::Inactive => {}
|
||||
TurnCyclerState::Active(l, current_turn_index) => {
|
||||
@ -74,12 +75,10 @@ impl Plugin for TurnCyclerState {
|
||||
Some(idx) => {
|
||||
let turn = relevant_turns[idx % relevant_turns.len()];
|
||||
let draw_turn = ctx.draw_map.get_t(turn.id);
|
||||
draw_turn.draw_full(g, ctx.cs.get(Colors::Turn));
|
||||
draw_turn.draw_full(g, color);
|
||||
}
|
||||
None => for turn in &relevant_turns {
|
||||
ctx.draw_map
|
||||
.get_t(turn.id)
|
||||
.draw_full(g, ctx.cs.get(Colors::Turn));
|
||||
ctx.draw_map.get_t(turn.id).draw_full(g, color);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -90,9 +89,7 @@ impl Plugin for TurnCyclerState {
|
||||
let (cycle, _) =
|
||||
signal.current_cycle_and_remaining_time(ctx.sim.time.as_time());
|
||||
for t in &cycle.turns {
|
||||
ctx.draw_map
|
||||
.get_t(*t)
|
||||
.draw_full(g, ctx.cs.get(Colors::Turn));
|
||||
ctx.draw_map.get_t(*t).draw_full(g, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +103,10 @@ impl Plugin for TurnCyclerState {
|
||||
|
||||
let relevant_turns = ctx.map.get_turns_from_lane(*l);
|
||||
if relevant_turns[idx % relevant_turns.len()].conflicts_with(ctx.map.get_t(t)) {
|
||||
Some(ctx.cs.get(Colors::ConflictingTurn))
|
||||
Some(ctx.cs.get(
|
||||
"turn conflicts with current turn",
|
||||
Color::rgba(255, 0, 0, 0.5),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use colors::Colors;
|
||||
use ezgui::GfxCtx;
|
||||
use colors::ColorScheme;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Polygon, Pt2D};
|
||||
use map_model::{Area, AreaID, AreaType, Map};
|
||||
use objects::{Ctx, ID};
|
||||
@ -9,18 +9,19 @@ use render::{RenderOptions, Renderable};
|
||||
pub struct DrawArea {
|
||||
pub id: AreaID,
|
||||
fill_polygon: Polygon,
|
||||
color: Colors,
|
||||
// TODO precomputing this means live color picker changes won't work. :(
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl DrawArea {
|
||||
pub fn new(area: &Area) -> DrawArea {
|
||||
pub fn new(area: &Area, cs: &mut ColorScheme) -> DrawArea {
|
||||
DrawArea {
|
||||
id: area.id,
|
||||
fill_polygon: area.get_polygon(),
|
||||
color: match area.area_type {
|
||||
AreaType::Park => Colors::ParkArea,
|
||||
AreaType::Swamp => Colors::SwampArea,
|
||||
AreaType::Water => Colors::WaterArea,
|
||||
AreaType::Park => cs.get("park area", Color::GREEN),
|
||||
AreaType::Swamp => cs.get("swamp area", Color::rgb_f(0.0, 1.0, 0.6)),
|
||||
AreaType::Water => cs.get("water area", Color::BLUE),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -31,11 +32,8 @@ impl Renderable for DrawArea {
|
||||
ID::Area(self.id)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
g.draw_polygon(
|
||||
opts.color.unwrap_or(ctx.cs.get(self.color)),
|
||||
&self.fill_polygon,
|
||||
);
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, _ctx: Ctx) {
|
||||
g.draw_polygon(opts.color.unwrap_or(self.color), &self.fill_polygon);
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> Bounds {
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Line, PolyLine, Polygon, Pt2D};
|
||||
use map_model::{Building, BuildingID, Map};
|
||||
use objects::{Ctx, ID};
|
||||
@ -35,14 +34,19 @@ impl Renderable for DrawBuilding {
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
// Buildings look better without boundaries, actually
|
||||
//g.draw_polygon(ctx.cs.get(Colors::BuildingBoundary), &self.boundary_polygon);
|
||||
//g.draw_polygon(ctx.cs.get("building boundary", Color::rgb(0, 100, 0)), &self.boundary_polygon);
|
||||
g.draw_polygon(
|
||||
opts.color.unwrap_or(ctx.cs.get(Colors::Building)),
|
||||
opts.color
|
||||
.unwrap_or(ctx.cs.get("building", Color::rgba_f(0.7, 0.7, 0.7, 0.8))),
|
||||
&self.fill_polygon,
|
||||
);
|
||||
|
||||
// TODO tune width
|
||||
g.draw_rounded_line(ctx.cs.get(Colors::BuildingPath), 1.0, &self.front_path);
|
||||
g.draw_rounded_line(
|
||||
ctx.cs.get("building path", Color::grey(0.6)),
|
||||
1.0,
|
||||
&self.front_path,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> Bounds {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use colors::Colors;
|
||||
use dimensioned::si;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, PolyLine, Polygon, Pt2D};
|
||||
use map_model::{BusStop, BusStopID, Map, LANE_THICKNESS};
|
||||
use objects::{Ctx, ID};
|
||||
@ -42,7 +41,10 @@ impl Renderable for DrawBusStop {
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
g.draw_polygon(
|
||||
opts.color.unwrap_or(ctx.cs.get(Colors::BusStopMarking)),
|
||||
opts.color.unwrap_or(
|
||||
ctx.cs
|
||||
.get("bus stop marking", Color::rgba(220, 160, 220, 0.8)),
|
||||
),
|
||||
&self.polygon,
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use colors::Colors;
|
||||
use ezgui::{shift_color, GfxCtx};
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Angle, Bounds, Line, PolyLine, Polygon, Pt2D};
|
||||
use map_model::Map;
|
||||
use objects::{Ctx, ID};
|
||||
@ -91,24 +90,34 @@ impl Renderable for DrawCar {
|
||||
let color = opts.color.unwrap_or_else(|| {
|
||||
// TODO if it's a bus, color it differently -- but how? :\
|
||||
match ctx.sim.get_car_state(self.id) {
|
||||
CarState::Debug => shift_color(ctx.cs.get(Colors::DebugCar), self.id.0),
|
||||
CarState::Moving => shift_color(ctx.cs.get(Colors::MovingCar), self.id.0),
|
||||
CarState::Stuck => shift_color(ctx.cs.get(Colors::StuckCar), self.id.0),
|
||||
CarState::Parked => shift_color(ctx.cs.get(Colors::ParkedCar), self.id.0),
|
||||
CarState::Debug => ctx
|
||||
.cs
|
||||
.get("debug car", Color::rgba(0, 0, 255, 0.8))
|
||||
.shift(self.id.0),
|
||||
CarState::Moving => ctx.cs.get("moving car", Color::CYAN).shift(self.id.0),
|
||||
CarState::Stuck => ctx.cs.get("stuck car", Color::RED).shift(self.id.0),
|
||||
CarState::Parked => ctx
|
||||
.cs
|
||||
.get("parked car", Color::rgb(180, 233, 76))
|
||||
.shift(self.id.0),
|
||||
}
|
||||
});
|
||||
g.draw_polygon(color, &self.body_polygon);
|
||||
for p in &self.window_polygons {
|
||||
g.draw_polygon([0.0, 0.0, 0.0, 1.0], p);
|
||||
g.draw_polygon(ctx.cs.get("car window", Color::BLACK), p);
|
||||
}
|
||||
|
||||
// TODO tune color, sizes
|
||||
if let Some(ref a) = self.turn_arrow {
|
||||
g.draw_arrow([0.0, 1.0, 1.0, 1.0], 0.25, 1.0, a);
|
||||
g.draw_arrow(ctx.cs.get("car turn arrow", Color::CYAN), 0.25, 1.0, a);
|
||||
}
|
||||
|
||||
if let Some(ref t) = self.stopping_buffer {
|
||||
g.draw_polygon([1.0, 0.0, 0.0, 0.7], t);
|
||||
g.draw_polygon(
|
||||
ctx.cs
|
||||
.get("car stopping buffer", Color::rgba(255, 0, 0, 0.7)),
|
||||
t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use colors::Colors;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Circle, Polygon, Pt2D};
|
||||
use kml::{ExtraShape, ExtraShapeGeom, ExtraShapeID};
|
||||
use map_model::Map;
|
||||
@ -50,7 +49,7 @@ impl Renderable for DrawExtraShape {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
let color = opts.color.unwrap_or(ctx.cs.get(Colors::ExtraShape));
|
||||
let color = opts.color.unwrap_or(ctx.cs.get("extra shape", Color::CYAN));
|
||||
match self.shape {
|
||||
Shape::Polygon(ref p) => g.draw_polygon(color, &p),
|
||||
Shape::Circle(ref c) => g.draw_circle(color, c),
|
||||
|
@ -1,8 +1,7 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use dimensioned::si;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Circle, Line, Polygon, Pt2D};
|
||||
use map_model::{Intersection, IntersectionID, LaneType, Map, LANE_THICKNESS};
|
||||
use objects::{Ctx, ID};
|
||||
@ -52,7 +51,7 @@ impl DrawIntersection {
|
||||
fn draw_stop_sign(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
// TODO rotate it
|
||||
g.draw_polygon(
|
||||
ctx.cs.get(Colors::StopSignBackground),
|
||||
ctx.cs.get("stop sign background", Color::RED),
|
||||
&Polygon::regular_polygon(self.center, 8, 1.5),
|
||||
);
|
||||
// TODO draw "STOP"
|
||||
@ -62,7 +61,7 @@ impl DrawIntersection {
|
||||
let radius = 0.5;
|
||||
|
||||
g.draw_rectangle(
|
||||
ctx.cs.get(Colors::TrafficSignalBox),
|
||||
ctx.cs.get("traffic signal box", Color::BLACK),
|
||||
[
|
||||
self.center.x() - (2.0 * radius),
|
||||
self.center.y() - (4.0 * radius),
|
||||
@ -72,17 +71,17 @@ impl DrawIntersection {
|
||||
);
|
||||
|
||||
g.draw_circle(
|
||||
ctx.cs.get(Colors::TrafficSignalYellow),
|
||||
ctx.cs.get("traffic signal yellow", Color::YELLOW),
|
||||
&Circle::new(self.center, radius),
|
||||
);
|
||||
|
||||
g.draw_circle(
|
||||
ctx.cs.get(Colors::TrafficSignalGreen),
|
||||
ctx.cs.get("traffic signal green", Color::GREEN),
|
||||
&Circle::new(self.center.offset(0.0, radius * 2.0), radius),
|
||||
);
|
||||
|
||||
g.draw_circle(
|
||||
ctx.cs.get(Colors::TrafficSignalRed),
|
||||
ctx.cs.get("traffic signal red", Color::RED),
|
||||
&Circle::new(self.center.offset(0.0, radius * -2.0), radius),
|
||||
);
|
||||
}
|
||||
@ -103,9 +102,10 @@ impl Renderable for DrawIntersection {
|
||||
false
|
||||
};
|
||||
if changed {
|
||||
ctx.cs.get(Colors::ChangedIntersection)
|
||||
ctx.cs
|
||||
.get("changed intersection", Color::rgb_f(0.8, 0.6, 0.6))
|
||||
} else {
|
||||
ctx.cs.get(Colors::UnchangedIntersection)
|
||||
ctx.cs.get("unchanged intersection", Color::grey(0.6))
|
||||
}
|
||||
});
|
||||
g.draw_polygon(color, &self.polygon);
|
||||
@ -113,7 +113,7 @@ impl Renderable for DrawIntersection {
|
||||
for crosswalk in &self.crosswalks {
|
||||
for line in crosswalk {
|
||||
g.draw_line(
|
||||
ctx.cs.get(Colors::Crosswalk),
|
||||
ctx.cs.get("crosswalk", Color::WHITE),
|
||||
// TODO move this somewhere
|
||||
0.25,
|
||||
line,
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use colors::ColorScheme;
|
||||
use control::ControlMap;
|
||||
use dimensioned::si;
|
||||
use ezgui::{GfxCtx, Text};
|
||||
use ezgui::{Color, GfxCtx, Text};
|
||||
use geom::{Bounds, Circle, Line, Polygon, Pt2D};
|
||||
use map_model;
|
||||
use map_model::{LaneID, LANE_THICKNESS};
|
||||
@ -15,7 +15,8 @@ const MIN_ZOOM_FOR_LANE_MARKERS: f64 = 5.0;
|
||||
#[derive(Debug)]
|
||||
struct Marking {
|
||||
lines: Vec<Line>,
|
||||
color: Colors,
|
||||
// TODO precomputing ruins live color picker changes
|
||||
color: Color,
|
||||
thickness: f64,
|
||||
round: bool,
|
||||
}
|
||||
@ -33,7 +34,12 @@ pub struct DrawLane {
|
||||
}
|
||||
|
||||
impl DrawLane {
|
||||
pub fn new(lane: &map_model::Lane, map: &map_model::Map, control_map: &ControlMap) -> DrawLane {
|
||||
pub fn new(
|
||||
lane: &map_model::Lane,
|
||||
map: &map_model::Map,
|
||||
control_map: &ControlMap,
|
||||
cs: &mut ColorScheme,
|
||||
) -> DrawLane {
|
||||
let road = map.get_r(lane.parent);
|
||||
let start = new_perp_line(lane.first_line(), LANE_THICKNESS);
|
||||
let end = new_perp_line(lane.last_line().reverse(), LANE_THICKNESS);
|
||||
@ -43,27 +49,27 @@ impl DrawLane {
|
||||
if road.is_canonical_lane(lane.id) {
|
||||
markings.push(Marking {
|
||||
lines: road.center_pts.lines(),
|
||||
color: Colors::RoadOrientation,
|
||||
color: cs.get("road center line", Color::YELLOW),
|
||||
thickness: BIG_ARROW_THICKNESS,
|
||||
round: true,
|
||||
});
|
||||
}
|
||||
match lane.lane_type {
|
||||
map_model::LaneType::Sidewalk => {
|
||||
markings.push(calculate_sidewalk_lines(lane));
|
||||
markings.push(calculate_sidewalk_lines(lane, cs));
|
||||
}
|
||||
map_model::LaneType::Parking => {
|
||||
markings.push(calculate_parking_lines(lane));
|
||||
markings.push(calculate_parking_lines(lane, cs));
|
||||
}
|
||||
map_model::LaneType::Driving => {
|
||||
for m in calculate_driving_lines(lane, road) {
|
||||
for m in calculate_driving_lines(lane, road, cs) {
|
||||
markings.push(m);
|
||||
}
|
||||
}
|
||||
map_model::LaneType::Biking => {}
|
||||
};
|
||||
if lane.is_driving() && !map.get_i(lane.dst_i).has_traffic_signal {
|
||||
if let Some(m) = calculate_stop_sign_line(lane, control_map) {
|
||||
if let Some(m) = calculate_stop_sign_line(lane, control_map, cs) {
|
||||
markings.push(m);
|
||||
}
|
||||
}
|
||||
@ -79,11 +85,13 @@ impl DrawLane {
|
||||
}
|
||||
|
||||
fn draw_debug(&self, g: &mut GfxCtx, ctx: Ctx) {
|
||||
let circle_color = ctx.cs.get(Colors::BrightDebug);
|
||||
let circle_color = ctx
|
||||
.cs
|
||||
.get("debug line endpoint", Color::rgb_f(0.8, 0.1, 0.1));
|
||||
|
||||
for l in ctx.map.get_l(self.id).lane_center_pts.lines() {
|
||||
g.draw_line(
|
||||
ctx.cs.get(Colors::Debug),
|
||||
ctx.cs.get("debug line", Color::RED),
|
||||
PARCEL_BOUNDARY_THICKNESS / 2.0,
|
||||
&l,
|
||||
);
|
||||
@ -117,13 +125,13 @@ impl Renderable for DrawLane {
|
||||
let color = opts.color.unwrap_or_else(|| {
|
||||
let l = ctx.map.get_l(self.id);
|
||||
let mut default = match l.lane_type {
|
||||
map_model::LaneType::Driving => ctx.cs.get(Colors::Road),
|
||||
map_model::LaneType::Parking => ctx.cs.get(Colors::Parking),
|
||||
map_model::LaneType::Sidewalk => ctx.cs.get(Colors::Sidewalk),
|
||||
map_model::LaneType::Biking => ctx.cs.get(Colors::Biking),
|
||||
map_model::LaneType::Driving => ctx.cs.get("driving lane", Color::BLACK),
|
||||
map_model::LaneType::Parking => ctx.cs.get("parking lane", Color::grey(0.2)),
|
||||
map_model::LaneType::Sidewalk => ctx.cs.get("sidewalk", Color::grey(0.8)),
|
||||
map_model::LaneType::Biking => ctx.cs.get("bike lane", Color::rgb(15, 125, 75)),
|
||||
};
|
||||
if l.probably_broken {
|
||||
default = ctx.cs.get(Colors::Broken);
|
||||
default = ctx.cs.get("broken lane", Color::rgb_f(1.0, 0.0, 0.565));
|
||||
}
|
||||
default
|
||||
});
|
||||
@ -133,9 +141,9 @@ impl Renderable for DrawLane {
|
||||
for m in &self.markings {
|
||||
for line in &m.lines {
|
||||
if m.round {
|
||||
g.draw_rounded_line(ctx.cs.get(m.color), m.thickness, line);
|
||||
g.draw_rounded_line(m.color, m.thickness, line);
|
||||
} else {
|
||||
g.draw_line(ctx.cs.get(m.color), m.thickness, line);
|
||||
g.draw_line(m.color, m.thickness, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,7 +199,7 @@ fn new_perp_line(l: Line, length: f64) -> Line {
|
||||
Line::new(pt1, pt2)
|
||||
}
|
||||
|
||||
fn calculate_sidewalk_lines(lane: &map_model::Lane) -> Marking {
|
||||
fn calculate_sidewalk_lines(lane: &map_model::Lane, cs: &mut ColorScheme) -> Marking {
|
||||
let tile_every = LANE_THICKNESS * si::M;
|
||||
|
||||
let length = lane.length();
|
||||
@ -209,13 +217,13 @@ fn calculate_sidewalk_lines(lane: &map_model::Lane) -> Marking {
|
||||
|
||||
Marking {
|
||||
lines,
|
||||
color: Colors::SidewalkMarking,
|
||||
color: cs.get("sidewalk lines", Color::grey(0.7)),
|
||||
thickness: 0.25,
|
||||
round: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_parking_lines(lane: &map_model::Lane) -> Marking {
|
||||
fn calculate_parking_lines(lane: &map_model::Lane, cs: &mut ColorScheme) -> Marking {
|
||||
// meters, but the dims get annoying below to remove
|
||||
// TODO make Pt2D natively understand meters, projecting away by an angle
|
||||
let leg_length = 1.0;
|
||||
@ -244,13 +252,17 @@ fn calculate_parking_lines(lane: &map_model::Lane) -> Marking {
|
||||
|
||||
Marking {
|
||||
lines,
|
||||
color: Colors::ParkingMarking,
|
||||
color: cs.get("parking line", Color::WHITE),
|
||||
thickness: 0.25,
|
||||
round: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_driving_lines(lane: &map_model::Lane, parent: &map_model::Road) -> Option<Marking> {
|
||||
fn calculate_driving_lines(
|
||||
lane: &map_model::Lane,
|
||||
parent: &map_model::Road,
|
||||
cs: &mut ColorScheme,
|
||||
) -> Option<Marking> {
|
||||
// The rightmost lanes don't have dashed white lines.
|
||||
if parent.dir_and_offset(lane.id).1 == 0 {
|
||||
return None;
|
||||
@ -281,13 +293,17 @@ fn calculate_driving_lines(lane: &map_model::Lane, parent: &map_model::Road) ->
|
||||
|
||||
Some(Marking {
|
||||
lines,
|
||||
color: Colors::DrivingLaneMarking,
|
||||
color: cs.get("dashed lane line", Color::WHITE),
|
||||
thickness: 0.25,
|
||||
round: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn calculate_stop_sign_line(lane: &map_model::Lane, control_map: &ControlMap) -> Option<Marking> {
|
||||
fn calculate_stop_sign_line(
|
||||
lane: &map_model::Lane,
|
||||
control_map: &ControlMap,
|
||||
cs: &mut ColorScheme,
|
||||
) -> Option<Marking> {
|
||||
if control_map.stop_signs[&lane.dst_i].is_priority_lane(lane.id) {
|
||||
return None;
|
||||
}
|
||||
@ -299,7 +315,7 @@ fn calculate_stop_sign_line(lane: &map_model::Lane, control_map: &ControlMap) ->
|
||||
let pt2 = pt1.project_away(1.0, angle);
|
||||
Some(Marking {
|
||||
lines: vec![perp_line(Line::new(pt1, pt2), LANE_THICKNESS)],
|
||||
color: Colors::StopSignMarking,
|
||||
color: cs.get("stop line for lane", Color::RED),
|
||||
thickness: 0.45,
|
||||
round: true,
|
||||
})
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use aabb_quadtree::QuadTree;
|
||||
use colors::ColorScheme;
|
||||
use control::ControlMap;
|
||||
use geom::{Bounds, LonLat, Pt2D};
|
||||
use kml::{ExtraShape, ExtraShapeID};
|
||||
@ -42,10 +43,16 @@ pub struct DrawMap {
|
||||
}
|
||||
|
||||
impl DrawMap {
|
||||
pub fn new(map: &Map, control_map: &ControlMap, raw_extra_shapes: Vec<ExtraShape>) -> DrawMap {
|
||||
// TODO really hacky to take ColorScheme here for lanes and areas. :(
|
||||
pub fn new(
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
raw_extra_shapes: Vec<ExtraShape>,
|
||||
cs: &mut ColorScheme,
|
||||
) -> DrawMap {
|
||||
let mut lanes: Vec<DrawLane> = Vec::new();
|
||||
for l in map.all_lanes() {
|
||||
lanes.push(DrawLane::new(l, map, control_map));
|
||||
lanes.push(DrawLane::new(l, map, control_map, cs));
|
||||
}
|
||||
|
||||
let mut turn_to_lane_offset: HashMap<TurnID, usize> = HashMap::new();
|
||||
@ -81,7 +88,11 @@ impl DrawMap {
|
||||
for s in map.all_bus_stops().values() {
|
||||
bus_stops.insert(s.id, DrawBusStop::new(s, map));
|
||||
}
|
||||
let areas: Vec<DrawArea> = map.all_areas().iter().map(|a| DrawArea::new(a)).collect();
|
||||
let areas: Vec<DrawArea> = map
|
||||
.all_areas()
|
||||
.iter()
|
||||
.map(|a| DrawArea::new(a, cs))
|
||||
.collect();
|
||||
|
||||
// min_y here due to the wacky y inversion
|
||||
let bounds = map.get_gps_bounds();
|
||||
@ -156,9 +167,15 @@ impl DrawMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit_lane_type(&mut self, id: LaneID, map: &Map, control_map: &ControlMap) {
|
||||
pub fn edit_lane_type(
|
||||
&mut self,
|
||||
id: LaneID,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
cs: &mut ColorScheme,
|
||||
) {
|
||||
// No need to edit the quadtree; the bbox shouldn't depend on lane type.
|
||||
self.lanes[id.0] = DrawLane::new(map.get_l(id), map, control_map);
|
||||
self.lanes[id.0] = DrawLane::new(map.get_l(id), map, control_map, cs);
|
||||
}
|
||||
|
||||
pub fn edit_remove_turn(&mut self, id: TurnID) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, PolyLine, Polygon, Pt2D};
|
||||
use map_model::{Map, Parcel, ParcelID};
|
||||
@ -9,20 +8,21 @@ use render::{RenderOptions, Renderable, PARCEL_BOUNDARY_THICKNESS};
|
||||
|
||||
const COLORS: [Color; 14] = [
|
||||
// TODO these are awful choices
|
||||
[1.0, 1.0, 0.0, 1.0],
|
||||
[1.0, 0.0, 1.0, 1.0],
|
||||
[0.0, 1.0, 1.0, 1.0],
|
||||
[0.5, 0.2, 0.7, 1.0],
|
||||
[0.5, 0.5, 0.0, 0.5],
|
||||
[0.5, 0.0, 0.5, 0.5],
|
||||
[0.0, 0.5, 0.5, 0.5],
|
||||
[0.0, 0.0, 0.5, 0.5],
|
||||
[0.3, 0.2, 0.5, 0.5],
|
||||
[0.4, 0.2, 0.5, 0.5],
|
||||
[0.5, 0.2, 0.5, 0.5],
|
||||
[0.6, 0.2, 0.5, 0.5],
|
||||
[0.7, 0.2, 0.5, 0.5],
|
||||
[0.8, 0.2, 0.5, 0.5],
|
||||
// TODO can we express these with the nicer functions? probably need constexpr
|
||||
Color([1.0, 1.0, 0.0, 1.0]),
|
||||
Color([1.0, 0.0, 1.0, 1.0]),
|
||||
Color([0.0, 1.0, 1.0, 1.0]),
|
||||
Color([0.5, 0.2, 0.7, 1.0]),
|
||||
Color([0.5, 0.5, 0.0, 0.5]),
|
||||
Color([0.5, 0.0, 0.5, 0.5]),
|
||||
Color([0.0, 0.5, 0.5, 0.5]),
|
||||
Color([0.0, 0.0, 0.5, 0.5]),
|
||||
Color([0.3, 0.2, 0.5, 0.5]),
|
||||
Color([0.4, 0.2, 0.5, 0.5]),
|
||||
Color([0.5, 0.2, 0.5, 0.5]),
|
||||
Color([0.6, 0.2, 0.5, 0.5]),
|
||||
Color([0.7, 0.2, 0.5, 0.5]),
|
||||
Color([0.8, 0.2, 0.5, 0.5]),
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -56,7 +56,10 @@ impl Renderable for DrawParcel {
|
||||
});
|
||||
g.draw_polygon(color, &self.fill_polygon);
|
||||
|
||||
g.draw_polygon(ctx.cs.get(Colors::ParcelBoundary), &self.boundary_polygon);
|
||||
g.draw_polygon(
|
||||
ctx.cs.get("parcel boundary", Color::grey(0.3)),
|
||||
&self.boundary_polygon,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> Bounds {
|
||||
|
@ -1,5 +1,4 @@
|
||||
use colors::Colors;
|
||||
use ezgui::{shift_color, GfxCtx};
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Circle, Line, Pt2D};
|
||||
use map_model::Map;
|
||||
use objects::{Ctx, ID};
|
||||
@ -39,14 +38,21 @@ impl Renderable for DrawPedestrian {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
let color = opts
|
||||
.color
|
||||
.unwrap_or(shift_color(ctx.cs.get(Colors::Pedestrian), self.id.0));
|
||||
let color = opts.color.unwrap_or_else(|| {
|
||||
ctx.cs
|
||||
.get("pedestrian", Color::rgb_f(0.2, 0.7, 0.7))
|
||||
.shift(self.id.0)
|
||||
});
|
||||
g.draw_circle(color, &self.circle);
|
||||
|
||||
// TODO tune color, sizes
|
||||
if let Some(ref a) = self.turn_arrow {
|
||||
g.draw_rounded_arrow([0.0, 1.0, 1.0, 1.0], 0.25, 0.3, a);
|
||||
g.draw_rounded_arrow(
|
||||
ctx.cs.get("pedestrian turn arrow", Color::CYAN),
|
||||
0.25,
|
||||
0.3,
|
||||
a,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use colors::Colors;
|
||||
use dimensioned::si;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Circle, Line, Pt2D};
|
||||
@ -66,10 +65,14 @@ impl Renderable for DrawTurn {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: Ctx) {
|
||||
g.draw_circle(ctx.cs.get(Colors::TurnIconCircle), &self.icon_circle);
|
||||
g.draw_circle(
|
||||
ctx.cs.get("turn icon circle", Color::grey(0.3)),
|
||||
&self.icon_circle,
|
||||
);
|
||||
|
||||
g.draw_arrow(
|
||||
opts.color.unwrap_or(ctx.cs.get(Colors::TurnIconInactive)),
|
||||
opts.color
|
||||
.unwrap_or_else(|| ctx.cs.get("inactive turn icon", Color::grey(0.7))),
|
||||
TURN_ICON_ARROW_THICKNESS,
|
||||
TURN_ICON_ARROW_TIP_LENGTH,
|
||||
&self.icon_arrow,
|
||||
|
@ -3,7 +3,7 @@
|
||||
// TODO this should just be a way to handle interactions between plugins
|
||||
|
||||
use abstutil;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use colors::ColorScheme;
|
||||
use control::ControlMap;
|
||||
//use cpuprofiler;
|
||||
use ezgui::{Canvas, Color, EventLoopMode, GfxCtx, Text, UserInput, BOTTOM_LEFT, GUI};
|
||||
@ -22,6 +22,7 @@ use plugins::Plugin;
|
||||
use render::{DrawMap, RenderOptions};
|
||||
use sim;
|
||||
use sim::{Sim, SimFlags};
|
||||
use std::cell::RefCell;
|
||||
use std::process;
|
||||
|
||||
const MIN_ZOOM_FOR_MOUSEOVER: f64 = 4.0;
|
||||
@ -38,7 +39,8 @@ pub struct UI {
|
||||
active_plugin: Option<usize>,
|
||||
|
||||
canvas: Canvas,
|
||||
cs: ColorScheme,
|
||||
// TODO mutable ColorScheme to slurp up defaults is NOT ideal.
|
||||
cs: RefCell<ColorScheme>,
|
||||
|
||||
// Remember this to support loading a new PerMapUI
|
||||
kml: Option<String>,
|
||||
@ -90,7 +92,7 @@ impl GUI for UI {
|
||||
};
|
||||
// TODO maybe make state line up with the map, so loading from a new map doesn't break
|
||||
abstutil::write_json("editor_state", &state).expect("Saving editor_state failed");
|
||||
abstutil::write_json("color_scheme", &self.cs).expect("Saving color_scheme failed");
|
||||
self.cs.borrow().save();
|
||||
info!("Saved editor_state and color_scheme");
|
||||
//cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
|
||||
process::exit(0);
|
||||
@ -119,7 +121,7 @@ impl GUI for UI {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, osd: Text) {
|
||||
g.clear(self.cs.get(Colors::Background));
|
||||
g.clear(self.cs.borrow_mut().get("map background", Color::WHITE));
|
||||
|
||||
let (statics, dynamics) = self.primary.draw_map.get_objects_onscreen(
|
||||
self.canvas.get_screen_bbox(),
|
||||
@ -139,7 +141,7 @@ impl GUI for UI {
|
||||
g,
|
||||
opts,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
cs: &mut self.cs.borrow_mut(),
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
draw_map: &self.primary.draw_map,
|
||||
@ -158,7 +160,7 @@ impl GUI for UI {
|
||||
g,
|
||||
opts,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
cs: &mut self.cs.borrow_mut(),
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
draw_map: &self.primary.draw_map,
|
||||
@ -172,7 +174,7 @@ impl GUI for UI {
|
||||
p.draw(
|
||||
g,
|
||||
Ctx {
|
||||
cs: &self.cs,
|
||||
cs: &mut self.cs.borrow_mut(),
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
draw_map: &self.primary.draw_map,
|
||||
@ -225,7 +227,11 @@ impl PluginsPerMap {
|
||||
}
|
||||
|
||||
impl PerMapUI {
|
||||
pub fn new(flags: SimFlags, kml: &Option<String>) -> (PerMapUI, PluginsPerMap) {
|
||||
pub fn new(
|
||||
flags: SimFlags,
|
||||
kml: &Option<String>,
|
||||
cs: &mut ColorScheme,
|
||||
) -> (PerMapUI, PluginsPerMap) {
|
||||
flame::start("setup");
|
||||
let (map, control_map, sim) = sim::load(flags.clone(), Some(sim::Tick::from_seconds(30)));
|
||||
let extra_shapes = if let Some(path) = kml {
|
||||
@ -235,7 +241,7 @@ impl PerMapUI {
|
||||
};
|
||||
|
||||
flame::start("draw_map");
|
||||
let draw_map = DrawMap::new(&map, &control_map, extra_shapes);
|
||||
let draw_map = DrawMap::new(&map, &control_map, extra_shapes, cs);
|
||||
flame::end("draw_map");
|
||||
|
||||
flame::end("setup");
|
||||
@ -299,7 +305,8 @@ impl UI {
|
||||
// Do this first, so anything logged by sim::load isn't lost.
|
||||
let logs = plugins::logs::DisplayLogs::new();
|
||||
|
||||
let (primary, primary_plugins) = PerMapUI::new(flags, &kml);
|
||||
let mut cs = ColorScheme::load().unwrap();
|
||||
let (primary, primary_plugins) = PerMapUI::new(flags, &kml, &mut cs);
|
||||
let mut ui = UI {
|
||||
primary,
|
||||
primary_plugins,
|
||||
@ -324,7 +331,7 @@ impl UI {
|
||||
active_plugin: None,
|
||||
|
||||
canvas: Canvas::new(),
|
||||
cs: ColorScheme::load("color_scheme").unwrap(),
|
||||
cs: RefCell::new(cs),
|
||||
|
||||
kml,
|
||||
};
|
||||
@ -377,11 +384,11 @@ impl UI {
|
||||
|
||||
fn color_obj(&self, id: ID) -> Option<Color> {
|
||||
if Some(id) == self.primary.current_selection {
|
||||
return Some(self.cs.get(Colors::Selected));
|
||||
return Some(self.cs.borrow_mut().get("selected", Color::BLUE));
|
||||
}
|
||||
|
||||
let ctx = Ctx {
|
||||
cs: &self.cs,
|
||||
cs: &mut self.cs.borrow_mut(),
|
||||
map: &self.primary.map,
|
||||
control_map: &self.primary.control_map,
|
||||
draw_map: &self.primary.draw_map,
|
||||
@ -414,7 +421,7 @@ impl UI {
|
||||
primary: &mut self.primary,
|
||||
secondary: &mut self.secondary,
|
||||
canvas: &mut self.canvas,
|
||||
cs: &mut self.cs,
|
||||
cs: &mut self.cs.borrow_mut(),
|
||||
input,
|
||||
osd,
|
||||
kml: &self.kml,
|
||||
|
@ -13,3 +13,5 @@ piston = "*"
|
||||
piston2d-graphics = "*"
|
||||
piston2d-opengl_graphics = "*"
|
||||
pistoncore-glutin_window = "*"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
112
ezgui/src/lib.rs
112
ezgui/src/lib.rs
@ -10,6 +10,8 @@ extern crate log;
|
||||
extern crate opengl_graphics;
|
||||
extern crate palette;
|
||||
extern crate piston;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod canvas;
|
||||
mod input;
|
||||
@ -32,6 +34,7 @@ pub use menu::Menu;
|
||||
use opengl_graphics::{GlGraphics, Texture};
|
||||
use piston::input::Key;
|
||||
pub use runner::{run, EventLoopMode, GUI};
|
||||
use std::fmt;
|
||||
pub use text::Text;
|
||||
pub use text_box::TextBox;
|
||||
pub use wizard::{Wizard, WrappedWizard};
|
||||
@ -59,13 +62,13 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
graphics::clear(color, self.gfx);
|
||||
graphics::clear(color.0, self.gfx);
|
||||
}
|
||||
|
||||
// Use graphics::Line internally for now, but make it easy to switch to something else by
|
||||
// picking this API now.
|
||||
pub fn draw_line(&mut self, color: Color, thickness: f64, line: &geom::Line) {
|
||||
graphics::Line::new(color, thickness).draw(
|
||||
graphics::Line::new(color.0, thickness).draw(
|
||||
line_to_array(line),
|
||||
&self.ctx.draw_state,
|
||||
self.ctx.transform,
|
||||
@ -74,7 +77,7 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn draw_rounded_line(&mut self, color: Color, thickness: f64, line: &geom::Line) {
|
||||
graphics::Line::new_round(color, thickness).draw(
|
||||
graphics::Line::new_round(color.0, thickness).draw(
|
||||
line_to_array(line),
|
||||
&self.ctx.draw_state,
|
||||
self.ctx.transform,
|
||||
@ -83,7 +86,7 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn draw_arrow(&mut self, color: Color, thickness: f64, head_size: f64, line: &geom::Line) {
|
||||
graphics::Line::new(color, thickness).draw_arrow(
|
||||
graphics::Line::new(color.0, thickness).draw_arrow(
|
||||
line_to_array(line),
|
||||
head_size,
|
||||
&self.ctx.draw_state,
|
||||
@ -99,7 +102,7 @@ impl<'a> GfxCtx<'a> {
|
||||
head_size: f64,
|
||||
line: &geom::Line,
|
||||
) {
|
||||
graphics::Line::new_round(color, thickness).draw_arrow(
|
||||
graphics::Line::new_round(color.0, thickness).draw_arrow(
|
||||
line_to_array(line),
|
||||
head_size,
|
||||
&self.ctx.draw_state,
|
||||
@ -110,7 +113,7 @@ impl<'a> GfxCtx<'a> {
|
||||
|
||||
pub fn draw_polygon(&mut self, color: Color, poly: &geom::Polygon) {
|
||||
for tri in &poly.triangles {
|
||||
graphics::Polygon::new(color).draw(
|
||||
graphics::Polygon::new(color.0).draw(
|
||||
&vec![
|
||||
[tri.pt1.x(), tri.pt1.y()],
|
||||
[tri.pt2.x(), tri.pt2.y()],
|
||||
@ -124,7 +127,7 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn draw_circle(&mut self, color: Color, circle: &geom::Circle) {
|
||||
graphics::Ellipse::new(color).draw(
|
||||
graphics::Ellipse::new(color.0).draw(
|
||||
[
|
||||
circle.center.x() - circle.radius,
|
||||
circle.center.y() - circle.radius,
|
||||
@ -139,7 +142,7 @@ impl<'a> GfxCtx<'a> {
|
||||
|
||||
// TODO probably better to have a Polygon::make_rectangle helper or something
|
||||
pub fn draw_rectangle(&mut self, color: Color, rect: [f64; 4]) {
|
||||
graphics::Rectangle::new(color).draw(
|
||||
graphics::Rectangle::new(color.0).draw(
|
||||
rect,
|
||||
&self.ctx.draw_state,
|
||||
self.ctx.transform,
|
||||
@ -206,26 +209,6 @@ impl ToggleableLayer {
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministically shift a color's brightness based on an ID.
|
||||
pub fn shift_color(c: Color, id: usize) -> Color {
|
||||
use palette::Shade;
|
||||
|
||||
// TODO this needs tuning. too easy to get too light/dark, but also too easy to have too few
|
||||
// variants. should maybe just manually come up with a list of 100 colors, hardcode in, modulo.
|
||||
let variants = 10;
|
||||
let half_variants = variants / 2;
|
||||
let modulo = id % variants;
|
||||
let scale = 1.0 / (variants as f32);
|
||||
|
||||
let color = palette::Srgb::new(c[0], c[1], c[2]).into_linear();
|
||||
let new_color = if modulo < half_variants {
|
||||
color.lighten(scale * (modulo as f32))
|
||||
} else {
|
||||
color.darken(scale * ((modulo - half_variants) as f32))
|
||||
};
|
||||
[new_color.red, new_color.green, new_color.blue, 1.0]
|
||||
}
|
||||
|
||||
fn line_to_array(l: &geom::Line) -> [f64; 4] {
|
||||
[l.pt1().x(), l.pt1().y(), l.pt2().x(), l.pt2().y()]
|
||||
}
|
||||
@ -236,4 +219,75 @@ pub enum InputResult<T: Clone> {
|
||||
Done(String, T),
|
||||
}
|
||||
|
||||
pub type Color = [f32; 4];
|
||||
// Copy could be reconsidered, but eh
|
||||
// TODO only pub so we can construct constants elsewhere. need const fn.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Color(pub [f32; 4]);
|
||||
|
||||
impl fmt::Display for Color {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Color(r={}, g={}, b={}, a={})",
|
||||
self.0[0], self.0[1], self.0[2], self.0[3]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Color = Color([0.0, 0.0, 0.0, 1.0]);
|
||||
pub const WHITE: Color = Color([1.0, 1.0, 1.0, 1.0]);
|
||||
pub const RED: Color = Color([1.0, 0.0, 0.0, 1.0]);
|
||||
pub const GREEN: Color = Color([0.0, 1.0, 0.0, 1.0]);
|
||||
pub const BLUE: Color = Color([0.0, 0.0, 1.0, 1.0]);
|
||||
pub const CYAN: Color = Color([0.0, 1.0, 1.0, 1.0]);
|
||||
pub const YELLOW: Color = Color([1.0, 1.0, 0.0, 1.0]);
|
||||
pub const PURPLE: Color = Color([0.5, 0.0, 0.5, 1.0]);
|
||||
|
||||
// TODO should assert stuff about the inputs
|
||||
|
||||
pub fn rgb(r: usize, g: usize, b: usize) -> Color {
|
||||
Color::rgba(r, g, b, 1.0)
|
||||
}
|
||||
|
||||
pub fn rgb_f(r: f32, g: f32, b: f32) -> Color {
|
||||
Color([r, g, b, 1.0])
|
||||
}
|
||||
|
||||
pub fn rgba(r: usize, g: usize, b: usize, a: f32) -> Color {
|
||||
Color([
|
||||
(r as f32) / 255.0,
|
||||
(g as f32) / 255.0,
|
||||
(b as f32) / 255.0,
|
||||
a,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn rgba_f(r: f32, g: f32, b: f32, a: f32) -> Color {
|
||||
Color([r, g, b, a])
|
||||
}
|
||||
|
||||
pub fn grey(f: f32) -> Color {
|
||||
Color([f, f, f, 1.0])
|
||||
}
|
||||
|
||||
// Deterministically shift a color's brightness based on an ID.
|
||||
pub fn shift(&self, id: usize) -> Color {
|
||||
use palette::Shade;
|
||||
|
||||
// TODO this needs tuning. too easy to get too light/dark, but also too easy to have too few
|
||||
// variants. should maybe just manually come up with a list of 100 colors, hardcode in, modulo.
|
||||
let variants = 10;
|
||||
let half_variants = variants / 2;
|
||||
let modulo = id % variants;
|
||||
let scale = 1.0 / (variants as f32);
|
||||
|
||||
let color = palette::Srgb::new(self.0[0], self.0[1], self.0[2]).into_linear();
|
||||
let new_color = if modulo < half_variants {
|
||||
color.lighten(scale * (modulo as f32))
|
||||
} else {
|
||||
color.darken(scale * ((modulo - half_variants) as f32))
|
||||
};
|
||||
Color([new_color.red, new_color.green, new_color.blue, 1.0])
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
use graphics::{Image, Rectangle, Transformed};
|
||||
use {Color, GfxCtx};
|
||||
|
||||
pub const TEXT_FG_COLOR: Color = [0.0, 0.0, 0.0, 1.0];
|
||||
pub const TEXT_QUERY_COLOR: Color = [0.0, 0.0, 1.0, 0.5];
|
||||
pub const TEXT_FOCUS_COLOR: Color = [1.0, 0.0, 0.0, 0.5];
|
||||
const TEXT_BG_COLOR: Color = [0.0, 1.0, 0.0, 0.5];
|
||||
pub const TEXT_FG_COLOR: Color = Color([0.0, 0.0, 0.0, 1.0]);
|
||||
pub const TEXT_QUERY_COLOR: Color = Color([0.0, 0.0, 1.0, 0.5]);
|
||||
pub const TEXT_FOCUS_COLOR: Color = Color([1.0, 0.0, 0.0, 0.5]);
|
||||
const TEXT_BG_COLOR: Color = Color([0.0, 1.0, 0.0, 0.5]);
|
||||
|
||||
const FONT_SIZE: u32 = 24;
|
||||
// TODO this is a hack, need a glyphs.height() method as well!
|
||||
@ -99,7 +99,7 @@ impl Text {
|
||||
|
||||
pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
|
||||
let (total_width, total_height) = txt.dims(g);
|
||||
Rectangle::new(txt.bg_color).draw(
|
||||
Rectangle::new(txt.bg_color.0).draw(
|
||||
[x1, y1, total_width, total_height],
|
||||
&g.orig_ctx.draw_state,
|
||||
g.orig_ctx.transform,
|
||||
@ -114,7 +114,7 @@ pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
|
||||
if let Some(color) = span.highlight_color {
|
||||
// TODO do we ever want to use total_width?
|
||||
let width = g.glyphs.width(FONT_SIZE, &span.text).unwrap();
|
||||
Rectangle::new(color).draw(
|
||||
Rectangle::new(color.0).draw(
|
||||
[x, y - LINE_HEIGHT, width, LINE_HEIGHT],
|
||||
&g.orig_ctx.draw_state,
|
||||
g.orig_ctx.transform,
|
||||
@ -122,7 +122,7 @@ pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
|
||||
);
|
||||
}
|
||||
|
||||
let fg_text = Image::new_color(span.fg_color);
|
||||
let fg_text = Image::new_color(span.fg_color.0);
|
||||
|
||||
for ch in span.text.chars() {
|
||||
if let Ok(draw_ch) = g.glyphs.character(FONT_SIZE, ch) {
|
||||
|
Loading…
Reference in New Issue
Block a user