diff --git a/src/BitmapSkin.js b/src/BitmapSkin.js index 8db200487..0deb8af0c 100644 --- a/src/BitmapSkin.js +++ b/src/BitmapSkin.js @@ -55,8 +55,10 @@ class BitmapSkin extends Skin { * Set the contents of this skin to a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. * @param {int} [costumeResolution=1] - The resolution to use for this bitmap. + * @param {number[]=} rotationCenter - Optional rotation center for the bitmap. If not supplied, it will be + * calculated from the bounding box */ - setBitmap (bitmapData, costumeResolution) { + setBitmap (bitmapData, costumeResolution, rotationCenter) { const gl = this._renderer.gl; if (this._texture) { @@ -78,6 +80,9 @@ class BitmapSkin extends Skin { this._costumeResolution = costumeResolution || 1; this._textureSize = BitmapSkin._getBitmapSize(bitmapData); + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this.setRotationCenter.apply(this, rotationCenter); + this.emit(Skin.Events.WasAltered); } diff --git a/src/RenderWebGL.js b/src/RenderWebGL.js index 2caa6ec0f..5ca80f757 100644 --- a/src/RenderWebGL.js +++ b/src/RenderWebGL.js @@ -140,10 +140,12 @@ class RenderWebGL { * Use `createBitmapSkin` or `createSVGSkin` instead. * @param {!string} skinUrl The URL of the skin. * @param {!int} [costumeResolution] Optional: resolution for the skin. Ignored unless creating a new Bitmap skin. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin + * will be used. * @returns {!int} The ID of the Skin. * @deprecated */ - createSkinFromURL (skinUrl, costumeResolution) { + createSkinFromURL (skinUrl, costumeResolution, rotationCenter) { if (this._skinUrlMap.hasOwnProperty(skinUrl)) { const existingId = this._skinUrlMap[skinUrl]; @@ -179,7 +181,7 @@ class RenderWebGL { url: skinUrl }, (err, response, body) => { if (!err) { - newSkin.setSVG(body); + newSkin.setSVG(body, rotationCenter); } }); } else { @@ -187,7 +189,7 @@ class RenderWebGL { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { - newSkin.setBitmap(img, costumeResolution); + newSkin.setBitmap(img, costumeResolution, rotationCenter); }; img.src = skinUrl; } @@ -200,12 +202,13 @@ class RenderWebGL { * Create a new bitmap skin from a snapshot of the provided bitmap data. * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin. * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin * @returns {!int} the ID for the new skin. */ - createBitmapSkin (bitmapData, costumeResolution) { + createBitmapSkin (bitmapData, costumeResolution, rotationCenter) { const skinId = this._nextSkinId++; const newSkin = new BitmapSkin(skinId, this); - newSkin.setBitmap(bitmapData, costumeResolution); + newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter); this._allSkins[skinId] = newSkin; return skinId; } @@ -213,12 +216,13 @@ class RenderWebGL { /** * Create a new SVG skin. * @param {!string} svgData - new SVG to use. + * @param {number[]=} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the skin * @returns {!int} the ID for the new skin. */ - createSVGSkin (svgData) { + createSVGSkin (svgData, rotationCenter) { const skinId = this._nextSkinId++; const newSkin = new SVGSkin(skinId, this); - newSkin.setSVG(svgData); + newSkin.setSVG(svgData, rotationCenter); this._allSkins[skinId] = newSkin; return skinId; } @@ -678,8 +682,8 @@ class RenderWebGL { } // TODO: remove this after fully deprecating URL-based skin paths if ('skin' in properties) { - const {skin, costumeResolution} = properties; - const skinId = this.createSkinFromURL(skin, costumeResolution); + const {skin, costumeResolution, rotationCenter} = properties; + const skinId = this.createSkinFromURL(skin, costumeResolution, rotationCenter); drawable.skin = this._allSkins[skinId]; } if ('skinId' in properties) { diff --git a/src/SVGSkin.js b/src/SVGSkin.js index 345c61f84..3ef4cd6f4 100644 --- a/src/SVGSkin.js +++ b/src/SVGSkin.js @@ -53,8 +53,10 @@ class SVGSkin extends Skin { /** * Set the contents of this skin to a snapshot of the provided SVG data. * @param {string} svgData - new SVG to use. + * @param {number[]=} rotationCenter - Optional rotation center for the SVG. If not supplied, it will be + * calculated from the bounding box */ - setSVG (svgData) { + setSVG (svgData, rotationCenter) { this._svgRenderer.fromString(svgData, () => { const gl = this._renderer.gl; if (this._texture) { @@ -71,6 +73,8 @@ class SVGSkin extends Skin { this._texture = twgl.createTexture(gl, textureOptions); } + if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); + this.setRotationCenter.apply(this, rotationCenter); this.emit(Skin.Events.WasAltered); }); } diff --git a/src/Skin.js b/src/Skin.js index a307134ad..90d7de934 100644 --- a/src/Skin.js +++ b/src/Skin.js @@ -83,6 +83,14 @@ class Skin extends EventEmitter { } } + /** + * Get the center of the current bounding box + * @return {[number,number]} the center of the current bounding box + */ + calculateRotationCenter () { + return [this.size[0] / 2, this.size[1] / 2]; + } + /** * @abstract * @param {[number,number]} scale - The scaling factors to be used.