Skip to content

Instantly share code, notes, and snippets.

@exavolt
Last active July 13, 2020 07:16
Show Gist options
  • Save exavolt/bdc830c8118aebbc00f7f03c089d1ed7 to your computer and use it in GitHub Desktop.
Save exavolt/bdc830c8118aebbc00f7f03c089d1ed7 to your computer and use it in GitHub Desktop.
Generate a palette from an input color similar to the official tool found at https://material.io/inline-tools/color/
// source
// https://github.com/filipeglfw/material-palette-generator/blob/master/src/index.js
import 'dart:math' as math;
// Main API.
//
// generateMaterialPalette(RGBColor.fromInt(0xffe91e63)).map<Color>((c) => Color(c.toInt()));
//
List<RGBColor> generateMaterialPalette(RGBColor rgbColor) {
return _generatePalette(
rgbColor,
goldenPalettes: _goldenPalettes,
lightnessTolerance: _defaultLightnessTolerance,
chromaTolerance: _defaultChromaTolerance,
);
}
class RGBColor {
const RGBColor({
this.red,
this.green,
this.blue,
this.alpha = 1.0,
}) : assert(0.0 <= red && red <= 1.0),
assert(0.0 <= green && green <= 1.0),
assert(0.0 <= blue && blue <= 1.0),
assert(0.0 <= alpha && alpha <= 1.0);
final double red;
final double green;
final double blue;
final double alpha;
const RGBColor.fromInt(int intColor)
: alpha = ((intColor & 0xff000000) >> 24) / 0xff,
red = ((intColor & 0x00ff0000) >> 16) / 0xff,
green = ((intColor & 0x0000ff00) >> 8) / 0xff,
blue = ((intColor & 0x000000ff)) / 0xff;
int toInt() =>
((alpha * 0xff).round() << 24) +
((red * 0xff).round() << 16) +
((green * 0xff).round() << 8) +
(blue * 0xff).round();
String toString() => 'RGBColor($red, $green, $blue, $alpha)';
}
class XYZColor {
const XYZColor({
this.x,
this.y,
this.z,
this.alpha = 1.0,
}) : assert(0.0 <= alpha && alpha <= 1.0); // other assert ?
factory XYZColor.fromRGBColor(RGBColor rgbColor) => XYZColor(
x: _channelFromRGBChannel(rgbColor.red),
y: _channelFromRGBChannel(rgbColor.green),
z: _channelFromRGBChannel(rgbColor.blue),
alpha: rgbColor.alpha,
);
final double x;
final double y;
final double z;
final double alpha;
RGBColor toRGBColor() {
double r = (x * 3.2404542) + (y * -1.5371385) + (z * -0.4985314);
double g = (x * -0.969266) + (y * 1.8760108) + (z * 0.041556);
double b = (x * 0.0556434) + (y * -0.2040259) + (z * 1.0572252);
// Assume sRGB
r = r > 0.0031308
? ((1.055 * math.pow(r, (1.0 / 2.4))) - 0.055)
: r * 12.92;
g = g > 0.0031308
? ((1.055 * math.pow(g, (1.0 / 2.4))) - 0.055)
: g * 12.92;
b = b > 0.0031308
? ((1.055 * math.pow(b, (1.0 / 2.4))) - 0.055)
: b * 12.92;
return RGBColor(
red: r.clamp(0.0, 1.0),
green: g.clamp(0.0, 1.0),
blue: b.clamp(0.0, 1.0),
alpha: alpha,
);
}
static double _channelFromRGBChannel(double ch) =>
0.04045 >= ch ? ch / 12.92 : math.pow((ch + 0.055) / 1.055, 2.4);
}
class LABColor {
const LABColor(
this.lightness,
this.a,
this.b, [
this.alpha = 1.0,
]) : assert(0.0 <= lightness && lightness <= double.maxFinite),
assert(0.0 <= alpha && alpha <= 1.0);
const LABColor.named({
this.lightness,
this.a,
this.b,
this.alpha = 1.0,
}) : assert(0.0 <= lightness && lightness <= double.maxFinite),
assert(0.0 <= alpha && alpha <= 1.0);
factory LABColor.fromRGBColor(RGBColor rgbColor) {
final xyzColor = XYZColor.fromRGBColor(rgbColor);
final e =
0.2126729 * xyzColor.x + 0.7151522 * xyzColor.y + 0.072175 * xyzColor.z;
return LABColor.named(
lightness: 116 * _valueFromXYZSpace(e) - 16,
a: 500 *
(_valueFromXYZSpace((0.4124564 * xyzColor.x +
0.3575761 * xyzColor.y +
0.1804375 * xyzColor.z) /
0.95047) -
_valueFromXYZSpace(e)),
b: 200 *
(_valueFromXYZSpace(e) -
_valueFromXYZSpace((0.0193339 * xyzColor.x +
0.119192 * xyzColor.y +
0.9503041 * xyzColor.z) /
1.08883)),
alpha: rgbColor.alpha,
);
}
factory LABColor.fromRGBInt(int rgbInt) =>
LABColor.fromRGBColor(RGBColor.fromInt(rgbInt));
final double lightness;
final double a;
final double b;
final double alpha;
bool equals(LABColor other) =>
lightness == other.lightness &&
a == other.a &&
b == other.b &&
alpha == other.alpha;
XYZColor toXYZColor() {
double y = (lightness + 16) / 116;
double x = a / 500 + y;
double z = y - b / 200;
final y2 = math.pow(y, 3);
final x2 = math.pow(x, 3);
final z2 = math.pow(z, 3);
y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
return XYZColor(
x: x * 0.95047,
y: y * 1.0,
z: z * 1.08883,
alpha: alpha,
);
}
RGBColor toRGBColor() => toXYZColor().toRGBColor();
static double _valueFromXYZSpace(double t) {
const double t0 = 4 / 29;
const double t1 = 6 / 29;
final t2 = 3 * math.pow(t1, 2);
final t3 = math.pow(t1, 3);
return t > t3 ? math.pow(t, 1 / 3) : t / t2 + t0;
}
}
class LCHColor {
const LCHColor({
this.lightness,
this.chroma,
this.hue,
this.alpha = 1,
}) : assert(0.0 <= lightness && lightness <= double.maxFinite),
assert(0.0 <= chroma && chroma <= double.maxFinite),
assert(0.0 <= hue && hue <= 360.0),
assert(0.0 <= alpha && alpha <= 1.0);
LCHColor.fromLABColor(LABColor labColor)
: lightness = labColor.lightness,
chroma = math.sqrt(math.pow(labColor.a, 2) + math.pow(labColor.b, 2)),
hue =
((180 * math.atan2(labColor.b, labColor.a)) / math.pi + 360) % 360,
alpha = labColor.alpha;
final double lightness;
final double chroma;
final double hue;
final double alpha;
LABColor toLABColor() {
final hr = hue / 360 * 2 * math.pi;
return LABColor.named(
lightness: lightness,
a: chroma * math.cos(hr),
b: chroma * math.sin(hr),
alpha: alpha,
);
}
RGBColor toRGBColor() => toLABColor().toRGBColor();
}
class HSVColor {
HSVColor({
this.hue,
this.saturation,
this.value,
this.alpha,
}) : assert(0.0 <= hue && hue <= 360.0),
assert(0.0 <= saturation && saturation <= 1.0),
assert(0.0 <= value && value <= 1.0),
assert(0.0 <= alpha && alpha <= 1.0);
factory HSVColor.fromRGBColor(RGBColor rgbColor) {
double value = math.max(
math.max(rgbColor.red, rgbColor.green),
rgbColor.blue,
);
double c = math.min(
math.min(rgbColor.red, rgbColor.green),
rgbColor.blue,
);
double hue = 0;
double saturation = 0;
if (value - c > _accuracy) {
saturation = (value - c) / value;
if (value == rgbColor.red) {
hue = (60 * (rgbColor.green - rgbColor.blue)) / (value - c);
} else if (value == rgbColor.green) {
hue = (60 * (rgbColor.blue - rgbColor.red)) / (value - c) + 120;
} else if (value == rgbColor.blue) {
hue = (60 * (rgbColor.red - rgbColor.green)) / (value - c) + 240;
}
}
hue = (hue + 360).roundToDouble() % 360;
return HSVColor(
hue: hue,
saturation: saturation,
value: value,
alpha: rgbColor.alpha,
);
}
final double hue;
final double saturation;
final double value;
final double alpha;
RGBColor toRGBColor() {
final chroma = value * saturation;
return _hsxToRGBColor(
hue,
chroma,
math.max(0, value - chroma),
alpha,
);
}
String toString() => 'HSVColor($hue, $saturation, $value, $alpha)';
static final _accuracy = math.pow(2, -16);
}
RGBColor _hsxToRGBColor(
double hue,
double chroma,
double m,
double alpha,
) {
double red = m;
double green = m;
double blue = m;
final h = (hue % 360) / 60;
final x = chroma * (1.0 - ((h % 2) - 1).abs());
switch (h.floor()) {
case 0:
red += chroma;
green += x;
break;
case 1:
red += x;
green += chroma;
break;
case 2:
green += chroma;
blue += x;
break;
case 3:
green += x;
blue += chroma;
break;
case 4:
red += x;
blue += chroma;
break;
case 5:
red += chroma;
blue += x;
break;
}
return RGBColor(
red: red,
green: green,
blue: blue,
alpha: alpha,
);
}
final _goldenPalettes = <List<LABColor>>[
[
LABColor(94.67497003305085, 7.266715066863771, 1.000743882272359),
LABColor(86.7897416761699, 18.370736761658012, 4.23637133971424),
LABColor(72.0939162832561, 31.7948058298117, 13.2972443996896),
LABColor(61.79353370051851, 44.129498163764545, 20.721477326799608),
LABColor(57.194195398949574, 59.6450006197361, 34.999830012940194),
LABColor(55.603951071861374, 66.01287384845483, 47.67169313982772),
LABColor(51.66348502954747, 64.7487785020625, 43.244876694855286),
LABColor(47.09455666350969, 62.29836039074277, 40.67775424698388),
LABColor(43.77122063388739, 60.28633509183384, 40.31444686692952),
LABColor(39.555187078007386, 58.703681355389975, 41.66495027798629),
LABColor.fromRGBInt(0xFFFF8A80),
LABColor.fromRGBInt(0xFFFF5252),
LABColor.fromRGBInt(0xFFFF1744),
LABColor.fromRGBInt(0xFFD50000),
],
[
LABColor(92.68053776327665, 9.515385232804263, -0.8994072969754852),
LABColor(81.86756643628922, 25.05688089723257, -1.9475235115390621),
LABColor(70.90987389545768, 42.21705257720526, -1.095154624057959),
LABColor(61.08140805216186, 58.871233307587204, 2.1008764804626434),
LABColor(54.97970219986448, 68.56530938366889, 7.327430728560569),
LABColor(50.872250340749176, 74.60459195925529, 15.353576256896073),
LABColor(47.27738650144558, 70.77855776427805, 11.70434273264508),
LABColor(42.58424189486517, 65.5411953138309, 7.595596439803797),
LABColor(37.977492407254836, 60.74362621842075, 2.9847124951453474),
LABColor(29.699290034849604, 51.90485023721311, -4.830186634107636),
LABColor.fromRGBInt(0xFFFF80AB),
LABColor.fromRGBInt(0xFFFF4081),
LABColor.fromRGBInt(0xFFF50057),
LABColor.fromRGBInt(0xFFC51162),
],
[
LABColor(92.4362655169016, 7.542927467702299, -6.039842848605881),
LABColor(81.07399776904751, 19.563870217805036, -15.719625491986044),
LABColor(68.71394717711831, 33.79992812490556, -26.49539972339321),
LABColor(56.596161226236305, 47.5856631835152, -36.480816605410915),
LABColor(48.002791217624434, 57.30866443934879, -43.2561127152548),
LABColor(40.66211534692161, 64.01910773818436, -48.05930162591041),
LABColor(37.690702208992185, 61.13762767732481, -49.384803274243026),
LABColor(33.56291870731981, 57.637381239254104, -51.39557249855828),
LABColor(29.865391314234515, 54.29737439901333, -52.6601973712463),
LABColor(23.16724235420436, 48.51764437280498, -55.16267949015293),
LABColor.fromRGBInt(0xFFEA80FC),
LABColor.fromRGBInt(0xFFE040FB),
LABColor.fromRGBInt(0xFFD500F9),
LABColor.fromRGBInt(0xFFAA00FF),
],
[
LABColor(92.49103426017201, 4.712320025752947, -6.532868071709763),
LABColor(81.24668319505597, 11.50642734909485, -16.666600637245367),
LABColor(68.61488216554629, 20.395329051982824, -28.522018851715416),
LABColor(55.60369793053023, 30.933537768905005, -41.16439122358484),
LABColor(45.834566190969426, 39.28806272235674, -50.523322052772635),
LABColor(36.608620229358664, 47.29686002828143, -59.111766586186846),
LABColor(34.189791237562616, 46.60426065139123, -59.53961627676729),
LABColor(30.52713367338361, 46.01498224754519, -60.19975052509064),
LABColor(27.44585524877222, 44.96180431854785, -60.46395810756433),
LABColor(21.98627670328218, 44.29296076245473, -60.93653655172098),
LABColor.fromRGBInt(0xFFB388FF),
LABColor.fromRGBInt(0xFF7C4DFF),
LABColor.fromRGBInt(0xFF651FFF),
LABColor.fromRGBInt(0xFF6200EA),
],
[
LABColor(92.86314411983918, 1.5318147061061937, -6.025243528950552),
LABColor(81.8348073705298, 4.460934955458907, -15.873561009736136),
LABColor(69.7796913795672, 7.9043652558912765, -26.3170846346932),
LABColor(57.48786519938736, 12.681019504822533, -37.23202012914528),
LABColor(47.74592578811101, 18.520799302452374, -46.47540679000397),
LABColor(38.334403614455404, 25.57700668170812, -55.28224153299287),
LABColor(35.15116453901552, 26.231812080381168, -54.53700978785404),
LABColor(31.080429988007957, 27.07394930110124, -53.97505274579958),
LABColor(27.026672080454922, 28.165266427558983, -53.28987325482218),
LABColor(19.751201587921678, 30.60784576895101, -52.13866519297474),
LABColor.fromRGBInt(0xFF8C9EFF),
LABColor.fromRGBInt(0xFF536DFE),
LABColor.fromRGBInt(0xFF3D5AFE),
LABColor.fromRGBInt(0xFF304FFE),
],
[
LABColor(94.70682457348717, -2.835484735987326, -6.978044694792707),
LABColor(86.8839842970016, -5.16908728759552, -17.88561192754956),
LABColor(79.0451532401558, -6.817753527015746, -28.968537490432176),
LABColor(71.15083697242613, -5.994763756850707, -39.72549451158927),
LABColor(65.48106058907833, -2.735745792537936, -48.15471238926561),
LABColor(60.43009440850862, 2.079928897321559, -55.10935847069616),
LABColor(55.62267676922188, 4.998684384486918, -55.02164729429915),
LABColor(49.27006645904875, 8.470398370314381, -54.494796838457546),
LABColor(43.16828856394358, 11.968483076143844, -53.972567377977974),
LABColor(32.17757793894193, 18.96054990229354, -53.45146365049088),
LABColor.fromRGBInt(0xFF82B1FF),
LABColor.fromRGBInt(0xFF448AFF),
LABColor.fromRGBInt(0xFF2979FF),
LABColor.fromRGBInt(0xFF2962FF),
],
[
LABColor(95.35713467762652, -4.797149155388203, -6.550002550504308),
LABColor(88.27942649540043, -10.836006614583892, -16.359361821940375),
LABColor(81.10009044900976, -15.323054522981716, -26.419121191320947),
LABColor(74.44713958259777, -16.664432625362547, -35.19702686900037),
LABColor(69.87836465637318, -14.291515332054693, -41.827430329755174),
LABColor(65.68851259178913, -9.612635721963692, -47.34091616039191),
LABColor(60.88357994308973, -7.252819027184943, -46.67753731595634),
LABColor(54.26166495426166, -3.8141836897908066, -45.97939475762498),
LABColor(48.10661895072673, -1.378998784464347, -44.34466750206778),
LABColor(36.34401147057282, 5.067812404713545, -43.11786257561915),
LABColor.fromRGBInt(0xFF80D8FF),
LABColor.fromRGBInt(0xFF40C4FF),
LABColor.fromRGBInt(0xFF00B0FF),
LABColor.fromRGBInt(0xFF0091EA),
],
[
LABColor(95.69295154599753, -6.898716127301141, -3.994284229654421),
LABColor(89.52842524059004, -16.412398289601725, -9.260466069266693),
LABColor(83.32031214655748, -24.83036840728098, -14.568673583304603),
LABColor(77.35338313752958, -30.201708572215104, -18.92358284721101),
LABColor(73.45322093857781, -31.88590390189383, -21.130459992513686),
LABColor(69.97638465064783, -30.679850324547953, -23.186685661136707),
LABColor(64.44491716553777, -29.08337434584457, -21.154935769156214),
LABColor(56.99816432961103, -27.31081477279451, -17.86988815767443),
LABColor(49.75464182255671, -25.335383503694242, -15.024722591662787),
LABColor(36.52725894264432, -22.129641744194515, -9.176159146894303),
LABColor.fromRGBInt(0xFF84FFFF),
LABColor.fromRGBInt(0xFF18FFFF),
LABColor.fromRGBInt(0xFF00E5FF),
LABColor.fromRGBInt(0xFF00B8D4),
],
[
LABColor(94.18453941589918, -6.08351703428972, -1.5488916051161983),
LABColor(85.68177077414457, -15.333179440298606, -2.8519825761476048),
LABColor(76.85067847190405, -24.844059173189713, -3.8750785132192656),
LABColor(68.02762242570138, -32.566861154120716, -4.015231084407134),
LABColor(61.667257304525464, -36.06752603289354, -3.4734046401753815),
LABColor(55.67310397390196, -36.66069960626328, -2.125617915169653),
LABColor(51.059149495197715, -34.65019160301408, -1.3910484300432513),
LABColor(45.269081019218405, -32.13244775422941, -0.4526371852697775),
LABColor(39.36899076059384, -29.25264468583161, -0.03562564673170732),
LABColor(28.58363043701477, -24.585465516136413, 1.8037402162492389),
LABColor.fromRGBInt(0xFFA7FFEB),
LABColor.fromRGBInt(0xFF64FFDA),
LABColor.fromRGBInt(0xFF1DE9B6),
LABColor.fromRGBInt(0xFF00BFA5),
],
[
LABColor(95.30530183565223, -6.430415645739263, 4.292950594459599),
LABColor(88.49014579152143, -15.23147744952702, 10.848261177683138),
LABColor(81.22616870575376, -24.993886168551583, 18.144696803330884),
LABColor(74.30361721558802, -35.56088696067356, 26.781515251907727),
LABColor(69.0430995277442, -42.61556126595995, 33.17109563126665),
LABColor(63.977421814072926, -48.54292673319982, 39.73241526342939),
LABColor(58.777960853461366, -46.1153692478013, 37.838910745225576),
LABColor(52.41108688974904, -43.21761792485762, 35.62250659009424),
LABColor(46.2813873076426, -40.25816227675361, 33.32343229338761),
LABColor(34.685655305814514, -34.75343878510312, 28.866739034359767),
LABColor.fromRGBInt(0xFFB9F6CA),
LABColor.fromRGBInt(0xFF69F0AE),
LABColor.fromRGBInt(0xFF00E676),
LABColor.fromRGBInt(0xFF00C853),
],
[
LABColor(96.70518169355954, -4.929987845095463, 6.397084523168894),
LABColor(91.66416061199438, -12.057032041945693, 16.054604579275143),
LABColor(86.2244395865449, -19.613646834080622, 26.384906423454236),
LABColor(80.83404879636919, -27.080171840756893, 37.378493742021334),
LABColor(76.79543725108964, -32.76659719736752, 45.912190572444445),
LABColor(72.90025297028019, -37.549139223927384, 53.51959496103027),
LABColor(67.21532310272079, -36.56304870773486, 50.49629051268894),
LABColor(59.91051142210195, -35.77011466063357, 46.56465847976187),
LABColor(52.51015841084511, -34.47903440699235, 42.20723868724268),
LABColor(39.41191983353878, -32.80460974352642, 35.255490585630014),
LABColor.fromRGBInt(0xFFCCFF90),
LABColor.fromRGBInt(0xFFB2FF59),
LABColor.fromRGBInt(0xFF76FF03),
LABColor.fromRGBInt(0xFF64DD17),
],
[
LABColor(97.99506057883428, -4.059632482741494, 9.355797602381521),
LABColor(94.80926235976536, -9.237091467352855, 23.230650064824985),
LABColor(91.85205843526167, -15.053917327011114, 38.86115182206598),
LABColor(88.75812142080242, -19.542900400164097, 53.71785675783709),
LABColor(86.27404180729515, -22.173992891121596, 63.978639065232514),
LABColor(84.20566835376492, -24.270643520989342, 72.79624067033038),
LABColor(78.27915100603997, -21.181850056402496, 68.82763412297965),
LABColor(70.82385811892824, -17.788148932525672, 64.00327817988128),
LABColor(62.936867012868035, -13.697412111684903, 58.513000509287835),
LABColor(49.498610881452535, -6.485230564384715, 49.67432722833751),
LABColor.fromRGBInt(0xFFF4FF81),
LABColor.fromRGBInt(0xFFEEFF41),
LABColor.fromRGBInt(0xFFC6FF00),
LABColor.fromRGBInt(0xFFAEEA00),
],
[
LABColor(98.93885129752759, -3.0098470288543178, 10.765736833790008),
LABColor(97.22689784824074, -6.174599368734491, 26.22932417355146),
LABColor(95.58092947828766, -8.907132848473886, 43.56297291446567),
LABColor(94.09009515702486, -10.509628942710735, 60.20019514231188),
LABColor(93.06546746683087, -11.008558476013008, 71.76500826005477),
LABColor(92.12975017760128, -10.830023094868302, 80.9090559640089),
LABColor(87.12188349168609, -2.3764300099239355, 78.14868195373407),
LABColor(80.96200442419905, 8.849333792729064, 75.05050700092679),
LABColor(75.00342770718086, 20.340173566879283, 72.24841925958934),
LABColor(65.48207757431567, 39.647064970476094, 68.34872841768654),
LABColor.fromRGBInt(0xFFFFFF8D),
LABColor.fromRGBInt(0xFFFFFF00),
LABColor.fromRGBInt(0xFFFFEA00),
LABColor.fromRGBInt(0xFFFFD600),
],
[
LABColor(97.5642392074337, -1.445525639405032, 11.881254316297674),
LABColor(93.67057953749456, -1.8693096862072434, 30.02888670415651),
LABColor(89.94571492804107, -1.0224503814769692, 49.649542361642276),
LABColor(86.71009164153801, 1.0496066396428194, 68.77377342409739),
LABColor(83.78773993319211, 5.248231820098425, 78.92920457852716),
LABColor(81.52191382080228, 9.403655370707199, 82.69257112982746),
LABColor(78.17240973804697, 16.628512886531887, 81.09358318806208),
LABColor(73.80899654381052, 26.53614315250874, 78.21754052181723),
LABColor(70.1134511665764, 35.3007623359744, 75.87510992138593),
LABColor(63.86460405565717, 50.94648214505959, 72.17815682124423),
LABColor.fromRGBInt(0xFFFFE57F),
LABColor.fromRGBInt(0xFFFFD740),
LABColor.fromRGBInt(0xFFFFC400),
LABColor.fromRGBInt(0xFFFFAB00),
],
[
LABColor(96.30459517801387, 0.923151172282477, 10.598439446083074),
LABColor(90.68320082865087, 4.103774964681062, 26.485793721916128),
LABColor(85.00055287186233, 9.047181758866651, 44.51407622580792),
LABColor(79.42428495742953, 16.452610724439875, 62.08721739074201),
LABColor(75.47792699289774, 23.395742928451867, 72.64347611236501),
LABColor(72.04246561548388, 30.681921012382098, 77.08579298904603),
LABColor(68.94724338946975, 35.22014778433863, 74.88425044595111),
LABColor(64.83017495535229, 40.91200730099703, 71.9596053545428),
LABColor(60.8534207471871, 46.41483590510681, 69.18061963415211),
LABColor(54.77571742962287, 55.282751019360035, 65.10193403547922),
LABColor.fromRGBInt(0xFFFFD180),
LABColor.fromRGBInt(0xFFFFAB40),
LABColor.fromRGBInt(0xFFFF9100),
LABColor.fromRGBInt(0xFFFF6D00),
],
[
LABColor(93.69219844671957, 5.763979334358293, 3.1700162796469034),
LABColor(86.04629434276428, 15.750843803958192, 14.828476927090994),
LABColor(77.54010042938336, 27.90113842540043, 25.99645229289065),
LABColor(69.74095456707857, 41.14487377552256, 39.443320178900024),
LABColor(64.37085344539341, 51.890379620443575, 50.81312471046415),
LABColor(60.06780837277435, 61.65258736118817, 61.54771829165221),
LABColor(57.28707915232363, 60.3250664308812, 60.07341536376447),
LABColor(53.810052616293845, 58.36760943780162, 58.19586806694884),
LABColor(50.301352405105874, 56.40104898089937, 55.924141992404344),
LABColor(43.86477994548343, 52.970887703910726, 52.30067989225532),
LABColor.fromRGBInt(0xFFFF9E80),
LABColor.fromRGBInt(0xFFFF6E40),
LABColor.fromRGBInt(0xFFFF3D00),
LABColor.fromRGBInt(0xFFDD2C00),
],
[
LABColor(93.29864888069987, 0.9915456090475727, 1.442353076378411),
LABColor(82.80884359004081, 3.116221903342209, 3.3523059451463055),
LABColor(70.95493047668185, 5.469742193344784, 5.449009494553492),
LABColor(58.712934619103066, 7.990991075363385, 8.352488495367627),
LABColor(49.150208552875895, 10.570984981000397, 10.831440151197924),
LABColor(39.63200151837749, 13.138881961627241, 13.531574711511885),
LABColor(35.600996682015754, 12.40352847757295, 12.10432183902449),
LABColor(30.084271265759952, 11.317148149878081, 10.547484304296217),
LABColor(24.555014696416578, 10.816613316782464, 8.506555306791984),
LABColor(18.35055226514404, 10.225725550338765, 7.058582769882571),
],
[
LABColor(98.27202740980219, -1.6418393644634932e-5, 6.567357457853973e-6),
LABColor(96.53749336548567, -1.616917905122861e-5, 6.467671598286984e-6),
LABColor(94.0978378987781, -1.581865383126768e-5, 6.327461532507073e-6),
LABColor(89.17728373493613, -1.511167768697419e-5, 6.044671074789676e-6),
LABColor(76.61119902231323, -1.330620591488696e-5, 5.322482343750323e-6),
LABColor(65.11424774127516, -1.1654345155598378e-5, 4.661738062239351e-6),
LABColor(49.238989620828065, -9.373417431124409e-6, 3.7493669724497636e-6),
LABColor(41.14266843804848, -8.210152946386273e-6, 3.2840611896567395e-6),
LABColor(27.974857206003705, -6.318226192236764e-6, 2.5272904768947058e-6),
LABColor(12.740011331302725, -4.129311698131133e-6, 1.6517246792524531e-6),
],
[
LABColor(94.27665212516236, -0.637571046109342, -1.313515378996688),
LABColor(85.77788001492097, -2.2777811084512822, -3.0177758416151557),
LABColor(76.12296325015231, -3.401502988883809, -5.16867892977908),
LABColor(66.16340108908365, -4.819627183079045, -7.520697631614404),
LABColor(58.35752478513645, -5.7195089100892105, -9.165988916613488),
LABColor(50.70748082202715, -6.837992965799455, -10.956055112409357),
LABColor(44.85917867647632, -6.411990559239578, -9.74511982878765),
LABColor(36.92458930566504, -5.319878610845596, -8.341943474561553),
LABColor(29.115334784637618, -4.168907828645069, -6.8629962199973304),
LABColor(19.958338450799914, -3.3116721453186617, -5.4486142104736786),
],
];
const _defaultLightnessTolerance = <double>[
2.048875457,
5.124792061,
8.751659557,
12.07628774,
13.91449542,
15.92738893,
15.46585818,
15.09779227,
15.13738673,
15.09818372,
12.16800645,
17.26178879,
17.87176166,
16.72047178,
];
const _defaultChromaTolerance = <double>[
1.762442714,
4.213532634,
7.395827458,
11.07174158,
13.89634504,
16.37591477,
16.27071136,
16.54160806,
17.35916727,
19.88410864,
12.82357630,
18.40545289,
21.71894697,
23.23753494,
];
_ClosestGoldenPalette _findClosestGoldenPalette(
LABColor labColor, {
List<List<LABColor>> goldenPalettes,
}) {
goldenPalettes ??= _goldenPalettes;
double differenceMinimum = double.infinity;
List<LABColor> closestGoldenPalette = goldenPalettes[0];
int closestReference = -1;
for (int paletteIndex = 0;
paletteIndex < goldenPalettes.length;
paletteIndex++)
for (int colorIndex = 0;
colorIndex < goldenPalettes[paletteIndex].length &&
0 < differenceMinimum;
colorIndex++) {
final goldenColor = goldenPalettes[paletteIndex][colorIndex];
final avgLightness = (goldenColor.lightness + labColor.lightness) / 2;
final goldenColorChroma = math.sqrt(
math.pow(goldenColor.a, 2) + math.pow(goldenColor.b, 2),
);
final labColorChroma = math.sqrt(
math.pow(labColor.a, 2) + math.pow(labColor.b, 2),
);
final avgChroma = (goldenColorChroma + labColorChroma) / 2;
final G = 0.5 *
(1 -
math.sqrt(math.pow(avgChroma, 7) /
(math.pow(avgChroma, 7) + math.pow(25, 7))));
final adjustedGoldenA = goldenColor.a * (1 + G);
final adjustedLabA = labColor.a * (1 + G);
final goldenColorAdjustedChroma =
math.sqrt(math.pow(adjustedGoldenA, 2) + math.pow(goldenColor.b, 2));
final labColorAdjustedChroma =
math.sqrt(math.pow(adjustedLabA, 2) + math.pow(labColor.b, 2));
final deltaAdjustedChroma =
labColorAdjustedChroma - goldenColorAdjustedChroma;
final avgAdjustedChroma =
(goldenColorAdjustedChroma + labColorAdjustedChroma) / 2;
final goldenColorModifiedHue = _lab2hue(goldenColor.b, adjustedGoldenA);
final labColorModifiedHue = _lab2hue(labColor.b, adjustedLabA);
final deltaHue = 2 *
math.sqrt(goldenColorAdjustedChroma * labColorAdjustedChroma) *
math.sin(
(((1e-4 > goldenColorChroma.abs() || 1e-4 > labColorChroma.abs()
? 0
: 180 >=
(labColorModifiedHue -
goldenColorModifiedHue)
.abs()
? labColorModifiedHue - goldenColorModifiedHue
: labColorModifiedHue <= goldenColorModifiedHue
? labColorModifiedHue -
goldenColorModifiedHue +
360
: labColorModifiedHue -
goldenColorModifiedHue -
360) /
2) *
math.pi) /
180,
);
final double avgHue = 1e-4 > goldenColorChroma.abs() ||
1e-4 > labColorChroma.abs()
? 0
: 180 >= (labColorModifiedHue - goldenColorModifiedHue).abs()
? (goldenColorModifiedHue + labColorModifiedHue) / 2
: 360 > goldenColorModifiedHue + labColorModifiedHue
? (goldenColorModifiedHue + labColorModifiedHue + 360) / 2
: (goldenColorModifiedHue + labColorModifiedHue - 360) / 2;
final chromaCompensation = 1 + 0.045 * avgAdjustedChroma;
final hueCompensation = 1 +
0.015 *
avgAdjustedChroma *
(1 -
0.17 * math.cos(((avgHue - 30) * math.pi) / 180) +
0.24 * math.cos((2 * avgHue * math.pi) / 180) +
0.32 * math.cos(((3 * avgHue + 6) * math.pi) / 180) -
0.2 * math.cos(((4 * avgHue - 63) * math.pi) / 180));
final lightnessCompensation = 1 +
(0.015 * math.pow(avgLightness - 50, 2)) /
math.sqrt(20 + math.pow(avgLightness - 50, 2));
final chromaRotation = 2 *
math.sqrt(math.pow(avgAdjustedChroma, 7) /
(math.pow(avgAdjustedChroma, 7) + math.pow(25, 7)));
final deltaTheta = 30 * math.exp(-math.pow((avgHue - 275) / 25, 2));
final hueRotation = -1 *
chromaRotation *
math.sin(
(2 * deltaTheta * math.pi) / 180,
);
final difference = math.sqrt(
math.pow(
(labColor.lightness - goldenColor.lightness) /
(lightnessCompensation),
2,
) +
math.pow(deltaAdjustedChroma / (1 * chromaCompensation), 2) +
math.pow(deltaHue / (1 * hueCompensation), 2) +
(deltaAdjustedChroma / (1 * chromaCompensation)) *
hueRotation *
(deltaHue / (1 * hueCompensation)),
);
if (difference < differenceMinimum) {
differenceMinimum = difference;
closestGoldenPalette = goldenPalettes[paletteIndex];
closestReference = colorIndex;
}
}
return _ClosestGoldenPalette(
colors: closestGoldenPalette,
closestReference: closestReference,
);
}
double _lab2hue(double a, double b) {
if (1e-4 > a.abs() && 1e-4 > b.abs()) {
return 0;
}
a = (180 * math.atan2(a, b)) / math.pi;
return 0 <= a ? a : a + 360;
}
List<RGBColor> _generatePalette(
RGBColor sourceRgbColor, {
List<List<LABColor>> goldenPalettes,
List<double> lightnessTolerance,
List<double> chromaTolerance,
}) {
goldenPalettes ??= _goldenPalettes;
lightnessTolerance ??= _defaultLightnessTolerance;
chromaTolerance ??= _defaultChromaTolerance;
final sourceLabColor = LABColor.fromRGBColor(sourceRgbColor);
final goldenPalette = _findClosestGoldenPalette(
sourceLabColor,
goldenPalettes: goldenPalettes,
);
final goldenColors = goldenPalette.colors;
final closestGoldenLabColor = goldenColors[goldenPalette.closestReference];
final closestGoldenLchColor = LCHColor.fromLABColor(closestGoldenLabColor);
final sourceLchColor = LCHColor.fromLABColor(sourceLabColor);
final isGoldenColorGreyInMiddle =
30 > LCHColor.fromLABColor(goldenColors[5]).chroma;
final deltaGoldenLightness =
closestGoldenLchColor.lightness - sourceLchColor.lightness;
final deltaGoldenChroma =
closestGoldenLchColor.chroma - sourceLchColor.chroma;
final deltaGoldenHue = closestGoldenLchColor.hue - sourceLchColor.hue;
double lightnessMaximum = 100.0;
const lightnessMinimumStep = 1.7;
return goldenColors.asMap().entries.map((e) {
final goldenLabColor = e.value;
final index = e.key;
if (goldenLabColor.equals(closestGoldenLabColor)) {
lightnessMaximum = math.max(sourceLchColor.lightness - 1.7, 0);
return sourceRgbColor;
}
if (index == 10) {
lightnessMaximum = 100; // reset for accents
}
final goldenLchColor = LCHColor.fromLABColor(goldenLabColor);
double lightness = goldenLabColor.lightness -
(lightnessTolerance[index] /
lightnessTolerance[goldenPalette.closestReference]) *
deltaGoldenLightness;
lightness = math.min(lightness, lightnessMaximum).clamp(0, 100);
double chroma = isGoldenColorGreyInMiddle
? goldenLchColor.chroma - deltaGoldenChroma
: goldenLchColor.chroma -
deltaGoldenChroma *
math.min(
chromaTolerance[index] /
chromaTolerance[goldenPalette.closestReference],
1.25);
chroma = math.max(0, chroma);
final hue = (goldenLchColor.hue - deltaGoldenHue + 360) % 360;
final lchColor = LCHColor(
lightness: lightness,
chroma: chroma,
hue: hue,
);
lightnessMaximum = math.max(lchColor.lightness - lightnessMinimumStep, 0);
return lchColor.toRGBColor();
}).toList(growable: false);
}
class _ClosestGoldenPalette {
_ClosestGoldenPalette({
this.colors,
this.closestReference = -1,
});
final List<LABColor> colors;
final int closestReference;
}
//
import 'package:flutter/material.dart';
import 'material_palette_generator.dart';
String generateFlutterSwatchCode(String colorName, Color inputColor) {
final colors = generateMaterialPalette(RGBColor.fromInt(inputColor.value));
return <String>[
'// input: $inputColor',
'static const MaterialColor $colorName = MaterialColor(',
' _${colorName}PrimaryValue,',
' <int, Color>{',
' 50: Color(0x${colors[0].toInt().toRadixString(16).toUpperCase()}),',
' 100: Color(0x${colors[1].toInt().toRadixString(16).toUpperCase()}),',
' 200: Color(0x${colors[2].toInt().toRadixString(16).toUpperCase()}),',
' 300: Color(0x${colors[3].toInt().toRadixString(16).toUpperCase()}),',
' 400: Color(0x${colors[4].toInt().toRadixString(16).toUpperCase()}),',
' 500: Color(_${colorName}PrimaryValue),',
' 600: Color(0x${colors[6].toInt().toRadixString(16).toUpperCase()}),',
' 700: Color(0x${colors[7].toInt().toRadixString(16).toUpperCase()}),',
' 800: Color(0x${colors[8].toInt().toRadixString(16).toUpperCase()}),',
' 900: Color(0x${colors[9].toInt().toRadixString(16).toUpperCase()}),',
' },',
');',
'static const int _${colorName}PrimaryValue = 0x${colors[5].toInt().toRadixString(16).toUpperCase()};',
'',
'static const MaterialAccentColor ${colorName}Accent = MaterialAccentColor(',
' _${colorName}AccentPrimaryValue,',
' <int, Color>{',
' 100: Color(0x${colors[10].toInt().toRadixString(16).toUpperCase()}),',
' 200: Color(_${colorName}AccentPrimaryValue),',
' 400: Color(0x${colors[12].toInt().toRadixString(16).toUpperCase()}),',
' 700: Color(0x${colors[13].toInt().toRadixString(16).toUpperCase()}),',
' },',
');',
'static const int _${colorName}AccentPrimaryValue = 0x${colors[11].toInt().toRadixString(16).toUpperCase()};',
].join('\n');
}
@exavolt
Copy link
Author

exavolt commented Jul 13, 2020

What is this for?

This code is for generating Material-Design-style color palettes from an input color. This code can be used to programmatically generate the swatches for your Flutter applications based on your brand base color.

Because your blue might not be the same as Flutter's blue.

The results should match MD2014's

Screenshot_1594615697

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment