diff --git a/README.md b/README.md index 91b27c0..630c413 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ These instructions will get you a copy of the project up and running on your loc ### Prerequisites -- Node.js 8+ +- Node.js 8.3.0+ ### Installing @@ -58,4 +58,4 @@ request.get('https://osu.ppy.sh/osu/1262832').then(osu => { ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details \ No newline at end of file +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details diff --git a/package.json b/package.json index 6bd9e33..c297192 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,12 @@ { "name": "osu-bpdpc", - "version": "0.2.2", + "version": "0.2.3", "description": "Osu beatmap parser, difficulty and performance calculator", "main": "index.js", + "engines": { + "node": ">=8.3.0", + "npm": ">=5.3.0" + }, "scripts": { "test": "mocha src/**/*.spec.js", "lint": "eslint **/*.{js,ts} && tsc" diff --git a/src/Utils/PathApproximator.js b/src/Utils/PathApproximator.js index aa10d21..f3f3b64 100644 --- a/src/Utils/PathApproximator.js +++ b/src/Utils/PathApproximator.js @@ -1,18 +1,18 @@ const Vector2 = require('./Vector2'); +const bezier_tolerance = Math.fround(0.25); +const circular_arc_tolerance = Math.fround(0.1); + +/** + * The amount of pieces to calculate for each control point quadruplet. + */ +const catmull_detail = 50; + /** * Helper methods to approximate a path by interpolating a sequence of control points. */ class PathApproximator { - static #bezier_tolerance = Math.fround(0.25); - static #circular_arc_tolerance = Math.fround(0.1); - - /** - * The amount of pieces to calculate for each control point quadruplet. - */ - static #catmull_detail = 50; - /** * Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing * the control points until their approximation error vanishes below a given threshold. @@ -38,12 +38,12 @@ class PathApproximator while (toFlatten.length > 0) { let parent = toFlatten.pop(); - if (PathApproximator.#bezierIsFlatEnough(parent)) { + if (PathApproximator._bezierIsFlatEnough(parent)) { // If the control points we currently operate on are sufficiently "flat", we use // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation // of the bezier curve represented by our control points, consisting of the same amount // of points as there are control points. - PathApproximator.#bezierApproximate(parent, output, subdivisionBuffer1, subdivisionBuffer2, count); + PathApproximator._bezierApproximate(parent, output, subdivisionBuffer1, subdivisionBuffer2, count); freeBuffers.push(parent); continue; @@ -53,7 +53,7 @@ class PathApproximator // subdividing the curve we are currently operating on. let rightChild = freeBuffers.length > 0 ? freeBuffers.pop() : []; - PathApproximator.#bezierSubdivide(parent, leftChild, rightChild, subdivisionBuffer1, count); + PathApproximator._bezierSubdivide(parent, leftChild, rightChild, subdivisionBuffer1, count); // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. for (let i = 0; i < count; ++i) { @@ -84,9 +84,9 @@ class PathApproximator let v3 = i < controlPointsLength - 1 ? controlPoints[i + 1] : v2.add(v2).subtract(v1); let v4 = i < controlPointsLength - 2 ? controlPoints[i + 2] : v3.add(v3).subtract(v2); - for (let c = 0; c < PathApproximator.#catmull_detail; c++) { - result.push(PathApproximator.#catmullFindPoint(v1, v2, v3, v4, Math.fround(c) / PathApproximator.#catmull_detail)); - result.push(PathApproximator.#catmullFindPoint(v1, v2, v3, v4, Math.fround(c + 1) / PathApproximator.#catmull_detail)); + for (let c = 0; c < catmull_detail; c++) { + result.push(PathApproximator._catmullFindPoint(v1, v2, v3, v4, Math.fround(c) / catmull_detail)); + result.push(PathApproximator._catmullFindPoint(v1, v2, v3, v4, Math.fround(c + 1) / catmull_detail)); } } @@ -157,8 +157,8 @@ class PathApproximator // is: 2 * Math.Acos(1 - TOLERANCE / r) // The special case is required for extremely short sliders where the radius is smaller than // the tolerance. This is a pathological rather than a realistic case. - let amountPoints = 2 * r <= PathApproximator.#circular_arc_tolerance ? 2 - : Math.max(2, Math.ceil(thetaRange / (2 * Math.acos(1 - PathApproximator.#circular_arc_tolerance / r)))); + let amountPoints = 2 * r <= circular_arc_tolerance ? 2 + : Math.max(2, Math.ceil(thetaRange / (2 * Math.acos(1 - circular_arc_tolerance / r)))); let output = []; let fract, theta, o; @@ -196,7 +196,7 @@ class PathApproximator let result = []; - let weights = PathApproximator.#barycentricWeights(controlPoints); + let weights = PathApproximator._barycentricWeights(controlPoints); let minX = controlPoints[0].x; let maxX = controlPoints[0].x; @@ -210,7 +210,7 @@ class PathApproximator for (let i = 0; i < num_steps; i++) { let x = minX + dx / (num_steps - 1) * i; - let y = Math.fround(PathApproximator.#barycentricLagrange(controlPoints, weights, x)); + let y = Math.fround(PathApproximator._barycentricLagrange(controlPoints, weights, x)); result.push(new Vector2(x, y)); } @@ -223,7 +223,7 @@ class PathApproximator * Can be used as a helper function to compute a Lagrange polynomial repeatedly. * @param points An array of coordinates. No two x should be the same. */ - static #barycentricWeights(points) + static _barycentricWeights(points) { let n = points.length; let w = []; @@ -249,7 +249,7 @@ class PathApproximator * @param weights An array of precomputed barycentric weights. * @param time The x coordinate to calculate the basis polynomial for. */ - static #barycentricLagrange(points, weights, time) + static _barycentricLagrange(points, weights, time) { if (points === null || points.Length === 0) { throw new Error("points must contain at least one point"); @@ -285,7 +285,7 @@ class PathApproximator * @param controlPoints The control points to check for flatness. * @returns Whether the control points are flat enough. */ - static #bezierIsFlatEnough(controlPoints) + static _bezierIsFlatEnough(controlPoints) { let sub, sum, scale; @@ -294,7 +294,7 @@ class PathApproximator sub = controlPoints[i - 1].subtract(scale); sum = sub.add(controlPoints[i + 1]); - if (sum.length() ** 2 > PathApproximator.#bezier_tolerance ** 2 * 4) { + if (sum.length() ** 2 > bezier_tolerance ** 2 * 4) { return false; } } @@ -312,7 +312,7 @@ class PathApproximator * @param subdivisionBuffer The first buffer containing the current subdivision state. * @param count The number of control points in the original list. */ - static #bezierSubdivide(controlPoints, l, r, subdivisionBuffer, count) + static _bezierSubdivide(controlPoints, l, r, subdivisionBuffer, count) { let midpoints = subdivisionBuffer; @@ -339,12 +339,12 @@ class PathApproximator * @param subdivisionBuffer1 The first buffer containing the current subdivision state. * @param subdivisionBuffer2 The second buffer containing the current subdivision state. */ - static #bezierApproximate(controlPoints, output, subdivisionBuffer1, subdivisionBuffer2, count) + static _bezierApproximate(controlPoints, output, subdivisionBuffer1, subdivisionBuffer2, count) { let l = subdivisionBuffer2; let r = subdivisionBuffer1; - PathApproximator.#bezierSubdivide(controlPoints, l, r, subdivisionBuffer1, count); + PathApproximator._bezierSubdivide(controlPoints, l, r, subdivisionBuffer1, count); for (let i = 0; i < count - 1; ++i) { l[count + i] = r[i + 1]; @@ -369,7 +369,7 @@ class PathApproximator * @param t The parameter at which to find the point on the spline, in the range [0, 1]. * @returns The point on the spline at t. */ - static #catmullFindPoint(vec1, vec2, vec3, vec4, t) + static _catmullFindPoint(vec1, vec2, vec3, vec4, t) { let t2 = Math.fround(t * t); let t3 = Math.fround(t * t2); diff --git a/src/Utils/SliderPath.js b/src/Utils/SliderPath.js index 678466a..1912974 100644 --- a/src/Utils/SliderPath.js +++ b/src/Utils/SliderPath.js @@ -3,26 +3,18 @@ const Vector2 = require('./Vector2'); class SliderPath { - /** - * The user-set distance of the path. If non-null, will match this value, - * and the path will be shortened/lengthened to match this length. - */ - expectedDistance; - - /** - * The control points of the path. - */ - controlPoints; - - calculatedPath; - cumulativeLength; - #pathCache; - - calculatedLength; constructor(controlPoints, expectedDistance = null) { + /** + * The control points of the path. + */ this.controlPoints = controlPoints.slice(); + + /** + * The user-set distance of the path. If non-null, will match this value, + * and the path will be shortened/lengthened to match this length. + */ this.expectedDistance = expectedDistance; } @@ -31,7 +23,7 @@ class SliderPath */ get distance() { - this.#ensureValid(); + this._ensureValid(); return this.cumulativeLength.length === 0 ? 0 : this.cumulativeLength[this.cumulativeLength.length - 1]; @@ -42,7 +34,7 @@ class SliderPath */ get calculatedDistance() { - this.#ensureValid(); + this._ensureValid(); return this.calculatedLength; } @@ -56,22 +48,22 @@ class SliderPath */ getPathToProgress(path, p0, p1) { - this.#ensureValid(); + this._ensureValid(); - let d0 = this.#progressToDistance(p0); - let d1 = this.#progressToDistance(p1); + let d0 = this._progressToDistance(p0); + let d1 = this._progressToDistance(p1); let i = 0; while (i < this.calculatedPath.length && this.cumulativeLength[i++] < d0); - path = [this.#interpolateVertices(i, d0)]; + path = [this._interpolateVertices(i, d0)]; while (i < this.calculatedPath.length && this.cumulativeLength[i++] <= d1) { path.push(this.calculatedPath[i]); } - path.push(this.#interpolateVertices(i, d1)); + path.push(this._interpolateVertices(i, d1)); } /** @@ -81,26 +73,26 @@ class SliderPath */ positionAt(progress) { - this.#ensureValid(); + this._ensureValid(); - let d = this.#progressToDistance(progress); + let d = this._progressToDistance(progress); - return this.#interpolateVertices(this.#indexOfDistance(d), d); + return this._interpolateVertices(this._indexOfDistance(d), d); } - #ensureValid() + _ensureValid() { - if (this.#pathCache) { + if (this._pathCache) { return; } - this.#calculatePath(); - this.#calculateLength(); + this._calculatePath(); + this._calculateLength(); - this.#pathCache = true; + this._pathCache = true; } - #calculatePath() + _calculatePath() { let controlPointsLength = this.controlPoints.length; @@ -127,7 +119,7 @@ class SliderPath let segmentVertices = vertices.slice(start, i + 1); let segmentType = this.controlPoints[start].type || 'L'; - for (let t of this.#calculateSubPath(segmentVertices, segmentType)) { + for (let t of this._calculateSubPath(segmentVertices, segmentType)) { if (this.calculatedPath.length === 0 || this.calculatedPath[this.calculatedPath.length - 1] != t) { this.calculatedPath.push(t); @@ -139,7 +131,7 @@ class SliderPath } } - #calculateSubPath(subControlPoints, type) + _calculateSubPath(subControlPoints, type) { switch (type) { case 'L': @@ -167,7 +159,7 @@ class SliderPath return PathApproximator.approximateBezier(subControlPoints); } - #calculateLength() + _calculateLength() { this.calculatedLength = 0; this.cumulativeLength = [0]; @@ -212,16 +204,16 @@ class SliderPath } } - #indexOfDistance(d) + _indexOfDistance(d) { - let i = this.#binarySearch(this.cumulativeLength, d); + let i = this._binarySearch(this.cumulativeLength, d); if (i < 0) i = ~i; return i; } - #binarySearch(arr, x) + _binarySearch(arr, x) { let start = 0, mid, end = arr.length - 1; @@ -242,12 +234,12 @@ class SliderPath return Math.floor((start + end) / 2); } - #progressToDistance(progress) + _progressToDistance(progress) { return Math.min(Math.max(progress, 0), 1) * this.distance; } - #interpolateVertices(i, d) + _interpolateVertices(i, d) { if (this.calculatedPath.length === 0) return new Vector2(0, 0);