McRogueFace/generate_color_table.py

76 lines
2.7 KiB
Python
Raw Normal View History

2024-03-24 03:06:36 +00:00
# data sources: CSS docs, jennyscrayoncollection 2017 article on Crayola colors, XKCD color survey
# target: Single C++ header file to provide a struct of color RGB codes and names.
# This file pre-computes the nearest neighbor of every color.
# if an RGB code being searched for is closer than the nearest neighbor, it's the closest color name.
def hex_to_rgb(txt):
if '#' in txt: txt = txt.replace('#', '')
r = txt[0:2]
g = txt[2:4]
b = txt[4:6]
return tuple([int(s, 16) for s in (r,g,b)])
class palette:
def __init__(self, name, filename, priority):
self.name = name
self.priority = priority
with open(filename, "r") as f:
print(f"scanning {filename}")
self.colors = {}
for line in f.read().split('\n'):
if len(line.split('\t')) < 2: continue
name, code = line.split('\t')
#print(name, code)
self.colors[name] = hex_to_rgb(code)
def __repr__(self):
return f"<Palette '{self.name}' - {len(self.colors)} colors, priority = {self.priority}>"
palettes = [
#palette("jenny", "jenny_colors.txt", 3), # I should probably use wikipedia as a source for copyright reasons
palette("crayon", "wikicrayons_colors.txt", 2),
palette("xkcd", "xkcd_colors.txt", 1),
palette("css", "css_colors.txt", 0),
#palette("matplotlib", "matplotlib_colors.txt", 2) # there's like 10 colors total, I think we'll survive without them
]
all_colors = []
from math import sqrt
def rgbdist(c1, c2):
return sqrt((c1.r - c2.r)**2 + (c1.g - c2.g)**2 + (c1.b - c2.b)**2)
class Color:
def __init__(self, r, g, b, name, prefix, priority):
self.r = r
self.g = g
self.b = b
self.name = name
self.prefix = prefix
self.priority = priority
self.nearest_neighbor = None
def __repr__(self):
return f"<Color ({self.r}, {self.g}, {self.b}) - '{self.prefix}:{self.name}', priority = {self.priority}, nearest_neighbor={self.nearest_neighbor.name if self.nearest_neighbor is not None else None}>"
def nn(self, colors):
nearest = None
nearest_dist = 999999
for c in colors:
dist = rgbdist(self, c)
if dist == 0: continue
if dist < nearest_dist:
nearest = c
nearest_dist = dist
self.nearest_neighbor = nearest
for p in palettes:
prefix = p.name
priority = p.priority
for name, rgb in p.colors.items():
all_colors.append(Color(*rgb, name, prefix, priority))
print(f"{prefix}->{len(all_colors)}")
for c in all_colors:
c.nn(all_colors)