pixie_dust 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README +16 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/game.js +9200 -0
  8. data/lib/corelib.js +3331 -0
  9. data/lib/pixie_dust/version.rb +3 -0
  10. data/lib/pixie_dust.rb +14 -0
  11. data/pixie.json +15 -0
  12. data/pixie_dust.gemspec +29 -0
  13. data/source/active_bounds.coffee +48 -0
  14. data/source/ageable.coffee +23 -0
  15. data/source/bounded.coffee +282 -0
  16. data/source/camera.coffee +138 -0
  17. data/source/camera.fade.coffee +69 -0
  18. data/source/camera.flash.coffee +69 -0
  19. data/source/camera.rotate.coffee +11 -0
  20. data/source/camera.shake.coffee +27 -0
  21. data/source/camera.zoom.coffee +25 -0
  22. data/source/camera.zsort.coffee +13 -0
  23. data/source/clampable.coffee +61 -0
  24. data/source/collidable.coffee +126 -0
  25. data/source/collision.coffee +272 -0
  26. data/source/collision_response.coffee +28 -0
  27. data/source/color.coffee +1113 -0
  28. data/source/color_table.coffee +2534 -0
  29. data/source/controllable.coffee +66 -0
  30. data/source/cooldown.coffee +82 -0
  31. data/source/debuggable.coffee +253 -0
  32. data/source/drawable.coffee +167 -0
  33. data/source/dust_emitter.coffee +36 -0
  34. data/source/easing.coffee +38 -0
  35. data/source/emitter.coffee +7 -0
  36. data/source/emitterable.coffee +68 -0
  37. data/source/engine.coffee +274 -0
  38. data/source/engine.collision.coffee +77 -0
  39. data/source/engine.data.coffee +23 -0
  40. data/source/engine.delay.coffee +41 -0
  41. data/source/engine.fps_counter.coffee +32 -0
  42. data/source/engine.game_state.coffee +86 -0
  43. data/source/engine.joysticks.coffee +47 -0
  44. data/source/engine.keyboard.coffee +17 -0
  45. data/source/engine.levels.coffee +69 -0
  46. data/source/engine.mouse.coffee +16 -0
  47. data/source/engine.selector.coffee +166 -0
  48. data/source/engine.stats.coffee +16 -0
  49. data/source/engine.tilemap.coffee +41 -0
  50. data/source/engine_background.coffee +32 -0
  51. data/source/expirable.coffee +47 -0
  52. data/source/flickerable.coffee +78 -0
  53. data/source/follow.coffee +65 -0
  54. data/source/framerate.coffee +42 -0
  55. data/source/game_object.coffee +181 -0
  56. data/source/game_object.effect.coffee +33 -0
  57. data/source/game_object.meter.coffee +191 -0
  58. data/source/game_over.coffee +40 -0
  59. data/source/game_state.coffee +67 -0
  60. data/source/game_state.save_state.coffee +76 -0
  61. data/source/game_state.single_camera.coffee +40 -0
  62. data/source/game_state_cameras.coffee +33 -0
  63. data/source/level_state.coffee +32 -0
  64. data/source/movable.coffee +57 -0
  65. data/source/oscillator.coffee +18 -0
  66. data/source/pixie_dust.coffee +2 -0
  67. data/source/resource_loader.coffee +35 -0
  68. data/source/rotatable.coffee +38 -0
  69. data/source/sprite.coffee +181 -0
  70. data/source/text_effect.coffee +74 -0
  71. data/source/text_effect.floating.coffee +22 -0
  72. data/source/text_screen.coffee +38 -0
  73. data/source/tilemap.coffee +56 -0
  74. data/source/timed_events.coffee +78 -0
  75. data/source/title_screen.coffee +38 -0
  76. data/source/tween.coffee +70 -0
  77. data/test/active_bounds.coffee +67 -0
  78. data/test/bounded.coffee +98 -0
  79. data/test/camera.coffee +29 -0
  80. data/test/clampable.coffee +18 -0
  81. data/test/collidable.coffee +51 -0
  82. data/test/collision.coffee +70 -0
  83. data/test/color.coffee +533 -0
  84. data/test/controllable.coffee +108 -0
  85. data/test/cooldown.coffee +116 -0
  86. data/test/debuggable.coffee +71 -0
  87. data/test/drawable.coffee +31 -0
  88. data/test/emitter.coffee +0 -0
  89. data/test/emitterable.coffee +15 -0
  90. data/test/engine.coffee +228 -0
  91. data/test/engine_data.coffee +12 -0
  92. data/test/engine_delay.coffee +14 -0
  93. data/test/engine_selector.coffee +100 -0
  94. data/test/expirable.coffee +35 -0
  95. data/test/flickerable.coffee +51 -0
  96. data/test/follow.coffee +34 -0
  97. data/test/game_object.coffee +78 -0
  98. data/test/game_object_effect.coffee +17 -0
  99. data/test/metered.coffee +33 -0
  100. data/test/movable.coffee +46 -0
  101. data/test/oscillator.coffee +28 -0
  102. data/test/resource_loader.coffee +7 -0
  103. data/test/rotatable.coffee +20 -0
  104. data/test/sprite.coffee +21 -0
  105. data/test/text.coffee +25 -0
  106. data/test/timed_events.coffee +23 -0
  107. data/test/tweening.coffee +18 -0
  108. metadata +233 -0
