Skip to content

Commit

Permalink
Fix a bug with attached entities and origin change
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
starwed committed Dec 8, 2018
1 parent e8e27f0 commit 9ca2a0f
Showing 1 changed file with 41 additions and 15 deletions.
56 changes: 41 additions & 15 deletions src/spatial/2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -695,18 +706,22 @@ 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,
dy = this._y - e._y,
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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
},
Expand Down

0 comments on commit 9ca2a0f

Please sign in to comment.