Skip to content

Commit

Permalink
Refactor animations
Browse files Browse the repository at this point in the history
Also make point decay animation a little snappier.
  • Loading branch information
jcreedcmu committed Nov 20, 2023
1 parent 53bda21 commit 54501a1
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 64 deletions.
26 changes: 26 additions & 0 deletions src/core/animations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Point } from "../util/types";
import { vadd } from "../util/vutil";

export type Animation =
| { t: 'explosion', start_ms: number, duration_ms: number, center_in_world: Point, radius: number }
| { t: 'point-decay', start_ms: number, duration_ms: number, p_in_world_int: Point }
;

export function mkPointDecayAnimation(p: Point): Animation {
return {
t: 'point-decay',
duration_ms: 500,
p_in_world_int: p,
start_ms: Date.now(),
};
}

export function mkExplosionAnimation(p: Point, radius: number): Animation {
return {
t: 'explosion',
center_in_world: vadd(p, { x: 0.5, y: 0.5 }),
duration_ms: (radius + 1) * 250,
start_ms: Date.now(),
radius,
}
}
18 changes: 4 additions & 14 deletions src/core/state-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mkPointDecayAnimation, Animation, mkExplosionAnimation } from './animations';
import { DragWidgetPoint, WidgetPoint } from "../ui/widget-helpers";
import { logger } from "../util/debug";
import { produce } from "../util/produce";
Expand All @@ -9,7 +10,7 @@ import { PanicData, PauseData } from "./clock";
import { getLetterSample } from "./distribution";
import { checkConnected, checkGridWords, mkGridOfMainTiles } from "./grid";
import { Layer, Overlay, getOverlay, getOverlayLayer, mkOverlayFrom, overlayAny, overlayForEach, overlayPoints, setOverlay } from "./layer";
import { Animation, GameState, Location, MainTile, SelectionState, Tile, TileEntity, getBonusLayer } from "./state";
import { GameState, Location, MainTile, SelectionState, Tile, TileEntity, getBonusLayer } from "./state";
import { addHandTile, addWorldTile, ensureTileId, get_hand_tiles, get_main_tiles, get_tiles, removeTile } from "./tile-helpers";

