Compare commits

...

28 Commits

Author SHA1 Message Date
greenkeeper[bot]
d26e88827b chore(package): update docdash to version 1.0.1
Closes #335
2018-12-01 07:36:55 +00:00
DD Liu
40cbe50238 Merge pull request #372 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181126212715
fix(package): update scratch-svg-renderer to version 0.2.0-prerelease…
2018-11-27 14:40:27 -05:00
Paul Kaplan
1a2b7a6253 Merge pull request #369 from towerofnix/fix-empty-speech-bubble-size
Fix empty speech bubbles showing up squished
2018-11-26 16:31:59 -05:00
greenkeeper[bot]
077d2f1d69 fix(package): update scratch-svg-renderer to version 0.2.0-prerelease.20181126212715
Closes #367
2018-11-26 21:28:37 +00:00
Florrie
4189377128 Fix empty speech bubbles showing up squished 2018-11-16 11:29:06 -04:00
Andrew Sliwinski
4ae7e32b8c Merge pull request #354 from mzgoddard/fix-touching-mouse
Fix touching mouse
2018-11-02 08:59:07 -04:00
Michael "Z" Goddard
1b86f2ca3a adjust pick test positions to avoid default matrices 2018-10-29 11:05:05 -04:00
Michael "Z" Goddard
1703afbbd0 split up pick integration tests to avoid cached info 2018-10-29 11:04:10 -04:00
Andrew Sliwinski
9ab14e34dd Merge pull request #339 from mzgoddard/round-position
Round drawable position
2018-10-29 08:52:17 -04:00
DD Liu
60210a4a11 Merge pull request #360 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20181024204838
chore(package): update scratch-vm to version 0.2.0-prerelease.2018102…
2018-10-24 17:53:15 -04:00
DD Liu
2683c98469 Merge pull request #359 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181024192149
Update scratch-svg-renderer to the latest version 🚀
2018-10-24 17:49:41 -04:00
greenkeeper[bot]
aacbd5e518 chore(package): update scratch-vm to version 0.2.0-prerelease.20181024204838
Closes #340
2018-10-24 20:57:25 +00:00
greenkeeper[bot]
858bff09e1 fix(package): update scratch-svg-renderer to version 0.2.0-prerelease.20181024192149 2018-10-24 19:23:12 +00:00
Michael "Z" Goddard
14813e590c replace hsl color handling with hsv (#355)
Scratch 2 color and brightness effects in HSV color space.
2018-10-23 15:13:54 -04:00
DD Liu
5bcbc5414f Merge pull request #356 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20181017193458
Update scratch-svg-renderer to the latest version 🚀
2018-10-17 15:46:07 -04:00
greenkeeper[bot]
94340deba8 fix(package): update scratch-svg-renderer to version 0.2.0-prerelease.20181017193458 2018-10-17 19:36:17 +00:00
Michael "Z" Goddard
bd14d53fb2 make sure drawable matrix and silhouette for mouse touching
RenderWebGL methods that test drawable touching should call
updateMatrix and updateSilhouette before calling drwaable.isTouching.
These methods are called by RenderWebGL instead of isTouching because
isTouching can be called repeatedly in quick succession. Calling update
methods in isTouching would waste a lot of cycles.
2018-10-04 11:50:38 -04:00
Chris Willis-Ford
5f9ca5b4fa Merge pull request #349 from wdr-data/fix/infinite-bounds-loop
Fix bug when bounds would be infinite and cause never ending loop
2018-10-02 12:18:21 -07:00
DD Liu
834c5eb984 Merge pull request #351 from fsih/updateSvgRender 2018-09-26 11:33:11 -04:00
DD
ef91583603 Also update webpack cli because it's breaking travis 2018-09-26 11:23:02 -04:00
DD
3e084dfe26 Update scratch svg render 2018-09-26 11:16:03 -04:00
Marcus Weiner
fdea47d31c Fix bug when bounds would be infinite and cause never ending loop 2018-09-22 22:29:23 +02:00
Ray Schamp
24737982f0 Merge pull request #347 from rschamp/snapshots
Add ability to get a snapshot of the next frame
2018-09-18 16:06:46 -04:00
Ray Schamp
cfaadfcc75 Merge pull request #348 from rschamp/get-canvas
Add getter for the renderer's canvas
2018-09-18 16:01:53 -04:00
Ray Schamp
997062c851 Add JSDoc for requestSnapshot 2018-09-18 15:55:49 -04:00
Ray Schamp
90b1c47c3e Add getter for the renderer's canvas
Resolves #309
2018-09-18 15:21:01 -04:00
Ray Schamp
550cd7aacf Add ability to get a snapshot of the next frame 2018-09-18 15:01:22 -04:00
Michael "Z" Goddard
b901c1ac75 round drawable position
Scratch 2 rendered sprite position is at whole numbers, while its vm
position may be rational numbers.
2018-08-24 18:19:19 -04:00
7 changed files with 139 additions and 60 deletions

View File

@@ -31,18 +31,18 @@
"babel-preset-env": "^1.6.1",
"chromeless": "^1.5.1",
"copy-webpack-plugin": "^4.5.1",
"docdash": "^0.4.0",
"docdash": "^1.0.1",
"eslint": "^4.6.1",
"eslint-config-scratch": "^5.0.0",
"gh-pages": "^1.0.0",
"jsdoc": "^3.5.5",
"json": "^9.0.4",
"scratch-vm": "0.2.0-prerelease.20180824135031",
"scratch-vm": "0.2.0-prerelease.20181024204838",
"tap": "^11.0.0",
"travis-after-all": "^1.4.4",
"uglifyjs-webpack-plugin": "^1.2.5",
"webpack": "^4.8.0",
"webpack-cli": "^2.0.15",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.4"
},
"dependencies": {
@@ -53,7 +53,7 @@
"minilog": "3.1.0",
"raw-loader": "^0.5.1",
"scratch-storage": "^1.0.0",
"scratch-svg-renderer": "0.2.0-prerelease.20180907141232",
"scratch-svg-renderer": "0.2.0-prerelease.20181126212715",
"twgl.js": "4.4.0"
}
}

