import Color from "colorjs.io/dist/color.js"; import {PNG} from "pngjs"; import fs from "fs"; function generateColors() { let pad = function(n) { while (colors.length < n) { colors.push(new Color("slategray")); } } let band = function(colors) { return function(point, shimmer) { point += shimmer; var ix2 = point * colors.length; while (ix2 < 0) { ix2 += colors.length; } return colors[Math.floor(ix2) % colors.length].mix( colors[Math.floor(ix2 + 1) % colors.length], ix2 % 1, {"space": "oklch", "outputSpace": "oklab"} ) } } let ramp = function(start, c0, c1, c2) { // deep base -> center -> pastel var r01 = c0.range(c1, {space: "oklab", outputSpace: "oklch"}); var r12 = c1.range(c2, {space: "oklab", outputSpace: "oklch"}); var n = 5; var i = 0; for (var amt = 0; amt < n; amt++) { colors[start + i * 8] = r01(amt/(n - 1)); i += 1; } n = 3; for (var amt = 1; amt < n; amt++) { colors[start + i * 8] = r12(amt/(n - 1)); i += 1; } } var colors = []; // we were originally appending these // but for now, just prefill all the slots pad(64); // so the slots are filled and we can replace them // system colors colors[0] = new Color("#000000"); colors[1] = new Color("#0000ff"); colors[2] = new Color("#00ff00"); colors[3] = new Color("#00ffff"); colors[4] = new Color("#ff0000"); colors[5] = new Color("#ff00ff"); colors[6] = new Color("#ffff00"); colors[7] = new Color("#ffffff"); for (var i = 0; i < 8; i++) { // colors[i].hsv.h += 135.0; } let lowBand = band([ new Color("oklch(11.2% 0.0744 268.04)"), new Color("oklch(15.55% 0.0782 310.39)"), new Color("oklch(21.51% 0.0864 357.28)"), new Color("oklch(22.74% 0.0755 43.16)"), new Color("oklch(23.84% 0.0733 145.59)"), new Color("oklch(27.4% 0.0444 171.2)"), // NOTE: This reproduces terribly new Color("oklch(19.1% 0.0396 233.51)"), // NOTE: This reproduces terribly new Color("oklch(18.63% 0.0575 259.12)"), // NOTE: This reproduces terribly ]); let middleBand = band([ new Color("oklch(55.34% 0.241 291.34)"), new Color("oklch(58.54% 0.1994 330.82)"), new Color("oklch(63.2% 0.2135 20.28)"), new Color("oklch(80.34% 0.16 70.47)"), new Color("oklch(91.95% 0.23681720512967358 126.89394948265881)"), new Color("oklch(87.99% 0.2353 147.34)"), new Color("oklch(86.44% 0.1505 187.42)"), new Color("oklch(74.91% 0.1505 235.22)"), ]); let highBand = band([ new Color("oklch(92.3% 0.041 317.16)"), new Color("oklch(94.93% 0.0217 358.04)"), new Color("oklch(95.21% 0.0101 7.31)"), new Color("oklch(98.41% 0.0172 88.4)"), new Color("oklch(99.58% 0.00969628196819721 125.6732391579389)"), new Color("oklch(97.53% 0.0584 127.22)"), new Color("oklch(96.38% 0.0465 167.45)"), new Color("oklch(95.21% 0.0475 214.73)"), ]); for (var i = 0; i < 4; i++) { var lowRampIx = (i - 0.25) / 4; var low = lowBand(lowRampIx, -0.3); var middle = middleBand(lowRampIx, 0.0); colors[8 + i * 2] = low.mix(middle, 0.2/4.0, {space: "oklab"}) colors[8 + i * 2 + 1] = low.mix(middle, 0.8/4.0, {space: "oklab"}) colors[8 + i * 2].oklch.c *= 0.4; colors[8 + i * 2 + 1].oklch.c *= 0.5; } for (var i = 0; i < 4; i++) { for (var c = 1; c < 3; c++) { var lowRampIx = (i - 0.25) / 4; var low = lowBand(lowRampIx, -0.3); var middle = middleBand(lowRampIx, 0.0); var cbase = low.mix(middle, ([2.1, 3.1, 4.0][c - 1])/4, {space: "oklab"}) colors[8 + i * 2 + c * 8] = cbase var color2 = low.mix(middle, ([1.7, 2.7, 3.5][c - 1])/4, {space: "oklab"}) color2.oklch.c *= 0.6 colors[8 + i * 2 + 1 + c * 8] = color2; } } for (var i = 0; i < 8; i++) { colors[32 + i] = middleBand((i - 1) / 8, 0.0); colors[32 + i + 8] = middleBand((i - 1) / 8, 0.0).mix(highBand((i - 1) / 8, 0.15), 1.5/5, {space: "oklch"}) } for (var i = 0; i < 4; i++) { var highRampIx = (i - 0.25) / 4; colors[48 + i * 2] = middleBand(highRampIx, 0.0).mix(highBand(highRampIx, 0.15), 2.4/5, {space: "oklch"}) colors[48 + i * 2 + 1] = middleBand(highRampIx, 0.0).mix(highBand(highRampIx, 0.15), 3.4/5, {space: "oklch"}) colors[48 + i * 2 + 1].lch.c *= 0.7 } for (var i = 0; i < 4; i++) { var highRampIx = (i - 0.25) / 4; colors[56 + i * 2] = middleBand(highRampIx, 0.0).mix(highBand(highRampIx, 0.15), 4.3/5, {space: "oklch"}) colors[56 + i * 2].lch.c *= 0.6 colors[56 + i * 2 + 1] = middleBand(highRampIx, 0.0).mix(highBand(highRampIx, 0.1), 5/5, {space: "oklch"}) colors[56 + i * 2 + 1].lch.c *= 0.4 colors[56 + i * 2].lch.l = colors[56 + i * 2].lch.l * 0.25 + colors[56 + i * 2 + 1].lch.l * 0.75 } /* for (var i = 0; i < 8; i++) { colors[8 + i] = middleBand(i/8.0); } */ return colors; } function clamp(x) { if (x < 0.0) { return 0.0; } if (x > 255.0) { return 255.0; } return Math.round(x); } function dump(fname, colors, width, height, scalar) { if (colors.length != width * height) { throw new Error("wrong number of colors: " + colors.length); } let pngdata = []; for (var y = 0; y < height * scalar; y++) { for (var x = 0; x < width * scalar; x++) { let i = Math.floor(y / scalar) * width + Math.floor(x / scalar); pngdata.push(clamp(255 * colors[i].srgb.r)); pngdata.push(clamp(255 * colors[i].srgb.g)); pngdata.push(clamp(255 * colors[i].srgb.b)); pngdata.push(255); } } var png = new PNG({ width: width * scalar, height: height * scalar }); png.data = pngdata; png.pack().pipe(fs.createWriteStream(fname)); } function dumpRust(fname, colors) { var paletteString = ""; paletteString += "pub const PALETTE: [[u8; 3]; " + palette.length + "] = [\n" for (var i = 0; i < colors.length; i++) { var c = colors[i]; paletteString += (" [" + clamp(255 * c.srgb.r) + ", " + clamp(255 * c.srgb.g) + ", " + clamp(255 * c.srgb.b) + "],\n"); } paletteString += "];\n" fs.writeFileSync(fname, paletteString); } var palette = generateColors(); dump("palette.png", palette, 8, 8, 64); dumpRust("../../src/palette.rs", palette)