← back to Designer Wallcoverings
onboarding/sangetsu-lilycolor/scripts/lexicon-colorway.py
84 lines
#!/usr/bin/env python3
"""Map a dominant hex -> nearest interior-designer-lexicon colorway NAME.
Local, $0. The lexicon is the working set from ~/.claude/skills/interior-designer/SKILL.md
(colorway vocabulary section). Each named colorway gets an approximate sRGB anchor; we pick
the nearest by weighted-Euclidean distance in RGB. This is a deterministic naming aid that
sits ALONGSIDE the VL's own free-text color name (we keep both)."""
import sys, json
# (name, (r,g,b)) — approximate sRGB anchors for the interior-designer colorway lexicon.
LEXICON = [
# whites / off-whites
("Chalk",(245,244,240)),("Alabaster","#EDEADE"),("Cotton",(252,252,250)),
("Swiss Coffee","#EAE3D6"),("Ivory","#FFFFF0"),("Eggshell","#F0EAD6"),
("Bone","#E3DAC9"),("Cream","#FFFDD0"),("Linen","#FAF0E6"),("Porcelain","#F5F4F0"),
("Antique White","#FAEBD7"),
# neutrals / greige
("Oatmeal","#E0D8C8"),("Flax","#EEDC82"),("Greige","#BEB6A6"),("Mushroom","#BDA697"),
("Putty","#C5B9A3"),("Stone","#B6ADA1"),("Wheat","#F5DEB3"),("Sand","#D9C9A8"),
("Almond","#EFDECD"),("Taupe","#8B8589"),("Pebble","#CFC8BB"),
# tans / camels / browns
("Buff","#DAA06D"),("Honey","#D9A441"),("Tan","#D2B48C"),("Camel","#C19A6B"),
("Khaki","#BDB76B"),("Caramel","#AF6E4D"),("Pecan","#9C6B30"),("Cognac","#9A463D"),
("Chestnut","#954535"),("Walnut","#5C4033"),("Mocha","#3D2B1F"),("Espresso","#3B2F2F"),
# greys
("Dove","#D7D2CB"),("Silver","#C0C0C0"),("Ash","#B2BEB5"),("Fog","#D6D6D1"),
("Smoke","#A6A6A6"),("Pewter","#8C8C8C"),("Slate","#708090"),("Zinc","#71797E"),
("Graphite","#41424C"),("Gunmetal","#2A3439"),("Charcoal","#36454F"),
# blacks
("Ink","#1B1B1E"),("Noir","#0B0B0B"),("Onyx","#0F0F0F"),("Ebony","#1A1110"),
("Jet","#0A0A0A"),("Obsidian","#0D0D0D"),
# greens
("Celadon","#ACE1AF"),("Pistachio","#93C572"),("Sage","#9CAF88"),("Eucalyptus","#A2C3A4"),
("Sea Glass","#C9E4DE"),("Moss","#8A9A5B"),("Fern","#4F7942"),("Olive","#808000"),
("Loden","#4A5D23"),("Juniper","#3A5311"),("Hunter","#355E3B"),("Forest","#228B22"),
("Emerald","#50C878"),("Verdigris","#43B3AE"),
# blues
("Powder","#B6D0E2"),("Sky","#87CEEB"),("Wedgwood","#5C8DB8"),("Cornflower","#6495ED"),
("French Blue","#0072BB"),("Cerulean","#2A52BE"),("Denim","#1560BD"),("Slate Blue","#6A5ACD"),
("Teal","#008080"),("Peacock","#1B6C75"),("Prussian","#003153"),("Indigo","#3F00FF"),
("Navy","#1B2A4A"),("Midnight","#191970"),
# yellows / golds
("Buttercream","#FDF1B8"),("Champagne","#F7E7CE"),("Maize","#FBEC5D"),("Citrine","#E4D00A"),
("Saffron","#F4C430"),("Amber","#FFBF00"),("Ochre","#CC7722"),("Mustard","#FFDB58"),
("Gold","#D4AF37"),("Brass","#B5A642"),
# oranges / earths
("Apricot","#FBCEB1"),("Peach","#FFE5B4"),("Clay","#B66A50"),("Terracotta","#E2725B"),
("Persimmon","#EC5800"),("Pumpkin","#FF7518"),("Copper","#B87333"),("Rust","#B7410E"),
("Sienna","#882D17"),
# reds / pinks
("Blush","#DE9DAC"),("Rose","#FF007F"),("Dusty Rose","#C09098"),("Coral","#FF7F50"),
("Cinnabar","#E34234"),("Brick","#8B3A2F"),("Oxblood","#4A0000"),("Claret","#7F1734"),
("Garnet","#733635"),("Burgundy","#800020"),("Crimson","#DC143C"),
# purples
("Lavender","#B57EDC"),("Lilac","#C8A2C8"),("Wisteria","#C9A0DC"),("Orchid","#DA70D6"),
("Heather","#9E7E9E"),("Mauve","#E0B0FF"),("Amethyst","#9966CC"),("Plum","#8E4585"),
("Aubergine","#3D0734"),
# metallics / leaf
("Platinum","#E5E4E2"),("Gilt","#CBA135"),("Bronze","#CD7F32"),
]
def _rgb(v):
if isinstance(v, str):
v = v.lstrip("#")
return (int(v[0:2],16), int(v[2:4],16), int(v[4:6],16))
return tuple(v)
ANCHORS = [(name, _rgb(rgb)) for name, rgb in LEXICON]
def nearest(hex_str):
try:
r,g,b = _rgb(hex_str)
except Exception:
return None
# weighted euclidean (perceptual-ish: 0.30/0.59/0.11 luminance weights)
best, bd = None, 1e18
for name,(ar,ag,ab) in ANCHORS:
d = 0.30*(r-ar)**2 + 0.59*(g-ag)**2 + 0.11*(b-ab)**2
if d < bd:
bd, best = d, name
return best
if __name__ == "__main__":
print(json.dumps({"colorway": nearest(sys.argv[1])}))