Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependent positions on origin change #1202

Merged
merged 5 commits into from
Dec 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 49 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,9 +904,31 @@ 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;

if (this._mbr) {
this._calculateMBR();
}

// Update position in spatial map
this._entry.update(this._cbr || this._mbr || this);

this.trigger("OriginChanged");
this.trigger("Invalidate");
return this;
},

Expand Down
74 changes: 74 additions & 0 deletions tests/unit/spatial/2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,80 @@
_.strictEqual(child._rotation, 90, "child also rotates 90deg");
});

test("origin change affects children", function(_) {
var parent = Crafty.e("2D").attr({
x: 0,
y: 0,
w: 50,
h: 50
});
var child = Crafty.e("2D").attr({
x: 100,
y: 0,
w: 50,
h: 50
});
parent.attach(child);
parent.rotation = 90;
_.strictEqual(
child._y,
100,
"child rotates around top left corner (x position)"
);
_.strictEqual(
child._x,
0,
"child rotates around top left corner (y position)"
);

parent.origin(50, 0);
_.strictEqual(
child._y,
50,
"child rotates around top right corner (x position)"
);
_.strictEqual(
child._x,
50,
"child rotates around top right corner (y position)"
);
});

test("origin change affects MBR", function(_) {
var parent = Crafty.e("2D").attr({
x: 0,
y: 0,
w: 50,
h: 50
});

parent.rotation = 90;
var mbr1 = parent.mbr();
_.strictEqual(
mbr1._x,
-50,
"MBR rotates around top left corner (x position)"
);
_.strictEqual(
mbr1._y,
0,
"MBR rotates around top left corner (y position)"
);

parent.origin(50, 0);
var mbr2 = parent.mbr();
_.strictEqual(
mbr2._x,
0,
"MBR rotates around top right corner (x position)"
);
_.strictEqual(
mbr2._y,
-50,
"MBR rotates around top right corner (y position)"
);
});

test("origin properties", function(_) {
var player = Crafty.e("2D, Centered").attr({
x: 0,
Expand Down