406 lines
8.9 KiB
PHP
406 lines
8.9 KiB
PHP
<?php
|
|
|
|
///////// Colour scheme generation - only web useful
|
|
// FIXME - snake_case
|
|
|
|
|
|
function rgbToHsl($r, $g, $b)
|
|
{
|
|
$r /= 255;
|
|
$g /= 255;
|
|
$b /= 255;
|
|
|
|
$max = max($r, $g, $b);
|
|
$min = min($r, $g, $b);
|
|
|
|
$l = ($max + $min) / 2;
|
|
|
|
if ($max == $min) {
|
|
// No saturation, this is a shade of gray
|
|
$h = $s = 0;
|
|
} else {
|
|
$delta = $max - $min;
|
|
$s = ($l > 0.5) ? $delta / (2 - $max - $min) : $delta / ($max + $min);
|
|
|
|
switch ($max) {
|
|
case $r:
|
|
$h = ($g - $b) / $delta + (($g < $b) ? 6 : 0);
|
|
break;
|
|
case $g:
|
|
$h = ($b - $r) / $delta + 2;
|
|
break;
|
|
case $b:
|
|
$h = ($r - $g) / $delta + 4;
|
|
break;
|
|
}
|
|
|
|
$h /= 6;
|
|
}
|
|
|
|
return [$h, $s, $l];
|
|
}
|
|
|
|
function hslToRgb($h, $s, $l)
|
|
{
|
|
if ($s == 0) {
|
|
// No saturation, this is a shade of gray
|
|
$r = $g = $b = $l * 255;
|
|
} else {
|
|
$q = ($l < 0.5) ? $l * (1 + $s) : $l + $s - ($l * $s);
|
|
$p = (2 * $l) - $q;
|
|
|
|
$r = round(255 * hueToRgb($p, $q, $h + (1 / 3)));
|
|
$g = round(255 * hueToRgb($p, $q, $h));
|
|
$b = round(255 * hueToRgb($p, $q, $h - (1 / 3)));
|
|
}
|
|
|
|
return [(int)$r, (int)$g, (int)$b];
|
|
}
|
|
|
|
function hueToRgb($p, $q, $t)
|
|
{
|
|
if ($t < 0) {
|
|
$t += 1;
|
|
}
|
|
if ($t > 1) {
|
|
$t -= 1;
|
|
}
|
|
if ($t < 1 / 6) {
|
|
return $p + ($q - $p) * 6 * $t;
|
|
}
|
|
if ($t < 1 / 2) {
|
|
return $q;
|
|
}
|
|
if ($t < 2 / 3) {
|
|
return $p + ($q - $p) * (2 / 3 - $t) * 6;
|
|
}
|
|
return $p;
|
|
}
|
|
|
|
function darkModeColor($r, $g, $b)
|
|
{
|
|
// Convert RGB to HSL
|
|
[$h, $s, $l] = rgbToHsl($r, $g, $b);
|
|
|
|
// Invert lightness
|
|
$l = 1 - $l;
|
|
|
|
// Convert back to RGB
|
|
return hslToRgb($h, $s, $l);
|
|
}
|
|
|
|
function interpolateRainbow($t)
|
|
{
|
|
$hue = $t * 360;
|
|
return hsvToRgb($hue, 1, 1);
|
|
}
|
|
|
|
function interpolateSinebow($t)
|
|
{
|
|
$t = 0.5 * ($t + 1);
|
|
$t = sin($t * M_PI);
|
|
$a = 0.5 * $t * $t;
|
|
$b = 0.5 * $t;
|
|
$c = 0.5 * $t * sqrt(1 - $t * $t);
|
|
return [
|
|
255 * (0.5 + $a),
|
|
255 * (0.5 + $b),
|
|
255 * (0.5 + $c),
|
|
];
|
|
}
|
|
|
|
function hsvToRgb($h, $s, $v)
|
|
{
|
|
$c = $v * $s;
|
|
$x = $c * (1 - abs(fmod($h / 60, 2) - 1));
|
|
$m = $v - $c;
|
|
|
|
if ($h < 60) {
|
|
$rgb = [$c, $x, 0];
|
|
} elseif ($h < 120) {
|
|
$rgb = [$x, $c, 0];
|
|
} elseif ($h < 180) {
|
|
$rgb = [0, $c, $x];
|
|
} elseif ($h < 240) {
|
|
$rgb = [0, $x, $c];
|
|
} elseif ($h < 300) {
|
|
$rgb = [$x, 0, $c];
|
|
} else {
|
|
$rgb = [$c, 0, $x];
|
|
}
|
|
|
|
return [
|
|
255 * ($rgb[0] + $m),
|
|
255 * ($rgb[1] + $m),
|
|
255 * ($rgb[2] + $m),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Generates an array of color codes based on the given interpolator function.
|
|
*
|
|
* @param int $n The number of colors to generate in the palette.
|
|
* If 0 or negative, an empty array will be returned.
|
|
* If 1, the palette will have a single color generated by the interpolator function.
|
|
* @param callable $interpolator A function that takes a float parameter between 0 and 1 (inclusive) and returns an
|
|
* array with three elements (red, green, blue) representing the color. Each element
|
|
* should be an integer between 0 and 255.
|
|
*
|
|
* @return array An array of color codes in the format "#RRGGBB", where RR, GG, and BB are two-digit hexadecimal
|
|
* values representing the red, green, and blue components of the color.
|
|
*/
|
|
function generate_palette($n, $interpolator)
|
|
{
|
|
if ($n <= 0) {
|
|
return [];
|
|
}
|
|
|
|
$palette = [];
|
|
|
|
if ($n == 1) {
|
|
$color = call_user_func($interpolator, 0);
|
|
$palette[] = sprintf("#%02x%02x%02x", $color[0], $color[1], $color[2]);
|
|
return $palette;
|
|
}
|
|
|
|
for ($i = 0; $i < $n; $i++) {
|
|
$t = $i / ($n - 1);
|
|
$color = call_user_func($interpolator, $t);
|
|
$palette[] = sprintf("#%02x%02x%02x", $color[0], $color[1], $color[2]);
|
|
}
|
|
|
|
return $palette;
|
|
}
|
|
|
|
function interpolateSpectral($t)
|
|
{
|
|
$colors = [
|
|
[158, 1, 66],
|
|
[213, 62, 79],
|
|
[244, 109, 67],
|
|
[253, 174, 97],
|
|
[254, 224, 139],
|
|
[255, 255, 191],
|
|
[230, 245, 152],
|
|
[171, 221, 164],
|
|
[102, 194, 165],
|
|
[50, 136, 189],
|
|
[94, 79, 162],
|
|
];
|
|
|
|
$segments = count($colors) - 1;
|
|
$index = ($t * $segments);
|
|
$lower = floor($index);
|
|
$upper = ceil($index);
|
|
$t = $index - $lower;
|
|
|
|
if ($lower == $upper) {
|
|
return $colors[$lower];
|
|
}
|
|
|
|
return interpolate($t, $colors[$lower], $colors[$upper]);
|
|
}
|
|
|
|
function interpolate($t, $color1, $color2)
|
|
{
|
|
return [
|
|
$color1[0] + $t * ($color2[0] - $color1[0]),
|
|
$color1[1] + $t * ($color2[1] - $color1[1]),
|
|
$color1[2] + $t * ($color2[2] - $color1[2]),
|
|
];
|
|
}
|
|
|
|
function interpolateCubehelix($t, $startAngle = 300, $rotations = -1.5, $hue = 1, $gamma = 1)
|
|
{
|
|
$angle = 2 * M_PI * (($startAngle / 360) + $rotations * $t);
|
|
|
|
$fr = $t ** $gamma;
|
|
$a = $hue * $fr * (1 - $fr) / 2;
|
|
|
|
$cosA = cos($angle);
|
|
$sinA = sin($angle);
|
|
|
|
$r = $fr + $a * (-0.14861 * $cosA + 1.78277 * $sinA);
|
|
$g = $fr + $a * (-0.29227 * $cosA - 0.90649 * $sinA);
|
|
$b = $fr + $a * (1.97294 * $cosA);
|
|
|
|
return [
|
|
(int)round(255 * max(0, min(1, $r))),
|
|
(int)round(255 * max(0, min(1, $g))),
|
|
(int)round(255 * max(0, min(1, $b))),
|
|
];
|
|
}
|
|
|
|
// The interpolateCubehelixDefault function
|
|
function interpolateCubehelixDefault($t)
|
|
{
|
|
return interpolateCubehelix($t, 300, -0.5, 1.7, 0.4);
|
|
}
|
|
|
|
function interpolateCubehelixGreen($t)
|
|
{
|
|
return interpolateCubehelix($t, 120, -0.5, 1.7, 0.4);
|
|
}
|
|
|
|
function interpolateCubehelixBlueToRed($t)
|
|
{
|
|
return interpolateCubehelix($t, 240, 0.5, 1.5, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixRedToGreen($t)
|
|
{
|
|
return interpolateCubehelix($t, 0, 1.0, 1.5, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixGrayscale($t)
|
|
{
|
|
return interpolateCubehelix($t, 0, 0, 0, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixViridisLike($t)
|
|
{
|
|
return interpolateCubehelix($t, 260, -0.5, 2.0, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixSunset($t)
|
|
{
|
|
return interpolateCubehelix($t, 0, 1.5, 2.0, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixCoolTones($t)
|
|
{
|
|
return interpolateCubehelix($t, 200, -0.5, 1.5, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixEarthTones($t)
|
|
{
|
|
return interpolateCubehelix($t, 100, -0.5, 1.5, 0.8);
|
|
}
|
|
|
|
function interpolateCubehelixCoolToWarm($t)
|
|
{
|
|
return interpolateCubehelix($t, 220, 0.5, 1.5, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixDivergingBlueYellow($t)
|
|
{
|
|
$middle_t = 0.5;
|
|
if ($t < $middle_t) {
|
|
return interpolateCubehelix(($middle_t - $t) * 2, 240, -0.5, 1.5, 1.0);
|
|
} else {
|
|
return interpolateCubehelix(($t - $middle_t) * 2, 60, 0.5, 1.5, 1.0);
|
|
}
|
|
}
|
|
|
|
function interpolateCubehelixOcean($t)
|
|
{
|
|
return interpolateCubehelix($t, 210, -1.0, 1.5, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixMagmaLike($t)
|
|
{
|
|
return interpolateCubehelix($t, 0, 1.5, 1.8, 1.0);
|
|
}
|
|
|
|
function interpolateCubehelixPastel($t)
|
|
{
|
|
return interpolateCubehelix($t, 300, 0.8, 0.7, 1.2);
|
|
}
|
|
|
|
function divergingColorMap($t, $colors)
|
|
{
|
|
$n = count($colors) - 1;
|
|
$index = min($n - 1, max(0, floor($t * $n)));
|
|
$t = ($t * $n) - $index;
|
|
return interpolateColor($t, $colors[$index], $colors[$index + 1]);
|
|
}
|
|
|
|
function interpolateRdYlBu($t)
|
|
{
|
|
$colors = [
|
|
[165, 0, 38],
|
|
[215, 48, 39],
|
|
[244, 109, 67],
|
|
[253, 174, 97],
|
|
[254, 224, 144],
|
|
[255, 255, 191],
|
|
[224, 243, 248],
|
|
[171, 217, 233],
|
|
[116, 173, 209],
|
|
[69, 117, 180],
|
|
[49, 54, 149],
|
|
];
|
|
return divergingColorMap($t, $colors);
|
|
}
|
|
|
|
function interpolateRdBu($t)
|
|
{
|
|
$colors = [
|
|
[103, 0, 31],
|
|
[178, 24, 43],
|
|
[214, 96, 77],
|
|
[244, 165, 130],
|
|
[253, 219, 199],
|
|
[247, 247, 247],
|
|
[209, 229, 240],
|
|
[146, 197, 222],
|
|
[67, 147, 195],
|
|
[33, 102, 172],
|
|
[5, 48, 97],
|
|
];
|
|
return divergingColorMap($t, $colors);
|
|
}
|
|
|
|
function interpolatePuOr($t)
|
|
{
|
|
$colors = [
|
|
[127, 59, 8],
|
|
[179, 88, 6],
|
|
[224, 130, 20],
|
|
[253, 184, 99],
|
|
[254, 224, 182],
|
|
[247, 247, 247],
|
|
[216, 218, 235],
|
|
[178, 171, 210],
|
|
[128, 115, 172],
|
|
[84, 39, 136],
|
|
[45, 0, 75],
|
|
];
|
|
return divergingColorMap($t, $colors);
|
|
}
|
|
|
|
function interpolateBrBG($t)
|
|
{
|
|
$colors = [
|
|
[84, 48, 5],
|
|
[140, 81, 10],
|
|
[191, 129, 45],
|
|
[223, 194, 125],
|
|
[246, 232, 195],
|
|
[245, 245, 245],
|
|
[199, 234, 229],
|
|
[128, 205, 193],
|
|
[53, 151, 143],
|
|
[1, 102, 94],
|
|
[0, 60, 48],
|
|
];
|
|
return divergingColorMap($t, $colors);
|
|
}
|
|
|
|
function interpolateCividis($t)
|
|
{
|
|
$colors = [
|
|
[0, 32, 76],
|
|
[0, 42, 102],
|
|
[0, 52, 110],
|
|
[12, 69, 132],
|
|
[38, 87, 141],
|
|
[61, 104, 147],
|
|
[83, 121, 153],
|
|
[109, 138, 158],
|
|
[134, 154, 163],
|
|
[160, 170, 168],
|
|
[185, 186, 172],
|
|
];
|
|
return divergingColorMap($t, $colors);
|
|
} |