mirror of
https://github.com/scratchfoundation/scratch-html5.git
synced 2024-12-04 21:01:04 -05:00
245 lines
8.2 KiB
JavaScript
245 lines
8.2 KiB
JavaScript
// Copyright (C) 2013 Massachusetts Institute of Technology
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License version 2,
|
|
// as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
'use strict';
|
|
|
|
var SensingPrims = function() {};
|
|
|
|
SensingPrims.prototype.addPrimsTo = function(primTable) {
|
|
primTable['touching:'] = this.primTouching;
|
|
primTable['touchingColor:'] = this.primTouchingColor;
|
|
primTable['color:sees:'] = this.primColorTouchingColor;
|
|
|
|
primTable['keyPressed:'] = this.primKeyPressed;
|
|
primTable['mousePressed'] = function(b) { return runtime.mouseDown; };
|
|
primTable['mouseX'] = function(b) { return runtime.mousePos[0]; };
|
|
primTable['mouseY'] = function(b) { return runtime.mousePos[1]; };
|
|
primTable['distanceTo:'] = this.primDistanceTo;
|
|
|
|
primTable['getAttribute:of:'] = this.primGetAttribute;
|
|
|
|
primTable['timeAndDate'] = function(b){ return runtime.getTimeString(interp.arg(b, 0)); };
|
|
primTable['timestamp'] = this.primTimestamp;
|
|
};
|
|
|
|
SensingPrims.prototype.primTouching = function(b) {
|
|
var s = interp.targetSprite();
|
|
if (s == null || !s.visible) return false;
|
|
|
|
var arg = interp.arg(b, 0);
|
|
if (arg == '_edge_') {
|
|
return false; // TODO
|
|
}
|
|
|
|
if (arg == '_mouse_') {
|
|
return false; // TODO
|
|
}
|
|
|
|
var s2 = runtime.spriteNamed(arg);
|
|
if (s2 == null || !s2.visible) return false;
|
|
|
|
return spriteHitTest(s, s2);
|
|
};
|
|
|
|
SensingPrims.prototype.primTouchingColor = function(b) {
|
|
var s = interp.targetSprite();
|
|
if (s == null || !s.visible) return false;
|
|
|
|
var color = interp.arg(b, 0);
|
|
|
|
return stageColorHitTest(s, color);
|
|
};
|
|
|
|
SensingPrims.prototype.primColorTouchingColor = function(b) {
|
|
var s = interp.targetSprite();
|
|
if (s == null || !s.visible) return false;
|
|
|
|
var myColor = interp.arg(b, 0);
|
|
var stageColor = interp.arg(b, 1);
|
|
|
|
return stageColorByColorHitTest(s, myColor, stageColor);
|
|
};
|
|
|
|
var spriteHitTest = function(a, b) {
|
|
var hitCanvas = document.createElement('canvas');
|
|
hitCanvas.width = 480;
|
|
hitCanvas.height = 360;
|
|
var hitTester = hitCanvas.getContext('2d');
|
|
hitTester.globalCompositeOperation = 'source-over';
|
|
a.stamp(hitTester, 100);
|
|
hitTester.globalCompositeOperation = 'source-in';
|
|
b.stamp(hitTester, 100);
|
|
|
|
var aData = hitTester.getImageData(0, 0, 480, 360).data;
|
|
|
|
var pxCount = aData.length;
|
|
for (var i = 0; i < pxCount; i += 4) {
|
|
if (aData[i+3] > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
var stageColorHitTest = function(target, color) {
|
|
var r, g, b;
|
|
r = (color >> 16);
|
|
g = (color >> 8 & 255);
|
|
b = (color & 255);
|
|
|
|
var targetCanvas = document.createElement('canvas');
|
|
targetCanvas.width = 480;
|
|
targetCanvas.height = 360;
|
|
var targetTester = targetCanvas.getContext('2d');
|
|
target.stamp(targetTester, 100);
|
|
|
|
var stageCanvas = document.createElement('canvas');
|
|
stageCanvas.width = 480;
|
|
stageCanvas.height = 360;
|
|
var stageContext = stageCanvas.getContext('2d');
|
|
|
|
$.each(runtime.sprites, function(i, sprite) {
|
|
if (sprite != target)
|
|
sprite.stamp(stageContext, 100);
|
|
});
|
|
|
|
var hitData = stageContext.getImageData(0, 0, stageCanvas.width, stageCanvas.height).data;
|
|
var meshData = targetTester.getImageData(0, 0, targetCanvas.width, targetCanvas.height).data;
|
|
var pxCount = meshData.length;
|
|
for (var i = 0; i < pxCount; i += 4) {
|
|
if (meshData[i+3] > 0 && hitData[i] == r && hitData[i+1] == g && hitData[i+2] == b)
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
var stageColorByColorHitTest = function(target, myColor, otherColor) {
|
|
var threshold_acceptable = function(a, b, c, x, y, z) {
|
|
var diff_a = Math.abs(a-x);
|
|
var diff_b = Math.abs(b-y);
|
|
var diff_c = Math.abs(c-z);
|
|
if (diff_a + diff_b + diff_c < 100) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var targetCanvas = document.createElement('canvas');
|
|
targetCanvas.width = 480;
|
|
targetCanvas.height = 360;
|
|
var targetTester = targetCanvas.getContext('2d');
|
|
target.stamp(targetTester, 100);
|
|
var targetData = targetTester.getImageData(0, 0, targetCanvas.width, targetCanvas.height).data;
|
|
|
|
// Calculate RGB values of the colors - TODO thresholding
|
|
//myColor = Math.abs(myColor);
|
|
//otherColor = Math.abs(otherColor);
|
|
var mr, mg, mb, or, og, ob;
|
|
mr = (myColor >> 16);
|
|
mg = (myColor >> 8 & 255);
|
|
mb = (myColor & 255);
|
|
or = (otherColor >> 16);
|
|
og = (otherColor >> 8 & 255);
|
|
ob = (otherColor & 255);
|
|
|
|
// Create the hit canvas for comparison
|
|
var hitCanvas = document.createElement('canvas');
|
|
hitCanvas.width = 480;
|
|
hitCanvas.height = 360;
|
|
var hitCtx = hitCanvas.getContext('2d');
|
|
$.each(runtime.sprites, function(i, sprite) {
|
|
if (sprite != target) {
|
|
sprite.stamp(hitCtx, 100);
|
|
}
|
|
});
|
|
|
|
var hitData = hitCtx.getImageData(0, 0, hitCanvas.width, hitCanvas.height).data;
|
|
var pxCount = targetData.length;
|
|
for (var i = 0; i < pxCount; i += 4) {
|
|
if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb) && threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
SensingPrims.prototype.primKeyPressed = function(b) {
|
|
var key = interp.arg(b, 0);
|
|
var ch = key.charCodeAt(0);
|
|
if (ch > 127) return false;
|
|
if (key == "left arrow") ch = 37;
|
|
if (key == "right arrow") ch = 39;
|
|
if (key == "up arrow") ch = 38;
|
|
if (key == "down arrow") ch = 40;
|
|
if (key == "space") ch = 32;
|
|
return (typeof(runtime.keysDown[ch]) != 'undefined');
|
|
};
|
|
|
|
SensingPrims.prototype.primDistanceTo = function(b) {
|
|
var s = interp.targetSprite();
|
|
var p = mouseOrSpritePosition(interp.arg(b, 0));
|
|
if (s == null || p == null) return 0;
|
|
var dx = p.x - s.scratchX;
|
|
var dy = p.y - s.scratchY;
|
|
return Math.sqrt((dx * dx) + (dy * dy));
|
|
};
|
|
|
|
SensingPrims.prototype.primGetAttribute = function(b) {
|
|
var attr = interp.arg(b, 0);
|
|
var targetSprite = runtime.spriteNamed(interp.arg(b, 1));
|
|
if (targetSprite == null) return 0;
|
|
if (attr == 'x position') return targetSprite.scratchX;
|
|
if (attr == 'y position') return targetSprite.scratchY;
|
|
if (attr == 'direction') return targetSprite.direction;
|
|
if (attr == 'costume #') return targetSprite.currentCostumeIndex + 1;
|
|
if (attr == 'costume name') return targetSprite.costumes[targetSprite.currentCostumeIndex]['costumeName'];
|
|
if (attr == 'size') return targetSprite.getSize();
|
|
if (attr == 'volume') return targetSprite.volume;
|
|
return 0;
|
|
};
|
|
|
|
SensingPrims.prototype.primTimeDate = function(b) {
|
|
var dt = interp.arg(b, 0);
|
|
var now = new Date();
|
|
if (dt == 'year') return now.getFullYear();
|
|
if (dt == 'month') return now.getMonth()+1;
|
|
if (dt == 'date') return now.getDate();
|
|
if (dt == 'day of week') return now.getDay()+1;
|
|
if (dt == 'hour') return now.getHours();
|
|
if (dt == 'minute') return now.getMinutes();
|
|
if (dt == 'second') return now.getSeconds();
|
|
return 0;
|
|
};
|
|
|
|
SensingPrims.prototype.primTimestamp = function(b) {
|
|
var now = new Date();
|
|
var epoch = new Date(2000, 0, 1);
|
|
var dst = now.getTimezoneOffset() - epoch.getTimezoneOffset();
|
|
var msSince = now.getTime() - epoch.getTime();
|
|
msSince += (now.getTimezoneOffset() - dst) * 60000;
|
|
return msSince / 86400000;
|
|
};
|
|
|
|
// Helpers
|
|
SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
|
|
if (arg == "_mouse_") {
|
|
var w = runtime.stage;
|
|
return new Point(runtime.mousePos[0], runtime.mousePos[1]);
|
|
} else {
|
|
var s = runtime.spriteNamed(arg);
|
|
if (s == null) return null;
|
|
return new Point(s.scratchX, s.scratchY);
|
|
}
|
|
return null;
|
|
};
|