export function addWorldTiles(state: GameState, tiles: Tile[]): GameState {
Expand Down Expand Up @@ -81,13 +82,7 @@ function killTileOfState(state: GameState, wp: DragWidgetPoint, radius: number,
switch (wp.t) {
case 'world': {
const p_in_world_int = vint(wp.p_in_local);
const anim: Animation = {
t: 'explosion',
center_in_world: vadd(p_in_world_int, { x: 0.5, y: 0.5 }),
duration_ms: (radius + 1) * 250,
start_ms: Date.now(),
radius,
}
const anim: Animation = mkExplosionAnimation(p_in_world_int, radius);

function tileAt(p: Point): MainTile | undefined {
return get_main_tiles(state).find(tile => vequal(tile.loc.p_in_world_int, p));
Expand Down Expand Up @@ -162,12 +157,7 @@ function resolveValid(state: GameState): GameState {
scorings.forEach(p => {
setOverlay(s.coreState.bonusOverlay, p, 'empty');
s.coreState.score++;
s.coreState.animations.push({
t: 'point-decay',
duration_ms: 1000,
p_in_world_int: p,
start_ms: Date.now(),
});
s.coreState.animations.push(mkPointDecayAnimation(p));
});
});
}
Expand Down
6 changes: 1 addition & 5 deletions src/core/state.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Animation } from './animations';
import { SE2 } from '../util/se2';
import { Point } from '../util/types';
import { Bonus, bonusGenerator, mkBonusLayer } from './bonus';
Expand Down Expand Up @@ -71,11 +72,6 @@ export type SelectionState = {
selectedIds: string[],
};

export type Animation =
| { t: 'explosion', start_ms: number, duration_ms: number, center_in_world: Point, radius: number }
| { t: 'point-decay', start_ms: number, duration_ms: number, p_in_world_int: Point }
;

export type CoreState = {
animations: Animation[],
toolIndex: number,
Expand Down
35 changes: 35 additions & 0 deletions src/ui/drawAnimation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Animation } from '../core/animations';
import { SE2 } from '../util/se2';
import { apply_to_rect } from "../util/se2-extra";
import { Point } from "../util/types";
import { unreachable } from "../util/util";
import { vscale, vsub } from "../util/vutil";
import { drawBonus } from "./drawBonus";

export function drawAnimation(d: CanvasRenderingContext2D, pan_canvas_from_world: SE2, time_ms: number, anim: Animation): void {
switch (anim.t) {
case 'explosion': {
const radius_in_world = (2 * anim.radius + 1) * 0.5 * (time_ms - anim.start_ms) / anim.duration_ms;
const radvec: Point = { x: radius_in_world, y: radius_in_world };
const rect_in_canvas = apply_to_rect(pan_canvas_from_world, {
p: vsub(anim.center_in_world, radvec), sz: vscale(radvec, 2)
});
d.strokeStyle = '#ff0000';
d.lineWidth = 3;
d.beginPath();
d.arc(rect_in_canvas.p.x + rect_in_canvas.sz.x / 2,
rect_in_canvas.p.y + rect_in_canvas.sz.y / 2,
rect_in_canvas.sz.y / 2,
0, 360,
);
d.stroke();
return;
} break;
case 'point-decay': {
const fraction = Math.min(1, Math.max(0, 1 - (time_ms - anim.start_ms) / anim.duration_ms));
drawBonus(d, pan_canvas_from_world, anim.p_in_world_int, fraction);
return;
} break;
}
unreachable(anim);
}
19 changes: 19 additions & 0 deletions src/ui/drawBonus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SE2 } from '../util/se2';
import { apply_to_rect } from "../util/se2-extra";
import { Point } from "../util/types";
import { midpointOfRect } from "../util/util";

export function drawBonus(d: CanvasRenderingContext2D, pan_canvas_from_world: SE2, p: Point, fraction: number = 1) {
const rect_in_canvas = apply_to_rect(pan_canvas_from_world, { p, sz: { x: 1, y: 1 } });
d.fillStyle = 'rgba(0,0,255,0.5)';
d.beginPath();
const m = midpointOfRect(rect_in_canvas);
d.moveTo(m.x, m.y);
d.arc(rect_in_canvas.p.x + rect_in_canvas.sz.x / 2,
rect_in_canvas.p.y + rect_in_canvas.sz.y / 2,
rect_in_canvas.sz.y * 0.4,
0, 2 * Math.PI * fraction,
);
d.fill();

}
49 changes: 4 additions & 45 deletions src/ui/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { getAssets } from "../core/assets";
import { getPanicFraction } from "../core/clock";
import { LocatedWord, getGrid } from "../core/grid";
import { getOverlay, getOverlayLayer } from "../core/layer";
import { Animation, GameState, TileEntity, getBonusLayer } from "../core/state";
import { GameState, TileEntity, getBonusLayer } from "../core/state";
import { getTileId, get_hand_tiles, get_main_tiles, isSelectedForDrag } from "../core/tile-helpers";
import { fillRect, fillText, strokeRect } from "../util/dutil";
import { SE2, apply, compose, inverse, translate } from '../util/se2';
import { apply_to_rect } from "../util/se2-extra";
import { Point, Rect } from "../util/types";
import { boundRect, midpointOfRect, unreachable } from "../util/util";
import { boundRect, midpointOfRect } from "../util/util";
import { vadd, vm, vscale, vsub, vtrans } from "../util/vutil";
import { drawAnimation } from "./drawAnimation";
import { drawBonus } from "./drawBonus";
import { CanvasInfo } from "./use-canvas";
import { canvas_from_drag_tile, pan_canvas_from_world_of_state } from "./view-helpers";
import { canvas_bds_in_canvas, canvas_from_hand, canvas_from_toolbar, hand_bds_in_canvas, pause_button_bds_in_canvas, toolbar_bds_in_canvas, world_bds_in_canvas } from "./widget-helpers";
Expand All @@ -24,34 +26,6 @@ export function paintWithScale(ci: CanvasInfo, state: GameState) {

const backgroundGray = '#eeeeee';

function drawAnimation(d: CanvasRenderingContext2D, pan_canvas_from_world: SE2, time_ms: number, anim: Animation): void {
switch (anim.t) {
case 'explosion': {
const radius_in_world = (2 * anim.radius + 1) * 0.5 * (time_ms - anim.start_ms) / anim.duration_ms;
const radvec: Point = { x: radius_in_world, y: radius_in_world };
const rect_in_canvas = apply_to_rect(pan_canvas_from_world, {
p: vsub(anim.center_in_world, radvec), sz: vscale(radvec, 2)
});
d.strokeStyle = '#ff0000';
d.lineWidth = 3;
d.beginPath();
d.arc(rect_in_canvas.p.x + rect_in_canvas.sz.x / 2,
rect_in_canvas.p.y + rect_in_canvas.sz.y / 2,
rect_in_canvas.sz.y / 2,
0, 360,
);
d.stroke();
return;
} break;
case 'point-decay': {
const fraction = Math.min(1, Math.max(0, 1 - (time_ms - anim.start_ms) / anim.duration_ms));
drawBonus(d, pan_canvas_from_world, anim.p_in_world_int, fraction);
return;
} break;
}
unreachable(anim);
}

export function drawPausedScreen(ci: CanvasInfo, state: GameState) {
const { d } = ci;

Expand All @@ -62,21 +36,6 @@ export function drawPausedScreen(ci: CanvasInfo, state: GameState) {
fillText(d, "paused", midpointOfRect(canvas_bds_in_canvas), 'black', '48px sans-serif');
}

function drawBonus(d: CanvasRenderingContext2D, pan_canvas_from_world: SE2, p: Point, fraction: number = 1) {
const rect_in_canvas = apply_to_rect(pan_canvas_from_world, { p, sz: { x: 1, y: 1 } });
d.fillStyle = 'rgba(0,0,255,0.5)';
d.beginPath();
const m = midpointOfRect(rect_in_canvas);
d.moveTo(m.x, m.y);
d.arc(rect_in_canvas.p.x + rect_in_canvas.sz.x / 2,
rect_in_canvas.p.y + rect_in_canvas.sz.y / 2,
rect_in_canvas.sz.y * 0.4,
0, 2 * Math.PI * fraction,
);
d.fill();

}

export function rawPaint(ci: CanvasInfo, state: GameState) {
const cs = state.coreState;

Expand Down

0 comments on commit 54501a1

Please sign in to comment.