forked from perelman/contra-renderer
Daniel Perelman
9fbf7d18ac
Currently just copied over the existing code and applied the quick fixes to get it to compile. Each move should be refactored to be handle its parameters earlier where applicable. But variants support should probably be added first so both refactors can happen together.
213 lines
8.8 KiB
TypeScript
213 lines
8.8 KiB
TypeScript
import * as animation from "./animation.js";
|
|
import { CoupleRole, DanceRole, DancerIdentity } from "./danceCommon.js";
|
|
import * as common from "./danceCommon.js";
|
|
import { Hand, setDistance } from "./rendererConstants.js";
|
|
import { nameLibFigureParameters, Move, chooser_pairz } from "./libfigureMapper.js";
|
|
import { LowLevelMove, SemanticAnimation, SemanticAnimationKind, animateFromLowLevelMoves } from "./lowLevelMove.js";
|
|
import { BalanceWeight, CirclePosition, CircleSide, CircleSideOrCenter, DancerDistance, Facing, HandConnection, HandTo, LongLines, PositionKind, SemanticPosition, ShortLinesPosition, StarGrip, handToDancerToSideInCircleFacingAcross, handsFourImproper, handsInCircle, handsInLine, handsInShortLine, oppositeFacing } from "./interpreterCommon.js";
|
|
import { ContraDBDance } from "./danceLibrary.js";
|
|
import { errorMoveInterpreterCtor, moveInterpreters } from "./moves/_moveInterpreter.js";
|
|
|
|
// Import all individual move files.
|
|
// ls [a-z]*.js | sed 's@.*@import "./moves/\0";@'
|
|
import "./moves/allemande.js";
|
|
import "./moves/balance.js";
|
|
import "./moves/balanceTheRing.js";
|
|
import "./moves/boxCirculate.js";
|
|
import "./moves/boxTheGnat.js";
|
|
import "./moves/butterflyWhirl.js";
|
|
import "./moves/californiaTwirl.js";
|
|
import "./moves/chain.js";
|
|
import "./moves/circle.js";
|
|
import "./moves/custom.js";
|
|
import "./moves/doSiDo.js";
|
|
import "./moves/downTheHall.js";
|
|
import "./moves/formAnOceanWave.js";
|
|
import "./moves/formLongWaves.js";
|
|
import "./moves/giveTake.js";
|
|
import "./moves/hey.js";
|
|
import "./moves/longLines.js";
|
|
import "./moves/madRobin.js";
|
|
import "./moves/passBy.js";
|
|
import "./moves/passThrough.js";
|
|
import "./moves/petronella.js";
|
|
import "./moves/promenade.js";
|
|
import "./moves/pullByDancers.js";
|
|
import "./moves/revolvingDoor.js";
|
|
import "./moves/rightLeftThrough.js";
|
|
import "./moves/rollAway.js";
|
|
import "./moves/roryOMore.js";
|
|
import "./moves/slice.js";
|
|
import "./moves/slideAlongSet.js";
|
|
import "./moves/star.js";
|
|
import "./moves/starPromenade.js";
|
|
import "./moves/swing.js";
|
|
import "./moves/turnAlone.js";
|
|
import "./moves/upTheHall.js";
|
|
|
|
|
|
function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: { move: Move; nextMove: Move; startingPos: Map<DancerIdentity, SemanticPosition>; numProgessions: number; }): Map<DancerIdentity, LowLevelMove[]> {
|
|
const moveInterpreter = moveInterpreters.get(move.move) ?? errorMoveInterpreterCtor;
|
|
return new moveInterpreter({ move, nextMove, numProgessions }).moveAsLowLevelMoves({ startingPos });
|
|
}
|
|
|
|
function danceAsLowLevelMoves(moves: Move[], startingPos: Map<DancerIdentity, SemanticPosition>): Map<DancerIdentity, LowLevelMove[]> {
|
|
const res = new Map<DancerIdentity, LowLevelMove[]>([...startingPos.keys()].map(id => [id, []]));
|
|
let currentPos = new Map<DancerIdentity, SemanticPosition>(startingPos);
|
|
let numProgessions = 0;
|
|
for (let i = 0; i < moves.length; i++) {
|
|
const move = moves[i];
|
|
const nextMove = i === moves.length - 1 ? moves[0] : moves[i + 1];
|
|
try {
|
|
if (i > 0 && move.beats === 0 && move.move === "slide along set") {
|
|
const slideLeft: boolean = move.parameters.slide;
|
|
for (const [id, currPos] of currentPos.entries()) {
|
|
const slideAmount = (currPos.which.leftRightSide() === CircleSide.Left) === slideLeft ? +0.5 : -0.5;
|
|
const setOffset = (currPos.setOffset ?? 0) + slideAmount;
|
|
currentPos.set(id, { ...currPos, setOffset });
|
|
|
|
const prevMove = res.get(id)!.at(-1)!;
|
|
prevMove.movementPattern.setSlideAmount = slideAmount;
|
|
prevMove.endPosition.setOffset = setOffset;
|
|
}
|
|
|
|
} else {
|
|
const newMoves = moveAsLowLevelMoves({ move, nextMove, startingPos: currentPos, numProgessions });
|
|
for (const [id, newMoveList] of newMoves.entries()) {
|
|
res.get(id)!.push(...newMoveList);
|
|
currentPos.set(id, newMoveList.at(-1)!.endPosition);
|
|
}
|
|
}
|
|
}
|
|
catch (ex) {
|
|
// catch exception so something can be displayed
|
|
for (const [id, pos] of currentPos.entries()) {
|
|
res.get(id)!.push({
|
|
beats: move.beats,
|
|
startPosition: pos,
|
|
endPosition: pos,
|
|
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
move,
|
|
startBeat: 0,
|
|
interpreterError: ex instanceof Error ? ex.message : ex,
|
|
});
|
|
}
|
|
}
|
|
if (move.progression) numProgessions++;
|
|
}
|
|
|
|
try {
|
|
const progression = animateFromLowLevelMoves(res).progression;
|
|
const progressionInSets = progression.y / setDistance;
|
|
|
|
// fixup end positions to match start of next move
|
|
// TODO Handle progression.
|
|
for (const [id, lowLevelMoves] of res.entries()) {
|
|
for (let i = 0; i < lowLevelMoves.length - 1; i++) {
|
|
if (!lowLevelMoves[i].endPosition) throw "endPosition is undefined";
|
|
lowLevelMoves[i].endPosition = lowLevelMoves[i + 1].startPosition;
|
|
if (!lowLevelMoves[i].endPosition) throw "endPosition is undefined now";
|
|
if (lowLevelMoves[i].movementPattern.kind === SemanticAnimationKind.StandStill) {
|
|
lowLevelMoves[i].startPosition = lowLevelMoves[i].endPosition;
|
|
if (i > 0) {
|
|
lowLevelMoves[i - 1].endPosition = lowLevelMoves[i].startPosition;
|
|
}
|
|
}
|
|
}
|
|
// If progression isn't detected properly, do nothing.
|
|
if (progressionInSets === 0) {
|
|
lowLevelMoves[lowLevelMoves.length - 1].interpreterError = "No progression detected. Not lining up end with start of dance.";
|
|
} else {
|
|
const startPos = lowLevelMoves[0].startPosition;
|
|
lowLevelMoves[lowLevelMoves.length - 1].endPosition = {
|
|
...lowLevelMoves[0].startPosition,
|
|
// progressed
|
|
setOffset: (startPos.setOffset ?? 0) + (id.coupleRole == CoupleRole.Ones ? 1 : -1) * progressionInSets,
|
|
};
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
catch (ex) {
|
|
res.get(DancerIdentity.OnesLark)![0].interpreterError = "Error detecting progression: " + (ex instanceof Error ? ex.message : ex);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
function StartingPosForFormation(formation: common.StartFormation, dance?: ContraDBDance): Map<DancerIdentity, SemanticPosition> {
|
|
const preamble = dance?.preamble ?? "";
|
|
if (preamble.includes("Starts in short waves, right hands to neighbors")) {
|
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
|
([id, pos]) => ([id, {
|
|
kind: PositionKind.ShortLines,
|
|
which: pos.which.toShortLines(Hand.Left),
|
|
facing: pos.which.isTop() ? Facing.Down : Facing.Up,
|
|
}])
|
|
));
|
|
} else if (preamble.includes("face down the hall in lines of 4, 1s splitting the 2s")) {
|
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
|
([id, pos]) => ([id, {
|
|
kind: PositionKind.ShortLines,
|
|
which: pos.which.unfoldToShortLines(CircleSide.Top),
|
|
facing: Facing.Down,
|
|
}])
|
|
));
|
|
}
|
|
|
|
switch (formation) {
|
|
case "improper":
|
|
return handsFourImproper;
|
|
case "Becket":
|
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
|
([id, pos]) => ([id, {...pos, which: pos.which.circleLeft(1)}])
|
|
));
|
|
case "Becket ccw":
|
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
|
([id, pos]) => ([id, {...pos, which: pos.which.circleRight(1)}])
|
|
));
|
|
case "Sawtooth Becket":
|
|
// Dancers start becket, then slide one person to the right. https://contradb.com/dances/848
|
|
// TODO Not sure this is right.
|
|
return new Map<common.DancerIdentity, SemanticPosition>([
|
|
[DancerIdentity.OnesLark, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.BottomLeft,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.OnesRobin, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.TopLeft,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.TwosLark, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.BottomRight,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.TwosRobin, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.TopRight,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
setOffset: 1,
|
|
}],
|
|
]);
|
|
}
|
|
}
|
|
|
|
export let mappedDance: Move[];
|
|
|
|
export let interpretedDance: Map<DancerIdentity, LowLevelMove[]>;
|
|
export let interpretedAnimation: animation.Animation;
|
|
|
|
export function loadDance(dance: ContraDBDance): animation.Animation {
|
|
mappedDance = dance.figures.map(nameLibFigureParameters);
|
|
interpretedDance = danceAsLowLevelMoves(mappedDance, StartingPosForFormation(dance.start_type, dance));
|
|
interpretedAnimation = animateFromLowLevelMoves(interpretedDance);
|
|
|
|
return interpretedAnimation;
|
|
}
|