Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script to make palette for R-G-colorblindness #398

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 235 additions & 0 deletions utils/generate-redgreen
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/usr/bin/env perl

# This script modifies the Solarized palette to have as high contrast as
# possible for red-green colorblind people. It keeps the saturation/lightness
# of all colors the same, as well as the hues of blue & yellow. It simply
# modifies the hues of the other colors so they are evenly spaced between blue
# and yellow (on the red side), and green/cyan get flipped back onto the green
# side. Running the script produces:
#
# SOLARIZED HEX
# --------- -------
# yellow #b58900
# orange #cb4916
# red #dc2f51
# magenta #bb36d3
# violet #6c76c4
# blue #268bd2
# cyan #2aa189
# green #189900
#
# Try to keep the values above up to date!

use strict;
use warnings;

sub rgb_to_hsl {
my ($r, $g, $b) = @_;

my $max = $r > $g ? $r : $g;
$max = $max > $b ? $max : $b;

my $min = $r < $g ? $r : $g;
$min = $min < $b ? $min : $b;

my $h;
if ($max == $min) {
$h = 0;
} elsif ($max == $r) {
$h = (0 + ($g - $b) / ($max - $min)) / 6;
} elsif ($max == $g) {
$h = (2 + ($b - $r) / ($max - $min)) / 6;
} else {
$h = (4 + ($r - $g) / ($max - $min)) / 6;
}
$h += 1 if $h < 0;

my $l = ($max + $min) / 2;

my $s;
if ($max == 0 || $min == 1) {
$s = 0;
} else {
$s = ($max - $l) / ($l < 0.5 ? $l : 1 - $l);
}

return ($h, $s, $l);
}

sub fmod2 {
my $x = shift;
my $x_ = int($x);
return $x - $x_ + ($x_ & 1);
}

sub hsl_to_rgb {
my ($h, $s, $l) = @_;

my $c = (1 - abs(2 * $l - 1)) * $s;

my $h_ = $h * 6;
my $x = $c * (1 - abs(fmod2($h_) - 1));
my ($r_, $g_, $b_);
if (0 <= $h_ && $h_ < 1) {
($r_, $g_, $b_) = ($c, $x, 0);
} elsif (1 <= $h_ && $h_ < 2) {
($r_, $g_, $b_) = ($x, $c, 0);
} elsif (2 <= $h_ && $h_ < 3) {
($r_, $g_, $b_) = (0, $c, $x);
} elsif (3 <= $h_ && $h_ < 4) {
($r_, $g_, $b_) = (0, $x, $c);
} elsif (4 <= $h_ && $h_ < 5) {
($r_, $g_, $b_) = ($x, 0, $c);
} else {
($r_, $g_, $b_) = ($c, 0, $x);
}

my $m = $l - $c / 2;
return ($r_ + $m, $g_ + $m, $b_ + $m);
}

sub hex_to_rgb {
my $color = shift;
die unless $color =~ /^#(?:[0-9A-Fa-f]{3}){1,2}$/;
$color = substr $color, 1;

my $val = hex $color;
my ($r, $g, $b);
if (length $color == 3) {
$b = $val & 0xf; $b |= $b << 4; $val >>= 4;
$g = $val & 0xf; $g |= $g << 4; $val >>= 4;
$r = $val & 0xf; $r |= $r << 4;
} else {
$b = $val & 0xff; $val >>= 8;
$g = $val & 0xff; $val >>= 8;
$r = $val & 0xff;
}

return ($r / 255.0, $g / 255.0, $b / 255.0);
}

sub hex_to_hsl {
rgb_to_hsl(hex_to_rgb(shift));
}

sub rgb_to_hex {
my ($r, $g, $b) = @_;
$r = int($r * 255.0 + 0.5);
$g = int($g * 255.0 + 0.5);
$b = int($b * 255.0 + 0.5);
my $val = ($r << 16) | ($g << 8) | $b;
sprintf '#%06x', $val;
}

