Commit 088ce803 authored by Jakob Stierhof's avatar Jakob Stierhof

Change color interface

This is a major change breaking backwards comp: Some functions are renamed to have
a more consistend naming scheme.

The color conversion functions, like hsl2rgb, have this form and should be
read as from space to space (here HSL to RGB). RGB colors are always a
triplet of 8 bit values. All other spaces are a triplet of Double_Type
in the range (0-1). Another useful converter is rgb2hex, here hex represents
either a 24 bit value representing a RGB tuple, or a string representing this
value in hex notation (e.g., 0xff0000 equals "ff0000", and is full red).

There were a number of functions that produced a RGB color (as hex value).
This functions are now all renamed to color_<something>, e.g., color_wavelength
calculates the hex value that corresponds to a given wavelength. In this sence
'color' should always be understood as RGB hex value (notice, 'hex' should be
understood either as hex value (that is, 'color') or as the string (see above)).

The biggest changes concerns the addition of a set of functions which are all
interfaced via 'get_color_palette'. This function allows to choose a palette name
(see 'get_color_palette_names') and how many colors the palette should contain.
See the help of the various palettes for more information.
Via this interface it is also very easy to generate colormaps to be used with
plotting functions.

Example: cmap = get_color_palette("hsluv", 256); % parameters to the generator
% function are passed via qualifiers. Not all palettes are generated, rather
% a static set of specific colors.

