import { DanceRole } from "../danceCommon.js"; import { HandTo, HandConnection, PositionKind } from "../interpreterCommon.js"; import { Move } from "../libfigureMapper.js"; import { SemanticAnimationKind } from "../lowLevelMove.js"; import { Hand } from "../rendererConstants.js"; import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js"; const moveName: Move["move"] = "chain"; class ChainSingleVariant extends SingleVariantMoveInterpreter { moveAsLowLevelMoves(): LowLevelMovesForAllDancers { const mainRole = this.move.parameters.who === "gentlespoons" ? DanceRole.Lark : DanceRole.Robin; const pullToTurnBeats = 2; const pullBeats = this.move.beats / 2 - pullToTurnBeats; const turnBeats = this.move.beats - pullBeats - pullToTurnBeats; const chainHand: Hand = this.move.parameters.hand ? Hand.Right : Hand.Left; const cwCourtesyTurn = chainHand === Hand.Left; return this.handleCircleMove(({ id, startPos }) => { if (id.danceRole === mainRole) { const endWhich = startPos.which.swapDiagonal(); let endSet = startPos.setOffset ?? 0; let to: HandTo; switch (this.move.parameters.dir) { case "along": throw "Don't know what chaining along the set means."; case "across": to = HandTo.DiagonalAcrossCircle; break; case "right diagonal": to = HandTo.RightDiagonalAcrossCircle; endSet += startPos.which.isLeft() ? -1 : +1; break; case "left diagonal": to = HandTo.LeftDiagonalAcrossCircle; endSet += startPos.which.isLeft() ? +1 : -1; break; } const startPosition = { ...startPos, hands: new Map([[chainHand, { hand: chainHand, to }]]), facing: startPos.which.facingAcross(), }; const turnTo = chainHand === Hand.Right ? HandTo.DancerRight : HandTo.DancerLeft; return this.combine([ { beats: pullBeats, endPosition: { ...startPos, which: endWhich, facing: endWhich.facingUpOrDown(), setOffset: endSet, hands: new Map([ [Hand.Left, { hand: Hand.Left, to: turnTo }], [Hand.Right, { hand: Hand.Right, to: turnTo }], ]), }, movementPattern: { kind: SemanticAnimationKind.PassBy, around: "Center", side: chainHand, withHands: true, facing: "Forward", otherPath: "Swap", } }, { beats: pullToTurnBeats, endPosition: { ...this.startingPos, kind: PositionKind.Circle, which: endWhich.swapUpAndDown(), facing: endWhich.facingOut(), setOffset: endSet, }, movementPattern: { kind: SemanticAnimationKind.PassBy, side: chainHand.opposite(), withHands: true, around: endWhich.leftRightSide(), facing: "Forward", // TODO Is this right? otherPath: "Swap", } }, prevEnd => ({ beats: turnBeats, endPosition: { kind: PositionKind.Circle, which: endWhich, facing: endWhich.facingAcross(), hands: prevEnd.hands, setOffset: prevEnd.setOffset, lineOffset: prevEnd.lineOffset, }, movementPattern: { kind: SemanticAnimationKind.CourtesyTurn, clockwise: cwCourtesyTurn, } }) ], startPosition); } else { const startingPos = { ...startPos, hands: undefined }; return this.combine([ { beats: pullBeats, endPosition: { ...startingPos, facing: startingPos.which.facingUpOrDown() }, movementPattern: { kind: SemanticAnimationKind.Linear, } }, { beats: pullToTurnBeats, endPosition: { ...startingPos, which: startingPos.which.swapUpAndDown(), facing: startingPos.which.facingOut() }, movementPattern: { kind: SemanticAnimationKind.PassBy, side: chainHand.opposite(), withHands: true, around: startingPos.which.leftRightSide(), facing: "Forward", // TODO Is this right? otherPath: "Swap", } }, { beats: turnBeats, endPosition: { ...startingPos, // TODO Does CourtesyTurn always end in same position? which: startPos.which, facing: startPos.which.facingAcross(), }, movementPattern: { kind: SemanticAnimationKind.CourtesyTurn, clockwise: cwCourtesyTurn, } } ], startingPos); } }); } } class Chain extends MoveInterpreter { buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter { return new ChainSingleVariant(this, startingPos); } } moveInterpreters.set(moveName, Chain);