Palette.js 16 KB

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