Compare commits

...

2 Commits

3 changed files with 133 additions and 16 deletions

View File

@ -64,6 +64,20 @@ function handsInLine(args: { wavy: boolean } & ({ which: ShortLinesPosition, fac
]); ]);
} }
} }
function handToDancerToSideInCircleFacingAcross(which: CirclePosition): Map<Hand, HandConnection> {
return new Map<Hand, HandConnection>([
which.isOnLeftLookingAcross()
? [Hand.Right, { hand: Hand.Left, to: HandTo.DancerRight }]
: [Hand.Left, { hand: Hand.Right, to: HandTo.DancerLeft }]
]);
}
function handToDancerToSideInCircleFacingUpOrDown(which: CirclePosition): Map<Hand, HandConnection> {
return new Map<Hand, HandConnection>([
which.isOnLeftLookingUpAndDown()
? [Hand.Right, { hand: Hand.Left, to: HandTo.DancerRight }]
: [Hand.Left, { hand: Hand.Right, to: HandTo.DancerLeft }]
]);
}
function balanceCircleInAndOut(move: Move, startPos: SemanticPosition, balanceBeats?: number): [LowLevelMove, LowLevelMove] { function balanceCircleInAndOut(move: Move, startPos: SemanticPosition, balanceBeats?: number): [LowLevelMove, LowLevelMove] {
if (startPos.kind !== PositionKind.Circle) { if (startPos.kind !== PositionKind.Circle) {
@ -450,7 +464,8 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
movementPattern: { movementPattern: {
// TODO Not sure loop should really be linear... // TODO Not sure loop should really be linear...
kind: SemanticAnimationKind.Linear, kind: SemanticAnimationKind.Linear,
minRotation: isCrossing ? undefined : circulateRight ? 180 : -180 minRotation: isCrossing ? undefined : circulateRight ? 180 : -180,
handsDuring: "None",
} }
}; };
return combine([...balance, circulate], startingPos); return combine([...balance, circulate], startingPos);
@ -1240,6 +1255,45 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
}], startingPos); }], startingPos);
}); });
case "slice":
if (move.parameters["slice increment"] === "dancer") {
// TODO Maybe this only actually gets used to move an entire couple by going diagonal back?
throw new Error("Slicing by a single dancer is unsupported.");
}
const sliceLeft = move.parameters.slide;
const sliceReturns = move.parameters["slice return"] !== "none";
const sliceForwardBeats = sliceReturns ? move.beats / 2 : move.beats;
const sliceBackwardBeats = move.beats - sliceForwardBeats;
return handleCircleMove(({ startPos }) => {
const startingPos: SemanticPosition & { setOffset: number } = {
kind: PositionKind.Circle,
which: startPos.which,
facing: startPos.which.facingAcross(),
hands: handToDancerToSideInCircleFacingAcross(startPos.which),
setOffset: startPos.setOffset ?? 0,
lineOffset: startPos.lineOffset,
};
const sliceAmount = startingPos.which.isLeft() === sliceLeft ? +0.5 : -0.5;
const forwardOffset = startingPos.setOffset + sliceAmount;
const endOffset = move.parameters["slice return"] === "diagonal" ? forwardOffset + sliceAmount : forwardOffset;
const sliceForward: PartialLowLevelMove = {
beats: sliceForwardBeats,
endPosition: { ...startingPos, setOffset: forwardOffset, longLines: LongLines.Forward },
movementPattern: { kind: SemanticAnimationKind.Linear },
};
const maybeSliceBackward: PartialLowLevelMove[] = sliceReturns ? [{
beats: sliceBackwardBeats,
endPosition: { ...startingPos, setOffset: endOffset },
movementPattern: { kind: SemanticAnimationKind.Linear },
}] : [];
return combine([sliceForward, ...maybeSliceBackward], startingPos);
});
case "down the hall": case "down the hall":
if (move.parameters.who !== "everyone") { if (move.parameters.who !== "everyone") {
throw new Error("Don't know what it means for not everyone to go down the hall."); throw new Error("Don't know what it means for not everyone to go down the hall.");
@ -1683,6 +1737,26 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
return combine(heySteps.map(heyStepToPartialLowLevelMove), { ...startingPos, hands: undefined }); return combine(heySteps.map(heyStepToPartialLowLevelMove), { ...startingPos, hands: undefined });
}); });
case "turn alone":
if (move.parameters.who !== "everyone" || move.beats !== 0) {
throw new Error("turn alone unsupported except for changing to new circle.");
}
return handleCircleMove(({startPos}) => {
const which = startPos.which.swapUpAndDown();
const startAndEndPos: SemanticPosition = {
...startPos,
which,
facing: which.facingUpOrDown(),
setOffset: (startPos.setOffset ?? 0) + (startPos.which.isTop() ? +0.5 : -0.5),
}
return combine([{
beats: move.beats,
endPosition: startAndEndPos,
movementPattern: { kind: SemanticAnimationKind.StandStill },
}], startAndEndPos);
})
case "custom": case "custom":
if (move.parameters.custom.includes("mirrored mad robin")) { if (move.parameters.custom.includes("mirrored mad robin")) {
return handleCircleMove(({ id, startPos }) => { return handleCircleMove(({ id, startPos }) => {

View File

@ -3,7 +3,7 @@ import * as common from "./danceCommon.js";
import { DanceRole, DancerIdentity, Rotation } from "./danceCommon.js"; import { DanceRole, DancerIdentity, Rotation } from "./danceCommon.js";
import { BalanceWeight, CirclePosition, CircleSide, CircleSideOrCenter, DancerDistance, Facing, HandConnection, HandTo, LongLines, PositionKind, SemanticPosition, ShortLinesPosition, StarGrip } from "./interpreterCommon.js"; import { BalanceWeight, CirclePosition, CircleSide, CircleSideOrCenter, DancerDistance, Facing, HandConnection, HandTo, LongLines, PositionKind, SemanticPosition, ShortLinesPosition, StarGrip } from "./interpreterCommon.js";
import { Move } from "./libfigureMapper.js"; import { Move } from "./libfigureMapper.js";
import { DancerSetPosition, Hand, Offset, dancerWidth, lineDistance, setDistance, setHeight, setSpacing, setWidth } from "./rendererConstants.js"; import { DancerSetPosition, Hand, Offset, OffsetPlus, OffsetRotate, OffsetTimes, OffsetTranspose, dancerWidth, lineDistance, offsetZero, setDistance, setHeight, setSpacing, setWidth } from "./rendererConstants.js";
export enum SemanticAnimationKind { export enum SemanticAnimationKind {
@ -238,6 +238,7 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
case LongLines.Forward: case LongLines.Forward:
position.x *= 0.25; position.x *= 0.25;
} }
let balanceOffset: Offset = offsetZero;
if (semantic.balance) { if (semantic.balance) {
if (semantic.facing === Facing.CenterOfCircle) { if (semantic.facing === Facing.CenterOfCircle) {
if (semantic.balance === BalanceWeight.Forward) { if (semantic.balance === BalanceWeight.Forward) {
@ -247,17 +248,40 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
} }
} else if ((semantic.facing === Facing.Up || semantic.facing === Facing.Down) } else if ((semantic.facing === Facing.Up || semantic.facing === Facing.Down)
&& (semantic.balance === BalanceWeight.Backward || semantic.balance === BalanceWeight.Forward)) { && (semantic.balance === BalanceWeight.Backward || semantic.balance === BalanceWeight.Forward)) {
position = { balanceOffset = {
x: position.x, x: 0,
y: position.y + position.y * ( y: position.y * (
semantic.balance === BalanceWeight.Forward semantic.balance === BalanceWeight.Forward
? -balanceAmount ? -balanceAmount
: balanceAmount) : balanceAmount)
}; };
} else if ((semantic.facing === Facing.Left || semantic.facing === Facing.Right)
&& (semantic.balance === BalanceWeight.Backward || semantic.balance === BalanceWeight.Forward)) {
balanceOffset = {
x: (semantic.balance === BalanceWeight.Forward) === (semantic.facing === Facing.Left)
? -balanceAmount
: balanceAmount,
y: 0
};
} else if ((semantic.facing === Facing.Left || semantic.facing === Facing.Right)
&& (semantic.balance === BalanceWeight.Left || semantic.balance === BalanceWeight.Right)) {
balanceOffset = {
x: 0,
y: (semantic.balance === BalanceWeight.Left) === (semantic.facing === Facing.Left)
? -balanceAmount
: balanceAmount
};
} else if ((semantic.facing === Facing.Up || semantic.facing === Facing.Down)
&& (semantic.balance === BalanceWeight.Left || semantic.balance === BalanceWeight.Right)) {
balanceOffset = {
x: (semantic.balance === BalanceWeight.Left) === (semantic.facing === Facing.Up)
? -balanceAmount
: balanceAmount,
y: 0
};
} }
} }
position.x += setOffset.x; position = OffsetPlus(position, balanceOffset, setOffset);
position.y += setOffset.y;
const isFacingUpOrDown = semantic.facing === Facing.Up || semantic.facing === Facing.Down; const isFacingUpOrDown = semantic.facing === Facing.Up || semantic.facing === Facing.Down;
if (semantic.dancerDistance === DancerDistance.SwingLark) { if (semantic.dancerDistance === DancerDistance.SwingLark) {
@ -292,11 +316,12 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
// TODO Hands. Might need more info? Or need context of nearby dancer SemanticPositions? // TODO Hands. Might need more info? Or need context of nearby dancer SemanticPositions?
if (!connection) return undefined; if (!connection) return undefined;
const balanceHandAdjustment = OffsetRotate(balanceOffset, -rotation);
switch (connection.to) { switch (connection.to) {
case HandTo.DancerLeft: case HandTo.DancerLeft:
return { x: -1, y: 0 }; return OffsetPlus({ x: -1, y: 0 }, balanceHandAdjustment);
case HandTo.DancerRight: case HandTo.DancerRight:
return { x: +1, y: 0 }; return OffsetPlus({ x: +1, y: 0 }, balanceHandAdjustment);
case HandTo.DancerForward: case HandTo.DancerForward:
if (hand === connection.hand) { if (hand === connection.hand) {
return { x: 0, y: +0.5 }; return { x: 0, y: +0.5 };

View File

@ -1,32 +1,50 @@
import { DancerIdentity } from "./danceCommon"; import { DancerIdentity, Rotation } from "./danceCommon";
export interface Offset { export interface Offset {
x: number; x: number;
y: number; y: number;
} }
export function OffsetPlus(a: Offset, b: Offset) { export function OffsetTranspose(a: Offset): Offset {
return { x: a.y, y: a.x };
}
export function OffsetRotate(a: Offset, r: Rotation): Offset {
const rad = degreesToRadians(r);
const sin = Math.sin(rad);
const cos = Math.cos(rad);
return {
x: a.x * cos - a.y * sin,
y: a.x * sin + a.y * cos,
};
}
function _OffsetPlus(a: Offset, b: Offset): Offset {
return { return {
x: a.x + b.x, x: a.x + b.x,
y: a.y + b.y, y: a.y + b.y,
}; };
} }
export function OffsetTimes(a: Offset, b: number) { export function OffsetPlus(...args: Offset[]): Offset {
return args.reduce(_OffsetPlus, offsetZero);
}
export function OffsetTimes(a: Offset, b: number): Offset {
return { return {
x: a.x * b, x: a.x * b,
y: a.y * b, y: a.y * b,
}; };
} }
export function OffsetMinus(a: Offset, b: Offset) { export function OffsetMinus(a: Offset, b: Offset): Offset {
return { return {
x: a.x - b.x, x: a.x - b.x,
y: a.y - b.y, y: a.y - b.y,
}; };
} }
export function OffsetEquals(a: Offset, b: Offset) { export function OffsetEquals(a: Offset, b: Offset): boolean {
return a.x === b.x && a.y === b.y; return a.x === b.x && a.y === b.y;
} }