UNPKG

universal-siteswap

Version:

A library for parsing, validating, examining and finding transitions between all types of siteswaps

280 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JugglerBeats = exports.JugglerBeat = exports.Throw = exports.allPositions = exports.unfixFraction = exports.fixFraction = exports.Hand = exports.ssToFloat = exports.ssToInt = exports.floatToSS = exports.intToSS = exports.fromLetter = exports.toLetter = void 0; function toLetter(n, base) { return String.fromCharCode(base.charCodeAt(0) + n); } exports.toLetter = toLetter; function fromLetter(letter, base) { return letter.charCodeAt(0) - base.charCodeAt(0); } exports.fromLetter = fromLetter; function intToSS(n) { if (0 <= n && n < 10) { return n.toString(); } else if (10 <= n && n < 36) { return toLetter(n - 10, 'a'); } throw new Error('Only siteswaps up to height 35 are accepted'); } exports.intToSS = intToSS; function floatToSS(n) { var intPart = Math.floor(n); var floatStr = (n - intPart).toLocaleString(undefined, { maximumFractionDigits: 2, }); if (floatStr === '1') { // Rounded up, e.g. was 4.999 return intToSS(intPart + 1); } return floatStr.replace(/^0/, intToSS(intPart)); } exports.floatToSS = floatToSS; function ssToInt(ss) { if (ss.length === 1) { if ('0' <= ss && ss <= '9') { return parseInt(ss); } else if ('a' <= ss && ss <= 'z') { return fromLetter(ss, 'a') + 10; } } throw new Error('Unknown siteswap throw "' + ss + '"'); } exports.ssToInt = ssToInt; function ssToFloat(ss) { var intPart = ssToInt(ss[0]); if (ss.length > 1) { var floatSS = ss.slice(1); if (!/^\.[0-9]+$/.test(floatSS)) { throw new Error('Unknown siteswap throw "' + ss + '"'); } return intPart + parseFloat(floatSS); } return intPart; } exports.ssToFloat = ssToFloat; var Hand; (function (Hand) { Hand[Hand["Right"] = 0] = "Right"; Hand[Hand["Left"] = 1] = "Left"; })(Hand = exports.Hand || (exports.Hand = {})); var EPS = 1e-7; // Lax epsilon to deal with inputs which are only 2 decimal places normally var LAX_EPS = 1e-2; // Deal with fractional throw heights function fixFraction(n, allow36) { if (allow36 === void 0) { allow36 = false; } var whole = Math.floor(n); var frac = n % 1; if (frac === 0) return n; if (allow36 && Math.abs(frac - 0.3) < EPS) return whole + 1 / 3; if (allow36 && Math.abs(frac - 0.6) < EPS) return whole + 2 / 3; for (var i = 2; i < 10; i++) { for (var num = 1; num < i; num++) { if (Math.abs(frac - num / i) < LAX_EPS) { return whole + num / i; } } } return n; } exports.fixFraction = fixFraction; function unfixFraction(n, allow36) { if (allow36 === void 0) { allow36 = false; } var whole = Math.floor(n); var frac = n % 1; if (frac === 0) return n; if (allow36 && Math.abs(frac - 1 / 3) < EPS) return whole + 0.3; if (allow36 && Math.abs(frac - 2 / 3) < EPS) return whole + 0.6; // Otherwise, just round to 2 decimal places return Math.round(n * 100) / 100; } exports.unfixFraction = unfixFraction; function allPositions(numJugglers, period) { var positions = []; for (var j = 0; j < numJugglers; j++) { for (var i = 0; i < period; i++) { for (var _i = 0, _a = [Hand.Right, Hand.Left]; _i < _a.length; _i++) { var hand = _a[_i]; positions.push({ juggler: j, time: i, hand: hand }); } } } return positions; } exports.allPositions = allPositions; // A single throw, has a height, which juggler it's to and whether or not it has an 'x' var Throw = /** @class */ (function () { function Throw(height, x, pass, passTo) { this.dispHeight = height; this.height = fixFraction(height, /*allow36=*/ false); this.x = x; this.pass = pass; this.passTo = passTo; } Throw.prototype.toString = function () { return [ floatToSS(this.dispHeight), this.pass ? 'p' : '', this.x ? 'x' : '', this.passTo != null ? toLetter(this.passTo, 'A') : '', ].join(''); }; Throw.prototype.throwSwapsHands = function () { // Passes swap hands normally ('straight') and with an x they don't ('crossing') if (this.pass) return !this.x; // Normal throws swap hands if they're even with an x or odd with no x return (this.height % 2 === 0) === this.x; }; Throw.prototype.landJuggler = function (startJuggler, numJugglers) { if (this.pass) { return this.passTo != null ? this.passTo : (startJuggler + 1) % numJugglers; } return startJuggler; }; Throw.prototype.clone = function () { return new Throw(this.dispHeight, this.x, this.pass, this.passTo); }; Throw.FromPositions = function (p1, p2) { var height = p2.time - p1.time; var pass = p1.juggler !== p2.juggler; var passTo = pass ? p2.juggler : undefined; var swapsHands = p1.hand === p2.hand; var x = pass ? swapsHands : (height % 2 === 0) !== swapsHands; return new Throw(height, x, pass, passTo); }; return Throw; }()); exports.Throw = Throw; // On a beat, each hand of a juggler can do a multiplex with any number of throws. var JugglerBeat = /** @class */ (function () { function JugglerBeat(LH, RH) { this.LH = LH; this.RH = RH; } JugglerBeat.prototype.isSync = function () { return this.LH.length > 0 && this.RH.length > 0; }; JugglerBeat.prototype.isEmpty = function () { return this.LH.length === 0 && this.RH.length === 0; }; JugglerBeat.prototype.isAsync = function () { return !this.isSync() && !this.isEmpty(); }; JugglerBeat.prototype.maxMultiplex = function () { return Math.max(this.LH.length, this.RH.length); }; JugglerBeat.prototype.hasPass = function () { return this.LH.some(function (th) { return th.pass; }) || this.RH.some(function (th) { return th.pass; }); }; JugglerBeat.prototype.flip = function () { return new JugglerBeat(this.RH, this.LH); }; JugglerBeat.prototype.clone = function () { return new JugglerBeat(this.LH.map(function (th) { return th.clone(); }), this.RH.map(function (th) { return th.clone(); })); }; JugglerBeat.prototype.toString = function (nextHand) { var leftThrows = this.LH.map(function (th) { return th.toString(); }).join(''); var rightThrows = this.RH.map(function (th) { return th.toString(); }).join(''); var left = this.LH.length > 1 ? "[".concat(leftThrows, "]") : leftThrows; var right = this.RH.length > 1 ? "[".concat(rightThrows, "]") : rightThrows; if (left && right) { return "(".concat(left, ",").concat(right, ")"); } else if (left) { return nextHand === Hand.Left ? left : "L".concat(left); } else if (right) { return nextHand === Hand.Right ? right : "R".concat(right); } else { return ''; } }; return JugglerBeat; }()); exports.JugglerBeat = JugglerBeat; var JugglerBeats = /** @class */ (function () { // Constructor deals with multiple types to make the parser cleaner. // If there is a Throw or Throw[], its hand is given as the opposite of the previous hand, resetting to RH at the beginning and after sync beats. function JugglerBeats(beats, repeatFlipped) { var _a; if (repeatFlipped === void 0) { repeatFlipped = false; } this.beats = []; for (var _i = 0, beats_1 = beats; _i < beats_1.length; _i++) { var beat = beats_1[_i]; if (beat instanceof JugglerBeat) { this.beats.push(beat); } else { var prev = this.beats.slice(-1)[0]; // Must be not at the start, and have only RH throwing previously if (prev && prev.isAsync() && prev.RH.length) { this.beats.push(new JugglerBeat(beat, [])); } else { this.beats.push(new JugglerBeat([], beat)); } } } if (repeatFlipped) { (_a = this.beats).push.apply(_a, this.beats.map(function (beat) { return beat.flip(); })); } } JugglerBeats.prototype.flip = function () { return new JugglerBeats(this.beats.map(function (beat) { return beat.flip(); })); }; JugglerBeats.prototype.clone = function () { return new JugglerBeats(this.beats.map(function (beat) { return beat.clone(); })); }; JugglerBeats.prototype.toString = function () { var prevSync = false; var result = ''; var curHand = Hand.Right; for (var _i = 0, _a = this.beats; _i < _a.length; _i++) { var beat = _a[_i]; if (prevSync && !beat.isEmpty()) { result += '!'; } var curString = beat.toString(curHand); if ((curString === 'x' || curString === 'p') && result !== '') { result += ' '; } result += curString; // If we have an empty beat that isn't hidden treat as async 0 if (beat.isEmpty() && !prevSync) { result += '0'; // Keep in line with whatever async was thrown previously curHand = 1 - curHand; } else if (beat.isEmpty()) { curHand = Hand.Right; } else if (beat.isSync()) { curHand = Hand.Right; } else if (beat.isAsync()) { curHand = beat.RH.length ? Hand.Left : Hand.Right; } prevSync = beat.isSync(); } if (prevSync) { result += '!'; } return result; }; return JugglerBeats; }()); exports.JugglerBeats = JugglerBeats; //# sourceMappingURL=common.js.map