diff --git a/include/color.h b/include/color.h index fdcbb32..285fef5 100644 --- a/include/color.h +++ b/include/color.h @@ -17,6 +17,14 @@ struct LAB { }; using LAB = struct LAB; +/** A color in LCH colorspace */ +struct LCH { + double l; + double c; + double h; +}; +using LCH = struct LCH; + /** A color in CIEXYZ colorspace */ struct XYZ { double x; @@ -33,6 +41,7 @@ struct RGB { }; using RGB = struct RGB; +/** A color in CMY colorspace */ struct CMY { double c; double m; @@ -59,17 +68,31 @@ using CMY = struct CMY; */ double CIEDE2000(const LAB &lab1, const LAB &lab2); -constexpr double deg2Rad(const double deg); -constexpr double rad2Deg(const double rad); +constexpr double deg2Rad(const double deg) { return (deg * (M_PI / 180.0)); } +constexpr double rad2Deg(const double rad) { return ((180.0 / M_PI) * rad); } RGB XYZtoRGB(const XYZ &xyz); XYZ LABtoXYZ(const LAB &lab); -RGB LABtoRGB(const LAB &lab); CMY RGBtoCMY(const RGB &rgb); +inline RGB LABtoRGB(const LAB &lab) { return XYZtoRGB(LABtoXYZ(lab)); } + +inline LCH LABtoLCH(const LAB &lab) { + double theta = atan2(lab.b, lab.a); + if (theta < 0) + theta += 2 * M_PI; + return LCH{lab.l, sqrt(lab.a * lab.a + lab.b * lab.b), color::rad2Deg(theta)}; +} + +inline LAB LCHtoLAB(const LCH &lch) { + const double theta = color::deg2Rad(lch.h); + return LAB{lch.l, lch.c * cos(theta), lch.c * sin(theta)}; +} + } // namespace color std::ostream &operator<<(std::ostream &s, const color::LAB &lab); +std::ostream &operator<<(std::ostream &s, const color::LCH &lch); std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz); std::ostream &operator<<(std::ostream &s, const color::RGB &rgb); std::ostream &operator<<(std::ostream &s, const color::CMY &rgb); diff --git a/include/constant.h b/include/constant.h index 454d9ed..1dd111d 100644 --- a/include/constant.h +++ b/include/constant.h @@ -17,12 +17,12 @@ const double A31 = -0.03269638; const double A32 = -0.18259425; const double A33 = 1.4716528; -static const double r2 = 0.2; // cyan's absorbance of green -static const double r3 = 0.1; // cyan's absorbance of blue -static const double g1 = 0.0; // magenta's absorbance of red -static const double g3 = 0.1; // magenta's absorbance of blue -static const double b1 = 0; // yellow's absorbance of red -static const double b2 = 0.1; // yellow's absorbance of green +static const double r2 = 0.2; // cyan's absorbance of green +static const double r3 = 0.1; // cyan's absorbance of blue +static const double g1 = 0.0; // magenta's absorbance of red +static const double g3 = 0.1; // magenta's absorbance of blue +static const double b1 = 0; // yellow's absorbance of red +static const double b2 = 0.1; // yellow's absorbance of green /* (1 - RGB) -> CMY */ const double B11 = 1 - b2 * g3; diff --git a/include/fitness.h b/include/fitness.h index c4f6ca4..bbcec21 100644 --- a/include/fitness.h +++ b/include/fitness.h @@ -8,8 +8,10 @@ inline double offRange(double x, double a, double b); double offRGB(const color::LAB &lab); +double offRGB(const color::LCH &lch); double offChroma(const color::LAB &lab, double C); +double offChroma(const color::LCH &lch, double C); /** * @param lab a vector of color::LAB, inter-distances of lab[freeM:] are ignored @@ -20,8 +22,7 @@ double offChroma(const color::LAB &lab, double C); LexiProduct fitnessFunc(const std::vector &lab, size_t M = 0, double maxC = -1.); -class PerceptionResult { -public: +struct PerceptionResult { unsigned long flags; double L; double maxC; @@ -29,6 +30,7 @@ class PerceptionResult { std::vector rgb; LexiProduct fitness; }; +using PerceptionResult = struct PerceptionResult; std::ostream &operator<<(std::ostream &os, const PerceptionResult &res); @@ -43,3 +45,5 @@ std::ostream &operator<<(std::ostream &os, const PerceptionResult &res); PerceptionResult perception(size_t M, double L = -1, double maxC = -1, std::vector const *fixed = nullptr, bool quiet = false); + +color::LCH maxChroma(const color::LCH &lch, double maxC = -1); diff --git a/python/__init__.py b/python/__init__.py index 98304b2..edb9b67 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,7 +1,7 @@ from argparse import ArgumentParser import getpass import os -from .native import perception, LABtoRGB +from .native import LABtoRGB, LABtoLCH, LCHtoLAB, maxChroma, perception from .version import __version__ from .pystache_tree import recursive_render @@ -14,13 +14,12 @@ def rgb_hex(rgb): return ''.join([hex(x) for x in rgb]) -def nearest(rgbs, pivot): - new = sorted( - rgbs, - key= - lambda rgb: (rgb[0] - pivot[0])**2 + (rgb[1] - pivot[1])**2 + (rgb[2] - pivot[2])**2 - ) - return new[0] +def interpolate2D(x1, y1, z1, x2, y2, z2, x3, y3, z3): + d = x2 * y1 - x3 * y1 - x1 * y2 + x3 * y2 + x1 * y3 - x2 * y3 + c = x3 * y2 * z1 - x2 * y3 * z1 - x3 * y1 * z2 + x1 * y3 * z2 + x2 * y1 * z3 - x1 * y2 * z3 + b = x3 * z1 + x1 * z2 - x3 * z2 - x1 * z3 + x2 * z3 - x2 * z1 + a = y2 * z1 - y3 * z1 - y1 * z2 + y3 * z2 + y1 * z3 - y2 * z3 + return lambda x, y: (c - y * b - x * a) / d def argparser(): @@ -53,17 +52,15 @@ def argparser(): '--maxC', type=float, default=-1, - help='maximal chroma for main-palette, negative value for unconstrainted') + help= + 'maximal chroma for main-palette, negative value for unconstrainted') ps.add_argument( '--sub-maxC', type=float, default=20, - help='maximal chroma for sub-palette, negative value for unconstrainted') - ps.add_argument( - '--mu', - type=float, - default=1.5, - help='mu') + help='maximal chroma for sub-palette, negative value for unconstrainted' + ) + ps.add_argument('--mu', type=float, default=1.5, help='mu') ps.add_argument('-D', '--debug', action='store_true', default=False) return ps @@ -72,45 +69,30 @@ def argparser(): def main(): args = argparser().parse_args() if args.background < 0: - args.background = 10 - if args.foreground < 0: + if args.foreground < 0: + args.foreground = 93 + args.background = 100 - args.foreground + elif args.foreground < 0: args.foreground = 100 - args.background - # if args.background < 50: - # args.foreground = min(100, args.background + 90) - # else: - # args.foreground = max(0, args.background - 90) - sgn = 1 if args.foreground > args.background else -1 - if args.L < 0: - if args.foreground > 50: - args.L = (args.foreground * 2 + 50) / (2 + 1) - else: - args.L = (args.foreground * 0.1 + 50) / (0.1 + 1) - if args.maxC < 0: - pass - # x = args.L - args.background - # x1 = -50 - # y1 = -70 - # x2 = 80 - # y2 = 50 - # args.maxC = (x * (y1 - y2) - x2 * y1 + x1 * y2) / (x1 - x2) if (args.debug): from pprint import pprint pprint(args) - L_main = args.L - L_sub = args.background + sgn * 5 - + # L3: text color, very close to foreground + L3 = interpolate2D(0, 100, 45, 100, 0, 80, 60, 60, 60)( + args.foreground, args.background) if args.L < 0 else args.L + # L2: UI line/background, large inter-distance, close to foreground + L2 = interpolate2D(0, 100, 50, 100, 0, 70, 60, 60, 60)(args.foreground, + args.background) + # L1: indicator color, large inter-distance, close to background + L1 = interpolate2D(0, 100, 70, 100, 0, 55, 60, 60, 60)(args.foreground, + args.background) + # L0: background color, very close to background + L0 = interpolate2D(0, 100, 90, 100, 0, 15, 60, 60, 60)(args.foreground, + args.background) + + semantic_hue = {'red': 30, 'yellow': 80, 'green': 120, 'blue': 260} palette_main = perception( - 7, - L=L_main, - maxC=args.maxC, - fixed=[(L_main, 0, 0)], - quiet=not args.debug) - palette_sub = perception( - 7, - L=L_sub, - maxC=args.sub_maxC, - fixed=[(L_sub, 0, 0)], - quiet=not args.debug) + 7, L=L3, maxC=args.maxC, fixed=[(L3, 0, 0)], quiet=not args.debug) context = { 'name': args.name, 'version': __version__, @@ -118,39 +100,29 @@ def main(): 'ui-theme': 'vs-dark' if args.background < 50 else 'vs' } - # legacy - for i, rgb in enumerate(palette_main['rgb']): - context[f'base{i + 8:02X}-hex'] = rgb_hex(rgb) - for i in range(7): - context[f'base{i:02X}-hex'] = rgb_hex( - LABtoRGB((args.background + i * sgn * 5, 0, 0))) - context['base07-hex'] = rgb_hex(LABtoRGB((args.foreground, 0, 0))) - + sgn = 1 if args.foreground > args.background else -1 context['fg-hex'] = rgb_hex(LABtoRGB((args.foreground, 0, 0))) - # context['bg-hex'] = rgb_hex(LABtoRGB((args.background, 0, 0))) - for i, delta in enumerate([0, 5, 10]): + for i, delta in enumerate([0, 5, 12]): context[f'bg-{i}-hex'] = rgb_hex( LABtoRGB((args.background + sgn * delta, 0, 0))) - for i, delta in enumerate([15, 25, 50]): + for i, delta in enumerate([15, 25, 60]): context[f'line-{i}-hex'] = rgb_hex( LABtoRGB((args.background + sgn * delta, 0, 0))) for i, rgb in enumerate(palette_main['rgb']): context[f'main-{i}-hex'] = rgb_hex(rgb) - for i, rgb in enumerate(palette_sub['rgb']): - context[f'sub-{i}-hex'] = rgb_hex(rgb) - for name, rgb in [('red-hex', (1, 0, 0)), ('green-hex', (0, 0.5, 0)), - ('blue-hex', (0, 0, 1)), ('yellow-hex', (0.5, 0.5, 0))]: - context['main-' + name] = rgb_hex(nearest(palette_main['rgb'], rgb)) - context['sub-' + name] = rgb_hex(nearest(palette_sub['rgb'], rgb)) + for name, hue in semantic_hue.items(): + for level, L in [(0, L0), (1, L1), (2, L2)]: + context[f'{name}-{level}-hex'] = rgb_hex( + LABtoRGB(LCHtoLAB(maxChroma([L, 0, hue], maxC=args.maxC)))) if (args.debug): from pprint import pprint - palette_main["fitness"] = palette_main["fitness"][0]; - palette_sub["fitness"] = palette_sub["fitness"][0]; + palette_main["fitness"] = palette_main["fitness"][0] + # palette_sub["fitness"] = palette_sub["fitness"][0]; pprint(palette_main) - pprint(palette_sub) + # pprint(palette_sub) pprint(context) recursive_render( os.path.join(os.path.dirname(__file__), 'data/vscode'), - '/Users/kinker/.vscode/extensions', context) + os.path.expanduser('~/.vscode/extensions'), context) diff --git a/python/data/vscode/perception-theme-{{name}}/themes/perception-theme.json.mustache b/python/data/vscode/perception-theme-{{name}}/themes/perception-theme.json.mustache index 1902a56..e2cf059 100644 --- a/python/data/vscode/perception-theme-{{name}}/themes/perception-theme.json.mustache +++ b/python/data/vscode/perception-theme-{{name}}/themes/perception-theme.json.mustache @@ -1,253 +1,281 @@ { "name": "Perception Colors Theme", - "type": "dark", "colors": { - // Contrast colors - // "contrastActiveBorder": "#f00", //7 - // "contrastBorder": "#f00", //6 + // Contrast colors (This is not a high contrast theme) + // "contrastActiveBorder": "#f00", + // "contrastBorder": "#f00", // Base colors - // "focusBorder": "#f00", //7 - "foreground": "#{{fg-hex}}", //5 - "widget.shadow": "#00000033", //0 - "selection.background": "#{{bg-1-hex}}", //2 - "errorForeground": "#{{main-0-hex}}", //8 + // "focusBorder": "#f00", + "foreground": "#{{fg-hex}}", + "widget.shadow": "#00000033", + "selection.background": "#{{yellow-0-hex}}", + "errorForeground": "#{{red-2-hex}}", // Button control - "button.background": "#{{main-1-hex}}", //1 - "button.foreground": "#{{main-5-hex}}", //5 - "button.hoverBackground": "#{{main-3-hex}}", //3 + "button.background": "#{{blue-2-hex}}", + "button.foreground": "#{{bg-0-hex}}", + "button.hoverBackground": "#{{blue-2-hex}}cc", // Dropdown control - "dropdown.background": "#{{bg-0-hex}}", //0 - // "dropdown.border": "#f00", //2 - "dropdown.foreground": "#{{main-5-hex}}", //5 + "dropdown.background": "#{{bg-1-hex}}", + "dropdown.border": "#{{blue-0-hex}}", + "dropdown.foreground": "#{{fg-hex}}", // Input control - "input.background": "#{{bg-0-hex}}", //0 - // "input.border": "#f00", //0 - "input.foreground": "#{{fg-hex}}", //5 - "input.placeholderForeground": "#{{line-2-hex}}", //3 - "inputOption.activeBorder": "#{{base09-hex}}", //9 - "inputValidation.errorBackground": "#{{main-0-hex}}", //8 - "inputValidation.errorBorder": "#{{main-0-hex}}", //8 - "inputValidation.infoBackground": "#{{main-5-hex}}", //D - "inputValidation.infoBorder": "#{{main-5-hex}}", //D - "inputValidation.warningBackground": "#{{main-2-hex}}", //A - "inputValidation.warningBorder": "#{{main-2-hex}}", //A + "input.background": "#{{bg-0-hex}}", + "input.border": "#{{blue-0-hex}}", + "input.foreground": "#{{fg-hex}}", + "input.placeholderForeground": "#{{line-1-hex}}", + "inputOption.activeBorder": "#{{blue-2-hex}}", + "inputValidation.errorBackground": "#{{red-0-hex}}", + "inputValidation.errorBorder": "#{{red-2-hex}}", + "inputValidation.infoBackground": "#{{blue-0-hex}}", + "inputValidation.infoBorder": "#{{blue-2-hex}}", + "inputValidation.warningBackground": "#{{yellow-0-hex}}", + "inputValidation.warningBorder": "#{{yellow-2-hex}}", // Scroll bar control - "scrollbar.shadow": "#00000033", //1 - "scrollbarSlider.activeBackground": "#{{main-4-hex}}44", //4 - "scrollbarSlider.background": "#{{fg-hex}}11", //2 - "scrollbarSlider.hoverBackground": "#{{main-4-hex}}22", //3 + "scrollbar.shadow": "#00000033", + "scrollbarSlider.activeBackground": "#{{blue-2-hex}}44", + "scrollbarSlider.background": "#{{fg-hex}}11", + "scrollbarSlider.hoverBackground": "#{{blue-2-hex}}22", // Badge - "badge.background": "#{{main-2-hex}}", //0 - "badge.foreground": "#{{main-5-hex}}", //5 + "badge.background": "#{{blue-2-hex}}", + "badge.foreground": "#{{bg-0-hex}}", // Progress bar - "progressBar.background": "#{{main-3-hex}}", //3 + "progressBar.background": "#{{green-1-hex}}", // Lists and trees - "list.activeSelectionBackground": "#{{bg-2-hex}}", //2 - "list.activeSelectionForeground": "#{{main-5-hex}}", //5 - "list.dropBackground": "#{{sub-6-hex}}", //7 - "list.focusBackground": "#{{main-1-hex}}", //2 - "list.focusForeground": "#{{main-5-hex}}", //5 - "list.highlightForeground": "#{{main-3-hex}}", //7 - "list.hoverBackground": "#{{bg-2-hex}}", //3 - "list.hoverForeground": "#{{main-5-hex}}", //5 - "list.inactiveSelectionBackground": "#{{bg-2-hex}}", //2 - "list.inactiveSelectionForeground": "#{{main-5-hex}}", //5 + "list.activeSelectionBackground": "#{{bg-2-hex}}", + "list.activeSelectionForeground": "#{{fg-hex}}", + "list.dropBackground": "#{{blue-0-hex}}", + "list.focusBackground": "#{{blue-0-hex}}", + "list.focusForeground": "#{{fg-hex}}", + "list.highlightForeground": "#{{yellow-2-hex}}", + "list.hoverBackground": "#{{bg-2-hex}}", + "list.hoverForeground": "#{{fg-hex}}", + "list.inactiveSelectionBackground": "#{{bg-2-hex}}", + "list.inactiveSelectionForeground": "#{{fg-hex}}", // Activity bar - "activityBar.background": "#{{bg-0-hex}}", //0 - "activityBar.border": "#{{line-0-hex}}", //0 - "activityBar.dropBackground": "#{{sub-6-hex}}", //7 - "activityBar.foreground": "#{{fg-hex}}", //5 - "activityBarBadge.background": "#{{main-5-hex}}", //D - "activityBarBadge.foreground": "#{{bg-0-hex}}", //7 + "activityBar.background": "#{{bg-0-hex}}", + "activityBar.border": "#{{line-0-hex}}", + "activityBar.dropBackground": "#{{blue-0-hex}}", + "activityBar.foreground": "#{{fg-hex}}", + "activityBarBadge.background": "#{{blue-2-hex}}", + "activityBarBadge.foreground": "#{{bg-0-hex}}", // Side bar - "sideBar.background": "#{{bg-1-hex}}", //1 - "sideBar.border": "#{{line-0-hex}}", //1 - "sideBar.foreground": "#{{line-2-hex}}", //5 - "sideBarSectionHeader.background": "#{{bg-0-hex}}", //3 - "sideBarSectionHeader.foreground": "#{{line-2-hex}}", //5 - "sideBarTitle.foreground": "#{{line-2-hex}}", //5 + "sideBar.background": "#{{bg-1-hex}}", + "sideBar.border": "#{{line-0-hex}}", + "sideBar.foreground": "#{{line-2-hex}}", + "sideBarSectionHeader.background": "#{{bg-0-hex}}", + "sideBarSectionHeader.foreground": "#{{line-2-hex}}", + "sideBarTitle.foreground": "#{{line-2-hex}}", // Editor groups and tabs - "editorGroup.background": "#{{main-4-hex}}", //0 - // "editorGroup.border": "#f00", - "editorGroup.dropBackground": "#{{bg-1-hex}}", //2 - "editorGroupHeader.noTabsBackground": "#{{bg-1-hex}}", //1 - "editorGroupHeader.tabsBackground": "#{{bg-1-hex}}", //1 - // "editorGroupHeader.tabsBorder": "#f00", - "tab.activeBackground": "#{{bg-0-hex}}", //0 - "tab.activeForeground": "#{{fg-hex}}", //5 + "editorGroup.background": "#{{bg-2-hex}}", + "editorGroup.border": "#{{line-0-hex}}", + "editorGroup.dropBackground": "#{{bg-1-hex}}", + "editorGroupHeader.noTabsBackground": "#{{bg-1-hex}}", + "editorGroupHeader.tabsBackground": "#{{bg-1-hex}}", + "editorGroupHeader.tabsBorder": "#{{line-0-hex}}", + "tab.activeBackground": "#{{bg-0-hex}}", + "tab.activeForeground": "#{{blue-2-hex}}", "tab.border": "#{{line-0-hex}}", - "tab.inactiveBackground": "#{{bg-1-hex}}", //1 + "tab.inactiveBackground": "#{{bg-1-hex}}", "tab.inactiveForeground": "#{{line-2-hex}}", - "tab.unfocusedActiveForeground": "#{{main-4-hex}}", //4 - "tab.unfocusedInactiveForeground": "#{{main-4-hex}}", //4 + "tab.unfocusedActiveForeground": "#{{line-2-hex}}", + "tab.unfocusedInactiveForeground": "#{{line-2-hex}}", // Editor colors - "editor.background": "#{{bg-0-hex}}", //0 - "editor.foreground": "#{{fg-hex}}", //5 - "editorLineNumber.foreground": "#{{line-1-hex}}", //3 - "editorCursor.foreground": "#{{main-5-hex}}", //5 - "editor.selectionBackground": "#{{sub-6-hex}}", //2 - "editor.selectionHighlightBackground": "#{{main-2-hex}}", //1 - "editor.inactiveSelectionBackground": "#{{bg-1-hex}}", //2 - "editor.wordHighlightBackground": "#{{main-2-hex}}", //2 - "editor.wordHighlightStrongBackground": "#{{main-3-hex}}", //3 - "editor.findMatchBackground": "#{{main-2-hex}}6f", //A - "editor.findMatchHighlightBackground": "#{{sub-2-hex}}", //9 - "editor.findRangeHighlightBackground": "#f0f", - "editor.hoverHighlightBackground": "#{{main-2-hex}}", //2 - "editor.lineHighlightBackground": "#{{bg-1-hex}}", //1 + + "editor.background": "#{{bg-0-hex}}", + "editor.foreground": "#{{fg-hex}}", + "editorLineNumber.foreground": "#{{line-0-hex}}", + "editorCursor.foreground": "#{{red-2-hex}}", + "editor.selectionBackground": "#{{yellow-0-hex}}", + "editor.selectionHighlightBackground": "#{{yellow-0-hex}}", + "editor.inactiveSelectionBackground": "#{{bg-1-hex}}", + "editor.wordHighlightBackground": "#{{blue-0-hex}}", + "editor.wordHighlightStrongBackground": "#{{green-0-hex}}", + "editor.findMatchBackground": "#{{yellow-0-hex}}", + "editor.findMatchHighlightBackground": "#{{blue-0-hex}}", + "editor.findRangeHighlightBackground": "#{{yellow-0-hex}}33", + "editor.hoverHighlightBackground": "#{{bg-2-hex}}", + "editor.lineHighlightBackground": "#{{bg-1-hex}}", // "editor.lineHighlightBorder": "#f00", - "editorLink.activeForeground": "#{{main-5-hex}}", //D - "editor.rangeHighlightBackground": "#{{sub-2-hex}}", //1 - "editorWhitespace.foreground": "#{{main-3-hex}}", //3 - "editorIndentGuide.background": "#{{main-3-hex}}", //3 - "editorRuler.foreground": "#{{main-3-hex}}", //3 - "editorCodeLens.foreground": "#{{main-2-hex}}", //2 - "editorBracketMatch.background": "#{{sub-4-hex}}", //2 - // "editorBracketMatch.border": "#f00", - // "editorOverviewRuler.border": "#f00", + "editorLink.activeForeground": "#{{main-5-hex}}", + "editor.rangeHighlightBackground": "#{{yellow-0-hex}}33", + "editorWhitespace.foreground": "#{{line-0-hex}}", + "editorIndentGuide.background": "#{{line-0-hex}}", + "editorRuler.foreground": "#{{line-1-hex}}", + "editorCodeLens.foreground": "#{{line-1-hex}}", + "editorBracketMatch.background": "#{{green-0-hex}}", + "editorBracketMatch.border": "#{{green-2-hex}}", + "editorOverviewRuler.border": "#{{line-0-hex}}", + "editorOverviewRuler.findMatchForeground": "#{{yellow-1-hex}}", + "editorOverviewRuler.rangeHighlightForeground": "#{{yellow-1-hex}}", + "editorOverviewRuler.selectionHighlightForeground": "#{{blue-1-hex}}", + "editorOverviewRuler.wordHighlightForeground": "#{{blue-1-hex}}", + "editorOverviewRuler.wordHighlightStrongForeground": "#{{green-1-hex}}", + "editorOverviewRuler.modifiedForeground": "#{{yellow-1-hex}}", + "editorOverviewRuler.addedForeground": "#{{green-1-hex}}", + "editorOverviewRuler.deletedForeground": "#{{red-1-hex}}", + "editorOverviewRuler.errorForeground": "#{{red-1-hex}}", + "editorOverviewRuler.warningForeground": "#{{red-1-hex}}", + "editorOverviewRuler.infoForeground": "#{{blue-1-hex}}", // "editorError.border": "#f0f", - "editorError.foreground": "#{{main-0-hex}}", //8 - "editorWarning.foreground": "#{{main-0-hex}}", //8 + "editorError.foreground": "#{{red-2-hex}}", + "editorWarning.foreground": "#{{yellow-2-hex}}", // "editorWarning.border": "#f00", - "editorGutter.addedBackground": "#{{sub-green-hex}}", //B - "editorGutter.background": "#{{bg-0-hex}}", //0 - "editorGutter.deletedBackground": "#{{sub-red-hex}}", //8 - "editorGutter.modifiedBackground": "#{{sub-yellow-hex}}", //E + "editorGutter.addedBackground": "#{{green-1-hex}}", + "editorGutter.background": "#{{bg-0-hex}}", + "editorGutter.deletedBackground": "#{{red-1-hex}}", + "editorGutter.modifiedBackground": "#{{yellow-1-hex}}", // Diff editor colors - "diffEditor.insertedTextBackground": "#{{base0B-hex}}20", - // "diffEditor.insertedTextBorder": "#f00", - "diffEditor.removedTextBackground": "#{{main-0-hex}}20", - // "diffEditor.removedTextBorder": "#f00", + "diffEditor.insertedTextBackground": "#{{green-1-hex}}20", + // "diffEditor.insertedTextBorder": "#{{green-2-hex}}", + "diffEditor.removedTextBackground": "#{{red-1-hex}}20", + // "diffEditor.removedTextBorder": "#{{green-2-hex}}", // Editor widget colors - "editorWidget.background": "#{{main-3-hex}}", //0 - // "editorWidget.border": "#f00", - "editorSuggestWidget.background": "#{{main-4-hex}}", //1 - // "editorSuggestWidget.border": "#f00", - "editorSuggestWidget.foreground": "#{{main-5-hex}}", //5 - "editorSuggestWidget.highlightForeground": "#f0f", - "editorSuggestWidget.selectedBackground": "#{{main-2-hex}}", //2 - "editorHoverWidget.background": "#{{main-0-hex}}", //1 - // "editorHoverWidget.border": "#f00", - "debugExceptionWidget.background": "#{{main-5-hex}}", //1 - // "debugExceptionWidget.border": "#f00", - "editorMarkerNavigation.background": "#{{main-6-hex}}", //1 - "editorMarkerNavigationError.background": "#{{main-0-hex}}", //8 - "editorMarkerNavigationWarning.background": "#{{main-2-hex}}", //A + "editorWidget.background": "#{{bg-1-hex}}", + "editorWidget.border": "#{{line-0-hex}}", + "editorSuggestWidget.background": "#{{bg-1-hex}}", + "editorSuggestWidget.border": "#{{fg-hex}}", + "editorSuggestWidget.foreground": "#{{fg-hex}}", + "editorSuggestWidget.highlightForeground": "#{{fg-hex}}", + "editorSuggestWidget.selectedBackground": "#{{green-1-hex}}", + "editorHoverWidget.background": "#{{bg-0-hex}}", + "editorHoverWidget.border": "#{{fg-hex}}", + "debugExceptionWidget.background": "#{{red-0-hex}}", + "debugExceptionWidget.border": "#f{{red-2-hex}}", + "editorMarkerNavigation.background": "#{{bg-1-hex}}", + "editorMarkerNavigationError.background": "#{{red-0-hex}}", + "editorMarkerNavigationWarning.background": "#{{yellow-0-hex}}", + "editorMarkerNavigationInfo.background": "#{{blue-0-hex}}", // Peek view colors - // "peekView.border": "#f00", - "peekViewEditor.background": "#{{main-3-hex}}", //1 - "peekViewEditor.matchHighlightBackground": "#{{base09-hex}}6f", //9 - "peekViewEditorGutter.background": "#{{main-0-hex}}", //1 - "peekViewResult.background": "#{{main-1-hex}}", //0 - "peekViewResult.fileForeground": "#{{main-5-hex}}", //5 - "peekViewResult.lineForeground": "#{{main-3-hex}}", //3 - "peekViewResult.matchHighlightBackground": "#{{base09-hex}}6f", //9 - "peekViewResult.selectionBackground": "#{{main-2-hex}}", //2 - "peekViewResult.selectionForeground": "#{{main-5-hex}}", //5 - "peekViewTitle.background": "#{{main-2-hex}}", //2 - "peekViewTitleDescription.foreground": "#{{main-3-hex}}", //3 - "peekViewTitleLabel.foreground": "#{{main-5-hex}}", //5 + "peekView.border": "#{{green-2-hex}}", + "peekViewEditor.background": "#{{bg-0-hex}}", + "peekViewEditor.matchHighlightBackground": "#{{yellow-0-hex}}", + "peekViewEditorGutter.background": "#{{bg-1-hex}}", + "peekViewResult.background": "#{{bg-0-hex}}", + "peekViewResult.fileForeground": "#{{fg-hex}}", + "peekViewResult.lineForeground": "#{{line-2-hex}}", + "peekViewResult.matchHighlightBackground": "#{{green-0-hex}}", + "peekViewResult.selectionBackground": "#{{green-1-hex}}", + "peekViewResult.selectionForeground": "#{{fg-hex}}", + "peekViewTitle.background": "#{{green-0-hex}}", + "peekViewTitleDescription.foreground": "#{{line-2-hex}}", + "peekViewTitleLabel.foreground": "#{{fg-hex}}", // Merge conflicts // "merge.border": "#f00", - "merge.currentContentBackground": "#{{main-5-hex}}40", //D - "merge.currentHeaderBackground": "#{{main-5-hex}}40", //D - "merge.incomingContentBackground": "#{{base0B-hex}}60", //B - "merge.incomingHeaderBackground": "#{{base0B-hex}}60", //B - "editorOverviewRuler.currentContentForeground": "#{{main-5-hex}}", //D - "editorOverviewRuler.incomingContentForeground": "#{{base0B-hex}}", //B + "merge.currentContentBackground": "#{{blue-0-hex}}", + "merge.currentHeaderBackground": "#{{blue-1-hex}}40", + "merge.incomingContentBackground": "#{{yellow-0-hex}}60", + "merge.incomingHeaderBackground": "#{{yellow-1-hex}}60", + "editorOverviewRuler.currentContentForeground": "#{{blue-2-hex}}", + "editorOverviewRuler.incomingContentForeground": "#{{yellow-2-hex}}", // Panel colors - "panel.background": "#{{bg-0-hex}}", //0 + "panel.background": "#{{bg-0-hex}}", "panel.border": "#{{line-0-hex}}", - "panelTitle.activeBorder": "#{{main-5-hex}}", - "panelTitle.activeForeground": "#{{main-5-hex}}", //5 - "panelTitle.inactiveForeground": "#{{line-2-hex}}", //3 + "panelTitle.activeBorder": "#{{blue-2-hex}}", + "panelTitle.activeForeground": "#{{blue-2-hex}}", + "panelTitle.inactiveForeground": "#{{line-2-hex}}", // Status bar colors - "statusBar.background": "#{{bg-2-hex}}", //D + "statusBar.background": "#{{bg-2-hex}}", // "statusBar.border": "#{{line-0-hex}}", - "statusBar.debuggingBackground": "#{{main-2-hex}}", //9 - "statusBar.debuggingForeground": "#{{bg-0-hex}}", //7 - "statusBar.foreground": "#{{fg-hex}}", //7 - "statusBar.noFolderBackground": "#{{main-6-hex}}", //E - "statusBar.noFolderForeground": "#{{main-3-hex}}", //7 - "statusBarItem.activeBackground": "#{{main-3-hex}}", //3 - "statusBarItem.hoverBackground": "#{{line-0-hex}}", //2 - "statusBarItem.prominentBackground": "#f0f", - "statusBarItem.prominentHoverBackground": "#f00", + "statusBar.debuggingBackground": "#{{yellow-2-hex}}", + "statusBar.debuggingForeground": "#{{bg-0-hex}}", + "statusBar.foreground": "#{{fg-hex}}", + "statusBar.noFolderBackground": "#{{bg-2-hex}}", + "statusBar.noFolderForeground": "#{{line-2-hex}}", + "statusBarItem.activeBackground": "#{{bg-1-hex}}", + "statusBarItem.hoverBackground": "#{{bg-1-hex}}", + "statusBarItem.prominentBackground": "#{{green-1-hex}}", + "statusBarItem.prominentHoverBackground": "#{{green-1-hex}}cc", // Title bar colors (macOS) - "titleBar.activeBackground": "#{{bg-2-hex}}", //0 - "titleBar.activeForelround": "#{{line-2-hex}}", //5 - "titleBar.inactiveBackground": "#{{bg-2-hex}}", //1 - "titleBar.inactiveForeground": "#{{line-0-hex}}", //3 + "titleBar.activeBackground": "#{{bg-2-hex}}", + "titleBar.activeForelround": "#{{line-2-hex}}", + "titleBar.inactiveBackground": "#{{bg-2-hex}}", + "titleBar.inactiveForeground": "#{{line-0-hex}}", // Notification dialog colors - "notification.background": "#{{main-2-hex}}", //2 - "notification.foreground": "#{{main-5-hex}}", //5 + "notification.background": "#{{bg-1-hex}}", + "notification.foreground": "#{{line-2-hex}}", + "notification.buttonBackground": "#{{blue-1-hex}}", + "notification.buttonHoverBackground": "#{{blue-1-hex}}cc", + "notification.buttonForeground": "#{{fg-hex}}", + "notification.infoBackground": "#{{blue-2-hex}}", + "notification.infoForeground": "#{{blue-0-hex}}", + "notification.warningBackground": "#{{yellow-2-hex}}", + "notification.warningForeground": "#{{yellow-0-hex}}", + "notification.errorBackground": "#{{red-2-hex}}", + "notification.errorForeground": "#{{red-0-hex}}", // Extensions - "extensionButton.prominentBackground": "#{{base0B-hex}}", //B - "extensionButton.prominentForeground": "#{{main-3-hex}}", //7 - "extensionButton.prominentHoverBackground": "#{{main-2-hex}}", //2 + "extensionButton.prominentBackground": "#{{green-2-hex}}", + "extensionButton.prominentForeground": "#{{bg-0-hex}}", + "extensionButton.prominentHoverBackground": "#{{green-2-hex}}cc", // Quick picker // "pickerGroup.border": "#f00", - "pickerGroup.foreground": "#{{main-3-hex}}", //3 + "pickerGroup.foreground": "#{{green-2-hex}}", // Integrated terminal colors - "terminal.background": "#{{main-4-hex}}", //0 - "terminal.foreground": "#{{main-5-hex}}", //5 - "terminal.ansiBlack": "#{{main-2-hex}}", //2 - "terminal.ansiBrightBlack": "#{{main-3-hex}}", //3 - "terminal.ansiRed": "#{{main-0-hex}}", //8 - "terminal.ansiBrightRed": "#{{main-0-hex}}", //8 - "terminal.ansiYellow": "#{{base09-hex}}", //9 - "terminal.ansiBrightYellow": "#{{main-2-hex}}", //A - "terminal.ansiGreen": "#{{base0B-hex}}", //B - "terminal.ansiBrightGreen": "#{{base0B-hex}}", //B - "terminal.ansiCyan": "#{{base0C-hex}}", //C - "terminal.ansiBrightCyan": "#{{base0C-hex}}", //C - "terminal.ansiBlue": "#{{main-5-hex}}", //D - "terminal.ansiBrightBlue": "#{{main-5-hex}}", //D - "terminal.ansiMagenta": "#{{main-6-hex}}", //E - "terminal.ansiBrightMagenta": "#{{main-6-hex}}", //E - "terminal.ansiWhite": "#{{main-6-hex}}", //6 - "terminal.ansiBrightWhite": "#{{main-3-hex}}", //7 + "terminal.background": "#{{bg-0-hex}}", + "terminal.foreground": "#{{line-2-hex}}", + "terminal.ansiBlack": "#{{line-2-hex}}", + "terminal.ansiBrightBlack": "#{{fg-hex}}", + "terminal.ansiRed": "#{{main-0-hex}}", + "terminal.ansiBrightRed": "#{{main-0-hex}}", + "terminal.ansiYellow": "#{{main-1-hex}}", + "terminal.ansiBrightYellow": "#{{main-1-hex}}", + "terminal.ansiGreen": "#{{main-2-hex}}", + "terminal.ansiBrightGreen": "#{{main-2-hex}}", + "terminal.ansiCyan": "#{{main-4-hex}}", + "terminal.ansiBrightCyan": "#{{main-4-hex}}", + "terminal.ansiBlue": "#{{main-5-hex}}", + "terminal.ansiBrightBlue": "#{{main-5-hex}}", + "terminal.ansiMagenta": "#{{main-6-hex}}", + "terminal.ansiBrightMagenta": "#{{main-6-hex}}", + "terminal.ansiWhite": "#{{line-0-hex}}", + "terminal.ansiBrightWhite": "#{{line-0-hex}}", // Debug - "debugToolBar.background": "#{{main-1-hex}}", //1 + "debugToolBar.background": "#{{yellow-1-hex}}", // Welcome page - "welcomePage.buttonBackground": "#{{main-1-hex}}", //1 - "welcomePage.buttonHoverBackground": "#{{main-2-hex}}", //2 - "walkThrough.embeddedEditorBackground": "#{{main-5-hex}}", //0 + "welcomePage.buttonBackground": "#{{blue-2-hex}}", + "welcomePage.buttonHoverBackground": "#{{blue-2-hex}}cc", + "walkThrough.embeddedEditorBackground": "#{{bg-0-hex}}", + + // Git Colors + "gitDecoration.modifiedResourceForeground": "#{{yellow-2-hex}}", + "gitDecoration.deletedResourceForeground": "#{{line-1-hex}}", + "gitDecoration.untrackedResourceForeground": "#{{green-2-hex}}", + "gitDecoration.ignoredResourceForeground": "#{{line-1-hex}}", + "gitDecoration.conflictingResourceForeground": "#{{red-2-hex}}", // Unknown - "descriptionForeground": "#{{main-3-hex}}", //3 - "textBlockQuote.background": "#{{main-1-hex}}", //1 - "textBlockQuote.border": "#{{main-5-hex}}", //D - "textCodeBlock.background": "#{{main-6-hex}}", //0 - "textLink.activeForeground": "#{{base0C-hex}}", //C - "textLink.foreground": "#{{main-5-hex}}", //D - "textPreformat.foreground": "#{{main-5-hex}}", //D - "textSeparator.foreground": "#f0f" + "descriptionForeground": "#{{line-2-hex}}" + // "textBlockQuote.background": "#{{fg-hex}}", + // "textBlockQuote.border": "#{{fg-hex}}", + // "textCodeBlock.background": "#{{fg-hex}}", + // "textLink.activeForeground": "#{{fg-hex}}", + // "textLink.foreground": "#{{fg-hex}}", + // "textPreformat.foreground": "#{{fg-hex}}", + // "textSeparator.foreground": "#f0f" }, "tokenColors": [ { @@ -257,7 +285,7 @@ "punctuation.separator" ], "settings": { - "foreground": "#{{main-4-hex}}" //3 + "foreground": "#{{main-4-hex}}" } }, { @@ -266,7 +294,7 @@ "keyword" ], "settings": { - "foreground": "#{{main-5-hex}}" //3 + "foreground": "#{{main-5-hex}}" } }, { @@ -276,7 +304,7 @@ "support.constant" ], "settings": { - "foreground": "#{{main-2-hex}}" //3 + "foreground": "#{{main-2-hex}}" } }, { @@ -286,7 +314,7 @@ "punctuation.definition.string" ], "settings": { - "foreground": "#{{main-3-hex}}" //3 + "foreground": "#{{main-3-hex}}" } }, { @@ -299,7 +327,7 @@ "meta.definition" ], "settings": { - "foreground": "#{{main-0-hex}}" //3 + "foreground": "#{{main-0-hex}}" } }, { @@ -309,7 +337,7 @@ "support.variable" ], "settings": { - "foreground": "#{{main-1-hex}}" //3 + "foreground": "#{{main-1-hex}}" } }, { @@ -322,7 +350,7 @@ "meta.type" ], "settings": { - "foreground": "#{{main-6-hex}}" //3 + "foreground": "#{{main-6-hex}}" } }, { @@ -333,7 +361,7 @@ ], "settings": { "fontStyle": "italic", - "foreground": "#{{line-1-hex}}" //3 + "foreground": "#{{line-1-hex}}" } }, { @@ -342,7 +370,7 @@ "invalid" ], "settings": { - "foreground": "#{{main-0-hex}}" //3 + "foreground": "#{{main-0-hex}}" } } ] diff --git a/python/pystache_tree.py b/python/pystache_tree.py index 9c9fca3..91dbb6e 100644 --- a/python/pystache_tree.py +++ b/python/pystache_tree.py @@ -7,14 +7,19 @@ TEMPLATE_SUFFIX_LEN = len(TEMPLATE_SUFFIX) -def recursive_render(src_path, dest_path, context, renderer=Renderer(missing_tags='strict')): +def recursive_render(src_path, + dest_path, + context, + renderer=Renderer(missing_tags='strict')): if (os.path.isdir(src_path)): os.makedirs(dest_path, exist_ok=True) print(f'Enter directory {dest_path}') for name in os.listdir(src_path): child_src_path = os.path.join(src_path, name) - child_dest_path = os.path.join(dest_path, renderer.render(name, context)) - recursive_render(child_src_path, child_dest_path, context, renderer) + child_dest_path = os.path.join(dest_path, + renderer.render(name, context)) + recursive_render(child_src_path, child_dest_path, context, + renderer) elif dest_path[-TEMPLATE_SUFFIX_LEN:] == TEMPLATE_SUFFIX: dest_path = dest_path[:-TEMPLATE_SUFFIX_LEN] with open(src_path, 'r') as src, open(dest_path, 'w') as dest: diff --git a/src/CIEDE2000.cpp b/src/CIEDE2000.cpp index ccd7674..8cb5e49 100644 --- a/src/CIEDE2000.cpp +++ b/src/CIEDE2000.cpp @@ -11,14 +11,6 @@ * Conversions. ******************************************************************************/ -constexpr double color::deg2Rad(const double deg) { - return (deg * (M_PI / 180.0)); -} - -constexpr double color::rad2Deg(const double rad) { - return ((180.0 / M_PI) * rad); -} - double color::CIEDE2000(const LAB &lab1, const LAB &lab2) { /* * "For these and all other numerical/graphical ō¸°€delta E00 values diff --git a/src/colorspace.cpp b/src/colorspace.cpp index 3d16240..f80bd31 100644 --- a/src/colorspace.cpp +++ b/src/colorspace.cpp @@ -40,8 +40,6 @@ XYZ LABtoXYZ(const LAB &lab) { return XYZ{x, y, z}; } -RGB LABtoRGB(const LAB &lab) { return XYZtoRGB(LABtoXYZ(lab)); } - CMY RGBtoCMY(const RGB &rgb) { const double cr = 1 - rgb.r; const double cg = 1 - rgb.g; @@ -59,6 +57,10 @@ std::ostream &operator<<(std::ostream &s, const color::LAB &lab) { return (s << "LAB(" << lab.l << "," << lab.a << "," << lab.b << ")"); } +std::ostream &operator<<(std::ostream &s, const color::LCH &lch) { + return (s << "LCH(" << lch.l << "," << lch.c << "," << lch.h << ")"); +} + std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz) { return (s << "XYZ(" << xyz.x << "," << xyz.y << "," << xyz.z << ")"); } diff --git a/src/fitness.cpp b/src/fitness.cpp index 9bf4a14..39ba001 100644 --- a/src/fitness.cpp +++ b/src/fitness.cpp @@ -28,17 +28,25 @@ inline double offRGB(const color::LAB &lab) { return std::sqrt(dr * dr + dg * dg + db * db + dc * dc + dm * dm + dy * dy); } +inline double offRGB(const color::LCH &lch) { return offRGB(LCHtoLAB(lch)); } + inline double offChroma(const color::LAB &lab, double C) { if (C < 0) return 0; return offRange(std::sqrt(lab.a * lab.a + lab.b * lab.b), 0, C); } -class Combination { -public: +inline double offChroma(const color::LCH &lch, double C) { + if (C < 0) + return 0; + return offRange(lch.c, 0, C); +} + +struct Combination { size_t i; size_t j; }; +using Combination = struct Combination; LexiProduct fitnessFunc(const std::vector &lab, size_t M, double maxC) { @@ -191,4 +199,15 @@ PerceptionResult perception(size_t M, double L, double maxC, return PerceptionResult{flags, freeL ? xfinal[2 * M] : L, finalMaxC, std::move(lab), std::move(rgb), std::move(fitness)}; -} \ No newline at end of file +} + +color::LCH maxChroma(const color::LCH &lch, double maxC) { + color::LCH inner{lch.l, 0, lch.h}, outer = lch; + while (offRGB(outer) == 0 && offChroma(outer, maxC) == 0) + outer.c = 2 * outer.c + 1; + while (abs(outer.c - inner.c) > 1e-13) { + color::LCH m{lch.l, (inner.c + outer.c) / 2., lch.h}; + (offRGB(m) == 0 && offChroma(m, maxC) == 0 ? inner : outer) = m; + } + return inner; +} diff --git a/src/python.cpp b/src/python.cpp index 04cbc69..1f8e176 100644 --- a/src/python.cpp +++ b/src/python.cpp @@ -35,6 +35,26 @@ static PyObject *LABtoRGB(PyObject *self, PyObject *args) { return Py_BuildValue("(ddd)", rgb.r, rgb.g, rgb.b); } +static PyObject *LABtoLCH(PyObject *self, PyObject *args) { + color::LAB lab; + + if (!PyArg_ParseTuple(args, "(ddd)", &lab.l, &lab.a, &lab.b)) + return nullptr; + + const auto lch = color::LABtoLCH(lab); + return Py_BuildValue("(ddd)", lch.l, lch.c, lch.h); +} + +static PyObject *LCHtoLAB(PyObject *self, PyObject *args) { + color::LCH lch; + + if (!PyArg_ParseTuple(args, "(ddd)", &lch.l, &lch.c, &lch.h)) + return nullptr; + + const auto lab = color::LCHtoLAB(lch); + return Py_BuildValue("(ddd)", lab.l, lab.a, lab.b); +} + static PyObject *pyperception(PyObject *self, PyObject *args, PyObject *kw) { // color::LAB fg, bg; unsigned long M; @@ -101,6 +121,21 @@ static PyObject *pyperception(PyObject *self, PyObject *args, PyObject *kw) { "fitness", fitness.ptr); } +static PyObject *pymaxChroma(PyObject *self, PyObject *args, PyObject *kw) { + color::LCH lch; + double maxC = -1; + + static char kw1[] = ""; + static char kw2[] = "maxC"; + static char *kwlist[] = {kw1, kw2, nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "(ddd)|d", kwlist, &lch.l, &lch.c, + &lch.h, &maxC)) + return nullptr; + + const auto lch2 = maxChroma(lch, maxC); + return Py_BuildValue("(ddd)", lch2.l, lch2.c, lch2.h); +} + static PyMethodDef methods[] = { {"perception", (PyCFunction)pyperception, METH_VARARGS | METH_KEYWORDS, "perception colors anchored by N colors\n" @@ -108,14 +143,35 @@ static PyMethodDef methods[] = { "\n" "@param M number of colors\n" "@param L=-1 luminocity, < 0 for no constraint\n" - "@param maxC=-1 maximal distance, < 0 for no constraint\n" + "@param maxC=-1 maximal chroma, < 0 for no constraint\n" "@param fixed=None iterable of (l, a, b)\n" "@param quiet=False print messages to stdout"}, + + {"maxChroma", (PyCFunction)pymaxChroma, METH_VARARGS | METH_KEYWORDS, + "find the color with maximal chroma\n" + "----------------------------------\n" + "\n" + "@param (l, c, h) color in LCH space\n" + "@param maxC=-1 maximal chroma, < 0 for no constraint"}, + {"LABtoRGB", LABtoRGB, METH_VARARGS, "convert LAB to RGB\n" "------------------\n" "\n" "@param lab (l,a,b)\n"}, + + {"LABtoLCH", LABtoLCH, METH_VARARGS, + "convert LAB to LCH\n" + "------------------\n" + "\n" + "@param lab (l,a,b)\n"}, + + {"LCHtoLAB", LCHtoLAB, METH_VARARGS, + "convert LCH to LAB\n" + "------------------\n" + "\n" + "@param lch (l,c,h)\n"}, + {nullptr, nullptr, 0, nullptr}}; static struct PyModuleDef module = { diff --git a/test/testcolorspace.cpp b/test/testcolorspace.cpp index 3ca4b00..8f14842 100644 --- a/test/testcolorspace.cpp +++ b/test/testcolorspace.cpp @@ -20,6 +20,11 @@ void check(bool res, std::string msg) { ++line; } +void check(double a, double b, const std::string &msg, double delta = 1e-3) { + // std::cout << std::setprecision(10) << a << " " << b << std::endl; + check(std::abs(a - b) < delta, msg); +} + /** * @brief * Run the test dataset run from the color paper. @@ -61,6 +66,18 @@ int testcolor() { check(rgb5.g < 0, "RGB limit"); check(rgb5.b > 1, "RGB limit"); + check(color::LABtoLCH(color::LAB{50, 20, 0}).h, 0, "LCH hue"); + check(color::LABtoLCH(color::LAB{50, 0, 20}).h, 90, "LCH hue"); + check(color::LABtoLCH(color::LAB{50, -20, 0}).h, 180, "LCH hue"); + check(color::LABtoLCH(color::LAB{50, 0, -20}).h, 270, "LCH hue"); + + check(color::LCHtoLAB(color::LCH{50, 20, 45}).a, 20. / sqrt(2), "LCH -> LAB"); + check(color::LCHtoLAB(color::LCH{50, 20, 45}).b, 20. / sqrt(2), "LCH -> LAB"); + check(color::LCHtoLAB(color::LCH{50, 20, 225}).a, -20. / sqrt(2), + "LCH -> LAB"); + check(color::LCHtoLAB(color::LCH{50, 20, 225}).b, -20. / sqrt(2), + "LCH -> LAB"); + std::cout << std::endl; int ret = EXIT_SUCCESS; for (unsigned int i = 0; i < passFail.size(); i++) { diff --git a/test/testfitness.cpp b/test/testfitness.cpp index 5b0f79c..c14acc6 100644 --- a/test/testfitness.cpp +++ b/test/testfitness.cpp @@ -36,7 +36,7 @@ int testcolor() { unsigned int line = 1; std::vector passFail; - check(offRGB(color::LAB{200, 0, 0}), 2.147648383, "offRGB"); + check(offRGB(color::LAB{200, 0, 0}) > 0, "offRGB"); check(fitnessFunc(std::vector{{50, 0, 0}, {50, 10, 10}, @@ -45,9 +45,11 @@ int testcolor() { {50, 10, -10}})[0], -12.8001, "fitnessFunc"); - check( - fitnessFunc(std::vector{{50, -200, -200}, {50, 200, 200}})[0], - 1626.445599, "fitnessFunc (off boundary)"); + check(fitnessFunc( + std::vector{{50, -200, -200}, {50, 200, 200}})[0] > 0, + "fitnessFunc (off boundary)"); + + check(maxChroma(color::LCH{50, 20, 0}, -1).c, 57.70569, "maxChroma: red"); std::cout << std::endl; int ret = EXIT_SUCCESS; diff --git a/test/testmaxchroma.py b/test/testmaxchroma.py new file mode 100644 index 0000000..0b52a78 --- /dev/null +++ b/test/testmaxchroma.py @@ -0,0 +1,5 @@ +#! /usr/bin/env python3 + +from perception import maxChroma + +print(maxChroma((50, 0, 0)))