2012. 06. 18.
"Follow the white rabbit."
Click on the screen to randomize the pattern. Press enter key to change the message.
rint = (a, b) -> parseInt random(a, b) Array::sample = -> this[rint(0, this.length)] String::sample = -> this[rint(0, this.length)] Object::keys = -> _(this).keys() Array::each = (f) -> _(this).each(f) msg = 'follow the white rabbit' message = -> (msg = window.prompt("message:", msg) or msg).split(/\s+/) p5 = processing nsizes = 3 sr = 1 srange = [64, 224] sparsity = 5 trials = 5 randness = 0.0 fillers = "~!@#$%^&*()-_+=?:{}[]<>,.|\\\"" reserved = message() themes = [ {bg: 255, fg: 0 } {bg: 0, fg: 255 } {bg: 0, fg: color(0, 255, 0) } ] font = null fonts = null words = null sizes = null grids = null theme = null pick_slots = (sz, len, r, c) -> sz = parseInt sz # Clean up words object _(words.keys()).filter((k) -> words[k].length == 0).each (k) -> delete words[k] word_lens = words.keys().map (k) -> parseInt(k) min_word_len = len or max(_(word_lens).min(), 5) max_word_len = _(word_lens).max() selected = { slots: [], l2r: null } size_keys = grids[sz].keys().map((k) -> parseInt k) for t in [0...trials] if c? x = c * parseInt(sz * sr) continue unless _(size_keys).include(x) else x = grids[sz].keys().sample() # Whoa. No space at all! Stop searching. return {ok: false, slots: []} unless x? # Traverse rightward or downward l2r = random(1.0) < 0.5 if r? y = r * parseInt(sz * sr) continue unless _(size_keys).include(y) else y = grids[sz][x].keys().sample() slots = [] while x? and y? and slots.length < (len or max_word_len) x = parseInt x y = parseInt y slots.push { x: x, y: y } # Left-to-right if l2r x = if grids[sz][x + sz]? then x + sz else null y = if x? and grids[sz][x][y]? then y else null # Top-to-bottom else y = if grids[sz][x]? and grids[sz][x][y + sz]? y + sz else null if slots.length > selected.slots.length selected.slots = slots selected.l2r = l2r # Do not incentivise long words break # Tried, but to no avail. Maybe, bad luck. return {ok: true, slots: []} if selected.slots.length < min_word_len [slots, l2r] = [selected.slots, selected.l2r] len = len or _(words.keys().map((l) -> parseInt l)). filter((l) -> l <= slots.length and l >= min_word_len).sample() slots = slots[0...len] slots.each (p) -> occupy sz, p.x, p.y {ok: true, l2r: l2r, slots: slots} occupy = (sz, x, y) -> _(sizes).each (_, s) -> s = parseInt s step = parseInt s * sr sx = floor((x - s) / step) * step sy = floor((y - s) / step) * step for xx in [sx...(x + sz)] by step for yy in [sy...(y + sz)] by step if grids[s][xx]? and xx > sx and yy > sy delete grids[s][xx][yy] if grids[s][xx].keys().length == 0 delete grids[s][xx] null preload = fonts: [ 'WHITRABT.TTF' 'rough_typewriter.otf' 'daisywhl.otf' 'type-ra.ttf' ].map((f) -> "/fonts/#{f}") setup = -> size window.p5w or $(window).width(), window.p5h or $(window).height() theme = themes.sample() fonts = _(preload.fonts).reduce( ((o, e) -> o[e] = createFont e, 0; o), {}) rectMode CENTER ellipseMode CENTER noStroke() noLoop() background 255 fill 0 textFont fonts[preload.fonts.sample()], 20 text 'please wait ...', 100, 100 $.ajax url: '/p5/data/words.json' dataType: 'json' success: (data, textStatus, jqXHR) -> words = data words[1] = ['.'] mousePressed() mousePressed = -> clear() randomize() paint() keyPressed = -> if keyCode() == ENTER || keyCode() == RETURN reserved = message() clear() paint() clear = -> size window.p5w or $(window).width(), window.p5h or $(window).height() background theme.bg randomize = -> theme = themes.sample() background theme.bg fill theme.fg font = fonts[preload.fonts.sample()] nsizes = rint 1, 6 sizes = {} max_sz = parseInt random(srange[0], srange[1]) for i in [0...nsizes] sz = parseInt max_sz /= pow(8, 1 / nsizes) assigned = p5.width * p5.height / nsizes sizes[sz] = parseInt(assigned / pow(sz, 2) / sparsity) paint_word = (sz, slots, word, hl) -> # Highlight (underline, sketchy circles) if hl f = slots[0] l = _(slots).last() w = l.x - f.x + sz * 0.8 h = l.y - f.y + sz * 0.8 cx = (slots[0].x + _(slots).last().x + sz) / 2 cy = (slots[0].y + _(slots).last().y + sz) / 2 - sz * 0.1 pushMatrix() translate cx, cy fill theme.fg rect 0, 0, w, h fill theme.bg popMatrix() else fill theme.fg _(slots).each (p, idx) -> c = word.substr(idx, 1) x = parseInt p.x + rint(-sz, sz) * randness y = parseInt p.y + rint(-sz, sz) * randness csz = parseInt(sz * random(1 - randness, 1 + randness)) pushMatrix() translate x + csz * 0.5, y + csz * 0.5 #rotate 0.015 * random(-PI, PI) textFont font, csz text c, -csz * 0.3, -csz * 0.4, csz, csz popMatrix() paint = -> return unless words? rwords = _(reserved).clone() # initialize grid grids = {} _(sizes).each (_, sz) -> sz = parseInt sz step = parseInt(sz * sr) grids[sz] = {} for x in [0..p5.width - sz] by step grids[sz][x] = {} for y in [0..p5.height - sz] by step grids[sz][x][y] = 1 max_ww = _(rwords.map((w) -> w.length)).max()# + rwords.length max_wh = max_ww #rwords.length + max_ww - 1 xpos = undefined ypos = undefined rsizes = _(sizes.keys()).sortBy((e) -> -parseInt(e)) _(rsizes).each (sz, idx) -> sz = parseInt sz ypos ?= max((p5.height - sz * max_wh) * random(), 0) count = sizes[sz] for i in [0...parseInt(count * (idx * 0.2 + 1))] if rwords.length > 0 word = rwords.shift() xpos ?= max((p5.width - sz * max_ww) * random(), 0) row = parseInt(ypos / sz) col = parseInt(xpos / sz) ret = pick_slots(sz, word.length, row, col) xpos += sz * parseInt(if ret.l2r then random(3) else 1 + random(2)) ypos += sz * parseInt(if ret.l2r then 1 else random(2)) hl = true else word = undefined ret = pick_slots(sz) hl = false break unless ret.ok slots = ret.slots if slots.length > 0 word ?= words[slots.length].sample() continue unless word? while word.indexOf('.') != -1 word = word.replace(/\./, fillers.sample()) if random() < 0.5 word = word.toUpperCase() else word = word.toLowerCase() paint_word sz, slots, word, hl sz = parseInt _(sizes.keys().map((s) -> parseInt s)).min() while (ret = pick_slots(sz, 1)).ok paint_word sz, ret.slots, fillers.sample(), false