Compare commits
2 Commits
f182962365
...
fe481f37b5
Author | SHA1 | Date | |
---|---|---|---|
fe481f37b5 | |||
83dd2953e2 |
|
@ -219,24 +219,44 @@ function danceAsLowLevelMoves(moves: Move[], startingVariants: AllVariantsForMov
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function improperShortWaves(slideTo: Hand) {
|
||||||
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
||||||
|
([id, pos]) => ([id, {
|
||||||
|
kind: PositionKind.ShortLines,
|
||||||
|
which: pos.which.toShortLines(slideTo),
|
||||||
|
facing: pos.which.facingUpOrDown(),
|
||||||
|
}])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function improperUnfoldToShortLines(center: CircleSide.Bottom | CircleSide.Top) {
|
||||||
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
||||||
|
([id, pos]) => ([id, {
|
||||||
|
kind: PositionKind.ShortLines,
|
||||||
|
which: pos.which.unfoldToShortLines(center),
|
||||||
|
facing: center === CircleSide.Top ? Facing.Down : Facing.Up,
|
||||||
|
}])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function improperLongWaves(facingOut: DanceRole) {
|
||||||
|
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
||||||
|
([id, pos]) => ([id, {
|
||||||
|
...pos,
|
||||||
|
facing: id.danceRole === facingOut ? pos.which.facingOut() : pos.which.facingAcross(),
|
||||||
|
hands: handsInLine({ wavy: true, which: pos.which, facing: undefined })
|
||||||
|
}])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
function StartingPosForFormation(formation: common.StartFormation, dance?: ContraDBDance): Map<DancerIdentity, SemanticPosition> {
|
function StartingPosForFormation(formation: common.StartFormation, dance?: ContraDBDance): Map<DancerIdentity, SemanticPosition> {
|
||||||
const preamble = dance?.preamble ?? "";
|
const preamble = dance?.preamble ?? "";
|
||||||
if (preamble.includes("Starts in short waves, right hands to neighbors")) {
|
if (preamble.includes("Starts in short waves, right hands to neighbors")) {
|
||||||
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
return improperShortWaves(Hand.Left);
|
||||||
([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")) {
|
} else if (preamble.includes("face down the hall in lines of 4, 1s splitting the 2s")) {
|
||||||
return new Map<DancerIdentity, SemanticPosition>([...handsFourImproper.entries()].map(
|
return improperUnfoldToShortLines(CircleSide.Top);
|
||||||
([id, pos]) => ([id, {
|
} else if (preamble.includes("Long waves, larks face out")) {
|
||||||
kind: PositionKind.ShortLines,
|
return improperLongWaves(DanceRole.Lark);
|
||||||
which: pos.which.unfoldToShortLines(CircleSide.Top),
|
|
||||||
facing: Facing.Down,
|
|
||||||
}])
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (formation) {
|
switch (formation) {
|
||||||
|
@ -284,12 +304,24 @@ function StartingPosForFormation(formation: common.StartFormation, dance?: Contr
|
||||||
}
|
}
|
||||||
|
|
||||||
function startingVariantsForFormation(formation: common.StartFormation, dance?: ContraDBDance) : AllVariantsForMoveArgs {
|
function startingVariantsForFormation(formation: common.StartFormation, dance?: ContraDBDance) : AllVariantsForMoveArgs {
|
||||||
// TODO Handle various different starting position that all count as "improper" like short/long waves, etc.
|
const defaultFormation = StartingPosForFormation(formation, dance);
|
||||||
const pos = StartingPosForFormation(formation, dance);
|
if (formation !== "improper" || defaultFormation !== handsFourImproper) {
|
||||||
|
return new Map<string, MoveAsLowLevelMovesArgs>([
|
||||||
|
["Initial", { startingPos: defaultFormation }]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Handle various different starting position that all count as "improper" like short/long waves, etc.
|
||||||
|
return new Map<string, MoveAsLowLevelMovesArgs>([
|
||||||
|
["InitialCircle", { startingPos: handsFourImproper }],
|
||||||
|
["InitialShortWavesLeft", { startingPos: improperShortWaves(Hand.Left) }],
|
||||||
|
["InitialShortWavesRight", { startingPos: improperShortWaves(Hand.Right) }],
|
||||||
|
["InitialShortLinesDown", { startingPos: improperUnfoldToShortLines(CircleSide.Top) }],
|
||||||
|
["InitialShortLinesUp", { startingPos: improperUnfoldToShortLines(CircleSide.Bottom) }],
|
||||||
|
["InitialLongWavesLarksOut", { startingPos: improperLongWaves(DanceRole.Lark) }],
|
||||||
|
["InitialLongWavesRobinsOut", { startingPos: improperLongWaves(DanceRole.Robin) }],
|
||||||
|
]);
|
||||||
|
|
||||||
return new Map<string, MoveAsLowLevelMovesArgs>([
|
}
|
||||||
["Initial", { startingPos: pos }]
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export let mappedDance: Move[];
|
export let mappedDance: Move[];
|
||||||
|
|
|
@ -343,13 +343,25 @@ export class ShortLinesPosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
public shift(dir: Hand, facing: Facing.Up | Facing.Down): ShortLinesPosition {
|
public shift(dir: Hand, facing: Facing.Up | Facing.Down): ShortLinesPosition {
|
||||||
const shift = (dir === Hand.Left) === (facing === Facing.Down) ? -1 : +1;
|
const { newPos, wrap } = this.shiftWithWrap(dir, facing);
|
||||||
const newNum = ShortLinesPosition.enumValueToNumber(this.enumValue) + shift;
|
if (wrap) {
|
||||||
if (newNum < 0 || newNum > 3) {
|
|
||||||
throw new Error("Invalid shift: " + this + " facing " + facing + " to " + dir + ".");
|
throw new Error("Invalid shift: " + this + " facing " + facing + " to " + dir + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
|
||||||
return ShortLinesPosition.get(ShortLinesPosition.numberToEnumValue(newNum));
|
public shiftWithWrap(dir: Hand, facing: Facing.Up | Facing.Down): { newPos: ShortLinesPosition, wrap?: -1 | 1 } {
|
||||||
|
const shift = (dir === Hand.Left) === (facing === Facing.Down) ? -1 : +1;
|
||||||
|
const newNum = ShortLinesPosition.enumValueToNumber(this.enumValue) + shift;
|
||||||
|
const newPos = ShortLinesPosition.get(ShortLinesPosition.numberToEnumValue(newNum))
|
||||||
|
if (newNum < 0) {
|
||||||
|
return { newPos, wrap: -1 };
|
||||||
|
} else if (newNum > 3) {
|
||||||
|
return { newPos, wrap: +1 };
|
||||||
|
} else {
|
||||||
|
return { newPos };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isMiddle() : boolean {
|
public isMiddle() : boolean {
|
||||||
|
@ -540,24 +552,27 @@ export const handsFourImproper: Map<DancerIdentity, SemanticPosition & { kind: P
|
||||||
}],
|
}],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function handsInShortLine({ which, facing, wavy }: { which: ShortLinesPosition; facing: Facing.Up | Facing.Down; wavy: boolean; }): Map<Hand, HandConnection> {
|
export function handsInLongLines(wavy: boolean) {
|
||||||
return which.isMiddle() ? new Map<Hand, HandConnection>([
|
return new Map<Hand, HandConnection>([
|
||||||
[Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }],
|
[Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }],
|
||||||
[Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }],
|
[Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }],
|
||||||
]) : new Map<Hand, HandConnection>([
|
|
||||||
which.isLeft() === (facing === Facing.Up)
|
|
||||||
? [Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }]
|
|
||||||
: [Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }]
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handsInShortLine({ which, facing, wavy }: { which: ShortLinesPosition; facing: Facing.Up | Facing.Down; wavy: boolean; }): Map<Hand, HandConnection> {
|
||||||
|
return which.isMiddle()
|
||||||
|
? handsInLongLines(wavy)
|
||||||
|
: new Map<Hand, HandConnection>([
|
||||||
|
which.isLeft() === (facing === Facing.Up)
|
||||||
|
? [Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }]
|
||||||
|
: [Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }]
|
||||||
|
]);
|
||||||
|
}
|
||||||
export function handsInLine(args: { wavy: boolean, which: ShortLinesPosition | CirclePosition, facing?: Facing }) {
|
export function handsInLine(args: { wavy: boolean, which: ShortLinesPosition | CirclePosition, facing?: Facing }) {
|
||||||
if (args.which instanceof ShortLinesPosition && (args.facing === Facing.Up || args.facing === Facing.Down)) {
|
if (args.which instanceof ShortLinesPosition && (args.facing === Facing.Up || args.facing === Facing.Down)) {
|
||||||
return handsInShortLine({ wavy: args.wavy, which: args.which, facing: args.facing });
|
return handsInShortLine({ wavy: args.wavy, which: args.which, facing: args.facing });
|
||||||
} else {
|
} else {
|
||||||
return new Map<Hand, HandConnection>([
|
return handsInLongLines(args.wavy);
|
||||||
[Hand.Left, { hand: args.wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }],
|
|
||||||
[Hand.Right, { hand: args.wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function handToDancerToSideInCircleFacingAcross(which: CirclePosition): Map<Hand, HandConnection> {
|
export function handToDancerToSideInCircleFacingAcross(which: CirclePosition): Map<Hand, HandConnection> {
|
||||||
|
|
|
@ -34,7 +34,7 @@ type chooser_slice_return = "straight" | "diagonal" | "none";
|
||||||
type chooser_all_or_center_or_outsides = "all" | "center" | "outsides";
|
type chooser_all_or_center_or_outsides = "all" | "center" | "outsides";
|
||||||
type chooser_down_the_hall_ender = "turn-alone" | "turn-couple" | "circle" | "cozy" | "cloverleaf" | "thread-needle" | "right-high" | "sliding-doors" | "";
|
type chooser_down_the_hall_ender = "turn-alone" | "turn-couple" | "circle" | "cozy" | "cloverleaf" | "thread-needle" | "right-high" | "sliding-doors" | "";
|
||||||
type chooser_zig_zag_ender = "" | "ring" | "allemande";
|
type chooser_zig_zag_ender = "" | "ring" | "allemande";
|
||||||
type chooser_hey_length = "full" | "half" | "less than half" | "between half and full";
|
type chooser_hey_length = "full" | "half" | "less than half" | "between half and full" | { dancer: chooser_pairz, time: 1 | 2};
|
||||||
type chooser_swing_prefix = "none" | "balance" | "meltdown";
|
type chooser_swing_prefix = "none" | "balance" | "meltdown";
|
||||||
type chooser_dancers = "everyone" | "gentlespoon" | "gentlespoons" | "ladle" | "ladles" | "partners" | "neighbors" | "ones" | "twos" | "same roles" | "first corners" | "second corners" | "first gentlespoon" | "first ladle" | "second gentlespoon" | "second ladle" | "shadows";
|
type chooser_dancers = "everyone" | "gentlespoon" | "gentlespoons" | "ladle" | "ladles" | "partners" | "neighbors" | "ones" | "twos" | "same roles" | "first corners" | "second corners" | "first gentlespoon" | "first ladle" | "second gentlespoon" | "second ladle" | "shadows";
|
||||||
type chooser_pair = "gentlespoons" | "ladles" | "ones" | "twos" | "first corners" | "second corners";
|
type chooser_pair = "gentlespoons" | "ladles" | "ones" | "twos" | "first corners" | "second corners";
|
||||||
|
@ -410,7 +410,20 @@ export function nameLibFigureParameters(move: LibFigureMove): Move {
|
||||||
.map((v, i, a) => a.indexOf(v) === i && a.lastIndexOf(v) === i ? v : v + (a.slice(0, i).filter(el => el === v).length + 1));
|
.map((v, i, a) => a.indexOf(v) === i && a.lastIndexOf(v) === i ? v : v + (a.slice(0, i).filter(el => el === v).length + 1));
|
||||||
const parameters = {};
|
const parameters = {};
|
||||||
for (let i = 0; i < parameterNames.length; i++) {
|
for (let i = 0; i < parameterNames.length; i++) {
|
||||||
parameters[parameterNames[i]] = move.parameter_values[i];
|
if (parameterNames[i] === "until") {
|
||||||
|
// parse chooser_hey_length
|
||||||
|
const hey_length: string = move.parameter_values[i];
|
||||||
|
if (hey_length.includes('%%')) {
|
||||||
|
parameters[parameterNames[i]] = {
|
||||||
|
dancer: hey_length.slice(0, -3),
|
||||||
|
time: parseInt(hey_length.at(-1)!),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
parameters[parameterNames[i]] = hey_length;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parameters[parameterNames[i]] = move.parameter_values[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const beats: number = parameters["beats"];
|
const beats: number = parameters["beats"];
|
||||||
parameters["beats"] = undefined;
|
parameters["beats"] = undefined;
|
||||||
|
|
|
@ -395,6 +395,15 @@ export abstract class MoveInterpreter<N extends MoveName> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.size === 0) throw error;
|
if (res.size === 0) throw error;
|
||||||
|
else if (res.size > 1) {
|
||||||
|
// TODO Try to reduce variants if possible.
|
||||||
|
// XXX TODO Simple hack: perfer starting improper in a circle.
|
||||||
|
if ([...res.values()].find(v => v.previousMoveVariant === "InitialCircle")) {
|
||||||
|
for (const key of [...res.entries()].filter(([k, v]) => v.previousMoveVariant !== "InitialCircle").map(([k, v]) => k)) {
|
||||||
|
res.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { SemanticPosition, PositionKind, CircleSide, Facing, CirclePosition, LongLines, HandConnection, DancerDistance } from "../interpreterCommon.js";
|
import { SemanticPosition, PositionKind, CircleSide, Facing, CirclePosition, LongLines, HandConnection, DancerDistance } from "../interpreterCommon.js";
|
||||||
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
||||||
import { Hand } from "../rendererConstants.js";
|
import { Hand } from "../rendererConstants.js";
|
||||||
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, MoveInterpreterCtorArgs, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
||||||
|
|
||||||
type allemandeMoves = "allemande" | "allemande orbit" | "gyre";
|
type allemandeMoves = "allemande" | "allemande orbit" | "gyre";
|
||||||
|
|
||||||
|
@ -10,20 +10,18 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
// Need to store this locally so checking move.move restricts move.parameters.
|
// Need to store this locally so checking move.move restricts move.parameters.
|
||||||
const move = this.move;
|
const move = this.move;
|
||||||
|
|
||||||
const allemandeCircling = move.move === "allemande orbit" ? move.parameters.circling1 : move.parameters.circling;
|
for (const [id, pos] of this.startingPos.entries()) {
|
||||||
const byHandOrShoulder = (move.move === "gyre" ? move.parameters.shoulder : move.parameters.hand) ? Hand.Right : Hand.Left;
|
if (this.findPairOpposite(this.move.parameters.who, id) === null) {
|
||||||
|
if (pos.dancerDistance && pos.dancerDistance !== DancerDistance.Normal) {
|
||||||
// TODO Not sure if this is right.
|
throw new Error("Dancers not involved in allemande must start at normal dancer distance.");
|
||||||
const swap = allemandeCircling % 360 === 180;
|
}
|
||||||
const returnToStart = allemandeCircling % 360 === 0;
|
if (pos.longLines) {
|
||||||
const intoWave = !swap && !returnToStart && allemandeCircling % 90 == 0;
|
throw new Error("Dancers not involved in allemande must start not in long lines.");
|
||||||
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 }) => {
|
return this.handlePairedMove(this.move.parameters.who, ({ startPos, around, withId, withPos }) => {
|
||||||
if (startPos.kind === PositionKind.ShortLines && withPos.kind === PositionKind.ShortLines
|
if (startPos.kind === PositionKind.ShortLines && withPos.kind === PositionKind.ShortLines
|
||||||
&& startPos.which.leftRightSide() != withPos.which.leftRightSide()
|
&& startPos.which.leftRightSide() != withPos.which.leftRightSide()
|
||||||
&& (!startPos.which.isMiddle() || !withPos.which.isMiddle())) {
|
&& (!startPos.which.isMiddle() || !withPos.which.isMiddle())) {
|
||||||
|
@ -32,16 +30,16 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
|
|
||||||
let endPosition: SemanticPosition = {...startPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
let endPosition: SemanticPosition = {...startPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
||||||
let startingPos = {...startPos };
|
let startingPos = {...startPos };
|
||||||
if (swap) {
|
if (this.moveInterpreter.swap) {
|
||||||
// TODO This was more complicated. Is this wrong?
|
// TODO This was more complicated. Is this wrong?
|
||||||
endPosition = {...withPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
endPosition = {...withPos, dancerDistance: undefined, longLines: undefined, balance: undefined };
|
||||||
} else if (intoWave) {
|
} else if (this.moveInterpreter.intoWave) {
|
||||||
if (startPos.kind === PositionKind.ShortLines) {
|
if (startPos.kind === PositionKind.ShortLines) {
|
||||||
if (around === CircleSide.Left || around === CircleSide.Right) {
|
if (around === CircleSide.Left || around === CircleSide.Right) {
|
||||||
// Fix startPos if necessary. Needed because pass through always swaps but sometimes shouldn't.
|
// Fix startPos if necessary. Needed because pass through always swaps but sometimes shouldn't.
|
||||||
let startWhich = startPos.which;
|
let startWhich = startPos.which;
|
||||||
if ((startPos.facing === Facing.Up || startPos.facing === Facing.Down) &&
|
if ((startPos.facing === Facing.Up || startPos.facing === Facing.Down) &&
|
||||||
((byHandOrShoulder === Hand.Right)
|
((this.moveInterpreter.byHandOrShoulder === Hand.Right)
|
||||||
!== (startPos.facing === Facing.Up)
|
!== (startPos.facing === Facing.Up)
|
||||||
!== startPos.which.isLeftOfSide())) {
|
!== startPos.which.isLeftOfSide())) {
|
||||||
startWhich = startPos.which.swapOnSide()
|
startWhich = startPos.which.swapOnSide()
|
||||||
|
@ -53,14 +51,14 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
|
|
||||||
const endWhich = CirclePosition.fromSides(startingPos.which.leftRightSide(),
|
const endWhich = CirclePosition.fromSides(startingPos.which.leftRightSide(),
|
||||||
startWhich.isLeftOfSide()
|
startWhich.isLeftOfSide()
|
||||||
!== (byHandOrShoulder === Hand.Right)
|
!== (this.moveInterpreter.byHandOrShoulder === Hand.Right)
|
||||||
!== (intoWavePositions === 1)
|
!== (this.moveInterpreter.intoWavePositions === 1)
|
||||||
? CircleSide.Top
|
? CircleSide.Top
|
||||||
: CircleSide.Bottom);
|
: CircleSide.Bottom);
|
||||||
endPosition = {
|
endPosition = {
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
which: endWhich,
|
which: endWhich,
|
||||||
facing: (startingPos.facing === Facing.Up) === (intoWavePositions === 1)
|
facing: (startingPos.facing === Facing.Up) === (this.moveInterpreter.intoWavePositions === 1)
|
||||||
? endWhich.facingAcross()
|
? endWhich.facingAcross()
|
||||||
: endWhich.facingOut(),
|
: endWhich.facingOut(),
|
||||||
setOffset: startingPos.setOffset,
|
setOffset: startingPos.setOffset,
|
||||||
|
@ -72,7 +70,7 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
} else {
|
} else {
|
||||||
if (around === "Center") {
|
if (around === "Center") {
|
||||||
const startCenter = startPos.longLines === LongLines.Center;
|
const startCenter = startPos.longLines === LongLines.Center;
|
||||||
const endWhich = startPos.which.circleRight(intoWavePositions);
|
const endWhich = startPos.which.circleRight(this.moveInterpreter.intoWavePositions);
|
||||||
endPosition = {
|
endPosition = {
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
which: endWhich,
|
which: endWhich,
|
||||||
|
@ -82,11 +80,11 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
lineOffset: startPos.lineOffset,
|
lineOffset: startPos.lineOffset,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const endWhich = startPos.which.toShortLines(intoWavePositions === 1 ? Hand.Right : Hand.Left);
|
const endWhich = startPos.which.toShortLines(this.moveInterpreter.intoWavePositions === 1 ? Hand.Right : Hand.Left);
|
||||||
endPosition = {
|
endPosition = {
|
||||||
kind: PositionKind.ShortLines,
|
kind: PositionKind.ShortLines,
|
||||||
which: endWhich,
|
which: endWhich,
|
||||||
facing: endWhich.isLeftOfSide() === (byHandOrShoulder === Hand.Left) ? Facing.Up : Facing.Down,
|
facing: endWhich.isLeftOfSide() === (this.moveInterpreter.byHandOrShoulder === Hand.Left) ? Facing.Up : Facing.Down,
|
||||||
setOffset: startPos.setOffset,
|
setOffset: startPos.setOffset,
|
||||||
lineOffset: startPos.lineOffset,
|
lineOffset: startPos.lineOffset,
|
||||||
}
|
}
|
||||||
|
@ -96,30 +94,23 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
|
|
||||||
return this.combine([
|
return this.combine([
|
||||||
{
|
{
|
||||||
beats: move.beats,
|
beats: this.move.beats,
|
||||||
endPosition,
|
endPosition,
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.RotateAround,
|
||||||
minAmount: byHandOrShoulder === Hand.Right ? allemandeCircling : -allemandeCircling,
|
minAmount: this.moveInterpreter.byHandOrShoulder === Hand.Right ? this.moveInterpreter.allemandeCircling : -this.moveInterpreter.allemandeCircling,
|
||||||
around,
|
around,
|
||||||
byHand: move.move === "allemande" || move.move === "allemande orbit" ? byHandOrShoulder : undefined,
|
byHand: move.move === "allemande" || move.move === "allemande orbit" ? this.moveInterpreter.byHandOrShoulder : undefined,
|
||||||
close: true,
|
close: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
], {
|
], {
|
||||||
...startingPos,
|
...startingPos,
|
||||||
hands: startPos.hands && move.move !== "gyre"
|
hands: startPos.hands && move.move !== "gyre"
|
||||||
? new Map<Hand, HandConnection>([...startPos.hands.entries()].filter(([h, c]) => h === byHandOrShoulder))
|
? new Map<Hand, HandConnection>([...startPos.hands.entries()].filter(([h, c]) => h === this.moveInterpreter.byHandOrShoulder))
|
||||||
: undefined
|
: undefined
|
||||||
});
|
});
|
||||||
}, move.move !== "allemande orbit" ? undefined : ({ id, startPos }) => {
|
}, 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 = {
|
const startingPos: SemanticPosition = {
|
||||||
...startPos,
|
...startPos,
|
||||||
hands: undefined,
|
hands: undefined,
|
||||||
|
@ -127,7 +118,7 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
dancerDistance: undefined,
|
dancerDistance: undefined,
|
||||||
}
|
}
|
||||||
let endPosition: SemanticPosition;
|
let endPosition: SemanticPosition;
|
||||||
if (swap) {
|
if (this.moveInterpreter.orbitSwap) {
|
||||||
if (startingPos.kind === PositionKind.Circle) {
|
if (startingPos.kind === PositionKind.Circle) {
|
||||||
endPosition =
|
endPosition =
|
||||||
{
|
{
|
||||||
|
@ -154,7 +145,7 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.RotateAround,
|
||||||
// Orbit is opposite direction of allemande.
|
// Orbit is opposite direction of allemande.
|
||||||
minAmount: byHandOrShoulder === Hand.Right ? -orbitAmount : +orbitAmount,
|
minAmount: this.moveInterpreter.byHandOrShoulder === Hand.Right ? -this.moveInterpreter.orbitCircling : +this.moveInterpreter.orbitCircling,
|
||||||
around: "Center",
|
around: "Center",
|
||||||
byHand: undefined,
|
byHand: undefined,
|
||||||
close: false,
|
close: false,
|
||||||
|
@ -166,6 +157,48 @@ class AllemandeSingleVariant extends SingleVariantMoveInterpreter<Allemande, all
|
||||||
}
|
}
|
||||||
|
|
||||||
class Allemande extends MoveInterpreter<allemandeMoves> {
|
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 {
|
override allowStartingClose(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,263 +1,242 @@
|
||||||
import { DancerIdentity } from "../danceCommon.js";
|
import { DancerIdentity } from "../danceCommon.js";
|
||||||
import { SemanticPosition, PositionKind, ShortLinesPosition, CirclePosition, CircleSide, Facing } from "../interpreterCommon.js";
|
import { SemanticPosition, PositionKind, ShortLinesPosition, CirclePosition, CircleSide, Facing } from "../interpreterCommon.js";
|
||||||
import { Move } from "../libfigureMapper.js";
|
|
||||||
import { LowLevelMove, SemanticAnimationKind } from "../lowLevelMove.js";
|
import { LowLevelMove, SemanticAnimationKind } from "../lowLevelMove.js";
|
||||||
import { Hand } from "../rendererConstants.js";
|
import { Hand } from "../rendererConstants.js";
|
||||||
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, MoveInterpreterCtorArgs, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
||||||
import { dancerIsPair } from "../libfigure/util.js";
|
import { dancerIsPair } from "../libfigure/util.js";
|
||||||
|
|
||||||
const moveName: Move["move"] = "hey";
|
const moveName = "hey";
|
||||||
|
|
||||||
|
type HeyStep = {
|
||||||
|
kind: "StandStill" | "Loop" | "CenterPass" | "EndsPassIn" | "EndsPassOut" | "Ricochet",
|
||||||
|
endPosition: SemanticPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixupHeyOtherPath(withoutOtherPath: Map<DancerIdentity, (LowLevelMove & { heyStep?: HeyStep })[]>): Map<DancerIdentity, LowLevelMove[]> {
|
||||||
|
const numSteps = withoutOtherPath.get(DancerIdentity.OnesLark)!.length;
|
||||||
|
for (let i = 0; i < numSteps; i++) {
|
||||||
|
for (const id of withoutOtherPath.keys()) {
|
||||||
|
const lowLevelMove = withoutOtherPath.get(id)![i];
|
||||||
|
if (lowLevelMove.movementPattern.kind !== SemanticAnimationKind.PassBy
|
||||||
|
|| !lowLevelMove.heyStep
|
||||||
|
|| lowLevelMove.movementPattern.otherPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const heyStepKind = lowLevelMove.heyStep.kind;
|
||||||
|
let foundPair = false;
|
||||||
|
for (const otherId of withoutOtherPath.keys()) {
|
||||||
|
const otherLowLevelMove = withoutOtherPath.get(otherId)![i];
|
||||||
|
if (id === otherId
|
||||||
|
|| otherLowLevelMove.movementPattern.kind !== SemanticAnimationKind.PassBy
|
||||||
|
|| !otherLowLevelMove.heyStep
|
||||||
|
|| otherLowLevelMove.movementPattern.otherPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherHeyStepKind = otherLowLevelMove.heyStep.kind;
|
||||||
|
|
||||||
|
if (heyStepKind === "CenterPass" && otherHeyStepKind === "CenterPass"
|
||||||
|
|| (lowLevelMove.startPosition.which.leftRightSide() === otherLowLevelMove.startPosition.which.leftRightSide()
|
||||||
|
&& (heyStepKind === "EndsPassIn" && otherHeyStepKind === "EndsPassOut"
|
||||||
|
|| heyStepKind === "EndsPassOut" && otherHeyStepKind === "EndsPassIn"))) {
|
||||||
|
lowLevelMove.movementPattern.otherPath = {
|
||||||
|
start: { ...otherLowLevelMove.startPosition, setOffset: lowLevelMove.startPosition.setOffset, lineOffset: lowLevelMove.startPosition.lineOffset },
|
||||||
|
end: { ...otherLowLevelMove.endPosition, setOffset: lowLevelMove.endPosition.setOffset, lineOffset: lowLevelMove.endPosition.lineOffset },
|
||||||
|
}
|
||||||
|
otherLowLevelMove.movementPattern.otherPath = {
|
||||||
|
start: { ...lowLevelMove.startPosition, setOffset: otherLowLevelMove.startPosition.setOffset, lineOffset: otherLowLevelMove.startPosition.lineOffset },
|
||||||
|
end: { ...lowLevelMove.endPosition, setOffset: otherLowLevelMove.endPosition.setOffset, lineOffset: otherLowLevelMove.endPosition.lineOffset },
|
||||||
|
}
|
||||||
|
foundPair = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPair && heyStepKind === "EndsPassOut") {
|
||||||
|
// Then other is standing still.
|
||||||
|
const pos = {
|
||||||
|
...([...withoutOtherPath.values()]
|
||||||
|
.map(otherMoves => otherMoves[i])
|
||||||
|
.filter(m => m.movementPattern.kind === SemanticAnimationKind.StandStill
|
||||||
|
&& m.endPosition.which.leftRightSide() === lowLevelMove.endPosition.which.leftRightSide())
|
||||||
|
[0].endPosition),
|
||||||
|
setOffset: lowLevelMove.startPosition.setOffset, lineOffset: lowLevelMove.startPosition.lineOffset
|
||||||
|
}
|
||||||
|
lowLevelMove.movementPattern.otherPath = { start: pos, end: pos };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of withoutOtherPath.keys()) {
|
||||||
|
const lowLevelMove = withoutOtherPath.get(id)![i];
|
||||||
|
if (lowLevelMove.movementPattern.kind === SemanticAnimationKind.PassBy
|
||||||
|
&& !lowLevelMove.movementPattern.otherPath) {
|
||||||
|
throw new Error("Failed to fill in otherPath for " + id + " on hey step " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object was mutated.
|
||||||
|
return withoutOtherPath;
|
||||||
|
}
|
||||||
|
|
||||||
class HeySingleVariant extends SingleVariantMoveInterpreter<Hey, typeof moveName> {
|
class HeySingleVariant extends SingleVariantMoveInterpreter<Hey, typeof moveName> {
|
||||||
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
heyStepToPartialLowLevelMove(heyStep: HeyStep): PartialLowLevelMove & { heyStep: HeyStep } {
|
||||||
// Needed for inner functions... that probably should be methods.
|
return {
|
||||||
const move = this.move;
|
beats: this.moveInterpreter.heyPartBeats,
|
||||||
|
// TODO use circle positions on ends? ... unless hey ends in a box the gnat or similar...
|
||||||
|
endPosition: heyStep.endPosition,
|
||||||
|
movementPattern: heyStep.kind === "StandStill" ? {
|
||||||
|
kind: SemanticAnimationKind.StandStill,
|
||||||
|
} : heyStep.kind === "Loop" ? {
|
||||||
|
// TODO Loop should probably be its own kind? Or RotateAround?
|
||||||
|
kind: SemanticAnimationKind.Linear,
|
||||||
|
minRotation: this.moveInterpreter.endsShoulder === Hand.Right ? +180 : -180,
|
||||||
|
} : heyStep.kind === "Ricochet" ? {
|
||||||
|
// TODO This is a hack.
|
||||||
|
kind: SemanticAnimationKind.PassBy,
|
||||||
|
around: heyStep.endPosition.which.leftRightSide(),
|
||||||
|
withHands: false,
|
||||||
|
otherPath: "Swap",
|
||||||
|
facing: "Start",
|
||||||
|
side: this.moveInterpreter.endsShoulder,
|
||||||
|
} : {
|
||||||
|
kind: SemanticAnimationKind.PassBy,
|
||||||
|
around: heyStep.kind === "CenterPass" ? "Center" : heyStep.endPosition.which.leftRightSide(),
|
||||||
|
withHands: false,
|
||||||
|
side: heyStep.kind === "CenterPass" ? this.moveInterpreter.centerShoulder : this.moveInterpreter.endsShoulder,
|
||||||
|
facing: "Start",
|
||||||
|
otherPath: undefined!, // Placeholder, fixup later.
|
||||||
|
},
|
||||||
|
heyStep,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
type HeyStep = {
|
continueHey(prevStep: HeyStep, stepsLeft: number,
|
||||||
kind: "StandStill" | "Loop" | "CenterPass" | "EndsPassIn" | "EndsPassOut" | "Ricochet",
|
{ beenInCenter, endsInCircle, inCenterFirst }:
|
||||||
endPosition: SemanticPosition,
|
{ beenInCenter: boolean, endsInCircle: boolean, inCenterFirst: boolean }): HeyStep {
|
||||||
}
|
// TODO Not sure why type checker requires rechecking this here.
|
||||||
|
if (this.move.move !== "hey") throw new Error("Unreachable.");
|
||||||
|
|
||||||
if (this.move.parameters.dir !== "across") {
|
// Continuing hey so everyone is either passing (in center or on ends) or looping on ends.
|
||||||
throw new Error("Unsupported hey direction: " + this.move.parameters.dir);
|
if (prevStep.endPosition.kind === PositionKind.Circle) {
|
||||||
}
|
if (prevStep.endPosition.facing === prevStep.endPosition.which.facingAcross()) {
|
||||||
|
if (stepsLeft === 0) {
|
||||||
let heyParts: number;
|
return {
|
||||||
switch (this.move.parameters.until) {
|
kind: "StandStill",
|
||||||
case "half":
|
endPosition: prevStep.endPosition,
|
||||||
heyParts = 4;
|
|
||||||
break;
|
|
||||||
case "full":
|
|
||||||
heyParts = 8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unsupported hey 'until': " + this.move.parameters.until);
|
|
||||||
}
|
|
||||||
const heyPartBeats: number = this.move.beats / heyParts;
|
|
||||||
// TODO is this right?
|
|
||||||
const firstPassInCenter: boolean = dancerIsPair(this.move.parameters.who);
|
|
||||||
const centerShoulder = firstPassInCenter === this.move.parameters.shoulder ? Hand.Right : Hand.Left;
|
|
||||||
const endsShoulder = centerShoulder.opposite();
|
|
||||||
|
|
||||||
function fixupHeyOtherPath(withoutOtherPath: Map<DancerIdentity, (LowLevelMove & { heyStep?: HeyStep })[]>): Map<DancerIdentity, LowLevelMove[]> {
|
|
||||||
const numSteps = withoutOtherPath.get(DancerIdentity.OnesLark)!.length;
|
|
||||||
for (let i = 0; i < numSteps; i++) {
|
|
||||||
for (const id of withoutOtherPath.keys()) {
|
|
||||||
const lowLevelMove = withoutOtherPath.get(id)![i];
|
|
||||||
if (lowLevelMove.movementPattern.kind !== SemanticAnimationKind.PassBy
|
|
||||||
|| !lowLevelMove.heyStep
|
|
||||||
|| lowLevelMove.movementPattern.otherPath) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const heyStepKind = lowLevelMove.heyStep.kind;
|
|
||||||
let foundPair = false;
|
|
||||||
for (const otherId of withoutOtherPath.keys()) {
|
|
||||||
const otherLowLevelMove = withoutOtherPath.get(otherId)![i];
|
|
||||||
if (id === otherId
|
|
||||||
|| otherLowLevelMove.movementPattern.kind !== SemanticAnimationKind.PassBy
|
|
||||||
|| !otherLowLevelMove.heyStep
|
|
||||||
|| otherLowLevelMove.movementPattern.otherPath) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const otherHeyStepKind = otherLowLevelMove.heyStep.kind;
|
|
||||||
|
|
||||||
if (heyStepKind === "CenterPass" && otherHeyStepKind === "CenterPass"
|
|
||||||
|| (lowLevelMove.startPosition.which.leftRightSide() === otherLowLevelMove.startPosition.which.leftRightSide()
|
|
||||||
&& (heyStepKind === "EndsPassIn" && otherHeyStepKind === "EndsPassOut"
|
|
||||||
|| heyStepKind === "EndsPassOut" && otherHeyStepKind === "EndsPassIn"))) {
|
|
||||||
lowLevelMove.movementPattern.otherPath = {
|
|
||||||
start: { ...otherLowLevelMove.startPosition, setOffset: lowLevelMove.startPosition.setOffset, lineOffset: lowLevelMove.startPosition.lineOffset },
|
|
||||||
end: { ...otherLowLevelMove.endPosition, setOffset: lowLevelMove.endPosition.setOffset, lineOffset: lowLevelMove.endPosition.lineOffset },
|
|
||||||
}
|
|
||||||
otherLowLevelMove.movementPattern.otherPath = {
|
|
||||||
start: { ...lowLevelMove.startPosition, setOffset: otherLowLevelMove.startPosition.setOffset, lineOffset: otherLowLevelMove.startPosition.lineOffset },
|
|
||||||
end: { ...lowLevelMove.endPosition, setOffset: otherLowLevelMove.endPosition.setOffset, lineOffset: otherLowLevelMove.endPosition.lineOffset },
|
|
||||||
}
|
|
||||||
foundPair = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundPair && heyStepKind === "EndsPassOut") {
|
|
||||||
// Then other is standing still.
|
|
||||||
const pos = {
|
|
||||||
...([...withoutOtherPath.values()]
|
|
||||||
.map(otherMoves => otherMoves[i])
|
|
||||||
.filter(m => m.movementPattern.kind === SemanticAnimationKind.StandStill
|
|
||||||
&& m.endPosition.which.leftRightSide() === lowLevelMove.endPosition.which.leftRightSide())
|
|
||||||
[0].endPosition),
|
|
||||||
setOffset: lowLevelMove.startPosition.setOffset, lineOffset: lowLevelMove.startPosition.lineOffset
|
|
||||||
}
|
|
||||||
lowLevelMove.movementPattern.otherPath = { start: pos, end: pos };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const id of withoutOtherPath.keys()) {
|
|
||||||
const lowLevelMove = withoutOtherPath.get(id)![i];
|
|
||||||
if (lowLevelMove.movementPattern.kind === SemanticAnimationKind.PassBy
|
|
||||||
&& !lowLevelMove.movementPattern.otherPath) {
|
|
||||||
throw new Error("Failed to fill in otherPath for " + id + " on hey step " + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object was mutated.
|
|
||||||
return withoutOtherPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixupHeyOtherPath(this.handleMove(({ id, startPos }) => {
|
|
||||||
const endsInCircle = startPos.kind === PositionKind.Circle;
|
|
||||||
function heyStepToPartialLowLevelMove(heyStep: HeyStep): PartialLowLevelMove & { heyStep: HeyStep } {
|
|
||||||
return {
|
return {
|
||||||
beats: heyPartBeats,
|
kind: "EndsPassIn",
|
||||||
// TODO use circle positions on ends? ... unless hey ends in a box the gnat or similar...
|
endPosition: {
|
||||||
endPosition: heyStep.endPosition,
|
kind: PositionKind.ShortLines,
|
||||||
movementPattern: heyStep.kind === "StandStill" ? {
|
which: prevStep.endPosition.which.isLeft() ? ShortLinesPosition.MiddleLeft : ShortLinesPosition.MiddleRight,
|
||||||
kind: SemanticAnimationKind.StandStill,
|
facing: prevStep.endPosition.which.facingAcross(),
|
||||||
} : heyStep.kind === "Loop" ? {
|
setOffset: prevStep.endPosition.setOffset,
|
||||||
// TODO Loop should probably be its own kind? Or RotateAround?
|
lineOffset: prevStep.endPosition.lineOffset,
|
||||||
kind: SemanticAnimationKind.Linear,
|
|
||||||
minRotation: endsShoulder === Hand.Right ? +180 : -180,
|
|
||||||
} : heyStep.kind === "Ricochet" ? {
|
|
||||||
// TODO This is a hack.
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
around: heyStep.endPosition.which.leftRightSide(),
|
|
||||||
withHands: false,
|
|
||||||
otherPath: "Swap",
|
|
||||||
facing: "Start",
|
|
||||||
side: endsShoulder,
|
|
||||||
} : {
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
around: heyStep.kind === "CenterPass" ? "Center" : heyStep.endPosition.which.leftRightSide(),
|
|
||||||
withHands: false,
|
|
||||||
side: heyStep.kind === "CenterPass" ? centerShoulder : endsShoulder,
|
|
||||||
facing: "Start",
|
|
||||||
otherPath: undefined!, // Placeholder, fixup later.
|
|
||||||
},
|
},
|
||||||
heyStep,
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
function continueHey(prevStep: HeyStep, stepsLeft: number, beenInCenter: boolean): HeyStep {
|
else {
|
||||||
// TODO Not sure why type checker requires rechecking this here.
|
if (stepsLeft === 1 && !endsInCircle) {
|
||||||
if (move.move !== "hey") throw new Error("Unreachable.");
|
return {
|
||||||
|
kind: "Loop",
|
||||||
// Continuing hey so everyone is either passing (in center or on ends) or looping on ends.
|
endPosition: {
|
||||||
if (prevStep.endPosition.kind === PositionKind.Circle) {
|
kind: PositionKind.ShortLines,
|
||||||
if (prevStep.endPosition.facing === prevStep.endPosition.which.facingAcross()) {
|
which: prevStep.endPosition.which.isLeft() ? ShortLinesPosition.FarLeft : ShortLinesPosition.FarRight,
|
||||||
if (stepsLeft === 0) {
|
facing: prevStep.endPosition.which.facingAcross(),
|
||||||
return {
|
setOffset: prevStep.endPosition.setOffset,
|
||||||
kind: "StandStill",
|
lineOffset: prevStep.endPosition.lineOffset,
|
||||||
endPosition: prevStep.endPosition,
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
kind: "EndsPassIn",
|
|
||||||
endPosition: {
|
|
||||||
kind: PositionKind.ShortLines,
|
|
||||||
which: prevStep.endPosition.which.isLeft() ? ShortLinesPosition.MiddleLeft : ShortLinesPosition.MiddleRight,
|
|
||||||
facing: prevStep.endPosition.which.facingAcross(),
|
|
||||||
setOffset: prevStep.endPosition.setOffset,
|
|
||||||
lineOffset: prevStep.endPosition.lineOffset,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (stepsLeft === 1 && !endsInCircle) {
|
|
||||||
return {
|
|
||||||
kind: "Loop",
|
|
||||||
endPosition: {
|
|
||||||
kind: PositionKind.ShortLines,
|
|
||||||
which: prevStep.endPosition.which.isLeft() ? ShortLinesPosition.FarLeft : ShortLinesPosition.FarRight,
|
|
||||||
facing: prevStep.endPosition.which.facingAcross(),
|
|
||||||
setOffset: prevStep.endPosition.setOffset,
|
|
||||||
lineOffset: prevStep.endPosition.lineOffset,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
kind: "Loop",
|
|
||||||
endPosition: {
|
|
||||||
...prevStep.endPosition,
|
|
||||||
which: prevStep.endPosition.which.swapUpAndDown(),
|
|
||||||
facing: prevStep.endPosition.which.facingAcross()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (prevStep.endPosition.kind === PositionKind.ShortLines) {
|
return {
|
||||||
const isFacingSide = prevStep.endPosition.facing === prevStep.endPosition.which.facingSide();
|
kind: "Loop",
|
||||||
const inMiddle = prevStep.endPosition.which.isMiddle();
|
endPosition: {
|
||||||
if (!inMiddle && !isFacingSide) {
|
...prevStep.endPosition,
|
||||||
return {
|
which: prevStep.endPosition.which.swapUpAndDown(),
|
||||||
kind: "Loop",
|
facing: prevStep.endPosition.which.facingAcross()
|
||||||
endPosition: { ...prevStep.endPosition, facing: prevStep.endPosition.which.facingSide() },
|
},
|
||||||
}
|
}
|
||||||
} else if (inMiddle && isFacingSide) {
|
}
|
||||||
return {
|
}
|
||||||
kind: "EndsPassOut",
|
else if (prevStep.endPosition.kind === PositionKind.ShortLines) {
|
||||||
endPosition: {
|
const isFacingSide = prevStep.endPosition.facing === prevStep.endPosition.which.facingSide();
|
||||||
...prevStep.endPosition,
|
const inMiddle = prevStep.endPosition.which.isMiddle();
|
||||||
kind: PositionKind.Circle,
|
if (!inMiddle && !isFacingSide) {
|
||||||
which: prevStep.endPosition.which.isLeft()
|
return {
|
||||||
? (endsShoulder === Hand.Right ? CirclePosition.TopLeft : CirclePosition.BottomLeft)
|
kind: "Loop",
|
||||||
: (endsShoulder === Hand.Right ? CirclePosition.BottomRight : CirclePosition.TopRight),
|
endPosition: { ...prevStep.endPosition, facing: prevStep.endPosition.which.facingSide() },
|
||||||
},
|
}
|
||||||
}
|
} else if (inMiddle && isFacingSide) {
|
||||||
}
|
return {
|
||||||
else if (!isFacingSide) {
|
kind: "EndsPassOut",
|
||||||
const rico = inCenterFirst
|
endPosition: {
|
||||||
? beenInCenter
|
...prevStep.endPosition,
|
||||||
? move.parameters.rico3
|
kind: PositionKind.Circle,
|
||||||
: move.parameters.rico1
|
which: prevStep.endPosition.which.isLeft()
|
||||||
: beenInCenter
|
? (this.moveInterpreter.endsShoulder === Hand.Right ? CirclePosition.TopLeft : CirclePosition.BottomLeft)
|
||||||
? move.parameters.rico4
|
: (this.moveInterpreter.endsShoulder === Hand.Right ? CirclePosition.BottomRight : CirclePosition.TopRight),
|
||||||
: move.parameters.rico2;
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isFacingSide) {
|
||||||
|
const rico = inCenterFirst
|
||||||
|
? beenInCenter
|
||||||
|
? this.move.parameters.rico3
|
||||||
|
: this.move.parameters.rico1
|
||||||
|
: beenInCenter
|
||||||
|
? this.move.parameters.rico4
|
||||||
|
: this.move.parameters.rico2;
|
||||||
|
|
||||||
if (rico) {
|
if (rico) {
|
||||||
const onLeftSide = prevStep.endPosition.which.isLeft();
|
const onLeftSide = prevStep.endPosition.which.isLeft();
|
||||||
return {
|
return {
|
||||||
kind: "Ricochet",
|
kind: "Ricochet",
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...prevStep.endPosition,
|
...prevStep.endPosition,
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
which: CirclePosition.fromSides(prevStep.endPosition.which.leftRightSide(),
|
which: CirclePosition.fromSides(prevStep.endPosition.which.leftRightSide(),
|
||||||
// TODO might be swapped
|
// TODO might be swapped
|
||||||
(endsShoulder === Hand.Left) === onLeftSide ? CircleSide.Top : CircleSide.Bottom),
|
(this.moveInterpreter.endsShoulder === Hand.Left) === onLeftSide ? CircleSide.Top : CircleSide.Bottom),
|
||||||
facing: onLeftSide ? Facing.Left : Facing.Right,
|
facing: onLeftSide ? Facing.Left : Facing.Right,
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
kind: "CenterPass",
|
|
||||||
endPosition: {
|
|
||||||
...prevStep.endPosition,
|
|
||||||
which: prevStep.endPosition.which.swapSides()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {
|
|
||||||
kind: inMiddle ? "EndsPassOut" : "EndsPassIn",
|
|
||||||
endPosition: {
|
|
||||||
...prevStep.endPosition,
|
|
||||||
which: prevStep.endPosition.which.swapOnSide()
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unexpected PositionKind: " + (<any>prevStep.endPosition).kind);
|
return {
|
||||||
|
kind: "CenterPass",
|
||||||
|
endPosition: {
|
||||||
|
...prevStep.endPosition,
|
||||||
|
which: prevStep.endPosition.which.swapSides()
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
kind: inMiddle ? "EndsPassOut" : "EndsPassIn",
|
||||||
|
endPosition: {
|
||||||
|
...prevStep.endPosition,
|
||||||
|
which: prevStep.endPosition.which.swapOnSide()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Unexpected PositionKind: " + (<any>prevStep.endPosition).kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const inCenterFirst = firstPassInCenter && this.findPairOpposite(this.move.parameters.who, id) !== null
|
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
||||||
|| this.move.parameters.who2 && this.findPairOpposite(this.move.parameters.who2, id) !== null;
|
return fixupHeyOtherPath(this.handleMove(({ id, startPos }) => {
|
||||||
|
const endsInCircle = startPos.kind === PositionKind.Circle;
|
||||||
|
|
||||||
|
const inCenterFirst = this.moveInterpreter.firstPassInCenter && (this.findPairOpposite(this.move.parameters.who, id) !== null)
|
||||||
|
|| !!this.move.parameters.who2 && (this.findPairOpposite(this.move.parameters.who2, id) !== null);
|
||||||
|
|
||||||
let firstHeyStep: HeyStep;
|
let firstHeyStep: HeyStep;
|
||||||
let startingPos: SemanticPosition;
|
let startingPos: SemanticPosition;
|
||||||
if (firstPassInCenter) {
|
if (this.moveInterpreter.firstPassInCenter) {
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
throw new Error("Hey starting in center not from circle is unsupported.");
|
throw new Error("Hey starting in center not from circle is unsupported.");
|
||||||
}
|
}
|
||||||
|
@ -319,18 +298,60 @@ class HeySingleVariant extends SingleVariantMoveInterpreter<Hey, typeof moveName
|
||||||
|
|
||||||
const heySteps: HeyStep[] = [firstHeyStep];
|
const heySteps: HeyStep[] = [firstHeyStep];
|
||||||
let beenInCenter = firstHeyStep.kind === "CenterPass" || firstHeyStep.kind === "Ricochet";
|
let beenInCenter = firstHeyStep.kind === "CenterPass" || firstHeyStep.kind === "Ricochet";
|
||||||
for (let i = 1; i < heyParts; i++) {
|
for (let i = 1; i < this.moveInterpreter.heyParts; i++) {
|
||||||
const isLast = i === heyParts - 1;
|
const nextHeyStep = this.continueHey(heySteps[i - 1], this.moveInterpreter.heyParts - i - 1, {beenInCenter, endsInCircle, inCenterFirst});
|
||||||
const nextHeyStep = continueHey(heySteps[i - 1], heyParts - i - 1, beenInCenter);
|
|
||||||
beenInCenter ||= nextHeyStep.kind === "CenterPass" || nextHeyStep.kind === "Ricochet";
|
beenInCenter ||= nextHeyStep.kind === "CenterPass" || nextHeyStep.kind === "Ricochet";
|
||||||
heySteps.push(nextHeyStep);
|
heySteps.push(nextHeyStep);
|
||||||
}
|
}
|
||||||
return this.combine(heySteps.map(heyStepToPartialLowLevelMove), { ...startingPos, hands: undefined });
|
return this.combine(heySteps.map(s => this.heyStepToPartialLowLevelMove(s)), { ...startingPos, hands: undefined });
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Hey extends MoveInterpreter<typeof moveName> {
|
class Hey extends MoveInterpreter<typeof moveName> {
|
||||||
|
public readonly heyParts: number;
|
||||||
|
public readonly heyPartBeats: number;
|
||||||
|
public readonly firstPassInCenter: boolean;
|
||||||
|
public readonly centerShoulder: Hand;
|
||||||
|
public readonly endsShoulder: Hand;
|
||||||
|
|
||||||
|
constructor(args: MoveInterpreterCtorArgs<typeof moveName>) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
if (this.move.parameters.dir !== "across") {
|
||||||
|
throw new Error("Unsupported hey direction: " + this.move.parameters.dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.move.parameters.until === "string") {
|
||||||
|
switch (this.move.parameters.until) {
|
||||||
|
case "half":
|
||||||
|
this.heyParts = 4;
|
||||||
|
break;
|
||||||
|
case "full":
|
||||||
|
this.heyParts = 8;
|
||||||
|
break;
|
||||||
|
// TODO Are these right? Can it sometimes be 1 or 3 instead of 2?
|
||||||
|
case "less than half":
|
||||||
|
this.heyParts = 2;
|
||||||
|
break;
|
||||||
|
case "between half and full":
|
||||||
|
this.heyParts = 6;
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupported hey 'until': " + this.move.parameters.until);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO Is this actually this simple?
|
||||||
|
this.heyParts = this.move.parameters.until.time === 1 ? 2 : 6;
|
||||||
|
//throw new Error("Unsupported hey 'until': " + this.move.parameters.until.dancer + " time " + this.move.parameters.until.time);
|
||||||
|
}
|
||||||
|
this.heyPartBeats = this.move.beats / this.heyParts;
|
||||||
|
// TODO is this right?
|
||||||
|
this.firstPassInCenter = dancerIsPair(this.move.parameters.who);
|
||||||
|
this.centerShoulder = this.firstPassInCenter === this.move.parameters.shoulder ? Hand.Right : Hand.Left;
|
||||||
|
this.endsShoulder = this.centerShoulder.opposite();
|
||||||
|
}
|
||||||
|
|
||||||
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
||||||
return new HeySingleVariant(this, startingPos);
|
return new HeySingleVariant(this, startingPos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,71 @@
|
||||||
import { HandConnection, HandTo, BalanceWeight } from "../interpreterCommon.js";
|
import { HandConnection, HandTo, BalanceWeight, PositionKind, ShortLinesPosition, SemanticPosition } from "../interpreterCommon.js";
|
||||||
import { Move } from "../libfigureMapper.js";
|
|
||||||
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
||||||
import { Hand } from "../rendererConstants.js";
|
import { Hand } from "../rendererConstants.js";
|
||||||
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, MoveInterpreterCtorArgs, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, Variant, VariantCollection, moveInterpreters } from "./_moveInterpreter.js";
|
||||||
|
|
||||||
const moveName: Move["move"] = "pull by dancers";
|
const moveName = "pull by dancers";
|
||||||
|
|
||||||
class PullByDancersSingleVariant extends SingleVariantMoveInterpreter<PullByDancers, typeof moveName> {
|
class PullByDancersSingleVariant extends SingleVariantMoveInterpreter<PullByDancers, typeof moveName> {
|
||||||
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
moveAsVariants(previousMoveVariant: string): VariantCollection {
|
||||||
|
const res = new Map<string, Variant>();
|
||||||
|
|
||||||
|
for (const toShortLines of [true, false]) {
|
||||||
|
try {
|
||||||
|
res.set(toShortLines ? "ToShortLines" : "FullPullBy", {
|
||||||
|
lowLevelMoves: this.moveAsLowLevelMovesTo(toShortLines),
|
||||||
|
previousMoveVariant
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
moveAsLowLevelMovesTo(toShortLines: boolean): LowLevelMovesForAllDancers {
|
||||||
// TODO Might make sense to think of pull by as not a full swap?
|
// TODO Might make sense to think of pull by as not a full swap?
|
||||||
// e.g., in Blue and Green Candles, it's treated as only getting to
|
// e.g., in Blue and Green Candles, it's treated as only getting to
|
||||||
// ShortLinesPosition.Middle* before doing an allemande.
|
// ShortLinesPosition.Middle* before doing an allemande.
|
||||||
return this.handlePairedMove(this.move.parameters.who, ({ startPos, around, withPos }) => {
|
return this.handlePairedMove(this.move.parameters.who, ({ startPos, around, withPos }) => {
|
||||||
const hand = this.move.parameters.hand ? Hand.Right : Hand.Left;
|
if (toShortLines) {
|
||||||
const balanceBeats = this.move.parameters.bal
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
? this.move.beats > 4
|
throw new Error("ToShortLines variant of " + this.move.move + " only makes sense starting from a circle.");
|
||||||
? this.move.beats - 4
|
}
|
||||||
: 2
|
}
|
||||||
: 0;
|
|
||||||
const balancePartBeats = balanceBeats / 2;
|
|
||||||
const pullBeats = this.move.beats - balanceBeats;
|
|
||||||
|
|
||||||
// TODO Adjust facing?
|
// TODO Adjust facing?
|
||||||
const startPosition = {
|
const startPosition = {
|
||||||
...startPos,
|
...startPos,
|
||||||
hands: new Map<Hand, HandConnection>([
|
hands: new Map<Hand, HandConnection>([
|
||||||
[
|
[
|
||||||
hand,
|
this.moveInterpreter.hand,
|
||||||
{ hand, to: around === "Center" ? HandTo.DiagonalAcrossCircle : HandTo.DancerForward }
|
{ hand: this.moveInterpreter.hand, to: around === "Center" ? HandTo.DiagonalAcrossCircle : HandTo.DancerForward }
|
||||||
]])
|
]]),
|
||||||
|
facing: startPos.which.facingAcross(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const endPos: SemanticPosition = toShortLines
|
||||||
|
? {
|
||||||
|
...withPos,
|
||||||
|
hands: undefined,
|
||||||
|
facing: startPosition.facing,
|
||||||
|
kind: PositionKind.ShortLines,
|
||||||
|
which: ShortLinesPosition.fromSide(withPos.which.leftRightSide(), "Middle"),
|
||||||
|
longLines: undefined, // Needed to satisfy type-checker.
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...withPos,
|
||||||
|
hands: undefined,
|
||||||
|
facing: startPosition.facing,
|
||||||
|
};
|
||||||
const passBy: PartialLowLevelMove = {
|
const passBy: PartialLowLevelMove = {
|
||||||
beats: pullBeats,
|
beats: this.moveInterpreter.pullBeats,
|
||||||
endPosition: { ...withPos, facing: startPos.facing },
|
endPosition: endPos,
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.PassBy,
|
kind: SemanticAnimationKind.PassBy,
|
||||||
around,
|
around,
|
||||||
side: hand,
|
side: this.moveInterpreter.hand,
|
||||||
withHands: true,
|
withHands: true,
|
||||||
facing: "Start",
|
facing: "Start",
|
||||||
otherPath: "Swap",
|
otherPath: "Swap",
|
||||||
|
@ -47,7 +75,7 @@ class PullByDancersSingleVariant extends SingleVariantMoveInterpreter<PullByDanc
|
||||||
if (this.move.parameters.bal) {
|
if (this.move.parameters.bal) {
|
||||||
return this.combine([
|
return this.combine([
|
||||||
{
|
{
|
||||||
beats: balancePartBeats,
|
beats: this.moveInterpreter.balancePartBeats,
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...startPosition,
|
...startPosition,
|
||||||
balance: BalanceWeight.Forward,
|
balance: BalanceWeight.Forward,
|
||||||
|
@ -57,7 +85,7 @@ class PullByDancersSingleVariant extends SingleVariantMoveInterpreter<PullByDanc
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
beats: balancePartBeats,
|
beats: this.moveInterpreter.balancePartBeats,
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...startPosition,
|
...startPosition,
|
||||||
balance: BalanceWeight.Backward,
|
balance: BalanceWeight.Backward,
|
||||||
|
@ -70,11 +98,45 @@ class PullByDancersSingleVariant extends SingleVariantMoveInterpreter<PullByDanc
|
||||||
} else {
|
} else {
|
||||||
return this.combine([passBy], startPosition);
|
return this.combine([passBy], startPosition);
|
||||||
}
|
}
|
||||||
|
}, !toShortLines ? undefined : ({startPos}) => {
|
||||||
|
return this.combine([{
|
||||||
|
beats: this.move.beats,
|
||||||
|
movementPattern: {
|
||||||
|
kind: SemanticAnimationKind.Linear,
|
||||||
|
},
|
||||||
|
endPosition: {
|
||||||
|
...startPos,
|
||||||
|
kind: PositionKind.ShortLines,
|
||||||
|
which: ShortLinesPosition.fromSide(startPos.which.leftRightSide(), "Far"),
|
||||||
|
facing: startPos.which.facingAcross(),
|
||||||
|
hands: undefined,
|
||||||
|
longLines: undefined,
|
||||||
|
}
|
||||||
|
}], startPos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PullByDancers extends MoveInterpreter<typeof moveName> {
|
class PullByDancers extends MoveInterpreter<typeof moveName> {
|
||||||
|
public readonly hand: Hand;
|
||||||
|
public readonly balancePartBeats: number;
|
||||||
|
public readonly pullBeats: number;
|
||||||
|
|
||||||
|
constructor(args: MoveInterpreterCtorArgs<typeof moveName>) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
this.hand = this.move.parameters.hand ? Hand.Right : Hand.Left;
|
||||||
|
const balanceBeats = this.move.parameters.bal
|
||||||
|
? this.move.beats > 4
|
||||||
|
? this.move.beats - 4
|
||||||
|
: this.move.beats > 2
|
||||||
|
? 2
|
||||||
|
: this.move.beats / 2
|
||||||
|
: 0;
|
||||||
|
this.balancePartBeats = balanceBeats / 2;
|
||||||
|
this.pullBeats = this.move.beats - balanceBeats;
|
||||||
|
}
|
||||||
|
|
||||||
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
||||||
return new PullByDancersSingleVariant(this, startingPos);
|
return new PullByDancersSingleVariant(this, startingPos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,14 @@
|
||||||
import { PositionKind, SemanticPosition, Facing, BalanceWeight, handsInLine } from "../interpreterCommon.js";
|
import { PositionKind, SemanticPosition, Facing, BalanceWeight, handsInLine, CircleSide, handsInLongLines } from "../interpreterCommon.js";
|
||||||
import { Move } from "../libfigureMapper.js";
|
|
||||||
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
import { SemanticAnimationKind } from "../lowLevelMove.js";
|
||||||
import { Hand } from "../rendererConstants.js";
|
import { Hand } from "../rendererConstants.js";
|
||||||
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
import { ISingleVariantMoveInterpreter, LowLevelMovesForAllDancers, MoveInterpreter, MoveInterpreterCtorArgs, PartialLowLevelMove, SemanticPositionsForAllDancers, SingleVariantMoveInterpreter, moveInterpreters } from "./_moveInterpreter.js";
|
||||||
|
|
||||||
const moveName: Move["move"] = "Rory O'More";
|
const moveName = "Rory O'More";
|
||||||
class RoryOMoreSingleVariant extends SingleVariantMoveInterpreter<RoryOMore, typeof moveName> {
|
class RoryOMoreSingleVariant extends SingleVariantMoveInterpreter<RoryOMore, typeof moveName> {
|
||||||
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
moveAsLowLevelMoves(): LowLevelMovesForAllDancers {
|
||||||
if (this.move.parameters.who !== "everyone") {
|
|
||||||
throw new Error(this.move.move + " that doesn't include everyone is unsupported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Could be in long or short lines.
|
// TODO Could be in long or short lines.
|
||||||
const roryDir = this.move.parameters.slide ? Hand.Left : Hand.Right;
|
|
||||||
const balBeats = this.move.parameters.bal ? this.move.beats / 2 : 0;
|
|
||||||
const balPartBeats = balBeats / 2;
|
|
||||||
const roryBeats = this.move.beats - balBeats;
|
|
||||||
return this.handleMove(({ startPos }) => {
|
return this.handleMove(({ startPos }) => {
|
||||||
const isShortLines: boolean = startPos.kind === PositionKind.ShortLines;
|
|
||||||
|
|
||||||
const startingPos: SemanticPosition = {
|
const startingPos: SemanticPosition = {
|
||||||
...startPos,
|
...startPos,
|
||||||
hands: handsInLine({ wavy: true, which: startPos.which, facing: startPos.facing })
|
hands: handsInLine({ wavy: true, which: startPos.which, facing: startPos.facing })
|
||||||
|
@ -27,38 +17,61 @@ class RoryOMoreSingleVariant extends SingleVariantMoveInterpreter<RoryOMore, typ
|
||||||
let endPos: SemanticPosition;
|
let endPos: SemanticPosition;
|
||||||
if (startPos.kind === PositionKind.ShortLines) {
|
if (startPos.kind === PositionKind.ShortLines) {
|
||||||
if (startPos.facing !== Facing.Up && startPos.facing !== Facing.Down) {
|
if (startPos.facing !== Facing.Up && startPos.facing !== Facing.Down) {
|
||||||
throw new Error("To be in wavy lines, must be facing up or down, not " + startPos.facing);
|
throw new Error("To be in short wavy lines, must be facing up or down, not " + startPos.facing);
|
||||||
|
}
|
||||||
|
const { newPos: endWhich, wrap } = startPos.which.shiftWithWrap(this.moveInterpreter.dir, startPos.facing);
|
||||||
|
const endLineOffset = !wrap
|
||||||
|
? startPos.lineOffset
|
||||||
|
: (startPos.lineOffset ?? 0) + wrap;
|
||||||
|
if (wrap) {
|
||||||
|
startingPos.hands = handsInLongLines(true)
|
||||||
}
|
}
|
||||||
const endWhich = startPos.which.shift(roryDir, startPos.facing);
|
|
||||||
endPos = {
|
endPos = {
|
||||||
...startPos,
|
...startPos,
|
||||||
which: endWhich,
|
which: endWhich,
|
||||||
hands: handsInLine({ wavy: true, which: endWhich, facing: startPos.facing })
|
hands: handsInLine({ wavy: true, which: endWhich, facing: startPos.facing }),
|
||||||
|
lineOffset: endLineOffset,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(this.move.move + " is currently only supported in short lines.");
|
if (startPos.facing !== Facing.Left && startPos.facing !== Facing.Right) {
|
||||||
|
throw new Error("To be in long wavy lines, must be facing left or right, not " + startPos.facing);
|
||||||
|
}
|
||||||
|
const endWhich = startPos.which.swapUpAndDown();
|
||||||
|
const sideTowards = (startPos.facing === Facing.Right)
|
||||||
|
=== (this.moveInterpreter.dir === Hand.Left)
|
||||||
|
? CircleSide.Bottom
|
||||||
|
: CircleSide.Top;
|
||||||
|
const endSetOffset = sideTowards === endWhich.topBottomSide()
|
||||||
|
? startPos.setOffset
|
||||||
|
: (startPos.setOffset ?? 0) + (sideTowards === CircleSide.Bottom ? +1 : -1);
|
||||||
|
endPos = {
|
||||||
|
...startPos,
|
||||||
|
which: endWhich,
|
||||||
|
hands: handsInLine({ wavy: true, which: endWhich, facing: startPos.facing }),
|
||||||
|
setOffset: endSetOffset,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const maybeBalance: PartialLowLevelMove[] = (this.move.parameters.bal ? [
|
const maybeBalance: PartialLowLevelMove[] = (this.move.parameters.bal ? [
|
||||||
{
|
{
|
||||||
beats: balPartBeats,
|
beats: this.moveInterpreter.balPartBeats,
|
||||||
endPosition: { ...startingPos, balance: roryDir === Hand.Left ? BalanceWeight.Left : BalanceWeight.Right },
|
endPosition: { ...startingPos, balance: this.moveInterpreter.dir === Hand.Left ? BalanceWeight.Left : BalanceWeight.Right },
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
movementPattern: { kind: SemanticAnimationKind.Linear },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
beats: balPartBeats,
|
beats: this.moveInterpreter.balPartBeats,
|
||||||
endPosition: { ...startingPos, balance: roryDir === Hand.Left ? BalanceWeight.Right : BalanceWeight.Left },
|
endPosition: { ...startingPos, balance: this.moveInterpreter.dir === Hand.Left ? BalanceWeight.Right : BalanceWeight.Left },
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
movementPattern: { kind: SemanticAnimationKind.Linear },
|
||||||
},
|
},
|
||||||
] : []);
|
] : []);
|
||||||
|
|
||||||
return this.combine([...maybeBalance,
|
return this.combine([...maybeBalance,
|
||||||
{
|
{
|
||||||
beats: roryBeats,
|
beats: this.moveInterpreter.roryBeats,
|
||||||
endPosition: endPos,
|
endPosition: endPos,
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.Linear,
|
kind: SemanticAnimationKind.Linear,
|
||||||
minRotation: roryDir === Hand.Right ? +360 : -360,
|
minRotation: this.moveInterpreter.dir === Hand.Right ? +360 : -360,
|
||||||
handsDuring: "None",
|
handsDuring: "None",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -68,6 +81,23 @@ class RoryOMoreSingleVariant extends SingleVariantMoveInterpreter<RoryOMore, typ
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoryOMore extends MoveInterpreter<typeof moveName> {
|
class RoryOMore extends MoveInterpreter<typeof moveName> {
|
||||||
|
public readonly dir: Hand;
|
||||||
|
public readonly balPartBeats: number;
|
||||||
|
public readonly roryBeats: number;
|
||||||
|
|
||||||
|
constructor(args: MoveInterpreterCtorArgs<typeof moveName>) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
if (this.move.parameters.who !== "everyone") {
|
||||||
|
throw new Error(this.move.move + " that doesn't include everyone is unsupported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dir = this.move.parameters.slide ? Hand.Left : Hand.Right;
|
||||||
|
const balBeats = this.move.parameters.bal ? this.move.beats / 2 : 0;
|
||||||
|
this.balPartBeats = balBeats / 2;
|
||||||
|
this.roryBeats = this.move.beats - balBeats;
|
||||||
|
}
|
||||||
|
|
||||||
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
buildSingleVariantMoveInterpreter(startingPos: SemanticPositionsForAllDancers): ISingleVariantMoveInterpreter {
|
||||||
return new RoryOMoreSingleVariant(this, startingPos);
|
return new RoryOMoreSingleVariant(this, startingPos);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user