// Cubehelix color scheme for Max/MSP/Jitter // By Théophile Clet // https://tflcl.xyz // // Based on Dave Green's work: // http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/cubetry.html // // Original publication: Green, D. A., 2011, `A colour scheme for the display of astronomical intensity images', Bulletin of the Astronomical Society of India, 39, 289. (2011BASI...39..289G at ADS.) // http://astron-soc.in/bulletin/11June/289392011.pdf ) // // Original js code taken straight from the online example implementation: // http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/cubetry.html autowatch = 0; inlets = 1; outlets = 4; var RGBOUTLET = 0; var HSLOUTLET = 1; var LISTOUTLET = 2 var DUMPOUTLET = 3; setinletassist(0, "List as input: {makeCubehelixRGB, start[float], rots[float], sign[-1,1], hue[float], gamma[float], levels(int), flip([0,1]}") setoutletassist(0, "To jit.matrix: always outputs the color palette as a ARGB 4-plane matrix"); setoutletassist(1, "To jit.matrix: if 'hslenable 1' outputs the color palette as a AHSL 4-plane matrix"); setoutletassist(2, "If 'listmode 1' outputs all colors in list format as RGB or HSL."); setoutletassist(3, "Bangs when generation is done."); var hslmode = false; var listmode = false; function hslenable(s){ hslmode = s; } function listenable(s){ listmode = s; } function makeCubehelix(start, rots, hue, gamma, levels, flip){ //dumpOut(start, rots, hue, gamma, levels, flip); outlet(RGBOUTLET, "dim", levels, 1); if (hslmode) { outlet(HSLOUTLET, "dim", levels, 1); } for (var i = 0; i < levels; i++) { var fract = CubeHelixFract(i,levels,flip); var color = CubeHelixRGB(fract,start,rots,hue,gamma); if(flip == 1){fract = 1.0-fract;} outlet(RGBOUTLET, "setcell", i, 0, "val", 1., color); if (hslmode) { color = rgb2hsl(color[0], color[1], color[2]); outlet(HSLOUTLET, "setcell", i, 0, "val", 1., color); } if (listmode) { outlet(LISTOUTLET, i, color); } } outlet(RGBOUTLET, "bang"); if (hslmode) { outlet(HSLOUTLET, "bang"); } if (listmode) { outlet(LISTOUTLET, "bang"); } outlet(DUMPOUTLET, "bang"); } function dumpOut(start, rots, hue, gamma, levels, flip){ outlet(DUMPOUTLET, "start", start); outlet(DUMPOUTLET, "rots", rots); outlet(DUMPOUTLET, "hue", hue); outlet(DUMPOUTLET, "gamma", gamma); outlet(DUMPOUTLET, "levels", levels); outlet(DUMPOUTLET, "flip", flip); } function rgb2hsl(r,g,b) { var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if (max == min) { h = s = 0; // achromatic } else { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [ h, s, l ]; } // ---------------------------------------------------------------------------- // 2017 May 30: make consistent with Fortran // 2021 mai 12: a bit of calculation optimization (I think) - Théophile Clet // ---------------------------------------------------------------------------- function CubeHelixRGB(fract,start,rots,hue,gamma){ var angle = 2*Math.PI*(start/3.0+1+rots*fract); var fract = Math.pow(fract, gamma); var amp=hue*fract*(1-fract)/2.0; var acos = Math.cos(angle); var asin = Math.sin(angle); var r=fract+amp*(-0.14861*acos+1.78277*asin); r=Math.max(Math.min(r,1.0),0.0); var g=fract+amp*(-0.29227*acos-0.90649*asin); g=Math.max(Math.min(g,1.0),0.0); var b=fract+amp*(+1.97294*acos); b=Math.max(Math.min(b,1.0),0.0); return [r, g, b]; } // ---------------------------------------------------------------------------- function CubeHelixFract(i,n,flip){ var fraction = i/(n-1); if (flip == 1) { fraction = 1 - fraction; } return fraction; }