Skip to content

Commit

Permalink
feat: use HOF for svg shapes
Browse files Browse the repository at this point in the history
some functions are still not as HOF, thus it still needs a bit more work
  • Loading branch information
vincentfrochot committed Dec 19, 2024
1 parent 1ca06ed commit af730f9
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 255 deletions.
2 changes: 1 addition & 1 deletion src/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function processDrag(s: State): void {
cur.pos = [cur.epos[0] - cur.rel[0], cur.epos[1] - cur.rel[1]];

// move piece
const translation = util.posToTranslateAbs(s.dom.bounds(), s.dimensions, s.variant)(cur.origPos, s.orientation);
const translation = util.posToTranslateAbs(s.dom.bounds(), s.dimensions, s.variant)(cur.origPos, s.orientation); // because of util.translateAbs, it has to remain invoked from util.
translation[0] += cur.pos[0] + cur.dec[0];
translation[1] += cur.pos[1] + cur.dec[1];
util.translateAbs(cur.element, translation);
Expand Down
3 changes: 1 addition & 2 deletions src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { State } from './state';
import {
key2pos,
createEl,
posToTranslateRel,
posToTranslateAbs,
translateRel,
translateAbs,
Expand All @@ -22,7 +21,7 @@ export type SquareClasses = Map<cg.Key, string>;
export function render(s: State): void {
const orientation = s.orientation,
asP1: boolean = p1Pov(s),
posToTranslate = s.dom.relative ? posToTranslateRel : posToTranslateAbs(s.dom.bounds(), s.dimensions, s.variant),
posToTranslate = s.dom.relative ? s.posToTranslateRelative : s.posToTranslateAbsolute(s.dom.bounds(), s.dimensions, s.variant),
translate = s.dom.relative ? translateRel : translateAbs,
boardEl: HTMLElement = s.dom.elements.board,
pieces: cg.Pieces = s.pieces,
Expand Down
12 changes: 11 additions & 1 deletion src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { baseMove } from './board';
import { DragCurrent } from './drag';
import { Drawable } from './draw';
import { render } from './render';
import { timer } from './util';
import { posToTranslateAbs, posToTranslateRel, timer } from './util';
import { pos2px } from './svg';
import { key2pos } from './util';

Check failure on line 9 in src/state.ts

View workflow job for this annotation

GitHub Actions / build

'./util' import is duplicated

Check failure on line 9 in src/state.ts

View workflow job for this annotation

GitHub Actions / build

'./util' import is duplicated
import * as cg from './types';

export interface HeadlessState {
Expand Down Expand Up @@ -140,6 +142,10 @@ export interface HeadlessState {
singleClickMoveVariant: boolean;
baseMove: (state: HeadlessState, orig: cg.Key, dest: cg.Key) => cg.Piece | boolean;
render: (state: State) => void;
posToTranslateRelative: (pos: cg.Pos, orientation: cg.Orientation, bt: cg.BoardDimensions, v: cg.Variant) => cg.NumberPair;
posToTranslateAbsolute: (bounds: ClientRect, bt: cg.BoardDimensions, variant: cg.Variant) => (pos: cg.Pos, orientation: cg.Orientation) => cg.NumberPair;
pos2px: (pos: cg.Pos, bounds: ClientRect, bd: cg.BoardDimensions) => cg.NumberPair;
key2pos: (k: cg.Key) => cg.Pos;
}

export interface State extends HeadlessState {
Expand Down Expand Up @@ -254,5 +260,9 @@ export function defaults(): HeadlessState {
singleClickMoveVariant: false,
baseMove,
render,
posToTranslateRelative: posToTranslateRel,
posToTranslateAbsolute: posToTranslateAbs,
pos2px,
key2pos
};
}
26 changes: 15 additions & 11 deletions src/svg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { State } from './state';
import { key2pos } from './util';
import { Drawable, DrawShape, DrawShapePiece, DrawBrush, DrawBrushes, DrawModifiers } from './draw';
import * as cg from './types';
import * as T from './transformations';
Expand Down Expand Up @@ -187,33 +186,35 @@ function renderShape(
): SVGElement {
let el: SVGElement;
if (shape.customSvg) {
const orig = orient(key2pos(shape.orig), state.orientation, state.dimensions);
const orig = orient(state.key2pos(shape.orig), state.orientation, state.dimensions);
el = renderCustomSvg(shape.customSvg, orig, bounds, state.dimensions);
} else if (shape.piece)
el = renderPiece(
state,
state.drawable.pieces.baseUrl,
orient(key2pos(shape.orig), state.orientation, state.dimensions),
orient(state.key2pos(shape.orig), state.orientation, state.dimensions),
shape.piece,
bounds,
state.dimensions,
state.myPlayerIndex,
state.variant,
);
else {
const orig = orient(key2pos(shape.orig), state.orientation, state.dimensions);
const orig = orient(state.key2pos(shape.orig), state.orientation, state.dimensions);
if (shape.orig && shape.dest) {

Check warning on line 204 in src/svg.ts

View workflow job for this annotation

GitHub Actions / build

Unnecessary conditional, value is always truthy

Check warning on line 204 in src/svg.ts

View workflow job for this annotation

GitHub Actions / build

Unnecessary conditional, value is always truthy
let brush: DrawBrush = brushes[shape.brush!];
if (shape.modifiers) brush = makeCustomBrush(brush, shape.modifiers);
el = renderArrow(
state,
brush,
orig,
orient(key2pos(shape.dest), state.orientation, state.dimensions),
orient(state.key2pos(shape.dest), state.orientation, state.dimensions),
current,
(arrowDests.get(shape.dest) || 0) > 1,
bounds,
state.dimensions,
);
} else el = renderCircle(brushes[shape.brush!], orig, current, bounds, state.dimensions);
} else el = renderCircle(state, brushes[shape.brush!], orig, current, bounds, state.dimensions);
}
el.setAttribute('cgHash', hash);
return el;
Expand All @@ -238,13 +239,14 @@ function renderCustomSvg(customSvg: string, pos: cg.Pos, bounds: ClientRect, bd:
}

function renderCircle(
state: State,
brush: DrawBrush,
pos: cg.Pos,
current: boolean,
bounds: ClientRect,
bd: cg.BoardDimensions,
): SVGElement {
const o = pos2px(pos, bounds, bd),
const o = state.pos2px(pos, bounds, bd),
widths = circleWidth(bounds, bd),
radius = (bounds.width + bounds.height) / (2 * (bd.height + bd.width));
return setAttributes(createElement('circle'), {
Expand All @@ -259,6 +261,7 @@ function renderCircle(
}

function renderArrow(
state: State,
brush: DrawBrush,
orig: cg.Pos,
dest: cg.Pos,
Expand All @@ -268,8 +271,8 @@ function renderArrow(
bd: cg.BoardDimensions,
): SVGElement {
const m = arrowMargin(bounds, shorten && !current, bd),
a = pos2px(orig, bounds, bd),
b = pos2px(dest, bounds, bd),
a = state.pos2px(orig, bounds, bd),
b = state.pos2px(dest, bounds, bd),
dx = b[0] - a[0],
dy = b[1] - a[1],
angle = Math.atan2(dy, dx),
Expand All @@ -289,6 +292,7 @@ function renderArrow(
}

function renderPiece(
state: State,
baseUrl: string,
pos: cg.Pos,
piece: DrawShapePiece,
Expand All @@ -297,7 +301,7 @@ function renderPiece(
myPlayerIndex: cg.PlayerIndex,
variant: cg.Variant,
): SVGElement {
const o = pos2px(pos, bounds, bd),
const o = state.pos2px(pos, bounds, bd),
width = (bounds.width / bd.width) * (piece.scale || 1),
height = (bounds.height / bd.height) * (piece.scale || 1),
//name = piece.playerIndex[0] + piece.role[0].toUpperCase();
Expand Down Expand Up @@ -370,7 +374,7 @@ export function arrowMargin(bounds: ClientRect, shorten: boolean, bd: cg.BoardDi
return ((shorten ? 20 : 10) / (bd.width * 64)) * bounds.width;
}

function pos2px(pos: cg.Pos, bounds: ClientRect, bd: cg.BoardDimensions): cg.NumberPair {
export function pos2px(pos: cg.Pos, bounds: ClientRect, bd: cg.BoardDimensions): cg.NumberPair {
return [((pos[0] - 0.5) * bounds.width) / bd.width, ((bd.height + 0.5 - pos[1]) * bounds.height) / bd.height];
}

Expand Down
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export const posToTranslateRel = (
): cg.NumberPair => {
if (v === 'abalone') {
// "working" WIP: have to use HOF
return abalonePosToTranslateRel(pos, orientation);
return abalonePosToTranslateRel(pos, orientation, bt, v);
}
return posToTranslateBase(
pos,
Expand Down
11 changes: 11 additions & 0 deletions src/variants/abalone/config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import type { HeadlessState } from '../../state';
import type * as cg from '../../types';

import { baseMove } from './engine';
import { render } from './render';
import { pos2px } from './svg';
import { key2pos, posToTranslateAbs2 as posToTranslateAbs2Original, posToTranslateRel } from './util';

const posToTranslateAbs2 = (bounds: ClientRect, _bt: cg.BoardDimensions, _variant: cg.Variant) =>
(pos: cg.Pos, orientation: "p1" | "p2" | "left" | "right" | "p1vflip") =>
posToTranslateAbs2Original()(bounds, pos, orientation);

export const configure = (state: HeadlessState): void => {
// HOF
state.baseMove = baseMove;
state.render = render;
state.posToTranslateRelative = posToTranslateRel;
state.posToTranslateAbsolute = posToTranslateAbs2;
state.pos2px = pos2px;
state.key2pos = key2pos;

// these below could just have been overriden by a config object
state.animation.enabled = false;
Expand Down
5 changes: 1 addition & 4 deletions src/variants/abalone/directions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type * as cg from '../../types';

import type { DirectionString } from './types';
import { isValidKey } from './util';

export enum DiagonalDirectionString {
UpLeft = 'NW',
Expand Down Expand Up @@ -145,10 +146,6 @@ const traverseUntil = (pos: cg.Key, stop: (pos: cg.Key) => boolean, direction: D
}
};

const isValidKey = (key: cg.Key): boolean => {
return /^(a[1-5]|b[1-6]|c[1-7]|d[1-8]|e[1-9]|f[2-9]|g[3-9]|h[4-9]|i[5-9])$/.test(key);
};

const directionMappings: { [key in DirectionString]: (key: cg.Key) => cg.Key } = {
NW: (key: cg.Key) => (String.fromCharCode(key[0].charCodeAt(0)) + (parseInt(key[1]) + 1).toString()) as cg.Key,
NE: (key: cg.Key) => (String.fromCharCode(key[0].charCodeAt(0) + 1) + (parseInt(key[1]) + 1).toString()) as cg.Key,
Expand Down
4 changes: 2 additions & 2 deletions src/variants/abalone/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ export function processDrag(s: State): void {
cur.pos = [cur.epos[0] - cur.rel[0], cur.epos[1] - cur.rel[1]];

// move piece
const translation = posToTranslateAbs(s.dom.bounds(), s.dimensions, 'chess')(cur.origPos, s.orientation); // "working" WIP: have to use HOF
const translation = posToTranslateAbs(s.dom.bounds(), s.dimensions, 'chess')(cur.origPos, s.orientation); // because of translateAbs, it has to remain invoked from util.
translation[0] += cur.pos[0] + cur.dec[0];
translation[1] += cur.pos[1] + cur.dec[1];
translateAbs(cur.element, translation);
translateAbs(cur.element, translation); // "working" WIP: have to use HOF
}
}
processDrag(s);
Expand Down
49 changes: 0 additions & 49 deletions src/variants/abalone/draw.ts

This file was deleted.

24 changes: 12 additions & 12 deletions src/variants/abalone/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { AnimCurrent, AnimFadings, AnimVector, AnimVectors } from '../../anim';
import { DragCurrent } from '../../drag';
import { appendValue, isPieceNode, isSquareNode, posZIndex, removeNodes } from '../../render';

import { key2pos, translateAbs, translateRel, posToTranslateAbs2, posToTranslateRel2 } from './util';
import { translateAbs, translateRel } from './util';
import { computeMoveVectorPostMove } from './engine';

// @TODO: remove parts unrelated to Abalone
export const render = (s: State): void => {
const orientation = s.orientation,
asP1: boolean = p1Pov(s),
posToTranslate = s.dom.relative ? posToTranslateRel2 : posToTranslateAbs2(),
posToTranslate = s.dom.relative ? s.posToTranslateRelative : s.posToTranslateAbsolute(s.dom.bounds(), s.dimensions, s.variant),
translate = s.dom.relative ? translateRel : translateAbs,
boardEl: HTMLElement = s.dom.elements.board,
pieces: cg.Pieces = s.pieces,
Expand Down Expand Up @@ -52,7 +52,7 @@ export const render = (s: State): void => {
// if piece not being dragged anymore, remove dragging style
if (el.cgDragging && (!curDrag || curDrag.orig !== k)) {
el.classList.remove('dragging');
translate(el, posToTranslate(s.dom.bounds(), key2pos(k), orientation));
translate(el, posToTranslate(s.key2pos(k), orientation, s.dimensions, s.variant));
el.cgDragging = false;
}
// remove fading class if it still remains
Expand All @@ -65,16 +65,16 @@ export const render = (s: State): void => {
// continue animation if already animating and same piece
// (otherwise it could animate a captured piece)
if (anim && el.cgAnimating && elPieceName === pieceNameOf(pieceAtKey, s.myPlayerIndex, s.orientation)) {
const pos = key2pos(k);
const pos = s.key2pos(k);
pos[0] += anim[2];
pos[1] += anim[3];
el.classList.add('anim');
translate(el, posToTranslate(s.dom.bounds(), pos, orientation));
translate(el, posToTranslate(s.key2pos(k), orientation, s.dimensions, s.variant));
} else if (el.cgAnimating) {
el.cgAnimating = false;
el.classList.remove('anim');
translate(el, posToTranslate(s.dom.bounds(), key2pos(k), orientation));
if (s.addPieceZIndex) el.style.zIndex = posZIndex(key2pos(k), orientation, asP1, s.dimensions);
translate(el, posToTranslate(s.key2pos(k), orientation, s.dimensions, s.variant));
if (s.addPieceZIndex) el.style.zIndex = posZIndex(s.key2pos(k), orientation, asP1, s.dimensions);
}
// same piece: flag as same
if (elPieceName === pieceNameOf(pieceAtKey, s.myPlayerIndex, s.orientation) && (!fading || !el.cgFading)) {
Expand Down Expand Up @@ -109,7 +109,7 @@ export const render = (s: State): void => {
// if (!sameSquares.has(sk)) {
sMvdset = movedSquares.get(className);
sMvd = sMvdset && sMvdset.pop();
const translation = posToTranslate(s.dom.bounds(), key2pos(sk), orientation);
const translation = posToTranslate(s.key2pos(sk), orientation, s.dimensions, s.variant);
if (sMvd) {
sMvd.cgKey = sk;
translate(sMvd, translation);
Expand Down Expand Up @@ -137,22 +137,22 @@ export const render = (s: State): void => {
pMvd.classList.remove('fading');
pMvd.cgFading = false;
}
const pos = key2pos(k);
const pos = s.key2pos(k);
if (s.addPieceZIndex) pMvd.style.zIndex = posZIndex(pos, orientation, asP1, s.dimensions);
if (anim) {
pMvd.cgAnimating = true;
pMvd.classList.add('anim');
pos[0] += anim[2];
pos[1] += anim[3];
}
translate(pMvd, posToTranslate(s.dom.bounds(), pos, orientation));
translate(pMvd, posToTranslate(pos, orientation, s.dimensions, s.variant));
}
// no piece in moved obj: insert the new piece
// assumes the new piece is not being dragged
else {
const pieceName = pieceNameOf(p, s.myPlayerIndex, s.orientation),
pieceNode = createEl('piece', pieceName) as cg.PieceNode,
pos = key2pos(k); // used here to compute position
pos = s.key2pos(k); // used here to compute position

pieceNode.cgPiece = pieceName;
pieceNode.cgKey = k;
Expand All @@ -161,7 +161,7 @@ export const render = (s: State): void => {
pos[0] += anim[2];
pos[1] += anim[3];
}
translate(pieceNode, posToTranslate(s.dom.bounds(), pos, orientation));
translate(pieceNode, posToTranslate(pos, orientation, s.dimensions, s.variant));

if (s.addPieceZIndex) pieceNode.style.zIndex = posZIndex(pos, orientation, asP1, s.dimensions);

Expand Down
Loading

0 comments on commit af730f9

Please sign in to comment.