First attempt at pass by / hey animation.

This commit is contained in:
Daniel Perelman 2023-09-24 22:53:53 -07:00
parent 43ffd7d87a
commit 1c7f80e1c3
2 changed files with 91 additions and 4 deletions

View File

@ -1,5 +1,5 @@
import { CoupleRole, DancerIdentity, Rotation, normalizeRotation } from "./danceCommon.js";
import { DancerSetPosition, DancersSetPositions, Hand, OffsetEquals, OffsetMinus, OffsetPlus, OffsetTimes, offsetZero, setHeight } from "./rendererConstants.js";
import { DancerSetPosition, DancersSetPositions, Hand, OffsetEquals, OffsetMinus, OffsetPlus, OffsetRotate, OffsetTimes, OffsetTranspose, offsetZero, setHeight } from "./rendererConstants.js";
import { Offset, leftShoulder, rightShoulder, degreesToRadians, radiansToDegrees } from "./rendererConstants.js";
export enum AnimationKind {
@ -325,9 +325,10 @@ export class RotationAnimationSegment extends AnimationSegment {
case RotationAnimationFacing.CenterRelative:
return degrees - this.startRotation + this.centerRelativeTo;
case RotationAnimationFacing.Forward:
return degrees + 180;
case RotationAnimationFacing.Backward:
return degrees;
return (this.endRotation > this.startRotation === (this.facing === RotationAnimationFacing.Forward))
? degrees + 180
: degrees;
}
}
}
@ -361,6 +362,60 @@ export class RotationAnimationSegment extends AnimationSegment {
}
}
// For "PassBy" and related moves where stepping straight forward would walk into the
// other dancer, so instead move in thin oval
export class StepWideLinearAnimationSegment extends AnimationSegment {
private readonly distanceAtMidpoint: number;
private readonly sidewaysVector: Offset;
private readonly progressCenter?: number;
private readonly center?: Offset;
constructor({ beats, startPosition, endPosition, distanceAtMidpoint, center }: {
beats: number;
startPosition: DancerSetPosition;
endPosition: DancerSetPosition;
distanceAtMidpoint: number;
center?: Offset;
}) {
super(beats, startPosition, endPosition);
this.distanceAtMidpoint = distanceAtMidpoint;
const vector = OffsetMinus(this.endPosition.position, this.startPosition.position);
const norm = vector.x * vector.x + vector.y * vector.y;
const distance = Math.sqrt(norm);
const sideways = OffsetRotate(vector, Rotation.Left);
const normalizedVector = OffsetTimes(sideways, 1/distance);
this.sidewaysVector = normalizedVector;
// Center is the point where the dancer is closest to the other dancer.
// If omitted, it's assumed the movement is symmetrical, so that happens halfway through the move.
// Otherwise, find the point on the line between the start and end closest to the center.
if (center) {
this.center = center;
this.progressCenter = ((center.x - this.startPosition.position.x) * vector.x
+ (center.y - this.startPosition.position.y) * vector.y)
/ norm;
}
}
interpolateOffset(progress: number): Offset {
let sidewaysProgress = progress;
if (this.progressCenter) {
if (progress === this.progressCenter) {
sidewaysProgress = 0.5;
} else if (progress < this.progressCenter) {
sidewaysProgress = progress / this.progressCenter / 2;
} else /* progress => this.progressCenter */ {
sidewaysProgress = ((progress - this.progressCenter) / (1 - this.progressCenter)) / 2 + 0.5;
}
}
return OffsetPlus(interpolateLinearOffset(progress, this.startPosition.position, this.endPosition.position),
OffsetTimes(this.sidewaysVector, this.distanceAtMidpoint * Math.sin(sidewaysProgress * Math.PI)));
}
}
export class Animation {
private readonly segments : Map<DancerIdentity, AnimationSegment[]>;
public readonly progression : Offset;

View File

@ -691,8 +691,40 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
endTransitionBeats: 1,
})
];
// TODO Unsupported moves, just doing linear for now.
case SemanticAnimationKind.PassBy:
const passByCenter = CenterOf(move.movementPattern.around, move.startPosition.setOffset, move.startPosition.lineOffset);
const wide = move.movementPattern.around === "Center";
const width = wide ? dancerWidth : dancerWidth/2;
const distanceAtMidpoint = move.movementPattern.side == Hand.Left ? +width : -width;
// "Pull By" is just "Pass By" with hands.
const passByHands = move.movementPattern.withHands
? new Map<Hand, animation.HandAnimation>([
[move.movementPattern.side, { kind: "Center" }],
[move.movementPattern.side.opposite(), { kind: "None" }],
])
: new Map<Hand, animation.HandAnimation>([
[Hand.Left, { kind: "None" }],
[Hand.Right, { kind: "None" }],
]);
let endRotation = endSetPosition.rotation;
const rotationAmount = endSetPosition.rotation - startSetPosition.rotation;
if (rotationAmount !== 0) {
if (rotationAmount >= 180 && move.movementPattern.side === Hand.Left) {
endRotation -= 360;
} else if (rotationAmount <= -180 && move.movementPattern.side === Hand.Right) {
endRotation += 360;
}
}
return [
new animation.StepWideLinearAnimationSegment({
beats: move.beats,
startPosition: startSetPosition,
endPosition: { ...endSetPosition, rotation: endRotation },
distanceAtMidpoint,
center: move.movementPattern.around === "Center" ? passByCenter : undefined, // TODO Better center?
})
];
// TODO Unsupported moves, just doing linear for now.
case SemanticAnimationKind.CourtesyTurn:
case SemanticAnimationKind.RollAway:
case SemanticAnimationKind.Promenade: