Skip to content
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
27 changes: 19 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
const config: IConfig = {
name: "BulkAnimationEditor",
title: "Bulk Animation Editor",
version: "1.4.0",
debug: false
};

Expand All @@ -16,6 +15,18 @@ const last_used = {
stride_vertical: 0
};

enum AnimationDirection {
Right,
Down,
Both
}

const directions = [
AnimationDirection.Right,
AnimationDirection.Down,
AnimationDirection.Both,
];

const action_animation_create = tiled.registerAction(`${config.name}_CreateFromSelection`, animation_create);
action_animation_create.text = "Create Animations From Selection";
action_animation_create.icon = "images/icon-create.png";
Expand Down Expand Up @@ -112,12 +123,12 @@ function animation_create() {
result.confirmation_button.clicked.connect(() => {
const duration = result.animation_frame_duration.value;
const frame_count = result.animation_frames_input.value;
const direction = ["Right", "Down", "Both"][result.animation_direction.currentIndex];
const direction = result.animation_direction.currentIndex as AnimationDirection;
const stride: IStride = {
horizontal: result.animation_stride_horizontal.value,
vertical: result.animation_stride_vertical.value
};
if (!direction) {
if (direction === undefined) {
tiled.alert("No direction selected. (This should not occur)");
return;
}
Expand All @@ -143,11 +154,11 @@ function animation_create() {
});
}

export function get_tile_frames(tile: Tile, frame_count: number, duration: number, direction: string, tileset_selected_tiles: rect, tile_dimensions: ITilesetDimensions, stride: IStride = { horizontal: 0, vertical: 0 }) {
export function get_tile_frames(tile: Tile, frame_count: number, duration: number, direction: AnimationDirection, tileset_selected_tiles: rect, tile_dimensions: ITilesetDimensions, stride: IStride = { horizontal: 0, vertical: 0 }) {
const columns = tile_dimensions.tileset.columns;
const max_tiles = columns * tile_dimensions.tileset.rows;

if (direction === "Both") {
if (direction === AnimationDirection.Both) {
const h_advance = tileset_selected_tiles.width + stride.horizontal;
const v_advance = tileset_selected_tiles.height + stride.vertical;
const cells_per_row = 1 + Math.floor((columns - tileset_selected_tiles.x - tileset_selected_tiles.width) / h_advance);
Expand Down Expand Up @@ -175,12 +186,12 @@ export function get_tile_frames(tile: Tile, frame_count: number, duration: numbe
return frames;
}

const stride_value = direction === "Down"
const stride_value = direction === AnimationDirection.Down
? columns * (tileset_selected_tiles.height + stride.vertical)
: tileset_selected_tiles.width + stride.horizontal;

if (frame_count > 0) {
if (direction === "Right") {
if (direction === AnimationDirection.Right) {
if (tile.id % columns + (frame_count - 1) * stride_value >= columns) return null;
} else {
if (tile.id + (frame_count - 1) * stride_value >= max_tiles) return null;
Expand Down Expand Up @@ -246,7 +257,7 @@ function dialog_create(title: string, min_width?: number, min_height?: number):
function animation_dialog_create(dialog: Dialog, tileset_selected_tiles: rect, tile_dimensions: ITilesetDimensions): IAnimationConfirmation {
dialog.addHeading("The direction that animation frames advance in the sprite sheet", true);
dialog.addNewRow();
const animation_direction = dialog.addComboBox('Direction: ', ['Right', 'Down', 'Both']);
const animation_direction = dialog.addComboBox('Direction: ', directions.map(d => AnimationDirection[d]));

dialog.addSeparator();

Expand Down
6 changes: 6 additions & 0 deletions src/tests/_helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export enum AnimationDirection {
Right,
Down,
Both
}

(globalThis as any).tiled = {
activeAssetChanged: { connect: () => {} },
registerAction: () => ({} as any),
Expand Down
4 changes: 2 additions & 2 deletions src/tests/frames_both.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { test, expect } from "bun:test";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule } from "./_helpers";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule, AnimationDirection } from "./_helpers";

test("Both fills a row left-to-right then wraps to the next", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames = get_tile_frames(tile, 3, 100, "Both", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Both, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 12, duration: 100 });
expect(frames[1]).toEqual({ tileId: 15, duration: 100 });
Expand Down
4 changes: 2 additions & 2 deletions src/tests/frames_both_wide.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { test, expect } from "bun:test";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule } from "./_helpers";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule, AnimationDirection } from "./_helpers";

test("Both wraps to next row per cells_per_row", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(1);
const frames = get_tile_frames(tile, 2, 100, "Both", makeRect(1, 0, 5, 2), makeTilesetDimensions(10, 8));
const frames = get_tile_frames(tile, 2, 100, AnimationDirection.Both, makeRect(1, 0, 5, 2), makeTilesetDimensions(10, 8));
expect(frames[0].tileId).toBe(1);
expect(frames[1].tileId).toBe(21);
});
4 changes: 2 additions & 2 deletions src/tests/frames_down.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { test, expect } from "bun:test";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule } from "./_helpers";
import { makeTile, makeRect, makeTilesetDimensions, getIndexModule, AnimationDirection } from "./_helpers";

test("Down frames advance by columns * selection height", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames = get_tile_frames(tile, 3, 100, "Down", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Down, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 12, duration: 100 });
expect(frames[1]).toEqual({ tileId: 32, duration: 100 });
Expand Down
18 changes: 9 additions & 9 deletions src/tests/frames_stride.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { test, expect } from "bun:test";
import { makeTile, makeRect, makeTilesetDimensions, makeStride, getIndexModule } from "./_helpers";
import { makeTile, makeRect, makeTilesetDimensions, makeStride, getIndexModule, AnimationDirection } from "./_helpers";

test("Right stride adds horizontal stride", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(2);
const frames = get_tile_frames(tile, 3, 100, "Right", makeRect(0, 0, 3, 2), makeTilesetDimensions(15, 8), makeStride(1));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Right, makeRect(0, 0, 3, 2), makeTilesetDimensions(15, 8), makeStride(1));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 2, duration: 100 });
expect(frames[1]).toEqual({ tileId: 6, duration: 100 });
Expand All @@ -14,7 +14,7 @@ test("Right stride adds horizontal stride", async () => {
test("Down stride adds vertical stride", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames = get_tile_frames(tile, 3, 100, "Down", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(0, 1));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Down, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(0, 1));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 12, duration: 100 });
expect(frames[1]).toEqual({ tileId: 42, duration: 100 });
Expand All @@ -24,7 +24,7 @@ test("Down stride adds vertical stride", async () => {
test("Both stride adds horizontal + vertical stride", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames = get_tile_frames(tile, 3, 100, "Both", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(1, 10));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Both, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(1, 10));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 12, duration: 100 });
expect(frames[1]).toEqual({ tileId: 16, duration: 100 });
Expand All @@ -34,22 +34,22 @@ test("Both stride adds horizontal + vertical stride", async () => {
test("Zero stride produces same results as no stride", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames_no_stride = get_tile_frames(tile, 3, 100, "Right", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
const frames_zero_stride = get_tile_frames(tile, 3, 100, "Right", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8), makeStride(0, 0));
const frames_no_stride = get_tile_frames(tile, 3, 100, AnimationDirection.Right, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
const frames_zero_stride = get_tile_frames(tile, 3, 100, AnimationDirection.Right, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8), makeStride(0, 0));
expect(frames_no_stride).toEqual(frames_zero_stride);
});

test("returns null when frames reference non-existent tiles", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(75);
const frames = get_tile_frames(tile, 3, 100, "Right", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Right, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 8));
expect(frames).toBeNull();
});

test("negative horizontal stride reduces advance below selection width", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(0);
const frames = get_tile_frames(tile, 3, 100, "Right", makeRect(0, 0, 3, 2), makeTilesetDimensions(10, 8), makeStride(-1));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Right, makeRect(0, 0, 3, 2), makeTilesetDimensions(10, 8), makeStride(-1));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 0, duration: 100 });
expect(frames[1]).toEqual({ tileId: 2, duration: 100 });
Expand All @@ -59,7 +59,7 @@ test("negative horizontal stride reduces advance below selection width", async (
test("negative vertical stride reduces advance below selection height", async () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(12);
const frames = get_tile_frames(tile, 3, 100, "Down", makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(0, -1));
const frames = get_tile_frames(tile, 3, 100, AnimationDirection.Down, makeRect(2, 1, 3, 2), makeTilesetDimensions(10, 15), makeStride(0, -1));
expect(frames).toHaveLength(3);
expect(frames[0]).toEqual({ tileId: 12, duration: 100 });
expect(frames[1]).toEqual({ tileId: 22, duration: 100 });
Expand Down
40 changes: 20 additions & 20 deletions src/tests/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect, describe } from "bun:test";
import { makeTile, makeRect, makeTilesetDimensions, makeStride, makeTilesetAsset, getIndexModule } from "./_helpers";
import { makeTile, makeRect, makeTilesetDimensions, makeStride, makeTilesetAsset, getIndexModule, AnimationDirection } from "./_helpers";

// These constants are derived from test_map.json tileset configurations
const TILESETS = {
Expand Down Expand Up @@ -98,7 +98,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group1.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Right",
tile, 4, 500, AnimationDirection.Right,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -114,7 +114,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group1.rect1;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Down",
tile, 4, 500, AnimationDirection.Down,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -130,7 +130,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group1.rect1;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Both",
tile, 4, 500, AnimationDirection.Both,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -146,7 +146,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group1.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Both",
tile, 4, 500, AnimationDirection.Both,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -162,7 +162,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group1.square;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Both",
tile, 4, 500, AnimationDirection.Both,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -178,7 +178,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group3.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Right",
tile, 4, 500, AnimationDirection.Right,
makeRect(0, 0, 2, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -193,7 +193,7 @@ describe("integration: get_tile_frames with real tileset layouts", () => {
const cfg = TILESETS.group3.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Both",
tile, 4, 500, AnimationDirection.Both,
makeRect(0, 0, 2, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th)
);
Expand All @@ -211,7 +211,7 @@ describe("integration: stride with real tileset layouts", () => {
const cfg = TILESETS.group1.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 3, 500, "Right",
tile, 3, 500, AnimationDirection.Right,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th),
makeStride(1)
Expand All @@ -226,7 +226,7 @@ describe("integration: stride with real tileset layouts", () => {
const cfg = TILESETS.group1.rect1;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 3, 500, "Down",
tile, 3, 500, AnimationDirection.Down,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th),
makeStride(0, 1)
Expand All @@ -241,7 +241,7 @@ describe("integration: stride with real tileset layouts", () => {
const cfg = TILESETS.group1.square;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 3, 500, "Both",
tile, 3, 500, AnimationDirection.Both,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th),
makeStride(3, 0)
Expand All @@ -257,7 +257,7 @@ describe("integration: stride with real tileset layouts", () => {
const cfg = TILESETS.group1.rect2;
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 4, 500, "Right",
tile, 4, 500, AnimationDirection.Right,
makeRect(0, 0, 2, 2),
makeTilesetDimensions(cfg.cols, cfg.rows, cfg.tw, cfg.th),
makeStride(1)
Expand All @@ -274,7 +274,7 @@ describe("integration: frame_count edge cases with real tilesets", () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 0, 100, "Right",
tile, 0, 100, AnimationDirection.Right,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(6, 15, 32, 32)
);
Expand All @@ -285,7 +285,7 @@ describe("integration: frame_count edge cases with real tilesets", () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(42);
const frames = get_tile_frames(
tile, 1, 100, "Down",
tile, 1, 100, AnimationDirection.Down,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(12, 9, 32, 32)
);
Expand All @@ -297,7 +297,7 @@ describe("integration: frame_count edge cases with real tilesets", () => {
const { get_tile_frames } = await getIndexModule();
const tile = makeTile(0);
const frames = get_tile_frames(
tile, 20, 500, "Right",
tile, 20, 500, AnimationDirection.Right,
makeRect(0, 0, 3, 2),
makeTilesetDimensions(100, 10, 32, 32)
);
Expand All @@ -318,7 +318,7 @@ describe("integration: consistency across tileset variants", () => {
for (const cols of tilesetColumnWidths) {
const rows = 10;
const frames = get_tile_frames(
tile, 2, 100, "Right",
tile, 2, 100, AnimationDirection.Right,
sel,
makeTilesetDimensions(cols, rows, 32, 32)
);
Expand All @@ -333,8 +333,8 @@ describe("integration: consistency across tileset variants", () => {
const tile = makeTile(0);
const sel = makeRect(0, 0, 3, 2);

const frames6 = get_tile_frames(tile, 3, 100, "Down", sel, makeTilesetDimensions(6, 15, 32, 32));
const frames12 = get_tile_frames(tile, 3, 100, "Down", sel, makeTilesetDimensions(12, 9, 32, 32));
const frames6 = get_tile_frames(tile, 3, 100, AnimationDirection.Down, sel, makeTilesetDimensions(6, 15, 32, 32));
const frames12 = get_tile_frames(tile, 3, 100, AnimationDirection.Down, sel, makeTilesetDimensions(12, 9, 32, 32));

// 6 cols: stride = 6*2 = 12 → 0, 12, 24
// 12 cols: stride = 12*2 = 24 → 0, 24, 48
Expand All @@ -349,8 +349,8 @@ describe("integration: consistency across tileset variants", () => {
const sel = makeRect(0, 0, 2, 2);
const st = makeStride(1);

const frames16 = get_tile_frames(tile, 3, 100, "Right", sel, makeTilesetDimensions(8, 10, 16, 16), st);
const frames32 = get_tile_frames(tile, 3, 100, "Right", sel, makeTilesetDimensions(8, 10, 32, 32), st);
const frames16 = get_tile_frames(tile, 3, 100, AnimationDirection.Right, sel, makeTilesetDimensions(8, 10, 16, 16), st);
const frames32 = get_tile_frames(tile, 3, 100, AnimationDirection.Right, sel, makeTilesetDimensions(8, 10, 32, 32), st);

// stride = 2 + 1 = 3, independent of tile width/height
expect(frames16[1].tileId).toBe(3);
Expand Down
1 change: 0 additions & 1 deletion types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ declare interface IResult {
declare interface IConfig {
name: string
title: string
version: string
debug?: boolean
}

Expand Down