sub hsl_to_hex {
rgb_to_hex(hsl_to_rgb(@_));
}

# Print the colors using ANSI 24-bit color escapes.
sub show_colors {
for my $color (@_) {
my ($r, $g, $b) = hsl_to_rgb(@$color);
my $ansi = sprintf '2;%d;%d;%d',
int($r * 255.0 + 0.5),
int($g * 255.0 + 0.5),
int($b * 255.0 + 0.5);
print "\e[48;${ansi}m ";
}
print "\e[m\n";
}

# The values are kinda made up. But if the hue is on the green side, it gets
# flipped to the red side and the saturation is reduced by 50%.
sub flat_filter {
my ($hue, $sat, $lig) = @_;
my $yellow = 60.0 / 360.0;
my $blue = 240.0 / 360.0;

if ($hue > $yellow && $hue < $blue) {
my $mid_green = ($blue + $yellow) / 2.0;
my $mid_red = $mid_green + 0.5;
$hue = $mid_red - ($hue - $mid_green);
$hue -= 1.0 if $hue >= 1.0;
$sat *= 0.5;
}
($hue, $sat, $lig);
}


# Note that all components are in [0,1] (Except hue, which won't be 1.).
my @yellow = hex_to_hsl('#b58900');
my @orange = hex_to_hsl('#cb4b16');
my @red = hex_to_hsl('#dc322f');
my @magenta = hex_to_hsl('#d33682');
my @violet = hex_to_hsl('#6c71c4');
my @blue = hex_to_hsl('#268bd2');
my @cyan = hex_to_hsl('#2aa198');
my @green = hex_to_hsl('#859900');

my @spectrum = (
\@magenta, \@red, \@orange, \@yellow,
\@green, \@cyan, \@blue, \@violet,
);

# Try to show what the current colors look like, along with what (I think)
# it'd look like for deuteranopia.
print "Solarized regular: ";
show_colors(@spectrum);
print "Solarized flat: ";
show_colors(map { [flat_filter @$_] } @spectrum);

# Divide hue space into 7 regions.
my $step = ($yellow[0] - $blue[0] + 1) / 7;

# Generate the six intermediate hues.
my @hues = ($blue[0]);
for my $i (1..6) {
my $next = $hues[$#hues] + $step;
$next -= 1 if $next >= 1;
push @hues, $next;
}
shift @hues; # Remove blue.

# To flip green/cyan back to the correct side. So pick a line that bisects the
# hue wheel and adjust based off that. Note that both of these are the same
# line, just different directions.
my $yellow = 60.0 / 360.0;
my $blue = 240.0 / 360.0;
my $green_mid = ($yellow + $blue) / 2.0;
my $red_mid = $green_mid + 0.5;

my @ordered = (\@violet, \@cyan, \@magenta, \@green, \@red, \@orange);

# Update the hues of intermediate colors. Note that this produces identical
# results for HSV because they define hue the same.
for my $i (0..$#ordered) {
my $hue = $ordered[$i][0];
if ($hue > $yellow && $hue < $blue) {
# green side
$hue = $green_mid - ($hues[$i] - $red_mid);
$hue -= 1.0 if $hue >= 1.0;
} else {
# red side
$hue = $hues[$i];
}
$ordered[$i][0] = $hue;
}

print "Solarized-RG regular: ";
show_colors(@spectrum);
print "Solarized-RG flat: ";
show_colors(map { [flat_filter @$_] } @spectrum);

print <<END;

SOLARIZED HEX
--------- -------
yellow @{[hsl_to_hex(@yellow )]}
orange @{[hsl_to_hex(@orange )]}
red @{[hsl_to_hex(@red )]}
magenta @{[hsl_to_hex(@magenta)]}
violet @{[hsl_to_hex(@violet )]}
blue @{[hsl_to_hex(@blue )]}
cyan @{[hsl_to_hex(@cyan )]}
green @{[hsl_to_hex(@green )]}
END