Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.glorb.com!news-out.readnews.com!news-xxxfer.readnews.com!panix!not-for-mail From: JohnF Newsgroups: comp.graphics.algorithms Subject: Re: decorative random colors algorithm Date: Thu, 5 Jul 2012 05:54:02 +0000 (UTC) Organization: PANIX Public Access Internet and UNIX, NYC Lines: 161 Message-ID: References: <34b5c6a0-c79e-4a8f-bf71-4d480c9a5dc3@googlegroups.com> NNTP-Posting-Host: panix3.panix.com X-Trace: reader1.panix.com 1341467642 18379 166.84.1.3 (5 Jul 2012 05:54:02 GMT) X-Complaints-To: abuse@panix.com NNTP-Posting-Date: Thu, 5 Jul 2012 05:54:02 +0000 (UTC) User-Agent: tin/2.0.0-20110823 ("Ardenistiel") (UNIX) (NetBSD/5.1.2 (i386)) Xref: csiph.com comp.graphics.algorithms:928 JohnF wrote: > gernot.hoffmann@hs-emden-leer.de wrote: >> On the other hand, I had recognized early, that this color space >> suffers much from a disadvantage: The saturation on the surface of >> the double cone is S=1. On the vertical axis we have S=0. Near to poles >> we have in vicinity S=1 and S=0, which is in my opinion a not acceptable >> discontinuity. In order to resolve this problem I had developed my own >> HLS space, which uses S as a true cylinder coordinate - small S for >> small radius. At that time, trigonometric functions were sufficiently >> fast, using the standard floating point unit. > > Okay, I'll add your Section 8 code to my C function, along with > an option to use either the "standard" or "hoffmann" method > (yours will become the default, of course:). Okay, that's done, and comparison "color circles" are at forkosh.com/decorativef.ps and forkosh.com/decorativeho.ps (both eps). The cgi has also been updated. Add attribute hlsmethod=1 for Foley and hlsmethod=2 for Hoffmann, with 2 the default. I don't see much visual difference, except maybe slightly brighter yellows for "f" in the lower-right quadrant. Of course, it's not a thorough exercise, with l=0.5,s=1.0 constant, with only variation h=3degrees (positive-x axis), incrementing three per line clockwise. But you can diff the downloaded files for more precise comparison, e.g., the first few lines of diff decorativef.ps decorativeho.ps are 132c132 < 0.78 0.00 0.78 1.00 0.05 0.00 setrgb01 % hls0=..., hls1=3.00,0.50,1.00 --- > 0.78 0.00 0.78 1.00 0.18 0.13 setrgb01 % hls0=..., hls1=3.00,0.50,1.00 135c135 < 0.78 0.00 0.78 1.00 0.10 0.00 setrgb01 % hls0=..., hls1=6.00,0.50,1.00 --- > 0.78 0.00 0.78 1.00 0.22 0.11 setrgb01 % hls0=..., hls1=6.00,0.50,1.00 So there's some numerical difference. I think I got your (and Foley,van Dam's) algorithms right, but have included the updated function below. For example, I didn't have your limtab, and just clipped your single r,g,b's to 1.0. Don't think that accounts for any of the "diff difference". /* ========================================================================== * Function: hlstorgb ( h,l,s, rgb, method ) * Purpose: convert h,l,s color model cylindrical coordinates * to equivalent cartesian r,g,b coordinates * -------------------------------------------------------------------------- * Arguments: h (I) double containing hue "angle" in degrees, * 0.0<=h<360.0 * l (I) double containing lightness "vertical axis", * 0.0<=l<=1.0 * s (I) double containing saturation "radius", * 0.0<=s<=1.0 * rgbswitch (I) int containing 1 to return r(ed) coord, * 2 for g(reen) coord, or 3 for b(lue) coord. * Alternatively, 0 returns ((256*r+g)*256)+b, * i.e., 24-bit composite with b in the low-order * eight bits, g in the next eight bits, * and r in the high-order (of 24) eight bits. * method (I) int containing 1 for standard Foley, van Dam * hls conversion, or containing 2 for * Hoffmann conversion (see Notes below). * -------------------------------------------------------------------------- * Returns: ( int ) r or g or b, if rgb=1 or 2 or 3, respectively, * or ((256*r+g)*256)+b composite if rgb=0, * or 0 for any error. * -------------------------------------------------------------------------- * Notes: o See http://www.fho-emden.de/~hoffmann/hlscone03052001.pdf * for discussion, derivation, and pascal/postscript code * from which this function was taken. * ======================================================================= */ /* --- entry point --- */ int hlstorgb ( double h, double l, double s, int rgbswitch, int method ) { /* ------------------------------------------------------------------------- allocations and declarations -------------------------------------------------------------------------- */ int rgb[3]={0,0,0}, irgb=0; /* r=rgb[0], g=rgb[1], b=rgb[2] */ /* ------------------------------------------------------------------------- check input arguments -------------------------------------------------------------------------- */ if ( h<0.0 || h>=360.0 ) { /* restrict 0<=h<360 */ h = fmod(h,360.0); /* now -3601.0) l=1.0; /* restrict 0<=l<=1 */ if (s<0.0) s=0.0; if (s>1.0) s=1.0; /* restrict 0<=s<=1 */ if (rgbswitch<0) rgbswitch=0; /* interpret as 0 */ if (rgbswitch>3) rgbswitch=(rgbswitch%4); /* interpret mod 4 */ if (method<1) method=1; if (method>2) method=2; /* only method=1,2 exist */ /* ------------------------------------------------------------------------- Foley, van Dam method... -------------------------------------------------------------------------- */ if ( method == 1 ) { /* --- allocations and declarations ---------------------------- */ double hlsmax=0.0, hlsmin=0.0, hlsmmm=0.0; /* hls params max,min,max-min */ double hlshue=0.0, hlscolor=0.0; /* hls hue and color */ /* --- hls parameterization -------------------- */ hlsmax = ((l<=0.5)? l*(1.0+s) : (l+s)-(l*s)); hlsmin = 2.0*l - hlsmax; hlsmmm = (hlsmax - hlsmin)/60.0; /* --- loop over red, green, blue -------------------------- */ for ( irgb=0; irgb<=2; irgb++ ) { /* for each rgb component */ if ( rgbswitch!=0 && rgbswitch!=irgb+1 ) continue;/*this one not needed*/ hlshue = h + 120.0*((double)(1-irgb)); /* h+120, h, h-120 */ if ( irgb==0 ) { if ( hlshue > 360.0 ) hlshue -= 360.0; } if ( irgb==2 ) { if ( hlshue < 0.0 ) hlshue += 360.0; } if ( hlshue < 60.0 ) hlscolor = hlsmin + hlsmmm*hlshue; else if ( hlshue < 180.0 ) hlscolor = hlsmax; else if ( hlshue < 240.0 ) hlscolor = hlsmin + hlsmmm*(240.0-hlshue); else hlscolor = hlsmin; rgb[irgb] = (int)(0.5+(hlscolor*255.0)); } /* --- end-of-for(irgb) --- */ } /* --- end-of-if(method==1) --- */ /* ------------------------------------------------------------------------- Hoffmann method... -------------------------------------------------------------------------- */ if ( method == 2 ) { /* --- allocations and declarations ---------------------------- */ double pi = 3.14159265359, /*pi (convert 0<=h<=360 to radians)*/ hcos = cos(h*pi/180.), hsin = sin(h*pi/180.), /* cos,sin(h in radians) */ h0 = 30.0 + 60.0*((double)((int)(h/60.))), h0cos=cos(h0*pi/180.), h0sin=sin(h0*pi/180.); /* cos,sin(h0 in radians)*/ double d = 0.5*sqrt(3.0)*s/((hcos*h0cos)+(hsin*h0sin)), u = hcos*d/3.0, v = hsin*d/sqrt(3.0); double r = l+u+u, g = l-u+v, b = l-u-v; double rgbmax = (r>g? (r>b?r:b) : (g>b?g:b)); if ( rgbmax > 1.0 ) { r /= rgbmax; g /= rgbmax; b /= rgbmax; } rgb[0] = (int)(255.0*r + 0.5); rgb[1] = (int)(255.0*g + 0.5); rgb[2] = (int)(255.0*b + 0.5); } /* --- end-of-if(method==2) --- */ /* ------------------------------------------------------------------------- end-of-job -------------------------------------------------------------------------- */ /*end_of_job:*/ return ( (rgbswitch==0? /* back with r,g,b or composite */ rgb[2] + 256*(rgb[1] + 256*rgb[0]) : /* rgb composite */ rgb[rgbswitch-1]) ); /* r,g,b component */ } /* --- end-of-function hlstorgb() --- */ -- John Forkosh ( mailto: j@f.com where j=john and f=forkosh )