Pretty Circles
The functions in this class require Underscore's library, and it should be loaded on the HTML page prior to including this module.
We can't pass an operator into Underscore's reduce
function, so we
simply create an add
function:
add = (x, y) -> x + y
The sum
function use's Underscore's reduce
function calling the
add
function on each element in our lst
.
sum = (lst) -> _.reduce(lst, add, 0)
A roll
function takes how_many
dice to roll, and also the number
of sides
those dice have. Basically, the range for the random
numbers.
roll = (how_many, sides) -> dice = -> _.random(1,sides) sum( _.times(how_many, dice) )
We now use the roll
function to skew the results towards a
particular goal
within a range
:
goal_roll = (goal, range) -> steep = 20 # Number of dice to roll sides = (range + steep) / steep # Number of sides on the dice delta = goal - range / 2 results = roll(steep, sides) - steep + delta console.log "Roll:", results, sides, goal, delta if (results > range) return results - range else return results
We now pick a random color (hue, actually) that clusters around a
particular goal
between 1 and 100:
get_color = (goal) -> hue = goal_roll(goal, 100) / 100 sat = _.random(.8, .9) lit = _.random(.5, .6) hslToRgb(hue, sat, lit)
Converts an HSL color value to RGB. Conversion formula adapted from http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are contained in the set [0, 1] and returns r, g, and b in the set [0, 255].
hslToRgb = (h, s, l) -> if (s == 0) r = g = b = l # achromatic else hue2rgb = (p, q, t) -> if (t < 0) then t += 1 if (t > 1) then t -= 1 if (t < 1/6) p + (q - p) * 6 * t else if (t < 1/2) q else if (t < 2/3) p + (q - p) * (2/3 - t) * 6 else p q = if l < 0.5 then l * (1 + s) else l + s - l * s p = 2 * l - q r = hue2rgb(p, q, h + 1/3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1/3); # return [r * 255, g * 255, b * 255]; "#" + dec2hex(r*255) + dec2hex(g*255) + dec2hex(b*255);
Since our RGB color values need to be 0 padded when they are converted to hex, we have this lovely little function:
dec2hex = (v) -> if (v < 16) "0" + Math.floor(v).toString(16) else Math.floor(v).toString(16);
While the HTML5 canvas is nice, it good use a little helper function to draw a circle with a single command:
disk = (canvas, color, x, y, radius) -> canvas.fillStyle = color canvas.beginPath() canvas.arc(x, y, radius, 0, Math.PI*2, true) canvas.closePath() canvas.fill()
Now it is time to acquire a drawing canvas and go to work, but not
until the HTML has been loaded with the window.onload
event:
window.onload = -> drawingCanvas = document.getElementById('pretty-circles') # Check the element is in the DOM and the browser supports canvas if drawingCanvas.getContext # Initaliaze a 2-dimensional drawing context context = drawingCanvas.getContext('2d') hue = _.random(1,100) for x in [10..490] by 20 for y in [10..490] by 20 disk context, get_color(hue), x, y, _.random(2,10)