From 9ca2a0fc8ba4a444b2ab070160e10a5d0d415dc5 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sat, 8 Dec 2018 14:17:25 -0500 Subject: [PATCH] Fix a bug with attached entities and origin change When the origin of a rotated entity changes, we need to update the position of any attached entities. Rotation around an origin is equivalent to shifting to the origin, rotating, and then shifting back. When the origin changes, conceptually we can handle this by reversing the current rotation, changing the origin, and then rotating back. However, this turns out to be equivavent to a simple shift in position, dependent on the current rotation, and the shift in origin. So th implementation calculates this shift and propagates it to child entities. --- src/spatial/2d.js | 56 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/spatial/2d.js b/src/spatial/2d.js index fdddb03a..7439859a 100644 --- a/src/spatial/2d.js +++ b/src/spatial/2d.js @@ -6,6 +6,20 @@ var M = Math, PI = M.PI, DEG_TO_RAD = PI / 180; +// These versions of cos and sin ensure that rotations very close to 0 return 0. +// This avoids some problems where entities are not quite aligned with the grid. +var rounded_cos = function(drad) { + var cos = Math.cos(drad); + cos = -1e-10 < cos && cos < 1e-10 ? 0 : cos; + return cos; +}; + +var rounded_sin = function(drad) { + var sin = Math.sin(drad); + sin = -1e-10 < sin && sin < 1e-10 ? 0 : sin; + return sin; +}; + /**@ * #2D * @category 2D @@ -364,11 +378,8 @@ Crafty.c("2D", { dy1 = this._y - this._by1 - oy, dy2 = this._y + this._h + this._by2 - oy; - var ct = Math.cos(rad), - st = Math.sin(rad); - // Special case 90 degree rotations to prevent rounding problems - ct = ct < 1e-10 && ct > -1e-10 ? 0 : ct; - st = st < 1e-10 && st > -1e-10 ? 0 : st; + var ct = rounded_cos(rad), + st = rounded_sin(rad); // Calculate the new points relative to the origin, then find the new (absolute) bounding coordinates! var x0 = dx1 * ct + dy1 * st, @@ -695,11 +706,7 @@ Crafty.c("2D", { * moves in the same way. */ _cascade: function(e) { - if (!e) return; //no change in position - var i = 0, - children = this._children, - l = children.length, - obj; + if (!e) return; //use current position var dx = this._x - e._x, @@ -707,6 +714,14 @@ Crafty.c("2D", { dw = this._w - e._w, dh = this._h - e._h; + this._cascadeInner(dx, dy, dw, dh); + }, + + _cascadeInner: function(dx, dy, dw, dh) { + var i = 0, + children = this._children, + l = children.length, + obj; for (; i < l; ++i) { obj = children[i]; if (obj.__frozen) continue; @@ -736,11 +751,8 @@ Crafty.c("2D", { obj; // precalculate rotation info var drad = deg * DEG_TO_RAD; - var cos = Math.cos(drad); - var sin = Math.sin(drad); - // Avoid some rounding problems - cos = -1e-10 < cos && cos < 1e-10 ? 0 : cos; - sin = -1e-10 < sin && sin < 1e-10 ? 0 : sin; + var cos = rounded_cos(drad); + var sin = rounded_sin(drad); var ox = this._origin.x + this._x; var oy = this._origin.y + this._y; @@ -892,8 +904,22 @@ Crafty.c("2D", { } } + // When entity is rotated, we'll need to shift attached entities on origin change + // The below transformation is equivalent to unrotating, shifting the origin, + // and then rotating around this new origin + if (this.rotation) { + var rad = -this._rotation * DEG_TO_RAD; + var d_ox = x - this._origin.x; + var d_oy = y - this._origin.y; + var cos = rounded_cos(rad); + var sin = rounded_sin(rad); + var dx = d_ox * (1 - cos) - d_oy * sin; + var dy = d_oy * (1 - cos) + d_ox * sin; + this._cascadeInner(dx, dy, 0, 0); + } this._origin.x = x; this._origin.y = y; + this.trigger("OriginChanged"); return this; },