Palette.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.color.Palette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.color.Palette"] = true;
  8. dojo.provide("dojox.color.Palette");
  9. dojo.require("dojox.color");
  10. (function(){
  11. var dxc = dojox.color;
  12. /***************************************************************
  13. * dojox.color.Palette
  14. *
  15. * The Palette object is loosely based on the color palettes
  16. * at Kuler (http://kuler.adobe.com). They are 5 color palettes
  17. * with the base color considered to be the third color in the
  18. * palette (for generation purposes).
  19. *
  20. * Palettes can be generated from well-known algorithms or they
  21. * can be manually created by passing an array to the constructor.
  22. *
  23. * Palettes can be transformed, using a set of specific params
  24. * similar to the way shapes can be transformed with dojox.gfx.
  25. * However, unlike with transformations in dojox.gfx, transforming
  26. * a palette will return you a new Palette object, in effect
  27. * a clone of the original.
  28. ***************************************************************/
  29. // ctor ----------------------------------------------------------------------------
  30. dxc.Palette = function(/* String|Array|dojox.color.Color|dojox.color.Palette */base){
  31. // summary:
  32. // An object that represents a palette of colors.
  33. // description:
  34. // A Palette is a representation of a set of colors. While the standard
  35. // number of colors contained in a palette is 5, it can really handle any
  36. // number of colors.
  37. //
  38. // A palette is useful for the ability to transform all the colors in it
  39. // using a simple object-based approach. In addition, you can generate
  40. // palettes using dojox.color.Palette.generate; these generated palettes
  41. // are based on the palette generators at http://kuler.adobe.com.
  42. //
  43. // colors: dojox.color.Color[]
  44. // The actual color references in this palette.
  45. this.colors = [];
  46. if(base instanceof dojox.color.Palette){
  47. this.colors = base.colors.slice(0);
  48. }
  49. else if(base instanceof dojox.color.Color){
  50. this.colors = [ null, null, base, null, null ];
  51. }
  52. else if(dojo.isArray(base)){
  53. this.colors = dojo.map(base.slice(0), function(item){
  54. if(dojo.isString(item)){ return new dojox.color.Color(item); }
  55. return item;
  56. });
  57. }
  58. else if (dojo.isString(base)){
  59. this.colors = [ null, null, new dojox.color.Color(base), null, null ];
  60. }
  61. }
  62. // private functions ---------------------------------------------------------------
  63. // transformations
  64. function tRGBA(p, param, val){
  65. var ret = new dojox.color.Palette();
  66. ret.colors = [];
  67. dojo.forEach(p.colors, function(item){
  68. var r=(param=="dr")?item.r+val:item.r,
  69. g=(param=="dg")?item.g+val:item.g,
  70. b=(param=="db")?item.b+val:item.b,
  71. a=(param=="da")?item.a+val:item.a
  72. ret.colors.push(new dojox.color.Color({
  73. r: Math.min(255, Math.max(0, r)),
  74. g: Math.min(255, Math.max(0, g)),
  75. b: Math.min(255, Math.max(0, b)),
  76. a: Math.min(1, Math.max(0, a))
  77. }));
  78. });
  79. return ret;
  80. }
  81. function tCMY(p, param, val){
  82. var ret = new dojox.color.Palette();
  83. ret.colors = [];
  84. dojo.forEach(p.colors, function(item){
  85. var o=item.toCmy(),
  86. c=(param=="dc")?o.c+val:o.c,
  87. m=(param=="dm")?o.m+val:o.m,
  88. y=(param=="dy")?o.y+val:o.y;
  89. ret.colors.push(dojox.color.fromCmy(
  90. Math.min(100, Math.max(0, c)),
  91. Math.min(100, Math.max(0, m)),
  92. Math.min(100, Math.max(0, y))
  93. ));
  94. });
  95. return ret;
  96. }
  97. function tCMYK(p, param, val){
  98. var ret = new dojox.color.Palette();
  99. ret.colors = [];
  100. dojo.forEach(p.colors, function(item){
  101. var o=item.toCmyk(),
  102. c=(param=="dc")?o.c+val:o.c,
  103. m=(param=="dm")?o.m+val:o.m,
  104. y=(param=="dy")?o.y+val:o.y,
  105. k=(param=="dk")?o.b+val:o.b;
  106. ret.colors.push(dojox.color.fromCmyk(
  107. Math.min(100, Math.max(0, c)),
  108. Math.min(100, Math.max(0, m)),
  109. Math.min(100, Math.max(0, y)),
  110. Math.min(100, Math.max(0, k))
  111. ));
  112. });
  113. return ret;
  114. }
  115. function tHSL(p, param, val){
  116. var ret = new dojox.color.Palette();
  117. ret.colors = [];
  118. dojo.forEach(p.colors, function(item){
  119. var o=item.toHsl(),
  120. h=(param=="dh")?o.h+val:o.h,
  121. s=(param=="ds")?o.s+val:o.s,
  122. l=(param=="dl")?o.l+val:o.l;
  123. ret.colors.push(dojox.color.fromHsl(h%360, Math.min(100, Math.max(0, s)), Math.min(100, Math.max(0, l))));
  124. });
  125. return ret;
  126. }
  127. function tHSV(p, param, val){
  128. var ret = new dojox.color.Palette();
  129. ret.colors = [];
  130. dojo.forEach(p.colors, function(item){
  131. var o=item.toHsv(),
  132. h=(param=="dh")?o.h+val:o.h,
  133. s=(param=="ds")?o.s+val:o.s,
  134. v=(param=="dv")?o.v+val:o.v;
  135. ret.colors.push(dojox.color.fromHsv(h%360, Math.min(100, Math.max(0, s)), Math.min(100, Math.max(0, v))));
  136. });
  137. return ret;
  138. }
  139. // helper functions
  140. function rangeDiff(val, low, high){
  141. // given the value in a range from 0 to high, find the equiv
  142. // using the range low to high.
  143. return high-((high-val)*((high-low)/high));
  144. }
  145. // object methods ---------------------------------------------------------------
  146. dojo.extend(dxc.Palette, {
  147. transform: function(/* dojox.color.Palette.__transformArgs */kwArgs){
  148. // summary:
  149. // Transform the palette using a specific transformation function
  150. // and a set of transformation parameters.
  151. // description:
  152. // {palette}.transform is a simple way to uniformly transform
  153. // all of the colors in a palette using any of 5 formulae:
  154. // RGBA, HSL, HSV, CMYK or CMY.
  155. //
  156. // Once the forumula to be used is determined, you can pass any
  157. // number of parameters based on the formula "d"[param]; for instance,
  158. // { use: "rgba", dr: 20, dg: -50 } will take all of the colors in
  159. // palette, add 20 to the R value and subtract 50 from the G value.
  160. //
  161. // Unlike other types of transformations, transform does *not* alter
  162. // the original palette but will instead return a new one.
  163. var fn=tRGBA; // the default transform function.
  164. if(kwArgs.use){
  165. // we are being specific about the algo we want to use.
  166. var use=kwArgs.use.toLowerCase();
  167. if(use.indexOf("hs")==0){
  168. if(use.charAt(2)=="l"){ fn=tHSL; }
  169. else { fn=tHSV; }
  170. }
  171. else if(use.indexOf("cmy")==0){
  172. if(use.charAt(3)=="k"){ fn=tCMYK; }
  173. else { fn=tCMY; }
  174. }
  175. }
  176. // try to guess the best choice.
  177. else if("dc" in kwArgs || "dm" in kwArgs || "dy" in kwArgs){
  178. if("dk" in kwArgs){ fn = tCMYK; }
  179. else { fn = tCMY; }
  180. }
  181. else if("dh" in kwArgs || "ds" in kwArgs){
  182. if("dv" in kwArgs){ fn = tHSV; }
  183. else { fn = tHSL; }
  184. }
  185. var palette = this;
  186. for(var p in kwArgs){
  187. // ignore use
  188. if(p=="use"){ continue; }
  189. palette = fn(palette, p, kwArgs[p]);
  190. }
  191. return palette; // dojox.color.Palette
  192. },
  193. clone: function(){
  194. // summary:
  195. // Clones the current palette.
  196. return new dxc.Palette(this); // dojox.color.Palette
  197. }
  198. });
  199. /*=====
  200. dojox.color.Palette.__transformArgs = function(use, dr, dg, db, da, dc, dm, dy, dk, dh, ds, dv, dl){
  201. // summary:
  202. // The keywords argument to be passed to the dojox.color.Palette.transform function. Note that
  203. // while all arguments are optional, *some* arguments must be passed. The basic concept is that
  204. // you pass a delta value for a specific aspect of a color model (or multiple aspects of the same
  205. // color model); for instance, if you wish to transform a palette based on the HSV color model,
  206. // you would pass one of "dh", "ds", or "dv" as a value.
  207. //
  208. // use: String?
  209. // Specify the color model to use for the transformation. Can be "rgb", "rgba", "hsv", "hsl", "cmy", "cmyk".
  210. // dr: Number?
  211. // The delta to be applied to the red aspect of the RGB/RGBA color model.
  212. // dg: Number?
  213. // The delta to be applied to the green aspect of the RGB/RGBA color model.
  214. // db: Number?
  215. // The delta to be applied to the blue aspect of the RGB/RGBA color model.
  216. // da: Number?
  217. // The delta to be applied to the alpha aspect of the RGBA color model.
  218. // dc: Number?
  219. // The delta to be applied to the cyan aspect of the CMY/CMYK color model.
  220. // dm: Number?
  221. // The delta to be applied to the magenta aspect of the CMY/CMYK color model.
  222. // dy: Number?
  223. // The delta to be applied to the yellow aspect of the CMY/CMYK color model.
  224. // dk: Number?
  225. // The delta to be applied to the black aspect of the CMYK color model.
  226. // dh: Number?
  227. // The delta to be applied to the hue aspect of the HSL/HSV color model.
  228. // ds: Number?
  229. // The delta to be applied to the saturation aspect of the HSL/HSV color model.
  230. // dl: Number?
  231. // The delta to be applied to the luminosity aspect of the HSL color model.
  232. // dv: Number?
  233. // The delta to be applied to the value aspect of the HSV color model.
  234. this.use = use;
  235. this.dr = dr;
  236. this.dg = dg;
  237. this.db = db;
  238. this.da = da;
  239. this.dc = dc;
  240. this.dm = dm;
  241. this.dy = dy;
  242. this.dk = dk;
  243. this.dh = dh;
  244. this.ds = ds;
  245. this.dl = dl;
  246. this.dv = dv;
  247. }
  248. dojox.color.Palette.__generatorArgs = function(base){
  249. // summary:
  250. // The keyword arguments object used to create a palette based on a base color.
  251. //
  252. // base: dojo.Color
  253. // The base color to be used to generate the palette.
  254. this.base = base;
  255. }
  256. dojox.color.Palette.__analogousArgs = function(base, high, low){
  257. // summary:
  258. // The keyword arguments object that is used to create a 5 color palette based on the
  259. // analogous rules as implemented at http://kuler.adobe.com, using the HSV color model.
  260. //
  261. // base: dojo.Color
  262. // The base color to be used to generate the palette.
  263. // high: Number?
  264. // The difference between the hue of the base color and the highest hue. In degrees, default is 60.
  265. // low: Number?
  266. // The difference between the hue of the base color and the lowest hue. In degrees, default is 18.
  267. this.base = base;
  268. this.high = high;
  269. this.low = low;
  270. }
  271. dojox.color.Palette.__splitComplementaryArgs = function(base, da){
  272. // summary:
  273. // The keyword arguments object used to create a palette based on the split complementary rules
  274. // as implemented at http://kuler.adobe.com.
  275. //
  276. // base: dojo.Color
  277. // The base color to be used to generate the palette.
  278. // da: Number?
  279. // The delta angle to be used to determine where the split for the complementary rules happen.
  280. // In degrees, the default is 30.
  281. this.base = base;
  282. this.da = da;
  283. }
  284. =====*/
  285. dojo.mixin(dxc.Palette, {
  286. generators: {
  287. analogous:function(/* dojox.color.Palette.__analogousArgs */args){
  288. // summary:
  289. // Create a 5 color palette based on the analogous rules as implemented at
  290. // http://kuler.adobe.com.
  291. var high=args.high||60, // delta between base hue and highest hue (subtracted from base)
  292. low=args.low||18, // delta between base hue and lowest hue (added to base)
  293. base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  294. hsv=base.toHsv();
  295. // generate our hue angle differences
  296. var h=[
  297. (hsv.h+low+360)%360,
  298. (hsv.h+Math.round(low/2)+360)%360,
  299. hsv.h,
  300. (hsv.h-Math.round(high/2)+360)%360,
  301. (hsv.h-high+360)%360
  302. ];
  303. var s1=Math.max(10, (hsv.s<=95)?hsv.s+5:(100-(hsv.s-95))),
  304. s2=(hsv.s>1)?hsv.s-1:21-hsv.s,
  305. v1=(hsv.v>=92)?hsv.v-9:Math.max(hsv.v+9, 20),
  306. v2=(hsv.v<=90)?Math.max(hsv.v+5, 20):(95+Math.ceil((hsv.v-90)/2)),
  307. s=[ s1, s2, hsv.s, s1, s1 ],
  308. v=[ v1, v2, hsv.v, v1, v2 ]
  309. return new dxc.Palette(dojo.map(h, function(hue, i){
  310. return dojox.color.fromHsv(hue, s[i], v[i]);
  311. })); // dojox.color.Palette
  312. },
  313. monochromatic: function(/* dojox.color.Palette.__generatorArgs */args){
  314. // summary:
  315. // Create a 5 color palette based on the monochromatic rules as implemented at
  316. // http://kuler.adobe.com.
  317. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  318. hsv = base.toHsv();
  319. // figure out the saturation and value
  320. var s1 = (hsv.s-30>9)?hsv.s-30:hsv.s+30,
  321. s2 = hsv.s,
  322. v1 = rangeDiff(hsv.v, 20, 100),
  323. v2 = (hsv.v-20>20)?hsv.v-20:hsv.v+60,
  324. v3 = (hsv.v-50>20)?hsv.v-50:hsv.v+30;
  325. return new dxc.Palette([
  326. dojox.color.fromHsv(hsv.h, s1, v1),
  327. dojox.color.fromHsv(hsv.h, s2, v3),
  328. base,
  329. dojox.color.fromHsv(hsv.h, s1, v3),
  330. dojox.color.fromHsv(hsv.h, s2, v2)
  331. ]); // dojox.color.Palette
  332. },
  333. triadic: function(/* dojox.color.Palette.__generatorArgs */args){
  334. // summary:
  335. // Create a 5 color palette based on the triadic rules as implemented at
  336. // http://kuler.adobe.com.
  337. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  338. hsv = base.toHsv();
  339. var h1 = (hsv.h+57+360)%360,
  340. h2 = (hsv.h-157+360)%360,
  341. s1 = (hsv.s>20)?hsv.s-10:hsv.s+10,
  342. s2 = (hsv.s>90)?hsv.s-10:hsv.s+10,
  343. s3 = (hsv.s>95)?hsv.s-5:hsv.s+5,
  344. v1 = (hsv.v-20>20)?hsv.v-20:hsv.v+20,
  345. v2 = (hsv.v-30>20)?hsv.v-30:hsv.v+30,
  346. v3 = (hsv.v-30>70)?hsv.v-30:hsv.v+30;
  347. return new dxc.Palette([
  348. dojox.color.fromHsv(h1, s1, hsv.v),
  349. dojox.color.fromHsv(hsv.h, s2, v2),
  350. base,
  351. dojox.color.fromHsv(h2, s2, v1),
  352. dojox.color.fromHsv(h2, s3, v3)
  353. ]); // dojox.color.Palette
  354. },
  355. complementary: function(/* dojox.color.Palette.__generatorArgs */args){
  356. // summary:
  357. // Create a 5 color palette based on the complementary rules as implemented at
  358. // http://kuler.adobe.com.
  359. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  360. hsv = base.toHsv();
  361. var h1 = ((hsv.h*2)+137<360)?(hsv.h*2)+137:Math.floor(hsv.h/2)-137,
  362. s1 = Math.max(hsv.s-10, 0),
  363. s2 = rangeDiff(hsv.s, 10, 100),
  364. s3 = Math.min(100, hsv.s+20),
  365. v1 = Math.min(100, hsv.v+30),
  366. v2 = (hsv.v>20)?hsv.v-30:hsv.v+30;
  367. return new dxc.Palette([
  368. dojox.color.fromHsv(hsv.h, s1, v1),
  369. dojox.color.fromHsv(hsv.h, s2, v2),
  370. base,
  371. dojox.color.fromHsv(h1, s3, v2),
  372. dojox.color.fromHsv(h1, hsv.s, hsv.v)
  373. ]); // dojox.color.Palette
  374. },
  375. splitComplementary: function(/* dojox.color.Palette.__splitComplementaryArgs */args){
  376. // summary:
  377. // Create a 5 color palette based on the split complementary rules as implemented at
  378. // http://kuler.adobe.com.
  379. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  380. dangle = args.da || 30,
  381. hsv = base.toHsv();
  382. var baseh = ((hsv.h*2)+137<360)?(hsv.h*2)+137:Math.floor(hsv.h/2)-137,
  383. h1 = (baseh-dangle+360)%360,
  384. h2 = (baseh+dangle)%360,
  385. s1 = Math.max(hsv.s-10, 0),
  386. s2 = rangeDiff(hsv.s, 10, 100),
  387. s3 = Math.min(100, hsv.s+20),
  388. v1 = Math.min(100, hsv.v+30),
  389. v2 = (hsv.v>20)?hsv.v-30:hsv.v+30;
  390. return new dxc.Palette([
  391. dojox.color.fromHsv(h1, s1, v1),
  392. dojox.color.fromHsv(h1, s2, v2),
  393. base,
  394. dojox.color.fromHsv(h2, s3, v2),
  395. dojox.color.fromHsv(h2, hsv.s, hsv.v)
  396. ]); // dojox.color.Palette
  397. },
  398. compound: function(/* dojox.color.Palette.__generatorArgs */args){
  399. // summary:
  400. // Create a 5 color palette based on the compound rules as implemented at
  401. // http://kuler.adobe.com.
  402. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  403. hsv = base.toHsv();
  404. var h1 = ((hsv.h*2)+18<360)?(hsv.h*2)+18:Math.floor(hsv.h/2)-18,
  405. h2 = ((hsv.h*2)+120<360)?(hsv.h*2)+120:Math.floor(hsv.h/2)-120,
  406. h3 = ((hsv.h*2)+99<360)?(hsv.h*2)+99:Math.floor(hsv.h/2)-99,
  407. s1 = (hsv.s-40>10)?hsv.s-40:hsv.s+40,
  408. s2 = (hsv.s-10>80)?hsv.s-10:hsv.s+10,
  409. s3 = (hsv.s-25>10)?hsv.s-25:hsv.s+25,
  410. v1 = (hsv.v-40>10)?hsv.v-40:hsv.v+40,
  411. v2 = (hsv.v-20>80)?hsv.v-20:hsv.v+20,
  412. v3 = Math.max(hsv.v, 20);
  413. return new dxc.Palette([
  414. dojox.color.fromHsv(h1, s1, v1),
  415. dojox.color.fromHsv(h1, s2, v2),
  416. base,
  417. dojox.color.fromHsv(h2, s3, v3),
  418. dojox.color.fromHsv(h3, s2, v2)
  419. ]); // dojox.color.Palette
  420. },
  421. shades: function(/* dojox.color.Palette.__generatorArgs */args){
  422. // summary:
  423. // Create a 5 color palette based on the shades rules as implemented at
  424. // http://kuler.adobe.com.
  425. var base = dojo.isString(args.base)?new dojox.color.Color(args.base):args.base,
  426. hsv = base.toHsv();
  427. var s = (hsv.s==100 && hsv.v==0)?0:hsv.s,
  428. v1 = (hsv.v-50>20)?hsv.v-50:hsv.v+30,
  429. v2 = (hsv.v-25>=20)?hsv.v-25:hsv.v+55,
  430. v3 = (hsv.v-75>=20)?hsv.v-75:hsv.v+5,
  431. v4 = Math.max(hsv.v-10, 20);
  432. return new dxc.Palette([
  433. new dojox.color.fromHsv(hsv.h, s, v1),
  434. new dojox.color.fromHsv(hsv.h, s, v2),
  435. base,
  436. new dojox.color.fromHsv(hsv.h, s, v3),
  437. new dojox.color.fromHsv(hsv.h, s, v4)
  438. ]); // dojox.color.Palette
  439. }
  440. },
  441. generate: function(/* String|dojox.color.Color */base, /* Function|String */type){
  442. // summary:
  443. // Generate a new Palette using any of the named functions in
  444. // dojox.color.Palette.generators or an optional function definition. Current
  445. // generators include "analogous", "monochromatic", "triadic", "complementary",
  446. // "splitComplementary", and "shades".
  447. if(dojo.isFunction(type)){
  448. return type({ base: base }); // dojox.color.Palette
  449. }
  450. else if(dxc.Palette.generators[type]){
  451. return dxc.Palette.generators[type]({ base: base }); // dojox.color.Palette
  452. }
  453. throw new Error("dojox.color.Palette.generate: the specified generator ('" + type + "') does not exist.");
  454. }
  455. });
  456. })();
  457. }