forked from perelman/contra-renderer
Daniel Perelman
9fbf7d18ac
Currently just copied over the existing code and applied the quick fixes to get it to compile. Each move should be refactored to be handle its parameters earlier where applicable. But variants support should probably be added first so both refactors can happen together.
465 lines
15 KiB
TypeScript
465 lines
15 KiB
TypeScript
import { DancerIdentity, Rotation } from "./danceCommon.js";
|
|
import { Hand } from "./rendererConstants.js";
|
|
|
|
export enum PositionKind {
|
|
Circle = "Circle",
|
|
ShortLines = "ShortLines",
|
|
};
|
|
// Which of the four corners of a circle.
|
|
// Note that "Left" is the left side of the set, not left from the dancer's PoV,
|
|
// so, in improper, the Robin starts in the BottomLeft.
|
|
enum CirclePositionEnum {
|
|
TopLeft = "TopLeft",
|
|
BottomLeft = "BottomLeft",
|
|
BottomRight = "BottomRight",
|
|
TopRight = "TopRight",
|
|
}
|
|
export enum CircleSide {
|
|
Top = "Top",
|
|
Bottom = "Bottom",
|
|
Left = "Left",
|
|
Right = "Right",
|
|
}
|
|
export type CircleSideOrCenter = CircleSide | "Center";
|
|
export class CirclePosition {
|
|
public static readonly TopLeft = new CirclePosition(CirclePositionEnum.TopLeft);
|
|
public static readonly BottomLeft = new CirclePosition(CirclePositionEnum.BottomLeft);
|
|
public static readonly BottomRight = new CirclePosition(CirclePositionEnum.BottomRight);
|
|
public static readonly TopRight = new CirclePosition(CirclePositionEnum.TopRight);
|
|
|
|
private readonly enumValue: CirclePositionEnum;
|
|
|
|
private constructor(enumValue: CirclePositionEnum) {
|
|
this.enumValue = enumValue;
|
|
}
|
|
|
|
public static fromSides(leftRightSide: CircleSide.Left | CircleSide.Right, topBottomSide: CircleSide.Bottom | CircleSide.Top) {
|
|
return leftRightSide === CircleSide.Left
|
|
? topBottomSide === CircleSide.Top
|
|
? CirclePosition.TopLeft
|
|
: CirclePosition.BottomLeft
|
|
: topBottomSide === CircleSide.Top
|
|
? CirclePosition.TopRight
|
|
: CirclePosition.BottomRight;
|
|
}
|
|
|
|
private static enumValueToNumber(enumValue: CirclePositionEnum) : number {
|
|
switch (enumValue) {
|
|
case CirclePositionEnum.TopLeft:
|
|
return 0;
|
|
case CirclePositionEnum.BottomLeft:
|
|
return 1;
|
|
case CirclePositionEnum.BottomRight:
|
|
return 2;
|
|
case CirclePositionEnum.TopRight:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
private static numberToEnumValue(num: number) : CirclePositionEnum {
|
|
if (num < 0 || num > 3) {
|
|
num %= 4;
|
|
if (num < 0) num += 4;
|
|
}
|
|
|
|
return [
|
|
CirclePositionEnum.TopLeft,
|
|
CirclePositionEnum.BottomLeft,
|
|
CirclePositionEnum.BottomRight,
|
|
CirclePositionEnum.TopRight,
|
|
][num];
|
|
}
|
|
|
|
private static get(enumValue: CirclePositionEnum) : CirclePosition {
|
|
return [
|
|
CirclePosition.TopLeft,
|
|
CirclePosition.BottomLeft,
|
|
CirclePosition.BottomRight,
|
|
CirclePosition.TopRight,
|
|
][this.enumValueToNumber(enumValue)];
|
|
}
|
|
|
|
public circleRight(places?: number) : CirclePosition {
|
|
return CirclePosition.get(CirclePosition.numberToEnumValue(CirclePosition.enumValueToNumber(this.enumValue) - (places ?? 1)));
|
|
}
|
|
|
|
public circleLeft(places?: number) : CirclePosition {
|
|
return CirclePosition.get(CirclePosition.numberToEnumValue(CirclePosition.enumValueToNumber(this.enumValue) + (places ?? 1)));
|
|
}
|
|
|
|
public swapAcross() : CirclePosition {
|
|
return [
|
|
CirclePosition.TopRight,
|
|
CirclePosition.BottomRight,
|
|
CirclePosition.BottomLeft,
|
|
CirclePosition.TopLeft,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public swapUpAndDown() : CirclePosition {
|
|
return [
|
|
CirclePosition.BottomLeft,
|
|
CirclePosition.TopLeft,
|
|
CirclePosition.TopRight,
|
|
CirclePosition.BottomRight,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public toShortLines(slideTo: Hand) : ShortLinesPosition {
|
|
return slideTo === Hand.Left
|
|
? [
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.FarRight,
|
|
ShortLinesPosition.MiddleRight,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)]
|
|
: [
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.MiddleRight,
|
|
ShortLinesPosition.FarRight,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public unfoldToShortLines(center: CircleSide.Bottom | CircleSide.Top) : ShortLinesPosition {
|
|
return center === CircleSide.Bottom
|
|
? [
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.MiddleRight,
|
|
ShortLinesPosition.FarRight,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)]
|
|
: [
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.FarRight,
|
|
ShortLinesPosition.MiddleRight,
|
|
][CirclePosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public swapDiagonal() : CirclePosition {
|
|
return this.swapAcross().swapUpAndDown();
|
|
}
|
|
|
|
public facingCenterRotation() : Rotation {
|
|
return (45 + 90 * CirclePosition.enumValueToNumber(this.enumValue)) % 360;
|
|
}
|
|
|
|
public topBottomSide() : CircleSide.Top | CircleSide.Bottom {
|
|
return this.enumValue === CirclePositionEnum.TopLeft || this.enumValue === CirclePositionEnum.TopRight
|
|
? CircleSide.Top
|
|
: CircleSide.Bottom;
|
|
}
|
|
|
|
public leftRightSide() : CircleSide.Left | CircleSide.Right {
|
|
return this.enumValue === CirclePositionEnum.TopLeft || this.enumValue === CirclePositionEnum.BottomLeft
|
|
? CircleSide.Left
|
|
: CircleSide.Right;
|
|
}
|
|
|
|
public isTop() : boolean {
|
|
return this.topBottomSide() === CircleSide.Top;
|
|
}
|
|
|
|
public isLeft() : boolean {
|
|
return this.leftRightSide() === CircleSide.Left;
|
|
}
|
|
|
|
public isOnLeftLookingAcross() : boolean {
|
|
return this.enumValue === CirclePositionEnum.TopRight || this.enumValue === CirclePositionEnum.BottomLeft;
|
|
}
|
|
|
|
public isOnLeftLookingUpAndDown() : boolean {
|
|
return this.enumValue === CirclePositionEnum.TopLeft || this.enumValue === CirclePositionEnum.BottomRight;
|
|
}
|
|
|
|
public facingAcross() : Facing.Left | Facing.Right {
|
|
return this.isLeft() ? Facing.Right : Facing.Left;
|
|
}
|
|
|
|
public facingOut() : Facing.Left | Facing.Right {
|
|
return this.isLeft() ? Facing.Left : Facing.Right;
|
|
}
|
|
|
|
public facingUpOrDown() : Facing.Up | Facing.Down {
|
|
return this.isTop() ? Facing.Down : Facing.Up;
|
|
}
|
|
|
|
public toString() : string {
|
|
return this.enumValue.toString();
|
|
}
|
|
}
|
|
|
|
enum ShortLinesPositionEnum {
|
|
FarLeft = "FarLeft",
|
|
MiddleLeft = "MiddleLeft",
|
|
MiddleRight = "MiddleRight",
|
|
FarRight = "FarRight",
|
|
}
|
|
export class ShortLinesPosition {
|
|
public static readonly FarLeft = new ShortLinesPosition(ShortLinesPositionEnum.FarLeft);
|
|
public static readonly MiddleLeft = new ShortLinesPosition(ShortLinesPositionEnum.MiddleLeft);
|
|
public static readonly MiddleRight = new ShortLinesPosition(ShortLinesPositionEnum.MiddleRight);
|
|
public static readonly FarRight = new ShortLinesPosition(ShortLinesPositionEnum.FarRight);
|
|
|
|
private readonly enumValue: ShortLinesPositionEnum;
|
|
|
|
private constructor(enumValue: ShortLinesPositionEnum) {
|
|
this.enumValue = enumValue;
|
|
}
|
|
|
|
private static enumValueToNumber(enumValue: ShortLinesPositionEnum) : number {
|
|
switch (enumValue) {
|
|
case ShortLinesPositionEnum.FarLeft:
|
|
return 0;
|
|
case ShortLinesPositionEnum.MiddleLeft:
|
|
return 1;
|
|
case ShortLinesPositionEnum.MiddleRight:
|
|
return 2;
|
|
case ShortLinesPositionEnum.FarRight:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
private static numberToEnumValue(num: number) : ShortLinesPositionEnum {
|
|
if (num < 0 || num > 3) {
|
|
num %= 4;
|
|
if (num < 0) num += 4;
|
|
}
|
|
|
|
return [
|
|
ShortLinesPositionEnum.FarLeft,
|
|
ShortLinesPositionEnum.MiddleLeft,
|
|
ShortLinesPositionEnum.MiddleRight,
|
|
ShortLinesPositionEnum.FarRight,
|
|
][num];
|
|
}
|
|
|
|
private static get(enumValue: ShortLinesPositionEnum) : ShortLinesPosition {
|
|
return [
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.MiddleRight,
|
|
ShortLinesPosition.FarRight,
|
|
][this.enumValueToNumber(enumValue)];
|
|
}
|
|
|
|
public swapOnSide() : ShortLinesPosition {
|
|
return [
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.FarLeft,
|
|
ShortLinesPosition.FarRight,
|
|
ShortLinesPosition.MiddleRight,
|
|
][ShortLinesPosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public swapSides() : ShortLinesPosition {
|
|
return [
|
|
ShortLinesPosition.FarRight,
|
|
ShortLinesPosition.MiddleRight,
|
|
ShortLinesPosition.MiddleLeft,
|
|
ShortLinesPosition.FarLeft,
|
|
][ShortLinesPosition.enumValueToNumber(this.enumValue)];
|
|
}
|
|
|
|
public shift(dir: Hand, facing: Facing.Up | Facing.Down): ShortLinesPosition {
|
|
const shift = (dir === Hand.Left) === (facing === Facing.Down) ? -1 : +1;
|
|
const newNum = ShortLinesPosition.enumValueToNumber(this.enumValue) + shift;
|
|
if (newNum < 0 || newNum > 3) {
|
|
throw new Error("Invalid shift: " + this + " facing " + facing + " to " + dir + ".");
|
|
}
|
|
|
|
return ShortLinesPosition.get(ShortLinesPosition.numberToEnumValue(newNum));
|
|
}
|
|
|
|
public isMiddle() : boolean {
|
|
return this.enumValue === ShortLinesPositionEnum.MiddleRight || this.enumValue === ShortLinesPositionEnum.MiddleLeft;
|
|
}
|
|
|
|
public leftRightSide() : CircleSide.Left | CircleSide.Right {
|
|
return this.enumValue === ShortLinesPositionEnum.FarLeft || this.enumValue === ShortLinesPositionEnum.MiddleLeft
|
|
? CircleSide.Left
|
|
: CircleSide.Right;
|
|
}
|
|
|
|
public isLeft() : boolean {
|
|
return this.leftRightSide() === CircleSide.Left;
|
|
}
|
|
|
|
// Of the two positions on the same leftRightSide() is this the one further to the left?
|
|
public isLeftOfSide() : boolean {
|
|
return this.enumValue === ShortLinesPositionEnum.FarLeft || this.enumValue === ShortLinesPositionEnum.MiddleRight;
|
|
}
|
|
|
|
public facingSide() : Facing.Left | Facing.Right {
|
|
return this.isLeft() === this.isMiddle() ? Facing.Left : Facing.Right;
|
|
}
|
|
|
|
public toString() : string {
|
|
return this.enumValue.toString();
|
|
}
|
|
}
|
|
|
|
export enum Facing {
|
|
CenterOfCircle = "CenterOfCircle",
|
|
// for star left/right
|
|
LeftInCircle = "LeftInCircle",
|
|
RightInCircle = "RightInCircle",
|
|
Up = "Up",
|
|
Down = "Down",
|
|
Left = "Left",
|
|
Right = "Right",
|
|
}
|
|
export function oppositeFacing(facing: Facing.Up | Facing.Down | Facing.Left | Facing.Right | Facing.LeftInCircle | Facing.RightInCircle) {
|
|
switch (facing) {
|
|
case Facing.LeftInCircle:
|
|
return Facing.RightInCircle;
|
|
case Facing.RightInCircle:
|
|
return Facing.LeftInCircle;
|
|
case Facing.Up:
|
|
return Facing.Down;
|
|
case Facing.Down:
|
|
return Facing.Up;
|
|
case Facing.Left:
|
|
return Facing.Right;
|
|
case Facing.Right:
|
|
return Facing.Left;
|
|
}
|
|
}
|
|
|
|
export enum StarGrip {
|
|
HandsAcross = "HandsAcross",
|
|
WristGrip = "WristGrip",
|
|
}
|
|
export enum HandTo {
|
|
LeftInCircle = "LeftInCircle",
|
|
RightInCircle = "RightInCircle",
|
|
AcrossCircle = "AcrossCircle",
|
|
DiagonalAcrossCircle = "DiagonalAcrossCircle",
|
|
LeftDiagonalAcrossCircle = "LeftDiagonalAcrossCircle",
|
|
RightDiagonalAcrossCircle = "RightDiagonalAcrossCircle",
|
|
DancerForward = "DancerForward",
|
|
DancerLeft = "DancerLeft",
|
|
DancerRight = "DancerRight",
|
|
}
|
|
export interface HandConnection {
|
|
to: HandTo,
|
|
hand: Hand,
|
|
}
|
|
export enum BalanceWeight {
|
|
Forward = "Forward",
|
|
Backward = "Backward",
|
|
Left = "Left",
|
|
Right = "Right",
|
|
}
|
|
export 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",
|
|
}
|
|
export enum LongLines {
|
|
// Walked forward into center.
|
|
Forward = "Forward",
|
|
|
|
// Only a little offset (has walked almost all the way from the other side after a give and take).
|
|
Near = "Near",
|
|
|
|
// Actually in center. May be slightly offset for wavy lines.
|
|
Center = "Center",
|
|
}
|
|
export type SemanticPosition = {
|
|
kind: PositionKind.Circle,
|
|
setOffset?: number,
|
|
lineOffset?: number,
|
|
which: CirclePosition,
|
|
facing: Facing,
|
|
hands?: Map<Hand, HandConnection>,
|
|
balance?: BalanceWeight,
|
|
dancerDistance?: DancerDistance,
|
|
longLines?: LongLines,
|
|
} | {
|
|
kind: PositionKind.ShortLines,
|
|
setOffset?: number,
|
|
lineOffset?: number,
|
|
which: ShortLinesPosition,
|
|
facing: Facing,
|
|
hands?: Map<Hand, HandConnection>,
|
|
balance?: BalanceWeight,
|
|
dancerDistance?: DancerDistance,
|
|
longLines?: undefined,
|
|
};
|
|
|
|
export const handsInCircle = new Map<Hand, HandConnection>([
|
|
[Hand.Left, {
|
|
to: HandTo.LeftInCircle,
|
|
hand: Hand.Right,
|
|
}],
|
|
[Hand.Right, {
|
|
to: HandTo.RightInCircle,
|
|
hand: Hand.Left,
|
|
}],
|
|
]);
|
|
export const handsFourImproper: Map<DancerIdentity, SemanticPosition & { kind: PositionKind.Circle }> = new Map<DancerIdentity, SemanticPosition & { kind: PositionKind.Circle }>([
|
|
[DancerIdentity.OnesLark, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.TopLeft,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.OnesRobin, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.TopRight,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.TwosLark, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.BottomRight,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
[DancerIdentity.TwosRobin, {
|
|
kind: PositionKind.Circle,
|
|
which: CirclePosition.BottomLeft,
|
|
facing: Facing.CenterOfCircle,
|
|
hands: handsInCircle,
|
|
}],
|
|
]);
|
|
|
|
export function handsInShortLine({ which, facing, wavy }: { which: ShortLinesPosition; facing: Facing.Up | Facing.Down; wavy: boolean; }): Map<Hand, HandConnection> {
|
|
return which.isMiddle() ? new Map<Hand, HandConnection>([
|
|
[Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }],
|
|
[Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }],
|
|
]) : new Map<Hand, HandConnection>([
|
|
which.isLeft() === (facing === Facing.Up)
|
|
? [Hand.Left, { hand: wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }]
|
|
: [Hand.Right, { hand: wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }]
|
|
]);
|
|
}
|
|
export function handsInLine(args: { wavy: boolean, which: ShortLinesPosition | CirclePosition, facing?: Facing }) {
|
|
if (args.which instanceof ShortLinesPosition && (args.facing === Facing.Up || args.facing === Facing.Down)) {
|
|
return handsInShortLine({ wavy: args.wavy, which: args.which, facing: args.facing });
|
|
} else {
|
|
return new Map<Hand, HandConnection>([
|
|
[Hand.Left, { hand: args.wavy ? Hand.Right : Hand.Left, to: HandTo.DancerLeft }],
|
|
[Hand.Right, { hand: args.wavy ? Hand.Left : Hand.Right, to: HandTo.DancerRight }],
|
|
]);
|
|
}
|
|
}
|
|
export 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 }]
|
|
]);
|
|
}
|
|
export 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 }]
|
|
]);
|
|
} |