Skip to content

Commit

Permalink
Script to make palette for R-G-colorblindness
Browse files Browse the repository at this point in the history
This produces an adjusted Solarized palette that evenly spaces hues
between blue & yellow, keeping everything else the same. It gives the
highest possible contrast for Deuteranopia.

SOLARIZED HEX
--------- -------
yellow    #b58900
orange    #cb4916
red       #dc2f51
magenta   #bb36d3
violet    #6c76c4
blue      #268bd2
cyan      #2aa189
green     #189900
  • Loading branch information
markuspeloquin committed Dec 12, 2019
1 parent 62f656a commit e54a9eb
Showing 1 changed file with 185 additions and 0 deletions.
185 changes: 185 additions & 0 deletions utils/generate-redgreen
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/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(@_));
}



# 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');

# 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 $red_side = ($blue[0] + $yellow[0] + 1) / 2;
my $green_side = $red_side - 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[0] && $hue < $blue[0]) {
# green side
$hue = $green_side - ($hues[$i] - $red_side);
} else {
# red side
$hue = $hues[$i];
}
$ordered[$i][0] = $hue;
}

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

0 comments on commit e54a9eb

Please sign in to comment.