roar/js/ext/nopaint.js

897 lines
22 KiB
JavaScript
Raw Normal View History

2018-10-24 15:14:46 +00:00
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
})()