Compare commits

...

5 Commits

Author SHA1 Message Date
greenkeeper[bot]
a300482389 chore(package): update webpack-cli to version 3.3.7 2019-08-18 06:07:23 +00:00
Karishma Chadha
c2e32d2baf Merge pull request #494 from LLK/revert-467-rect-init-matrix
Revert "Initialialize AABB Rectangle "
2019-08-13 11:38:41 -04:00
Karishma Chadha
c8839b2feb Merge pull request #493 from LLK/revert-470-skin-alter-push
Revert "Skin alter push"
2019-08-13 11:38:24 -04:00
Karishma Chadha
c8f7496fba Revert "Initialialize AABB Rectangle " 2019-08-13 11:26:07 -04:00
Karishma Chadha
4a28cffcd4 Revert "Skin alter push" 2019-08-13 11:22:27 -04:00
12 changed files with 73 additions and 365 deletions

View File

@@ -15,7 +15,6 @@
"private": true,
"readme": "README.md",
"recurse": true,
"template": "node_modules/docdash",
"tutorials": "docs"
"template": "node_modules/docdash"
}
}

View File

@@ -1,192 +0,0 @@
# Rectangle AABB Matrix
Initialize a Rectangle to a 1 unit square centered at 0 x 0 transformed by a model matrix.
-----
Every drawable is a 1 x 1 unit square that is rotated by its direction, scaled by its skin size and scale, and offset by its rotation center and position. The square representation is made up of 4 points that are transformed by the drawable properties. Often we want a shape that simplifies those 4 points into a non-rotated shape, a axis aligned bounding box.
One approach is to compare the x and y components of each transformed vector and find the minimum and maximum x component and the minimum and maximum y component.
We can start from this approach and determine an alternative one that prodcues the same output with less work.
Starting with transforming one point, here is a 3D point, `v`, transformation by a matrix, `m`.
```js
const v0 = v[0];
const v1 = v[1];
const v2 = v[2];
const d = v0 * m[(0 * 4) + 3] + v1 * m[(1 * 4) + 3] + v2 * m[(2 * 4) + 3] + m[(3 * 4) + 3];
dst[0] = (v0 * m[(0 * 4) + 0] + v1 * m[(1 * 4) + 0] + v2 * m[(2 * 4) + 0] + m[(3 * 4) + 0]) / d;
dst[1] = (v0 * m[(0 * 4) + 1] + v1 * m[(1 * 4) + 1] + v2 * m[(2 * 4) + 1] + m[(3 * 4) + 1]) / d;
dst[2] = (v0 * m[(0 * 4) + 2] + v1 * m[(1 * 4) + 2] + v2 * m[(2 * 4) + 2] + m[(3 * 4) + 2]) / d;
```
As this is a 2D rectangle we can cancel out the third dimension, and the determinant, 'd'.
```js
const v0 = v[0];
const v1 = v[1];
dst = [
v0 * m[(0 * 4) + 0] + v1 * m[(1 * 4) + 0] + m[(3 * 4) + 0,
v0 * m[(0 * 4) + 1] + v1 * m[(1 * 4) + 1] + m[(3 * 4) + 1
];
```
Let's set the matrix points to shorter names for convenience.
```js
const m00 = m[(0 * 4) + 0];
const m01 = m[(0 * 4) + 1];
const m10 = m[(1 * 4) + 0];
const m11 = m[(1 * 4) + 1];
const m30 = m[(3 * 4) + 0];
const m31 = m[(3 * 4) + 1];
```
We need 4 points with positive and negative 0.5 values so the square has sides of length 1.
```js
let p = [0.5, 0.5];
let q = [-0.5, 0.5];
let r = [-0.5, -0.5];
let s = [0.5, -0.5];
```
Transform the points by the matrix.
```js
p = [
0.5 * m00 + 0.5 * m10 + m30,
0.5 * m01 + 0.5 * m11 + m31
];
q = [
-0.5 * m00 + -0.5 * m10 + m30,
0.5 * m01 + 0.5 * m11 + m31
];
r = [
-0.5 * m00 + -0.5 * m10 + m30,
-0.5 * m01 + -0.5 * m11 + m31
];
s = [
0.5 * m00 + 0.5 * m10 + m30,
-0.5 * m01 + -0.5 * m11 + m31
];
```
With 4 transformed points we can build the left, right, top, and bottom values for the Rectangle. Each will use the minimum or the maximum of one of the components of all points.
```js
const left = Math.min(p[0], q[0], r[0], s[0]);
const right = Math.max(p[0], q[0], r[0], s[0]);
const top = Math.max(p[1], q[1], r[1], s[1]);
const bottom = Math.min(p[1], q[1], r[1], s[1]);
```
Fill those calls with the vector expressions.
```js
const left = Math.min(
0.5 * m00 + 0.5 * m10 + m30,
-0.5 * m00 + 0.5 * m10 + m30,
-0.5 * m00 + -0.5 * m10 + m30,
0.5 * m00 + -0.5 * m10 + m30
);
const right = Math.max(
0.5 * m00 + 0.5 * m10 + m30,
-0.5 * m00 + 0.5 * m10 + m30,
-0.5 * m00 + -0.5 * m10 + m30,
0.5 * m00 + -0.5 * m10 + m30
);
const top = Math.max(
0.5 * m01 + 0.5 * m11 + m31,
-0.5 * m01 + 0.5 * m11 + m31,
-0.5 * m01 + -0.5 * m11 + m31,
0.5 * m01 + -0.5 * m11 + m31
);
const bottom = Math.min(
0.5 * m01 + 0.5 * m11 + m31,
-0.5 * m01 + 0.5 * m11 + m31,
-0.5 * m01 + -0.5 * m11 + m31,
0.5 * m01 + -0.5 * m11 + m31
);
```
Pull out the `0.5 * m??` patterns.
```js
const x0 = 0.5 * m00;
const x1 = 0.5 * m10;
const y0 = 0.5 * m01;
const y1 = 0.5 * m11;
const left = Math.min(x0 + x1 + m30, -x0 + x1 + m30, -x0 + -x1 + m30, x0 + -x1 + m30);
const right = Math.max(x0 + x1 + m30, -x0 + x1 + m30, -x0 + -x1 + m30, x0 + -x1 + m30);
const top = Math.max(y0 + y1 + m31, -y0 + y1 + m31, -y0 + -y1 + m31, y0 + -y1 + m31);
const bottom = Math.min(y0 + y1 + m31, -y0 + y1 + m31, -y0 + -y1 + m31, y0 + -y1 + m31);
```
Now each argument for the min and max calls take an expression like `(a * x0 + b * x1 + m3?)`. As each expression has the x0, x1, and m3? variables we can split the min and max calls on the addition operators. Each new call has all the coefficients of that variable.
```js
const left = Math.min(x0, -x0) + Math.min(x1, -x1) + Math.min(m30, m30);
const right = Math.max(x0, -x0) + Math.max(x1, -x1) + Math.max(m30, m30);
const top = Math.max(y0, -y0) + Math.max(y1, -y1) + Math.max(m31, m31);
const bottom = Math.min(y0, -y0) + Math.min(y1, -y1) + Math.min(m31, m31);
```
The min or max of two copies of the same value will just be that value.
```js
const left = Math.min(x0, -x0) + Math.min(x1, -x1) + m30;
const right = Math.max(x0, -x0) + Math.max(x1, -x1) + m30;
const top = Math.max(y0, -y0) + Math.max(y1, -y1) + m31;
const bottom = Math.min(y0, -y0) + Math.min(y1, -y1) + m31;
```
The max of a negative and positive variable will be the absolute value of that variable. The min of a negative and positive variable will the negated absolute value of that variable.
```js
const left = -Math.abs(x0) + -Math.abs(x1) + m30;
const right = Math.abs(x0) + Math.abs(x1) + m30;
const top = Math.abs(y0) + Math.abs(y1) + m31;
const bottom = -Math.abs(y0) + -Math.abs(y1) + m31;
```
Pulling out the negations of the absolute values, left and right as well as top and bottom are the positive or negative sum of the absolute value of the saled and rotated unit value.
```js
const left = -(Math.abs(x0) + Math.abs(x1)) + m30;
const right = Math.abs(x0) + Math.abs(x1) + m30;
const top = Math.abs(y0) + Math.abs(y1) + m31;
const bottom = -(Math.abs(y0) + Math.abs(y1)) + m31;
```
We call pull out those sums and use them twice.
```js
const x = Math.abs(x0) + Math.abs(x1);
const y = Math.abs(y0) + Math.abs(y1);
const left = -x + m30;
const right = x + m30;
const top = y + m31;
const bottom = -y + m31;
```
This lets us arrive at our goal. Inlining some of our variables we get this block that will initialize a Rectangle to a unit square transformed by a matrix.
```js
const m30 = m[(3 * 4) + 0];
const m31 = m[(3 * 4) + 1];
const x = Math.abs(0.5 * m[(0 * 4) + 0]) + Math.abs(0.5 * m[(1 * 4) + 0]);
const y = Math.abs(0.5 * m[(0 * 4) + 1]) + Math.abs(0.5 * m[(1 * 4) + 1]);
const left = -x + m30;
const right = x + m30;
const top = y + m31;
const bottom = -y + m31;
```

