#!/usr/bin/perl -w
use strict;
use lib '.';
sub pi() { 4 * atan2(1, 1) }
my $used = {};
sub sift {
for (1..50) {
my $c = randomColor();
printColor($c, 1);
my($match, $s);
do {
$s = randomColor();
($match) = colorsAreSimilar($c, $s);
} until $match;
printColor($s, 1);
print "
\n";
}
}
sub group {
for (1..40) {
my $c = randomColor();
printColor($c, 1);
my @colours = ($c);
attempt: for (1..10) {
my $s = randomColor();
foreach (@colours) {
my($match) = colorsAreSimilar($_, $s);
next attempt if $match;
}
printColor($s, 1);
push(@colours, $s);
}
print "
\n";
}
}
sub compare {
my $tests = [[[68,208,59], [216,251,177]], # DIFFERENT
[[255,0,0], [0,75,0]],
[[176,16,0], [39,9,197]],
[[0,51,242], [172,26,255]],
[[114,12,182], [157,0,10]],
[[6,36,21], [88,31,0]],
[[159,89,1], [0,131,0]],
[[221,179,111], [125,212,130]],
[[85,85,85], [0,0,0]],
[[154,117,252], [253,79,185]],
[[199,3,247], [110,16,252]],
[[136,2,8], [16,11,11]],
[[42,34,160], [0,4,13]],
[[135,12,80], [44,47,99]],
[[47,31,24], [143,3,66]],
[[111,250,44], [228,230,10]], #a
[[162,252,157], [215,236,89]], #b
[[228,230,10], [111,250,44]], #a again, backwards
[[215,236,89], [162,252,157]], #b again, backwards
[[64,32,251], [160,72,228]],
[[50,214,70], [226,238,190]],
[[163,16,39], [38,0,7]],
[[77,0,231], [190,43,237]],
[[172,206,0], [255,255,239]],
[[174,173,219], [227,253,205]],
[[226,213,221], [244,134,201]],
[], # SAME
[[11,246,168], [79,243,160]],
[[193,211,194], [200,240,250]],
[[0,75,242], [9,19,222]],
[[1,39,0], [16,8,46]],
[[6,214,120], [60,248,135]],
[[203,249,6], [249,251,147], [240,221,45]],
[[64,32,251], [128,79,253]],
[[95,6,220], [160,72,228]],
[[118,216,147], [150,184,88]], # hard to say
[[0,125,0], [53,79,41]], # probably best to be the same
[[227,255,157], [158,234,52]],
[[75,154,229], [75,164,170], [1,184,206]],
[[224,23,21], [240,77,18], [186,35,36]],
[[186,41,198], [218,50,254], [198,74,178]],
[[254,7,207], [218,50,254]],
[[0,18,196], [87,1,251]],
[[255,115,204], [199,98,175], [236,145,254], [255,66,230], [233,47,155]],
[[199,98,175], [202,1,235], [236,145,254], [255,66,230], [233,47,155]],
[[0,119,23], [85,98,70]], # definitely the same
[[95,6,220], [128,79,253]],
[[64,64,64], [128,128,128]],
[[2,255,229], [139,248,217]],
[[230,223,254], [191,214,214]],
[[36,58,173], [13,1,224], [34,0,154], [4,5,93]],
[[182,0,1], [218,26,2], [202,0,48]],
[[2,254,113], [0,253,5]],
[[54,22,41], [32,24,0], [3,0,10]],
[[180,0,251], [159,47,252]],
[[86,12,92], [102,23,129]]];
foreach my $test (@$tests) {
foreach my $color (@$test) {
$color = {
'red' => $color->[0],
'green' => $color->[1],
'blue' => $color->[2],
'reason' => '',
'source' => 'T',
};
convertsRGBtoHCY($color);
}
}
my $fails = 0;
my $wantSame = 0;
print 'tests that should all be different
';
foreach my $test (@$tests) {
if (not @$test) {
die if $wantSame;
print 'tests that should all be the same
';
$wantSame = 1;
next;
}
print "";
use Data::Dumper;
print Dumper($test);
print "
";
my $master = shift @$test;
printColor($master, 1, undef, 0);
foreach (@$test) {
my(@results) = colorsAreSimilar($master, $_);
printColor($_, @results, 1);
if ($results[0] != $wantSame) {
$fails += 1;
print "FAIL";
}
}
print "
\n";
}
warn "$fails failures\n";
print "$fails failures";
}
sub randomColor {
my $colours = [
#[1,39,0], [16,8,46], [255,255,255],
#[64,32,251], [128,79,253], [68,208,59], [216,251,177], [95,6,220],
#[160,72,228], [227,255,157], [158,234,52], [255,0,0], [0,75,0],
#[85,85,85], [0,0,0],
[randcomp(), randcomp(), randcomp()]
];
my $n = int rand @$colours;
my $colour = $colours->[$n];
my $c = {
'red' => $colour->[0],
'green' => $colour->[1],
'blue' => $colour->[2],
'source' => $n == $#$colours ? '???' : $n,
'reason' => '*',
};
convertsRGBtoHCY($c);
return $c;
}
sub printColor {
my($c, $left, $data, $printData) = @_;
print "
$data
" if $printData;
printf "%s
\n", $left ? 'left' : 'right', $c->{red}, $c->{green}, $c->{blue}, $c->{source} . ' ' . $c->{reason};
}
my $maxH = 160;
sub colorsAreSimilar {
my($c, $s) = @_;
my $reqDRGB = 90;
my $reqDYdiff = 70;
my $reqDMS = 100;
my $reqGrayDC = 50;
my $reqGrayMC = 35;
my $reqGrayDMDiff = 150;
my $reqGrayDMSame = 100;
my $reqContrastDC = 90;
my $reqContrastDM = 40;
my $reqMinC = 20;
my $reqDC = 90;
my $reqDRGBDYCombined = 75;
my $reqDHreallySame = 2;
my $reqDCDY = 170;
my $reqDHsame = 6;
my $DRGB = abs($c->{red}-$s->{red}) + abs($c->{green}-$s->{green}) + abs($c->{blue}-$s->{blue});
my $DH = abs($s->{H}-$c->{H});
if ($DH > $maxH/2) {
$DH = $maxH - $DH;
}
my $DC = abs($s->{C}-$c->{C});
my $MC = ($s->{C}+$c->{C})/2;
my $DM = abs($s->{M}-$c->{M});
my $DMS = abs($s->{M}**2-$c->{M}**2)/255;
my $MM = ($s->{M}+$c->{M})/2;
my $DY = abs($s->{"Y'"}-$c->{"Y'"});
my $DRGBDYCombined = $DRGB + $DY;
my $DCDY = $DC + $DY;
my $minC = $c->{C} < $s->{C} ? $c->{C} : $s->{C};
my $output = <$reqDMS diff)
DRGBDY: $DRGBDYCombined (<$reqDRGBDYCombined same)
DC:$DC<$reqGrayDC and MC:$MC<$reqGrayMC and DM:$DM>$reqGrayDMDiff (different grays MCG)
DC:$DC<$reqGrayDC and MC:$MC<$reqGrayMC and DM:$DM<$reqGrayDMSame (same grays MCG)
DC:$DC>$reqContrastDC and and DM:$DM>$reqContrastDM (different contrast 1 MCC)
minC:$minC<$reqMinC and DC:$DC>$reqDC (bright contrast - diff - BC)
DH:$DH (≤$reqDHreallySame same)
DCDY:$DCDY>$reqDCDY (diff) (CY)
DH:$DH (≤$reqDHsame same)
END
if ($DM < 128 and $MM < 128) {
$DH *= ($MM/128)**2;
}
$output .= "new DH: $DH after scaling for MM $MM (0..128 goes down but only if DM $DM is less than 128)\n";
$s->{reason} = "";
$used->{$s->{reason}} += 1;
if ($DRGB < $reqDRGB) {
$s->{reason} = "<RGB";
$used->{$s->{reason}} += 1;
return 1, $output;
}
if ($DMS > $reqDMS) {
$s->{reason} = ">DMS";
$used->{$s->{reason}} += 1;
return 0, $output;
}
if ($DY > $reqDYdiff) {
$s->{reason} = ">DY";
$used->{$s->{reason}} += 1;
return 0, $output;
}
if ($DRGBDYCombined < $reqDRGBDYCombined) {
$s->{reason} = "<RY";
$used->{$s->{reason}} += 1;
return 1, $output;
}
if ($DC < $reqGrayDC and $DM < $reqGrayDMSame and $MC < $reqGrayMC) {
$s->{reason} = "<MCG";
$used->{$s->{reason}} += 1;
return 1, $output;
}
# if ($DC < $reqGrayDC and $DM > $reqGrayDMDiff and $MC < $reqGrayMC) {
# $s->{reason} = ">MCG";
# $used->{$s->{reason}} += 1;
# return 0, $output;
# }
if ($DC > $reqContrastDC and $DM > $reqContrastDM) {
$s->{reason} = ">MCC";
$used->{$s->{reason}} += 1;
return 0, $output;
}
if ($minC < $reqMinC and $DC > $reqDC) {
$s->{reason} = "minC";
$used->{$s->{reason}} += 1;
return 0, $output;
}
if ($DH <= $reqDHreallySame) {
$s->{reason} = "<DH!";
$used->{$s->{reason}} += 1;
return 1, $output;
}
if ($DCDY > $reqDCDY) {
$s->{reason} = ">CY";
$used->{$s->{reason}} += 1;
return 0, $output;
}
if ($DH <= $reqDHsame) {
$s->{reason} = "<DH";
$used->{$s->{reason}} += 1;
return 1, $output;
}
$s->{reason} = "?";
$used->{$s->{reason}} += 1;
return 0, $output;
}
sub randcomp {
return (cos(pi*rand(1))+1)*128;
}
sub convertsRGBtoHCY {
my($c) = @_;
my $R = $c->{red};
my $G = $c->{green};
my $B = $c->{blue};
my $M = $R > $G ? $R > $B ? $R : $B > $G ? $B : $G : $B > $G ? $B : $G;
my $m = $R < $G ? $R < $B ? $R : $B < $G ? $B : $G : $B < $G ? $B : $G;
my $C = $M - $m;
my $H;
if ($C == 0) {
$H = 0;
} else {
if ($M == $R) {
$H = 60 * ((($G-$B)/$C) + 0);
} elsif ($M == $G) {
$H = 60 * ((($B-$R)/$C) + 2);
} else { # ($M == $B)
$H = 60 * ((($R-$G)/$C) + 4);
}
}
$H += 360 if $H < 0;
die if $H < 0;
die if $H > 360;
$c->{'H-original'} = $H;
$c->{C} = $C;
$c->{M} = $M;
$c->{H} = adjustHue($H);
# Luma (Y') with sRGB (Rec. 709) primaries:
# http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness
$c->{"Y'"} = 0.21*$R + 0.72*$G + 0.07*$B;
}
my $hueClipInput = [];
my $hueClipOutput = [];
my $maxAdjustedHue = 360;
sub initHueAdjuster {
my $hueClipBoundaries = [[0, 15], [55, 70], [75, 85], [90, 150], [160, 170], [185, 205], [230, 250], [255, 265], [270, 280], [290, 325]];
die unless $hueClipBoundaries->[0]->[0] == 0; # algorithm would have to be different if not true
my $currentMax = 0;
my $lastMax = 0;
foreach my $subrange (@$hueClipBoundaries) {
push(@$hueClipInput, $subrange->[0]);
push(@$hueClipInput, $subrange->[1]);
$maxAdjustedHue -= $subrange->[1] - $subrange->[0];
$currentMax += $subrange->[0] - $lastMax;
push(@$hueClipOutput, $currentMax);
$lastMax = $subrange->[1];
}
}
initHueAdjuster();
sub adjustHue {
my($hue) = @_;
my $min = 0;
my $max = scalar @$hueClipInput;
my $pos = int($max/2);
while ($pos > $min) {
if ($hue < $hueClipInput->[$pos]) {
$max = $pos;
} else {
$min = $pos;
}
$pos = $min + int(($max - $min) / 2);
}
if ($pos % 2 == 0) {
$hue = $hueClipOutput->[$pos/2];
} else {
$hue = ($hue - $hueClipInput->[$pos]) + $hueClipOutput->[int($pos/2)];
}
return $hue;
}
print <
Test
TOP
print 'colours that are the same as each other
';
sift();
print 'colours that are different from each other
';
group();
print 'test patterns
';
compare();
print 'results
';
print Dumper($used);