Morph subclass: #JSCanvas instanceVariableNames: 'image canvas this fgcolor' classVariableNames: 'CanvasProto PointProto' poolDictionaries: '' category: 'Javascript-Canvas'! !JSCanvas methodsFor: 'accessing' stamp: 'tak 9/18/2007 00:16'! jsAt: key key == #width ifTrue: [^ self width]. key == #height ifTrue: [^ self height]. key == #color ifTrue: [^ fgcolor]. ^ this jsAt: key! ! !JSCanvas methodsFor: 'accessing' stamp: 'tak 9/18/2007 00:17'! jsAt: key put: val key == #width ifTrue: [^ self width: val]. key == #height ifTrue: [^ self height: val]. key == #color ifTrue: [fgcolor := (Color r: (val jsAt: #r) g: (val jsAt: #g) b: (val jsAt: #b) alpha: (val jsAt: #alpha))]. ^ this jsAt: key put: val! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/15/2007 14:17'! jsDelete self delete! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/18/2007 13:29'! jsDrawString: s at: pt | aFont | aFont := ((this jsAt: #font) jsAt: #'__font__') ifNil: [TextStyle defaultFont].. canvas drawString: s at: pt font: aFont color: fgcolor. self invalidLocalRect: (pt extent: ((s inject: 0 into: [:total :next | total + (aFont widthOf: next)]) @ aFont height))! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/18/2007 13:30'! jsFillRectangleTopLeft: p1 bottomRight: p2 | rect | rect := p1 extent: p2 - p1. canvas fillRectangle: rect color: fgcolor. self invalidLocalRect: rect.! ! !JSCanvas methodsFor: 'accessing' stamp: 'tak 9/17/2007 23:15'! jsFlush "self invalidRect: self bounds"! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/15/2007 14:17'! jsHide self hide! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/14/2007 16:46'! jsKeys ^ this jsKeys asSet addAll: #(width height); asArray! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/18/2007 13:31'! jsLineFrom: p1 to: p2 canvas line: p1 to: p2 width: 1 color: fgcolor! ! !JSCanvas methodsFor: 'accessing' stamp: 'aw 9/15/2007 14:17'! jsShow self show! ! !JSCanvas methodsFor: 'drawing' stamp: 'tak 9/18/2007 01:36'! drawOn: aCanvas aCanvas translucentImage: image at: self bounds origin sourceRect: (0 @ 0 extent: self extent)! ! !JSCanvas methodsFor: 'drawing' stamp: 'tak 9/18/2007 00:36'! invalidLocalRect: aRectangle self invalidRect: ((aRectangle translateBy: self topLeft) intersect: self bounds)! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/15/2007 22:31'! handlesKeyboard: evt ^ true! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/18/2007 13:13'! handlesMouseDown: evt ^ true! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/15/2007 22:41'! keyDown: evt (this jsAt: #keyDown) jsCallWithArguments: {evt keyValue}! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/15/2007 22:40'! keyStroke: evt (this jsAt: #keyStroke) jsCallWithArguments: {evt keyString}! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/15/2007 22:41'! keyUp: evt (this jsAt: #keyUp) jsCallWithArguments: {evt keyValue}! ! !JSCanvas methodsFor: 'event handling' stamp: 'aw 9/18/2007 14:24'! mouseDown: evt (this jsAt: #mouseDown) jsCallWithArguments: {evt position - self position}! ! !JSCanvas methodsFor: 'initialization' stamp: 'aw 9/18/2007 16:33'! initialize super initialize. self width: 500; height: 500. image := Form extent: 2000@2000 depth: 16. image fillBlack. canvas := image getCanvas. fgcolor := Color white. this := CanvasProto makeChild jsAt: #fontSize put: 10; yourself. self startStepping; openInWorld.! ! !JSCanvas methodsFor: 'stepping and presenter' stamp: 'aw 9/16/2007 11:14'! step (this jsAt: #step) jsCallWithArguments: #()! ! !JSCanvas methodsFor: 'stepping and presenter' stamp: 'aw 9/16/2007 11:04'! stepTime ^ this jsAt: #stepTime! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! JSCanvas class instanceVariableNames: ''! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/18/2007 13:39'! addCanvasSupportTo: g | canvasConstructor pointConstructor fontConstructor | canvasConstructor := (JSObject functionWithEnv: g args: #() body: [:ctxt | ctxt jsAt: #this put: (JSCanvas new openInWorld hide jsAt: #font put: ((g jsAt: #Font) jsNewWithArguments: #(Accuny 15)); yourself) ]) jsAt: #prototype put: CanvasProto; yourself. pointConstructor := (JSObject functionWithEnv: g args: #(x y) body: [:ctxt | ctxt jsAt: #this put: (ctxt jsAt: #x) @ (ctxt jsAt: #y) ]) jsAt: #prototype put: PointProto; yourself. fontConstructor := (JSObject functionWithEnv: g args: #(family size) body: [:ctxt | | family size | family := ctxt jsAt: #family. size := ctxt jsAt: #size. (ctxt jsAt: #this) jsAt: #'__font__' put: (StrikeFont familyName: family asSymbol pointSize: size); jsAt: #family put: family; jsAt: #size put: size ]). (fontConstructor jsAt: #prototype) jsAt: #stringWidth put: (JSObject functionWithEnv: g args: #(s) body: [:ctxt | ((ctxt jsAt: #this) jsAt: #'__font__') perform: #approxWidthOfText: withArguments: {(ctxt jsAt: #s) asText} ]). g jsAt: #Canvas put: canvasConstructor; jsAt: #Point put: pointConstructor; jsAt: #Font put: fontConstructor. JSObject eval: ' Point.prototype["+"] = function(that) { return new Point(this.x + that.x, this.y + that.y) } Point.prototype["-"] = function(that) { return new Point(this.x - that.x, this.y - that.y) } Point.prototype.toString = function() { return "Point(x=" + this.x.toString() + ", y=" + this.y.toString() + ")" } Color = function(r, g, b, alpha) { this.r = r this.g = g this.b = b this.alpha = alpha == undefined ? 1.0 : alpha } Color.prototype.darker = function() { return new Color(this.r / 2, this.g / 2, this.b / 2, this.alpha) } Color.red = new Color(1, 0, 0) Color.green = new Color(0, 1, 0) Color.blue = new Color(0, 0, 1) Color.black = new Color(0, 0, 0) Color.white = new Color(1, 1, 1) Color.gray = new Color(0.5, 0.5, 0.5) Color.darkGray = Color.gray.darker() Color.yellow = new Color(1, 1, 0) Font.prototype.toString = function() { return "[" + this.family + ", " + this.size.toString() + "]" } ' withGlobal: g.! ! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/17/2007 18:00'! fontTest " JSCanvas fontTest " | g | g := JSObject global makeChild. JSCanvas addCanvasSupportTo: g. JSObject eval: ' c = new Canvas() c.show() c.stepTime = 10 c.font = new Font("Accuny", 30) function delta() { return Math.round(Math.random() * 2) - 1 } function Word(s, pos) { this.chars =s this.pos = pos } Word.prototype.draw = function() { c.drawString(this.chars, this.pos) } Word.prototype.jiggle = function() { this.pos.x += delta() this.pos.y += delta() } strings = ["this", "is", "a", "test"] string = "" for (var idx in strings) string += (idx == 0 ? "" : " ") + strings[idx] var x = Math.round((c.width - c.font.stringWidth(string)) / 2) var y = Math.round((c.height - c.font.size) / 2) words = [] for (var idx in strings) { if (idx > 0) x += c.font.stringWidth(strings[idx - 1] + " ") words.push(new Word(strings[idx], new Point(x, y))) } color = new Color(0.5, 0.5, 0.5) Color.prototype.jiggle = function() { this.r += delta() / 128 this.g += delta() / 128 this.b += delta() / 128 } c.step = function() { /* c.color = Color.white for (var idx in words) words[idx].draw() c.flush() */ color.jiggle() c.color = color for (var idx in words) { words[idx].jiggle() words[idx].draw() } c.flush() } ' withGlobal: g ! ! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/18/2007 13:37'! initialize " JSCanvas initialize " CanvasProto := JSObject objectProto makeChild jsAt: #stepTime put: 250; jsAt: #step put: (JSObject functionWithEnv: JSObject global args: #() body: [:ctxt | ]); bridgeSqueakMethod: #jsShow as: #show; bridgeSqueakMethod: #jsHide as: #hide; bridgeSqueakMethod: #jsFlush as: #flush; bridgeSqueakMethod: #jsLineFrom:to: as: #line; bridgeSqueakMethod: #jsFillRectangleTopLeft:bottomRight: as: #fillRectangle; bridgeSqueakMethod: #jsDrawString:at: as: #drawString; jsAt: #keyUp put: (JSObject functionWithEnv: JSObject global args: #(evt) body: [:ctxt | ]); jsAt: #keyDown put: (JSObject functionWithEnv: JSObject global args: #(evt) body: [:ctxt | ]); jsAt: #keyStroke put: (JSObject functionWithEnv: JSObject global args: #(evt) body: [:ctxt | ]); jsAt: #mouseDown put: (JSObject functionWithEnv: JSObject global args: #(evt) body: [:ctxt | ]); yourself. PointProto := JSObject objectProto makeChild.! ! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/18/2007 13:18'! pointProto ^ PointProto! ! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/17/2007 14:17'! tetris " JSCanvas tetris " | g | g := JSObject global makeChild. JSCanvas addCanvasSupportTo: g. JSObject eval: ' function randomInt(n) { return Math.round((Math.random() * (n - 1))) } Array.prototype.randomElement = function() { return this[randomInt(this.length)] } Array.prototype.mirror = function() { var r = new Array(this.length) for (var i = 0; i < this.length; i++) { r[i] = new Array(this[i].length) for (var j = 0; j < this[i].length; j++) r[i][this[i].length - j - 1] = this[i][j] } return r } Array.prototype.transpose = function() { var r = new Array(this[0].length) for (var j = 0; j < this[0].length; j++) { r[j] = new Array(this.length) for (var i = 0; i < this.length; i++) r[j][i] = this[i][j] } return r } Array.prototype.flip = function() { var r = this.mirror().transpose() r.color = this.color return r } Array.prototype.setColor = function(c) { this.color = c return this } var blocks = [ [[1, 1, 1, 1]].setColor(Color.red), [[1, 0, 0], [1, 1, 1]].setColor(Color.yellow), [[0, 1, 1], [1, 1, 0]].setColor(Color.green), [[0, 1, 0], [1, 1, 1]].setColor(Color.darkGray), [[1, 1], [1, 1]].setColor(Color.blue) ] function nextBlock() { var r = blocks.randomElement() return randomInt(2) % 2 == 0 ? r : r.flip() } var x = 5, y = -5, block = nextBlock() var bl = 15, cols = 10, rows = 20 var spaces = new Array(rows) for (var i = 0; i < rows; i++) { spaces[i] = new Array(cols) for (var j = 0; j < cols; j++) spaces[i][j] = 0 } function collides(x, y, b) { for (var i = 0; i < b.length; i++) for (var j = 0; j < b[i].length; j++) if (b[i][j] && (x + j < 0 || x + j >= cols || y + i >= rows || y + i >= 0 && spaces[y + i][x + j])) return true return false } function park(x, y, b) { for (var i = 0; i < b.length; i++) for (var j = 0; j < b[i].length; j++) if ( b[i][j] !!= 0 && x + j >= 0 && x + j < cols && y + i >= 0 && y + i < rows) spaces[y + i][x + j] = b.color var i = rows - 1 while (i >= 0) { var complete = true for (var j = 0; j < cols; j++) if (spaces[i][j] == 0) complete = false if (complete) { for (var ii = i; ii > 0; ii--) spaces[ii] = spaces[ii - 1] spaces[0] = new Array(cols) for (var j = 0; j < cols; j++) spaces[0][j] = 0 } else i-- } } function drawSquare(color, x, y) { c.color = color c.fillRectangle( new Point(x * bl, y * bl), new Point((x + 1) * bl - 1, (y + 1) * bl - 1) ) c.color = color.darker() c.line( new Point(x * bl, (y + 1) * bl - 1), new Point((x + 1) * bl - 1, (y + 1) * bl - 1) ) c.line( new Point((x + 1) * bl - 1, y * bl), new Point((x + 1) * bl - 1, (y + 1) * bl - 1) ) } function draw() { c.color = Color.black c.fillRectangle(new Point(0, 0), new Point(cols * bl, rows * bl)) for (var i = 0; i < rows; i++) for (j = 0; j < cols; j++) if (spaces[i][j]) drawSquare(spaces[i][j], j, i) for (var i = 0; i < block.length; i++) for (var j = 0; j < block[i].length; j++) if (block[i][j]) drawSquare(block.color, x + j, y + i) } c = new Canvas() c.width = cols * bl c.height = rows * bl c.keyStroke = function(k) { switch (k) { case "": var midx = Math.round(x + block[0].length / 2) var color = block.color var tmpBlock = block.flip(), tmpx = Math.round(midx - block[0].length / 2) if (!!collides(tmpx, y, tmpBlock)) { x = tmpx block = tmpBlock } break case "": if (!!collides(x, y + 1, block)) y++ break case "": if (!!collides(x - 1, y, block)) x-- break case "": if (!!collides(x + 1, y, block)) x++ break } draw() } c.flush() c.show() c.stepTime = 250 c.step = function() { if (collides(x, y + 1, block)) { park(x, y, block) block = nextBlock() y = -block.length x = Math.round((cols - block[0].length) / 2) } else y++ draw() } ' withGlobal: g! ! !JSCanvas class methodsFor: 'as yet unclassified' stamp: 'aw 9/18/2007 19:10'! wanderingLetters " JSCanvas wanderingLetters " | g | g := JSObject global makeSandbox. JSCanvas addCanvasSupportTo: g. JSObject eval: ' canvas = new Canvas() canvas.font = new Font("Accuny", 30) function Thing() {} Thing.prototype.pos = new Point(0, 0) Thing.prototype.width = 0 Thing.prototype.wordWidth = 0 Thing.prototype.height = canvas.font.size Thing.prototype.randomizePos = function() { this.pos = new Point(Math.randomInt(canvas.width), Math.randomInt(canvas.height)) } Thing.prototype.draw = function() { if (this == cursor) { canvas.color = Color.yellow canvas.line(this.pos, this.pos + new Point(0, this.height)) canvas.color = Color.blue } } Thing.prototype.step = function() { this.draw() } Thing.prototype.mouseDown = function(pos) { pos -= this.pos if (pos.x >= 0 && pos.x < this.width && pos.y >= 0 && pos.y < this.height) { //alert(this) cursor = this } } Thing.prototype.moveToward = function(thing) { var dx = this.pos.x - (thing.pos.x + thing.width) var dy = this.pos.y - thing.pos.y if (dx !!= 0) this.pos.x -= dx * 0.85 if (dy !!= 0) this.pos.y -= dy * 0.85 } Thing.prototype.toString = function() { return "Thing" } function Start() { } Start.prototype = new Thing() Start.prototype.toString = function() { return "Start" } Start.prototype.prev = null function End() { this.randomizePos() }s End.prototype = new Thing() End.prototype.toString = function() { return "End" } End.prototype.next = null End.prototype.step = function() { this.moveToward(this.prev) this.draw() } function Letter(letter) { this.randomizePos() this.letter = letter this.width = canvas.font.stringWidth(letter) } Letter.prototype = new Thing() Letter.prototype.draw = function() { canvas.drawString(this.letter, this.pos) this.__proto__.__proto__.draw.call(this) } Letter.prototype.isHead = function() { return !!(this.prev instanceof Letter) } Letter.prototype.step = function() { this.wordWidth = this.next.wordWidth + this.width var prevRight = this.prev.pos.x + this.prev.width if (this.isHead() && prevRight + this.wordWidth > canvas.width) { this.pos.x = 0 this.pos.y = this.prev.pos.y + this.height } else this.moveToward(this.prev) this.draw() } Letter.prototype.toString = function() { return "Letter " + this.letter } function Space() { this.randomizePos() } Space.prototype = new Thing() Space.prototype.width = canvas.font.stringWidth(" ") Space.prototype.wordWidth = Space.prototype.width Space.prototype.step = function() { this.moveToward(this.prev) this.draw() } Space.prototype.toString = function() { return "Space" } text = "There was me, that is Alex, and my three droogs, that is Pete, Georgie, and Dim and we sat in the Korova milk bar "// + "trying to make up our rassoodocks what to do with the evening. The Korova milk bar sold milkplus, milk plus vellocet or " + "synthemesc or drencrom which is what we were drinking. This would sharpen you up and make you ready for a bit of " + "the old ultra-violence." function makeThing(c) { return c == " " ? new Space() : new Letter(c) } start = new Start() last = start for (var idx in text) { var c = text.charAt(idx) last.next = makeThing(c) last.next.prev = last last = last.next } last.next = new End() end = last.next end.prev = last cursor = end //start.next canvas.stepTime = 100 canvas.step = function() { canvas.color = Color.black canvas.fillRectangle(new Point(0, 0), new Point(canvas.width, canvas.height)) canvas.color = Color.blue var x = start while (x !!= null) { x.step() x = x.next } } canvas.mouseDown = function(pos) { cursor = null var x = start while (x !!= null) { x.mouseDown(pos) x = x.next } if (cursor == null) cursor = end } canvas.keyStroke = function(k) { if (k == "") { if (cursor.prev !!= null && cursor.prev.prev !!= null) { cursor.prev.prev.next = cursor cursor.prev = cursor.prev.prev } } else { var newThing = makeThing(k) newThing.next = cursor newThing.prev = cursor.prev cursor.prev.next = newThing cursor.prev = newThing cursor = newThing.next } } canvas.show() ' withGlobal: g ! ! JSCanvas initialize!