forked from perelman/contra-renderer
216 lines
9.1 KiB
TypeScript
216 lines
9.1 KiB
TypeScript
import { SemanticPosition, PositionKind, CircleSide, Facing, CirclePosition, LongLines, HandConnection, DancerDistance } from "../interpreterCommon.js";
|
|
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
|
import { Hand } from "../rendererConstants.js";
|
|
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, MoveInterpreterCtorArgs, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
|
|
|
type allemandeMoves = "allemande" | "allemande orbit" | "gyre";
|
|
|
|
class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, allemandeMoves> {
|
|
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
|
// Need to store this locally so checking move.move restricts move.parameters.
|
|
const move = this.move;
|
|
|
|
for (const [id, pos] of this.startingPos.entries()) {
|
|
if (this.findPairOpposite(this.move.parameters.who, id) === null) {
|
|
if (pos.dancerDistance && pos.dancerDistance !== DancerDistance.Normal) {
|
|
throw new Error("Dancers not involved in allemande must start at normal dancer distance.");
|
|
}
|
|
if (pos.longLines) {
|
|
throw new Error("Dancers not involved in allemande must start not in long lines.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.handlePairedMove(this.move.parameters.who, ({ startPos, around, withId, withPos }) => {
|
|
if (startPos.kind === PositionKind.ShortLines && withPos.kind === PositionKind.ShortLines
|
|
&& startPos.which.leftRightSide() != withPos.which.leftRightSide()
|
|
&& (!startPos.which.isMiddle() || !withPos.which.isMiddle())) {
|
|
throw new Error("Allemande in short lines must either be on same side of set or in the middle.");
|
|
}
|
|
|
|
let endPosition: SemanticPosition = {...startPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
|
let startingPos = {...startPos };
|
|
if (this.moveInterpreter.swap) {
|
|
// TODO This was more complicated. Is this wrong?
|
|
endPosition = {...withPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
|
} else if (this.moveInterpreter.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) &&
|
|
((this.moveInterpreter.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()
|
|
!== (this.moveInterpreter.byHandOrShoulder === Hand.Right)
|
|
!== (this.moveInterpreter.intoWavePositions === 1)
|
|
? CircleSide.Top
|
|
: CircleSide.Bottom);
|
|
endPosition = {
|
|
kind: PositionKind.Circle,
|
|
which: endWhich,
|
|
facing: (startingPos.facing === Facing.Up) === (this.moveInterpreter.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(this.moveInterpreter.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(this.moveInterpreter.intoWavePositions === 1 ? Hand.Right : Hand.Left);
|
|
endPosition = {
|
|
kind: PositionKind.ShortLines,
|
|
which: endWhich,
|
|
facing: endWhich.isLeftOfSide() === (this.moveInterpreter.byHandOrShoulder === Hand.Left) ? Facing.Up : Facing.Down,
|
|
setOffset: startPos.setOffset,
|
|
lineOffset: startPos.lineOffset,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.combine([
|
|
{
|
|
beats: this.move.beats,
|
|
endPosition,
|
|
movementPattern: {
|
|
kind: SemanticAnimationKind.RotateAround,
|
|
minAmount: this.moveInterpreter.byHandOrShoulder === Hand.Right ? this.moveInterpreter.allemandeCircling : -this.moveInterpreter.allemandeCircling,
|
|
around,
|
|
byHand: move.move === "allemande" || move.move === "allemande orbit" ? this.moveInterpreter.byHandOrShoulder : undefined,
|
|
close: true,
|
|
},
|
|
},
|
|
], {
|
|
...startingPos,
|
|
hands: startPos.hands && move.move !== "gyre"
|
|
? new Map<Hand, HandConnection>([...startPos.hands.entries()].filter(([h, c]) => h === this.moveInterpreter.byHandOrShoulder))
|
|
: undefined
|
|
});
|
|
}, move.move !== "allemande orbit" ? undefined : ({ id, startPos }) => {
|
|
const startingPos: SemanticPosition = {
|
|
...startPos,
|
|
hands: undefined,
|
|
balance: undefined,
|
|
dancerDistance: undefined,
|
|
}
|
|
let endPosition: SemanticPosition;
|
|
if (this.moveInterpreter.orbitSwap) {
|
|
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: this.moveInterpreter.byHandOrShoulder === Hand.Right ? -this.moveInterpreter.orbitCircling : +this.moveInterpreter.orbitCircling,
|
|
around: "Center",
|
|
byHand: undefined,
|
|
close: false,
|
|
},
|
|
},
|
|
], startingPos);
|
|
});
|
|
}
|
|
}
|
|
|
|
class Allemande extends MoveInterpreter<allemandeMoves> {
|
|
public readonly allemandeCircling: number;
|
|
public readonly orbitCircling: number;
|
|
public readonly byHandOrShoulder: Hand;
|
|
public readonly swap: boolean;
|
|
public readonly orbitSwap: boolean;
|
|
public readonly returnToStart: boolean;
|
|
public readonly intoWave: boolean;
|
|
public readonly intoWavePositions: number;
|
|
|
|
constructor(args: MoveInterpreterCtorArgs<allemandeMoves>) {
|
|
super(args);
|
|
|
|
this.allemandeCircling = this.move.move === "allemande orbit" ? this.move.parameters.circling1 : this.move.parameters.circling;
|
|
this.byHandOrShoulder = (this.move.move === "gyre" ? this.move.parameters.shoulder : this.move.parameters.hand) ? Hand.Right : Hand.Left;
|
|
|
|
// TODO Not sure if this is right.
|
|
this.swap = this.allemandeCircling % 360 === 180;
|
|
this.returnToStart = this.allemandeCircling % 360 === 0;
|
|
this.intoWave = !this.swap && !this.returnToStart && this.allemandeCircling % 90 == 0;
|
|
this.intoWavePositions = !this.intoWave
|
|
? 0
|
|
: (this.allemandeCircling % 360 === 90) === (this.byHandOrShoulder === Hand.Left)
|
|
? +1
|
|
: -1;
|
|
if (!this.swap && !this.returnToStart && !this.intoWave) {
|
|
// TODO Support allemande that's not a swap or no-op.
|
|
throw "Unsupported allemande circle amount: " + this.allemandeCircling;
|
|
}
|
|
|
|
if (this.move.move === "allemande orbit") {
|
|
this.orbitCircling = this.move.parameters.circling2;
|
|
this.orbitSwap = this.orbitCircling % 360 === 180;
|
|
if (!this.orbitSwap && this.orbitCircling % 360 !== 0) {
|
|
// TODO Support allemande that's not a swap or no-op.
|
|
throw "Unsupported allemande orbit amount: " + this.orbitCircling;
|
|
}
|
|
} else {
|
|
this.orbitCircling = 0;
|
|
this.orbitSwap = false;
|
|
}
|
|
}
|
|
|
|
override allowStartingClose(): boolean {
|
|
return true;
|
|
}
|
|
override allowStartingLongLines(): boolean {
|
|
return true;
|
|
}
|
|
|
|
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
|
return new AllemandeSingleVariant(this, startingPos);
|
|
}
|
|
}
|
|
|
|
moveInterpreters.set("allemande", Allemande);
|
|
moveInterpreters.set("allemande orbit", Allemande);
|
|
moveInterpreters.set("gyre", Allemande); |