asciiblaster

- draw irc art in your web browser
git clone git://git.acid.vegas/asciiblaster.git
Log | Files | Refs | Archive | README

nopaint.js (22684B)

      1 var nopaint = (function(){
      2   
      3   controls.no = new Tool (nopaint_no_el)
      4   controls.no.use = function(state){
      5     undo.undo()
      6     controls.paint.focus()
      7   }
      8   controls.no.context = function(e){
      9     e.preventDefault()
     10     nopaint.turbo()
     11   }
     12 
     13   controls.paint = new Tool (nopaint_paint_el)
     14   controls.paint.use = function(state){
     15     nopaint.paint()
     16     nopaint_pause_el.classList.toggle("hidden", false)
     17     focused = controls.paint.lex
     18   }
     19   controls.paint.context = function(e){
     20     e.preventDefault()
     21     nopaint.autoplay()
     22   }
     23   
     24   controls.nopaint_pause = new Tool (nopaint_pause_el)
     25   controls.nopaint_pause.use = function(state){
     26     // nopaint.pause()
     27     nopaint.autoplay(false)
     28     nopaint_pause_el.classList.toggle("hidden", true)
     29     focused = canvas.aa[0][0]
     30   }
     31   
     32   // use own stepwise clock to drive tweens
     33   oktween.raf = function(){}  
     34   
     35   var nopaint = {}
     36   nopaint.debug = true
     37   nopaint.delay = nopaint.normal_delay = 100
     38   nopaint.turbo_delay = 0
     39   nopaint.tool = null
     40   nopaint.tools = {}
     41   nopaint.keys = []
     42   nopaint.weights = []
     43   nopaint.step = 0
     44   nopaint.time = 0
     45   nopaint.timeout = false
     46   nopaint.toggle = function(state){
     47     var state = typeof state == "boolean" ? state : nopaint_rapper.classList.contains("hidden")
     48     nopaint_rapper.classList.toggle("hidden", ! state)
     49     nopaint_pause_el.classList.toggle("hidden", true)
     50     document.body.classList.toggle("nopaint", state)
     51     return state
     52   }
     53   nopaint.no = function(){
     54     undo.undo()
     55     nopaint.paint()
     56   }
     57   nopaint.raw_key = controls.paint.lex.raw_key = keys.left_right_key(function(n){
     58     if (! nopaint.timeout) return
     59     if (n < 0) nopaint.no()
     60     else if (n > 0) nopaint.paint()
     61     else nopaint.pause()
     62   })
     63   nopaint.pause = nopaint.blur = function(){
     64     clearTimeout(nopaint.timeout)
     65     nopaint.timeout = 0
     66     nopaint.step = 0
     67   }
     68   nopaint.paint = function(){
     69     var state = undo.new()
     70     delete state.focus
     71     nopaint.pause()
     72     nopaint.switch_tool()
     73     nopaint.go()
     74   }
     75   nopaint.go = function(){
     76     nopaint.timeout = setTimeout(nopaint.go, nopaint.delay)
     77     oktween.update(nopaint.time)
     78     nopaint.tool.paint( nopaint.step )
     79     nopaint.time += 1
     80     nopaint.step += 1
     81   }
     82   nopaint.switch_tool = function(){
     83     last_tool = nopaint.tool
     84     last_tool && last_tool.finish()
     85     nopaint.tool = nopaint.get_random_tool( last_tool )
     86     nopaint.tool.start( last_tool )
     87     nopaint.debug && console.log("> %s", nopaint.tool.type)
     88   }
     89   nopaint.add_tool = function(fn){
     90     nopaint.tools[fn.type] = fn
     91   }
     92   nopaint.disable_all_tools = function(){
     93     Object.keys(nopaint.tools).forEach(function(key){
     94       nopaint.tools[key].disabled = true
     95     })
     96   }
     97   nopaint.enable_tools = function(keys){
     98     keys.forEach(function(key){
     99       if (nopaint.tools[key]) nopaint.tools[key].disabled = false
    100     })
    101   }
    102   nopaint.get_random_tool = function( last_tool ){
    103     var n = rand( nopaint.sum )
    104     for (var i = 0, _len = nopaint.weights.length; i < _len; i++) {
    105       if (n < nopaint.weights[i] && (! last_tool || nopaint.keys[i] !== last_tool.key)) {
    106         return nopaint.tools[ nopaint.keys[i] ]
    107       }
    108     }
    109     return nopaint.tools[ choice(nopaint.keys) ]
    110   }
    111   nopaint.regenerate_weights = function(){
    112     nopaint.sum = 0
    113     nopaint.weights = []
    114     nopaint.keys = Object.keys( nopaint.tools ).sort(function(a,b){
    115       return nopaint.tools[b].opt.weight-nopaint.tools[a].opt.weight
    116     }).filter(function(key){
    117       return ! nopaint.tools[key].disabled
    118     })
    119     nopaint.keys.forEach(function(key){
    120       nopaint.sum += nopaint.tools[key].opt.weight
    121       nopaint.weights.push( nopaint.sum )
    122     })
    123   }
    124   
    125   nopaint.is_turbo = false
    126   nopaint.turbo = function(state){
    127     nopaint.is_turbo = typeof state == "boolean" ? state : ! nopaint.is_turbo
    128     nopaint.delay = nopaint.is_turbo ? nopaint.turbo_delay : nopaint.normal_delay
    129     if (nopaint.is_turbo) {
    130       nopaint_no_el.classList.add("locked")
    131     }
    132     else {
    133       nopaint_no_el.classList.remove("locked")
    134     }
    135   }
    136 
    137   nopaint.is_autoplay = false  
    138   nopaint.autoplay = function(state){ 
    139     nopaint.is_autoplay = typeof state == "boolean" ? state : ! nopaint.is_autoplay
    140     if (nopaint.is_autoplay) {
    141       nopaint_paint_el.classList.add("locked")
    142       if (! nopaint.player) {
    143         nopaint.player = new RandomPlayer ()
    144       }
    145       if (! nopaint.timeout) nopaint.paint()
    146       nopaint.player.play()
    147     }
    148     else {
    149       nopaint_paint_el.classList.remove("locked")
    150       nopaint.pause()
    151       nopaint.player && nopaint.player.pause()
    152     }
    153   }
    154   
    155   var NopaintPlayer = Model({
    156     type: "player",
    157     upload_png: false,
    158     upload_interval: 100,
    159     step: 0,
    160     timeout: null,
    161     delay: function(){
    162       return nopaint.is_turbo ? randrange(150, 300) : randrange(400, 800)
    163     },
    164     reset: function(){
    165       this.no_count = 0
    166       this.paint_count = 0
    167     },
    168     pause: function(){
    169       clearTimeout(this.timeout)
    170       this.timeout = 0
    171     },
    172     play: function(){
    173       clearTimeout(this.timeout)
    174       var delay = this.delay()
    175       this.timeout = setTimeout(this.play.bind(this), delay)
    176       this.check_fitness()
    177       this.step += 1
    178     },
    179     check_fitness: function(){
    180       switch (this.fitness()) {
    181         case "no":
    182           nopaint.no_count += 1
    183           nopaint.since_last_no = 0
    184           nopaint.since_last_paint += 1
    185           nopaint.no()
    186           break
    187         case "paint":
    188           nopaint.paint_count += 1
    189           nopaint.since_last_no += 1
    190           nopaint.since_last_paint = 0
    191           nopaint.paint()
    192           break
    193         case "screenshot":
    194           if (this.save_as_png) break
    195           console.log("uploading...")
    196           setTimeout(clipboard.upload_png, 0)
    197           // fall thru
    198         default:
    199           nopaint.since_last_no += 1
    200           nopaint.since_last_paint += 1
    201           break
    202       }
    203     },
    204     fitness: function(){},
    205   })
    206   
    207   var RandomPlayer = NopaintPlayer.extend({
    208     type: "random_player",
    209     upload_png: false,
    210     fitness: function(){
    211       var no_prob = random()
    212       var paint_prob = 1 - no_prob 
    213       if (paint_prob < 0.3) {
    214         return "paint"
    215       }
    216       else if (no_prob < 0.5) {
    217         return "no"
    218       }
    219       else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
    220         return "screenshot"
    221       }
    222     }
    223   })
    224 
    225   var StylePlayer = NopaintPlayer.extend({
    226     type: "style_player",
    227     upload_png: false,
    228     fitness: function(){
    229       var no_prob = random()
    230       var paint_prob = 1 - no_prob
    231       var np, pp
    232       var steps = this.since_last_paint
    233 
    234       if (nopaint.tool.is_brush) {
    235         if (nopaint.tool.is_clone) {
    236           if (steps < randrange(3,8)) return
    237           np = 0.3
    238           pp = 0.4
    239         }
    240         else if (nopaint.tool.is_erase) {
    241           if (steps < randrange(2,6)) return
    242           np = 0.3
    243           pp = 0.4
    244         }
    245         else {
    246           if (steps < randrange(2,4)) return
    247           np = 0.1
    248           pp = 0.3
    249         }
    250       }
    251       if (nopaint.tool.is_shader) {
    252         switch (nopaint.tool.name) {
    253           case "rotate":
    254           case "scale":
    255             if (steps < randrange(2,4)) return
    256             np = 0.1
    257             pp = 0.2
    258             break
    259           default:
    260             np = 0.2
    261             pp = 0.2
    262         }
    263       }
    264       if (nopaint.tool.is_fill) {
    265         np = 0.4
    266         pp = 0.1
    267       }
    268 
    269       if (steps > 10) {
    270         np *= 0.7
    271         pp *= 1.5
    272 
    273         if (nopaint.is_turbo) {
    274           np *= 1.2
    275           pp *= 1.2
    276         }
    277       }
    278 
    279       if (paint_prob < np) {
    280         return "paint"
    281       }
    282       else if (no_prob < np) {
    283         return "no"
    284       }
    285       else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
    286         return "screenshot"
    287       }
    288     }
    289   })
    290 
    291   /* Base models for brushes */
    292 
    293   var NopaintTool = Model({
    294     type: "none",
    295     init: function(opt){
    296       this.opt = opt || {}
    297     },
    298     start: function(){},
    299     paint: function(t){},
    300     update: function(t){},
    301     finish: function(){},
    302   })
    303   
    304   var NopaintBrush = NopaintTool.extend({
    305     type: "brush",
    306     is_brush: true,
    307     init: function(opt){
    308       this.opt = opt || {}
    309       this.opt.max_radius = this.opt.max_radius || 10
    310       this.p = {x: randint(canvas.w), y: randint(canvas.h)}
    311       this.fg = 0
    312       this.bg = 1
    313       this.char = " "
    314       this.tweens = []
    315     },
    316     
    317     start: function(last_brush){
    318       this.set_brush_mask()
    319       this.toggle_channels()
    320       this.reset( last_brush )
    321       this.regenerate()
    322       draw.down({}, null, [this.p.x, this.p.y])
    323     },
    324     
    325     paint: function(t){
    326       this.update(t)
    327       draw.move_toroidal({}, null, [this.p.x, this.p.y])
    328     },
    329     
    330     finish: function(){
    331       this.tweens.forEach(function(t){ t.cancel() })
    332       this.tweens = []
    333     },
    334     
    335     reorient: function(last_brush){
    336       var a = {}, b
    337       
    338       if (last_brush) {
    339         this.p.x = a.x = randint(canvas.w)
    340         this.p.y = a.y = randint(canvas.h)
    341       }
    342       else {
    343         a.x = this.p.x
    344         a.y = this.p.y
    345       }
    346 
    347       b = this.get_next_point()
    348 
    349       var tween = oktween.add({
    350         obj: this.p,
    351         from: a,
    352         to: b,
    353         duration: b.duration,
    354         easing: b.easing,
    355         update: b.update,
    356         finished: function(){
    357           this.iterate()
    358           this.regenerate()
    359         }.bind(this)
    360       })
    361       this.tweens.push(tween)
    362     },
    363 
    364     get_next_point: function(){
    365       var radius = randrange(2, this.opt.max_radius)
    366       var b = {}
    367       b.duration = randrange(1, 7)
    368       b.easing = choice(easings)
    369       b.x = this.p.x + randrange(-radius, radius)
    370       b.y = this.p.y + randrange(-radius, radius)
    371       return b
    372     },
    373     
    374     set_brush_mask: function(){
    375       var r = Math.random()
    376       if (r < 0.2) {
    377         brush.mask = blit.square
    378       }
    379       else if (r < 0.6) {
    380         brush.mask = blit.circle
    381       }
    382       else if (r < 0.9) {
    383         brush.mask = blit.cross
    384       }
    385       else{
    386         brush.mask = blit.inverted_cross
    387       }
    388     },
    389     
    390     toggle_channels: function(){
    391       if (Math.random() < 0.001) { controls.bg.use(false) }
    392       else if (! brush.draw_bg && Math.random() < 0.25) { controls.bg.use(true) }
    393 
    394       if (Math.random() < 0.1) { controls.fg.use(false) }
    395       else if (! brush.draw_fg && Math.random() < 0.5) { controls.fg.use(true) }
    396 
    397       if (Math.random() < 0.02) { controls.char.use(false) }
    398       else if (! brush.draw_char && Math.random() < 0.2) { controls.char.use(true) }
    399     },
    400 
    401     iterate: function( last_brush ){
    402       this.reorient( last_brush )
    403     },
    404 
    405     regenerate: function(){
    406       brush.load( this )
    407       brush.generate()
    408     }
    409   })
    410   
    411   var easings = "linear circ_out circ_in circ_in_out quad_in quad_out quad_in_out".split(" ")
    412   
    413   /* Standard brushes */
    414   
    415   var SolidBrush = NopaintBrush.extend({
    416     type: "solid",
    417     
    418     recolor: function(){
    419       this.fg = this.bg = randint(16)
    420       this.char = " "
    421     },
    422     
    423     resize: function(m,n){
    424       m = m || 3
    425       n = n || 0
    426       var bw = xrandrange(5, 0, m) + n
    427       brush.resize( round(bw * randrange(0.9, 1.8)) || 1, round(bw) || 1 )
    428     },
    429     
    430     reset: function( last_brush ){
    431       this.opt.max_radius = randrange(5,20)
    432       this.resize()
    433       this.reorient( last_brush )
    434       this.recolor( last_brush )
    435       this.regenerate()
    436     },
    437     
    438     iterate: function( last_brush ){
    439       this.resize()
    440       this.reorient( last_brush )
    441     },
    442   })
    443   
    444   var EraseBrush = SolidBrush.extend({
    445     type: "erase",
    446     reset: function( last_brush ){
    447       this.opt.max_radius = randrange(8, 20)
    448       this.reorient( last_brush )
    449       this.bg = random() < 0.2 ? colors.white : colors.black
    450       this.char = " "
    451       brush.load( this )
    452       this.resize(3,2)
    453     },
    454   })
    455 
    456   var ShadowBrush = NopaintBrush.extend({
    457     type: "shadow",
    458     pairs: [
    459       [ colors.yellow, colors.orange ],
    460       [ colors.orange, colors.darkred ],
    461       [ colors.red, colors.darkred ],
    462       [ colors.lime, colors.green ],
    463       [ colors.cyan, colors.teal ],
    464       [ colors.cyan, colors.blue ],
    465       [ colors.blue, colors.darkblue ],
    466       [ colors.magenta, colors.purple ],
    467       [ colors.lightgray, colors.darkgray ],
    468       [ colors.darkgray, colors.black ],
    469       [ colors.white, colors.lightgray ],
    470       [ colors.white, colors.black ],
    471     ],
    472     shapes: [
    473       [[0],[1]],
    474       [[0,0],[1,1]],
    475       [[1,0,0],[1,1,1]],
    476       [[0,0,1],[1,1,1]],
    477       [[0,0,0],[1,1,1]],
    478       [[0,0,0,0],[1,1,1,1]],
    479       [[1,0,0,0],[null,1,1,1]],
    480       [[0,0,0,1],[1,1,1,null]],
    481       [[0,0],[1,0],[1,1]],
    482       [[0,0],[0,1],[1,1]],
    483     ],
    484     reset: function( last_brush ){
    485       var pair = choice(this.pairs)
    486       var shape = choice(this.shapes)
    487       this.reorient( last_brush )
    488       brush.char = " "
    489       brush.resize(shape[0].length, shape.length)
    490       brush.generate()
    491       brush.rebuild()
    492       brush.forEach(function(lex,x,y){
    493         if (shape[y][x] == null) {
    494           lex.opacity = 0
    495         }
    496         else {
    497           lex.fg = lex.bg = pair[ shape[y][x] ]
    498           lex.opacity = 1
    499         }
    500         lex.build()
    501       })
    502     },
    503     regenerate: function(){},
    504   })
    505   
    506   var RandomBrush = SolidBrush.extend({
    507     type: "random",
    508     iterate: function( last_brush ){
    509       this.reorient( last_brush )
    510       this.recolor( last_brush )
    511     },
    512   })
    513   
    514   var HueBrush = SolidBrush.extend({
    515     type: "hue",
    516     recolor: function(){
    517       this.fg = this.bg = rand_hue()
    518       this.char = " "
    519     },
    520   })
    521 
    522   var GrayBrush = SolidBrush.extend({
    523     type: "gray",
    524     recolor: function(){
    525       this.fg = this.bg = rand_gray()
    526       this.char = " "
    527     },
    528   })
    529 
    530   var LetterBrush = SolidBrush.extend({
    531     type: "letter",
    532     recolor: function(){
    533       this.fg = rand_hue()
    534       this.bg = rand_hue()
    535       this.char = choice( unicode.block(letters.charset, 32) )
    536     },
    537   })
    538   
    539   var RandomLetterBrush = LetterBrush.extend({
    540     type: "random-letter",
    541     iterate: function(){
    542       if (Math.random() < 0.01) {
    543         this.fg += 1
    544       }
    545       if (Math.random() < 0.05) {
    546         var n = this.fg
    547         this.fg = this.bg
    548         this.bg = n
    549       }
    550       if (Math.random() < 0.7) {
    551         this.char = choice( unicode.block(letters.charset, 32) )
    552       }
    553       this.regenerate()
    554       this.__iterate()
    555     },
    556     update: function(){
    557       if (Math.random() < 0.3) {
    558         this.char = choice( unicode.block(letters.charset, 32) )
    559       }
    560       this.regenerate()
    561     },
    562   })
    563 
    564   var CloneBrush = SolidBrush.extend({
    565     type: "clone",
    566     
    567     is_clone: true,
    568     
    569     reset: function( last_brush ){
    570       this.opt.max_radius = randrange(5, 20)
    571       this.reorient( last_brush )
    572       this.resize(4,2)
    573       this.clone_random_region()
    574     },
    575     
    576     clone_random_region: function(x, y){
    577       var x = randrange(0, canvas.w - brush.w)
    578       var y = randrange(0, canvas.h - brush.h)
    579       this.clone_region(x, y)
    580     },
    581     
    582     clone_region: function(x, y){
    583       blit.copy_toroidal_from(canvas, brush, round(x-brush.w/2), round(y-brush.h/2))
    584       brush.mask(brush)
    585     },
    586 
    587     iterate: function( last_brush ){
    588       this.reorient( last_brush )
    589     },
    590     
    591     regenerate: function(){},
    592   })
    593   
    594   var SmearBrush = CloneBrush.extend({
    595     type: "smear",
    596     
    597     update: function(){
    598       var r = random()
    599       var jitter_x = randnullsign() * xrand(2, 2)
    600       var jitter_y = randnullsign() * xrand(2, 2)
    601       this.clone_region( this.p.x + jitter_x, this.p.y + jitter_y )
    602     },
    603 
    604     iterate: function( last_brush ){
    605       this.resize(4, 2)
    606       this.update()
    607       this.reorient( last_brush )
    608     }
    609   })
    610 
    611   var StarsTool = NopaintBrush.extend({
    612     type: "stars",
    613     chars: "....,,'''*",
    614 
    615     start: function(last_brush){
    616       this.reorient( last_brush )
    617     },
    618     
    619     paint: function(t){
    620       if (Math.random() < 0.5) {
    621         var lex = canvas.get(this.p.x, this.p.y)
    622         undo.save_lex(lex.x, lex.y, lex)
    623         lex.fg = rand_hue()
    624         // lex.bg = colors.black
    625         lex.char = choice(this.chars)
    626         lex.build()
    627       }
    628     },
    629   })
    630 
    631   /* Fill tool */
    632 
    633   var FillTool = NopaintTool.extend({
    634     type: "fill",
    635     rate: 25,
    636     is_fill: true,
    637     start: function(){
    638       this.fill()
    639     },
    640     paint: function(t){
    641       if ((t % this.rate) == this.rate-1) {
    642         this.fill()
    643       }
    644     },
    645     recolor: function(){
    646       this.fg = this.bg = randint(16)
    647       this.char = " "
    648       this.opacity = 1
    649     },
    650     fill: function(){
    651       var x = randint(canvas.w)
    652       var y = randint(canvas.h)
    653       this.recolor()
    654       draw.fill(this, x, y)
    655     }
    656   })
    657   
    658   var FillLetterTool = FillTool.extend({
    659     type: "fill-letter",
    660     rate: 25,
    661     recolor: function(){
    662       this.fg = randint(16)
    663       this.bg = randint(16)
    664       this.char = choice( unicode.block(letters.charset, 32) )
    665       this.opacity = 1
    666     },
    667   })
    668 
    669   /* Shader Tools */
    670   
    671   var ShaderTool = NopaintTool.extend({
    672     type: "shader",
    673     speed: 3,
    674     is_shader: true,
    675     is_recursive: false,
    676     start: function(){
    677       undo.save_rect(0, 0, canvas.w, canvas.h)
    678       this.canvas = canvas.clone()
    679     },
    680     paint: function(t){
    681       if ((t % this.speed) == 0) {
    682         var w = canvas.w
    683         var h = canvas.h
    684         var lex
    685         if (this.is_recursive) {
    686           this.canvas.assign(canvas)
    687         }
    688         this.before_shade()
    689         for (var x = 0; x < w; x++) {
    690           for (var y = 0; y < h; y++) {
    691             lex = canvas.get(x, y)
    692             if (! this.shade( this.canvas, canvas, lex, x, y, w, h )) {
    693               lex.build()
    694             }
    695           }
    696         }
    697       }
    698     },
    699     before_shade: function(){},
    700     shade: function(src, dest, lex, x, y, w, h){},
    701     finish: function(){
    702       this.canvas.demolish()
    703     }
    704   })
    705   
    706   var ColorizeTool = ShaderTool.extend({
    707     type: "colorize",
    708     fns: [mirc_color_reverse,hue,inv_hue,gray,fire,red,yellow,green,blue,purple,dark_gray],
    709     speed: 5,
    710     start: function(){
    711       this.__start()
    712       this.i = randint(this.fns.length)
    713     },
    714     before_shade: function(){
    715       this.i = (this.i + 1) % this.fns.length
    716       this.fn = this.fns[this.i]
    717     },
    718     shade: function(src, dest, lex, x, y, w, h){
    719       lex.bg = this.fn( lex.bg )
    720       return false
    721     },
    722   })
    723   
    724   var TranslateTool = ShaderTool.extend({
    725     type: "translate",
    726     dx: 0,
    727     dy: 0,
    728     speed: 3,
    729     start: function(){
    730       this.__start()
    731       this.dx = randint(3)-1
    732       this.dy = randint(3)-1
    733       this.x = this.y = 0
    734       if (! this.dx && ! this.dy) {
    735         this.dx = 1
    736         this.dy = 0
    737       }
    738     },
    739     before_shade: function(){
    740       this.x += this.dx
    741       this.y += this.dy
    742     },
    743     shade: function(src, dest, lex, x, y, w, h){
    744       var copy = src.get(x+this.x, y+this.y)
    745       lex.assign(copy)
    746       return true
    747     },
    748   })
    749 
    750   var SliceTool = ShaderTool.extend({
    751     type: "slice",
    752     dx: 0,
    753     dy: 0,
    754     speed: 1,
    755     is_recursive: true,
    756     start: function(){
    757       this.__start()
    758       this.is_y = Math.random() > 0.3
    759       this.limit = this.is_y ? canvas.h : canvas.w
    760       this.position = randint(this.limit)
    761       this.direction = 1
    762     },
    763     before_shade: function(){
    764       if (Math.random() < 0.6) {
    765         this.position = mod(this.position + 1, this.limit)
    766       }
    767       if (Math.random() > 0.8) {
    768         this.direction = randsign()
    769       }
    770     },
    771     shade: function(src, dest, lex, x, y, w, h){
    772       if (this.is_y) {
    773         if (y >= this.position) {
    774           var copy = src.get(x + this.direction, y)
    775           lex.assign(copy)
    776         }
    777       }
    778       else if (x >= this.position) {
    779         var copy = src.get(x, y + this.direction)
    780         lex.assign(copy)
    781       }
    782       return true
    783     },
    784   })
    785 
    786   var ScaleTool = ShaderTool.extend({
    787     type: "scale",
    788     scale: 1,
    789     dscale: 0,
    790     speed: 3,
    791     start: function(){
    792       this.__start()
    793       var sign = randsign()
    794       this.x_scale = 1
    795       this.y_scale = 1
    796       this.dx_scale = randsign() * randrange(0.0005, 0.01)
    797       var r = Math.random()
    798       if (r < 0.333) {
    799         this.dy_scale = this.dx_scale * randrange(0.85, 1.25)
    800       }
    801       else if (r < 0.666) {
    802         this.dy_scale = this.dx_scale
    803       }
    804       else {
    805         this.dy_scale = randsign() * randrange(0.0005, 0.01)
    806       }
    807     },
    808     before_shade: function(){
    809       this.x_scale += this.dx_scale
    810       this.y_scale += this.dy_scale
    811     },
    812     shade: function(src, dest, lex, x, y, w, h){
    813       x = (x/w) * 2 - 1
    814       y = (y/h) * 2 - 1
    815       x *= this.x_scale
    816       y *= this.y_scale
    817       x = (x + 1) / 2 * w
    818       y = (y + 1) / 2 * h
    819       var copy = src.get(x, y)
    820       lex.assign(copy)
    821       return true
    822     },
    823   })
    824 
    825   var RotateTool = ShaderTool.extend({
    826     type: "rotate",
    827     theta: 0,
    828     d_theta: 0,
    829     
    830     start: function(){
    831       this.__start()
    832       var sign = randsign()
    833       this.theta = 0
    834       this.d_theta = randsign() * randrange(0.001, 0.05)
    835     },
    836     before_shade: function(){
    837       this.theta += this.d_theta
    838     },
    839     shade: function(src, dest, lex, x, y, w, h){
    840       x = (x/w) * 2 - 1
    841       y = (y/h) * 2 - 1
    842       var ca = cos(this.theta)
    843       var sa = sin(this.theta)
    844       var a = x * ca - y * sa
    845       var b = x * sa + y * ca
    846       x = (a + 1) / 2 * w
    847       y = (b + 1) / 2 * h
    848       var copy = src.get(x, y)
    849       lex.assign(copy)
    850       return true
    851     },
    852   })
    853 
    854   var CycleTool = ShaderTool.extend({
    855     type: "cycle",
    856     n: 0,
    857     speed: 5,
    858     is_recursive: true,
    859     start: function(){
    860       this.__start()
    861       this.n = randsign()
    862       if (random() < 0.2) this.n *= randint(15)
    863     },
    864     shade: function(src, dest, lex, x, y){
    865       lex.bg += this.n
    866       return false
    867     },
    868   })
    869   
    870   nopaint.add_tool( new SolidBrush({ weight: 5 }) )
    871   nopaint.add_tool( new ShadowBrush({ weight: 10 }) )
    872   nopaint.add_tool( new EraseBrush({ weight: 5 }) )
    873   nopaint.add_tool( new RandomBrush({ weight: 4 }) )
    874   nopaint.add_tool( new HueBrush({ weight: 5 }) )
    875   nopaint.add_tool( new GrayBrush({ weight: 5 }) )
    876   nopaint.add_tool( new LetterBrush({ weight: 2 }) )
    877   nopaint.add_tool( new RandomLetterBrush({ weight: 12 }) )
    878   nopaint.add_tool( new CloneBrush({ weight: 8 }) )
    879   nopaint.add_tool( new SmearBrush({ weight: 10 }) )
    880   nopaint.add_tool( new FillTool({ weight: 3 }) )
    881   nopaint.add_tool( new FillLetterTool({ weight: 6 }) )
    882   nopaint.add_tool( new StarsTool({ weight: 2 }) )
    883   nopaint.add_tool( new TranslateTool({ weight: 4 }) )
    884   nopaint.add_tool( new CycleTool({ weight: 1 }) )
    885   nopaint.add_tool( new ScaleTool({ weight: 3 }) )
    886   nopaint.add_tool( new RotateTool({ weight: 3 }) )
    887   nopaint.add_tool( new SliceTool({ weight: 4 }) )
    888   nopaint.add_tool( new ColorizeTool({ weight: 1 }) )
    889   nopaint.regenerate_weights()
    890 
    891   nopaint.toggle(true)
    892 
    893   nopaint.player = new StylePlayer ()
    894 
    895   return nopaint
    896 })()