Compare commits

...

4 Commits

4 changed files with 104 additions and 39 deletions

View File

@ -238,7 +238,21 @@ export class TransitionAnimationSegment extends AnimationSegment {
} }
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) { override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
// TODO display transition somehow? // TODO better way to display transition?
if (progress < this.startTransitionProgress) {
ctx.beginPath();
ctx.moveTo(this.startPosition.position.x, this.startPosition.position.y);
const transitionStart = this.actualAnimation.interpolateOffset(this.startTransitionProgress);
ctx.lineTo(transitionStart.x, transitionStart.y);
ctx.stroke();
} else if (progress > 1 - this.endTransitionProgress) {
ctx.beginPath();
const transitionEnd = this.actualAnimation.interpolateOffset(this.endTransitionProgress);
ctx.moveTo(transitionEnd.x, transitionEnd.y);
ctx.lineTo(this.endPosition.position.x, this.endPosition.position.y);
ctx.stroke();
}
this.actualAnimation.drawDebug(ctx, progress); this.actualAnimation.drawDebug(ctx, progress);
} }
} }

View File

@ -594,7 +594,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
}); });
case "swing": case "swing":
return handlePairedMove(move.parameters.who, ({ id, startPos, around, withId }) => { return handlePairedMove(move.parameters.who, ({ id, startPos, around, withId, withPos }) => {
// TODO swing can start from non-circle positions. // TODO swing can start from non-circle positions.
// TODO swing end is only in notes / looking at next move. // TODO swing end is only in notes / looking at next move.
@ -602,6 +602,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
// TODO more structured way to do this than enumerating next moves here? // TODO more structured way to do this than enumerating next moves here?
// maybe instead of nextMove an optional endPosition for fixing up positions? // maybe instead of nextMove an optional endPosition for fixing up positions?
// ... but then every move would have to handle that... // ... but then every move would have to handle that...
const afterTake = startPos.longLines === LongLines.Near || withPos.longLines === LongLines.Near;
const toShortLines = nextMove.move === "down the hall" || nextMove.move === "up the hall"; const toShortLines = nextMove.move === "down the hall" || nextMove.move === "up the hall";
const endFacingAcross = (around === CircleSide.Left || around === CircleSide.Right) && !toShortLines; const endFacingAcross = (around === CircleSide.Left || around === CircleSide.Right) && !toShortLines;
@ -609,7 +610,10 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
const startPosition: SemanticPosition = { const startPosition: SemanticPosition = {
...startPos, ...startPos,
facing: around === CircleSide.Left || CircleSide.Right facing: around === CircleSide.Left || CircleSide.Right
? (startWhich instanceof CirclePosition ? (startWhich.topBottomSide() === CircleSide.Bottom ? Facing.Up : Facing.Down) ? (startWhich instanceof CirclePosition
? afterTake
? startPos.longLines === LongLines.Near ? startWhich.facingOut() : startWhich.facingAcross()
: (startWhich.topBottomSide() === CircleSide.Bottom ? Facing.Up : Facing.Down)
: startWhich.facingSide()) : startWhich.facingSide())
: (startWhich.isLeft() ? Facing.Right : Facing.Left), : (startWhich.isLeft() ? Facing.Right : Facing.Left),
}; };
@ -691,6 +695,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
around, around,
endFacing: startWhich.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left, endFacing: startWhich.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
swingRole, swingRole,
afterTake,
}, },
}; };
@ -766,8 +771,9 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
return handleCirclePairedMove(move.parameters.who, ({ startPos }) => { return handleCirclePairedMove(move.parameters.who, ({ startPos }) => {
const handTo = startPos.which.isOnLeftLookingAcross() ? HandTo.DancerRight : HandTo.DancerLeft; const handTo = startPos.which.isOnLeftLookingAcross() ? HandTo.DancerRight : HandTo.DancerLeft;
const startingPos = { const startingPos: SemanticPosition = {
...startPos, ...startPos,
facing: startPos.which.facingAcross(),
dancerDistance: DancerDistance.Compact, dancerDistance: DancerDistance.Compact,
hands: new Map<Hand, HandConnection>([ hands: new Map<Hand, HandConnection>([
[Hand.Left, { hand: Hand.Left, to: handTo }], [Hand.Left, { hand: Hand.Left, to: handTo }],
@ -874,35 +880,52 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
endPosition = withPos; endPosition = withPos;
} else if (intoWave) { } else if (intoWave) {
if (startPos.kind === PositionKind.ShortLines) { if (startPos.kind === PositionKind.ShortLines) {
// Fix startPos if necessary. Needed because pass through always swaps but sometimes shouldn't. 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.
(startPos.facing === Facing.Up || startPos.facing === Facing.Down) && if ((startPos.facing === Facing.Up || startPos.facing === Facing.Down) &&
((byHandOrShoulder === Hand.Right) ((byHandOrShoulder === Hand.Right)
!== (startPos.facing === Facing.Up) !== (startPos.facing === Facing.Up)
!== (startPos.which === ShortLinesPosition.FarLeft || startPos.which === ShortLinesPosition.MiddleRight))) { !== (startPos.which === ShortLinesPosition.FarLeft || startPos.which === ShortLinesPosition.MiddleRight))) {
startingPos = { startingPos = {
...startPos, ...startPos,
which: startPos.which.swapOnSide() which: startPos.which.swapOnSide()
}; };
} }
const endWhich = CirclePosition.fromSides(startingPos.which.leftRightSide(), const endWhich = CirclePosition.fromSides(startingPos.which.leftRightSide(),
(startingPos.which === ShortLinesPosition.FarLeft || startingPos.which === ShortLinesPosition.MiddleRight) (startingPos.which === ShortLinesPosition.FarLeft || startingPos.which === ShortLinesPosition.MiddleRight)
!== (byHandOrShoulder === Hand.Right) !== (byHandOrShoulder === Hand.Right)
!== (intoWavePositions === 1) !== (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) === (intoWavePositions === 1)
? endWhich.facingAcross() ? endWhich.facingAcross()
: endWhich.facingOut(), : endWhich.facingOut(),
setOffset: startingPos.setOffset, setOffset: startingPos.setOffset,
lineOffset: startingPos.lineOffset, lineOffset: startingPos.lineOffset,
}
} else {
throw new Error("Allemande from short lines to line line in middle unsupported.");
} }
} else { } else {
throw new Error("Allemande from circle to short lines is unsupported."); if (around === "Center") {
const startCenter = startPos.longLines === LongLines.Center;
const endWhich = startPos.which.circleRight(intoWavePositions);
endPosition = {
kind: PositionKind.Circle,
which: endWhich,
facing: startPos.which.facingOut(),
longLines: startCenter ? undefined : LongLines.Center,
setOffset: startPos.setOffset,
lineOffset: startPos.lineOffset,
}
} else {
throw new Error("Allemande from circle to short lines is unsupported.");
}
} }
} }
@ -1446,7 +1469,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
{ {
beats: backwardBeats, beats: backwardBeats,
endPosition: startPosition, endPosition: startPosition,
movementPattern: { kind: SemanticAnimationKind.Linear }, movementPattern: { kind: SemanticAnimationKind.Linear, minRotation: 0 },
}, },
], startPosition); ], startPosition);
} else { } else {

View File

@ -345,6 +345,9 @@ export enum LongLines {
// Only a little offset (has walked almost all the way from the other side after a give and take). // Only a little offset (has walked almost all the way from the other side after a give and take).
Near = "Near", Near = "Near",
// Actually in center. May be slightly offset for wavy lines.
Center = "Center",
} }
export type SemanticPosition = { export type SemanticPosition = {
kind: PositionKind.Circle, kind: PositionKind.Circle,

View File

@ -88,6 +88,9 @@ export type SemanticAnimation = {
// Swings are asymmetric. This is usually but not always the dancer's role. // Swings are asymmetric. This is usually but not always the dancer's role.
swingRole: common.DanceRole, swingRole: common.DanceRole,
// After a take need to fixup the position.
afterTake: boolean,
} | { } | {
kind: SemanticAnimationKind.TwirlSwap, kind: SemanticAnimationKind.TwirlSwap,
@ -270,6 +273,8 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
throw "Invalid circle position: " + semantic.which; throw "Invalid circle position: " + semantic.which;
} }
switch(semantic.longLines) { switch(semantic.longLines) {
case LongLines.Center:
position.x *= 0.1;
case LongLines.Forward: case LongLines.Forward:
position.x *= 0.3; position.x *= 0.3;
case LongLines.Near: case LongLines.Near:
@ -585,6 +590,7 @@ function animateLowLevelMoveWithoutSlide(move: LowLevelMove): animation.Animatio
]) ])
}), }),
flags: { flags: {
rotation: true,
hands: true, hands: true,
handsDuring: circleHands, handsDuring: circleHands,
}, },
@ -669,15 +675,26 @@ function animateLowLevelMoveWithoutSlide(move: LowLevelMove): animation.Animatio
const baseSwingStart = { const baseSwingStart = {
...move.startPosition, ...move.startPosition,
dancerDistance, dancerDistance: move.movementPattern.afterTake ? undefined : dancerDistance,
balance: undefined, balance: undefined,
longLines: undefined, longLines: undefined,
} }
const swingStart = SemanticToSetPosition(move.startPosition.longLines === LongLines.Near ? { const swingStartUnadjusted = SemanticToSetPosition(move.startPosition.longLines === LongLines.Near ? {
...baseSwingStart, ...baseSwingStart,
kind: PositionKind.Circle, kind: PositionKind.Circle,
which: move.startPosition.which.swapUpAndDown(), which: move.startPosition.which.swapUpAndDown(),
} : baseSwingStart); } : baseSwingStart);
const swingStart = move.movementPattern.afterTake
? {
...swingStartUnadjusted, position: {
x: swingStartUnadjusted.position.x
+ ((move.startPosition.longLines === LongLines.Near) === (move.startPosition.which.isLeft())
? +0.5
: -0.5),
y: rotateSwingCenter.y,
}
}
: swingStartUnadjusted;
const beforeUnfold = SemanticToSetPosition({ const beforeUnfold = SemanticToSetPosition({
...move.endPosition, ...move.endPosition,
dancerDistance, dancerDistance,
@ -696,15 +713,16 @@ function animateLowLevelMoveWithoutSlide(move: LowLevelMove): animation.Animatio
const turns = Math.ceil(Math.abs(move.movementPattern.minAmount / 360)); const turns = Math.ceil(Math.abs(move.movementPattern.minAmount / 360));
const swingMinRotation = (minTurns - turns) * (move.movementPattern.minAmount < 0 ? -360 : 360) + move.movementPattern.minAmount; const swingMinRotation = (minTurns - turns) * (move.movementPattern.minAmount < 0 ? -360 : 360) + move.movementPattern.minAmount;
return [ const slideAmount = move.movementPattern.afterTake ? { x: 0, y: rotateSwingCenter.y - startSetPosition.position.y } : undefined;
new animation.TransitionAnimationSegment({
const swingAnimation = new animation.TransitionAnimationSegment({
actualAnimation: new animation.RotationAnimationSegment({ actualAnimation: new animation.RotationAnimationSegment({
beats: swingBeats, beats: swingBeats,
startPosition: swingStart, startPosition: slideAmount ? { ...swingStart, position: OffsetPlus(swingStart.position, OffsetTimes(slideAmount, -1)) } : swingStart,
endPosition: beforeUnfold, endPosition: slideAmount ? { ...beforeUnfold, position: OffsetPlus(beforeUnfold.position, OffsetTimes(slideAmount, -1)) } : beforeUnfold,
rotation: swingMinRotation, rotation: swingMinRotation,
around: { around: {
center: rotateSwingCenter, center: slideAmount ? OffsetPlus(rotateSwingCenter, OffsetTimes(slideAmount, -1)) : rotateSwingCenter,
width: 1, width: 1,
height: 1, height: 1,
}, },
@ -722,7 +740,14 @@ function animateLowLevelMoveWithoutSlide(move: LowLevelMove): animation.Animatio
}, },
startTransitionBeats: 1, startTransitionBeats: 1,
endTransitionBeats: 0, endTransitionBeats: 0,
}), });
return [
slideAmount ? new animation.SlideAnimationSegment(
swingAnimation.beats,
[swingAnimation],
slideAmount,
) : swingAnimation,
new animation.LinearAnimationSegment({ new animation.LinearAnimationSegment({
beats: 1, beats: 1,
startPosition: beforeUnfold, startPosition: beforeUnfold,