@@ -0,0 +1,1113 @@
1
+ ( ->
2
+ rgbParser = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),?\s*(\d?\.?\d*)?\)$/
3
+ hslParser = /^hsla?\((\d{1,3}),\s*(\d?\.?\d*),\s*(\d?\.?\d*),?\s*(\d?\.?\d*)?\)$/
4
+
5
+ parseRGB = (colorString) ->
6
+ return undefined unless channels = rgbParser.exec(colorString)
7
+
8
+ parsedColor = (parseFloat channel for channel in channels[1..4])
9
+
10
+ parsedColor[3] = 1 if isNaN(parsedColor[3])
11
+
12
+ return parsedColor
13
+
14
+ parseHSL = (colorString) ->
15
+ return undefined unless channels = hslParser.exec(colorString)
16
+
17
+ parsedColor = (parseFloat channel for channel in channels[1..4])
18
+
19
+ parsedColor[3] = 1 if isNaN(parsedColor[3])
20
+
21
+ return hslToRgb(parsedColor)
22
+
23
+ hsvToRgb = (hsv) ->
24
+ r = g = b = null
25
+
26
+ [h, s, v, a] = hsv
27
+
28
+ a = 1 unless a?
29
+
30
+ i = (h / 60).floor()
31
+ f = h / 60 - i
32
+ p = v * (1 - s)
33
+ q = v * (1 - f * s)
34
+ t = v * (1 - (1 - f) * s)
35
+
36
+ switch (i % 6)
37
+ when 0
38
+ r = v
39
+ g = t
40
+ b = p
41
+ when 1
42
+ r = q
43
+ g = v
44
+ b = p
45
+ when 2
46
+ r = p
47
+ g = v
48
+ b = t
49
+ when 3
50
+ r = p
51
+ g = q
52
+ b = v
53
+ when 4
54
+ r = t
55
+ g = p
56
+ b = v
57
+ when 5
58
+ r = v
59
+ g = p
60
+ b = q
61
+
62
+ rgb = [(r * 255).round(), (g * 255).round(), (b * 255).round()]
63
+
64
+ return rgb.concat(a)
65
+
66
+ hslToRgb = (hsl) ->
67
+ [h, s, l, a] = hsl
68
+
69
+ h = h % 360
70
+ a = 1 unless a?
71
+
72
+ r = g = b = null
73
+
74
+ hueToRgb = (p, q, hue) ->
75
+ hue = hue.mod(360)
76
+
77
+ return p + (q - p) * (hue / 60) if hue < 60
78
+ return q if hue < 180
79
+ return p + (q - p) * ((240 - hue) / 60) if hue < 240
80
+ return p
81
+
82
+ if s == 0
83
+ r = g = b = l
84
+ else
85
+ q = (if l < 0.5 then l * (1 + s) else l + s - l * s)
86
+ p = 2 * l - q
87
+ r = hueToRgb(p, q, h + 120)
88
+ g = hueToRgb(p, q, h)
89
+ b = hueToRgb(p, q, h - 120)
90
+
91
+ rgbMap = ((channel * 255).round() for channel in [r, g, b])
92
+
93
+ return rgbMap.concat(a)
94
+
95
+ channelize = (color, alpha) ->
96
+ return color.channels() if color.channels?
97
+ if Object.isArray color
98
+ if alpha?
99
+ alpha = parseFloat(alpha)
100
+ else if color[3]?
101
+ alpha = parseFloat(color[3])
102
+ else
103
+ alpha = 1
104
+
105
+ result = (parseFloat(channel) for channel in color[0..2]).concat(alpha)
106
+ else
107
+ result = Color.lookup?(color) || color.parseHex() || parseRGB(color) || parseHSL(color)
108
+
109
+ if alpha?
110
+ result[3] = parseFloat(alpha)
111
+
112
+ return result
113
+
114
+ ###*
115
+ Create a new color. The constructor is very flexible. It accepts individual r, g, b, a values,
116
+ arrays of r, g, b values, hex strings, rgb strings, hsl strings, other Color objects,
117
+ and even the named colors from the xkcd survey: http://blog.xkcd.com/2010/05/03/color-survey-results/.
118
+ If no arguments are given, defaults to transparent.
119
+
120
+ individualRgb = Color(23, 56, 49, 0.4)
121
+
122
+ arrayRgb = Color([59, 100, 230])
123
+
124
+ hex = Color('#ff0000')
125
+
126
+ rgb = Color('rgb(0, 255, 0)')
127
+
128
+ hsl = Color('hsl(180, 1, 0.5)')
129
+
130
+ anotherColor = Color('blue')
131
+
132
+ Color(anotherColor)
133
+ # => a new color with the same r, g, b, and alpha values as `anotherColor`
134
+
135
+ # You have access to all sorts of weird colors.
136
+ # We give you all the named colors the browser recognizes
137
+ # and the ones from this survey
138
+ # http://blog.xkcd.com/2010/05/03/color-survey-results/
139
+ namedBrown = Color('Fuzzy Wuzzy Brown')
140
+
141
+ # Uutput color in Hex format
142
+ namedBrown.toHex()
143
+ # => '#c45655'
144
+
145
+ # Default behavior
146
+ transparent = Color()
147
+
148
+ transparent.toString()
149
+ # => 'rgba(0, 0, 0, 0)'
150
+
151
+ # let's print out the colors on a canvas to see what they look like
152
+ canvas.font('14px Helvetica')
153
+ for color, index in ['individualRgb', 'arrayRgb', 'hex', 'rgb', 'hsl', 'anotherColor', 'namedBrown']
154
+ canvas.centerText
155
+ color: eval(color)
156
+ text: color
157
+ y: 20 * (index + 1)
158
+
159
+ @name Color
160
+ @param {Array|Number|String|Color} args... An Array, r, g, b values,
161
+ a sequence of numbers defining r, g, b values, a hex or hsl string, another Color object, or a named color
162
+ @constructor
163
+ ###
164
+ Color = (args...) ->
165
+ parsedColor =
166
+ switch args.length
167
+ when 0
168
+ [0, 0, 0, 0]
169
+ when 1
170
+ channelize(args.first())
171
+ when 2
172
+ channelize(args.first(), args.last())
173
+ else
174
+ channelize(args)
175
+
176
+ throw "#{args.join(',')} is an unknown color" unless parsedColor
177
+
178
+ __proto__: Color::
179
+ r: parsedColor[0].round()
180
+ g: parsedColor[1].round()
181
+ b: parsedColor[2].round()
182
+ a: parsedColor[3]
183
+
184
+ Color:: =
185
+ ###*
186
+ Returns the rgba color channels in an array.
187
+
188
+ transparent = Color()
189
+
190
+ transparent.channels()
191
+ # => [0, 0, 0, 0]
192
+
193
+ red = Color("#FF0000")
194
+
195
+ red.channels()
196
+ # => [255, 0, 0, 1]
197
+
198
+ rgb = Color(200, 34, 2)
199
+
200
+ rgb.channels()
201
+ # => [200, 34, 2, 1]
202
+
203
+ @name channels
204
+ @methodOf Color#
205
+
206
+ @returns {Array} Array of r, g, b, and alpha values of the color
207
+ ###
208
+ channels: ->
209
+ [@r, @g, @b, @a]
210
+
211
+ ###*
212
+ A copy of the calling color that is its complementary color on the color wheel.
213
+
214
+ red = Color(255, 0, 0)
215
+
216
+ cyan = red.complement()
217
+
218
+ # to see what they look like
219
+ for color, index in [red, cyan]
220
+ canvas.drawRect
221
+ color: color
222
+ x: 20 + (60 * index)
223
+ y: 20 + (60 * index)
224
+ width: 60
225
+ height: 60
226
+
227
+ @name complement
228
+ @methodOf Color#
229
+
230
+ @returns {Color} new color that is a copy of the calling color with its hue shifted by 180 degrees on the color wheel
231
+ ###
232
+ complement: ->
233
+ @copy().complement$()
234
+
235
+ ###*
236
+ Modifies the calling color to make it the complement of its previous value.
237
+
238
+ red = Color(255, 0, 0)
239
+
240
+ # modifies red in place to make it into cyan
241
+ red.complement$()
242
+
243
+ red.toString()
244
+ # => 'rgba(0, 255, 255, 1)'
245
+
246
+ @name complement$
247
+ @methodOf Color#
248
+
249
+ @returns {Color} the color hue shifted by 180 degrees on the color wheel. Modifies the existing color.
250
+ ###
251
+ complement$: ->
252
+ @shiftHue$(180)
253
+
254
+ ###*
255
+ A copy of the calling color.
256
+
257
+ color = Color(0, 100, 200)
258
+
259
+ copy = color.copy()
260
+
261
+ color == copy
262
+ # => false
263
+
264
+ color.equal(copy)
265
+ # => true
266
+
267
+ @name copy
268
+ @methodOf Color#
269
+
270
+ @returns {Color} A new color. A copy of the calling color
271
+ ###
272
+ copy: ->
273
+ Color(@r, @g, @b, @a)
274
+
275
+ ###*
276
+ Returns a copy of the calling color darkened by `amount` (Lightness of the color ranges from 0 to 1).
277
+
278
+ green = Color(0, 255, 0)
279
+
280
+ darkGreen = green.darken(0.3)
281
+
282
+ # to see what they look like
283
+ for color, index in [green, darkGreen]
284
+ canvas.drawRect
285
+ color: color
286
+ x: 20 + (60 * index)
287
+ y: 20 + (60 * index)
288
+ width: 60
289
+ height: 60
290
+
291
+ @name darken
292
+ @methodOf Color#
293
+ @param {Number} amount Amount to darken color by (between 0 - 1)
294
+
295
+ @returns {Color} A new color. The lightness value is reduced by `amount` from the original.
296
+ ###
297
+ darken: (amount) ->
298
+ @copy().darken$(amount)
299
+
300
+ ###*
301
+ Modifies the color so that it is darkened by `amount` (Lightness of the color ranges from 0 to 1).
302
+
303
+ green = Color(0, 255, 0)
304
+
305
+ # Modifies green to be darkGreen
306
+ green.darken$(0.3)
307
+
308
+ green.toString()
309
+ # => 'rgba(0, 102, 0, 1)'
310
+
311
+ @name darken$
312
+ @methodOf Color#
313
+ @param {Number} amount Amount to darken color by (between 0 - 1)
314
+
315
+ @returns {Color} the color with the lightness value reduced by `amount`
316
+ ###
317
+ darken$: (amount) ->
318
+ hsl = @toHsl()
319
+ hsl[2] -= amount
320
+
321
+ [@r, @g, @b, @a] = hslToRgb(hsl)
322
+
323
+ return this
324
+
325
+ ###*
326
+ A copy of the calling color with its saturation reduced by `amount`.
327
+
328
+ blue = Color(0, 0, 255)
329
+
330
+ desaturatedBlue = blue.desaturate(0.4)
331
+
332
+ # to see what they look like
333
+ for color, index in [blue, desaturatedBlue]
334
+ canvas.drawRect
335
+ color: color
336
+ x: 20 + (60 * index)
337
+ y: 20 + (60 * index)
338
+ width: 60
339
+ height: 60
340
+
341
+ @name desaturate
342
+ @methodOf Color#
343
+ @param {Number} amount Amount to reduce color saturation by (between 0 and 1)
344
+
345
+ @returns {Color} A copy of the color with the saturation value reduced by `amount`
346
+ ###
347
+ desaturate: (amount) ->
348
+ @copy().desaturate$(amount)
349
+
350
+ ###*
351
+ The modified color with its saturation reduced by `amount`.
352
+
353
+ blue = Color(0, 0, 255)
354
+
355
+ # modifies blue to be desaturatedBlue
356
+ blue.desaturate$(0.4)
357
+
358
+ blue.toString()
359
+ # => 'rgba(38, 38, 217, 1)'
360
+
361
+ @name desaturate$
362
+ @methodOf Color#
363
+ @param {Number} amount Amount to reduce color saturation by (between 0 and 1)
364
+
365
+ @returns {Color} the color with the saturation value reduced by `amount`
366
+ ###
367
+ desaturate$: (amount) ->
368
+ hsl = @toHsl()
369
+ hsl[1] -= amount
370
+
371
+ [@r, @g, @b, @a] = hslToRgb(hsl)
372
+
373
+ return this
374
+
375
+ ###*
376
+ Determine whether two colors are equal. Compares their r, g, b, and alpha values.
377
+
378
+ hex = Color('#ffff00')
379
+ rgb = Color(255, 255, 0)
380
+
381
+ hex == rgb
382
+ # => false
383
+
384
+ hex.equal(rgb)
385
+ # => true
386
+
387
+ @name equal
388
+ @methodOf Color#
389
+ @param {Color} other the color to compare to the calling color
390
+
391
+ @returns {Boolean} true if the r, g, b, a values of the colors agree, false otherwise
392
+ ###
393
+ equal: (other) ->
394
+ other.r == @r &&
395
+ other.g == @g &&
396
+ other.b == @b &&
397
+ other.a == @a
398
+
399
+ ###*
400
+ A copy of the calling color converted to grayscale.
401
+
402
+ yellow = Color(255, 255, 0)
403
+
404
+ gray = yellow.grayscale()
405
+
406
+ # to see what they look like
407
+ for color, index in [yellow, gray]
408
+ canvas.drawRect
409
+ color: color
410
+ x: 20 + (60 * index)
411
+ y: 20 + (60 * index)
412
+ width: 60
413
+ height: 60
414
+
415
+ @name grayscale
416
+ @methodOf Color#
417
+
418
+ @returns {Color} A copy of the calling color converted to grayscale.
419
+ ###
420
+ grayscale: ->
421
+ @copy().grayscale$()
422
+
423
+ ###*
424
+ The calling color converted to grayscale.
425
+
426
+ color = Color(255, 255, 0)
427
+
428
+ # modifies color into gray
429
+ color.grayscale$()
430
+
431
+ color.toString()
432
+ # => 'rgba(128, 128, 128, 1)'
433
+
434
+ @name grayscale$
435
+ @methodOf Color#
436
+
437
+ @returns {Color} The calling color converted to grayscale.
438
+ ###
439
+ grayscale$: ->
440
+ hsl = @toHsl()
441
+
442
+ g = (hsl[2] * 255).round()
443
+
444
+ @r = @g = @b = g
445
+
446
+ return this
447
+
448
+ ###*
449
+ A getter / setter for the hue value of the color. Passing no argument returns the
450
+ current hue value. Passing a value will set the hue to that value and return the color.
451
+
452
+ magenta = Color(255, 0, 255)
453
+
454
+ magenta.hue()
455
+ # => 300
456
+
457
+ # modifies the color to be yellow
458
+ magenta.hue(60)
459
+
460
+ # to see what it looks like
461
+ canvas.drawRect
462
+ color: magenta
463
+ x: 50
464
+ y: 30
465
+ width: 80
466
+ height: 80
467
+
468
+ @name hue
469
+ @methodOf Color#
470
+ @param {Number} [newVal] the new hue value
471
+
472
+ @returns {Color|Number} returns the color object if you pass a new hue value and returns the hue otherwise
473
+ ###
474
+ hue: (newVal) ->
475
+ hsl = @toHsl()
476
+ if newVal?
477
+ hsl[0] = newVal
478
+
479
+ [@r, @g, @b, @a] = hslToRgb(hsl)
480
+
481
+ return this
482
+ else
483
+ return hsl[0]
484
+
485
+ ###*
486
+ A getter / setter for the lightness value of the color. Passing no argument returns the
487
+ current lightness value. Passing a value will set the lightness to that value and return the color.
488
+
489
+ magenta = Color(255, 0, 255)
490
+
491
+ magenta.lightness()
492
+ # => 0.9
493
+
494
+ # modifies magenta in place to be lighter
495
+ magenta.lightness(0.75)
496
+
497
+ # to see what it looks like
498
+ canvas.drawRect
499
+ color: magenta
500
+ x: 50
501
+ y: 30
502
+ width: 80
503
+ height: 80
504
+
505
+ @name lightness
506
+ @methodOf Color#
507
+ @param {Number} [newVal] the new lightness value
508
+
509
+ @returns {Color|Number} returns the color object if you pass a new lightness value and returns the lightness otherwise
510
+ ###
511
+ lightness: (newVal) ->
512
+ hsl = @toHsl()
513
+ if newVal?
514
+ hsl[2] = newVal
515
+
516
+ [@r, @g, @b, @a] = hslToRgb(hsl)
517
+
518
+ return this
519
+ else
520
+ return hsl[2]
521
+
522
+ value: (newVal) ->
523
+ hsv = @toHsv()
524
+
525
+ if newVal?
526
+ hsv[2] = newVal
527
+
528
+ [@r, @g, @b, @a] = hsvToRgb(hsv)
529
+
530
+ return this
531
+ else
532
+ return hsv[2]
533
+
534
+ ###*
535
+ A copy of the calling color with its hue shifted by `degrees`. This differs from the hue setter in that it adds to the existing hue value and will wrap around 0 and 360.
536
+
537
+ magenta = Color(255, 0, 255)
538
+
539
+ magenta.hue()
540
+ # => 300
541
+
542
+ yellow = magenta.shiftHue(120)
543
+
544
+ # since magenta's hue is 300 we have wrapped
545
+ # around 360 to end up at 60
546
+ yellow.hue()
547
+ # => 60
548
+
549
+ # to see what they look like
550
+ for color, index in [magenta, yellow]
551
+ canvas.drawRect
552
+ color: color
553
+ x: 20 + (60 * index)
554
+ y: 20 + (60 * index)
555
+ width: 60
556
+ height: 60
557
+
558
+ @name shiftHue
559
+ @methodOf Color#
560
+ @param {Number} degrees number of degrees to shift the hue on the color wheel.
561
+
562
+ @returns {Color} A copy of the color with its hue shifted by `degrees`
563
+ ###
564
+ shiftHue: (degrees) ->
565
+ @copy().shiftHue$(degrees)
566
+
567
+ ###*
568
+ The calling color with its hue shifted by `degrees`. This differs from the hue setter in that it adds to the existing hue value and will wrap around 0 and 360.
569
+
570
+ magenta = Color(255, 0, 255)
571
+
572
+ magenta.hue()
573
+ # => 300
574
+
575
+ magenta.shiftHue$(120)
576
+
577
+ # since magenta's hue is 300 we have wrapped
578
+ # around 360 to end up at 60. Also we have
579
+ # modified magenta in place to become yellow
580
+ magenta.hue()
581
+ # => 60
582
+
583
+ magenta.toString()
584
+ # => 'rgba(255, 255, 0, 1)'
585
+
586
+ @name shiftHue$
587
+ @methodOf Color#
588
+ @param {Number} degrees number of degrees to shift the hue on the color wheel.
589
+
590
+ @returns {Color} The color with its hue shifted by `degrees`
591
+ ###
592
+ shiftHue$: (degrees) ->
593
+ hsl = @toHsl()
594
+
595
+ hsl[0] = (hsl[0] + degrees.round()).mod 360
596
+
597
+ [@r, @g, @b, @a] = hslToRgb(hsl)
598
+
599
+ return this
600
+
601
+ ###*
602
+ Returns a copy of the calling color lightened by `amount` (Lightness of the color ranges from 0 to 1).
603
+
604
+ green = Color(0, 255, 0)
605
+
606
+ lightGreen = green.lighten(0.3)
607
+
608
+ # to see what they look like
609
+ for color, index in [green, lightGreen]
610
+ canvas.drawRect
611
+ color: color
612
+ x: 20 + (60 * index)
613
+ y: 20 + (60 * index)
614
+ width: 60
615
+ height: 60
616
+
617
+ @name lighten
618
+ @methodOf Color#
619
+ @param {Number} amount Amount to lighten color by (between 0 to 1)
620
+
621
+ @returns {Color} A new color. The lightness value is increased by `amount` from the original.
622
+ ###
623
+ lighten: (amount) ->
624
+ @copy().lighten$(amount)
625
+
626
+ ###*
627
+ The calling color lightened by `amount` (Lightness of the color ranges from 0 to 1).
628
+
629
+ green = Color(0, 255, 0)
630
+
631
+ green.lighten$(0.2)
632
+
633
+ # we have modified green in place
634
+ # to become lightGreen
635
+ green.toString()
636
+ # => 'rgba(102, 255, 102, 1)'
637
+
638
+ @name lighten$
639
+ @methodOf Color#
640
+ @param {Number} amount Amount to lighten color by (between 0 - 1)
641
+
642
+ @returns {Color} The calling color with its lightness value increased by `amount`.
643
+ ###
644
+ lighten$: (amount) ->
645
+ hsl = @toHsl()
646
+ hsl[2] += amount
647
+
648
+ [@r, @g, @b, @a] = hslToRgb(hsl)
649
+
650
+ return this
651
+
652
+ ###*
653
+ A copy of the calling color mixed with `other` using `amount` as the
654
+ mixing ratio. If amount is not passed, then the colors are mixed evenly.
655
+
656
+ red = Color(255, 0, 0)
657
+ yellow = Color(255, 255, 0)
658
+
659
+ # With no amount argument the colors are mixed evenly
660
+ orange = red.mixWith(yellow)
661
+
662
+ # With an amount of 0.3 we are mixing the color 30% red and 70% yellow
663
+ somethingCloseToOrange = red.mixWith(yellow, 0.3)
664
+
665
+ # to see what they look like
666
+ for color, index in [red, yellow, orange, somethingCloseToOrange]
667
+ canvas.drawRect
668
+ color: color
669
+ x: 20 + (60 * (index % 2))
670
+ y: 20 + (60 * (if index > 1 then 1 else 0))
671
+ width: 60
672
+ height: 60
673
+
674
+ @name mixWith
675
+ @methodOf Color#
676
+ @param {Color} other the other color to mix
677
+ @param {Number} [amount] the mixing ratio of the calling color to `other`
678
+
679
+ @returns {Color} A new color that is a mix of the calling color and `other`
680
+ ###
681
+ mixWith: (other, amount) ->
682
+ @copy().mixWith$(other, amount)
683
+
684
+ ###*
685
+ A copy of the calling color mixed with `other` using `amount` as the
686
+ mixing ratio. If amount is not passed, then the colors are mixed evenly.
687
+
688
+ red = Color(255, 0, 0)
689
+ yellow = Color(255, 255, 0)
690
+ anotherRed = Color(255, 0, 0)
691
+
692
+ # With no amount argument the colors are mixed evenly
693
+ red.mixWith$(yellow)
694
+
695
+ # We have modified red in place to be orange
696
+ red.toString()
697
+ # => 'rgba(255, 128, 0, 1)'
698
+
699
+ # With an amount of 0.3 we are mixing the color 30% red and 70% yellow
700
+ anotherRed.mixWith$(yellow, 0.3)
701
+
702
+ # We have modified `anotherRed` in place to be somethingCloseToOrange
703
+ anotherRed.toString()
704
+ # => rgba(255, 179, 0, 1)
705
+
706
+ @name mixWith$
707
+ @methodOf Color#
708
+ @param {Color} other the other color to mix
709
+ @param {Number} [amount] the mixing ratio of the calling color to `other`
710
+
711
+ @returns {Color} The modified calling color after mixing it with `other`
712
+ ###
713
+ mixWith$: (other, amount) ->
714
+ amount ||= 0.5
715
+
716
+ [@r, @g, @b, @a] = [@r, @g, @b, @a].zip([other.r, other.g, other.b, other.a]).map (array) ->
717
+ (array[0] * amount) + (array[1] * (1 - amount))
718
+
719
+ [@r, @g, @b] = [@r, @g, @b].map (color) ->
720
+ color.round()
721
+
722
+ return this
723
+
724
+ ###*
725
+ A copy of the calling color with its saturation increased by `amount`.
726
+
727
+ color = Color(50, 50, 200)
728
+
729
+ color.saturation()
730
+ # => 0.6
731
+
732
+ saturatedColor = color.saturate(0.2)
733
+
734
+ saturatedColor.saturation()
735
+ # => 0.8
736
+
737
+ # to see what they look like
738
+ for color, index in [color, saturatedColor]
739
+ canvas.drawRect
740
+ color: color
741
+ x: 20 + (60 * index)
742
+ y: 20 + (60 * index)
743
+ width: 60
744
+ height: 60
745
+
746
+ @name saturate
747
+ @methodOf Color#
748
+ @param {Number} amount the amount to increase saturation by
749
+
750
+ @returns {Color} A copy of the calling color with its saturation increased by `amount`
751
+ ###
752
+ saturate: (amount) ->
753
+ @copy().saturate$(amount)
754
+
755
+ ###*
756
+ The calling color with its saturation increased by `amount`.
757
+
758
+ color = Color(50, 50, 200)
759
+
760
+ color.saturation()
761
+ # => 0.6
762
+
763
+ color.saturate$(0.2)
764
+
765
+ # We have modified color in place and increased its saturation to 0.8
766
+ color.saturation()
767
+ # => 0.8
768
+
769
+ color.toString()
770
+ # => rgba(25, 25, 225, 1)
771
+
772
+ @name saturate$
773
+ @methodOf Color#
774
+ @param {Number} amount the amount to increase saturation by
775
+
776
+ @returns {Color} The calling color with its saturation increased by `amount`
777
+ ###
778
+ saturate$: (amount) ->
779
+ hsl = @toHsl()
780
+ hsl[1] += amount
781
+
782
+ [@r, @g, @b, @a] = hslToRgb(hsl)
783
+
784
+ return this
785
+
786
+ ###*
787
+ A getter / setter for the saturation value of the color. Passing no argument returns the
788
+ current saturation value. Passing a value will set the saturation to that value and return the color.
789
+
790
+ yellow = Color('hsl(60, 0.5, 0.5)')
791
+
792
+ yellow.saturation()
793
+ # => 0.5
794
+
795
+ yellow.saturation(0.8)
796
+
797
+ # to see what it looks like
798
+ canvas.drawRect
799
+ color: yellow
800
+ x: 50
801
+ y: 30
802
+ width: 80
803
+ height: 80
804
+
805
+ @name saturation
806
+ @methodOf Color#
807
+ @param {Number} [newVal] the new saturation value
808
+
809
+ @returns {Color|Number} returns the color object if you pass a new saturation value and returns the saturation otherwise
810
+ ###
811
+ saturation: (newVal, mode) ->
812
+ if mode == 'hsv'
813
+ hsv = @toHsv()
814
+ if newVal?
815
+ hsv[1] = newVal
816
+
817
+ [@r, @g, @b, @a] = hsvToRgb(hsv)
818
+
819
+ return this
820
+ else
821
+ return hsv[1]
822
+
823
+ else
824
+ hsl = @toHsl()
825
+ if newVal?
826
+ hsl[1] = newVal
827
+
828
+ [@r, @g, @b, @a] = hslToRgb(hsl)
829
+
830
+ return this
831
+ else
832
+ return hsl[1]
833
+
834
+ ###*
835
+ returns the Hex representation of the color. Exclude the leading `#` by passing false.
836
+
837
+ color = Color('hsl(60, 1, 0.5)')
838
+
839
+ # passing nothing will leave the `#` intact
840
+ color.toHex()
841
+ # => '#ffff00'
842
+
843
+ # passing false will remove the `#`
844
+ color.toHex(false)
845
+ # => 'ffff00'
846
+
847
+ @name toHex
848
+ @methodOf Color#
849
+ @param {Boolean} [leadingHash] if passed as false excludes the leading `#` from the string
850
+
851
+ @returns {String} returns the Hex representation of the color
852
+ ###
853
+ toHex: (leadingHash) ->
854
+ padString = (hexString) ->
855
+ if hexString.length == 1 then pad = "0" else pad = ""
856
+
857
+ return pad + hexString
858
+
859
+ hexFromNumber = (number) ->
860
+ return padString(number.toString(16))
861
+
862
+ if leadingHash == false
863
+ "#{hexFromNumber(@r)}#{hexFromNumber(@g)}#{hexFromNumber(@b)}"
864
+ else
865
+ "##{hexFromNumber(@r)}#{hexFromNumber(@g)}#{hexFromNumber(@b)}"
866
+
867
+ ###*
868
+ returns an array of the hue, saturation, lightness, and alpha values of the color.
869
+
870
+ magenta = Color(255, 0, 255)
871
+
872
+ magenta.toHsl()
873
+ # => [300, 1, 0.5, 1]
874
+
875
+ @name toHsl
876
+ @methodOf Color#
877
+
878
+ @returns {Array} An array of the hue, saturation, lightness, and alpha values of the color.
879
+ ###
880
+ toHsl: ->
881
+ [r, g, b] = (channel / 255 for channel in [@r, @g, @b])
882
+
883
+ {min, max} = [r, g, b].extremes()
884
+
885
+ hue = saturation = lightness = (max + min) / 2
886
+ chroma = max - min
887
+
888
+ if chroma.abs() < 0.00001
889
+ hue = saturation = 0
890
+ else
891
+ saturation =
892
+ if lightness > 0.5
893
+ chroma / (1 - lightness)
894
+ else
895
+ chroma / lightness
896
+
897
+ saturation /= 2
898
+
899
+ switch max
900
+ when r then hue = ((g - b) / chroma) + 0
901
+ when g then hue = ((b - r) / chroma) + 2
902
+ when b then hue = ((r - g) / chroma) + 4
903
+
904
+ hue = (hue * 60).mod(360)
905
+
906
+ return [hue, saturation, lightness, @a]
907
+
908
+ toHsv: ->
909
+ r = @r / 255
910
+ g = @g / 255
911
+ b = @b / 255
912
+
913
+ {min, max} = [r, g, b].extremes()
914
+
915
+ h = s = v = max
916
+
917
+ d = max - min
918
+ s = (if max == 0 then 0 else d / max)
919
+
920
+ if max == min
921
+ h = 0
922
+ else
923
+ switch max
924
+ when r
925
+ h = (g - b) / d + (if g < b then 6 else 0)
926
+ when g
927
+ h = (b - r) / d + 2
928
+ when b
929
+ h = (r - g) / d + 4
930
+
931
+ h *= 60
932
+
933
+ return [h, s, v]
934
+
935
+ ###*
936
+ returns string rgba representation of the color.
937
+
938
+ red = Color('#ff0000')
939
+
940
+ red.toString()
941
+ # => 'rgba(255, 0, 0, 1)'
942
+
943
+ @name toString
944
+ @methodOf Color#
945
+
946
+ @returns {String} The rgba string representation of the color
947
+ ###
948
+ toString: ->
949
+ "rgba(#{@r}, #{@g}, #{@b}, #{@a})"
950
+
951
+ ###*
952
+ A copy of the calling color with its alpha reduced by `amount`.
953
+
954
+ color = Color(0, 0, 0, 1)
955
+
956
+ color.a
957
+ # => 1
958
+
959
+ transparentColor = color.transparentize(0.5)
960
+
961
+ transparentColor.a
962
+ # => 0.5
963
+
964
+ # to see what they look like
965
+ for color, index in [color, transparentColor]
966
+ canvas.drawRect
967
+ color: color
968
+ x: 20 + (60 * index)
969
+ y: 20 + (60 * index)
970
+ width: 60
971
+ height: 60
972
+
973
+ @name transparentize
974
+ @methodOf Color#
975
+
976
+ @returns {Color} A copy of the calling color with its alpha reduced by `amount`
977
+ ###
978
+ transparentize: (amount) ->
979
+ @copy().transparentize$(amount)
980
+
981
+ ###*
982
+ The calling color with its alpha reduced by `amount`.
983
+
984
+ color = Color(0, 0, 0, 1)
985
+
986
+ color.a
987
+ # => 1
988
+
989
+ # We modify color in place
990
+ color.transparentize$(0.5)
991
+
992
+ color.a
993
+ # => 0.5
994
+
995
+ @name transparentize$
996
+ @methodOf Color#
997
+
998
+ @returns {Color} The calling color with its alpha reduced by `amount`
999
+ ###
1000
+ transparentize$: (amount) ->
1001
+ @a = (@a - amount).clamp(0, 1)
1002
+
1003
+ return this
1004
+
1005
+ ###*
1006
+ A copy of the calling color with its alpha increased by `amount`.
1007
+
1008
+ color = Color(0, 0, 0, 0.25)
1009
+
1010
+ color.a
1011
+ # => 0.25
1012
+
1013
+ opaqueColor = color.opacify(0.5)
1014
+
1015
+ opaqueColor.a
1016
+ # => 0.75
1017
+
1018
+ # to see what they look like
1019
+ for color, index in [color, opaqueColor]
1020
+ canvas.drawRect
1021
+ color: color
1022
+ x: 20 + (60 * index)
1023
+ y: 20 + (60 * index)
1024
+ width: 60
1025
+ height: 60
1026
+
1027
+ @name opacify
1028
+ @methodOf Color#
1029
+
1030
+ @returns {Color} A copy of the calling color with its alpha increased by `amount`
1031
+ ###
1032
+ opacify: (amount) ->
1033
+ @copy().opacify$(amount)
1034
+
1035
+ ###*
1036
+ The calling color with its alpha increased by `amount`.
1037
+
1038
+ color = Color(0, 0, 0, 0)
1039
+
1040
+ color.a
1041
+ # => 0
1042
+
1043
+ # We modify color in place
1044
+ color.opacify$(0.25)
1045
+
1046
+ color.a
1047
+ # => 0.25
1048
+
1049
+ @name opacify$
1050
+ @methodOf Color#
1051
+
1052
+ @returns {Color} The calling color with its alpha increased by `amount`
1053
+ ###
1054
+ opacify$: (amount) ->
1055
+ @a = (@a + amount).clamp(0, 1)
1056
+
1057
+ return this
1058
+
1059
+ ###*
1060
+ returns a random color.
1061
+
1062
+ Color.random().toString()
1063
+ # => 'rgba(213, 144, 202, 1)'
1064
+
1065
+ Color.random().toString()
1066
+ # => 'rgba(1, 211, 24, 1)'
1067
+
1068
+ @name random
1069
+ @methodOf Color
1070
+
1071
+ @returns {Color} A random color.
1072
+ ###
1073
+ Color.random = ->
1074
+ Color(rand(256), rand(256), rand(256))
1075
+
1076
+ ###*
1077
+ Mix two colors. Behaves just like `#mixWith` except that you are passing two colors.
1078
+
1079
+ red = Color(255, 0, 0)
1080
+ yellow = Color(255, 255, 0)
1081
+
1082
+ # With no amount argument the colors are mixed evenly
1083
+ orange = Color.mix(red, yellow)
1084
+
1085
+ orange.toString()
1086
+ # => 'rgba(255, 128, 0, 1)'
1087
+
1088
+ # With an amount of 0.3 we are mixing the color 30% red and 70% yellow
1089
+ somethingCloseToOrange = Color.mix(red, yellow, 0.3)
1090
+
1091
+ somethingCloseToOrange.toString()
1092
+ # => rgba(255, 179, 0, 1)
1093
+
1094
+ @name mix
1095
+ @methodOf Color
1096
+ @see Color#mixWith
1097
+ @param {Color} color1 the first color to mix
1098
+ @param {Color} color2 the second color to mix
1099
+ @param {Number} amount the ratio to mix the colors
1100
+
1101
+ @returns {Color} A new color that is the two colors mixed at the ratio defined by `amount`
1102
+ ###
1103
+ Color.mix = (color1, color2, amount) ->
1104
+ amount ||= 0.5
1105
+
1106
+ newColors = [color1.r, color1.g, color1.b, color1.a].zip([color2.r, color2.g, color2.b, color2.a]).map (array) ->
1107
+ (array[0] * amount) + (array[1] * (1 - amount))
1108
+
1109
+ return Color(newColors)
1110
+
1111
+ (exports ? this)["Color"] = Color
1112
+ )()
1113
+