joliclic

home

  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