Skip to content

Instantly share code, notes, and snippets.

@SEVEZ
Created August 29, 2015 11:23
Show Gist options
  • Save SEVEZ/3dfa1f7807dab5379fe3 to your computer and use it in GitHub Desktop.
Save SEVEZ/3dfa1f7807dab5379fe3 to your computer and use it in GitHub Desktop.
Shift RGB colors along RYB artistic color wheel
// Preview online
// http://www.deathbysoftware.com/colors/index.html
function rgb2ryb ( iRed, iGreen, iBlue )
{
// Remove the white from the color
var iWhite = Math.min( iRed, iGreen, iBlue );
iRed -= iWhite;
iGreen -= iWhite;
iBlue -= iWhite;
var iMaxGreen = Math.max( iRed, iGreen, iBlue );
// Get the yellow out of the red+green
var iYellow = Math.min( iRed, iGreen );
iRed -= iYellow;
iGreen -= iYellow;
// If this unfortunate conversion combines blue and green, then cut each in half to
// preserve the value's maximum range.
if (iBlue > 0 && iGreen > 0)
{
iBlue /= 2;
iGreen /= 2;
}
// Redistribute the remaining green.
iYellow += iGreen;
iBlue += iGreen;
// Normalize to values.
var iMaxYellow = Math.max( iRed, iYellow, iBlue );
if ( iMaxYellow > 0 )
{
var iN = iMaxGreen / iMaxYellow;
iRed *= iN;
iYellow *= iN;
iBlue *= iN;
}
// Add the white back in.
iRed += iWhite;
iYellow += iWhite;
iBlue += iWhite;
return [ iRed, iYellow, iBlue ];
}
function ryb2rgb ( iRed, iYellow, iBlue )
{
// Remove the whiteness from the color.
var iWhite = Math.min( iRed, iYellow, iBlue );
iRed -= iWhite;
iYellow -= iWhite;
iBlue -= iWhite;
var iMaxYellow = Math.max( iRed, iYellow, iBlue );
// Get the green out of the yellow and blue
var iGreen = Math.min( iYellow, iBlue );
iYellow -= iGreen;
iBlue -= iGreen;
if ( iBlue > 0 && iGreen > 0 )
{
iBlue *= 2.0;
iGreen *= 2.0;
}
// Redistribute the remaining yellow.
iRed += iYellow;
iGreen += iYellow;
// Normalize to values.
var iMaxGreen = Math.max( iRed, iGreen, iBlue );
if ( iMaxGreen > 0 )
{
var iN = iMaxYellow / iMaxGreen;
iRed *= iN;
iGreen *= iN;
iBlue *= iN;
}
// Add the white back in.
iRed += iWhite;
iGreen += iWhite;
iBlue += iWhite;
// Save the RGB
return [ iRed, iGreen, iBlue ];
}
// RGB2HSV and HSV2RGB are based on Color Match Remix [http://color.twysted.net/]
// which is based on or copied from ColorMatch 5K [http://colormatch.dk/]
function rgbToHsl( r, g, b )
{
var h, s, l
max = max3( r, g, b )
dif = max - min3( r, g, b )
s = ( max == 0.0 ) ? 0 : ( dif / max )
if ( s == 0 ) h = 0
else if ( r == max ) h = 60.0 * ( g - b ) / dif
else if ( g == max ) h = 120.0 + 60.0 * ( b - r ) / dif
else if ( b == max ) h = 240.0 + 60.0 * ( r - g ) / dif
if ( h < 0.0 ) h += 360.0
l = max
h /= 360.0
return [ h, s, l ]
}
function hslToRgb( h, s, l )
{
var r, g, b
if ( s == 0 ) return [ l, l, l ]
else
{
var i, f, p ,q ,t
h *= 6
i = Math.floor( h )
f = h - i
p = l * ( 1 - s )
q = l * ( 1 - s * f )
t = l * ( 1 - s * ( 1 - f ) )
switch( i )
{
case 0: return [ l, t, p ]
case 1: return [ q, l, p ]
case 2: return [ p, l, t ]
case 3: return [ p, q, l ]
case 4: return [ t, p, l ]
default: return [ l, p, q ]
}
}
}
//min max via Hairgami_Master (see comments)
function min3(a,b,c) { return ( a < b ) ? ( ( a < c ) ? a : c ) : ( ( b < c ) ? b : c ) }
function max3(a,b,c) { return ( a > b ) ? ( ( a > c ) ? a : c ) : ( ( b > c ) ? b : c ) }
//Adding HueShift via Jacob (see comments)
function HueShift( h, s ) { h += s; while ( h >= 360.0 ) h -= 360.0; while ( h < 0.0 ) h += 360.0; return h; }
function ryb2rgb2 ( R, Y, B )
{
R = R * R * ( 3 - 2 * R )
Y = Y * Y * ( 3 - 2 * Y )
B = B * B * ( 3 - 2 * B )
return [1.0 + B * ( R * (0.337 + Y * -0.137) + (-0.837 + Y * -0.163) ),
1.0 + B * ( -0.627 + Y * 0.287) + R * (-1.0 + Y * (0.5 + B * -0.693) - B * (-0.627) ),
1.0 + B * (-0.4 + Y * 0.6) - Y + R * ( -1.0 + B * (0.9 + Y * -1.1) + Y )]
}
/*
function rotate_ryb( h, angle )
{
// Returns a color rotated on the artistic RYB color wheel.
// An artistic color wheel has slightly different opposites
// (e.g. purple-yellow instead of purple-lime).
// It is mathematically incorrect but generally assumed
// to provide better complementary colors.
// http://en.wikipedia.org/wiki/RYB_color_model
h *= 360
angle %= 360
// log += h + "\n"
// log += angle + "\n"
// Approximation of Itten's RYB color wheel.
// In HSB, colors hues range from 0-360.
// However, on the artistic color wheel these are not evenly distributed.
// The second tuple value contains the actual distribution.
wheel =
[
[ 0, 0],
[ 15, 8],
[ 30, 17],
[ 45, 26],
[ 60, 34],
[ 75, 41],
[ 90, 48],
[105, 54],
[120, 60],
[135, 81],
[150, 103],
[165, 123],
[180, 138],
[195, 155],
[210, 171],
[225, 187],
[240, 204],
[255, 219],
[270, 234],
[285, 251],
[300, 267],
[315, 282],
[330, 298],
[345, 329],
[360, 0 ]
]
// Given a hue, find out under what angle it is
// located on the artistic color wheel.
var a, x0, x1, y0, y1
for ( var i = 0; i < wheel.length - 1; i++ )
{
x0 = wheel[i][0]
y0 = wheel[i][1]
x1 = wheel[i+1][0]
y1 = wheel[i+1][1]
//WScript.Echo( y0 + " <= " + h + " <= " + y1 + "?\n" )
if ( y1 < y0 ) y1 += 360
if ( ( y0 <= h ) && ( h <= y1 ) )
{
a = 1.0 * x0 + ( x1 - x0 ) * ( h - y0 ) / ( y1 - y0 ) // lerp
break
}
}
a = ( a + angle ) % 360
// For the given angle, find out what hue is
// located there on the artistic color wheel.
for ( var i = 0; i < wheel.length-1; i++ )
{
x0 = wheel[i][0]
y0 = wheel[i][1]
x1 = wheel[i+1][0]
y1 = wheel[i+1][1]
if ( y1 < y0 ) y1 += 360
if ( ( x0 <= a ) && ( a <= x1 ) )
{
h = 1.0 * y0 + ( y1 - y0 ) * ( a - x0 ) / ( x1 - x0 ) // lerp
break
}
}
h %= 360
return h/360
}
*/
//----------------------- TESTING
var c = [ 0, 0, 1 ]
c = rgb2ryb( c[0], c[1], c[2] )
c = rgbToHsl( c[0], c[1], c[2] )
c[0] = HueShift( Math.round( c[0] * 360 ), 0 ) / 360.0
c = hslToRgb( c[0], c[1], c[2] )
c = ryb2rgb2( c[0], c[1], c[2] )
WScript.Echo( "complementary = " + [ Math.round( c[0] * 255 ), Math.round( c[1] * 255 ), Math.round( c[2] * 255 ) ] )
@aleclarson
Copy link

Thank you for this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment