/
index.jade
738 lines (637 loc) · 27.7 KB
/
index.jade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
doctype html
html
head
meta( charset='utf-8' )
meta( name='viewport' content='initial-scale=1,user-scalable=no' )
title HSL – The RGB You’ve Been Waiting For
link( href='slides.css' rel='stylesheet' type='text/css' )
body( ng-app='hsl' ): deck
slide.orly: :marked( smartypants )
---
![A Loris](loris.png)
HSL
===
The RGB You've Been Waiting For
-------------------------------
notes: :marked( smartypants )
This is a talk put together for the April 2016 [DonutJS][1] meetup. You
can read it like a blog post or use your ←→ arrow keys to navigate
it presentation-like.
The code is at [github.com/visnup/hsl][2].
[1]: http://donutjs.club/
[2]: https://github.com/visnup/hsl
slide.xs-center
:marked( smartypants )
# The <a href="javascript:void(0)" class="hidden" ng-click='lhs=Color("orange"); rhs=Color().rgb(128, 35, 100)'>Problem</a>
div( ng-init='lhs=Color("orange"); rhs=Color().rgb(128, 35, 100)' )
span.swatch( ng-style="{backgroundColor: lhs}" )
input.c( ng-model='lhs.r' placeholder='R' ng-style='{borderBottomColor: lhs.rx}' )
input.c( ng-model='lhs.g' placeholder='G' ng-style='{borderBottomColor: lhs.gx}' )
input.c( ng-model='lhs.b' placeholder='B' ng-style='{borderBottomColor: lhs.bx}' )
.op.text-muted +
span.swatch( ng-style="{backgroundColor: rhs}" )
input.c( ng-model='rhs.r' placeholder='R' ng-style='{borderBottomColor: rhs.rx}' )
input.c( ng-model='rhs.g' placeholder='G' ng-style='{borderBottomColor: rhs.gx}' )
input.c( ng-model='rhs.b' placeholder='B' ng-style='{borderBottomColor: rhs.bx}' )
.op.text-muted =
span( ng-show='false' ) {{sum = lhs.plus(rhs)}}
span.swatch( ng-style="{backgroundColor: sum}" )
input.c( ng-model='sum.r' placeholder='R' ng-style='{borderBottomColor: sum.rx}' readonly )
input.c( ng-model='sum.g' placeholder='G' ng-style='{borderBottomColor: sum.gx}' readonly )
input.c( ng-model='sum.b' placeholder='B' ng-style='{borderBottomColor: sum.bx}' readonly )
notes: :marked( smartypants )
RGB + RGB = 🤔
--------------
`#ffa500` -- that's orange. `#ffd833` -- that's not a lighter orange;
it's yellow. _Of course_, you're thinking, _one does not simply add
some GB to RGB to lighten a color_.
Ok fine, then how do you lighten orange to get light orange? And wait,
hold on: why can’t I just add Rs, Gs, and Bs to lighten my colors? I
mean, my computer knows `1 + 1 = 2`; it and I can agree on that. Yet
`#ffa500` + `#004999` yields some kinda related color, but in a way
that doesn’t exactly make sense.
![One does not simply add RGB together](https://i.imgflip.com/12zvtk.jpg)
In what world is that cool? I'll tell you in what world: in an
adding-colors-of-light-together world. You know who lives in that
world? LEDs and CRTs, that’s who. And scientists who play with lasers,
AKA crazy people.
slide
:marked( smartypants )
RGB: adding lasers together
===========================
![You actually want to cross the streams](lasers.gif)
notes: :marked( smartypants )
RGB is _not_ like some single origin, shade-grown, 1,300 meter
elevation coffee beans you found in Alajuela, Costa Rica and roasted in
small batches to give to your friends in Oakland.
RGB _is_ like that guy in a North Face jacket and hoodie on the 38L
Geary headed downtown, doing a line of cocaine off his iPhone. Yeah,
RGB is not a "so-uncool-it’s-cool-again" thing.
slide.xs-center
h1 Probably still cool
!= '<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Three cordless screwdrivers and a microcontroller = perfect small batch coffee roasting. <a href="http://t.co/vrEWlzDP9A">http://t.co/vrEWlzDP9A</a> <a href="http://t.co/GbOVyJ1jFh">pic.twitter.com/GbOVyJ1jFh</a></p>— Make: (@make) <a href="https://twitter.com/make/status/643920371780087808">September 15, 2015</a></blockquote>'
script( async src="https://platform.twitter.com/widgets.js" charset="utf-8" )
slide.xs-center
h1 Not cool
!= '<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Some guy doing lines of cocaine off his iphone on muni... <a href="http://t.co/8KgMSljp">pic.twitter.com/8KgMSljp</a></p>— Nolan Evans (@nolman) <a href="https://twitter.com/nolman/status/229042680498368513">July 28, 2012</a></blockquote>'
script( async src="https://platform.twitter.com/widgets.js" charset="utf-8" )
slide( ng-init='space = Color()' )
:marked( smartypants )
RGB is a color space
====================
p.center-xs
span.swatch( ng-style="{backgroundColor: space}" )
input.c(
ng-model='space.r'
ng-style='{borderBottomColor: space.rx}'
type='number' step=8 placeholder='R'
)
input.c(
ng-model='space.g'
ng-style='{borderBottomColor: space.gx}'
type='number' step=8 placeholder='G'
)
input.c(
ng-model='space.b'
ng-style='{borderBottomColor: space.bx}'
type='number' step=8 placeholder='B'
)
|
label
input( type='checkbox' ng-model='space.fill' )
| Fill
rgb-space( color='space' fill='space.fill' )
notes: :marked( smartypants )
RGB is what we call a **color space**, which sounds more scary than it
really is. I'd define a color space as "a system to specify a color and
reason about sets of colors."
It's actually really similar to the **Cartesian X, Y, Z** system to
specify positions in _space_. So, just like I can say the corner of
the room is `{ 0, 0, 0 }` and to get to me, you go 20' along that wall
and 10' along this other wall, and then another 5'8" upwards to get to
my head, we can do the same thing with color. Instead of X, Y, and Z,
we use R, G, and B.
But what about the case where all I care about is answering "how far
away you are from me and in what direction?" If we're constrainted to
XYZ, I have to go back to that corner and figure out your position and
then do some vector math to find the distance and angle.
And that's where **polar coordinates** come from (if you remember that
mind-blowing 6th grade geometry class). It's just a different way of
specifying all the same positions that you could with X,Y, and Z, but
re-frames it in a totally different system. And when we shift our
perspective that way, some operations become a _lot simpler_ and _more
intuitive_.
slide: :marked( smartypants )
Let's make our own color space
==============================
- Communicate to another person what color we want
- Perform some common tasks on those colors
- Intuitive
![Jake loves colors](https://media.giphy.com/media/FVbsOFYqdARtS/giphy.gif)
notes: :marked( smartypants )
How would you describe _to the person next to you_ the color of the <span
class="swatch sm" style="background:skyblue"></span> sky? How would you
compare that to the color of the <span class="swatch sm"
style="background: aquamarine"></span> ocean? How would you compare those
colors to each other?
Let's pretend we were tasked with representing all the colors in an
_intuitive_ way. How would we start?
Why not just take the rainbow (**ROY G. BIV!**) and stick it in a kinda
big array:
slide
:marked( smartypants )
Color / Swatch / Hue
--------------------
.hue( ng-init='buckets.h = 9' )
p Let’s divide the rainbow up into
input.c( ng-model='buckets.h' type='number' step=3 )
| colors:
.scale
span.swatch(
ng-repeat='h in _.range(0, buckets.h)'
ng-style="{backgroundColor: Color().hsl(h*360/buckets.h, 100, 50)}"
)
.scale
span( ng-repeat='c in [0, 1, 2, 3]' ) {{c*buckets.h/3 | number}}
notes: :marked( smartypants )
That's kinda intuitive-looking already. We've got lots of the colors we
care about in an array indexed from 0 to {{buckets.h}}. So, if I want
<span class="swatch sm" style="background:red"></span> red I just grab the
color at 0 and if I want <span class="swatch sm"
style="background:magenta"></span> magenta, I grab the one at
{{buckets.h}}. {{buckets.h/3}}ish is <span class="swatch sm"
style="background:green"></span> green and {{buckets.h*2/3}}ish is <span
class="swatch sm" style="background:blue"></span> blue; it’s just a
mapping and as we move up and down the scale we get a predictable color
from the rainbow.
We want to have more crayons in our box though, so increase that number
to something high, like 99. Now we're getting a proper rainbow.
So now that we have a box of crayons to work with, what if I don't want
to use like *all* a color at once? What about those duskier, more
washed-out colors we get on a rainy day, like <span class="swatch sm"
style="background:steelblue"></span> steel blue?
For each color we have in the rainbow, let's create a scale for
"colorfulness":
slide
:marked( smartypants )
Brightness / Colorfulness / Saturation
--------------------------------------
.saturation( ng-init='hsl = Color().hsl(300, 100, 50); buckets.s = 10' )
p Picking from our rainbow,
.scale
span.swatch(
ng-repeat='h in _.range(0, 360)'
ng-style="::{backgroundColor: Color().hsl(h, 100, 50)}"
ng-class="{active: hsl.h === h}"
ng-mouseenter='hsl.h = h'
)
.scale
span( ng-repeat='c in [0, 1, 2, 3]' ) {{c*360/3 | number}}
p a hue of
span.swatch( ng-style='{backgroundColor: Color().hsl(hsl.h, 100, 50)}' )
input.c( ng-model='hsl.h' placeholder='H' ng-style='{borderBottomColor: Color().hsl(hsl.h, 100, 50)}' )
| , let’s bucket its <b>colorfulness</b> on a scale from 0 to
input.c( ng-model='buckets.s' type='number' step=10 )
| :
.scale
span.swatch(
ng-repeat='s in _.range(0, buckets.s)'
ng-style="{backgroundColor: Color().hsl(hsl.h, s*100/buckets.s, hsl.l)}"
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*buckets.s/2 | number}}
notes: :marked( smartypants )
Neat! So we've got all the colors in the rainbow plus a measure of
colorfulness to get us everthing between those colors and gray.
But what happened to <span class="swatch sm"
style="background:black"></span> black, <span class="swatch sm"
style="background:white"></span> white?
What if we add one more dimension that is about how much light we have
that's falling on our color?
slide
:marked( smartypants )
Whiteness / Light / Lightness
-----------------------------
.lightness( ng-init='buckets.l = 10' )
p How much <b>light</b> is falling on our color?
input.c( ng-model='buckets.l' type='number' step=10 )
.scale
span.swatch(
ng-repeat='l in _.range(0, buckets.l)'
ng-style="{backgroundColor: Color().hsl(hsl.h, hsl.s, l*100/buckets.l)}"
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*buckets.l/2 | number}}
slide( ng-init='color = Color("magenta")' )
h1 Putting it all together
span.swatch( ng-style='{backgroundColor: color}' )
.picker
h4 HSL
.row.hue
.col-xs-3.col-sm-2
input.c(
ng-model='color.h'
placeholder='H'
ng-style='{borderBottomColor: Color().hsl(color.h, 100, 50)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='h in _.range(0, 360)'
ng-style="::{backgroundColor: Color().hsl(h, 100, 50)}"
ng-class="{active: color.h === h}"
ng-mouseenter='color.h = h'
)
.scale
span( ng-repeat='c in [0, 1, 2, 3]' ) {{c*360/3 | number}}
.row.saturation
.col-xs-3.col-sm-2
input.c(
ng-model='color.s'
placeholder='S'
ng-style='{borderBottomColor: Color().hsl(color.h, color.s, 50)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='s in _.range(0, 101)'
ng-style="{backgroundColor: Color().hsl(color.h, s, 50)}"
ng-class="{active: color.s === s}"
ng-mouseenter='color.s = s'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
.row.lightness
.col-xs-3.col-sm-2
input.c(
ng-model='color.l'
placeholder='L'
ng-style='{borderBottomColor: Color().hsl(color.h, 100, color.l)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='l in _.range(0, 101)'
ng-style="{backgroundColor: Color().hsl(color.h, 100, l)}"
ng-class="{active: color.l === l}"
ng-mouseenter='color.l = l'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
h4 RGB
.row.red
.col-xs-3.col-sm-2
input.c(
ng-model='color.r'
placeholder='R'
ng-style='{borderBottomColor: color.rx}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='r in _.range(0, 256)'
ng-style="::{backgroundColor: Color().red(r)}"
ng-class="{active: color.r === r}"
ng-mouseenter='color.r = r'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*256/2 | number}}
.row.green
.col-xs-3.col-sm-2
input.c(
ng-model='color.g'
placeholder='G'
ng-style='{borderBottomColor: color.gx}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='g in _.range(0, 256)'
ng-style="::{backgroundColor: Color().green(g)}"
ng-class="{active: color.g === g}"
ng-mouseenter='color.g = g'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*256/2 | number}}
.row.blue
.col-xs-3.col-sm-2
input.c(
ng-model='color.b'
placeholder='B'
ng-style='{borderBottomColor: color.bx}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='b in _.range(0, 256)'
ng-style="::{backgroundColor: Color().blue(b)}"
ng-class="{active: color.b === b}"
ng-mouseenter='color.b = b'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*256/2 | number}}
notes
:marked( smartypants )
Play with the sliders above and see how different values affect the
resulting color.
A few things to look out for:
- As you desaturate a color, the RGB components come together.
- Conversely, if you set the saturation to 30 and rotate hues, the
RGB components are constrained within a narrow band.
- As you lighten a color, the RGB components increase in value, but
not uniformly nor intuitively.
slide
:marked( smartypants )
Let's do some stuff with HSL
============================
![Damn it, Leeroy](https://i.ytimg.com/vi/8lplrZKRtbY/maxresdefault.jpg)
slide( ng-init='lighten = Color("orange")')
h2 Lighten (or darken) a color
span.swatch( ng-style='{backgroundColor: lighten}' )
.picker
.row.hue
.col-xs-3.col-sm-2
input.c(
ng-model='lighten.h'
placeholder='H'
ng-style='{borderBottomColor: Color().hsl(lighten.h, 100, 50)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='h in _.range(0, 360)'
ng-style="::{backgroundColor: Color().hsl(h, 100, 50)}"
ng-class="{active: lighten.h === h}"
ng-mouseenter='lighten.h = h'
)
.scale
span( ng-repeat='c in [0, 1, 2, 3]' ) {{c*360/3 | number}}
.row.lightness
.col-xs-3.col-sm-2
input.c(
ng-model='lighten.l'
placeholder='L'
ng-style='{borderBottomColor: Color().hsl(lighten.h, 100, lighten.l)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='l in _.range(0, 101)'
ng-style="{backgroundColor: Color().hsl(lighten.h, 100, l)}"
ng-class="{active: lighten.l === l}"
ng-mouseenter='lighten.l = l'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
notes
:marked( smartypants )
This should be pretty obvious since we've been doing it now a bunch
throughout this last section. But just to drive it home, above you can
pick whatever hue in HSL and then adjust the lightness value to be
darker or lighter to your whims of fancy. This is exactly the problem
we wanted to solve in the first place with lightening that orange.
slide( ng-init='desaturate = Color("magenta") ')
:marked( smartypants )
Grayscale a photo
-----------------
div: img(
ng-style='{"-webkit-filter": "saturate("+desaturate.s+"%)"}'
src='https://scontent-sjc2-1.cdninstagram.com/t51.2885-15/e35/11244497_1651371941744361_677993537_n.jpg?ig_cache_key=MTAzNDk5MzU1MDQ3MjYzNjE3Ng%3D%3D.2' )
.row.hue
.col-xs-3.col-sm-2
input.c(
ng-model='desaturate.s'
placeholder='S'
ng-style='{borderBottomColor: Color().hsl(desaturate.h, desaturate.s, 50)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='s in _.range(0, 101)'
ng-style="::{backgroundColor: Color().hsl(desaturate.h, s, 50)}"
ng-class="{active: desaturate.s === s}"
ng-mouseenter='desaturate.s = s'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
slide
:marked( smartypants )
Make a color palette
--------------------
.palette( ng-init='palette = Color("red"); buckets.palette = 1' )
.row.saturation
.col-xs-3.col-sm-2
input.c(
ng-model='palette.s'
placeholder='S'
ng-style='{borderBottomColor: Color().hsl(palette.h, palette.s, 50)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='s in _.range(0, 101)'
ng-style="{backgroundColor: Color().hsl(palette.h, s, 50)}"
ng-class="{active: palette.s === s}"
ng-mouseenter='palette.s = s'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
.row.lightness
.col-xs-3.col-sm-2
input.c(
ng-model='palette.l'
placeholder='L'
ng-style='{borderBottomColor: Color().hsl(palette.h, 100, palette.l)}'
)
.col-xs-9.col-sm-10
.scale
span.swatch(
ng-repeat='l in _.range(0, 101)'
ng-style="{backgroundColor: Color().hsl(palette.h, 100, l)}"
ng-class="{active: palette.l === l}"
ng-mouseenter='palette.l = l'
)
.scale
span( ng-repeat='c in [0, 1, 2]' ) {{c*100/2 | number}}
p Make me a palette with
input.c( ng-model='buckets.palette' type='number' )
| colors!
.scale
span.swatch(
ng-repeat='h in _.range(0, buckets.palette)'
ng-style="{backgroundColor: Color().hsl(h*360/buckets.palette, palette.s, palette.l)}"
)
slide
:marked( smartypants )
HSL is everywhere already
=========================
notes
:marked( smartypants )
- Chrome dev tools
- Color pickers
- OS X
- Sketch
- Google slides
- colorhexa.com
slide.orly: :marked( smartypants )
---
![A Loris](loris.png)
LAB
===
The HSL You've Been Waiting For
-------------------------------
notes
:marked( smartypants )
So, I know I just told you HSL was like the new Kanye album, but there
are some problems with HSL.
Luckily, there are some people who _really care_ about this type of
stuff. They call themselves the **International Commission on
Illumination** or if you like French, **Commission internationale de
l'éclairage**, or if you think that's a mouthful, **CIE** for short.
They've published an _even better_ colour space definition that takes
into account how the human eye perceives colour along with what
lighting conditions you're in and how long you've been fermenting that
kombucha you're drinking. It's called [CIELAB][1] and it's _crazy_.
- Lightness
- Perceptual uniformity
[1]: https://en.wikipedia.org/wiki/Lab_color_space
slide: :marked( smartypants )
About
=====
Created by [@visnup][1] and hosted at <https://visnup.github.io/hsl>.
[1]: https://twitter.com/visnup
[2]: https://donutjs.club
notes: :marked( smartypants )
These slides are a hackery of [Pug][3] and [Markdown][4] in a (hopefully)
entertaining [one-file implementation][5]. Presentation logic is ~35
lines of [Angular][6]. [three.js][7] RGB color space took another ~60
lines.
If that's interesting to you, come work with me at [Opendoor][8].
[3]: https://github.com/pugjs
[4]: https://github.com/jstransformers/jstransformer-marked
[5]: https://github.com/visnup/hsl/blob/master/index.jade
[6]: https://angularjs.org
[7]: http://threejs.org
[8]: https://www.opendoor.com/jobs
script( src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js' )
script( src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.0/lodash.min.js' )
script( src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js' )
script( src='https://npmcdn.com/three@0.74.0/examples/js/controls/TrackballControls.js' )
script( src='color.min.js' )
script.
'use strict'
angular
.module('slides', [])
.component('deck', {
controller($document) {
this.slides = []
this.add = function(slide) {
this.slides.push(slide)
return this.slides.length - 1
}
$document.on('keydown', function(e) {
if (e.target.nodeName !== 'BODY') return
var index = +location.hash.slice(1) || 0
switch (e.keyIdentifier) {
case 'Right':
index++
break
case 'Left':
index--
break
}
location.hash = _.clamp(index, 0, this.slides.length-1)
}.bind(this))
}
})
.component('slide', {
require: { deck: '^deck' },
controller($element) {
this.$onInit = function() {
$element.attr('id', this.deck.add(this))
}
}
})
angular.module('hsl', ['slides'])
.component('rgbSpace', {
bindings: { color: '<', fill: '<' },
controller($scope, $element) {
var canvas = $element[0]
var scene = new THREE.Scene()
var axes = new THREE.AxisHelper(256)
scene.add(axes)
var material = new THREE.MeshBasicMaterial()
var cube = new THREE.Mesh(new THREE.BoxGeometry(20,20,20), material)
scene.add(cube)
var camera = new THREE.PerspectiveCamera(75, 1, 1, 1000)
camera.position.set(300, 280, 360)
camera.lookAt(new THREE.Vector3(0, 0, 0))
var renderer = new THREE.WebGLRenderer({ alpha: true })
renderer.setSize(canvas.offsetWidth, canvas.offsetWidth)
$element.append(renderer.domElement)
var controls = new THREE.TrackballControls(camera, renderer.domElement)
var self = this
$scope.$watch(function() { return self.color }, function() {
cube.position.set(self.color.r, self.color.g, self.color.b)
material.color.set(self.color.rgbNumber())
}, true)
$scope.$watch(function() { return self.fill }, function() {
self.toggleFill(self.fill)
})
;(function render() {
controls.update()
renderer.render(scene, camera)
requestAnimationFrame(render)
})()
this.toggleFill = function(fill) {
if (fill) {
var step = 50;
var geometry = new THREE.BoxGeometry(step/2, step/2, step/2)
for (var r = 0; r < 256; r += step) {
for (var g = 0; g < 256; g += step) {
for (var b = 0; b < 256; b += step) {
var color = Color().rgb(r, g, b).rgbNumber()
var material = new THREE.MeshBasicMaterial({ color: color })
var cube = new THREE.Mesh(geometry, material)
cube.name = 'fill'
cube.position.set(r, g, b)
scene.add(cube)
}
}
}
} else {
var cubes = _.filter(scene.children, { name: 'fill' })
scene.remove.apply(scene, cubes)
}
}
this.removeFill = function() {
_.forEach(cubes, scene.remove.bind(scene))
}
}
})
.run(function($rootScope) {
$rootScope._ = _
$rootScope.Color = Color
})
// extend Color
Object.defineProperties(Color.prototype, {
r: { get: Color.prototype.red, set: Color.prototype.red },
g: { get: Color.prototype.green, set: Color.prototype.green },
b: { get: Color.prototype.blue, set: Color.prototype.blue },
rx: { get: function() { return Color().red(this.red()) } },
gx: { get: function() { return Color().green(this.green()) } },
bx: { get: function() { return Color().blue(this.blue()) } },
h: { get: Color.prototype.hue, set: Color.prototype.hue },
s: { get: Color.prototype.saturation, set: Color.prototype.saturation },
l: { get: Color.prototype.lightness, set: Color.prototype.lightness },
toString: { value: Color.prototype.rgbString },
// reductio ad absurdum
plus: {
value: function(other) {
var r = _.clamp(this.r + other.r, 0, 255),
g = _.clamp(this.g + other.g, 0, 255),
b = _.clamp(this.b + other.b, 0, 255)
return Color().rgb(r, g, b)
}
},
})