View File

@@ -190,8 +190,8 @@ class Drawable {
if ('position' in properties && (
this._position[0] !== properties.position[0] ||
this._position[1] !== properties.position[1])) {
this._position[0] = properties.position[0];
this._position[1] = properties.position[1];
this._position[0] = Math.round(properties.position[0]);
this._position[1] = Math.round(properties.position[1]);
dirty = true;
}
if ('direction' in properties && this._direction !== properties.direction) {

View File

@@ -178,6 +178,9 @@ class RenderWebGL extends EventEmitter {
/** @type {function} */
this._exitRegion = null;
/** @type {Array.<snapshotCallback>} */
this._snapshotCallbacks = [];
this._svgTextBubble = new SVGTextBubble();
this._createGeometry();
@@ -201,6 +204,13 @@ class RenderWebGL extends EventEmitter {
return this._gl;
}
/**
* @returns {HTMLCanvasElement} the canvas of the WebGL rendering context associated with this renderer.
*/
get canvas () {
return this._gl && this._gl.canvas;
}
/**
* Set the physical size of the stage in device-independent pixels.
* This will be multiplied by the device's pixel ratio on high-DPI displays.
@@ -589,6 +599,11 @@ class RenderWebGL extends EventEmitter {
gl.clear(gl.COLOR_BUFFER_BIT);
this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection);
if (this._snapshotCallbacks.length > 0) {
const snapshot = gl.canvas.toDataURL();
this._snapshotCallbacks.forEach(cb => cb(snapshot));
this._snapshotCallbacks = [];
}
}
/**
@@ -915,6 +930,9 @@ class RenderWebGL extends EventEmitter {
const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight);
const worldPos = twgl.v3.create();
drawable.updateMatrix();
drawable.skin.updateSilhouette();
for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) {
for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) {
if (drawable.isTouching(worldPos)) {
@@ -953,7 +971,12 @@ class RenderWebGL extends EventEmitter {
if (candidateIDs.length === 0) {
return false;
}
const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight);
if (bounds.left === -Infinity || bounds.bottom === -Infinity) {
return false;
}
const hits = [];
const worldPos = twgl.v3.create(0, 0, 0);
// Iterate over the scratch pixels and check if any candidate can be
@@ -1704,6 +1727,18 @@ class RenderWebGL extends EventEmitter {
dst[2] += blendAlpha * 255;
return dst;
}
/**
* @callback RenderWebGL#snapshotCallback
* @param {string} dataURI Data URI of the snapshot of the renderer
*/
/**
* @param {snapshotCallback} callback Function called in the next frame with the snapshot data
*/
requestSnapshot (callback) {
this._snapshotCallbacks.push(callback);
}
}
// :3

View File

@@ -54,9 +54,9 @@ varying vec2 v_texCoord;
// Smaller values can cause problems with "color" and "brightness" effects on some mobile devices
const float epsilon = 1e-3;
// Convert an RGB color to Hue, Saturation, and Lightness.
// Convert an RGB color to Hue, Saturation, and Value.
// All components of input and output are expected to be in the [0,1] range.
vec3 convertRGB2HSL(vec3 rgb)
vec3 convertRGB2HSV(vec3 rgb)
{
// Hue calculation has 3 cases, depending on which RGB component is largest, and one of those cases involves a "mod"
// operation. In order to avoid that "mod" we split the M==R case in two: one for G<B and one for B>G. The B>G case
@@ -80,13 +80,13 @@ vec3 convertRGB2HSL(vec3 rgb)
// Chroma = M - m
float C = temp2.x - m;
// Lightness = 1/2 * (M + m)
float L = 0.5 * (temp2.x + m);
// Value = M
float V = temp2.x;
return vec3(
abs(temp2.z + (temp2.w - temp2.y) / (6.0 * C + epsilon)), // Hue
C / (1.0 - abs(2.0 * L - 1.0) + epsilon), // Saturation
L); // Lightness
C / (temp2.x + epsilon), // Saturation
V); // Value
}
vec3 convertHue2RGB(float hue)
@@ -97,11 +97,11 @@ vec3 convertHue2RGB(float hue)
return clamp(vec3(r, g, b), 0.0, 1.0);
}
vec3 convertHSL2RGB(vec3 hsl)
vec3 convertHSV2RGB(vec3 hsv)
{
vec3 rgb = convertHue2RGB(hsl.x);
float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y;
return (rgb - 0.5) * c + hsl.z;
vec3 rgb = convertHue2RGB(hsv.x);
float c = hsv.z * hsv.y;
return rgb * c + hsv.z - c;
}
#endif // !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color) || defined(ENABLE_brightness))
@@ -166,7 +166,7 @@ void main()
#if defined(ENABLE_color) || defined(ENABLE_brightness)
{
vec3 hsl = convertRGB2HSL(gl_FragColor.xyz);
vec3 hsv = convertRGB2HSV(gl_FragColor.xyz);
#ifdef ENABLE_color
{
@@ -174,19 +174,19 @@ void main()
// so that some slight change of hue will be visible
const float minLightness = 0.11 / 2.0;
const float minSaturation = 0.09;
if (hsl.z < minLightness) hsl = vec3(0.0, 1.0, minLightness);
else if (hsl.y < minSaturation) hsl = vec3(0.0, minSaturation, hsl.z);
if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
hsl.x = mod(hsl.x + u_color, 1.0);
if (hsl.x < 0.0) hsl.x += 1.0;
hsv.x = mod(hsv.x + u_color, 1.0);
if (hsv.x < 0.0) hsv.x += 1.0;
}
#endif // ENABLE_color
#ifdef ENABLE_brightness
hsl.z = clamp(hsl.z + u_brightness, 0.0, 1.0);
hsv.z = clamp(hsv.z + u_brightness, 0.0, 1.0);
#endif // ENABLE_brightness
gl_FragColor.rgb = convertHSL2RGB(hsl);
gl_FragColor.rgb = convertHSV2RGB(hsv);
}
#endif // defined(ENABLE_color) || defined(ENABLE_brightness)

