enum DanceRoleEnum { Lark = "Lark", Robin = "Robin", } export class DanceRole { public static readonly Lark = new DanceRole(DanceRoleEnum.Lark); public static readonly Robin = new DanceRole(DanceRoleEnum.Robin); private readonly enumValue: DanceRoleEnum; private constructor(danceRole: DanceRoleEnum) { this.enumValue = danceRole; } public opposite() : DanceRole { return this.enumValue === DanceRoleEnum.Lark ? DanceRole.Robin : DanceRole.Lark; } public toString() : string { return this.enumValue.toString(); } } enum CoupleRoleEnum { Ones = "Ones", // aka "Actives" Twos = "Twos", // aka "Inactives" } export class CoupleRole { public static readonly Ones = new CoupleRole(CoupleRoleEnum.Ones); public static readonly Twos = new CoupleRole(CoupleRoleEnum.Twos); readonly enumValue: CoupleRoleEnum; private constructor(danceRole: CoupleRoleEnum) { this.enumValue = danceRole; } public opposite() : CoupleRole { return this.enumValue === CoupleRoleEnum.Ones ? CoupleRole.Twos : CoupleRole.Ones; } public toString() : string { return this.enumValue.toString(); } } export type StartFormation = "improper" | "Becket" | "Becket ccw" | "Sawtooth Becket"; export enum Rotation { Up = 180, Down = 0, Left = 270, Right = 90, } export function normalizeRotation(rotation: number, minRotation: number | undefined) { if (minRotation === undefined) { if (rotation > 180) { rotation = rotation - 360; } else if (rotation < -180) { rotation = rotation + 360; } } else if (minRotation === 0) { if (rotation !== 0) { throw "Expected zero rotation" } } else if (minRotation > 0) { while (rotation <= 0 || rotation < minRotation - 180) rotation += 360; } else /* minRotation < 0 */ { while (rotation >= 0 || rotation > minRotation + 180) rotation -= 360; } return rotation; } export class DancerIdentity { public static readonly OnesLark = new DancerIdentity(DanceRole.Lark, CoupleRole.Ones); public static readonly OnesRobin = new DancerIdentity(DanceRole.Robin, CoupleRole.Ones); public static readonly TwosLark = new DancerIdentity(DanceRole.Lark, CoupleRole.Twos); public static readonly TwosRobin = new DancerIdentity(DanceRole.Robin, CoupleRole.Twos); public readonly danceRole: DanceRole; public readonly coupleRole: CoupleRole; // TODO better name for this? private constructor(danceRole: DanceRole, coupleRole: CoupleRole) { this.danceRole = danceRole; this.coupleRole = coupleRole; } public static get(danceRole: DanceRole, coupleRole: CoupleRole) : DancerIdentity { if (coupleRole === CoupleRole.Ones) { if (danceRole === DanceRole.Lark) { return DancerIdentity.OnesLark; } else { return DancerIdentity.OnesRobin; } } else { if (danceRole === DanceRole.Lark) { return DancerIdentity.TwosLark; } else { return DancerIdentity.TwosRobin; } } } public neighbor() : DancerIdentity { return DancerIdentity.get(this.danceRole.opposite(), this.coupleRole.opposite()); } public partner() : DancerIdentity { return DancerIdentity.get(this.danceRole.opposite(), this.coupleRole); } public oppositeSameRole() : DancerIdentity { return DancerIdentity.get(this.danceRole, this.coupleRole.opposite()); } public asExtendedDancerIdentity(relativeSet?: number, relativeLine?: number) : ExtendedDancerIdentity { return { setIdentity: this, relativeSet: relativeSet ?? 0, relativeLine: relativeLine ?? 0, } } public toString() : string { return this.danceRole.toString() + "!" + this.coupleRole.toString(); } public static all() : DancerIdentity[] { return [ DancerIdentity.OnesLark, DancerIdentity.OnesRobin, DancerIdentity.TwosLark, DancerIdentity.TwosRobin, ] } } export interface ExtendedDancerIdentity { // Identity within own set. setIdentity: DancerIdentity, // 0 = focused line, -1 = line to left, +1 = line to right, ... relativeLine: number, // 0 = focused set, +1 = next set up, -1 = next set down. relativeSet: number, }