View File

@@ -42,7 +42,7 @@
"travis-after-all": "^1.4.4",
"uglifyjs-webpack-plugin": "^1.2.5",
"webpack": "^4.8.0",
"webpack-cli": "^3.1.0",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.1.4"
},
"dependencies": {

View File

@@ -23,12 +23,6 @@ class BitmapSkin extends Skin {
/** @type {Array<int>} */
this._textureSize = [0, 0];
/**
* The "native" size, in texels, of this skin.
* @type {Array<number>}
*/
this.size = [0, 0];
}
/**
@@ -49,6 +43,13 @@ class BitmapSkin extends Skin {
return true;
}
/**
* @return {Array<number>} the "native" size, in texels, of this skin.
*/
get size () {
return [this._textureSize[0] / this._costumeResolution, this._textureSize[1] / this._costumeResolution];
}
/**
* @param {Array<number>} scale - The scaling factors to be used.
* @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale.
@@ -61,11 +62,10 @@ class BitmapSkin extends Skin {
/**
* Get the bounds of the drawable for determining its fenced position.
* @param {Array<number>} drawable - The Drawable instance this skin is using.
* @param {?Rectangle} result - Optional destination for bounds calculation.
* @return {!Rectangle} The drawable's bounds. For compatibility with Scratch 2, we always use getAABB for bitmaps.
*/
getFenceBounds (drawable, result) {
return drawable.getAABB(result);
getFenceBounds (drawable) {
return drawable.getAABB();
}
/**
@@ -109,7 +109,6 @@ class BitmapSkin extends Skin {
// Do these last in case any of the above throws an exception
this._costumeResolution = costumeResolution || 2;
this._textureSize = BitmapSkin._getBitmapSize(bitmapData);
this.size = [this._textureSize[0] / this._costumeResolution, this._textureSize[1] / this._costumeResolution];
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
this.setRotationCenter.apply(this, rotationCenter);

View File

@@ -3,6 +3,7 @@ const twgl = require('twgl.js');
const Rectangle = require('./Rectangle');
const RenderConstants = require('./RenderConstants');
const ShaderManager = require('./ShaderManager');
const Skin = require('./Skin');
const EffectTransform = require('./EffectTransform');
/**
@@ -100,6 +101,8 @@ class Drawable {
/** @todo move convex hull functionality, maybe bounds functionality overall, to Skin classes */
this._convexHullPoints = null;
this._convexHullDirty = true;
this._skinWasAltered = this._skinWasAltered.bind(this);
}
/**
@@ -138,7 +141,13 @@ class Drawable {
*/
set skin (newSkin) {
if (this._skin !== newSkin) {
if (this._skin) {
this._skin.removeListener(Skin.Events.WasAltered, this._skinWasAltered);
}
this._skin = newSkin;
if (this._skin) {
this._skin.addListener(Skin.Events.WasAltered, this._skinWasAltered);
}
this._skinWasAltered();
}
}
@@ -442,10 +451,9 @@ class Drawable {
* This function applies the transform matrix to the known convex hull,
* and then finds the minimum box along the axes.
* Before calling this, ensure the renderer has updated convex hull points.
* @param {?Rectangle} result optional destination for bounds calculation
* @return {!Rectangle} Bounds for a tight box around the Drawable.
*/
getBounds (result) {
getBounds () {
if (this.needsConvexHullPoints()) {
throw new Error('Needs updated convex hull points before bounds calculation.');
}
@@ -454,19 +462,18 @@ class Drawable {
}
const transformedHullPoints = this._getTransformedHullPoints();
// Search through transformed points to generate box on axes.
result = result || new Rectangle();
result.initFromPointsAABB(transformedHullPoints);
return result;
const bounds = new Rectangle();
bounds.initFromPointsAABB(transformedHullPoints);
return bounds;
}
/**
* Get the precise bounds for the upper 8px slice of the Drawable.
* Used for calculating where to position a text bubble.
* Before calling this, ensure the renderer has updated convex hull points.
* @param {?Rectangle} result optional destination for bounds calculation
* @return {!Rectangle} Bounds for a tight box around a slice of the Drawable.
*/
getBoundsForBubble (result) {
getBoundsForBubble () {
if (this.needsConvexHullPoints()) {
throw new Error('Needs updated convex hull points before bubble bounds calculation.');
}
@@ -478,9 +485,9 @@ class Drawable {
const maxY = Math.max.apply(null, transformedHullPoints.map(p => p[1]));
const filteredHullPoints = transformedHullPoints.filter(p => p[1] > maxY - slice);
// Search through filtered points to generate box on axes.
result = result || new Rectangle();
result.initFromPointsAABB(filteredHullPoints);
return result;
const bounds = new Rectangle();
bounds.initFromPointsAABB(filteredHullPoints);
return bounds;
}
/**
@@ -490,32 +497,35 @@ class Drawable {
* which is tightly snapped to account for a Drawable's transparent regions.
* `getAABB` returns a much less accurate bounding box, but will be much
* faster to calculate so may be desired for quick checks/optimizations.
* @param {?Rectangle} result optional destination for bounds calculation
* @return {!Rectangle} Rough axis-aligned bounding box for Drawable.
*/
getAABB (result) {
getAABB () {
if (this._transformDirty) {
this._calculateTransform();
}
const tm = this._uniforms.u_modelMatrix;
result = result || new Rectangle();
result.initFromModelMatrix(tm);
return result;
const bounds = new Rectangle();
bounds.initFromPointsAABB([
twgl.m4.transformPoint(tm, [-0.5, -0.5, 0]),
twgl.m4.transformPoint(tm, [0.5, -0.5, 0]),
twgl.m4.transformPoint(tm, [-0.5, 0.5, 0]),
twgl.m4.transformPoint(tm, [0.5, 0.5, 0])
]);
return bounds;
}
/**
* Return the best Drawable bounds possible without performing graphics queries.
* I.e., returns the tight bounding box when the convex hull points are already
* known, but otherwise return the rough AABB of the Drawable.
* @param {?Rectangle} result optional destination for bounds calculation
* @return {!Rectangle} Bounds for the Drawable.
*/
getFastBounds (result) {
getFastBounds () {
this.updateMatrix();
if (!this.needsConvexHullPoints()) {
return this.getBounds(result);
return this.getBounds();
}
return this.getAABB(result);
return this.getAABB();
}
/**

View File

@@ -88,9 +88,6 @@ class PenSkin extends Skin {
/** @type {HTMLCanvasElement} */
this._canvas = document.createElement('canvas');
/** @type {Array<number>} */
this._canvasSize = twgl.v3.create();
/** @type {WebGLTexture} */
this._texture = null;
@@ -168,7 +165,7 @@ class PenSkin extends Skin {
* @return {Array<number>} the "native" size, in texels, of this skin. [width, height]
*/
get size () {
return this._canvasSize;
return [this._canvas.width, this._canvas.height];
}
/**
@@ -191,13 +188,13 @@ class PenSkin extends Skin {
clear () {
const gl = this._renderer.gl;
twgl.bindFramebufferInfo(gl, this._framebuffer);
/* Reset framebuffer to transparent black */
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
const ctx = this._canvas.getContext('2d');
ctx.clearRect(0, 0, this._canvasSize[0], this._canvasSize[1]);
ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
this._silhouetteDirty = true;
}
@@ -454,7 +451,7 @@ class PenSkin extends Skin {
* @param {number} x - centered at x
* @param {number} y - centered at y
*/
_drawRectangle (currentShader, texture, bounds, x = -this._canvasSize[0] / 2, y = this._canvasSize[1] / 2) {
_drawRectangle (currentShader, texture, bounds, x = -this._canvas.width / 2, y = this._canvas.height / 2) {
const gl = this._renderer.gl;
const projection = twgl.m4.ortho(
@@ -517,7 +514,7 @@ class PenSkin extends Skin {
* @param {number} x - texture centered at x
* @param {number} y - texture centered at y
*/
_drawToBuffer (texture = this._texture, x = -this._canvasSize[0] / 2, y = this._canvasSize[1] / 2) {
_drawToBuffer (texture = this._texture, x = -this._canvas.width / 2, y = this._canvas.height / 2) {
if (texture !== this._texture && this._canvasDirty) {
this._drawToBuffer();
}
@@ -531,7 +528,7 @@ class PenSkin extends Skin {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._canvas);
const ctx = this._canvas.getContext('2d');
ctx.clearRect(0, 0, this._canvasSize[0], this._canvasSize[1]);
ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
this._canvasDirty = false;
}
@@ -567,8 +564,8 @@ class PenSkin extends Skin {
this._bounds = new Rectangle();
this._bounds.initFromBounds(width / 2, width / -2, height / 2, height / -2);
this._canvas.width = this._canvasSize[0] = width;
this._canvas.height = this._canvasSize[1] = height;
this._canvas.width = width;
this._canvas.height = height;
this._rotationCenter[0] = width / 2;
this._rotationCenter[1] = height / 2;
@@ -654,8 +651,8 @@ class PenSkin extends Skin {
this._renderer.enterDrawRegion(this._toBufferDrawRegionId);
// Sample the framebuffer's pixels into the silhouette instance
const skinPixels = new Uint8Array(Math.floor(this._canvasSize[0] * this._canvasSize[1] * 4));
gl.readPixels(0, 0, this._canvasSize[0], this._canvasSize[1], gl.RGBA, gl.UNSIGNED_BYTE, skinPixels);
const skinPixels = new Uint8Array(Math.floor(this._canvas.width * this._canvas.height * 4));
gl.readPixels(0, 0, this._canvas.width, this._canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, skinPixels);
const skinCanvas = this._canvas;
skinCanvas.width = bounds.width;

View File

@@ -54,31 +54,6 @@ class Rectangle {
}
}
/**
* Initialize a Rectangle to a 1 unit square centered at 0 x 0 transformed
* by a model matrix.
* @param {Array.<number>} m A 4x4 matrix to transform the rectangle by.
* @tutorial Rectangle-AABB-Matrix
*/
initFromModelMatrix (m) {
// In 2D space, we will soon use the 2x2 "top left" scale and rotation
// submatrix, while we store and the 1x2 "top right" that position
// vector.
const m30 = m[(3 * 4) + 0];
const m31 = m[(3 * 4) + 1];
// "Transform" a (0.5, 0.5) vector by the scale and rotation matrix but
// sum the absolute of each component instead of use the signed values.
const x = Math.abs(0.5 * m[(0 * 4) + 0]) + Math.abs(0.5 * m[(1 * 4) + 0]);
const y = Math.abs(0.5 * m[(0 * 4) + 1]) + Math.abs(0.5 * m[(1 * 4) + 1]);
// And adding them to the position components initializes our Rectangle.
this.left = -x + m30;
this.right = x + m30;
this.top = y + m31;
this.bottom = -y + m31;
}
/**
* Determine if this Rectangle intersects some other.
* Note that this is a comparison assuming the Rectangle was

View File

@@ -3,7 +3,6 @@ const EventEmitter = require('events');
const hull = require('hull.js');
const twgl = require('twgl.js');
const Skin = require('./Skin');
const BitmapSkin = require('./BitmapSkin');
const Drawable = require('./Drawable');
const Rectangle = require('./Rectangle');
@@ -17,7 +16,6 @@ const log = require('./util/log');
const __isTouchingDrawablesPoint = twgl.v3.create();
const __candidatesBounds = new Rectangle();
const __fenceBounds = new Rectangle();
const __touchingColor = new Uint8ClampedArray(4);
const __blendColor = new Uint8ClampedArray(4);
@@ -292,20 +290,6 @@ class RenderWebGL extends EventEmitter {
this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize});
}
/**
* Notify Drawables whose skin is the skin that changed.
* @param {Skin} skin - the skin that changed.
* @private
*/
_skinWasAltered (skin) {
for (let i = 0; i < this._allDrawables.length; i++) {
const drawable = this._allDrawables[i];
if (drawable && drawable._skin === skin) {
drawable._skinWasAltered();
}
}
}
/**
* Create a new bitmap skin from a snapshot of the provided bitmap data.
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
@@ -318,7 +302,6 @@ class RenderWebGL extends EventEmitter {
const skinId = this._nextSkinId++;
const newSkin = new BitmapSkin(skinId, this);
newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter);
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
this._allSkins[skinId] = newSkin;
return skinId;
}
@@ -334,7 +317,6 @@ class RenderWebGL extends EventEmitter {
const skinId = this._nextSkinId++;
const newSkin = new SVGSkin(skinId, this);
newSkin.setSVG(svgData, rotationCenter);
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
this._allSkins[skinId] = newSkin;
return skinId;
}
@@ -346,7 +328,6 @@ class RenderWebGL extends EventEmitter {
createPenSkin () {
const skinId = this._nextSkinId++;
const newSkin = new PenSkin(skinId, this);
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
this._allSkins[skinId] = newSkin;
return skinId;
}
@@ -363,7 +344,6 @@ class RenderWebGL extends EventEmitter {
const skinId = this._nextSkinId++;
const newSkin = new TextBubbleSkin(skinId, this);
newSkin.setTextBubble(type, text, pointsLeft);
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
this._allSkins[skinId] = newSkin;
return skinId;
}
@@ -1377,7 +1357,7 @@ class RenderWebGL extends EventEmitter {
const dx = x - drawable._position[0];
const dy = y - drawable._position[1];
const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds);
const aabb = drawable._skin.getFenceBounds(drawable);
const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2);
const sx = this._xRight - Math.min(FENCE_WIDTH, inset);
@@ -1647,14 +1627,14 @@ class RenderWebGL extends EventEmitter {
}
twgl.setUniforms(currentShader, uniforms);
/* adjust blend function for this skin */
if (drawable.skin.hasPremultipliedAlpha){
gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
} else {
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
}
twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES);
}

View File

@@ -30,24 +30,6 @@ class SVGSkin extends Skin {
/** @type {Number} */
this._maxTextureScale = 0;
/**
* The natural size, in Scratch units, of this skin.
* @type {Array<number>}
*/
this.size = [0, 0];
/**
* The viewbox offset of the svg.
* @type {Array<number>}
*/
this._viewOffset = [0, 0];
/**
* The rotation center before offset by _viewOffset.
* @type {Array<number>}
*/
this._rawRotationCenter = [NaN, NaN];
}
/**
@@ -61,17 +43,21 @@ class SVGSkin extends Skin {
super.dispose();
}
/**
* @return {Array<number>} the natural size, in Scratch units, of this skin.
*/
get size () {
return this._svgRenderer.size;
}
/**
* Set the origin, in object space, about which this Skin should rotate.
* @param {number} x - The x coordinate of the new rotation center.
* @param {number} y - The y coordinate of the new rotation center.
*/
setRotationCenter (x, y) {
if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) {
this._rawRotationCenter[0] = x;
this._rawRotationCenter[1] = y;
super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]);
}
const viewOffset = this._svgRenderer.viewOffset;
super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]);
}
/**
@@ -148,11 +134,7 @@ class SVGSkin extends Skin {
}
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
this.size = this._svgRenderer.size;
this._viewOffset = this._svgRenderer.viewOffset;
// Reset rawRotationCenter when we update viewOffset.
this._rawRotationCenter = [NaN, NaN];
this.setRotationCenter(rotationCenter[0], rotationCenter[1]);
this.setRotationCenter.apply(this, rotationCenter);
this.emit(Skin.Events.WasAltered);
});
}

View File

@@ -33,13 +33,6 @@ class Skin extends EventEmitter {
/** @type {Vec3} */
this._rotationCenter = twgl.v3.create(0, 0);
/**
* The "native" size, in texels, of this skin.
* @member size
* @abstract
* @type {Array<number>}
*/
/**
* The uniforms to be used by the vertex and pixel shaders.
* Some of these are used by other parts of the renderer as well.
@@ -104,6 +97,14 @@ class Skin extends EventEmitter {
return this._rotationCenter;
}
/**
* @abstract
* @return {Array<number>} the "native" size, in texels, of this skin.
*/
get size () {
return [0, 0];
}
/**
* Set the origin, in object space, about which this Skin should rotate.
* @param {number} x - The x coordinate of the new rotation center.
@@ -145,11 +146,10 @@ class Skin extends EventEmitter {
/**
* Get the bounds of the drawable for determining its fenced position.
* @param {Array<number>} drawable - The Drawable instance this skin is using.
* @param {?Rectangle} result - Optional destination for bounds calculation.
* @return {!Rectangle} The drawable's bounds.
*/
getFenceBounds (drawable, result) {
return drawable.getFastBounds(result);
getFenceBounds (drawable) {
return drawable.getFastBounds();
}
/**

View File

@@ -1,35 +0,0 @@
const Skin = require('../../src/Skin');
class MockSkinPool {
constructor () {
this._allDrawables = [];
}
static forDrawableSkin (drawable) {
const pool = new MockSkinPool();
pool.addDrawable(drawable);
pool.addSkin(drawable.skin);
return pool;
}
_skinWasAltered (skin) {
for (let i = 0; i < this._allDrawables.length; i++) {
const drawable = this._allDrawables[i];
if (drawable && drawable._skin === skin) {
drawable._skinWasAltered();
}
}
}
addDrawable (drawable) {
this._allDrawables.push(drawable);
return drawable;
}
addSkin (skin) {
skin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, skin));
return skin;
}
}
module.exports = MockSkinPool;

View File

@@ -8,7 +8,6 @@ global.document = {
const Drawable = require('../../src/Drawable');
const MockSkin = require('../fixtures/MockSkin');
const MockSkinPool = require('../fixtures/MockSkinPool');
const Rectangle = require('../../src/Rectangle');
/**
@@ -32,7 +31,6 @@ test('translate by position', t => {
const drawable = new Drawable();
drawable.skin = new MockSkin();
drawable.skin.size = [200, 50];
MockSkinPool.forDrawableSkin(drawable);
expected.initFromBounds(0, 200, -50, 0);
t.same(snapToNearest(drawable.getAABB()), expected);
@@ -49,7 +47,6 @@ test('translate by costume center', t => {
const drawable = new Drawable();
drawable.skin = new MockSkin();
drawable.skin.size = [200, 50];
MockSkinPool.forDrawableSkin(drawable);
drawable.skin.setRotationCenter(1, 0);
expected.initFromBounds(-1, 199, -50, 0);
@@ -67,7 +64,6 @@ test('translate and rotate', t => {
const drawable = new Drawable();
drawable.skin = new MockSkin();
drawable.skin.size = [200, 50];
MockSkinPool.forDrawableSkin(drawable);
drawable.updateProperties({position: [1, 2], direction: 0});
expected.initFromBounds(1, 51, 2, 202);
@@ -94,7 +90,6 @@ test('rotate by non-right-angles', t => {
drawable.skin = new MockSkin();
drawable.skin.size = [10, 10];
drawable.skin.setRotationCenter(5, 5);
MockSkinPool.forDrawableSkin(drawable);
expected.initFromBounds(-5, 5, -5, 5);
t.same(snapToNearest(drawable.getAABB()), expected);
@@ -111,7 +106,6 @@ test('scale', t => {
const drawable = new Drawable();
drawable.skin = new MockSkin();
drawable.skin.size = [200, 50];
MockSkinPool.forDrawableSkin(drawable);
drawable.updateProperties({scale: [100, 50]});
expected.initFromBounds(0, 200, -25, 0);
@@ -134,7 +128,6 @@ test('rotate and scale', t => {
const drawable = new Drawable();
drawable.skin = new MockSkin();
drawable.skin.size = [100, 1000];
MockSkinPool.forDrawableSkin(drawable);
drawable.skin.setRotationCenter(50, 50);
expected.initFromBounds(-50, 50, -950, 50);