Skip to content

Commit

Permalink
cloning speedup
Browse files Browse the repository at this point in the history
significantly speed up sprite cloning through partial shallow-copying
of scripts and costumes instead of deep-duplication
  • Loading branch information
jmoenig committed May 4, 2016
1 parent d7479f9 commit 7133302
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 45 deletions.
46 changes: 28 additions & 18 deletions blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph,
fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject*/
isSnapObject, copy*/

// Global stuff ////////////////////////////////////////////////////////

modules.blocks = '2016-May-02';
modules.blocks = '2016-May-04';

var SyntaxElementMorph;
var BlockMorph;
Expand Down Expand Up @@ -3251,7 +3251,14 @@ BlockMorph.prototype.setCategory = function (aString) {

// 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);
ans.removeHighlight();
ans.isDraggable = true;
Expand All @@ -3261,22 +3268,15 @@ BlockMorph.prototype.fullCopy = function () {
ans.allChildren().filter(function (block) {
if (block instanceof SyntaxElementMorph) {
block.cachedInputs = null;
// if (block instanceof InputSlotMorph) {
// block.contents().clearSelection();
// } else
if (block.definition) {
block.initializeVariables();
}
// } else if (block instanceof CursorMorph) {
// block.destroy();
}
return !isNil(block.comment);
}).forEach(function (block) {
var cmnt = block.comment.fullCopy();
block.comment = cmnt;
cmnt.block = block;
//block.comment = null;

});
ans.cachedInputs = null;
return ans;
Expand All @@ -3286,6 +3286,12 @@ BlockMorph.prototype.reactToTemplateCopy = function () {
this.forceNormalColoring();
};

BlockMorph.prototype.hasBlockVars = function () {
return this.anyChild(function (any) {
return any.definition && any.definition.variableNames.length;
});
};

// BlockMorph events

BlockMorph.prototype.mouseClickLeft = function () {
Expand Down Expand Up @@ -5177,7 +5183,7 @@ ScriptsMorph.prototype.init = function (owner) {

// ScriptsMorph deep copying:

ScriptsMorph.prototype.fullCopy = function () {
ScriptsMorph.prototype.fullCopy = function (forClone) {
var cpy = new ScriptsMorph(),
pos = this.position(),
child;
Expand All @@ -5186,17 +5192,21 @@ ScriptsMorph.prototype.fullCopy = function () {
}
this.children.forEach(function (morph) {
if (!morph.block) { // omit anchored comments
child = morph.fullCopy();
child.setPosition(morph.position().subtract(pos));
child = morph.fullCopy(forClone);
cpy.add(child);
if (child instanceof BlockMorph) {
child.allComments().forEach(function (comment) {
comment.align(child);
});
if (!forClone) {
child.setPosition(morph.position().subtract(pos));
if (child instanceof BlockMorph) {
child.allComments().forEach(function (comment) {
comment.align(child);
});
}
}
}
});
cpy.adjustBounds();
if (!forClone) {
cpy.adjustBounds();
}
return cpy;
};

Expand Down
4 changes: 2 additions & 2 deletions gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ isSnapObject*/

// Global stuff ////////////////////////////////////////////////////////

modules.gui = '2016-May-02';
modules.gui = '2016-May-04';

// Declarations

Expand Down Expand Up @@ -2835,7 +2835,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
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 '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
Expand Down
7 changes: 6 additions & 1 deletion history.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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#cloud:Username=jens&ProjectName=rotation

* new Indonesian translation. Yay!! Thank you, Alexander Liu!!
* Translation updates: Slovenian, Portuguese, Chinese
* minor bug fixes

== 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
2 changes: 1 addition & 1 deletion locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

/*global modules, contains*/

modules.locale = '2016-May-02';
modules.locale = '2016-May-04';

// Global stuff

Expand Down
23 changes: 18 additions & 5 deletions morphic.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@
(c) an application
-------------------
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 you'll create your own morph prototypes, perhaps
assemble your own "window frame" and bring it all to life in a
Expand Down Expand Up @@ -1054,10 +1054,9 @@

// Global settings /////////////////////////////////////////////////////

/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
FileList, getBlurredShadowSupport*/
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/

var morphicVersion = '2016-February-24';
var morphicVersion = '2016-May-04';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug

Expand Down Expand Up @@ -1938,7 +1937,7 @@ Rectangle.prototype.round = function () {
Rectangle.prototype.spread = function () {
// 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
// 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);
};

Expand Down Expand Up @@ -2090,6 +2089,20 @@ Node.prototype.forAllChildren = function (aFunction) {
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 () {
var result = [];
this.allChildren().forEach(function (element) {
Expand Down
50 changes: 34 additions & 16 deletions objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph*/

modules.objects = '2016-May-02';
modules.objects = '2016-May-04';

var SpriteMorph;
var StageMorph;
Expand Down Expand Up @@ -1364,7 +1364,7 @@ SpriteMorph.prototype.init = function (globals) {

// SpriteMorph duplicating (fullCopy)

SpriteMorph.prototype.fullCopy = function () {
SpriteMorph.prototype.fullCopy = function (forClone) {
var c = SpriteMorph.uber.fullCopy.call(this),
myself = this,
arr = [],
Expand All @@ -1374,20 +1374,22 @@ SpriteMorph.prototype.fullCopy = function () {
c.color = this.color.copy();
c.blocksCache = {};
c.paletteCache = {};
c.scripts = this.scripts.fullCopy();
c.scripts = this.scripts.fullCopy(forClone);
c.scripts.owner = c;
c.variables = this.variables.copy();
c.variables.owner = c;
c.customBlocks = [];
this.customBlocks.forEach(function (def) {
cb = def.copyAndBindTo(c);
c.customBlocks.push(cb);
c.allBlockInstances(def).forEach(function (block) {
block.definition = cb;
if (!forClone) {
this.customBlocks.forEach(function (def) {
cb = def.copyAndBindTo(c);
c.customBlocks.push(cb);
c.allBlockInstances(def).forEach(function (block) {
block.definition = cb;
});
});
});
}
this.costumes.asArray().forEach(function (costume) {
var cst = costume.copy();
var cst = forClone ? costume : costume.copy();
arr.push(cst);
if (costume === myself.costume) {
c.costume = cst;
Expand All @@ -1404,12 +1406,11 @@ SpriteMorph.prototype.fullCopy = function () {
c.anchor = null;
c.parts = [];
this.parts.forEach(function (part) {
var dp = part.fullCopy();
var dp = part.fullCopy(forClone);
dp.nestingScale = part.nestingScale;
dp.rotatesWithAnchor = part.rotatesWithAnchor;
c.attachPart(dp);
});

return c;
};

Expand Down Expand Up @@ -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 () {
var stage = this.parentThatIsA(StageMorph);
if (stage && stage.cloneCount <= 1000) {
this.fullCopy().clonify(stage);
if (stage && stage.cloneCount <= 2000) {
this.fullCopy(true).clonify(stage);
}
};

Expand Down Expand Up @@ -6636,7 +6655,6 @@ Costume.prototype.copy = function () {
var canvas = newCanvas(this.extent()),
cpy,
ctx;

ctx = canvas.getContext('2d');
ctx.drawImage(this.contents, 0, 0);
cpy = new Costume(canvas, this.name ? copy(this.name) : null);
Expand Down
4 changes: 2 additions & 2 deletions threads.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, isSnapObject*/

modules.threads = '2016-May-02';
modules.threads = '2016-May-04';

var ThreadManager;
var Process;
Expand Down Expand Up @@ -354,7 +354,7 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
if (invoke(
pred,
null,
null,
block.receiver(), // needed for shallow copied clones - was null
50,
'the predicate takes\ntoo long for a\ncustom hat block',
true // suppress errors => handle them right here instead
Expand Down

0 comments on commit 7133302

Please sign in to comment.