Further, I moved the xfig_mix_color to 'plot' as it is an extension of xfig (something
to be fixed in the future). I also deleted the X11 rgb.txt reader functions as this file
is no longer provided on most systems. If we want to provide a list of color names, maybe
we should pack a file with the isisscripts?
parent 1dbaac3f
......@@ -2267,7 +2267,7 @@ define atime_sim_residuals()
else
{
variable res = (data.value - model.value)*86400 / pulseperiod(data.value, teph, torb);
pl.plot(data.value - tpl, res; color = rgb2hex(v<0 ? cs : 0, 0, v<0 ? 0 : cs; str));
pl.plot(data.value - tpl, res; color = "#"+rgb2hex(v<0 ? cs : 0, 0, v<0 ? 0 : cs; string));
pl.xylabel(0, .8*mres*v/n, sprintf("%.2e",value*mm*v/n), 0, 0; color = rgb2hex(v<0 ? cs : 0, 0, v<0 ? 0 : cs; str));
}
}
......
......@@ -2,18 +2,6 @@ require( "xfig" );
require( "gcontour" );
require( "png" );
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEFINITION OF DEFAULT COLOR MAP FOR POLAR %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% yellow - red - blue
png_add_colormap("iceandfire",(
reverse([int([255: 255:#85]), int([255: 0 :#85]), int([0 : 255:#85]) ]) * 0x10000 + % red
reverse([int([255: 0 :#85]), int([0 : 0 :#85]), int([0 : 255:#85]) ]) * 0x100 + % green
reverse([int([0 : 0 :#85]), int([0 : 255:#85]), int([255: 255:#85]) ]) % blue
));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEFINITION OF WCS SYSTEM FOR AXIS SCALING %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
......
define get_diverging(t)
{
%% cold hot scheme
variable R = 0.237 - 2.13*t + 26.92*t*t - 65.5*t*t*t + 63.5*t^4 - 22.36*t^5 ;
variable G = ((0.572 + 1.524*t -1.811*t*t)/(1. - 0.291*t + 0.1574*t*t))^2 ;
variable B = 1./(1.579 - 4.03*t + 12.92*t*t - 31.4*t*t*t + 48.6*t^4 -23.36*t^5) ;
return rgb2hex(R,G,B ; str) ;
}
private define get_rainbow (t)
{
%% rainbow scheme
variable R = (0.472 - 0.567*t + 4.05*t*t)/(1 + 8.72*t - 19.17*t*t + 14.1*t*t*t) ;
variable G = (0.108932 - 1.22635*t + 27.284*t*t - 98.577*t*t*t + 163.3*t^4 - 131.395*t^5 + 40.634*t^6) ;
variable B = 1./(1.97 + 3.54*t - 68.5*t*t + 243*t*t*t - 297*t^4 + 125*t^5) ;
return rgb2hex(R,G,B ; str) ;
}
%%%%%%%%%%%%%%%%%%%%%%%
define get_sron_colors()
%%%%%%%%%%%%%%%%%%%%%%%
%!%+
%\function{get_sron_colors}
%\synopsis{get colors for different data, with good color blind readability}
%\usage{String_Type colors = get_sron_colors(Integer_Type number)}
%\altusage{Struct_Type colors = get_sron_colors(Integer_Type number ; names)}
%\altusage{String_Type color = get_sron_colors(Double_Type value ; cont)}
%
%\description
% The color schemes in this function are based on SRON Technical
% note SRON/EPS/TN/09-002 (Issue 2.2 from 2012-12-29): "Colour
% Schemes" prepared by Paul Tol. Three color schemes are avaible:
% - a quantitative (default): up to 12 distinct colors, not sorted
% (based on Fig. 3)
% - a diverging continuous one, running from blue to red (Fig. 8)
% - a continouos rainbow one (Fig. 13)
%
% Selection of the color schemes is done with the qualifier "type"
% ("qualitative" (default), "diverging" or "rainbow")
%
% The function returns the hex-code strings for each color.
% For the quantitative scheme it is also possible to return a
% structure with colornames and hex value (qualifier "names").
%
% For the continouous schemes it is also possible to get the color
% corresponding to a (float) value between 0 and 1 (qualifier
% "cont").
%
%\qualifiers{
%\qualifier{type}{=String_Type select color scheme ("qualitative" (default),"diverging", or "rainbow"}
%\qualifier{names}{(Boolean) return color names in structure for qualitative color scheme}
%\qualifier{cont}{(Boolean) change meaning of argument to value between 0 and 1, return corresponding color}
%}
%!%-
{
variable n ;
switch(_NARGS)
{ case 1: n = () ;}
{ help(_function_name()); return; }
variable type = qualifier("type", "qualitative") ;
if (qualifier_exists("cont") == 0 and typeof(n) != Integer_Type)
{
()=printf("Error (get_srong_colors): Non Integer input only allowed with \"cont\" qualifier.\n", type);
return ;
}
if (type == "qualitative")
{
if (qualifier_exists("cont"))
{
()=printf("Error (get_sron_colors): cont qualifier not compatible with selected color scheme (%s).\n", type);
return ;
}
variable cols = [
"#4477AA",
"#CC6677",
"#DDCC77",
"#117733",
"#332288",
"#88CCEE",
"#AA4499",
"#44AA99",
"#999933",
"#882255",
"#661100",
"#6699CC",
"#AA4466",
];
variable names = [
"blue1",
"red1",
"yellow1",
"green1",
"purple1",
"blue2",
"pink1",
"green2",
"green3",
"red2",
"red3",
"blue3",
"red4"
];
variable ndx ;
switch (n)
{ n == 1 : ndx = [0] ; }
{ n == 2 : ndx = [0,1] ; }
{ n == 3 : ndx = [0,2,1] ; }
{ n == 4 : ndx = [0,3,2,1] ; }
{ n == 5 : ndx = [4,5,3,2,1] ; }
{ n == 6 : ndx = [4,5,3,2,1,6] ; }
{ n == 7 : ndx = [4,5,7,3,2,1,6] ; }
{ n == 8 : ndx = [4,5,7,3,8,2,1,6] ; }
{ n == 9 : ndx = [4,5,7,3,8,2,1,9,6] ; }
{ n == 10 : ndx = [4,5,7,3,8,2,10,1,9,6] ; }
{ n == 11 : ndx = [4,11,5,7,3,8,2,10,1,9,6] ; }
{ n == 12 : ndx = [4,11,5,7,3,8,2,10,1,12,9,6] ; }
{
()=printf("Error (get_srong_colors): requested more than 12 colors for qualitative color schme!\n");
return;
}
variable i ;
if (qualifier_exists("names"))
{
variable mystruct = @Struct_Type(names[ndx]) ;
_for i (0, length(ndx)-1, 1)
{
set_struct_field(mystruct, names[ndx[i]], cols[ndx[i]]) ;
}
return mystruct ;
}
return cols[ndx] ;
}
if (type == "diverging" or type == "rainbow")
{
if (qualifier_exists("names"))
{
()=printf("Warning (get_sron_colors): names not available with %s color scheme, \
will return color values only!\n", qualifier("type"));
}
variable colfun ;
if (type == "diverging")
{
colfun = &get_diverging ;
}
if (type == "rainbow")
{
colfun = &get_rainbow ;
}
if (qualifier_exists("cont"))
{
ifnot (min(n) >= 0 and max(n) <= 1)
{
()=printf("Error (get_sron_colors): for continuous colors, argument needs to be between 0 and 1!\n");
return ;
}
return @colfun(n) ;
}
return array_map(String_Type, colfun , [0:1:#n]) ;
}
else
{
()=printf("Error (get_sron_colors): Unknown color scheme type (%s) requested!\n", type);
return ;
}
}
......@@ -106,7 +106,7 @@ variable stx, sty ;
variable xymm = xfgx.get_world() ;
variable starcol = qualifier("star_color",rgb2hex(0.4, 0.6,1.0 ; str) );
variable starcol = qualifier("star_color", "#"+rgb2hex(0.4, 0.6,1.0 ; string) );
xfgx.shade_region(stx,sty ; color=starcol, depth = 25) ;
......@@ -136,7 +136,7 @@ if(qualifier_exists("phaselines")) {
variable theta_cgrid = 2.*atan( sqrt( (1.+ecc)/(1.-ecc) )*tan(eccanom_cgrid/2.) ); % true anomaly from eccentric anomaly
variable xlab,ylab ;
variable labscal = qualifier("label_pos",0.85) ;
variable darkgray = rgb2hex(0.4,0.4,0.4 ; str) ; % used for the phase labels
variable darkgray = "#" + rgb2hex(0.4,0.4,0.4 ; string) ; % used for the phase labels
variable phaswitch = qualifier("label_phase_switch", 0) ;
ifnot (0<=phaswitch<1) {
......
......@@ -20,8 +20,8 @@ define xfig_mix_colors(color1,color2,fraction) {
% name qualifier is given (recommended).
%\seealso{mix_rgb_colors}
%!%-
variable rgb=mix_rgb_colors(xfig_lookup_color_rgb(color1),
xfig_lookup_color_rgb(color2),fraction);
variable rgb=color_mix(xfig_lookup_color_rgb(color1),
xfig_lookup_color_rgb(color2),fraction);
variable colname;
if (qualifier_exists("name")) {
......
......@@ -211,7 +211,7 @@ define xfig_plot_params()
% Colorize xticlabels, IF chi2 plots are plotted
if( qualifier_exists("steppar") ){
xtics = "\\color[rgb]{"+rgb2r_g_b(ticmap;norm,str=",")+"}{"+xtics+"}";
xtics = "\\color[rgb]{"+array_map(String_Type, &sprintf, "%0.3f,%03f,%03f", hex2rgb(ticmap;float))+"}{"+xtics+"}";
}
% Set x/y plotting ranges
......@@ -357,7 +357,7 @@ define xfig_plot_params()
% the color of the individual chi2 landscapes matches the color of the
% ticlabels in the main plot
xs.plot( spinfo[i].chi2, spinfo[i].pval ;
color = rgb2hex( rgb2r_g_b(ticmap[i];norm) ; str ),
color = sprintf("#%06x", ticmap[i]),
depth = 25
);
}
......
......@@ -203,7 +203,7 @@ define xfig_plot_spectrum()
% Color/style options
% These are set up so that if the user provides a single string (rather than
% an array) the same color/style is used for all spectra
variable colors = qualifier("colors",get_sron_colors(numInst));
variable colors = qualifier("colors", get_color_palette("sron-rainbow", numInst; repeat));
if(typeof(colors) == String_Type or typeof(colors) == Integer_Type)
colors = xfps_create_string_array(numInst,colors);
variable mcolors = qualifier("mcolors",colors);
......
require("pcre");
define X11color2rgb(searchname) {
%!%+
%\function{X11color2rgb}
%\synopsis{return rgb value for x11's rgb.txt}
%\usage{variable rgb=X11color2rgb(colorname)}
%\qualifiers{
%\qualifier{rgbfile}{File defining the colors, default: /usr/share/X11/rgb.txt}
%}
%\description
%This function returns the integer specifying the rgb code of a color
%named in the file rgb.txt accompanying the X11 distribution.
%\seealso{xfig_lookup_w3c_color}
%!%-
variable rr,gg,bb,name;
variable rgbfile=qualifier("rgbfile","/usr/share/X11/rgb.txt");
variable exists=stat_file(rgbfile);
if (exists==NULL) {
throw OpenError,"X11 rgb file "+rgbfile+" does not exist";
}
variable lin;
variable pcre=pcre_compile("(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*([ a-zA-Z0-9]*)");
variable fp=fopen(rgbfile,"r");
% skip 1st line
()=fgets(&lin,fp);
while (fgets(&lin,fp)>0) {
lin=substr(lin,1,strlen(lin)-1);
variable res=pcre_exec(pcre,lin);
name=pcre_nth_substr(pcre,lin,4);
if (name==searchname) {
rr=integer(pcre_nth_substr(pcre,lin,1));
gg=integer(pcre_nth_substr(pcre,lin,2));
bb=integer(pcre_nth_substr(pcre,lin,3));
()=fclose(fp);
return((rr<<16)|(gg<<8)|bb);
}
}
()=fclose(fp);
throw RunTimeError,"No color named "+searchname+" exists";
% not that this is ever reached
return -1;
}
define blackbody_to_rgb()
%!%+
%\function{blackbody_to_rgb}
%\synopsis{computes RGB colors of hot blackbody radiation.}
%\usage{Integer_Type blackbody_to_rgb(Double_Type T)}
%\description
% \code{T} is the temperature of the blackbody in Kelvin.
% The return value is its 24 bit RGB value.
%
% It is computed after the code "RGB VALUES FOR HOT OBJECTS"
% by Dan Bruton (astro@sfasu.edu), which can be found at
% http://www.physics.sfasu.edu/astro/color.html.
%!%-
{
variable T;
switch(_NARGS)
{ case 1: T = (); }
{ help(_function_name()); return; }
if(typeof(T)==Array_Type)
return array_map(Integer_Type, &blackbody_to_rgb, T);
variable wavelength = [380:780:5]; % wavelength in nm
% integration by trapezoid method
variable weight = wavelength*0 + 1.; weight[[0,-1]] = 0.5; % properly weight end points
% generate a black body spectrum
variable dis = 3.74183e-16/wavelength^5/(exp(1240./8.617e-5/(wavelength*T))-1);
% CIE Color Matching Functions (x_bar,y_bar,z_bar)
% for wavelenghts in 5 nm increments from 380 nm to 780 nm.
% simple integration over bands
variable x = sum( weight*dis*[0.0014, 0.0022, 0.0042, 0.0076, 0.0143, 0.0232, 0.0435, 0.0776, 0.1344,
0.2148, 0.2839, 0.3285, 0.3483, 0.3481, 0.3362, 0.3187, 0.2908, 0.2511,
0.1954, 0.1421, 0.0956, 0.0580, 0.0320, 0.0147, 0.0049, 0.0024, 0.0093,
0.0291, 0.0633, 0.1096, 0.1655, 0.2257, 0.2904, 0.3597, 0.4334, 0.5121,
0.5945, 0.6784, 0.7621, 0.8425, 0.9163, 0.9786, 1.0263, 1.0567, 1.0622,
1.0456, 1.0026, 0.9384, 0.8544, 0.7514, 0.6424, 0.5419, 0.4479, 0.3608,
0.2835, 0.2187, 0.1649, 0.1212, 0.0874, 0.0636, 0.0468, 0.0329, 0.0227,
0.0158, 0.0114, 0.0081, 0.0058, 0.0041, 0.0029, 0.0020, 0.0014, 0.0010,
0.0007, 0.0005, 0.0003, 0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0000] );
variable y = sum( weight*dis*[0.0000, 0.0001, 0.0001, 0.0002, 0.0004, 0.0006, 0.0012, 0.0022, 0.0040,
0.0073, 0.0116, 0.0168, 0.0230, 0.0298, 0.0380, 0.0480, 0.0600, 0.0739,
0.0910, 0.1126, 0.1390, 0.1693, 0.2080, 0.2586, 0.3230, 0.4073, 0.5030,
0.6082, 0.7100, 0.7932, 0.8620, 0.9149, 0.9540, 0.9803, 0.9950, 1.0000,
0.9950, 0.9786, 0.9520, 0.9154, 0.8700, 0.8163, 0.7570, 0.6949, 0.6310,
0.5668, 0.5030, 0.4412, 0.3810, 0.3210, 0.2650, 0.2170, 0.1750, 0.1382,
0.1070, 0.0816, 0.0610, 0.0446, 0.0320, 0.0232, 0.0170, 0.0119, 0.0082,
0.0057, 0.0041, 0.0029, 0.0021, 0.0015, 0.0010, 0.0007, 0.0005, 0.0004,
0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000] );
variable z = sum( weight*dis*[0.0065, 0.0105, 0.0201, 0.0362, 0.0679, 0.1102, 0.2074, 0.3713, 0.6456,
1.0391, 1.3856, 1.6230, 1.7471, 1.7826, 1.7721, 1.7441, 1.6692, 1.5281,
1.2876, 1.0419, 0.8130, 0.6162, 0.4652, 0.3533, 0.2720, 0.2123, 0.1582,
0.1117, 0.0782, 0.0573, 0.0422, 0.0298, 0.0203, 0.0134, 0.0087, 0.0057,
0.0039, 0.0027, 0.0021, 0.0018, 0.0017, 0.0014, 0.0011, 0.0010, 0.0008,
0.0006, 0.0003, 0.0002, 0.0002, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000] );
% re-normalize the color scale
variable mx = max([x, y, z]);
x /= mx;
y /= mx;
z /= mx;
% Chromaticity Coordinates for Red, Green, Blue and White
variable xr=0.64 , yr=0.33 , zr=1-xr-yr;
variable xg=0.29 , yg=0.60 , zg=1-xg-yg;
variable xb=0.15 , yb=0.06 , zb=1-xb-yb;
variable xw=0.3127, yw=0.3291, zw=1-xw-yw;
% convert to RGB
variable denominator = (xr*yg-xg*yr)*zb + (xb*yr-xr*yb)*zg + (xg*yb-xb*yg)*zr;
variable R = _max(0, _min(1, ( (x *yg-xg*y )*zb + (xg*yb-xb*yg)*z + (xb*y -x *yb)*zg )/denominator ));
variable G = _max(0, _min(1, ( (xr*y -x *yr)*zb + (xb*yr-xr*yb)*z + (x *yb-xb*y )*zr )/denominator ));
variable B = _max(0, _min(1, ( (xr*yg-xg*yr)*z + (x *yr-xr*y )*zg + (xg*y -x *yg)*zr )/denominator ));
variable gamma = 0.8;
variable rangeMax = max([1e-10, R, G, B]);
R = typecast(255.999999*(R/rangeMax)^gamma, UChar_Type);
G = typecast(255.999999*(G/rangeMax)^gamma, UChar_Type);
B = typecast(255.999999*(B/rangeMax)^gamma, UChar_Type);
return R*0x010000 + G*0x0100 + B;
}
define color_blackbody (T)
%define blackbody_to_rgb (T)
%!%+
%\function{color_blackbody}
%\synopsis{Computes RGB colors of hot blackbody radiation.}
%\usage{Integer_Type color = color_blackbody(Double_Type T);}
%\description
% \code{T} is the temperature of the blackbody in Kelvin.
% The return value is its 24 bit RGB value.
%
% It is computed after the code "RGB VALUES FOR HOT OBJECTS"
% by Dan Bruton (astro@sfasu.edu), which can be found at
% http://www.physics.sfasu.edu/astro/color.html.
%!%-
{
variable wavelength = [380:780:5]; % wavelength in nm
% integration by trapezoid method
variable weight = wavelength*0 + 1.; weight[[0,-1]] = 0.5; % properly weight end points
% generate a black body spectrum
variable dis, x, y, z;
variable i;
x = Double_Type[length(T)];
y = Double_Type[length(T)];
z = Double_Type[length(T)];
_for i (0, length(T)-1)
{
dis = 3.74183e-16/wavelength^5/(exp(1240./8.617e-5/(wavelength*T[i]))-1);
% CIE Color Matching Functions (x_bar,y_bar,z_bar)
% for wavelenghts in 5 nm increments from 380 nm to 780 nm.
% simple integration over bands
x[i] = sum( weight*dis*[0.0014, 0.0022, 0.0042, 0.0076, 0.0143, 0.0232, 0.0435, 0.0776, 0.1344,
0.2148, 0.2839, 0.3285, 0.3483, 0.3481, 0.3362, 0.3187, 0.2908, 0.2511,
0.1954, 0.1421, 0.0956, 0.0580, 0.0320, 0.0147, 0.0049, 0.0024, 0.0093,
0.0291, 0.0633, 0.1096, 0.1655, 0.2257, 0.2904, 0.3597, 0.4334, 0.5121,
0.5945, 0.6784, 0.7621, 0.8425, 0.9163, 0.9786, 1.0263, 1.0567, 1.0622,
1.0456, 1.0026, 0.9384, 0.8544, 0.7514, 0.6424, 0.5419, 0.4479, 0.3608,
0.2835, 0.2187, 0.1649, 0.1212, 0.0874, 0.0636, 0.0468, 0.0329, 0.0227,
0.0158, 0.0114, 0.0081, 0.0058, 0.0041, 0.0029, 0.0020, 0.0014, 0.0010,
0.0007, 0.0005, 0.0003, 0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0000] );
y[i] = sum( weight*dis*[0.0000, 0.0001, 0.0001, 0.0002, 0.0004, 0.0006, 0.0012, 0.0022, 0.0040,
0.0073, 0.0116, 0.0168, 0.0230, 0.0298, 0.0380, 0.0480, 0.0600, 0.0739,
0.0910, 0.1126, 0.1390, 0.1693, 0.2080, 0.2586, 0.3230, 0.4073, 0.5030,
0.6082, 0.7100, 0.7932, 0.8620, 0.9149, 0.9540, 0.9803, 0.9950, 1.0000,
0.9950, 0.9786, 0.9520, 0.9154, 0.8700, 0.8163, 0.7570, 0.6949, 0.6310,
0.5668, 0.5030, 0.4412, 0.3810, 0.3210, 0.2650, 0.2170, 0.1750, 0.1382,
0.1070, 0.0816, 0.0610, 0.0446, 0.0320, 0.0232, 0.0170, 0.0119, 0.0082,
0.0057, 0.0041, 0.0029, 0.0021, 0.0015, 0.0010, 0.0007, 0.0005, 0.0004,
0.0002, 0.0002, 0.0001, 0.0001, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000] );
z[i] = sum( weight*dis*[0.0065, 0.0105, 0.0201, 0.0362, 0.0679, 0.1102, 0.2074, 0.3713, 0.6456,
1.0391, 1.3856, 1.6230, 1.7471, 1.7826, 1.7721, 1.7441, 1.6692, 1.5281,
1.2876, 1.0419, 0.8130, 0.6162, 0.4652, 0.3533, 0.2720, 0.2123, 0.1582,
0.1117, 0.0782, 0.0573, 0.0422, 0.0298, 0.0203, 0.0134, 0.0087, 0.0057,
0.0039, 0.0027, 0.0021, 0.0018, 0.0017, 0.0014, 0.0011, 0.0010, 0.0008,
0.0006, 0.0003, 0.0002, 0.0002, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000] );
% re-normalize the color scale
variable mx = max([x[i], y[i], z[i]]);
x[i] /= mx;
y[i] /= mx;
z[i] /= mx;
}
% Chromaticity Coordinates for Red, Green, Blue and White
variable xr=0.64 , yr=0.33 , zr=1-xr-yr;
variable xg=0.29 , yg=0.60 , zg=1-xg-yg;
variable xb=0.15 , yb=0.06 , zb=1-xb-yb;
variable xw=0.3127, yw=0.3291, zw=1-xw-yw;
% convert to RGB
variable denominator = (xr*yg-xg*yr)*zb + (xb*yr-xr*yb)*zg + (xg*yb-xb*yg)*zr;
variable r = _max(0, _min(1, ( (x *yg-xg*y )*zb + (xg*yb-xb*yg)*z + (xb*y -x *yb)*zg )/denominator ));
variable g = _max(0, _min(1, ( (xr*y -x *yr)*zb + (xb*yr-xr*yb)*z + (x *yb-xb*y )*zr )/denominator ));
variable b = _max(0, _min(1, ( (xr*yg-xg*yr)*z + (x *yr-xr*y )*zg + (xg*y -x *yg)*zr )/denominator ));
variable gamma = 0.8;
variable rangeMax = _max([1e-10, r, g, b]);
r = (r/rangeMax)^gamma;
g = (g/rangeMax)^gamma;
b = (b/rangeMax)^gamma;
return rgb2hex(r,g,b);
}
define color_complex (z)
%!%+
%\function{color_complex}
%\synopsis{Converts a complex number to a RGB color}
%\usage{Integer_Type color = complex2rgb(Complex_Type z);}
%\description
% arg(\code{z}) determines the hue,
% |\code{z}| determines the lightness:
% 0<=|\code{z}|<=1 is mapped from black to color,
% 1<=|\code{z}|<inf is mapped from color to white.
% The return value is a 24-bit RGB value.
%\qualifiers{
%\qualifier{s[=1.0]}{saturation (from 0 to 1) of the color}
%}
%\seealso{hsl2rgb, png_write}
%!%-
{
variable s = _max(0.0, _min(1.0, qualifier("s", 1.0)));
variable h = (atan2(Imag(z), Real(z))/2/PI + 1.0) mod 1.0;
variable l = abs(z)*0.5;
variable w = where(l>0.5);
l[w] = 1.0-0.25/l[w];
return rgb2hex(hsluv2rgb(h,s,l));
}
define color_desaturate (color)
%!%+
%\function{color_desaturate}
%\synopsis{Weighted color to gray conversion}
%\usage{UInt_Type gray = color_desaturate(UInt_Type[] color);}
%\description
% Converts a RGB color, hex encoded, into a weighted gray scale using:
%
% \code{gray} = (0.3 * R) + (0.59 * G) + (0.11 * B)
%
% The 8 bit gray value(s) are return as UInt_Type[] in the interval [0,255].
%
%\seealso{rgb2hsl, hex2rgb}
%!%-
{
variable r, g, b;
(r,g,b) = hex2rgb(color);
variable gray = (0.3 * r/255.) + (0.59 * g/255.) + (0.11 * b/255.);
return rgb2hex(r, g, b);
}
define color_mix (c1,c2,fraction)
%!%+
%\function{color_mix}
%\synopsis{Mix two rgb colors}
%\usage{UInt_Type color = color_mix (UInt_Type c1, UInt_Type c2, Double_Type fraction);}
%\description
% This function mixes two colors in rgb space. Given the rgb values in the
% usual encoding in one 24bit integer, the color is mixed according to
% new color = c*fraction + c2*(1-fraction)
% The operations are analoguous to the color mixing performed by the xcolor
% package of LaTeX (the operation is similar to the color1!fraction!color2
% syntax).
% The function returns the integer specifying the new rgb values. \code{fraction}
% can be an array. The colors can also be a hex string.
%!%-
{
fraction = _max(0., _min(1., fraction));
variable r1,g1,b1,r2,g2,b2;
(r1,g1,b1) = hex2rgb(c1);
(r2,g2,b2) = hex2rgb(c2);
return rgb2hex((r1*fraction+(1.0-fraction)*r2)/255.,
(g1*fraction+(1.0-fraction)*g2)/255.,
(b1*fraction+(1.0-fraction)*b2)/255.);
}
define color_wavelength (lambda)
%!%+
%\function{color_wavelength}
%\synopsis{Computes the color values of visual light}
%\usage{UInt_Type[] color_wavelength (Double_Type[] lambda);}
%\description
% \code{380 <= lambda <= 780} is the wavelength in nm.
% The return value is its 24bit RGB color value.
%
% The conversion is performed after the code by Dan Bruton,
% see http://www.physics.sfasu.edu/astro/color.html.
%!%-
{
variable r = Double_Type[length(lambda)];
variable g = @r;
variable b = @r;
variable w, spec;
w = where(380 <= lambda < 420);
spec = 0.3 + 0.7*(lambda[w]-380.)/(420-380);
r[w] = (440.-lambda[w])/(440-380)*spec;
% g[w] = 0;
b[w] = spec;
w = where(420 <= lambda < 440);
r[w] = (440.-lambda[w])/(440-380);
% g[w] = 0;
b[w] = 255;
w = where(440 <= lambda < 490);
% r[w] = 0;
g[w] = (lambda[w]-440.)/(490-440);
b[w] = 1.0;
w = where(490 <= lambda < 510);
% r[w] = 0;
g[w] = 1.0;
b[w] = (510.-lambda[w])/(510-490);
w = where(510 <= lambda < 580);
r[w] = (lambda[w]-510.)/(580-510);
g[w] = 1.0;
% b[w] = 0;
w = where(580 <= lambda < 645);
r[w] = 1.0;
g[w] = (645.-lambda[w])/(645-580);
% b[w] = 0;
w= where(645 <= lambda < 700);
r[w] = 1.0;
% b[w] = 0;
% g[w] = 0;
w = where(700 <= lambda < 780);
r[w] = 0.3 + 0.7*(780.-lambda[w])/(780-700);
% g[w] = 0;
% b[w] = 0;
return rgb2hex(r, g, b);
}
define complex2rgb()
%!%+
%\function{complex2rgb}
%\synopsis{converts a complex number to a RGB color}
%\usage{Integer_Type complex2rgb(Complex_Type z);}
%\description
% arg(code{z}) determines the hue,
% |\code{z}| determines the lightness:
% 0<=|\code{z}|<=1 is mapped from black to color,
% 1<=|\code{z}|<inf is mapped from color to white.
% The return value is a 24-bit RGB value.
%\qualifiers{
%\qualifier{sat}{[=1]: saturation (from 0 to 1) of the color}
%}
%\seealso{hsl2rgb, png_write}
%!%-
{
variable z;
switch(_NARGS)
{ case 1: z = (); }
{ help(_function_name()); return; }
variable arrayshape = NULL;
variable len = length(z);
if(typeof(z)==Array_Type)
{ arrayshape = array_shape(z);
reshape(z, len);
}
variable RGB = Integer_Type[len];
variable s = _max(0, _min(1, qualifier("sat", 1)));
variable i;
_for i (0, len-1, 1)
{ variable z0 = [z][i];
variable h = (atan2(Imag(z0), Real(z0))/(2*PI) + 1.) mod 1.;
variable r = abs(z0);
variable l = 0.5*r;
if(r>1) l = 1.-0.5/r;
RGB[i] = hsl2rgb(h, s, l);
}
if(arrayshape==NULL)
return RGB[0];
else
{ reshape(z, arrayshape);
return _reshape(RGB, arrayshape);
}
}
private define qph6_to_col(q, p, h6)
{ if(h6<1.) return p + (q-p)*h6;
else if(h6<3.) return q;
else if(h6<4.) return p + (q-p)*(4.-h6);
return p;
private define qphcol (q, p, h6)
{
if (h6 < 1.0) return p + (q-p)*h6;
if (h6 < 3.0) return q;
if (h6 < 4.0) return p + (q-p)*(4.0-h6);
return p;
}
define hsl2rgb()
private define map_hsl(h, s, l)
{
if (s == 0)
return l,l,l;
variable q = l*(1.0+s);
if (l > 0.5) q = l+s - l*s;
variable p = 2.0*l - q;
return qphcol(q, p, (h*6.0+2.0) mod 6.0), qphcol(q, p, h*6.0), qphcol(q, p, (h*6.0+4.0) mod 6.0);
}
define hsl2rgb(h, s, l)
%!%+
%\function{hsl2rgb}
%\synopsis{converts a (hue, saturation, lightness) color to (red, green, blue)}
......@@ -20,35 +34,55 @@ define hsl2rgb()
%\seealso{rgb2hsl}
%!%-
{
variable h, s, l, r, g, b;
switch(_NARGS)
{ case 3: (h, s, l) = (); }
{ return help(_function_name()); }
if(typeof(h)==Array_Type)
return array_map(Integer_Type, &hsl2rgb, h, s, l);
% {
% variable rgb = array_map(Integer_Type, &hsl2rgb, h, s, l);
% if(qualifier_exists("r_g_b"))
% return (rgb & 0xFF0000) >> 16, (rgb & 0xFF00) >> 8, rgb & 0xFF; % in inverval [0, 255]
% else
% return rgb;
% }
if(s==0)
(r, g, b) = (l, l, l);
else
{ variable q = l*(1.+s); if(l>0.5) q = l + s - l*s;
variable p = 2.*l - q;
r = qph6_to_col(q, p, (h*6+2.) mod 6.);
g = qph6_to_col(q, p, h*6 );
b = qph6_to_col(q, p, (h*6+4.) mod 6.);
}
% if(qualifier_exists("r_g_b"))
% return r, g, b; % in interval [0, 1]
% else
return ((typecast(255.9999999999999*r, UChar_Type) & 0xFF) shl 16)
+ ((typecast(255.9999999999999*g, UChar_Type) & 0xFF) shl 8)
+ (typecast(255.9999999999999*b, UChar_Type) & 0xFF);
variable r, g, b;
(r, g, b) = array_map(Double_Type, Double_Type, Double_Type, &map_hsl, h, s, l);
return length(r) == 1 ? (int(r*255)[0], int(g*255)[0], int(b*255)[0]) :
(int(r*255), int(g*255), int(b*255));
}
private define map_rhsl (ma, mi, l, r, g, b)
{
if (ma == mi)
return 0.0, 0.0, l;
variable d = ma - mi;
variable s = l > 0.5 ? d/(2 - ma - mi) : d/(ma+mi);
variable h;
switch (ma)
{ case r: h = (g - b)/d + (g < b ? 6.0 : 0.0); }
{ case g: h = (b - r)/d + 2.0; }
{ case b: h = (r - g)/d + 4.0; }
return h, s, l;
}
define rgb2hsl(r, g, b)
%!%+
%\function{rgb2hsl}
%\synopsis{converts (red, green, blue) values to (hue, saturation, lightness)}
%\usage{(Double_Type h, s, l) = rgb2hsl(Integer_Type r, g, b);}
%\description
% Converts 8 bit RGB values (0-255) to HSL values.
%
% The return values are always normalized to [0, 1].
% See \code{hsl2rgb} for a definition of hue, saturation, lightness.
%\seealso{hsl2rgb}
%!%-
{
r = r/255.;
g = g/255.;
b = b/255.;
variable ma = _max(r, g, b);
variable mi = _min(r, g, b);
variable h, s, l;
l = (ma+mi)/2.0;
(h,s,l) = array_map(Double_Type, Double_Type, Double_Type, &map_rhsl, ma, mi, l, r, g, b);
return length(h) == 1 ? (h[0],s[0],l[0]) : (h, s, l);
}
% Stolen from the HSLUV project, C implementation
private variable Triplet_m = {
[3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366],
[-0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247],
[0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072]
};
private variable Triplet_minv = {
[0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751],
[0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500],
[0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086]
};
private variable Ref_U = 0.19783000664283680764;
private variable Ref_V = 0.46831999493879100370;
private variable Kappa = 903.29629629629629629630;
private variable Epsilon = 0.00885645167903563082;
private define get_bounds (l)
{
variable tl = l+16.0;
variable sub1 = (tl*tl*tl)/1560896.0;
variable sub2 = (sub1 > Epsilon ? sub1 : (l / Kappa));
variable bounds = Double_Type[6,2];
variable i,j;
_for i (0, 2)
{
variable m1 = Triplet_m[i][0];
variable m2 = Triplet_m[i][1];
variable m3 = Triplet_m[i][2];
_for j (0, 1)
{
variable top1 = (284517.0 * m1 - 94839.0 * m3) * sub2;
variable top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * j * l;
variable bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * j;
bounds[i*2 + j, 0] = top1/bottom;
bounds[i*2 + j, 1] = top2/bottom;
}
}
return bounds;
}
private define intersect_line_line (line1, line2)
{
return (line1[1] - line2[1])/(line2[0] - line1[0]);
}
private define ray_length_until_intersect (theta, line)
{
return line[1]/(sin(theta)-line[0]*cos(theta));
}
private define max_safe_chroma_for_l (l)
{
variable min_len_sqr = DOUBLE_MAX;
variable bounds = get_bounds(l);
variable i;
_for i (0, 5)
{
variable m1 = bounds[i, 0];
variable b1 = bounds[i, 1];
variable line2 = [-1.0/m1, 0.0];
variable x = intersect_line_line (bounds[i, *], line2);
variable dist = sumsq([x, b1+x*m1]);
if (dist < min_len_sqr)
min_len_sqr = dist;
}
return sqrt(min_len_sqr);
}
private define max_chroma_for_lh (l, h)
{
variable min_len = DOUBLE_MAX;
variable hrad = h * 0.01745329251994329577; % (2 * pi / 360)
variable bounds = get_bounds(l);
variable i;
_for i (0, 5)
{
variable len = ray_length_until_intersect(hrad, bounds[i,*]);
if (len >= 0 && len < min_len)
min_len = len;
}
return min_len;
}
private define from_linear (c)
{
if (c <= 0.0031308)
return 12.92 * c;
else
return 1.055 * c^(1.0/2.4) - 0.055;
}
private define to_linear (c)
{
if (c > 0.04045)
return ((c + 0.055)/1.055)^2.4;
else
return c / 12.92;
}
private define xyz2rgb (x, y, z)
{
variable r = from_linear(sum(Triplet_m[0]*[x,y,z]));
variable g = from_linear(sum(Triplet_m[1]*[x,y,z]));
variable b = from_linear(sum(Triplet_m[2]*[x,y,z]));
return r,g,b;
}
private define rgb2xyz (r, g, b)
{
variable rgbl = array_map(Double_Type, &to_linear, [r,g,b]);
variable x, y, z;
x = sum(Triplet_minv[0]*rgbl);
y = sum(Triplet_minv[1]*rgbl);
z = sum(Triplet_minv[2]*rgbl);
return x, y, z;
}
private define y2l (y)
{
if (y <= Epsilon)
return y*Kappa;
else
return 116.0*y^(1./3) - 16.0;
}
private define l2y (l)
{
if (l <= 8.0)
return l/Kappa;
else
return ((l+16.0)/116.0)^3;
}
private define xyz2luv (x, y, z)
{
variable var_u = (4.0 * x) / (x + (15.0 * y) + (3.0 * z));
variable var_v = (9.0 * y) / (x + (15.0 * y) + (3.0 * z));
variable l = y2l(y);
variable u = 13.0*l*(var_u - Ref_U);
variable v = 13.0*l*(var_v - Ref_V);
if (l < 0.00000001)
return l, 0.0, 0.0;
else
return l, u, v;
}
private define luv2xyz (l, u, v)
{
if (l <= 0.00000001)
return 0.0, 0.0, 0.0;
variable var_u = u/(13.0*l) + Ref_U;
variable var_v = v/(13.0*l) + Ref_V;
variable y = l2y(l);
variable x = -(9.0*y*var_u)/((var_u-4.0)*var_v-var_u*var_v);
variable z = (9.0*y-(15.0*var_v*y)-(var_v*x))/(3.0*var_v);
return x, y, z;
}
private define luv2lch (l, u, v)
{
variable h, c = sqrt(u*u+v*v);
if (c < 0.00000001)
h = 0.0;
else
{
h = atan2(v, u) * 57.29577951308232; % 180. / PI
if (h < 0)
h += 360.;
}
return l, c, h;
}
private define lch2luv (l, c, h)
{
variable hrad = h * 0.017453292519943295; % PI / 180.
variable u = cos(hrad)*c;
variable v = sin(hrad)*c;
return l, u, v;
}
private define hsluv2lch (h, s, l)
{
variable c;
if (l > 99.9999999 || l < 0.00000001)
c = 0.0;
else
c = max_chroma_for_lh(l,h)/100.0*s;
if (s<0.00000001)
h = 0.0;
return l, c, h;
}
private define lch2hsluv (l, c, h)
{
variable s;
if (l > 99.9999999 || l < 0.00000001)
s = 0.0;
else
s = c / max_chroma_for_lh(l, h) * 100.0;
if (c < 0.00000001)
h = 0.0;
return h, s, l;
}
private define hpluv2lch (h, s, l)
{
variable c;
if(l > 99.9999999 || l < 0.00000001)
c = 0.0;
else
c = max_safe_chroma_for_l(l) / 100.0 * s;
if (s < 0.00000001)
h = 0.0;
return l, c, h;
}
private define lch2hpluv (l, c, h)
{
variable s;
if (l > 99.9999999 || l < 0.00000001)
s = 0.0;
else
s = c / max_safe_chroma_for_l(l) * 100.0;
if (c < 0.00000001)
h = 0.0;
return h, s, l;
}
define hsluv2rgb (h, s, l)
%!%+
%\function{hsluv2rgb}
%\usage{Int_Type r, g, b = hsluv2rgb(Double_Type h, s, l);}
%\synopsis{Calculate RGB triplet from HSLuv space}
%\description
% This function returns the RGB colors (as 8 bit values) of
% the corresponding HSLuv color (see https://www.hsluv.org/).
% The inputs range from 0 to 1.
%
% This colorpsace is useful to generate custom color maps and
% palettes as it attemps to give the same perceived lightness (l)
% and saturation (s) for different hues (h). It does not contain
% the full RGB space, however.
%
%\seealso{rgb2hsluv}
%!%-
{
variable A, B, C;
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &hsluv2lch, h*360.,s*100.,l*100.);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &lch2luv, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &luv2xyz, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &xyz2rgb, A, B, C);
(A, B, C) = (int(_max(0, _min(A*255, 255))), int(_max(0, _min(B*255, 255))), int(_max(0, _min(C*255, 255))));
return length(A) == 1 ? (A[0], B[0], C[0]) : (A, B, C);
}
define hpluv2rgb (h, s, l)
%!%+
%\function{hpluv2rgb}
%\usage{Int_Type r, g, b = hpluv2rgb(Double_Type h, s, l);}
%\synopsis{Calculate RGB triplet from HPLuv space}
%\description
% This function returns the RGB colors (as 8 bit values) of
% the corresponding HPLuv color (see https://www.hsluv.org/).
% The inputs range from 0 to 1.
%
% This colorpsace is useful to generate custom color maps and
% palettes as it attemps to give the same perceived lightness (l)
% and saturation (s) for different hues (h). Compared to HSLuv
% it tries to maximize the used color range.
%
%\seealso{rgb2hpluv, hsluv2rgb}
%!%-
{
variable A, B, C;
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &hpluv2lch, h*360.,s*100.,l*100.);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &lch2luv, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &luv2xyz, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &xyz2rgb, A, B, C);
(A, B, C) = (int(_max(0, _min(A*255, 255))), int(_max(0, _min(B*255, 255))), int(_max(0, _min(C*255, 255))));
return length(A) == 1 ? (A[0], B[0], C[0]) : (A, B, C);
}
define rgb2hsluv (r, g, b)
%!%+
%\function{rgb2hsluv}
%\usage{Double_Type h, s, l = rgb2hsluv(Int_Type r, g, b);}
%\synopsis{Calculate HSLuv triplet from RGB space}
%\description
% Inverse function of \code{hsluv2rgb}.
%
%\seealso{hsluv2rgb}
%!%-
{
variable A, B, C;
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &rgb2xyz, r/255., g/255., b/255.);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &xyz2luv, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &luv2lch, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &lch2hsluv, A, B, C);
(A, B, C) = (_max(0, _min(A/360., 1)), _max(0, _min(B/100., 1)), _max(0, _min(C/100., 1)));
return length(A) == 1 ? (A[0], B[0], C[0]) : (A, B, C);
}
define rgb2hpluv (r, g, b)
%!%+
%\function{rgb2hpluv}
%\usage{Double_Type h, s, l = rgb2hpluv(Int_Type r, g, b);}
%\synopsis{Calculate HSPuv triplet from RGB space}
%\description
% Inverse function of \code{hpluv2rgb}.
%
%\seealso{hpluv2rgb}
%!%-
{
variable A, B, C;
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &rgb2xyz, r/255., g/255., b/255.);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &xyz2luv, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &luv2lch, A, B, C);
(A, B, C) = array_map(Double_Type, Double_Type, Double_Type, &lch2hpluv, A, B, C);
(A, B, C) = (_max(0, _min(A/360., 1)), _max(0, _min(B/100., 1)), _max(0, _min(C/100., 1)));
return length(A) == 1 ? (A[0], B[0], C[0]) : (A, B, C);
}
private define map_hue (i,v,q,p,t)
{
switch (i mod 6)
{ case 0: return v, t, p; }
{ case 1: return q, v, p; }
{ case 2: return p, v, t; }
{ case 3: return p, q, v; }
{ case 4: return t, p, v; }
{ case 5: return v, p, q; }
}
define hsv2rgb (h, s, v)
%!%+
%\function{hsv2rgb}
%\synopsis{Convert HSV color to RGB}
%\usage{(Int_Type r,g,b) = hsv2rgb(Double_Type h,s,v);}
%\description
% HSV (Hue, Saturation, Value) as doubles (0-1) are
% converted to RGB values (defined as [0-255] 8 bit
% values). Works for array values two (lengths must
% match).
%!%-
{
variable i = int(floor(h*6));
variable f = h*6-i;
variable p = v*(1-s);
variable q = v*(1-f*s);
variable t = v*(1 - (1 - f)*s);
variable r,g,b;
(r,g,b) = array_map(Double_Type,Double_Type,Double_Type,&map_hue,i,v,q,p,t);
return length(r) == 1 ? (int(r*255)[0], int(g*255)[0], int(b*255)[0]) :
(int(r*255), int(g*255), int(b*255));
}
private define map_max (ma, mi, r, g, b, d)
{
variable h;
if (ma == mi)
return 0.0;
else
{
switch (ma)
{ case r: h = (g-b)/d + (g < b ? 6.0 : 0.0); }
{ case g: h = (b-r)/d + 2.0; }
{ case b: h = (r-g)/d + 4.0; }
}
return h/6.0;
}
define rgb2hsv (r, g, b)
%!%+
%\function{rgb2hsv}
%\synopsis{Convert RGB color to HSV}
%\usage{(Double_Type h,s,v) = rgb2hsv(Int_Type r,g,b);}
%\description
% RGB colores encoded as 8 bit (0-255) values get
% converted to HSV (Hue, Saturation, Value) as doubles
% (0-1) are. Works for array values two (lengths must
% match).
%!%-
{
r = r/255.;
g = g/255.;
b = b/255.;
variable ma = _max(r, g, b);
variable mi = _min(r, g, b);
variable h, s, v;
v = ma;
variable d = ma - mi;
s = ma == 0.0 ? 0 : d/ma;
return length(r) == 1 ? (array_map(Double_Type, &map_max, ma, mi, r, g, b, d)[0], s[0], v[0]) :
(array_map(Double_Type, &map_max, ma, mi, r, g, b, d), s, v);
}
\ No newline at end of file
define get_color_map (name)
%!%+
%\function{get_color_map}
%\synopsis{Get an intrinsic color map or construct one}
%\usage{UInt_Type[256] map = get_color_map(name);}
%\qualifiers{
% \qualifier{reverse}{Reverse returned map}
%}
%\description
% Retreive a colormap, either from the internal list, or
% constructed from a color palette.
%
% Qualifiers are passed to the constructor.
%
%\seealso{get_color_palette, png_get_colormap_names}
%!%-
{
variable map = NULL;
#ifexists png_add_colormap
ifnot (qualifier_exists("palette"))
{
try
map = png_get_colormap(name);
catch AnyError;
}
if (NULL != map)
return map;
#endif
try
map = get_color_palette(name, 256;; __qualifiers);
catch AnyError:
throw UsageError, "Unknown palette name '$name'";
if (length(map)<256)
map = get_color_palette("blend", 256; colors=map);
if (qualifier_exists("reverse"))
map = reverse(map);
if (_typeof(map) == String_Type)
return rgb2hex(hex2rgb(map));
return map;
}
define mix_rgb_colors(rgb1,rgb2,fraction) {
%!%+
%\function{mix_rgb_colors}
%\synopsis{mix two rgb colors}
%\usage{rgb=mix_rgb_colors(rgb1,rgb2,fraction)}
%\description
% This function mixes two colors in rgb space. Given the rgb values in the
% usual encoding in one 24bit integer, the color is mixed according to
% new rgb=rgb1*fraction+rgb2*(1-fraction)
% The operations are analoguous to the color mixing performed by the xcolor
% package of LaTeX (the operation is similar to LaTeX's color1!fraction!color2
% syntax).
% The function returns the integer specifying the new rgb values.
%\seealso{xfig_mix_color}
%!%-
% get rgb values
variable bb1=double(rgb1 & 0x0000FF);
variable gg1=double((rgb1>>8) & 0x0000FF);
variable rr1=double((rgb1>>16) & 0x0000FF);
variable bb2=double(rgb2 & 0x0000FF);
variable gg2=double((rgb2>>8) & 0x0000FF);
variable rr2=double((rgb2>>16) & 0x0000FF);
if (fraction<0.) {fraction=0.;}
if (fraction>1.) {fraction=1.;}
% Mix colors in floating point space
variable rr=rr1 * fraction + (1.-fraction)*rr2;
variable gg=gg1 * fraction + (1.-fraction)*gg2;
variable bb=bb1 * fraction + (1.-fraction)*bb2;
if (rr>255.) { rr=255;};
if (gg>255.) { gg=255;};
if (bb>255.) { bb=255;};
if (rr<0) { rr=0;};
if (gg<0) { gg=0;};
if (bb<0) { bb=0;};
% put everything back together
variable rgb=(int(rr) << 16) | (int(gg) << 8) | int(bb);
return rgb;
}
This diff is collapsed.
require("png");
define png_get_colormap_color (colormap, len)
%!%+
%\function{png_get_colormap_color}
%\synopsis{Retrieve a set of colors from a colormap}
%\usage{UInt_Type[] png_get_colormap_color (String_Type map, Int_Type len);}
%\qualifiers{
% \qualifier{string}{Colors are returned as hex string.}
%}
%\description
% This function is useful if a range of colors is required.
% It returns \code{len} values from a linear mapping of the
% given colormap. \code{len} may at most be 256.
%
%\example
% variable colors = png_get_colormap_colors("cool", 25);
% % returns 25 colors from the "cool" map
% variable colors = png_get_colormap_colors("cool", 25; string);
% % same, but now as strings
%!%-
{
if (len > 256)
throw UsageError, "Can only map into arrays with a length of at most 256 entires.";
variable map = png_get_colormap(colormap);
variable colors = UInt_Type[len];
variable i;
_for i (0, len-1)
{
colors[i] = map[int(round(i*1./(len-1)*255))];
}
if (qualifier_exists("string"))
colors = array_map(String_Type, &sprintf, "%06X", colors);
return colors;
}
define rgb2gray()
%!%+
%\function{rgb2gray}
%\synopsis{Weighted RGB to GRAY convertion}
%\usage{Integer_Type gray = rgb2gray( Integer_Type[] RGB );}
%\usage{Integer_Type gray = rgb2gray( Integer_Type[] R, G, B );}
%\description
% Converts a RGB color, which is either encoded in one Integer or
% as single channels (R,G,B), into a weighted gray scale using:
%
% GRAY = (0.3 * R) + (0.59 * G) + (0.11 * B)
%
% The 8 bit gray value(s) are return as
% Integer_Type[] in the interval [0,255].
%\qualifiers{
%\qualifier{norm}{return values are in Double_Type interval [0,1].}
%\qualifier{rgb}{return values as single RGB encoding:
% GRAY << 16 | GRAY << 8 | GRAY}
%}
%
%\seealso{rgb2hsl, hex2rgb}
%!%-
{
variable R, G, B;
switch(_NARGS)
{ case 1 : (R, G, B) = rgb2r_g_b(()); }
{ case 3 : (R, G, B) = (); }
{ help(_function_name); };
variable GRAY = nint((0.3 * R) + (0.59 * G) + (0.11 * B));
if( qualifier_exists("norm") ){
return GRAY/255.;
}
if( qualifier_exists("rgb") ){
return (GRAY << 16 | GRAY << 8 | GRAY);
}
return GRAY;
}
define rgb2hex()
define rgb2hex (r, g, b)
%!%+
%\function{rgb2hex}
%\synopsis{converts r, g, b values between 0 and 1 to a 24-bit integer}
%\usage{Integer_Type rgb2hex(Double_Type r, g, b)}
%\synopsis{converts r, g, b values to 24 bit color values.}
%\usage{Integer_Type rgb2hex(Double/Int_Type r, g, b)}
%\qualifiers{
%\qualifier{str}{return value is a string with preceding "#".}
%\qualifier{des}{input color will be desaturated to gray-scale values.
% If a value is given, the saturation will be scaled accordingly.}
% \qualifier{string}{returns value as hex encoded string.}
%}
%\seealso{rgb2hsl}
%\description
% Convert given color values to one 24 bit value.
% If values are doubles they are interpreted as ranging
% from 0 to 1. If integers they are interpreted as
% 8 bit (0-255) color values.
%\seealso{rgb2hex}
%!%-
{
variable r, g, b;
switch(_NARGS)
{ case 3: (r, g, b) = (); }
{ return help(_function_name()); }
ifnot(length(r) == length(g) ==length(b))
return "ERROR (rgb2hex): color arrays must of be of the same length.";
variable col = (int(_max(0, _min(r*256, 255)))<<16)
+(int(_max(0, _min(g*256, 255)))<< 8)
+ int(_max(0, _min(b*256, 255))) ;
if (_typeof(r) == Double_Type) r *= 255;
if (_typeof(g) == Double_Type) g *= 255;
if (_typeof(b) == Double_Type) b *= 255;
if (qualifier_exists("des")) {
variable h,s,l ;
(h, s, l) = rgb2hsl(col);
variable s_scale = qualifier("des");
if(s_scale == NULL) s_scale = 0;
s *= s_scale;
col = hsl2rgb(h, _max(0, _min(s, 1)), l);
}
variable col = (int(_max(0, _min(r, 255)))<<16)
+ (int(_max(0, _min(g, 255)))<<8)
+ (int(_max(0, _min(b, 255))));
if (qualifier_exists("str") ) {
col = array_map (String_Type, &sprintf, "#%06X", col) ;
}
if (qualifier_exists("str") or qualifier_exists("string"))
col = array_map (String_Type, &sprintf, "%06x", col) ;
else if (qualifier_exists("Str") or qualifier_exists("String")
or qualifier_exists("STR") or qualifier_exists("STRING"))
col = array_map (String_Type, &sprintf, "%06X", col) ;
return length(col)==1 ? col[0] : col;
}
private define map_hexstr (str)
{
variable r, g, b;
() = sscanf(str, "%02x%02x%02x", &r, &g, &b);
return r, g, b;
}
define hex2rgb (col)
%!%+
%\function{hex2rgb}
%\synopsis{converts a 24 bit color value to r, g, b, values.}
%\usage{Integer_Type r, g, b hex2rgb(Int/String_Type col)}
%\qualifiers{
% \qualifier{float}{If given, the channels are instead returned in the range (0-1).}
%}
%\description
% Converts a hex color to RGB values. Can be either 24 bit
% value or string.
%\seealso{rgb2hex}
%!%-
{
variable r, g, b;
if (_typeof(col) == String_Type)
(r, g, b) = array_map(UInt_Type, UInt_Type, UInt_Type, &map_hexstr, strlow(col));
else
(r, g, b) = (col & (0xff0000))>>16, (col & 0xff00)>>8, col & 0xff;
if (qualifier_exists("float"))
(r, g, b) = (r/255., g/255., b/255.);
return length(r) == 1 ? (r[0], g[0], b[0]) : (r, g, b);
}
%%%%%%%%%%%%%%
define rgb2hsl()
%%%%%%%%%%%%%%
%!%+
%\function{rgb2hsl}
%\synopsis{converts (red, green, blue) values to (hue, saturation, lightness)}
%\usage{(Double_Type h, s, l) = rgb2hsl(Integer_Type rgb);
%\altusage{(Double_Type h, s, l) = rgb2hsl(Double_Type r, g, b);}
%}
%\description
% In the first usage, rgb is an (array of) 24 bit RGB values.
% In the second form, r, g, and b are values between 0. and 1.
%
% The return values are always normalized to [0, 1].
% See \code{hsl2rgb} for a definition of hue, saturation, lightness.
%\seealso{hsl2rgb}
%!%-
{
variable r, g, b, h, s, l;
switch(_NARGS)
{ case 1:
variable rgb = ();
r = ((rgb & 0xFF0000) >> 16)/255.;
g = ((rgb & 0x00FF00) >> 8)/255.;
b = ( rgb & 0x0000FF )/255.;
}
{ case 3: (r, g, b) = (); }
{ return help(_function_name()); }
if(typeof(r)==Array_Type)
{
variable dim = array_shape(r);
variable i, n = length(r);
reshape(r, n);
reshape(g, n);
reshape(b, n);
h = Double_Type[n];
s = Double_Type[n];
l = Double_Type[n];
_for i (0, n-1, 1)
(h[i], s[i], l[i]) = rgb2hsl(r[i], g[i], b[i]);
reshape(r, dim);
reshape(g, dim);
reshape(b, dim);
reshape(h, dim);
reshape(s, dim);
reshape(l, dim);
return h, s, l;
}
variable mn, mx; (mn, mx) = min_max([r, g, b]);
l = (mx+mn)/2.;
if(mx == mn)
{
h = 0;
s = 0;
}
else
{
variable tmp = 1./6./(mx-mn);
if(r == mx) h = ((g-b)*tmp + 1.) mod 1.;
if(g == mx) h = (b-r)*tmp + 1./3.;
if(b == mx) h = (r-g)*tmp + 2./3.;
s = (l<=.5) ? (mx-mn)/(mx+mn) : (mx-mn)/(2-mx-mn);
}
return h, s, l;
}
define rgb2r_g_b( rgb )
%!%+
%\function{rgb2r_g_b}
%\synopsis{converts a ( rgb ) color to (red, green, blue)}
%\usage{Integer_Type r,g,b = rgb2r_g_b( Integer_Type[] );}
%\description
% Converts a color encoded in one Integer into its single
% r, g, b values.
% The return value is a(n array of) 24 bit RGB value(s)
% in Integer_Type interval [0,255].
%\qualifiers{
%\qualifier{norm}{return values are in Double_Type interval [0,1].}
%\qualifier{str}{If qualifier is given ('str'=NULL) r,g,b will be retruned
% as strings. If 'str' != NULL, e.g., 'str'=',', than
% the singe color channels are combined and returned:
% r + str + g + str + b}
%}
%
%\seealso{rgb2hsl, hex2rgb}
%!%-
{
variable r = (rgb & 0xFF0000) >> 16;
variable g = (rgb & 0xFF00) >> 8;
variable b = rgb & 0xFF;
if( qualifier_exists("norm") ){
r /= 1.*255;
g /= 1.*255;
b /= 1.*255;
}
variable str = qualifier("str");
if( qualifier_exists("str") ){
r = array_map( String_Type, &string, r );
g = array_map( String_Type, &string, g );
b = array_map( String_Type, &string, b );
if( str != NULL ){
return r+str+g+str+b;
}
}
return r,g,b;
}
define rgb_interpol_colmap(rgbind, rgbcol, nintcol){
%!%+
%\function{rgb_interpol_colmap}
%\synopsis{interpolate between multiple rgb colors}
%\usage{col=rgb_interpol_colmap(rgbind, rgbcol, nintcol)}
%\description
%This function builds a color array by interpolating between
%multiple colors. The resulting array has nintcol colors.
%In this array, the colors rgbcol[ii] will be placed at
%col[rgbind[ii]].
%The interpolation between the individual colrs is linear.
%The input color indices rgbind[*] have to be sorted in
%ascending order.
%The result can be assigned as a color map directly using
% png_add_colormap.
%\seealso{mix_rgb_colors}
%!%-
% check input values for consistency
variable ncol=length(rgbind);
if(ncol!=length(rgbcol)){
vmessage("Number of color indices must be equal to number of colors.");
return NULL;
}
if(rgbind[0]!=0){
vmessage("First color index must be 0.");
}
if(rgbind[ncol-1]!=nintcol-1){
vmessage("Last color index must be nintcol-1.");
}
% initialize variables
variable col=Integer_Type[nintcol];
variable ind, ii;
ind=0;
% build color array
for(ii=0; ii<nintcol; ii++){
if(ii>rgbind[ind+1]){
ind++;
}
variable fraction=1.*(rgbind[ind+1]-ii)/(1.*(rgbind[ind+1]-rgbind[ind]));
col[ii]=mix_rgb_colors(rgbcol[ind], rgbcol[ind+1], fraction);
}
return col;
}
define wavelength_to_rgb()
%!%+
%\function{wavelength_to_rgb}
%\synopsis{computes the color values of visual light}
%\usage{Integer_Type[] wavelength_to_rgb(Double_Type lambda[]);}
%\description
% \code{380 <= lambda <= 780} is the wavelength in nm.
% The return value is its 24bit RGB color value.
%
% The conversion is performed after the code by Dan Bruton,
% see http://www.physics.sfasu.edu/astro/color.html.
%!%-
{
variable lambda;
switch(_NARGS)
{ case 1: lambda = (); }
{ return help(_function_name()); }
variable i, sss, eightbit=255.99999, R=Integer_Type[length(lambda)], G=@R, B=@R;
i = where(380 <= lambda < 420);
sss = .3 + .7*(lambda[i]-380.)/(420-380);
R[i] = int(eightbit*(440.-lambda[i])/(440-380)*sss);
% G[i] = 0;
B[i] = int(eightbit*sss);
i = where(420 <= lambda < 440);
R[i] = int(eightbit*(440.-lambda[i])/(440-380));
% G[i] = 0;
B[i] = 255;
i = where(440 <= lambda < 490);
% R[i] = 0;
G[i] = int(eightbit*(lambda[i]-440.)/(490-440));
B[i] = 255;
i = where(490 <= lambda < 510);
% R[i] = 0;
G[i] = 255;
B[i] = int(eightbit*(510.-lambda[i])/(510-490));
i = where(510 <= lambda < 580);
R[i] = int(eightbit*(lambda[i]-510.)/(580-510));
G[i] = 255;
% B[i] = 0;
i = where(580 <= lambda < 645);
R[i] = 255;
G[i] = int(eightbit*(645.-lambda[i])/(645-580));;
% B[i] = 0;
i = where(645 <= lambda < 700);
R[i] = 255;
% G[i] = 0;
% B[i] = 0;
i = where(700 <= lambda < 780);
sss = .3 + .7*(780.-lambda[i])/(780-700);
R[i] = int(eightbit*sss);
% G[i] = 0;
% B[i] = 0;
variable RGB = R*0x010000 + G*0x0100 + B;
return (typeof(lambda)==Array_Type ? RGB : RGB[0]);
}
require("xfig");
define xfig_new_X11color(nam) {
%!%+
%\function{xfig_new_X11color}
%\synopsis{define a new color from x11's rgb.txt for xfig}
%\usage{xfig_new_X11color(colorname)}
%\qualifiers{
%\qualifier{x11name}{Name of the color in rgb.txt}
%\qualifier{rgbfile}{File defining the colors, default: /usr/share/X11/rgb.txt}
%}
%\description
%This function is a wrapper around X11color2rgb. It searches for the color
%using x11color2rgb and then defines a new xfig color with the same name.
%Note that some of xfig's colors can have the same name as names in
%rgb.txt but different rgb values. In this case the default of this function
%is to override xfig's definition. If this is not desired, use the x11name
%qualifier.
%\seealso{x11color2rgb,xfig_lookup_w3c_color}
%!%-
variable searchname=qualifier("x11name",nam);
variable col=X11color2rgb(searchname;;__qualifiers);
xfig_new_color(nam,col);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment