pixie_dust 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+