You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
4.5 KiB
Python
122 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Generates a set of highway colors to be stored in road-colors-generated.mss.
|
|
|
|
from colormath.color_conversions import convert_color
|
|
from colormath.color_objects import LabColor, LCHabColor, sRGBColor
|
|
from colormath.color_diff import delta_e_cie2000
|
|
import argparse
|
|
import sys
|
|
import yaml
|
|
|
|
from collections import OrderedDict, namedtuple
|
|
|
|
class Color:
|
|
"""A color in the CIE lch color space."""
|
|
|
|
def __init__(self, lch_tuple):
|
|
self.m_lch = LCHabColor(*lch_tuple)
|
|
|
|
def lch(self):
|
|
return "Lch({:.0f},{:.0f},{:.0f})".format(*(self.m_lch.get_value_tuple()))
|
|
|
|
def rgb(self):
|
|
rgb = convert_color(self.m_lch, sRGBColor)
|
|
if (rgb.rgb_r != rgb.clamped_rgb_r or rgb.rgb_g != rgb.clamped_rgb_g or rgb.rgb_b != rgb.clamped_rgb_b):
|
|
raise Exception("Colour {} is outside sRGB".format(self.lch()))
|
|
return rgb.get_rgb_hex()
|
|
|
|
def rgb_error(self):
|
|
return delta_e_cie2000(convert_color(self.m_lch, LabColor),
|
|
convert_color(sRGBColor.new_from_rgb_hex(self.rgb()), LabColor))
|
|
|
|
def load_settings():
|
|
"""Read the settings from YAML."""
|
|
return yaml.safe_load(open('road-colors.yaml', 'r'))
|
|
|
|
def generate_colours(settings, section):
|
|
"""Generate colour ranges.
|
|
|
|
Arguments:
|
|
settings -- The settings loaded by load_settings.
|
|
section -- Which section of the settings under 'classes' to use. Typically
|
|
'mss' or 'shields'.
|
|
"""
|
|
road_classes = settings['roads']
|
|
colour_divisions = len(road_classes) - 1
|
|
hues = OrderedDict()
|
|
|
|
min_h = settings['hue'][0]
|
|
max_h = settings['hue'][1]
|
|
|
|
delta_h = (max_h - min_h) / colour_divisions
|
|
|
|
h = min_h
|
|
for name in road_classes:
|
|
hues[name] = h
|
|
h = (h + delta_h) % 360
|
|
|
|
# A class to hold information for each line
|
|
ColourInfo = namedtuple("ColourInfo", ["start_l", "end_l", "start_c", "end_c"])
|
|
|
|
line_colour_infos = OrderedDict()
|
|
|
|
# The lightness (l) and chroma (c; also known as saturation) for each type of colour.
|
|
# Lightness ranges from 0 to 100; dark to bright.
|
|
# Chroma ranges from 0 to 100 too; unsaturated to fully saturated.
|
|
|
|
# The higher the road classification, the higher its saturation. Conversely,
|
|
# the roads get brighter towards the lower end of the classification.
|
|
|
|
classes = settings['classes'][section]
|
|
for cls, params in sorted(classes.items()):
|
|
l = params['lightness']
|
|
c = params['chroma']
|
|
line_colour_infos[cls] = ColourInfo(start_l = l[0], end_l = l[1], start_c = c[0], end_c = c[1])
|
|
|
|
# Colours for the MSS
|
|
colours = OrderedDict()
|
|
|
|
for line_name, line_colour_info in line_colour_infos.items():
|
|
c = line_colour_info.start_c
|
|
delta_c = (line_colour_info.end_c - line_colour_info.start_c) / colour_divisions
|
|
l = line_colour_info.start_l
|
|
delta_l = (line_colour_info.end_l - line_colour_info.start_l) / colour_divisions
|
|
|
|
colours[line_name] = OrderedDict()
|
|
for name in road_classes:
|
|
colours[line_name][name] = Color((l, c, hues[name]))
|
|
c += delta_c
|
|
l += delta_l
|
|
|
|
return colours
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Generates road colours')
|
|
parser.add_argument('-v', '--verbose', dest='verbose', help='Generates information about colour differences', action='store_true', default=False)
|
|
args = parser.parse_args()
|
|
|
|
settings = load_settings()
|
|
road_classes = settings['roads']
|
|
colour_divisions = len(road_classes) - 1
|
|
colours = generate_colours(settings, 'mss')
|
|
|
|
# Print a warning about the nature of these definitions.
|
|
print("/* This is generated code, do not change this file manually. */")
|
|
print("/* */")
|
|
print("/* To change these definitions, alter road-colors.yaml and run: */")
|
|
print("/* */")
|
|
print("/* scripts/generate_road_colours.py > style/road-colors-generated.mss */")
|
|
print("/* */")
|
|
|
|
for line_name, line_colours in colours.items():
|
|
for name, colour in line_colours.items():
|
|
if args.verbose:
|
|
line = "@{name}-{line_name}: {rgb}; // {lch}, error {delta:.1f}"
|
|
else:
|
|
line = "@{name}-{line_name}: {rgb};"
|
|
print((line.format(name = name, line_name=line_name, rgb = colour.rgb(), lch = colour.lch(), delta = colour.rgb_error())))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|