File indexing completed on 2025-01-05 04:00:26
0001 #!/usr/bin/env python3 0002 0003 import sys 0004 import os 0005 from functools import reduce 0006 import argparse 0007 sys.path.insert(0, os.path.join( 0008 os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 0009 "lib" 0010 )) 0011 from lottie.parsers.svg.importer import parse_color 0012 from lottie.parsers.svg.svgdata import color_table 0013 from lottie import NVector 0014 from lottie.utils.color import ColorMode, Color 0015 0016 0017 class ColorCompare: 0018 def __init__(self, name): 0019 self.name = name 0020 rgb = Color(*parse_color(name)[:3]) 0021 xyz = rgb.converted(ColorMode.XYZ) 0022 self.representations = { 0023 "RGB": rgb, 0024 "HSV": rgb.converted(ColorMode.HSV), 0025 "XYZ": xyz, 0026 "LAB": xyz.converted(ColorMode.LAB), 0027 "LUV": xyz.converted(ColorMode.LUV), 0028 "LCH_uv": xyz.converted(ColorMode.LCH_uv), 0029 } 0030 0031 def __repr__(self): 0032 return "<ColorCompare %s>" % self.name 0033 0034 def dist(self, oth, space): 0035 return (self.representations[space] - oth.representations[space]).length 0036 0037 def rep(self, space): 0038 return self.representations[space] 0039 0040 def ansi_str(self, length): 0041 return color_str(self.rep("RGB"), length) 0042 0043 0044 class SimilarColor: 0045 def __init__(self, name, rgbvec, space): 0046 self.name = name 0047 self.rgb = Color(*rgbvec[:3]) 0048 self.vec = self.rgb.converted(ColorMode[space]) 0049 self.space = space 0050 0051 def dist(self, colorcompare): 0052 return (self.vec - colorcompare.rep(self.space)).length 0053 0054 def ansi_str(self, length): 0055 return color_str(self.rgb, length) 0056 0057 0058 def color_str(rgbvec, length): 0059 comps = [ 0060 str(int(round(c * 255))) 0061 for c in rgbvec[:3] 0062 ] 0063 return "\x1b[48;2;%sm%s\x1b[m" % (";".join(comps), " " * length) 0064 0065 0066 def table_row(name, items=[""], pad=" "): 0067 print("|".join([name.ljust(titlepad, pad)]+[item.center(itempad, pad) for item in items])+"|") 0068 0069 0070 def table_sep(length): 0071 table_row("", [""]*length, "-") 0072 0073 0074 def vfmt(vec): 0075 return "%6.2f %6.2f %6.2f" % tuple(vec[:3]) 0076 0077 0078 spaces = ["RGB", "HSV", "XYZ", "LAB", "LUV", "LCH_uv"] 0079 0080 parser = argparse.ArgumentParser( 0081 description="Compares colors in different spaces" 0082 ) 0083 parser.add_argument( 0084 "colors", 0085 help="Colors to inspect (in one of the CSS color formats)", 0086 nargs="+", 0087 type=ColorCompare, 0088 ) 0089 parser.add_argument( 0090 "--space", "-s", 0091 choices=spaces+["all"], 0092 default="LCH_uv", 0093 help="Color space for the distance" 0094 ) 0095 parser.add_argument( 0096 "--count", "-c", 0097 type=int, 0098 default=1, 0099 help="Number of similar colors to get" 0100 ) 0101 parser.add_argument( 0102 "--colors", 0103 action="store_true", 0104 default=os.environ.get("COLORTERM", "") in {"truecolor", "24bit"}, 0105 dest="term_colors", 0106 help="Enables color previews", 0107 ) 0108 parser.add_argument( 0109 "--no-colors", 0110 action="store_false", 0111 dest="term_colors", 0112 help="Disables color previews", 0113 ) 0114 0115 0116 def compare(colors, space, similar_count): 0117 print("=" * linelen) 0118 print(("Distances [%s]" % space).center(linelen)) 0119 print("-" * linelen) 0120 table_row("", (c.name for c in colors)) 0121 table_sep(len(colors)) 0122 0123 for color in colors: 0124 table_row(color.name, ("%10.8f" % color.dist(c2, space) if c2 is not color else "-" for c2 in colors)) 0125 0126 if similar_count: 0127 print("=" * linelen) 0128 print(("Nearest CSS Name [%s]" % space).center(linelen)) 0129 print("-" * linelen) 0130 table_row(space, (c.name for c in colors)) 0131 if term_colors: 0132 table_row("", (c.ansi_str(itempad) for c in colors)) 0133 table_sep(len(colors)) 0134 0135 csscolors = [ 0136 SimilarColor(name, vec, space) 0137 for name, vec in color_table.items() 0138 ] 0139 0140 for color in colors: 0141 color.matches = sorted( 0142 ((c, c.dist(color)) for c in csscolors), 0143 key=lambda c: c[1] 0144 ) 0145 0146 for i in range(similar_count): 0147 table_row("", (c.matches[i][0].name for c in colors)) 0148 if term_colors: 0149 table_row("", (c.matches[i][0].ansi_str(itempad) for c in colors)) 0150 table_row("", ("%10.8f" % c.matches[i][1] for c in colors)) 0151 table_sep(len(colors)) 0152 0153 0154 if __name__ == "__main__": 0155 ns = parser.parse_args() 0156 0157 namepad = max(len(c.name) for c in ns.colors) 0158 titlepad = max(namepad, 6) 0159 itempad = max(namepad, 6*3+2) 0160 linelen = titlepad + (itempad + 1) * len(ns.colors) + 1 0161 term_colors = ns.term_colors 0162 0163 print("=" * linelen) 0164 print("Colors".center(linelen)) 0165 print("-" * linelen) 0166 table_row("Color", (c.name for c in ns.colors)) 0167 if term_colors: 0168 table_row("", (c.ansi_str(itempad) for c in ns.colors)) 0169 table_sep(len(ns.colors)) 0170 for cs in ["RGB", "HSV", "XYZ", "LAB", "LUV", "LCH_uv"]: 0171 table_row(cs, (vfmt(c.representations[cs]) for c in ns.colors)) 0172 table_sep(len(ns.colors)) 0173 0174 if ns.space == "all": 0175 for space in spaces: 0176 compare(ns.colors, space, ns.count) 0177 else: 0178 compare(ns.colors, ns.space, ns.count) 0179 0180 print("=" * linelen)