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)