cloning speedup

significantly speed up sprite cloning through partial shallow-copying
of scripts and costumes instead of deep-duplication
dev
Jens Mönig 2016-05-04 16:37:46 +02:00
rodzic d7479f90e4
commit 71333021d4
7 zmienionych plików z 91 dodań i 45 usunięć

Wyświetl plik

@ -145,11 +145,11 @@ radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph,
fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph, fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil, Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject*/ isSnapObject, copy*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2016-May-02'; modules.blocks = '2016-May-04';
var SyntaxElementMorph; var SyntaxElementMorph;
var BlockMorph; var BlockMorph;
@ -3251,7 +3251,14 @@ BlockMorph.prototype.setCategory = function (aString) {
// BlockMorph copying // BlockMorph copying
BlockMorph.prototype.fullCopy = function () { BlockMorph.prototype.fullCopy = function (forClone) {
if (forClone) {
if (this.hasBlockVars()) {
forClone = false;
} else {
return copy(this);
}
}
var ans = BlockMorph.uber.fullCopy.call(this); var ans = BlockMorph.uber.fullCopy.call(this);
ans.removeHighlight(); ans.removeHighlight();
ans.isDraggable = true; ans.isDraggable = true;
@ -3261,22 +3268,15 @@ BlockMorph.prototype.fullCopy = function () {
ans.allChildren().filter(function (block) { ans.allChildren().filter(function (block) {
if (block instanceof SyntaxElementMorph) { if (block instanceof SyntaxElementMorph) {
block.cachedInputs = null; block.cachedInputs = null;
// if (block instanceof InputSlotMorph) {
// block.contents().clearSelection();
// } else
if (block.definition) { if (block.definition) {
block.initializeVariables(); block.initializeVariables();
} }
// } else if (block instanceof CursorMorph) {
// block.destroy();
} }
return !isNil(block.comment); return !isNil(block.comment);
}).forEach(function (block) { }).forEach(function (block) {
var cmnt = block.comment.fullCopy(); var cmnt = block.comment.fullCopy();
block.comment = cmnt; block.comment = cmnt;
cmnt.block = block; cmnt.block = block;
//block.comment = null;
}); });
ans.cachedInputs = null; ans.cachedInputs = null;
return ans; return ans;
@ -3286,6 +3286,12 @@ BlockMorph.prototype.reactToTemplateCopy = function () {
this.forceNormalColoring(); this.forceNormalColoring();
}; };
BlockMorph.prototype.hasBlockVars = function () {
return this.anyChild(function (any) {
return any.definition && any.definition.variableNames.length;
});
};
// BlockMorph events // BlockMorph events
BlockMorph.prototype.mouseClickLeft = function () { BlockMorph.prototype.mouseClickLeft = function () {
@ -5177,7 +5183,7 @@ ScriptsMorph.prototype.init = function (owner) {
// ScriptsMorph deep copying: // ScriptsMorph deep copying:
ScriptsMorph.prototype.fullCopy = function () { ScriptsMorph.prototype.fullCopy = function (forClone) {
var cpy = new ScriptsMorph(), var cpy = new ScriptsMorph(),
pos = this.position(), pos = this.position(),
child; child;
@ -5186,17 +5192,21 @@ ScriptsMorph.prototype.fullCopy = function () {
} }
this.children.forEach(function (morph) { this.children.forEach(function (morph) {
if (!morph.block) { // omit anchored comments if (!morph.block) { // omit anchored comments
child = morph.fullCopy(); child = morph.fullCopy(forClone);
child.setPosition(morph.position().subtract(pos));
cpy.add(child); cpy.add(child);
if (!forClone) {
child.setPosition(morph.position().subtract(pos));
if (child instanceof BlockMorph) { if (child instanceof BlockMorph) {
child.allComments().forEach(function (comment) { child.allComments().forEach(function (comment) {
comment.align(child); comment.align(child);
}); });
} }
} }
}
}); });
if (!forClone) {
cpy.adjustBounds(); cpy.adjustBounds();
}
return cpy; return cpy;
}; };

4
gui.js
Wyświetl plik

@ -70,7 +70,7 @@ isSnapObject*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.gui = '2016-May-02'; modules.gui = '2016-May-04';
// Declarations // Declarations
@ -2835,7 +2835,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn, module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world(); world = this.world();
aboutTxt = 'Snap! 4.0.7\nBuild Your Own Blocks\n\n' aboutTxt = 'Snap! 4.0.7.1\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2016 Jens M\u00F6nig and ' + 'Copyright \u24B8 2016 Jens M\u00F6nig and '
+ 'Brian Harvey\n' + 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n' + 'jens@moenig.org, bh@cs.berkeley.edu\n\n'

Wyświetl plik

@ -2908,10 +2908,15 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=PathFollower
http://snap.berkeley.edu/run#present:Username=jens&ProjectName=cartwheel http://snap.berkeley.edu/run#present:Username=jens&ProjectName=cartwheel
http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
* new Indonesian translation. Yay!! Thank you, Alexander Liu!!
* Translation updates: Slovenian, Portuguese, Chinese * Translation updates: Slovenian, Portuguese, Chinese
* minor bug fixes * minor bug fixes
== v4.0.7 ==== - first class sprites == v4.0.7 ==== - first class sprites
160504
------
* Morphic, Objects, Blocks, Threads, GUI: Partially shallow-copy clones for speed
* new Estonian translation! Yay!! Thanks, Hasso Tepper!
== v4.0.7.1 ==== - cloning speed-up

Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/ /*global modules, contains*/
modules.locale = '2016-May-02'; modules.locale = '2016-May-04';
// Global stuff // Global stuff

Wyświetl plik

@ -337,7 +337,7 @@
(c) an application (c) an application
------------------- -------------------
Of course, most of the time you don't want to just plain use the Of course, most of the time you don't want to just plain use the
standard Morhic World "as is" out of the box, but write your own standard Morphic World "as is" out of the box, but write your own
application (something like Scratch!) in it. For such an application (something like Scratch!) in it. For such an
application you'll create your own morph prototypes, perhaps application you'll create your own morph prototypes, perhaps
assemble your own "window frame" and bring it all to life in a assemble your own "window frame" and bring it all to life in a
@ -1054,10 +1054,9 @@
// Global settings ///////////////////////////////////////////////////// // Global settings /////////////////////////////////////////////////////
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio, /*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
FileList, getBlurredShadowSupport*/
var morphicVersion = '2016-February-24'; var morphicVersion = '2016-May-04';
var modules = {}; // keep track of additional loaded modules var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -1938,7 +1937,7 @@ Rectangle.prototype.round = function () {
Rectangle.prototype.spread = function () { Rectangle.prototype.spread = function () {
// round me by applying floor() to my origin and ceil() to my corner // round me by applying floor() to my origin and ceil() to my corner
// expand by 1 to be on the safe side, this eliminates rounding // expand by 1 to be on the safe side, this eliminates rounding
// artefacts caused by Safari's auto-scaling on retina displays // artifacts caused by Safari's auto-scaling on retina displays
return this.origin.floor().corner(this.corner.ceil()).expandBy(1); return this.origin.floor().corner(this.corner.ceil()).expandBy(1);
}; };
@ -2090,6 +2089,20 @@ Node.prototype.forAllChildren = function (aFunction) {
aFunction.call(null, this); aFunction.call(null, this);
}; };
Node.prototype.anyChild = function (aPredicate) {
// includes myself
var i;
if (aPredicate.call(null, this)) {
return true;
}
for (i = 0; i < this.children.length; i += 1) {
if (this.children[i].anyChild(aPredicate)) {
return true;
}
}
return false;
};
Node.prototype.allLeafs = function () { Node.prototype.allLeafs = function () {
var result = []; var result = [];
this.allChildren().forEach(function (element) { this.allChildren().forEach(function (element) {

Wyświetl plik

@ -82,7 +82,7 @@ SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize, BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph*/ TableMorph, TableFrameMorph*/
modules.objects = '2016-May-02'; modules.objects = '2016-May-04';
var SpriteMorph; var SpriteMorph;
var StageMorph; var StageMorph;
@ -1364,7 +1364,7 @@ SpriteMorph.prototype.init = function (globals) {
// SpriteMorph duplicating (fullCopy) // SpriteMorph duplicating (fullCopy)
SpriteMorph.prototype.fullCopy = function () { SpriteMorph.prototype.fullCopy = function (forClone) {
var c = SpriteMorph.uber.fullCopy.call(this), var c = SpriteMorph.uber.fullCopy.call(this),
myself = this, myself = this,
arr = [], arr = [],
@ -1374,11 +1374,12 @@ SpriteMorph.prototype.fullCopy = function () {
c.color = this.color.copy(); c.color = this.color.copy();
c.blocksCache = {}; c.blocksCache = {};
c.paletteCache = {}; c.paletteCache = {};
c.scripts = this.scripts.fullCopy(); c.scripts = this.scripts.fullCopy(forClone);
c.scripts.owner = c; c.scripts.owner = c;
c.variables = this.variables.copy(); c.variables = this.variables.copy();
c.variables.owner = c; c.variables.owner = c;
c.customBlocks = []; c.customBlocks = [];
if (!forClone) {
this.customBlocks.forEach(function (def) { this.customBlocks.forEach(function (def) {
cb = def.copyAndBindTo(c); cb = def.copyAndBindTo(c);
c.customBlocks.push(cb); c.customBlocks.push(cb);
@ -1386,8 +1387,9 @@ SpriteMorph.prototype.fullCopy = function () {
block.definition = cb; block.definition = cb;
}); });
}); });
}
this.costumes.asArray().forEach(function (costume) { this.costumes.asArray().forEach(function (costume) {
var cst = costume.copy(); var cst = forClone ? costume : costume.copy();
arr.push(cst); arr.push(cst);
if (costume === myself.costume) { if (costume === myself.costume) {
c.costume = cst; c.costume = cst;
@ -1404,12 +1406,11 @@ SpriteMorph.prototype.fullCopy = function () {
c.anchor = null; c.anchor = null;
c.parts = []; c.parts = [];
this.parts.forEach(function (part) { this.parts.forEach(function (part) {
var dp = part.fullCopy(); var dp = part.fullCopy(forClone);
dp.nestingScale = part.nestingScale; dp.nestingScale = part.nestingScale;
dp.rotatesWithAnchor = part.rotatesWithAnchor; dp.rotatesWithAnchor = part.rotatesWithAnchor;
c.attachPart(dp); c.attachPart(dp);
}); });
return c; return c;
}; };
@ -2834,12 +2835,30 @@ SpriteMorph.prototype.remove = function () {
} }
}; };
// SpriteMorph cloning (experimental) // SpriteMorph cloning
/*
clones are temporary, partially shallow copies of sprites that don't
appear as icons in the corral. Clones get deleted when the red stop button
is pressed. Shallow-copying clones' scripts and costumes makes spawning
very fast, so they can be used for particle system simulations.
This speed-up, however, comes at the cost of some detrimental side
effects: Changes to a costume or a script of the original sprite are
in some cases shared with all of its clones, however such shared changes
are hard to predict for users and not actively propagated, so they don't
offer any reliable feature, and will not be supported as such.
Changes to the original sprite's scripts affect all of its clones, unless
the script contains any custom block whose definition contains one or more
block variables (in which case the script does get deep-copied).
The original sprite's scripting area, costumes wardrobe or sounds jukebox
are also not shared. therefore adding or deleting a script, sound or
costume in the original sprite has no effect on any of its clones.
*/
SpriteMorph.prototype.createClone = function () { SpriteMorph.prototype.createClone = function () {
var stage = this.parentThatIsA(StageMorph); var stage = this.parentThatIsA(StageMorph);
if (stage && stage.cloneCount <= 1000) { if (stage && stage.cloneCount <= 2000) {
this.fullCopy().clonify(stage); this.fullCopy(true).clonify(stage);
} }
}; };
@ -6636,7 +6655,6 @@ Costume.prototype.copy = function () {
var canvas = newCanvas(this.extent()), var canvas = newCanvas(this.extent()),
cpy, cpy,
ctx; ctx;
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d');
ctx.drawImage(this.contents, 0, 0); ctx.drawImage(this.contents, 0, 0);
cpy = new Costume(canvas, this.name ? copy(this.name) : null); cpy = new Costume(canvas, this.name ? copy(this.name) : null);

Wyświetl plik

@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, isSnapObject*/ TableFrameMorph, isSnapObject*/
modules.threads = '2016-May-02'; modules.threads = '2016-May-04';
var ThreadManager; var ThreadManager;
var Process; var Process;
@ -354,7 +354,7 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
if (invoke( if (invoke(
pred, pred,
null, null,
null, block.receiver(), // needed for shallow copied clones - was null
50, 50,
'the predicate takes\ntoo long for a\ncustom hat block', 'the predicate takes\ntoo long for a\ncustom hat block',
true // suppress errors => handle them right here instead true // suppress errors => handle them right here instead