Compare commits
2 Commits
084088dded
...
f533e3d202
Author | SHA1 | Date | |
---|---|---|---|
f533e3d202 | |||
ea9871d545 |
|
@ -40,6 +40,11 @@
|
||||||
}
|
}
|
||||||
table, tr, th, td {
|
table, tr, th, td {
|
||||||
border: solid black 1px;
|
border: solid black 1px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
table pre {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
||||||
th.Ones.Lark {
|
th.Ones.Lark {
|
||||||
background-color: hsl(0, 80%, 50%);
|
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 { DancerSetPosition, DancersSetPositions, Hand } from "./rendererConstants.js";
|
||||||
import { Offset, leftShoulder, rightShoulder, degreesToRadians, radiansToDegrees } from "./rendererConstants.js";
|
import { Offset, leftShoulder, rightShoulder, degreesToRadians, radiansToDegrees } from "./rendererConstants.js";
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@ export class LinearAnimationSegment extends AnimationSegment {
|
||||||
export enum RotationAnimationFacing {
|
export enum RotationAnimationFacing {
|
||||||
Linear = "Linear", // Default, linearly interpolate.
|
Linear = "Linear", // Default, linearly interpolate.
|
||||||
Center = "Center", // Always face the center.
|
Center = "Center", // Always face the center.
|
||||||
|
CenterRelative = "CenterRelative", // Always face the center.
|
||||||
Forward = "Forward", // Always face the direction of the rotation.
|
Forward = "Forward", // Always face the direction of the rotation.
|
||||||
Backward = "Backward", // Opposite of forward.
|
Backward = "Backward", // Opposite of forward.
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,7 @@ export class RotationAnimationSegment extends AnimationSegment {
|
||||||
private readonly endRotation: number;
|
private readonly endRotation: number;
|
||||||
private readonly center: Offset;
|
private readonly center: Offset;
|
||||||
private readonly facing: RotationAnimationFacing;
|
private readonly facing: RotationAnimationFacing;
|
||||||
|
private readonly startFacing: Rotation;
|
||||||
|
|
||||||
constructor(beats: number, startPosition: DancerSetPosition, endPosition: DancerSetPosition,
|
constructor(beats: number, startPosition: DancerSetPosition, endPosition: DancerSetPosition,
|
||||||
rotation: number, around: RotationAround, facing: RotationAnimationFacing) {
|
rotation: number, around: RotationAround, facing: RotationAnimationFacing) {
|
||||||
|
@ -113,6 +115,7 @@ export class RotationAnimationSegment extends AnimationSegment {
|
||||||
return -radiansToDegrees(radians);
|
return -radiansToDegrees(radians);
|
||||||
}
|
}
|
||||||
this.startRotation = positionToDegrees(startPosition.position);
|
this.startRotation = positionToDegrees(startPosition.position);
|
||||||
|
this.startFacing = startPosition.rotation;
|
||||||
const actualRotation = normalizeRotation(positionToDegrees(endPosition.position) - this.startRotation,
|
const actualRotation = normalizeRotation(positionToDegrees(endPosition.position) - this.startRotation,
|
||||||
rotation);
|
rotation);
|
||||||
this.endRotation = this.startRotation + actualRotation;
|
this.endRotation = this.startRotation + actualRotation;
|
||||||
|
@ -142,6 +145,8 @@ export class RotationAnimationSegment extends AnimationSegment {
|
||||||
switch (this.facing) {
|
switch (this.facing) {
|
||||||
case RotationAnimationFacing.Center:
|
case RotationAnimationFacing.Center:
|
||||||
return degrees - 90;
|
return degrees - 90;
|
||||||
|
case RotationAnimationFacing.CenterRelative:
|
||||||
|
return degrees - this.startRotation + this.startFacing;
|
||||||
case RotationAnimationFacing.Forward:
|
case RotationAnimationFacing.Forward:
|
||||||
return degrees + 180;
|
return degrees + 180;
|
||||||
case RotationAnimationFacing.Backward:
|
case RotationAnimationFacing.Backward:
|
||||||
|
|
|
@ -160,6 +160,14 @@ enum BalanceWeight {
|
||||||
Left = "Left",
|
Left = "Left",
|
||||||
Right = "Right",
|
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 = {
|
type SemanticPosition = {
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
setOffset?: number,
|
setOffset?: number,
|
||||||
|
@ -168,7 +176,7 @@ type SemanticPosition = {
|
||||||
facing: Facing,
|
facing: Facing,
|
||||||
hands?: Map<Hand, HandConnection>,
|
hands?: Map<Hand, HandConnection>,
|
||||||
balance?: BalanceWeight,
|
balance?: BalanceWeight,
|
||||||
sideCompact?: boolean, // if true, have moved in for swing/allemande/etc.
|
dancerDistance?: DancerDistance,
|
||||||
} | {
|
} | {
|
||||||
kind: PositionKind.ShortLines,
|
kind: PositionKind.ShortLines,
|
||||||
setOffset?: number,
|
setOffset?: number,
|
||||||
|
@ -205,8 +213,10 @@ enum SemanticAnimationKind {
|
||||||
// California twirl, box the gnat, etc.
|
// California twirl, box the gnat, etc.
|
||||||
TwirlSwap = "TwirlSwap",
|
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",
|
RotateAround = "RotateAround",
|
||||||
|
|
||||||
|
Swing = "Swing",
|
||||||
}
|
}
|
||||||
type SemanticAnimation = {
|
type SemanticAnimation = {
|
||||||
kind: SemanticAnimationKind.StandStill,
|
kind: SemanticAnimationKind.StandStill,
|
||||||
|
@ -235,6 +245,14 @@ type SemanticAnimation = {
|
||||||
minAmount: number,
|
minAmount: number,
|
||||||
|
|
||||||
around: CircleSide | "Center",
|
around: CircleSide | "Center",
|
||||||
|
} | {
|
||||||
|
kind: SemanticAnimationKind.Swing,
|
||||||
|
|
||||||
|
minAmount: number,
|
||||||
|
|
||||||
|
around: CircleSide | "Center",
|
||||||
|
|
||||||
|
endFacing: Facing,
|
||||||
} | {
|
} | {
|
||||||
kind: SemanticAnimationKind.TwirlSwap,
|
kind: SemanticAnimationKind.TwirlSwap,
|
||||||
}
|
}
|
||||||
|
@ -556,23 +574,25 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
: startPos.which.swapUpAndDown(),
|
: startPos.which.swapUpAndDown(),
|
||||||
facing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
facing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
||||||
balance: undefined,
|
balance: undefined,
|
||||||
sideCompact: undefined,
|
dancerDistance: undefined,
|
||||||
hands: new Map<Hand, HandConnection>([id.danceRole === DanceRole.Lark
|
hands: new Map<Hand, HandConnection>([id.danceRole === DanceRole.Lark
|
||||||
? [Hand.Right, { to: HandTo.DancerRight, hand: Hand.Left }]
|
? [Hand.Right, { to: HandTo.DancerRight, hand: Hand.Left }]
|
||||||
: [Hand.Left, { to: HandTo.DancerLeft, hand: Hand.Right }]]),
|
: [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) {
|
switch (move.parameters.prefix) {
|
||||||
case "none":
|
case "none":
|
||||||
res.set(id, combine([
|
res.set(id, combine([
|
||||||
{
|
{
|
||||||
beats: 1,
|
beats: 1,
|
||||||
endPosition: { ...startPosition, sideCompact: true },
|
endPosition: { ...startPosition, dancerDistance: DancerDistance.Compact },
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
beats: move.beats - 2,
|
beats: move.beats - 2,
|
||||||
endPosition: { ...endPosition, sideCompact: true },
|
endPosition: { ...endPosition, dancerDistance: DancerDistance.Compact },
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.RotateAround,
|
||||||
minAmount: 360,
|
minAmount: 360,
|
||||||
|
@ -603,7 +623,7 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...startWithBalHands,
|
...startWithBalHands,
|
||||||
balance: BalanceWeight.Forward,
|
balance: BalanceWeight.Forward,
|
||||||
sideCompact: true,
|
dancerDistance: DancerDistance.Compact,
|
||||||
},
|
},
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||||
},
|
},
|
||||||
|
@ -620,16 +640,18 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...prevEnd,
|
...prevEnd,
|
||||||
balance: undefined,
|
balance: undefined,
|
||||||
|
dancerDistance: swingRole,
|
||||||
},
|
},
|
||||||
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
movementPattern: { kind: SemanticAnimationKind.Linear, },
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
beats: move.beats - 2 * balancePartBeats - 1,
|
beats: move.beats - 2 * balancePartBeats - 1,
|
||||||
endPosition: { ...endPosition, sideCompact: true },
|
endPosition: { ...endPosition, dancerDistance: DancerDistance.Compact },
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.Swing,
|
||||||
minAmount: 360,
|
minAmount: 360,
|
||||||
around: startPos.which.leftRightSide(),
|
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,
|
beats: meltdownBeats,
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...prevEnd,
|
...prevEnd,
|
||||||
sideCompact: true,
|
dancerDistance: swingRole,
|
||||||
},
|
},
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.RotateAround,
|
||||||
|
@ -660,9 +682,10 @@ function moveAsLowLevelMoves(move: Move, startingPos: Map<DancerIdentity, Semant
|
||||||
beats: move.beats - meltdownBeats,
|
beats: move.beats - meltdownBeats,
|
||||||
endPosition: endPosition,
|
endPosition: endPosition,
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
kind: SemanticAnimationKind.RotateAround,
|
kind: SemanticAnimationKind.Swing,
|
||||||
minAmount: 360,
|
minAmount: 360,
|
||||||
around: startPos.which.leftRightSide(),
|
around: startPos.which.leftRightSide(),
|
||||||
|
endFacing: startPos.which.leftRightSide() === CircleSide.Left ? Facing.Right : Facing.Left,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
], startPos));
|
], startPos));
|
||||||
|
@ -1025,7 +1048,22 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
||||||
default:
|
default:
|
||||||
throw "Unexpected facing: " + semantic.facing;
|
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;
|
let position: Offset;
|
||||||
switch (semantic.which) {
|
switch (semantic.which) {
|
||||||
case CirclePosition.TopLeft:
|
case CirclePosition.TopLeft:
|
||||||
|
@ -1066,6 +1104,35 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
||||||
position.x += setOffset.x;
|
position.x += setOffset.x;
|
||||||
position.y += setOffset.y;
|
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 {
|
function processHand(hand: Hand, connection: HandConnection | undefined): Offset | undefined {
|
||||||
// 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;
|
||||||
|
@ -1095,8 +1162,8 @@ function SemanticToSetPosition(semantic: SemanticPosition): DancerSetPosition {
|
||||||
return {
|
return {
|
||||||
position,
|
position,
|
||||||
rotation,
|
rotation,
|
||||||
leftArmEnd: processHand(Hand.Left, semantic.hands?.get(Hand.Left))
|
leftArmEnd: processHand(Hand.Left, semantic.hands?.get(Hand.Left)),
|
||||||
rightArmEnd: processHand(Hand.Right, semantic.hands?.get(Hand.Right))
|
rightArmEnd: processHand(Hand.Right, semantic.hands?.get(Hand.Right)),
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
throw "Unsupported PositionKind: " + semantic.kind;
|
throw "Unsupported PositionKind: " + semantic.kind;
|
||||||
|
@ -1174,7 +1241,7 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
||||||
// TODO Parameters: rotation, amount, direction, diagonal?
|
// TODO Parameters: rotation, amount, direction, diagonal?
|
||||||
|
|
||||||
if (move.startPosition.kind !== PositionKind.Circle) {
|
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);
|
const center = CenterOfSideOfSet(move.startPosition.which.leftRightSide(), move.startPosition.setOffset, move.startPosition.lineOffset);
|
||||||
return [
|
return [
|
||||||
|
@ -1195,7 +1262,7 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
||||||
// TODO Parameters: rotation, amount, direction, diagonal?
|
// TODO Parameters: rotation, amount, direction, diagonal?
|
||||||
|
|
||||||
if (move.startPosition.kind !== PositionKind.Circle) {
|
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.
|
// TODO Could rotate around other things.
|
||||||
const rotateCenter = move.movementPattern.around === "Center"
|
const rotateCenter = move.movementPattern.around === "Center"
|
||||||
|
@ -1214,6 +1281,36 @@ export function animateLowLevelMove(move: LowLevelMove): animation.AnimationSegm
|
||||||
animation.RotationAnimationFacing.Center,
|
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:
|
case SemanticAnimationKind.TwirlSwap:
|
||||||
// TODO
|
// TODO
|
||||||
const twirlRotation : number = 180;
|
const twirlRotation : number = 180;
|
||||||
|
|
|
@ -79,10 +79,15 @@ function createJsonCell(content: any, rowSpan?: number, id?: DancerIdentity) {
|
||||||
// from https://stackoverflow.com/a/56150320
|
// from https://stackoverflow.com/a/56150320
|
||||||
function replacer(key, value) {
|
function replacer(key, value) {
|
||||||
if (value instanceof Map) {
|
if (value instanceof Map) {
|
||||||
return {
|
let res = {};
|
||||||
dataType: 'Map',
|
for (var entry of value.entries()) {
|
||||||
value: Array.from(value.entries()), // or with spread: value: [...value]
|
res[entry[0].toString()] = entry[1];
|
||||||
};
|
}
|
||||||
|
return res;
|
||||||
|
} else if (!value) {
|
||||||
|
return value;
|
||||||
|
} else if (value.enumValue) {
|
||||||
|
return value.enumValue;
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user