diff options
| -rw-r--r-- | blocks.js | 30 | ||||
| -rw-r--r-- | byob.js | 39 | ||||
| -rw-r--r-- | gui.js | 62 | ||||
| -rwxr-xr-x | history.txt | 9 | ||||
| -rw-r--r-- | lang-de.js | 10 | ||||
| -rw-r--r-- | lists.js | 4 | ||||
| -rw-r--r-- | locale.js | 4 | ||||
| -rw-r--r-- | objects.js | 173 | ||||
| -rw-r--r-- | store.js | 4 |
9 files changed, 309 insertions, 26 deletions
@@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2014-May-02'; +modules.blocks = '2014-June-04'; var SyntaxElementMorph; @@ -913,17 +913,13 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part = new InputSlotMorph( null, false, - { - /* - color : 'color', - fisheye : 'fisheye', - whirl : 'whirl', - pixelate : 'pixelate', - mosaic : 'mosaic', - brightness : 'brightness', - */ - ghost : ['ghost'] - }, + { brightness : ['brightness'], + ghost : ['ghost'], + negative : ['negative'], + comic: ['comic'], + duplicate: ['duplicate'], + confetti: ['confetti'] + }, true ); part.setContents(['ghost']); @@ -4957,6 +4953,14 @@ ScriptsMorph.prototype.cleanUp = function () { }; ScriptsMorph.prototype.exportScriptsPicture = function () { + var pic = this.scriptsPicture(); + if (pic) { + window.open(pic.toDataURL()); + } +}; + +ScriptsMorph.prototype.scriptsPicture = function () { + // private - answer a canvas containing the pictures of all scripts var boundingBox, pic, ctx; if (this.children.length === 0) {return; } boundingBox = this.children[0].fullBounds(); @@ -4977,7 +4981,7 @@ ScriptsMorph.prototype.exportScriptsPicture = function () { ); } }); - window.open(pic.toDataURL()); + return pic; }; ScriptsMorph.prototype.addComment = function () { @@ -106,7 +106,7 @@ SymbolMorph, isNil*/ // Global stuff //////////////////////////////////////////////////////// -modules.byob = '2014-May-02'; +modules.byob = '2014-Jun-04'; // Declarations @@ -336,6 +336,43 @@ CustomBlockDefinition.prototype.parseSpec = function (spec) { return parts; }; +// CustomBlockDefinition picturing + +CustomBlockDefinition.prototype.scriptsPicture = function () { + var scripts, proto, block, comment; + + scripts = new ScriptsMorph(); + scripts.cleanUpMargin = 10; + proto = new PrototypeHatBlockMorph(this); + proto.setPosition(scripts.position().add(10)); + if (this.comment !== null) { + comment = this.comment.fullCopy(); + proto.comment = comment; + comment.block = proto; + } + if (this.body !== null) { + proto.nextBlock(this.body.expression.fullCopy()); + } + scripts.add(proto); + proto.fixBlockColor(null, true); + this.scripts.forEach(function (element) { + block = element.fullCopy(); + block.setPosition(scripts.position().add(element.position())); + scripts.add(block); + if (block instanceof BlockMorph) { + block.allComments().forEach(function (comment) { + comment.align(block); + }); + } + }); + proto.allComments().forEach(function (comment) { + comment.align(proto); + }); + proto.children[0].fixLayout(); + scripts.fixMultiArgs(); + return scripts.scriptsPicture(); +}; + // CustomCommandBlockMorph ///////////////////////////////////////////// // CustomCommandBlockMorph inherits from CommandBlockMorph: @@ -69,7 +69,7 @@ SpeechBubbleMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2014-May-26'; +modules.gui = '2014-Jun-04'; // Declarations @@ -2350,6 +2350,15 @@ IDE_Morph.prototype.projectMenu = function () { 'show global custom block definitions as XML\nin a new browser window' ); + if (shiftClicked) { + menu.addItem( + 'Export all scripts as pic...', + function () {myself.exportScriptsPicture(); }, + 'show a picture of all scripts\nand block definitions', + new Color(100, 0, 0) + ); + } + menu.addLine(); menu.addItem( 'Import tools', @@ -2526,6 +2535,7 @@ IDE_Morph.prototype.aboutSnap = function () { + '\n\nNathan Dinsmore: Saving/Loading, Snap-Logo Design, ' + 'countless bugfixes' + '\nKartik Chandra: Paint Editor' + + '\nYuan Yuan: Graphic Effects' + '\nIan Reynolds: UI Design, Event Bindings, ' + 'Sound primitives' + '\nIvan Motyashov: Initial Squeak Porting' @@ -2847,6 +2857,56 @@ IDE_Morph.prototype.exportSprite = function (sprite) { + '</sprites>'); }; +IDE_Morph.prototype.exportScriptsPicture = function () { + var pics = [], + pic, + padding = 20, + w = 0, + h = 0, + y = 0, + ctx; + + // collect all script pics + this.sprites.asArray().forEach(function (sprite) { + pics.push(sprite.image); + pics.push(sprite.scripts.scriptsPicture()); + sprite.customBlocks.forEach(function (def) { + pics.push(def.scriptsPicture()); + }); + }); + pics.push(this.stage.image); + pics.push(this.stage.scripts.scriptsPicture()); + this.stage.customBlocks.forEach(function (def) { + pics.push(def.scriptsPicture()); + }); + + // collect global block pics + this.stage.globalBlocks.forEach(function (def) { + pics.push(def.scriptsPicture()); + }); + + pics = pics.filter(function (each) {return !isNil(each); }); + + // determine dimensions of composite + pics.forEach(function (each) { + w = Math.max(w, each.width); + h += (each.height); + h += padding; + }); + h -= padding; + pic = newCanvas(new Point(w, h)); + ctx = pic.getContext('2d'); + + // draw all parts + pics.forEach(function (each) { + ctx.drawImage(each, 0, y); + y += padding; + y += each.height; + }); + + window.open(pic.toDataURL()); +}; + IDE_Morph.prototype.openProjectString = function (str) { var msg, myself = this; diff --git a/history.txt b/history.txt index bd4d9fe..a6b3276 100755 --- a/history.txt +++ b/history.txt @@ -2139,3 +2139,12 @@ ______ * Objects: Fixed #445 (minor search + zoom issues) * Localization additions and Portuguese translation update, thanks, Manuel! * GUI, cloud: Show last-changed-timestamp when opening cloud projects + +140604 +------ +* Blocks: refactor “script pics” feature +* BYOB: new scriptsPicture() method for custom block definitions +* GUI: new (hidden) feature: “Export all scripts as pic” (including custom block refs) +* Graphic effects!!! Yay, thanks, Yuan! +* Bug fixes from Nathan, yay, thank, Nathan!! +* German translation update @@ -185,7 +185,7 @@ SnapTranslator.dict.de = { 'translator_e-mail': 'jens@moenig.org', // optional 'last_changed': - '2014-02-13', // this, too, will appear in the Translators tab + '2014-06-04', // this, too, will appear in the Translators tab // GUI // control bar: @@ -1122,8 +1122,16 @@ SnapTranslator.dict.de = { 'Leer', // graphical effects + 'brightness': + 'Helligeit', 'ghost': 'Durchsichtigkeit', + 'negative': + 'Farbumkehr', + 'comic': + 'Moire', + 'confetti': + 'Farbverschiebung', // keys 'space': @@ -61,7 +61,7 @@ PushButtonMorph, SyntaxElementMorph, Color, Point, WatcherMorph, StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph, MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/ -modules.lists = '2014-January-09'; +modules.lists = '2014-Jun-04'; var List; var ListWatcherMorph; @@ -305,7 +305,7 @@ List.prototype.equalTo = function (other) { if (this.length() !== other.length()) { return false; } - for (i = 0; i < this.length(); i += 1) { + for (i = 1; i <= this.length(); i += 1) { if (!snapEquals(this.at(i), other.at(i))) { return false; } @@ -42,7 +42,7 @@ /*global modules, contains*/ -modules.locale = '2014-May-26'; +modules.locale = '2014-Jun-04'; // Global stuff @@ -149,7 +149,7 @@ SnapTranslator.dict.de = { 'translator_e-mail': 'jens@moenig.org', 'last_changed': - '2014-02-13' + '2014-06-04' }; SnapTranslator.dict.it = { @@ -61,6 +61,7 @@ sound handling Achal Dave contributed research and prototyping for creating music using the Web Audio API + Yuan Yuan contributed graphic effects for costumes */ @@ -124,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2014-May-26'; +modules.objects = '2014-Jun-04'; var SpriteMorph; var StageMorph; @@ -1292,6 +1293,18 @@ SpriteMorph.prototype.init = function (globals) { this.idx = 0; // not to be serialized (!) - used for de-serialization this.wasWarped = false; // not to be serialized, used for fast-tracking + this.graphicsValues = { 'negative': 0, + 'fisheye': 0, + 'whirl': 0, + 'pixelate': 0, + 'mosaic': 0, + 'brightness': 0, + 'color': 0, + 'comic': 0, + 'duplicate': 0, + 'confetti': 0 + }; + SpriteMorph.uber.init.call(this); this.isDraggable = true; @@ -1421,6 +1434,9 @@ SpriteMorph.prototype.drawNew = function () { ctx.rotate(radians(facing - 90)); ctx.drawImage(pic.contents, 0, 0); + // apply graphics effects to image + this.image = this.applyGraphicsEffects(this.image); + // adjust my position to the rotation this.setCenter(currentCenter, true); // just me @@ -1443,6 +1459,7 @@ SpriteMorph.prototype.drawNew = function () { this.setCenter(currentCenter, true); // just me SpriteMorph.uber.drawNew.call(this, facing); this.rotationOffset = this.extent().divideBy(2); + this.image = this.applyGraphicsEffects(this.image); if (isLoadingCostume) { // retry until costume is done loading cst = this.costume; handle = setInterval( @@ -2831,14 +2848,132 @@ SpriteMorph.prototype.changeScale = function (delta) { this.setScale(this.getScale() + (+delta || 0)); }; -// SpriteMorph graphic effects +// Spritemorph graphic effects + +SpriteMorph.prototype.graphicsChanged = function () { + var myself = this; + return Object.keys(this.graphicsValues).some( + function (any) { + return myself.graphicsValues[any] < 0 || + myself.graphicsValues[any] > 0; + } + ); +}; + +SpriteMorph.prototype.applyGraphicsEffects = function (canvas) { +// For every effect: apply transform of that effect(canvas, stored value) +// The future: write more effects here + var ctx, imagedata, pixels, newimagedata; + + function transform_negative(p, value) { + var i, rcom, gcom, bcom; + if (value !== 0) { + for (i = 0; i < p.length; i += 4) { + rcom = 255 - p[i]; + gcom = 255 - p[i + 1]; + bcom = 255 - p[i + 2]; + + if (p[i] < rcom) { //compare to the complement + p[i] += value; + } else if (p[i] > rcom) { + p[i] -= value; + } + if (p[i + 1] < gcom) { + p[i + 1] += value; + } else if (p[i + 1] > gcom) { + p[i + 1] -= value; + } + if (p[i + 2] < bcom) { + p[i + 2] += value; + } else if (p[i + 2] > bcom) { + p[i + 2] -= value; + } + } + } + return p; + } + + function transform_brightness(p, value) { + var i; + if (value !== 0) { + for (i = 0; i < p.length; i += 4) { + p[i] += value; //255 = 100% of this color + p[i + 1] += value; + p[i + 2] += value; + } + } + return p; + } + + function transform_comic(p, value) { + var i; + if (value !== 0) { + for (i = 0; i < p.length; i += 4) { + p[i] += Math.sin(i * value) * 127 + 128; + p[i + 1] += Math.sin(i * value) * 127 + 128; + p[i + 2] += Math.sin(i * value) * 127 + 128; + } + } + return p; + } + + function transform_duplicate(p, value) { + var i; + if (value !== 0) { + for (i = 0; i < p.length; i += 4) { + p[i] = p[i * value]; + p[i + 1] = p[i * value + 1]; + p[i + 2] = p[i * value + 2]; + p[i + 3] = p[i * value + 3]; + } + } + return p; + } + + function transform_confetti(p, value) { + var i; + if (value !== 0) { + for (i = 0; i < p.length; i += 1) { + p[i] = Math.sin(value * p[i]) * 127 + p[i]; + } + } + return p; + } + + if (this.graphicsChanged()) { + ctx = canvas.getContext("2d"); + imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height); + pixels = imagedata.data; + + //A sprite should wear all 7 effects at once + /*pixels = transform_whirl(pixels, this.graphicsValues.whirl);*/ + pixels = transform_negative(pixels, this.graphicsValues.negative); + pixels = transform_brightness(pixels, this.graphicsValues.brightness); + pixels = transform_comic(pixels, this.graphicsValues.comic); + /*pixels = transform_pixelate(pixels, this.graphicsValues.pixelate);*/ + pixels = transform_duplicate(pixels, this.graphicsValues.duplicate); + /*pixels = transform_color(pixels, this.graphicsValues.color);*/ + /*pixels = transform_fisheye(pixels, this.graphicsValues.fisheye);*/ + pixels = transform_confetti(pixels, this.graphicsValues.confetti); + + //the last object will have all the transformations done on it + newimagedata = ctx.createImageData(imagedata); //make imgdata object + newimagedata.data.set(pixels); //add transformed pixels + ctx.putImageData(newimagedata, 0, 0); + } + + return canvas; +}; SpriteMorph.prototype.setEffect = function (effect, value) { var eff = effect instanceof Array ? effect[0] : null; if (eff === 'ghost') { this.alpha = 1 - Math.min(Math.max(+value || 0, 0), 100) / 100; - this.changed(); + } else { + this.graphicsValues[eff] = value; } + this.drawNew(); + this.changed(); }; SpriteMorph.prototype.getGhostEffect = function () { @@ -2849,10 +2984,18 @@ SpriteMorph.prototype.changeEffect = function (effect, value) { var eff = effect instanceof Array ? effect[0] : null; if (eff === 'ghost') { this.setEffect(effect, this.getGhostEffect() + (+value || 0)); + } else { + this.setEffect(effect, this.graphicsValues[eff] + value); } }; SpriteMorph.prototype.clearEffects = function () { + var effect; + for (effect in this.graphicsValues) { + if (this.graphicsValues.hasOwnProperty(effect)) { + this.setEffect([effect], 0); + } + } this.setEffect(['ghost'], 0); }; @@ -4006,6 +4149,18 @@ StageMorph.prototype.init = function (globals) { this.trailsCanvas = null; this.isThreadSafe = false; + this.graphicsValues = { 'negative': 0, + 'fisheye': 0, + 'whirl': 0, + 'pixelate': 0, + 'mosaic': 0, + 'brightness': 0, + 'color': 0, + 'comic': 0, + 'duplicate': 0, + 'confetti': 0 + }; + StageMorph.uber.init.call(this); this.acceptsDrops = false; @@ -4070,6 +4225,7 @@ StageMorph.prototype.drawNew = function () { (this.width() / this.scale - this.costume.width()) / 2, (this.height() / this.scale - this.costume.height()) / 2 ); + this.image = this.applyGraphicsEffects(this.image); } }; @@ -4395,7 +4551,7 @@ StageMorph.prototype.processKeyEvent = function (event, action) { default: keyName = String.fromCharCode(event.keyCode || event.charCode); if (event.ctrlKey || event.metaKey) { - keyName = 'ctrl ' + keyName; + keyName = 'ctrl ' + (event.shiftKey ? 'shift ' : '') + keyName; } } action.call(this, keyName); @@ -4420,6 +4576,9 @@ StageMorph.prototype.fireKeyEvent = function (key) { if (evt === 'ctrl s') { return this.parentThatIsA(IDE_Morph).save(); } + if (evt === 'ctrl shift s') { + return this.parentThatIsA(IDE_Morph).saveProjectsBrowser(); + } if (evt === 'esc') { return this.fireStopAllEvent(); } @@ -5093,6 +5252,12 @@ StageMorph.prototype.reportCostumes // StageMorph graphic effects +StageMorph.prototype.graphicsChanged + = SpriteMorph.prototype.graphicsChanged; + +StageMorph.prototype.applyGraphicsEffects + = SpriteMorph.prototype.applyGraphicsEffects; + StageMorph.prototype.setEffect = SpriteMorph.prototype.setEffect; @@ -61,7 +61,7 @@ SyntaxElementMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2014-May-02'; +modules.store = '2014-Jun-04'; // XML_Serializer /////////////////////////////////////////////////////// @@ -1032,7 +1032,7 @@ SnapSerializer.prototype.loadInput = function (model, input, block) { input.setColor(this.loadColor(model.contents)); } else { val = this.loadValue(model); - if (val) { + if (!isNil(val) && input.setContents) { input.setContents(this.loadValue(model)); } } |
