Compare commits
2 Commits
084088dded
...
f533e3d202
Author | SHA1 | Date | |
---|---|---|---|
f533e3d202 | |||
ea9871d545 |
|
@ -40,6 +40,11 @@
|
|||
}
|
||||
table, tr, th, td {
|
||||
border: solid black 1px;
|
||||
vertical-align: top;
|
||||
}
|
||||
table pre {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
th.Ones.Lark {
|
||||
background-color: hsl(0, 80%, 50%);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DancerIdentity, normalizeRotation } from "./danceCommon.js";
|
||||
import { DancerIdentity, Rotation, normalizeRotation } from "./danceCommon.js";
|
||||
import { DancerSetPosition, DancersSetPositions, Hand } from "./rendererConstants.js";
|
||||
import { Offset, leftShoulder, rightShoulder, degreesToRadians, radiansToDegrees } from "./rendererConstants.js";
|
||||
|
||||
|
@ -82,6 +82,7 @@ export class LinearAnimationSegment extends AnimationSegment {
|
|||
export enum RotationAnimationFacing {
|
||||
Linear = "Linear", // Default, linearly interpolate.
|
||||
Center = "Center", // Always face the center.
|
||||
CenterRelative = "CenterRelative", // Always face the center.
|
||||
Forward = "Forward", // Always face the direction of the rotation.
|
||||
Backward = "Backward", // Opposite of forward.
|
||||
}
|
||||
|
@ -95,6 +96,7 @@ export class RotationAnimationSegment extends AnimationSegment {
|
|||
private readonly endRotation: number;
|
||||
private readonly center: Offset;
|
||||
private readonly facing: RotationAnimationFacing;
|
||||
private readonly startFacing: Rotation;
|
||||
|
||||
constructor(beats: number, startPosition: DancerSetPosition, endPosition: DancerSetPosition,
|
||||
rotation: number, around: RotationAround, facing: RotationAnimationFacing) {
|
||||
|
@ -113,6 +115,7 @@ export class RotationAnimationSegment extends AnimationSegment {
|
|||
return -radiansToDegrees(radians);
|
||||
}
|
||||
this.startRotation = positionToDegrees(startPosition.position);
|
||||
this.startFacing = startPosition.rotation;
|
||||
const actualRotation = normalizeRotation(positionToDegrees(endPosition.position) - this.startRotation,
|
||||
rotation);
|
||||
this.endRotation = this.startRotation + actualRotation;
|
||||
|
@ -142,6 +145,8 @@ export class RotationAnimationSegment extends AnimationSegment {
|
|||
switch (this.facing) {
|
||||
case RotationAnimationFacing.Center:
|
||||
return degrees - 90;
|
||||
case RotationAnimationFacing.CenterRelative:
|
||||
return degrees - this.startRotation + this.startFacing;
|
||||
case RotationAnimationFacing.Forward:
|
||||
return degrees + 180;
|
||||
case RotationAnimationFacing.Backward:
|
||||
|
|
|
@ -160,6 +160,14 @@ enum BalanceWeight {
|
|||
Left = "Left",
|
||||
Right = "Right",
|
||||
}
|
||||
enum DancerDistance {
|
||||
Normal = "Normal",
|
||||
Compact = "Compact", // allemande, etc.
|
||||
// Swings asymmetrical, but some dances may have the Lark do a swing as a Robin or vice versa,
|
||||
// especially any dance with Larks Swing or Robins Swing.
|
||||
SwingLark = "SwingLark",
|
||||
SwingRobin = "SwingRobin",
|
||||
}
|
||||
type SemanticPosition = {
|
||||
kind: PositionKind.Circle,
|
||||
setOffset?: number,
|
||||
|
@ -168,7 +176,7 @@ type SemanticPosition = {
|
|||
facing: Facing,
|
||||
hands?: Map<Hand, HandConnection>,
|
||||
balance?: BalanceWeight,
|
||||
sideCompact?: boolean, // if true, have moved in for swing/allemande/etc.
|
||||
dancerDistance?: DancerDistance,
|
||||
} | {
|
||||
kind: PositionKind.ShortLines,
|
||||
setOffset?: number,
|
||||
|
@ -205,8 +213,10 @@ enum SemanticAnimationKind {
|
|||
// California twirl, box the gnat, etc.
|
||||
TwirlSwap = "TwirlSwap",
|
||||
|
||||
// Rotate as a pair around a point. Allemande, right/left shoulder round, maybe swing?
|
||||
// Rotate as a pair around a point. Allemande, right/left shoulder round
|
||||
RotateAround = "RotateAround",
|
||||
|
||||
Swing = "Swing",
|
||||
}
|
||||
type SemanticAnimation = {
|
||||
kind: SemanticAnimationKind.StandStill,
|
||||
|
@ -235,6 +245,14 @@ type SemanticAnimation = {
|
|||
minAmount: number,
|
||||
|
||||
around: CircleSide | "Center",
|
||||
} | {
|
||||
kind: SemanticAnimationKind.Swing,
|
||||
|
||||
minAmount: number,
|
||||
|
||||
around: CircleSide | "Center",
|
||||
|
||||
endFacing: Facing,
|
||||
} | {
|
||||
kind: SemanticAnimationKind.TwirlSwap,
|
||||
}
|
||||
|
@ -556,23 +574,25 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
|||
: startPos.which.swapUpAndDown(),
|
||||
facing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
||||
balance: undefined,
|
||||
sideCompact: undefined,
|
||||
dancerDistance: undefined,
|
||||
hands: new Map<Hand, HandConnection>([id.danceRole === DanceRole.Lark
|
||||
? [Hand.Right, { to: HandTo.DancerRight, hand: Hand.Left }]
|
||||
: [Hand.Left, { to: HandTo.DancerLeft, hand: Hand.Right }]]),
|
||||
};
|
||||
// TODO same role swing currently unsupported.
|
||||
const swingRole = id.danceRole === DanceRole.Lark ? DancerDistance.SwingLark : DancerDistance.SwingRobin;
|
||||
|
||||
switch (move.parameters.prefix) {
|
||||
case "none":
|
||||
res.set(id, combine([
|
||||
{
|
||||
beats: 1,
|
||||
endPosition: { ...startPosition, sideCompact: true },
|
||||
endPosition: { ...startPosition, dancerDistance: DancerDistance.Compact },
|
||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||
},
|
||||
{
|
||||
beats: move.beats - 2,
|
||||
endPosition: { ...endPosition, sideCompact: true },
|
||||
endPosition: { ...endPosition, dancerDistance: DancerDistance.Compact },
|
||||
movementPattern: {
|
||||
kind: SemanticAnimationKind.RotateAround,
|
||||
minAmount: 360,
|
||||
|
@ -603,7 +623,7 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
|||
endPosition: {
|
||||
...startWithBalHands,
|
||||
balance: BalanceWeight.Forward,
|
||||
sideCompact: true,
|
||||
dancerDistance: DancerDistance.Compact,
|
||||
},
|
||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||
},
|
||||
|
@ -620,16 +640,18 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
|||
endPosition: {
|
||||
...prevEnd,
|
||||
balance: undefined,
|
||||
dancerDistance: swingRole,
|
||||
},
|
||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||
}),
|
||||
{
|
||||
beats: move.beats - 2 * balancePartBeats - 1,
|
||||
endPosition: { ...endPosition, sideCompact: true },
|
||||
endPosition: { ...endPosition, dancerDistance: DancerDistance.Compact },
|
||||
movementPattern: {
|
||||
kind: SemanticAnimationKind.RotateAround,
|
||||
kind: SemanticAnimationKind.Swing,
|
||||
minAmount: 360,
|
||||
around: startPos.which.leftRightSide(),
|
||||
endFacing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -648,7 +670,7 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
|||
beats: meltdownBeats,
|
||||
endPosition: {
|
||||
...prevEnd,
|
||||
sideCompact: true,
|
||||
dancerDistance: swingRole,
|
||||
},
|
||||
movementPattern: {
|
||||
kind: SemanticAnimationKind.RotateAround,
|
||||
|
@ -660,9 +682,10 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
|||
beats: move.beats - meltdownBeats,
|
||||
endPosition: endPosition,
|
||||
movementPattern: {
|
||||
kind: SemanticAnimationKind.RotateAround,
|
||||
kind: SemanticAnimationKind.Swing,
|
||||
minAmount: 360,
|
||||
around: startPos.which.leftRightSide(),
|
||||
endFacing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
||||
},
|
||||
},
|
||||
], startPos));
|
||||
|
@ -1025,7 +1048,22 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
|||
default:
|
||||
throw "Unexpected facing: " + semantic.facing;
|
||||
}
|
||||
const yAmount = semantic.sideCompact ? 0.75 : 1;
|
||||
let yAmount : number;
|
||||
switch (semantic.dancerDistance) {
|
||||
case DancerDistance.Normal:
|
||||
case undefined:
|
||||
yAmount = 1;
|
||||
break;
|
||||
case DancerDistance.Compact:
|
||||
yAmount = 0.75;
|
||||
break;
|
||||
case DancerDistance.SwingLark:
|
||||
case DancerDistance.SwingRobin:
|
||||
yAmount = 0.5;
|
||||
break;
|
||||
default:
|
||||
throw "Unknown DancerDistance: " + semantic.dancerDistance;
|
||||
}
|
||||
let position: Offset;
|
||||
switch (semantic.which) {
|
||||
case CirclePosition.TopLeft:
|
||||
|
@ -1066,6 +1104,35 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
|||
position.x += setOffset.x;
|
||||
position.y += setOffset.y;
|
||||
|
||||
const isFacingUpOrDown = semantic.facing === Facing.Up || semantic.facing === Facing.Down;
|
||||
if (semantic.dancerDistance === DancerDistance.SwingLark) {
|
||||
if (isFacingUpOrDown) {
|
||||
rotation -= 45;
|
||||
} else {
|
||||
rotation += 45;
|
||||
}
|
||||
|
||||
return {
|
||||
position,
|
||||
rotation,
|
||||
leftArmEnd: { x: 0, y: Math.sqrt(1/2)},
|
||||
rightArmEnd: { x: 0.8, y: 0.5 },
|
||||
};
|
||||
} else if (semantic.dancerDistance === DancerDistance.SwingRobin) {
|
||||
if (isFacingUpOrDown) {
|
||||
rotation += 45;
|
||||
} else {
|
||||
rotation -= 45;
|
||||
}
|
||||
|
||||
return {
|
||||
position,
|
||||
rotation,
|
||||
leftArmEnd: { x: -0.8, y: 0.5 },
|
||||
rightArmEnd: { x: 0, y: Math.sqrt(1/2) },
|
||||
};
|
||||
}
|
||||
|
||||
function processHand(hand: Hand, connection: HandConnection | undefined): Offset | undefined {
|
||||
// TODO Hands. Might need more info? Or need context of nearby dancer SemanticPositions?
|
||||
if (!connection) return undefined;
|
||||
|
@ -1095,8 +1162,8 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
|||
return {
|
||||
position,
|
||||
rotation,
|
||||
leftArmEnd: processHand(Hand.Left, semantic.hands?.get(Hand.Left))
|
||||
rightArmEnd: processHand(Hand.Right, semantic.hands?.get(Hand.Right))
|
||||
leftArmEnd: processHand(Hand.Left, semantic.hands?.get(Hand.Left)),
|
||||
rightArmEnd: processHand(Hand.Right, semantic.hands?.get(Hand.Right)),
|
||||
};
|
||||
default:
|
||||
throw "Unsupported PositionKind: " + semantic.kind;
|
||||
|
@ -1174,7 +1241,7 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
|||
// TODO Parameters: rotation, amount, direction, diagonal?
|
||||
|
||||
if (move.startPosition.kind !== PositionKind.Circle) {
|
||||
throw "DoSoDo must start in a circle: " + JSON.stringify(move);
|
||||
throw "DoSiDo must start in a circle: " + JSON.stringify(move);
|
||||
}
|
||||
const center = CenterOfSideOfSet(move.startPosition.which.leftRightSide(), move.startPosition.setOffset, move.startPosition.lineOffset);
|
||||
return [
|
||||
|
@ -1195,7 +1262,7 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
|||
// TODO Parameters: rotation, amount, direction, diagonal?
|
||||
|
||||
if (move.startPosition.kind !== PositionKind.Circle) {
|
||||
throw "DoSoDo must start in a circle: " + JSON.stringify(move);
|
||||
throw "Rotate must start in a circle: " + JSON.stringify(move);
|
||||
}
|
||||
// TODO Could rotate around other things.
|
||||
const rotateCenter = move.movementPattern.around === "Center"
|
||||
|
@ -1214,6 +1281,36 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
|||
animation.RotationAnimationFacing.Center,
|
||||
),
|
||||
];
|
||||
case SemanticAnimationKind.Swing:
|
||||
// TODO Handle swing.
|
||||
// TODO Rotation
|
||||
// TODO Parameters: rotation, amount, direction, diagonal?
|
||||
|
||||
if (move.startPosition.kind !== PositionKind.Circle || move.endPosition.kind !== PositionKind.Circle) {
|
||||
throw "Swing must start in a circle: " + JSON.stringify(move);
|
||||
}
|
||||
// TODO Could rotate around other things.
|
||||
const rotateSwingCenter = move.movementPattern.around === "Center"
|
||||
? CenterOfSet(move.startPosition.setOffset, move.startPosition.lineOffset)
|
||||
: CenterOfSideOfSet(move.movementPattern.around, move.startPosition.setOffset, move.startPosition.lineOffset);
|
||||
const beforeUnfold = SemanticToSetPosition({...move.endPosition, dancerDistance: move.startPosition.dancerDistance });
|
||||
return [
|
||||
new animation.RotationAnimationSegment(move.beats,
|
||||
startSetPosition,
|
||||
beforeUnfold,
|
||||
move.movementPattern.minAmount,
|
||||
{
|
||||
center: rotateSwingCenter,
|
||||
width: setWidth / 5,
|
||||
height: setHeight / 5,
|
||||
},
|
||||
animation.RotationAnimationFacing.CenterRelative,
|
||||
),
|
||||
new animation.LinearAnimationSegment(move.beats,
|
||||
beforeUnfold,
|
||||
endSetPosition,
|
||||
),
|
||||
];
|
||||
case SemanticAnimationKind.TwirlSwap:
|
||||
// TODO
|
||||
const twirlRotation : number = 180;
|
||||
|
|
|
@ -79,10 +79,15 @@ function createJsonCell(content: any, rowSpan?: number, id?: DancerIdentity) {
|
|||
// from https://stackoverflow.com/a/56150320
|
||||
function replacer(key, value) {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
dataType: 'Map',
|
||||
value: Array.from(value.entries()), // or with spread: value: [...value]
|
||||
};
|
||||
let res = {};
|
||||
for (var entry of value.entries()) {
|
||||
res[entry[0].toString()] = entry[1];
|
||||
}
|
||||
return res;
|
||||
} else if (!value) {
|
||||
return value;
|
||||
} else if (value.enumValue) {
|
||||
return value.enumValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user