Compare commits
No commits in common. "1b3411b21ad03bf5bd5efbfddb1eee2eeef09242" and "f96165963fe4adb86849171a3d1c61705613f353" have entirely different histories.
1b3411b21a
...
f96165963f
|
@ -4,7 +4,7 @@ import * as common from "./danceCommon.js";
|
||||||
import { Hand, setDistance, setHeight } from "./rendererConstants.js";
|
import { Hand, setDistance, setHeight } from "./rendererConstants.js";
|
||||||
import { nameLibFigureParameters, Move, LibFigureDance, chooser_pairz } from "./libfigureMapper.js";
|
import { nameLibFigureParameters, Move, LibFigureDance, chooser_pairz } from "./libfigureMapper.js";
|
||||||
import { LowLevelMove, SemanticAnimation, SemanticAnimationKind, animateFromLowLevelMoves } from "./lowLevelMove.js";
|
import { LowLevelMove, SemanticAnimation, SemanticAnimationKind, animateFromLowLevelMoves } from "./lowLevelMove.js";
|
||||||
import { BalanceWeight, CirclePosition, CircleSide, CircleSideOrCenter, DancerDistance, Facing, HandConnection, HandTo, LongLines, PositionKind, SemanticPosition } from "./interpreterCommon.js";
|
import { BalanceWeight, CirclePosition, CircleSide, DancerDistance, Facing, HandConnection, HandTo, PositionKind, SemanticPosition } from "./interpreterCommon.js";
|
||||||
|
|
||||||
|
|
||||||
const handsInCircle = new Map<Hand, HandConnection>([
|
const handsInCircle = new Map<Hand, HandConnection>([
|
||||||
|
@ -139,29 +139,6 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If true, dancer is not selected by who so they stand still for the move (or possibly do a meanwhile figure).
|
|
||||||
function excludedByWho(who: chooser_pairz, id: DancerIdentity): boolean {
|
|
||||||
switch (who) {
|
|
||||||
case "partners":
|
|
||||||
case "neighbors":
|
|
||||||
case "same roles":
|
|
||||||
case "shadows":
|
|
||||||
case "first corners":
|
|
||||||
case "second corners":
|
|
||||||
return false;
|
|
||||||
case "gentlespoons":
|
|
||||||
return id.danceRole === DanceRole.Robin;
|
|
||||||
case "ladles":
|
|
||||||
return id.danceRole === DanceRole.Lark;
|
|
||||||
case "ones":
|
|
||||||
return id.coupleRole === CoupleRole.Twos;
|
|
||||||
case "twos":
|
|
||||||
return id.coupleRole === CoupleRole.Ones;
|
|
||||||
default:
|
|
||||||
throw "Unsupported who: " + who;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findPairOpposite(who: chooser_pairz, id: DancerIdentity): common.ExtendedDancerIdentity | null {
|
function findPairOpposite(who: chooser_pairz, id: DancerIdentity): common.ExtendedDancerIdentity | null {
|
||||||
switch (who) {
|
switch (who) {
|
||||||
case "partners":
|
case "partners":
|
||||||
|
@ -193,20 +170,16 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPosFor(id: common.ExtendedDancerIdentity) {
|
// TOOD Type for this? Probably SemanticPosition?
|
||||||
const basePos = startingPos.get(id.setIdentity)!;
|
function findCenterBetween(id: DancerIdentity, other: common.ExtendedDancerIdentity) {
|
||||||
return {...basePos,
|
|
||||||
setOffset: (basePos.setOffset ?? 0) + id.relativeSet,
|
|
||||||
lineOffset: (basePos.lineOffset ?? 0) + id.relativeLine,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function findCenterBetween(id: DancerIdentity, other: common.ExtendedDancerIdentity): CircleSideOrCenter {
|
|
||||||
const selfPos = startingPos.get(id)!;
|
const selfPos = startingPos.get(id)!;
|
||||||
selfPos.setOffset ??= 0;
|
selfPos.setOffset ??= 0;
|
||||||
selfPos.lineOffset ??= 0;
|
selfPos.lineOffset ??= 0;
|
||||||
|
const otherBasePos = startingPos.get(other.setIdentity)!;
|
||||||
const otherPos = getPosFor(other);
|
const otherPos : SemanticPosition = {...otherBasePos,
|
||||||
|
setOffset: (otherBasePos.setOffset ?? 0) + other.relativeSet,
|
||||||
|
lineOffset: (otherBasePos.lineOffset ?? 0) + other.relativeLine,
|
||||||
|
};
|
||||||
|
|
||||||
if (selfPos.kind === PositionKind.Circle && otherPos.kind === PositionKind.Circle) {
|
if (selfPos.kind === PositionKind.Circle && otherPos.kind === PositionKind.Circle) {
|
||||||
if (!(selfPos.lineOffset === otherPos.lineOffset && selfPos.setOffset === otherPos.setOffset)) {
|
if (!(selfPos.lineOffset === otherPos.lineOffset && selfPos.setOffset === otherPos.setOffset)) {
|
||||||
|
@ -225,11 +198,6 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
|
|
||||||
const res = new Map<DancerIdentity, LowLevelMove[]>();
|
const res = new Map<DancerIdentity, LowLevelMove[]>();
|
||||||
switch (move.move) {
|
switch (move.move) {
|
||||||
case "balance the ring":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
res.set(id, balanceCircleInAndOut(move, startPos));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
case "petronella":
|
case "petronella":
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
for (const [id, startPos] of startingPos.entries()) {
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
|
@ -300,16 +268,6 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
return res;
|
return res;
|
||||||
case "swing":
|
case "swing":
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
for (const [id, startPos] of startingPos.entries()) {
|
||||||
if (excludedByWho(move.parameters.who, id)) {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: { ...startPos, hands: undefined },
|
|
||||||
endPosition: { ...startPos, hands: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
||||||
}]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO swing can start from other positions.
|
// TODO swing can start from other positions.
|
||||||
// TODO swing end is only in notes / looking at next move.
|
// TODO swing end is only in notes / looking at next move.
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
|
@ -442,18 +400,7 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
case "allemande":
|
case "allemande":
|
||||||
case "gyre":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
for (const [id, startPos] of startingPos.entries()) {
|
||||||
if (excludedByWho(move.parameters.who, id)) {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: { ...startPos, hands: undefined },
|
|
||||||
endPosition: { ...startPos, hands: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
||||||
}]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO allemande can start from other positions.
|
// TODO allemande can start from other positions.
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
||||||
|
@ -462,12 +409,18 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
const withId = findPairOpposite(move.parameters.who, id);
|
const withId = findPairOpposite(move.parameters.who, id);
|
||||||
// findPairOpposite of null means this dancer doesn't participate in this move.
|
// findPairOpposite of null means this dancer doesn't participate in this move.
|
||||||
if (!withId) {
|
if (!withId) {
|
||||||
throw "fairPairOpposite and excludedByWho disagree on who does not act: who=" + move.parameters.who + ", id=" + id;
|
res.set(id, combine([prevEnd => ({
|
||||||
}
|
beats: move.beats,
|
||||||
|
startPosition: { ...prevEnd, hands: undefined },
|
||||||
|
endPosition: { ...prevEnd, hands: undefined },
|
||||||
|
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
||||||
|
})], startPos));
|
||||||
|
continue;
|
||||||
|
};
|
||||||
const around = findCenterBetween(id, withId);
|
const around = findCenterBetween(id, withId);
|
||||||
|
|
||||||
// TODO Not sure if this is right.
|
// TODO Not sure if this is right.
|
||||||
const byHandOrShoulder = (move.move === "allemande" ? move.parameters.hand : move.parameters.shoulder) ? Hand.Right : Hand.Left;
|
const byHand = move.parameters.hand ? Hand.Right : Hand.Left;
|
||||||
const swap = move.parameters.circling % 360 === 180;
|
const swap = move.parameters.circling % 360 === 180;
|
||||||
if (!swap && move.parameters.circling % 360 !== 0) {
|
if (!swap && move.parameters.circling % 360 !== 0) {
|
||||||
throw "Unsupported allemande circle amount: " + move.parameters.circling;
|
throw "Unsupported allemande circle amount: " + move.parameters.circling;
|
||||||
|
@ -485,9 +438,9 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
endPosition,
|
endPosition,
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.RotateAround,
|
||||||
minAmount: byHandOrShoulder === Hand.Right ? move.parameters.circling : -move.parameters.circling,
|
minAmount: byHand === Hand.Right ? move.parameters.circling : -move.parameters.circling,
|
||||||
around,
|
around,
|
||||||
byHand: move.move === "allemande" ? byHandOrShoulder : undefined,
|
byHand,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
], startPos));
|
], startPos));
|
||||||
|
@ -504,11 +457,10 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
}
|
}
|
||||||
|
|
||||||
res.set(id, combine([
|
res.set(id, combine([
|
||||||
{
|
prevEnd => ({
|
||||||
beats: move.beats,
|
beats: move.beats,
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...startPos,
|
...prevEnd,
|
||||||
facing: Facing.CenterOfCircle,
|
|
||||||
hands: handsInCircle,
|
hands: handsInCircle,
|
||||||
which: startPos.which.circleLeft(places),
|
which: startPos.which.circleLeft(places),
|
||||||
},
|
},
|
||||||
|
@ -516,7 +468,7 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
kind: SemanticAnimationKind.Circle,
|
kind: SemanticAnimationKind.Circle,
|
||||||
places,
|
places,
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
], { ...startPos, facing: Facing.CenterOfCircle }));
|
], { ...startPos, facing: Facing.CenterOfCircle }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,24 +476,11 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
|
|
||||||
case "California twirl":
|
case "California twirl":
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
for (const [id, startPos] of startingPos.entries()) {
|
||||||
if (excludedByWho(move.parameters.who, id)) {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: { ...startPos, hands: undefined },
|
|
||||||
endPosition: { ...startPos, hands: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
||||||
}]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
if (startPos.kind !== PositionKind.Circle) {
|
||||||
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO does "who" matter here or is it entirely positional? At least need to know who to omit.
|
|
||||||
|
|
||||||
const onLeft : boolean = startPos.which.isOnLeftLookingUpAndDown();
|
const onLeft : boolean = startPos.which.isOnLeftLookingUpAndDown();
|
||||||
// TODO get rid of this 1 beat set up and make it part of TwirlSwap?
|
|
||||||
res.set(id, combine([
|
res.set(id, combine([
|
||||||
{
|
{
|
||||||
beats: 1,
|
beats: 1,
|
||||||
|
@ -571,380 +510,6 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
}], startPos));
|
}], startPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case "box the gnat":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
if (excludedByWho(move.parameters.who, id)) {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: { ...startPos, hands: undefined },
|
|
||||||
endPosition: { ...startPos, hands: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
||||||
}]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hand = move.parameters.hand ? Hand.Right : Hand.Left;
|
|
||||||
const balanceBeats = move.parameters.bal
|
|
||||||
? move.beats > 4
|
|
||||||
? move.beats - 4
|
|
||||||
: 2
|
|
||||||
: 0;
|
|
||||||
const balancePartBeats = balanceBeats / 2;
|
|
||||||
const twirlBeats = move.beats - balanceBeats;
|
|
||||||
|
|
||||||
// TODO Adjust facing?
|
|
||||||
const startPosition = { ...startPos, hands: new Map<Hand, HandConnection>([[hand, { hand, to: HandTo.DancerForward }]]) };
|
|
||||||
|
|
||||||
const withId = findPairOpposite(move.parameters.who, id);
|
|
||||||
// findPairOpposite of null means this dancer doesn't participate in this move.
|
|
||||||
if (!withId) {
|
|
||||||
throw "fairPairOpposite and excludedByWho disagree on who does not act: who=" + move.parameters.who + ", id=" + id;
|
|
||||||
}
|
|
||||||
const around = findCenterBetween(id, withId);
|
|
||||||
if (around === "Center") {
|
|
||||||
throw "TwirlSwap around center is unsupported.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (move.parameters.bal) {
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: balancePartBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPosition,
|
|
||||||
balance: BalanceWeight.Forward,
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.Linear,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: balancePartBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPosition,
|
|
||||||
balance: BalanceWeight.Backward,
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.Linear,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: twirlBeats,
|
|
||||||
endPosition: getPosFor(withId),
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.TwirlSwap,
|
|
||||||
around,
|
|
||||||
hand,
|
|
||||||
}
|
|
||||||
}], startPosition));
|
|
||||||
} else {
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: twirlBeats,
|
|
||||||
endPosition: getPosFor(withId),
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.TwirlSwap,
|
|
||||||
around,
|
|
||||||
hand,
|
|
||||||
}
|
|
||||||
}], startPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case "pull by dancers":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
if (excludedByWho(move.parameters.who, id)) {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: { ...startPos, hands: undefined },
|
|
||||||
endPosition: { ...startPos, hands: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.StandStill },
|
|
||||||
}]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hand = move.parameters.hand ? Hand.Right : Hand.Left;
|
|
||||||
const balanceBeats = move.parameters.bal
|
|
||||||
? move.beats > 4
|
|
||||||
? move.beats - 4
|
|
||||||
: 2
|
|
||||||
: 0;
|
|
||||||
const balancePartBeats = balanceBeats / 2;
|
|
||||||
const pullBeats = move.beats - balanceBeats;
|
|
||||||
|
|
||||||
const withId = findPairOpposite(move.parameters.who, id);
|
|
||||||
// findPairOpposite of null means this dancer doesn't participate in this move.
|
|
||||||
if (!withId) {
|
|
||||||
throw "fairPairOpposite and excludedByWho disagree on who does not act: who=" + move.parameters.who + ", id=" + id;
|
|
||||||
}
|
|
||||||
const around = findCenterBetween(id, withId);
|
|
||||||
|
|
||||||
// TODO Adjust facing?
|
|
||||||
const startPosition = {
|
|
||||||
...startPos,
|
|
||||||
hands: new Map<Hand, HandConnection>([
|
|
||||||
[
|
|
||||||
hand,
|
|
||||||
{ hand, to: around === "Center" ? HandTo.DiagonalAcrossCircle : HandTo.DancerForward }
|
|
||||||
]])
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (move.parameters.bal) {
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: balancePartBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPosition,
|
|
||||||
balance: BalanceWeight.Forward,
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.Linear,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: balancePartBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPosition,
|
|
||||||
balance: BalanceWeight.Backward,
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.Linear,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: pullBeats,
|
|
||||||
endPosition: getPosFor(withId),
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
around,
|
|
||||||
side: hand,
|
|
||||||
withHands: true,
|
|
||||||
}
|
|
||||||
}], startPosition));
|
|
||||||
} else {
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: pullBeats,
|
|
||||||
endPosition: getPosFor(withId),
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
around,
|
|
||||||
side: hand,
|
|
||||||
withHands: true,
|
|
||||||
}
|
|
||||||
}], startPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case "chain":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
|
||||||
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainRole = move.parameters.who === "gentlespoons" ? DanceRole.Lark : DanceRole.Robin;
|
|
||||||
const pullBeats = move.beats / 2;
|
|
||||||
const turnBeats = move.beats - pullBeats;
|
|
||||||
|
|
||||||
if (id.danceRole === mainRole) {
|
|
||||||
const hand = move.parameters.hand ? Hand.Right : Hand.Left;
|
|
||||||
const endWhich = startPos.which.swapDiagonal();
|
|
||||||
let endSet = startPos.setOffset ?? 0;
|
|
||||||
let to : HandTo;
|
|
||||||
switch (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<Hand, HandConnection>([[hand, { hand, to }]]) };
|
|
||||||
|
|
||||||
const turnTo = hand === Hand.Right ? HandTo.DancerRight : HandTo.DancerLeft;
|
|
||||||
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: pullBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPos,
|
|
||||||
which: endWhich,
|
|
||||||
setOffset: endSet,
|
|
||||||
hands: new Map<Hand, HandConnection>([
|
|
||||||
[Hand.Left, { hand: Hand.Left, to: turnTo }],
|
|
||||||
[Hand.Right, { hand: Hand.Right, to: turnTo }],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
around: "Center",
|
|
||||||
side: hand,
|
|
||||||
withHands: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
prevEnd => ({
|
|
||||||
beats: turnBeats,
|
|
||||||
endPosition: {
|
|
||||||
kind: PositionKind.Circle,
|
|
||||||
which: endWhich.swapUpAndDown(),
|
|
||||||
facing: endWhich.isLeft() ? Facing.Right : Facing.Left,
|
|
||||||
hands: prevEnd.hands,
|
|
||||||
setOffset: prevEnd.setOffset,
|
|
||||||
lineOffset: prevEnd.lineOffset,
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.CourtesyTurn,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
], startPosition));
|
|
||||||
} else {
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: pullBeats,
|
|
||||||
endPosition: startPos,
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.StandStill,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: turnBeats,
|
|
||||||
endPosition: {
|
|
||||||
...startPos,
|
|
||||||
// TODO Does CourtesyTurn always swap?
|
|
||||||
which: startPos.which.swapUpAndDown(),
|
|
||||||
},
|
|
||||||
movementPattern: {
|
|
||||||
kind: SemanticAnimationKind.CourtesyTurn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
], startPos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case "long lines":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
|
||||||
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const startPosition: SemanticPosition = {
|
|
||||||
...startPos,
|
|
||||||
longLines: undefined,
|
|
||||||
// TODO Occassionally dances have long lines facing out. This will get that wrong.
|
|
||||||
facing: startPos.which.isLeft() ? Facing.Right : Facing.Left,
|
|
||||||
hands: new Map<Hand, HandConnection>([
|
|
||||||
[Hand.Left, { hand: Hand.Right, to: HandTo.DancerLeft }],
|
|
||||||
[Hand.Right, { hand: Hand.Left, to: HandTo.DancerRight }],
|
|
||||||
]),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (move.parameters.go) {
|
|
||||||
const forwardBeats = move.beats / 2;
|
|
||||||
const backwardBeats = move.beats - forwardBeats;
|
|
||||||
res.set(id, combine([
|
|
||||||
{
|
|
||||||
beats: forwardBeats,
|
|
||||||
endPosition: { ...startPosition, longLines: LongLines.Forward },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beats: backwardBeats,
|
|
||||||
endPosition: startPosition,
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
|
||||||
},
|
|
||||||
], startPosition));
|
|
||||||
} else {
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
endPosition: { ...startPosition, longLines: LongLines.Forward },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
|
||||||
}], startPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case "roll away":
|
|
||||||
for (const [id, startPos] of startingPos.entries()) {
|
|
||||||
if (startPos.kind !== PositionKind.Circle) {
|
|
||||||
throw move.move + " must start in a circle, but " + id + " is at " + startPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isRoller: boolean;
|
|
||||||
switch (move.parameters.who) {
|
|
||||||
case "gentlespoons":
|
|
||||||
isRoller = id.danceRole === DanceRole.Lark;
|
|
||||||
break;
|
|
||||||
case "ladles":
|
|
||||||
isRoller = id.danceRole === DanceRole.Robin;
|
|
||||||
break;
|
|
||||||
case "ones":
|
|
||||||
isRoller = id.coupleRole === CoupleRole.Ones;
|
|
||||||
break;
|
|
||||||
case "twos":
|
|
||||||
isRoller = id.coupleRole === CoupleRole.Twos;
|
|
||||||
break;
|
|
||||||
case "first corners":
|
|
||||||
case "second corners":
|
|
||||||
throw "Roll away in contra corners is unsupported.";
|
|
||||||
}
|
|
||||||
|
|
||||||
let oppositeId = findPairOpposite(move.parameters.whom, id);
|
|
||||||
if (!oppositeId) {
|
|
||||||
// TODO
|
|
||||||
throw "Failed to identify dancer to roll away with.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO This isn't quite right if there's no 1/2 sash?
|
|
||||||
const oppositePos = getPosFor(oppositeId);
|
|
||||||
let swapPos = oppositePos;
|
|
||||||
if (swapPos.kind === PositionKind.Circle && swapPos.longLines) {
|
|
||||||
swapPos = { ...swapPos, longLines: undefined };
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO animate hands?
|
|
||||||
if (isRoller) {
|
|
||||||
if (move.parameters["½sash"]) {
|
|
||||||
// swap positions by sliding
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
endPosition: swapPos,
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
|
||||||
}], startPos));
|
|
||||||
} else {
|
|
||||||
// just stand still
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
endPosition: { ...startPos, longLines: undefined },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear },
|
|
||||||
}], startPos));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// being rolled away, so do a spin
|
|
||||||
res.set(id, combine([{
|
|
||||||
beats: move.beats,
|
|
||||||
// TODO Is this the right end position logic?
|
|
||||||
endPosition: move.parameters["½sash"] ? swapPos : { ...startPos, which: startPos.which.swapDiagonal() },
|
|
||||||
movementPattern: { kind: SemanticAnimationKind.RollAway },
|
|
||||||
}], startPos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,10 +95,6 @@ export class CirclePosition {
|
||||||
][CirclePosition.enumValueToNumber(this.enumValue)];
|
][CirclePosition.enumValueToNumber(this.enumValue)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public swapDiagonal() : CirclePosition {
|
|
||||||
return this.swapAcross().swapUpAndDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public facingCenterRotation() : Rotation {
|
public facingCenterRotation() : Rotation {
|
||||||
return (45 + 90 * CirclePosition.enumValueToNumber(this.enumValue)) % 360;
|
return (45 + 90 * CirclePosition.enumValueToNumber(this.enumValue)) % 360;
|
||||||
}
|
}
|
||||||
|
@ -136,79 +132,6 @@ export class CirclePosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ShortLinesPositionEnum {
|
|
||||||
FarLeft = "FarLeft",
|
|
||||||
MiddleLeft = "MiddleLeft",
|
|
||||||
MiddleRight = "MiddleRight",
|
|
||||||
FarRight = "FarRight",
|
|
||||||
}
|
|
||||||
export class ShortLinesPosition {
|
|
||||||
public static readonly FarLeft = new ShortLinesPosition(ShortLinesPositionEnum.FarLeft);
|
|
||||||
public static readonly MiddleLeft = new ShortLinesPosition(ShortLinesPositionEnum.MiddleLeft);
|
|
||||||
public static readonly MiddleRight = new ShortLinesPosition(ShortLinesPositionEnum.MiddleRight);
|
|
||||||
public static readonly FarRight = new ShortLinesPosition(ShortLinesPositionEnum.FarRight);
|
|
||||||
|
|
||||||
private readonly enumValue: ShortLinesPositionEnum;
|
|
||||||
|
|
||||||
private constructor(enumValue: ShortLinesPositionEnum) {
|
|
||||||
this.enumValue = enumValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static enumValueToNumber(enumValue: ShortLinesPositionEnum) : number {
|
|
||||||
switch (enumValue) {
|
|
||||||
case ShortLinesPositionEnum.FarLeft:
|
|
||||||
return 0;
|
|
||||||
case ShortLinesPositionEnum.MiddleLeft:
|
|
||||||
return 1;
|
|
||||||
case ShortLinesPositionEnum.MiddleRight:
|
|
||||||
return 2;
|
|
||||||
case ShortLinesPositionEnum.FarRight:
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static numberToEnumValue(num: number) : ShortLinesPositionEnum {
|
|
||||||
if (num < 0 || num > 3) {
|
|
||||||
num %= 4;
|
|
||||||
if (num < 0) num += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
ShortLinesPositionEnum.FarLeft,
|
|
||||||
ShortLinesPositionEnum.MiddleLeft,
|
|
||||||
ShortLinesPositionEnum.MiddleRight,
|
|
||||||
ShortLinesPositionEnum.FarRight,
|
|
||||||
][num];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static get(enumValue: ShortLinesPositionEnum) : ShortLinesPosition {
|
|
||||||
return [
|
|
||||||
ShortLinesPosition.FarLeft,
|
|
||||||
ShortLinesPosition.MiddleLeft,
|
|
||||||
ShortLinesPosition.MiddleRight,
|
|
||||||
ShortLinesPosition.FarRight,
|
|
||||||
][this.enumValueToNumber(enumValue)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public isMiddle() : boolean {
|
|
||||||
return this.enumValue === ShortLinesPositionEnum.MiddleRight || this.enumValue === ShortLinesPositionEnum.MiddleLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
public leftRightSide() : CircleSide.Left | CircleSide.Right {
|
|
||||||
return this.enumValue === ShortLinesPositionEnum.FarLeft || this.enumValue === ShortLinesPositionEnum.MiddleLeft
|
|
||||||
? CircleSide.Left
|
|
||||||
: CircleSide.Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isLeft() : boolean {
|
|
||||||
return this.leftRightSide() === CircleSide.Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toString() : string {
|
|
||||||
return this.enumValue.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Facing {
|
export enum Facing {
|
||||||
CenterOfCircle = "CenterOfCircle",
|
CenterOfCircle = "CenterOfCircle",
|
||||||
Up = "Up",
|
Up = "Up",
|
||||||
|
@ -220,9 +143,6 @@ export enum HandTo {
|
||||||
LeftInCircle = "LeftInCircle",
|
LeftInCircle = "LeftInCircle",
|
||||||
RightInCircle = "RightInCircle",
|
RightInCircle = "RightInCircle",
|
||||||
AcrossCircle = "AcrossCircle",
|
AcrossCircle = "AcrossCircle",
|
||||||
DiagonalAcrossCircle = "DiagonalAcrossCircle",
|
|
||||||
LeftDiagonalAcrossCircle = "LeftDiagonalAcrossCircle",
|
|
||||||
RightDiagonalAcrossCircle = "RightDiagonalAcrossCircle",
|
|
||||||
DancerForward = "DancerForward",
|
DancerForward = "DancerForward",
|
||||||
DancerLeft = "DancerLeft",
|
DancerLeft = "DancerLeft",
|
||||||
DancerRight = "DancerRight",
|
DancerRight = "DancerRight",
|
||||||
|
@ -245,9 +165,6 @@ export enum DancerDistance {
|
||||||
SwingLark = "SwingLark",
|
SwingLark = "SwingLark",
|
||||||
SwingRobin = "SwingRobin",
|
SwingRobin = "SwingRobin",
|
||||||
}
|
}
|
||||||
export enum LongLines {
|
|
||||||
Forward = "Forward",
|
|
||||||
}
|
|
||||||
export type SemanticPosition = {
|
export type SemanticPosition = {
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
setOffset?: number,
|
setOffset?: number,
|
||||||
|
@ -257,13 +174,10 @@ export type SemanticPosition = {
|
||||||
hands?: Map<Hand, HandConnection>,
|
hands?: Map<Hand, HandConnection>,
|
||||||
balance?: BalanceWeight,
|
balance?: BalanceWeight,
|
||||||
dancerDistance?: DancerDistance,
|
dancerDistance?: DancerDistance,
|
||||||
longLines?: LongLines,
|
|
||||||
} | {
|
} | {
|
||||||
kind: PositionKind.ShortLines,
|
kind: PositionKind.ShortLines,
|
||||||
setOffset?: number,
|
setOffset?: number,
|
||||||
lineOffset?: number,
|
lineOffset?: number,
|
||||||
which: ShortLinesPosition,
|
|
||||||
facing: Facing,
|
|
||||||
hands?: Map<Hand, HandConnection>,
|
hands?: Map<Hand, HandConnection>,
|
||||||
balance?: BalanceWeight,
|
balance?: BalanceWeight,
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,11 +28,6 @@ export enum SemanticAnimationKind {
|
||||||
RotateAround = "RotateAround",
|
RotateAround = "RotateAround",
|
||||||
|
|
||||||
Swing = "Swing",
|
Swing = "Swing",
|
||||||
|
|
||||||
CourtesyTurn = "CourtesyTurn",
|
|
||||||
|
|
||||||
// Dancer being rolled away / spinning. Other dancer is just Linear.
|
|
||||||
RollAway = "RollAway",
|
|
||||||
}
|
}
|
||||||
export type SemanticAnimation = {
|
export type SemanticAnimation = {
|
||||||
kind: SemanticAnimationKind.StandStill,
|
kind: SemanticAnimationKind.StandStill,
|
||||||
|
@ -82,19 +77,6 @@ export type SemanticAnimation = {
|
||||||
around: CircleSide,
|
around: CircleSide,
|
||||||
|
|
||||||
hand: Hand,
|
hand: Hand,
|
||||||
} | {
|
|
||||||
kind: SemanticAnimationKind.PassBy,
|
|
||||||
|
|
||||||
around: CircleSideOrCenter,
|
|
||||||
|
|
||||||
side: Hand,
|
|
||||||
|
|
||||||
// If true, pull by the specified hand, if false, just pass by that side without hands.
|
|
||||||
withHands: boolean,
|
|
||||||
} | {
|
|
||||||
kind: SemanticAnimationKind.CourtesyTurn,
|
|
||||||
} | {
|
|
||||||
kind: SemanticAnimationKind.RollAway,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -271,9 +253,6 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
||||||
const xSign = connection.to === HandTo.LeftInCircle ? -1 : +1;
|
const xSign = connection.to === HandTo.LeftInCircle ? -1 : +1;
|
||||||
const balanceFactor = semantic.balance === BalanceWeight.Forward ? 1-balanceAmount : 1;
|
const balanceFactor = semantic.balance === BalanceWeight.Forward ? 1-balanceAmount : 1;
|
||||||
return { x: balanceFactor*xSign/Math.sqrt(2), y: balanceFactor/Math.sqrt(2) };
|
return { x: balanceFactor*xSign/Math.sqrt(2), y: balanceFactor/Math.sqrt(2) };
|
||||||
case HandTo.DiagonalAcrossCircle:
|
|
||||||
// TODO Is "diagonal" even enough information?
|
|
||||||
return { x: -0.5, y: +0.5 }
|
|
||||||
default:
|
default:
|
||||||
throw "Unkown connection: " + connection.to;
|
throw "Unkown connection: " + connection.to;
|
||||||
}
|
}
|
||||||
|
@ -515,17 +494,6 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
||||||
endTransitionBeats: 1,
|
endTransitionBeats: 1,
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
// TODO Unsupported moves, just doing linear for now.
|
|
||||||
case SemanticAnimationKind.PassBy:
|
|
||||||
case SemanticAnimationKind.CourtesyTurn:
|
|
||||||
case SemanticAnimationKind.RollAway:
|
|
||||||
return [
|
|
||||||
new animation.LinearAnimationSegment({
|
|
||||||
beats: move.beats,
|
|
||||||
startPosition: startSetPosition,
|
|
||||||
endPosition: endSetPosition,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw "Unsupported move in animateLowLevelMove: " + JSON.stringify(move);
|
throw "Unsupported move in animateLowLevelMove: " + JSON.stringify(move);
|
||||||
|
|
|
@ -143,9 +143,7 @@ export class Renderer {
|
||||||
const trailLengthInBeats = 1;
|
const trailLengthInBeats = 1;
|
||||||
const incrementLength = trailLengthInBeats / increments;
|
const incrementLength = trailLengthInBeats / increments;
|
||||||
progression ??= 0;
|
progression ??= 0;
|
||||||
const offsetSets = this.animation.progression.y === 0
|
const offsetSets = -((progression - (progression % 2)) / 2) / ((this.animation.progression.y * 2) / setDistance);
|
||||||
? 0
|
|
||||||
: -((progression - (progression % 2)) / 2) / ((this.animation.progression.y * 2) / setDistance);
|
|
||||||
for (var i = increments; i >= 0; i--) {
|
for (var i = increments; i >= 0; i--) {
|
||||||
const beatToDraw = beat - i*incrementLength;
|
const beatToDraw = beat - i*incrementLength;
|
||||||
this.ctx.globalAlpha = i == 0 ? 1 : (1 - i/increments)*0.3;
|
this.ctx.globalAlpha = i == 0 ? 1 : (1 - i/increments)*0.3;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user