joliclic
1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is Khromaxul 15 * 16 * The Initial Developer of the Original Code is 17 * Nicolas Martin. 18 * Portions created by the Initial Developer are Copyright (C) 2009 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * Nicolas Martin <joliclic@gmail.com> 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either the GNU General Public License Version 2 or later (the "GPL"), or 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 if (!kh) var kh = {}; 39 40 /** 41 * @namespace 42 */ 43 kh.schemer = { 44 /** 45 * get the complement color (opposite hue) 46 * @param {RGB} aRGB 47 * @returns {RGB} 48 */ 49 getComplement: function kh_schemer_getComplement(aRGB) { 50 var hsv = aRGB.toHSV(); 51 if (hsv.h < 180) 52 hsv.h += 180; 53 else 54 hsv.h -= 180; 55 56 return hsv.toRGB(); 57 }, 58 59 /** 60 * get the negative color (opposite r, g, and b) 61 * @param {RGB} aRGB 62 * @returns {RGB} 63 */ 64 getNegative: function kh_schemer_getNegative(aRGB) { 65 var r = Math.abs(aRGB.r - 255); 66 var g = Math.abs(aRGB.g - 255); 67 var b = Math.abs(aRGB.b - 255); 68 69 return new kh.color.RGB(r, g, b); 70 }, 71 72 /** 73 * get triads color. Note that if alpha = 150, it's the split complement 74 * colors, and if alpha = 30, it's analogous colors. 75 * @param {RGB} aRGB 76 * @param {Number} aAlpha in [0, 180]. Optional, default = 120 77 * @returns {Array of RGB} length = 2 78 */ 79 getTriads: function kh_schemer_getTriads(aRGB, aAlpha) { 80 if (aAlpha === undefined) 81 aAlpha = 120; 82 83 if (aAlpha < 0) aAlpha = 0; 84 if (aAlpha > 180) aAlpha = 180; 85 86 var hsv1 = aRGB.toHSV(); 87 hsv1.h = kh.schemer.ensureHue(hsv1.h + aAlpha); 88 var hsv2 = aRGB.toHSV(); 89 hsv2.h = kh.schemer.ensureHue(hsv2.h - aAlpha); 90 91 return [hsv1.toRGB(), hsv2.toRGB()] 92 }, 93 94 /** 95 * get tetradic colors (complement of a color + a color with espaced of 96 * alpha and its complement). 97 * @param {RGB} aRGB 98 * @param {Number} aAlpha in [0, 180]. Optional, default = 90 99 * @returns {Array of RGB} length = 3 100 */ 101 getTetrads: function hs_schemer_getTetrads(aRGB, aAlpha) { 102 if (aAlpha === undefined) 103 aAlpha = 90; 104 105 if (aAlpha < 0) aAlpha = 0; 106 if (aAlpha > 180) aAlpha = 180; 107 108 var hsv = aRGB.toHSV(); 109 hsv.h = kh.schemer.ensureHue(hsv.h + aAlpha); 110 111 var rgb1 = hsv.toRGB(); 112 var rgb2 = kh.schemer.getComplement(aRGB); 113 var rgb3 = kh.schemer.getComplement(rgb1); 114 115 return [rgb1, rgb2, rgb3]; 116 }, 117 118 /** 119 * get a array of associated monochromatic color (same h, s, different l). 120 * @param {RGB} aRGB 121 * @param {Integer} aN Number of returned colors. Optional, default = 3. 122 * @param {Boolean} aReturnRef the aRGB reference is present in the returned 123 * array. Optional, default = true 124 * @param {Float} aK in [0, 1] a coefficient to change the gap of the 125 * luminosity: 1 will returns max of luminosity difference, 0 means 126 * that returned colors are the same of the initial. optional, default 127 * = 1. 128 * @returns {Array of RGB} Note that the array contains the aRGB value 129 */ 130 getMonochromatic: 131 function kh_schemer_getMonochromatic(aRGB, aN, aReturnRef, aK) { 132 if (aN === undefined) aN = 3; 133 if (aReturnRef === undefined) aReturnRef = true; 134 if (aK === undefined) aK = 1; 135 136 if (!aReturnRef) aN++; 137 138 var step = 100 * aK / (aN); 139 140 function inRange(n) { 141 if (n > 100) n -= 100; 142 if (n < 0) n += 100; 143 return n; 144 } 145 146 var mc = []; 147 var j = 0; 148 var cIndex = 0; 149 for (var i = 1; i < aN; i++) { 150 var hsl = aRGB.toHSL(); 151 var l = hsl.l; 152 153 if (i % 2 != 0) { 154 j++; 155 hsl.l = inRange(Math.round(l + j * step)); 156 } else { 157 hsl.l = inRange(Math.round(l - j * step)); 158 } 159 160 if (hsl.l < l) 161 cIndex++; 162 163 mc.push(hsl); 164 } 165 166 function sort(aHSL1, aHSL2) { 167 return (aHSL1.l - aHSL2.l); 168 } 169 mc.sort(sort); 170 171 var cs = []; 172 for (var k = 0, l = mc.length; k < l; k++) { 173 cs.push(mc[k].toRGB()) 174 } 175 176 if (aReturnRef) { 177 // RGB -> HSL -> RGB can be a little different because of the 178 // mathematical conversion, it's better to put the initial aRGB value 179 var rgb = new kh.color.RGB(aRGB.r, aRGB.g, aRGB.b); 180 cs.splice(cIndex, 0, rgb); 181 } 182 183 return cs; 184 }, 185 186 /** 187 * get an array of colors between 2 colors. 188 * @param {RGB} aRGB1 189 * @param {RGB} aRGB2 190 * @param {Integer} aN number of returned colors 191 * @returns {Array of RGB} 192 */ 193 getGradients: function kh_schemer_getGradient(aRGB1, aRGB2, aN) { 194 //var stepR = Math.abs((aRGB1.r - aRGB2.r) / (aN + 1)); 195 //var stepG = Math.abs((aRGB1.g - aRGB2.g) / (aN + 1)); 196 //var stepB = Math.abs((aRGB1.b - aRGB2.b) / (aN + 1)); 197 var stepR = (aRGB2.r - aRGB1.r) / (aN + 1); 198 var stepG = (aRGB2.g - aRGB1.g) / (aN + 1); 199 var stepB = (aRGB2.b - aRGB1.b) / (aN + 1); 200 //alert(stepR + "\n" + stepG + "\n" + stepB + "\n") 201 202 var colors = []; 203 for (var i = 1; i <= aN; i++) { 204 //alert((aRGB1.r + i * stepR) + "\n" + 205 // (aRGB1.g + i * stepG) + "\n" + 206 // (aRGB1.b + i * stepB) + "\n") 207 colors.push(new kh.color.RGB(aRGB1.r + i * stepR, 208 aRGB1.g + i * stepG, 209 aRGB1.b + i * stepB)); 210 } 211 212 return colors; 213 }, 214 215 /** 216 * get a array of equally espaced tint variations of a color, 217 * i.e. adding white to the color. 218 * @param {RGB} aRGB the color reference. Note that this color is not part 219 * of the returned array. 220 * @param {Integer} aN number of returned tints 221 * @returns {Array of RGB} It doesn't contain the aRGB value. 222 */ 223 getTints: function kh_schemer_getTints(aRGB, aN) { 224 return kh.schemer.getGradients(aRGB, new kh.color.RGB(255, 255, 255), aN); 225 }, 226 227 /** 228 * get a array of equally espaced shade variations of a color, 229 * i.e. adding black to the color. 230 * @param {RGB} aRGB the color reference. Note that this color is not part 231 * of the returned array. 232 * @param {Integer} aN number of returned shades 233 * @returns {Array of RGB} It doesn't contain the aRGB value. 234 */ 235 getShades: function kh_schemer_getShades(aRGB, aN) { 236 return kh.schemer.getGradients(aRGB, new kh.color.RGB(0, 0, 0), aN); 237 }, 238 239 /** 240 * get a array of equally espaced tone variations of a color, 241 * i.e. adding gray to the color. 242 * @param {RGB} aRGB the color reference. Note that this color is not part 243 * of the returned array. 244 * @param {Integer} aN number of returned tones 245 * @returns {Array of RGB} It doesn't contain the aRGB value. 246 */ 247 getTones: function kh_schemer_getTones(aRGB, aN) { 248 return kh.schemer.getGradients(aRGB, new kh.color.RGB(128, 128, 128), aN); 249 }, 250 251 /** 252 * return an angle in [0, 360] 253 * @param {Number} aH 254 * @return {Number} 255 */ 256 ensureHue: function kh_schemer_ensureHue(aH) { 257 //if (aH > 360) aH -= 360; 258 //if (aH < 0) aH += 360; 259 while (aH > 360) aH -= 360; 260 while (aH < 0) aH += 360; 261 return aH ; 262 } 263 } 264