asciiblaster/docs/js/ui/nopaint.js

897 lines
22 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var nopaint = (function(){
controls.no = new Tool (nopaint_no_el)
controls.no.use = function(state){
undo.undo()
controls.paint.focus()
}
controls.no.context = function(e){
e.preventDefault()
nopaint.turbo()
}
controls.paint = new Tool (nopaint_paint_el)
controls.paint.use = function(state){
nopaint.paint()
nopaint_pause_el.classList.toggle("hidden", false)
focused = controls.paint.lex
}
controls.paint.context = function(e){
e.preventDefault()
nopaint.autoplay()
}
controls.nopaint_pause = new Tool (nopaint_pause_el)
controls.nopaint_pause.use = function(state){
// nopaint.pause()
nopaint.autoplay(false)
nopaint_pause_el.classList.toggle("hidden", true)
focused = canvas.aa[0][0]
}
// use own stepwise clock to drive tweens
oktween.raf = function(){}
var nopaint = {}
nopaint.debug = true
nopaint.delay = nopaint.normal_delay = 100
nopaint.turbo_delay = 0
nopaint.tool = null
nopaint.tools = {}
nopaint.keys = []
nopaint.weights = []
nopaint.step = 0
nopaint.time = 0
nopaint.timeout = false
nopaint.toggle = function(state){
var state = typeof state == "boolean" ? state : nopaint_rapper.classList.contains("hidden")
nopaint_rapper.classList.toggle("hidden", ! state)
nopaint_pause_el.classList.toggle("hidden", true)
document.body.classList.toggle("nopaint", state)
return state
}
nopaint.no = function(){
undo.undo()
nopaint.paint()
}
nopaint.raw_key = controls.paint.lex.raw_key = keys.left_right_key(function(n){
if (! nopaint.timeout) return
if (n < 0) nopaint.no()
else if (n > 0) nopaint.paint()
else nopaint.pause()
})
nopaint.pause = nopaint.blur = function(){
clearTimeout(nopaint.timeout)
nopaint.timeout = 0
nopaint.step = 0
}
nopaint.paint = function(){
var state = undo.new()
delete state.focus
nopaint.pause()
nopaint.switch_tool()
nopaint.go()
}
nopaint.go = function(){
nopaint.timeout = setTimeout(nopaint.go, nopaint.delay)
oktween.update(nopaint.time)
nopaint.tool.paint( nopaint.step )
nopaint.time += 1
nopaint.step += 1
}
nopaint.switch_tool = function(){
last_tool = nopaint.tool
last_tool && last_tool.finish()
nopaint.tool = nopaint.get_random_tool( last_tool )
nopaint.tool.start( last_tool )
nopaint.debug && console.log("> %s", nopaint.tool.type)
}
nopaint.add_tool = function(fn){
nopaint.tools[fn.type] = fn
}
nopaint.disable_all_tools = function(){
Object.keys(nopaint.tools).forEach(function(key){
nopaint.tools[key].disabled = true
})
}
nopaint.enable_tools = function(keys){
keys.forEach(function(key){
if (nopaint.tools[key]) nopaint.tools[key].disabled = false
})
}
nopaint.get_random_tool = function( last_tool ){
var n = rand( nopaint.sum )
for (var i = 0, _len = nopaint.weights.length; i < _len; i++) {
if (n < nopaint.weights[i] && (! last_tool || nopaint.keys[i] !== last_tool.key)) {
return nopaint.tools[ nopaint.keys[i] ]
}
}
return nopaint.tools[ choice(nopaint.keys) ]
}
nopaint.regenerate_weights = function(){
nopaint.sum = 0
nopaint.weights = []
nopaint.keys = Object.keys( nopaint.tools ).sort(function(a,b){
return nopaint.tools[b].opt.weight-nopaint.tools[a].opt.weight
}).filter(function(key){
return ! nopaint.tools[key].disabled
})
nopaint.keys.forEach(function(key){
nopaint.sum += nopaint.tools[key].opt.weight
nopaint.weights.push( nopaint.sum )
})
}
nopaint.is_turbo = false
nopaint.turbo = function(state){
nopaint.is_turbo = typeof state == "boolean" ? state : ! nopaint.is_turbo
nopaint.delay = nopaint.is_turbo ? nopaint.turbo_delay : nopaint.normal_delay
if (nopaint.is_turbo) {
nopaint_no_el.classList.add("locked")
}
else {
nopaint_no_el.classList.remove("locked")
}
}
nopaint.is_autoplay = false
nopaint.autoplay = function(state){
nopaint.is_autoplay = typeof state == "boolean" ? state : ! nopaint.is_autoplay
if (nopaint.is_autoplay) {
nopaint_paint_el.classList.add("locked")
if (! nopaint.player) {
nopaint.player = new RandomPlayer ()
}
if (! nopaint.timeout) nopaint.paint()
nopaint.player.play()
}
else {
nopaint_paint_el.classList.remove("locked")
nopaint.pause()
nopaint.player && nopaint.player.pause()
}
}
var NopaintPlayer = Model({
type: "player",
upload_png: false,
upload_interval: 100,
step: 0,
timeout: null,
delay: function(){
return nopaint.is_turbo ? randrange(150, 300) : randrange(400, 800)
},
reset: function(){
this.no_count = 0
this.paint_count = 0
},
pause: function(){
clearTimeout(this.timeout)
this.timeout = 0
},
play: function(){
clearTimeout(this.timeout)
var delay = this.delay()
this.timeout = setTimeout(this.play.bind(this), delay)
this.check_fitness()
this.step += 1
},
check_fitness: function(){
switch (this.fitness()) {
case "no":
nopaint.no_count += 1
nopaint.since_last_no = 0
nopaint.since_last_paint += 1
nopaint.no()
break
case "paint":
nopaint.paint_count += 1
nopaint.since_last_no += 1
nopaint.since_last_paint = 0
nopaint.paint()
break
case "screenshot":
if (this.save_as_png) break
console.log("uploading...")
setTimeout(clipboard.upload_png, 0)
// fall thru
default:
nopaint.since_last_no += 1
nopaint.since_last_paint += 1
break
}
},
fitness: function(){},
})
var RandomPlayer = NopaintPlayer.extend({
type: "random_player",
upload_png: false,
fitness: function(){
var no_prob = random()
var paint_prob = 1 - no_prob
if (paint_prob < 0.3) {
return "paint"
}
else if (no_prob < 0.5) {
return "no"
}
else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
return "screenshot"
}
}
})
var StylePlayer = NopaintPlayer.extend({
type: "style_player",
upload_png: false,
fitness: function(){
var no_prob = random()
var paint_prob = 1 - no_prob
var np, pp
var steps = this.since_last_paint
if (nopaint.tool.is_brush) {
if (nopaint.tool.is_clone) {
if (steps < randrange(3,8)) return
np = 0.3
pp = 0.4
}
else if (nopaint.tool.is_erase) {
if (steps < randrange(2,6)) return
np = 0.3
pp = 0.4
}
else {
if (steps < randrange(2,4)) return
np = 0.1
pp = 0.3
}
}
if (nopaint.tool.is_shader) {
switch (nopaint.tool.name) {
case "rotate":
case "scale":
if (steps < randrange(2,4)) return
np = 0.1
pp = 0.2
break
default:
np = 0.2
pp = 0.2
}
}
if (nopaint.tool.is_fill) {
np = 0.4
pp = 0.1
}
if (steps > 10) {
np *= 0.7
pp *= 1.5
if (nopaint.is_turbo) {
np *= 1.2
pp *= 1.2
}
}
if (paint_prob < np) {
return "paint"
}
else if (no_prob < np) {
return "no"
}
else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
return "screenshot"
}
}
})
/* Base models for brushes */
var NopaintTool = Model({
type: "none",
init: function(opt){
this.opt = opt || {}
},
start: function(){},
paint: function(t){},
update: function(t){},
finish: function(){},
})
var NopaintBrush = NopaintTool.extend({
type: "brush",
is_brush: true,
init: function(opt){
this.opt = opt || {}
this.opt.max_radius = this.opt.max_radius || 10
this.p = {x: randint(canvas.w), y: randint(canvas.h)}
this.fg = 0
this.bg = 1
this.char = " "
this.tweens = []
},
start: function(last_brush){
this.set_brush_mask()
this.toggle_channels()
this.reset( last_brush )
this.regenerate()
draw.down({}, null, [this.p.x, this.p.y])
},
paint: function(t){
this.update(t)
draw.move_toroidal({}, null, [this.p.x, this.p.y])
},
finish: function(){
this.tweens.forEach(function(t){ t.cancel() })
this.tweens = []
},
reorient: function(last_brush){
var a = {}, b
if (last_brush) {
this.p.x = a.x = randint(canvas.w)
this.p.y = a.y = randint(canvas.h)
}
else {
a.x = this.p.x
a.y = this.p.y
}
b = this.get_next_point()
var tween = oktween.add({
obj: this.p,
from: a,
to: b,
duration: b.duration,
easing: b.easing,
update: b.update,
finished: function(){
this.iterate()
this.regenerate()
}.bind(this)
})
this.tweens.push(tween)
},
get_next_point: function(){
var radius = randrange(2, this.opt.max_radius)
var b = {}
b.duration = randrange(1, 7)
b.easing = choice(easings)
b.x = this.p.x + randrange(-radius, radius)
b.y = this.p.y + randrange(-radius, radius)
return b
},
set_brush_mask: function(){
var r = Math.random()
if (r < 0.2) {
brush.mask = blit.square
}
else if (r < 0.6) {
brush.mask = blit.circle
}
else if (r < 0.9) {
brush.mask = blit.cross
}
else{
brush.mask = blit.inverted_cross
}
},
toggle_channels: function(){
if (Math.random() < 0.001) { controls.bg.use(false) }
else if (! brush.draw_bg && Math.random() < 0.25) { controls.bg.use(true) }
if (Math.random() < 0.1) { controls.fg.use(false) }
else if (! brush.draw_fg && Math.random() < 0.5) { controls.fg.use(true) }
if (Math.random() < 0.02) { controls.char.use(false) }
else if (! brush.draw_char && Math.random() < 0.2) { controls.char.use(true) }
},
iterate: function( last_brush ){
this.reorient( last_brush )
},
regenerate: function(){
brush.load( this )
brush.generate()
}
})
var easings = "linear circ_out circ_in circ_in_out quad_in quad_out quad_in_out".split(" ")
/* Standard brushes */
var SolidBrush = NopaintBrush.extend({
type: "solid",
recolor: function(){
this.fg = this.bg = randint(16)
this.char = " "
},
resize: function(m,n){
m = m || 3
n = n || 0
var bw = xrandrange(5, 0, m) + n
brush.resize( round(bw * randrange(0.9, 1.8)) || 1, round(bw) || 1 )
},
reset: function( last_brush ){
this.opt.max_radius = randrange(5,20)
this.resize()
this.reorient( last_brush )
this.recolor( last_brush )
this.regenerate()
},
iterate: function( last_brush ){
this.resize()
this.reorient( last_brush )
},
})
var EraseBrush = SolidBrush.extend({
type: "erase",
reset: function( last_brush ){
this.opt.max_radius = randrange(8, 20)
this.reorient( last_brush )
this.bg = random() < 0.2 ? colors.white : colors.black
this.char = " "
brush.load( this )
this.resize(3,2)
},
})
var ShadowBrush = NopaintBrush.extend({
type: "shadow",
pairs: [
[ colors.yellow, colors.orange ],
[ colors.orange, colors.darkred ],
[ colors.red, colors.darkred ],
[ colors.lime, colors.green ],
[ colors.cyan, colors.teal ],
[ colors.cyan, colors.blue ],
[ colors.blue, colors.darkblue ],
[ colors.magenta, colors.purple ],
[ colors.lightgray, colors.darkgray ],
[ colors.darkgray, colors.black ],
[ colors.white, colors.lightgray ],
[ colors.white, colors.black ],
],
shapes: [
[[0],[1]],
[[0,0],[1,1]],
[[1,0,0],[1,1,1]],
[[0,0,1],[1,1,1]],
[[0,0,0],[1,1,1]],
[[0,0,0,0],[1,1,1,1]],
[[1,0,0,0],[null,1,1,1]],
[[0,0,0,1],[1,1,1,null]],
[[0,0],[1,0],[1,1]],
[[0,0],[0,1],[1,1]],
],
reset: function( last_brush ){
var pair = choice(this.pairs)
var shape = choice(this.shapes)
this.reorient( last_brush )
brush.char = " "
brush.resize(shape[0].length, shape.length)
brush.generate()
brush.rebuild()
brush.forEach(function(lex,x,y){
if (shape[y][x] == null) {
lex.opacity = 0
}
else {
lex.fg = lex.bg = pair[ shape[y][x] ]
lex.opacity = 1
}
lex.build()
})
},
regenerate: function(){},
})
var RandomBrush = SolidBrush.extend({
type: "random",
iterate: function( last_brush ){
this.reorient( last_brush )
this.recolor( last_brush )
},
})
var HueBrush = SolidBrush.extend({
type: "hue",
recolor: function(){
this.fg = this.bg = rand_hue()
this.char = " "
},
})
var GrayBrush = SolidBrush.extend({
type: "gray",
recolor: function(){
this.fg = this.bg = rand_gray()
this.char = " "
},
})
var LetterBrush = SolidBrush.extend({
type: "letter",
recolor: function(){
this.fg = rand_hue()
this.bg = rand_hue()
this.char = choice( unicode.block(letters.charset, 32) )
},
})
var RandomLetterBrush = LetterBrush.extend({
type: "random-letter",
iterate: function(){
if (Math.random() < 0.01) {
this.fg += 1
}
if (Math.random() < 0.05) {
var n = this.fg
this.fg = this.bg
this.bg = n
}
if (Math.random() < 0.7) {
this.char = choice( unicode.block(letters.charset, 32) )
}
this.regenerate()
this.__iterate()
},
update: function(){
if (Math.random() < 0.3) {
this.char = choice( unicode.block(letters.charset, 32) )
}
this.regenerate()
},
})
var CloneBrush = SolidBrush.extend({
type: "clone",
is_clone: true,
reset: function( last_brush ){
this.opt.max_radius = randrange(5, 20)
this.reorient( last_brush )
this.resize(4,2)
this.clone_random_region()
},
clone_random_region: function(x, y){
var x = randrange(0, canvas.w - brush.w)
var y = randrange(0, canvas.h - brush.h)
this.clone_region(x, y)
},
clone_region: function(x, y){
blit.copy_toroidal_from(canvas, brush, round(x-brush.w/2), round(y-brush.h/2))
brush.mask(brush)
},
iterate: function( last_brush ){
this.reorient( last_brush )
},
regenerate: function(){},
})
var SmearBrush = CloneBrush.extend({
type: "smear",
update: function(){
var r = random()
var jitter_x = randnullsign() * xrand(2, 2)
var jitter_y = randnullsign() * xrand(2, 2)
this.clone_region( this.p.x + jitter_x, this.p.y + jitter_y )
},
iterate: function( last_brush ){
this.resize(4, 2)
this.update()
this.reorient( last_brush )
}
})
var StarsTool = NopaintBrush.extend({
type: "stars",
chars: "....,,'''*",
start: function(last_brush){
this.reorient( last_brush )
},
paint: function(t){
if (Math.random() < 0.5) {
var lex = canvas.get(this.p.x, this.p.y)
undo.save_lex(lex.x, lex.y, lex)
lex.fg = rand_hue()
// lex.bg = colors.black
lex.char = choice(this.chars)
lex.build()
}
},
})
/* Fill tool */
var FillTool = NopaintTool.extend({
type: "fill",
rate: 25,
is_fill: true,
start: function(){
this.fill()
},
paint: function(t){
if ((t % this.rate) == this.rate-1) {
this.fill()
}
},
recolor: function(){
this.fg = this.bg = randint(16)
this.char = " "
this.opacity = 1
},
fill: function(){
var x = randint(canvas.w)
var y = randint(canvas.h)
this.recolor()
draw.fill(this, x, y)
}
})
var FillLetterTool = FillTool.extend({
type: "fill-letter",
rate: 25,
recolor: function(){
this.fg = randint(16)
this.bg = randint(16)
this.char = choice( unicode.block(letters.charset, 32) )
this.opacity = 1
},
})
/* Shader Tools */
var ShaderTool = NopaintTool.extend({
type: "shader",
speed: 3,
is_shader: true,
is_recursive: false,
start: function(){
undo.save_rect(0, 0, canvas.w, canvas.h)
this.canvas = canvas.clone()
},
paint: function(t){
if ((t % this.speed) == 0) {
var w = canvas.w
var h = canvas.h
var lex
if (this.is_recursive) {
this.canvas.assign(canvas)
}
this.before_shade()
for (var x = 0; x < w; x++) {
for (var y = 0; y < h; y++) {
lex = canvas.get(x, y)
if (! this.shade( this.canvas, canvas, lex, x, y, w, h )) {
lex.build()
}
}
}
}
},
before_shade: function(){},
shade: function(src, dest, lex, x, y, w, h){},
finish: function(){
this.canvas.demolish()
}
})
var ColorizeTool = ShaderTool.extend({
type: "colorize",
fns: [mirc_color_reverse,hue,inv_hue,gray,fire,red,yellow,green,blue,purple,dark_gray],
speed: 5,
start: function(){
this.__start()
this.i = randint(this.fns.length)
},
before_shade: function(){
this.i = (this.i + 1) % this.fns.length
this.fn = this.fns[this.i]
},
shade: function(src, dest, lex, x, y, w, h){
lex.bg = this.fn( lex.bg )
return false
},
})
var TranslateTool = ShaderTool.extend({
type: "translate",
dx: 0,
dy: 0,
speed: 3,
start: function(){
this.__start()
this.dx = randint(3)-1
this.dy = randint(3)-1
this.x = this.y = 0
if (! this.dx && ! this.dy) {
this.dx = 1
this.dy = 0
}
},
before_shade: function(){
this.x += this.dx
this.y += this.dy
},
shade: function(src, dest, lex, x, y, w, h){
var copy = src.get(x+this.x, y+this.y)
lex.assign(copy)
return true
},
})
var SliceTool = ShaderTool.extend({
type: "slice",
dx: 0,
dy: 0,
speed: 1,
is_recursive: true,
start: function(){
this.__start()
this.is_y = Math.random() > 0.3
this.limit = this.is_y ? canvas.h : canvas.w
this.position = randint(this.limit)
this.direction = 1
},
before_shade: function(){
if (Math.random() < 0.6) {
this.position = mod(this.position + 1, this.limit)
}
if (Math.random() > 0.8) {
this.direction = randsign()
}
},
shade: function(src, dest, lex, x, y, w, h){
if (this.is_y) {
if (y >= this.position) {
var copy = src.get(x + this.direction, y)
lex.assign(copy)
}
}
else if (x >= this.position) {
var copy = src.get(x, y + this.direction)
lex.assign(copy)
}
return true
},
})
var ScaleTool = ShaderTool.extend({
type: "scale",
scale: 1,
dscale: 0,
speed: 3,
start: function(){
this.__start()
var sign = randsign()
this.x_scale = 1
this.y_scale = 1
this.dx_scale = randsign() * randrange(0.0005, 0.01)
var r = Math.random()
if (r < 0.333) {
this.dy_scale = this.dx_scale * randrange(0.85, 1.25)
}
else if (r < 0.666) {
this.dy_scale = this.dx_scale
}
else {
this.dy_scale = randsign() * randrange(0.0005, 0.01)
}
},
before_shade: function(){
this.x_scale += this.dx_scale
this.y_scale += this.dy_scale
},
shade: function(src, dest, lex, x, y, w, h){
x = (x/w) * 2 - 1
y = (y/h) * 2 - 1
x *= this.x_scale
y *= this.y_scale
x = (x + 1) / 2 * w
y = (y + 1) / 2 * h
var copy = src.get(x, y)
lex.assign(copy)
return true
},
})
var RotateTool = ShaderTool.extend({
type: "rotate",
theta: 0,
d_theta: 0,
start: function(){
this.__start()
var sign = randsign()
this.theta = 0
this.d_theta = randsign() * randrange(0.001, 0.05)
},
before_shade: function(){
this.theta += this.d_theta
},
shade: function(src, dest, lex, x, y, w, h){
x = (x/w) * 2 - 1
y = (y/h) * 2 - 1
var ca = cos(this.theta)
var sa = sin(this.theta)
var a = x * ca - y * sa
var b = x * sa + y * ca
x = (a + 1) / 2 * w
y = (b + 1) / 2 * h
var copy = src.get(x, y)
lex.assign(copy)
return true
},
})
var CycleTool = ShaderTool.extend({
type: "cycle",
n: 0,
speed: 5,
is_recursive: true,
start: function(){
this.__start()
this.n = randsign()
if (random() < 0.2) this.n *= randint(15)
},
shade: function(src, dest, lex, x, y){
lex.bg += this.n
return false
},
})
nopaint.add_tool( new SolidBrush({ weight: 5 }) )
nopaint.add_tool( new ShadowBrush({ weight: 10 }) )
nopaint.add_tool( new EraseBrush({ weight: 5 }) )
nopaint.add_tool( new RandomBrush({ weight: 4 }) )
nopaint.add_tool( new HueBrush({ weight: 5 }) )
nopaint.add_tool( new GrayBrush({ weight: 5 }) )
nopaint.add_tool( new LetterBrush({ weight: 2 }) )
nopaint.add_tool( new RandomLetterBrush({ weight: 12 }) )
nopaint.add_tool( new CloneBrush({ weight: 8 }) )
nopaint.add_tool( new SmearBrush({ weight: 10 }) )
nopaint.add_tool( new FillTool({ weight: 3 }) )
nopaint.add_tool( new FillLetterTool({ weight: 6 }) )
nopaint.add_tool( new StarsTool({ weight: 2 }) )
nopaint.add_tool( new TranslateTool({ weight: 4 }) )
nopaint.add_tool( new CycleTool({ weight: 1 }) )
nopaint.add_tool( new ScaleTool({ weight: 3 }) )
nopaint.add_tool( new RotateTool({ weight: 3 }) )
nopaint.add_tool( new SliceTool({ weight: 4 }) )
nopaint.add_tool( new ColorizeTool({ weight: 1 }) )
nopaint.regenerate_weights()
nopaint.toggle(true)
nopaint.player = new StylePlayer ()
return nopaint
})()