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

Tileengine rotated #402

Merged
merged 8 commits into from
Jun 21, 2024
60 changes: 56 additions & 4 deletions src/tileEngine.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { removeFromArray } from './utils.js';
// @see https://doc.mapeditor.org/en/stable/reference/global-tile-ids/
let FLIPPED_HORIZONTALLY = 0x80000000;
let FLIPPED_VERTICALLY = 0x40000000;
// tile can be rotated also and use the bit 30 in conjunction
// with bit 32 or/and 31 to denote that
let FLIPPED_DIAGONALLY = 0x20000000;
// @endif

/**
Expand Down Expand Up @@ -638,14 +641,41 @@ class TileEngine {
if (!tile) return;

let flipped = 0;
let rotated = 0;

// @ifdef TILEENGINE_TILED
// read flags
let flippedHorizontal = tile & FLIPPED_HORIZONTALLY;
let flippedVertical = tile & FLIPPED_VERTICALLY;
let turnedClockwise = 0;
let turnedAntiClockwise = 0;
let flippedAndturnedClockwise = 0;
let flippedAndturnedAntiClockwise = 0;
let flippedDiagonally = 0;
flipped = flippedHorizontal || flippedVertical;

tile &= ~(FLIPPED_HORIZONTALLY | FLIPPED_VERTICALLY);

flippedDiagonally = tile & FLIPPED_DIAGONALLY;
// Identify tile rotation
if (flippedDiagonally) {
if (flippedHorizontal && flippedVertical) {
flippedAndturnedClockwise = 1;
} else if (flippedHorizontal) {
turnedClockwise = 1;
} else if (flippedVertical) {
turnedAntiClockwise = 1;
} else {
flippedAndturnedAntiClockwise = 1;
}
rotated =
turnedClockwise ||
turnedAntiClockwise ||
flippedAndturnedClockwise ||
flippedAndturnedAntiClockwise;

tile &= ~FLIPPED_DIAGONALLY;
}
// @endif

// find the tileset the tile belongs to
Expand All @@ -669,7 +699,27 @@ class TileEngine {
let sy = ((offset / cols) | 0) * (tileheight + margin);

// @ifdef TILEENGINE_TILED
if (flipped) {
if (rotated) {
context.save();
// Translate to the center of the tile
context.translate(x + tilewidth / 2, y + tileheight / 2);
if (turnedAntiClockwise || flippedAndturnedAntiClockwise) {
// Rotate 90° anticlockwise
context.rotate(-Math.PI / 2); // 90° in radians
} else if (turnedClockwise || flippedAndturnedClockwise) {
// Rotate 90° clockwise
context.rotate(Math.PI / 2); // 90° in radians
}
if (
flippedAndturnedClockwise ||
flippedAndturnedAntiClockwise
) {
// Then flip horizontally
context.scale(-1, 1);
}
x = -tilewidth / 2;
y = -tileheight / 2;
} else if (flipped) {
context.save();
context.translate(
x + (flippedHorizontal ? tilewidth : 0),
Expand All @@ -679,6 +729,8 @@ class TileEngine {
flippedHorizontal ? -1 : 1,
flippedVertical ? -1 : 1
);
x = flipped ? 0 : x;
y = flipped ? 0 : y;
}
// @endif

Expand All @@ -688,14 +740,14 @@ class TileEngine {
sy,
tilewidth,
tileheight,
flipped ? 0 : x,
flipped ? 0 : y,
x,
y,
tilewidth,
tileheight
);

// @ifdef TILEENGINE_TILED
if (flipped) {
if (flipped || rotated) {
context.restore();
}
// @endif
Expand Down
104 changes: 104 additions & 0 deletions test/unit/tileEngine.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,110 @@ describe(
)
).to.be.true;
});

it('a tile flipped and turned clockwise', () => {
let tileEngine = TileEngine({
tilewidth: 10,
tileheight: 10,
width: 1,
height: 1,
tilesets: [
{
firstgid: 1,
image: new Image(),
columns: 10
}
],
layers: [
{
name: 'test',
data: [3 + 0x80000000 + 0x40000000 + 0x20000000]
}
]
});

let r = tileEngine._r.bind(tileEngine);
let ctx;
tileEngine._r = function overrideR(layer, context) {
ctx = context;
sinon.stub(context, 'drawImage').callsFake(noop);
sinon.stub(context, 'translate').callsFake(noop);
sinon.stub(context, 'scale').callsFake(noop);
sinon.stub(context, 'rotate').callsFake(noop);
r(layer, context);
};

tileEngine.renderLayer('test');

expect(ctx.translate.calledWith(5, 5)).to.be.true;
expect(ctx.rotate.calledWith(Math.PI / 2)).to.be.true;
expect(ctx.scale.calledWith(-1, 1)).to.be.true;
expect(
ctx.drawImage.calledWith(
tileEngine.tilesets[0].image,
20,
0,
10,
10,
-5,
-5,
10,
10
)
).to.be.true;
});

it('a tile flipped and turned anticlockwise', () => {
let tileEngine = TileEngine({
tilewidth: 10,
tileheight: 10,
width: 1,
height: 1,
tilesets: [
{
firstgid: 1,
image: new Image(),
columns: 10
}
],
layers: [
{
name: 'test',
data: [3 + 0x20000000]
}
]
});

let r = tileEngine._r.bind(tileEngine);
let ctx;
tileEngine._r = function overrideR(layer, context) {
ctx = context;
sinon.stub(context, 'drawImage').callsFake(noop);
sinon.stub(context, 'translate').callsFake(noop);
sinon.stub(context, 'scale').callsFake(noop);
sinon.stub(context, 'rotate').callsFake(noop);
r(layer, context);
};

tileEngine.renderLayer('test');

expect(ctx.translate.calledWith(5, 5)).to.be.true;
expect(ctx.rotate.calledWith(-Math.PI / 2)).to.be.true;
expect(ctx.scale.calledWith(-1, 1)).to.be.true;
expect(
ctx.drawImage.calledWith(
tileEngine.tilesets[0].image,
20,
0,
10,
10,
-5,
-5,
10,
10
)
).to.be.true;
});
} else {
it('does not rotate tile', () => {
let tileEngine = TileEngine({
Expand Down
Loading