@rian8337/osu-base 3.0.0-beta.4 → 3.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2797,6 +2797,49 @@ class Precision {
2797
2797
  return (this.almostEqualsNumber(vec1.x, vec2.x, acceptableDifference) &&
2798
2798
  this.almostEqualsNumber(vec1.y, vec2.y, acceptableDifference));
2799
2799
  }
2800
+ /**
2801
+ * Checks whether two real numbers are almost equal.
2802
+ *
2803
+ * @param a The first number.
2804
+ * @param b The second number.
2805
+ * @param maximumError The accuracy required for being almost equal. Defaults to `10 * 2^(-53)`.
2806
+ * @returns Whether the two values differ by no more than 10 * 2^(-52).
2807
+ */
2808
+ static almostEqualRelative(a, b, maximumError = 10 * Math.pow(2, -53)) {
2809
+ return this.almostEqualNormRelative(a, b, a - b, maximumError);
2810
+ }
2811
+ /**
2812
+ * Compares two numbers and determines if they are equal within the specified maximum error.
2813
+ *
2814
+ * @param a The norm of the first value (can be negative).
2815
+ * @param b The norm of the second value (can be negative).
2816
+ * @param diff The norm of the difference of the two values (can be negative).
2817
+ * @param maximumError The accuracy required for being almost equal.
2818
+ * @returns Whether both numbers are almost equal up to the specified maximum error.
2819
+ */
2820
+ static almostEqualNormRelative(a, b, diff, maximumError) {
2821
+ // If A or B are infinity (positive or negative) then
2822
+ // only return true if they are exactly equal to each other -
2823
+ // that is, if they are both infinities of the same sign.
2824
+ if (!Number.isFinite(a) || !Number.isFinite(b)) {
2825
+ return a === b;
2826
+ }
2827
+ // If A or B are a NAN, return false. NANs are equal to nothing,
2828
+ // not even themselves.
2829
+ if (Number.isNaN(a) || Number.isNaN(b)) {
2830
+ return false;
2831
+ }
2832
+ // If one is almost zero, fall back to absolute equality.
2833
+ const doublePrecision = Math.pow(2, -53);
2834
+ if (Math.abs(a) < doublePrecision || Math.abs(b) < doublePrecision) {
2835
+ return Math.abs(diff) < maximumError;
2836
+ }
2837
+ if ((a === 0 && Math.abs(b) < maximumError) ||
2838
+ (b === 0 && Math.abs(a) < maximumError)) {
2839
+ return true;
2840
+ }
2841
+ return (Math.abs(diff) < maximumError * Math.max(Math.abs(a), Math.abs(b)));
2842
+ }
2800
2843
  }
2801
2844
 
2802
2845
  /**
@@ -6200,6 +6243,236 @@ class BeatmapEncoder extends Encoder {
6200
6243
  }
6201
6244
  }
6202
6245
 
6246
+ class ZeroCrossingBracketing {
6247
+ /**
6248
+ * Detect a range containing at least one root.
6249
+ *
6250
+ * This iterative method stops when two values with opposite signs are found.
6251
+ *
6252
+ * @param f The function to detect roots from.
6253
+ * @param bounds The upper and lower value of the range.
6254
+ * @param factor The growing factor of research. Defaults to 1.6.
6255
+ * @param maxIterations Maximum number of iterations. Defaults to 50.
6256
+ * @returns Whether the bracketing operation succeeded.
6257
+ */
6258
+ static expand(f, bounds, factor = 1.6, maxIterations = 50) {
6259
+ const originalUpperBound = bounds.upperBound;
6260
+ const originalLowerBound = bounds.lowerBound;
6261
+ if (originalLowerBound >= originalUpperBound) {
6262
+ throw new RangeError("Upper bound must be greater than lower bound.");
6263
+ }
6264
+ let fmin = f(originalLowerBound);
6265
+ let fmax = f(originalUpperBound);
6266
+ for (let i = 0; i < maxIterations; ++i) {
6267
+ if (Math.sign(fmin) !== Math.sign(fmax)) {
6268
+ return true;
6269
+ }
6270
+ if (Math.abs(fmin) < Math.abs(fmax)) {
6271
+ bounds.lowerBound +=
6272
+ factor * (bounds.lowerBound - bounds.upperBound);
6273
+ fmin = f(bounds.lowerBound);
6274
+ }
6275
+ else {
6276
+ bounds.upperBound +=
6277
+ factor * (bounds.upperBound - bounds.lowerBound);
6278
+ fmax = f(bounds.upperBound);
6279
+ }
6280
+ }
6281
+ bounds.lowerBound = originalLowerBound;
6282
+ bounds.upperBound = originalUpperBound;
6283
+ return false;
6284
+ }
6285
+ static reduce(f, bounds, subdivisions = 1000) {
6286
+ const originalUpperBound = bounds.upperBound;
6287
+ const originalLowerBound = bounds.lowerBound;
6288
+ if (originalLowerBound >= originalUpperBound) {
6289
+ throw new RangeError("Upper bound must be greater than lower bound.");
6290
+ }
6291
+ // TODO: Consider binary-style search instead of linear scan
6292
+ const fmin = f(bounds.lowerBound);
6293
+ const fmax = f(bounds.upperBound);
6294
+ if (Math.sign(fmin) != Math.sign(fmax)) {
6295
+ return true;
6296
+ }
6297
+ const subdiv = (bounds.upperBound - bounds.lowerBound) / subdivisions;
6298
+ let smin = bounds.lowerBound;
6299
+ const sign = Math.sign(fmin);
6300
+ for (let i = 0; i < subdivisions; ++i) {
6301
+ const smax = smin + subdiv;
6302
+ const sfmax = f(smax);
6303
+ if (!Number.isFinite(sfmax)) {
6304
+ // expand interval to include pole
6305
+ smin = smax;
6306
+ continue;
6307
+ }
6308
+ if (Math.sign(sfmax) != sign) {
6309
+ bounds.upperBound = smax;
6310
+ bounds.lowerBound = smin;
6311
+ return true;
6312
+ }
6313
+ smin = smax;
6314
+ }
6315
+ bounds.lowerBound = originalLowerBound;
6316
+ bounds.upperBound = originalUpperBound;
6317
+ return false;
6318
+ }
6319
+ static expandReduce(f, bounds, expansionFactor = 1.6, expansionMaxIterations = 50, reduceSubdivisions = 100) {
6320
+ return (this.expand(f, bounds, expansionFactor, expansionMaxIterations) ||
6321
+ this.reduce(f, bounds, reduceSubdivisions));
6322
+ }
6323
+ }
6324
+
6325
+ /**
6326
+ * Algorithm by Brent, Van Wijngaarden, Dekker et al.
6327
+ *
6328
+ * Implementation inspired by Press, Teukolsky, Vetterling, and Flannery, "Numerical Recipes in C", 2nd edition, Cambridge University Press.
6329
+ */
6330
+ class Brent {
6331
+ /**
6332
+ * Finds a solution to the equation f(x) = 0.
6333
+ *
6334
+ * @param f The function to find roots from.
6335
+ * @param bounds The upper and lower root bounds.
6336
+ * @param accuracy The desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Defaults to 1e-8. Must be greater than 0.
6337
+ * @param maxIterations The maximum number of iterations. Defaults to 100.
6338
+ * @param expandFactor The factor at which to expand the bounds, if needed. Defaults to 1.6.
6339
+ * @param maxExpandIterations The maximum number of expand iterations. Defaults to 100.
6340
+ * @returns The root with the specified accuracy. Throws an error if the algorithm failed to converge.
6341
+ */
6342
+ static findRootExpand(f, bounds, accuracy = 1e-8, maxIterations = 100, expandFactor = 1.6, maxExpandIterations = 100) {
6343
+ ZeroCrossingBracketing.expandReduce(f, bounds, expandFactor, maxExpandIterations, maxExpandIterations * 10);
6344
+ return this.findRoot(f, bounds, accuracy, maxIterations);
6345
+ }
6346
+ /**
6347
+ * Finds a solution to the equation f(x) = 0.
6348
+ *
6349
+ * @param f The function to find roots from.
6350
+ * @param bounds The upper and lower root bounds.
6351
+ * @param accuracy The desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Defaults to 1e-8. Must be greater than 0.
6352
+ * @param maxIterations The maximum number of iterations. Defaults to 100.
6353
+ * @returns The root with the specified accuracy. Throws an error if the algorithm failed to converge.
6354
+ */
6355
+ static findRoot(f, bounds, accuracy = 1e-8, maxIterations = 100) {
6356
+ const root = this.tryFindRoot(f, bounds, accuracy, maxIterations);
6357
+ if (root === null) {
6358
+ throw new Error("The algorithm has failed, exceeded the number of iterations allowed or there is no root within the provided bounds.");
6359
+ }
6360
+ return root;
6361
+ }
6362
+ /**
6363
+ * Finds a solution to the equation f(x) = 0.
6364
+ *
6365
+ * @param f The function to find roots from.
6366
+ * @param bounds The upper and lower root bounds.
6367
+ * @param accuracy The desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached. Must be greater than 0.
6368
+ * @param maxIterations The maximum number of iterations. Usually 100.
6369
+ * @returns The root with the specified accuracy, `null` if not found.
6370
+ */
6371
+ static tryFindRoot(f, bounds, accuracy, maxIterations) {
6372
+ if (accuracy <= 0) {
6373
+ throw new RangeError("Accuracy must be greater than 0.");
6374
+ }
6375
+ let { lowerBound, upperBound } = bounds;
6376
+ let fmin = f(lowerBound);
6377
+ let fmax = f(upperBound);
6378
+ let froot = fmax;
6379
+ let d = 0;
6380
+ let e = 0;
6381
+ let root = upperBound;
6382
+ let xMid = Number.NaN;
6383
+ // Root must be bracketed.
6384
+ if (Math.sign(fmin) === Math.sign(fmax)) {
6385
+ return null;
6386
+ }
6387
+ for (let i = 0; i <= maxIterations; ++i) {
6388
+ // Adjust bounds.
6389
+ if (Math.sign(froot) === Math.sign(fmax)) {
6390
+ upperBound = lowerBound;
6391
+ fmax = fmin;
6392
+ e = d = root - lowerBound;
6393
+ }
6394
+ if (Math.abs(fmax) < Math.abs(froot)) {
6395
+ lowerBound = root;
6396
+ root = upperBound;
6397
+ upperBound = lowerBound;
6398
+ fmin = froot;
6399
+ froot = fmax;
6400
+ fmax = fmin;
6401
+ }
6402
+ // Convergence check
6403
+ const xAcc1 = 2 * Math.pow(2, -53) * Math.abs(root) + accuracy / 2;
6404
+ const xMidOld = xMid;
6405
+ xMid = (upperBound - root) / 2;
6406
+ if (Math.abs(xMid) <= xAcc1 ||
6407
+ Precision.almostEqualNormRelative(froot, 0, froot, accuracy)) {
6408
+ return root;
6409
+ }
6410
+ if (xMid === xMidOld) {
6411
+ // accuracy not sufficient, but cannot be improved further
6412
+ return null;
6413
+ }
6414
+ if (Math.abs(e) >= xAcc1 && Math.abs(fmin) > Math.abs(froot)) {
6415
+ // Attempt inverse quadratic interpolation
6416
+ const s = froot / fmin;
6417
+ let p;
6418
+ let q;
6419
+ if (Precision.almostEqualRelative(lowerBound, upperBound)) {
6420
+ p = 2 * xMid * s;
6421
+ q = 1 - s;
6422
+ }
6423
+ else {
6424
+ q = fmin / fmax;
6425
+ const r = froot / fmax;
6426
+ p =
6427
+ s *
6428
+ (2 * xMid * q * (q - r) -
6429
+ (root - lowerBound) * (r - 1));
6430
+ q = (q - 1) * (r - 1) * (s - 1);
6431
+ }
6432
+ if (p > 0) {
6433
+ // Check whether in bounds
6434
+ q = -q;
6435
+ }
6436
+ p = Math.abs(p);
6437
+ if (2 * p <
6438
+ Math.min(3 * xMid * q - Math.abs(xAcc1 * q), Math.abs(e * q))) {
6439
+ // Accept interpolation
6440
+ e = d;
6441
+ d = p / q;
6442
+ }
6443
+ else {
6444
+ // Interpolation failed, use bisection
6445
+ d = xMid;
6446
+ e = d;
6447
+ }
6448
+ }
6449
+ else {
6450
+ // Bounds decreasing too slowly, use bisection
6451
+ d = xMid;
6452
+ e = d;
6453
+ }
6454
+ lowerBound = root;
6455
+ fmin = froot;
6456
+ if (Math.abs(d) > xAcc1) {
6457
+ root += d;
6458
+ }
6459
+ else {
6460
+ root += this.sign(xAcc1, xMid);
6461
+ }
6462
+ froot = f(root);
6463
+ }
6464
+ return null;
6465
+ }
6466
+ /**
6467
+ * Helper method useful for preventing rounding errors.
6468
+ *
6469
+ * @returns a * sign(b)
6470
+ */
6471
+ static sign(a, b) {
6472
+ return b >= 0 ? (a >= 0 ? a : -a) : a >= 0 ? -a : a;
6473
+ }
6474
+ }
6475
+
6203
6476
  /**
6204
6477
  * Types of easing.
6205
6478
  *
@@ -6665,6 +6938,29 @@ class ErrorFunction {
6665
6938
  }
6666
6939
  return this.erfImp(x, false);
6667
6940
  }
6941
+ /**
6942
+ * Calculates the complementary error function.
6943
+ *
6944
+ * @param x The value to evaluate.
6945
+ * @returns The complementary error function evaluated at given value, or:
6946
+ * - 0 if `x === Number.POSITIVE_INFINITY`;
6947
+ * - 2 if `x === Number.NEGATIVE_INFINITY`.
6948
+ */
6949
+ static erfc(x) {
6950
+ if (x === 0) {
6951
+ return 1;
6952
+ }
6953
+ if (x === Number.POSITIVE_INFINITY) {
6954
+ return 0;
6955
+ }
6956
+ if (x === Number.NEGATIVE_INFINITY) {
6957
+ return 2;
6958
+ }
6959
+ if (Number.isNaN(x)) {
6960
+ return Number.NaN;
6961
+ }
6962
+ return this.erfImp(x, true);
6963
+ }
6668
6964
  /**
6669
6965
  * Calculates the inverse error function evaluated at z.
6670
6966
  *
@@ -7508,6 +7804,16 @@ class MapInfo {
7508
7804
  }
7509
7805
  }
7510
7806
 
7807
+ /**
7808
+ * Represents the osu! playfield.
7809
+ */
7810
+ class Playfield {
7811
+ /**
7812
+ * The size of the playfield, which is 512x384.
7813
+ */
7814
+ static baseSize = new Vector2(512, 384);
7815
+ }
7816
+
7511
7817
  dotenv.config();
7512
7818
 
7513
7819
  exports.Accuracy = Accuracy;
@@ -7526,6 +7832,7 @@ exports.BeatmapMetadata = BeatmapMetadata;
7526
7832
  exports.BeatmapVideo = BeatmapVideo;
7527
7833
  exports.BlendingParameters = BlendingParameters;
7528
7834
  exports.BreakPoint = BreakPoint;
7835
+ exports.Brent = Brent;
7529
7836
  exports.Circle = Circle;
7530
7837
  exports.Command = Command;
7531
7838
  exports.CommandLoop = CommandLoop;
@@ -7570,6 +7877,7 @@ exports.ModUtil = ModUtil;
7570
7877
  exports.OsuAPIRequestBuilder = OsuAPIRequestBuilder;
7571
7878
  exports.OsuHitWindow = OsuHitWindow;
7572
7879
  exports.PathApproximator = PathApproximator;
7880
+ exports.Playfield = Playfield;
7573
7881
  exports.Polynomial = Polynomial;
7574
7882
  exports.Precision = Precision;
7575
7883
  exports.RGBColor = RGBColor;
@@ -7595,4 +7903,5 @@ exports.TimingControlPoint = TimingControlPoint;
7595
7903
  exports.TimingControlPointManager = TimingControlPointManager;
7596
7904
  exports.Utils = Utils;
7597
7905
  exports.Vector2 = Vector2;
7906
+ exports.ZeroCrossingBracketing = ZeroCrossingBracketing;
7598
7907
  //# sourceMappingURL=index.js.map