View File

@@ -145,11 +145,16 @@ class SVGTextBubble {
</g>`;
}
_getTextSize () {
const svgString = this._wrapSvgFragment(this._textFragment);
_getTextSize (textFragment) {
const svgString = this._wrapSvgFragment(textFragment);
if (!this._textSizeCache[svgString]) {
this._textSizeCache[svgString] = this.svgRenderer.measure(svgString);
if (this._textSizeCache[svgString].height === 0) {
// The speech bubble is empty, so use the height of a single line with content (or else it renders
// weirdly, see issue #302).
const dummyFragment = this._buildTextFragment('X');
this._textSizeCache[svgString] = this._getTextSize(dummyFragment);
}
}
return this._textSizeCache[svgString];
}
@@ -183,7 +188,7 @@ class SVGTextBubble {
let fragment = '';
const radius = 16;
const {x, y, width, height} = this._getTextSize();
const {x, y, width, height} = this._getTextSize(this._textFragment);
const padding = 10;
const fullWidth = Math.max(MIN_WIDTH, width) + (2 * padding);
const fullHeight = height + (2 * padding);

View File

@@ -7,55 +7,94 @@ const chromeless = new Chromeless();
const indexHTML = path.resolve(__dirname, 'index.html');
const testDir = (...args) => path.resolve(__dirname, 'pick-tests', ...args);
const runFile = (file, script) =>
const runFile = (file, action, script) =>
// start each test by going to the index.html, and loading the scratch file
chromeless.goto(`file://${indexHTML}`)
.setFileInput('#file', testDir(file))
// the index.html handler for file input will add a #loaded element when it
// finishes.
.wait('#loaded')
.evaluate(script)
.evaluate(`function () {return (${script})(${action});}`)
;
// immediately invoked async function to let us wait for each test to finish before starting the next.
(async () => {
await test('pick tests', async t => {
const testOperation = async function (name, action, expect) {
await test(name, async t => {
const results = await runFile('test-mouse-touch.sb2', () => {
vm.greenFlag();
const sendResults = [];
const results = await runFile('test-mouse-touch.sb2', action, boundAction => {
vm.greenFlag();
const sendResults = [];
const idToTargetName = id => {
const target = vm.runtime.targets.find(tar => tar.drawableID === id);
if (!target) {
return `[Unknown drawableID: ${id}]`;
}
return target.sprite.name;
};
const sprite = vm.runtime.targets.find(target => target.sprite.name === 'Sprite1');
const idToTargetName = id => {
const target = vm.runtime.targets.find(tar => tar.drawableID === id);
if (!target) {
return `[Unknown drawableID: ${id}]`;
}
return target.sprite.name;
};
const sprite = vm.runtime.targets.find(target => target.sprite.name === 'Sprite1');
sendResults.push(['center', idToTargetName(render.pick(240, 180))]);
sendResults.push(['left', idToTargetName(render.pick(200, 180))]);
sendResults.push(['over', render.drawableTouching(sprite.drawableID, 240, 180)]);
sprite.setVisible(false);
sendResults.push(['hidden sprite pick center', idToTargetName(render.pick(240, 180))]);
sendResults.push(['hidden over', render.drawableTouching(sprite.drawableID, 240, 180)]);
return sendResults;
boundAction({
sendResults,
idToTargetName,
render,
sprite
});
return sendResults;
});
t.plan(expect.length);
for (let x = 0; x < expect.length; x++) {
t.deepEqual(results[x], expect[x], expect[x][0]);
}
t.end();
});
const expect = [
['center', 'Sprite1'],
['left', 'Stage'],
['over', true],
['hidden sprite pick center', 'Stage'],
['hidden over', true]
];
t.plan(expect.length);
for (let x = 0; x < expect.length; x++) {
t.deepEqual(results[x], expect[x], expect[x][0]);
};
const tests = [
{
name: 'pick Sprite1',
action: ({sendResults, render, idToTargetName}) => {
sendResults.push(['center', idToTargetName(render.pick(360, 180))]);
},
expect: [['center', 'Sprite1']]
},
{
name: 'pick Stage',
action: ({sendResults, render, idToTargetName}) => {
sendResults.push(['left', idToTargetName(render.pick(320, 180))]);
},
expect: [['left', 'Stage']]
},
{
name: 'touching Sprite1',
action: ({sprite, sendResults, render}) => {
sendResults.push(['over', render.drawableTouching(sprite.drawableID, 360, 180)]);
},
expect: [['over', true]]
},
{
name: 'pick Stage through hidden Sprite1',
action: ({sprite, sendResults, render, idToTargetName}) => {
sprite.setVisible(false);
sendResults.push(['hidden sprite pick center', idToTargetName(render.pick(360, 180))]);
},
expect: [['hidden sprite pick center', 'Stage']]
},
{
name: 'touching hidden Sprite1',
action: ({sprite, sendResults, render}) => {
sprite.setVisible(false);
sendResults.push(['hidden over', render.drawableTouching(sprite.drawableID, 360, 180)]);
},
expect: [['hidden over', true]]
}
t.end();
});
];
for (const {name, action, expect} of tests) {
await testOperation(name, action, expect);
}
// close the browser window we used
await chromeless.end();