import { SemanticPosition, PositionKind, CircleSide, Facing, CirclePosition, LongLines, HandConnection } from "../interpreterCommon.js"; import { SemanticAnimationKind } from "../lowLevelMove.js"; import { Hand } from "../rendererConstants.js"; import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js"; type allemandeMoves = "allemande" | "allemande orbit" | "gyre"; class AllemandeSingleVariant extends SingleVariantMoveInterpreter { moveAsLowLevelMoves(): LowLevelMovesForAllDancers { // Need to store this locally so checking move.move restricts move.parameters. const move = this.move; const allemandeCircling = move.move === "allemande orbit" ? move.parameters.circling1 : move.parameters.circling; const byHandOrShoulder = (move.move === "gyre" ? move.parameters.shoulder : move.parameters.hand) ? Hand.Right : Hand.Left; // TODO Not sure if this is right. const swap = allemandeCircling % 360 === 180; const returnToStart = allemandeCircling % 360 === 0; const intoWave = !swap && !returnToStart && allemandeCircling % 90 == 0; const intoWavePositions = !intoWave ? 0 : (allemandeCircling % 360 === 90) === (byHandOrShoulder === Hand.Left) ? 1 : -1; if (!swap && !returnToStart && !intoWave) { // TODO Support allemande that's not a swap or no-op. throw "Unsupported allemande circle amount: " + allemandeCircling; } return this.handlePairedMove(move.parameters.who, ({ startPos, around, withId, withPos }) => { let endPosition: SemanticPosition = startPos; let startingPos = startPos; if (swap) { // TODO This was more complicated. Is this wrong? endPosition = withPos; } else if (intoWave) { if (startPos.kind === PositionKind.ShortLines) { if (around === CircleSide.Left || around === CircleSide.Right) { // Fix startPos if necessary. Needed because pass through always swaps but sometimes shouldn't. let startWhich = startPos.which; if ((startPos.facing === Facing.Up || startPos.facing === Facing.Down) && ((byHandOrShoulder === Hand.Right) !== (startPos.facing === Facing.Up) !== startPos.which.isLeftOfSide())) { startWhich = startPos.which.swapOnSide() startingPos = { ...startPos, which: startWhich, }; } const endWhich = CirclePosition.fromSides(startingPos.which.leftRightSide(), startWhich.isLeftOfSide() !== (byHandOrShoulder === Hand.Right) !== (intoWavePositions === 1) ? CircleSide.Top : CircleSide.Bottom); endPosition = { kind: PositionKind.Circle, which: endWhich, facing: (startingPos.facing === Facing.Up) === (intoWavePositions === 1) ? endWhich.facingAcross() : endWhich.facingOut(), setOffset: startingPos.setOffset, lineOffset: startingPos.lineOffset, } } else { throw new Error("Allemande from short lines to line line in middle unsupported."); } } else { if (around === "Center") { const startCenter = startPos.longLines === LongLines.Center; const endWhich = startPos.which.circleRight(intoWavePositions); endPosition = { kind: PositionKind.Circle, which: endWhich, facing: startPos.which.facingOut(), longLines: startCenter ? undefined : LongLines.Center, setOffset: startPos.setOffset, lineOffset: startPos.lineOffset, } } else { const endWhich = startPos.which.toShortLines(intoWavePositions === 1 ? Hand.Right : Hand.Left); endPosition = { kind: PositionKind.ShortLines, which: endWhich, facing: endWhich.isLeftOfSide() === (byHandOrShoulder === Hand.Left) ? Facing.Up : Facing.Down, setOffset: startPos.setOffset, lineOffset: startPos.lineOffset, } } } } return this.combine([ { beats: move.beats, endPosition, movementPattern: { kind: SemanticAnimationKind.RotateAround, minAmount: byHandOrShoulder === Hand.Right ? allemandeCircling : -allemandeCircling, around, byHand: move.move === "allemande" || move.move === "allemande orbit" ? byHandOrShoulder : undefined, close: true, }, }, ], { ...startingPos, hands: startPos.hands && move.move !== "gyre" ? new Map([...startPos.hands.entries()].filter(([h, c]) => h === byHandOrShoulder)) : undefined }); }, move.move !== "allemande orbit" ? undefined : ({ id, startPos }) => { const orbitAmount = move.parameters.circling2; const swap = orbitAmount % 360 === 180; if (!swap && orbitAmount % 360 !== 0) { // TODO Support allemande that's not a swap or no-op. throw "Unsupported allemande orbit amount: " + orbitAmount; } const startingPos: SemanticPosition = { ...startPos, hands: undefined, balance: undefined, dancerDistance: undefined, } let endPosition: SemanticPosition; if (swap) { if (startingPos.kind === PositionKind.Circle) { endPosition = { ...startingPos, which: startingPos.which.swapDiagonal(), facing: startingPos.which.isLeft() ? Facing.Left : Facing.Right, } } else { endPosition = { ...startingPos, which: startingPos.which.swapSides(), facing: startingPos.which.isLeft() ? Facing.Left : Facing.Right, } } } else { endPosition = startingPos; } return this.combine([ { beats: move.beats, endPosition, movementPattern: { kind: SemanticAnimationKind.RotateAround, // Orbit is opposite direction of allemande. minAmount: byHandOrShoulder === Hand.Right ? -orbitAmount : +orbitAmount, around: "Center", byHand: undefined, close: false, }, }, ], startingPos); }); } } class Allemande extends MoveInterpreter { buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter { return new AllemandeSingleVariant(this, startingPos); } } moveInterpreters.set("allemande", Allemande); moveInterpreters.set("allemande orbit", Allemande); moveInterpreters.set("gyre", Allemande);