@rian8337/osu-base 3.0.0-beta.1 → 3.0.0-beta.11
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 +564 -122
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/typings/index.d.ts +431 -266
package/dist/index.js
CHANGED
|
@@ -344,9 +344,7 @@ class APIRequestBuilder {
|
|
|
344
344
|
* @param param The parameter to remove.
|
|
345
345
|
*/
|
|
346
346
|
removeParameter(param) {
|
|
347
|
-
|
|
348
|
-
this.params.delete(param);
|
|
349
|
-
}
|
|
347
|
+
this.params.delete(param);
|
|
350
348
|
return this;
|
|
351
349
|
}
|
|
352
350
|
}
|
|
@@ -357,18 +355,6 @@ class DroidAPIRequestBuilder extends APIRequestBuilder {
|
|
|
357
355
|
host = "https://osudroid.moe/api/";
|
|
358
356
|
APIkey = process.env.DROID_API_KEY;
|
|
359
357
|
APIkeyParam = `apiKey=${this.APIkey}&`;
|
|
360
|
-
setEndpoint(endpoint) {
|
|
361
|
-
// Compatibility with old API. Can be removed in v3.0.
|
|
362
|
-
switch (endpoint) {
|
|
363
|
-
case "banscore.php":
|
|
364
|
-
endpoint = "single_score_wipe.php";
|
|
365
|
-
break;
|
|
366
|
-
case "rename.php":
|
|
367
|
-
endpoint = "user_rename.php";
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
return super.setEndpoint(endpoint);
|
|
371
|
-
}
|
|
372
358
|
}
|
|
373
359
|
/**
|
|
374
360
|
* API request builder for osu!standard.
|
|
@@ -379,6 +365,15 @@ class OsuAPIRequestBuilder extends APIRequestBuilder {
|
|
|
379
365
|
APIkeyParam = `k=${this.APIkey}&`;
|
|
380
366
|
}
|
|
381
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Mode enum to switch things between osu!droid and osu!standard.
|
|
370
|
+
*/
|
|
371
|
+
exports.modes = void 0;
|
|
372
|
+
(function (modes) {
|
|
373
|
+
modes["droid"] = "droid";
|
|
374
|
+
modes["osu"] = "osu";
|
|
375
|
+
})(exports.modes || (exports.modes = {}));
|
|
376
|
+
|
|
382
377
|
/**
|
|
383
378
|
* Bitmask constant of object types. This is needed as osu! uses bits to determine object types.
|
|
384
379
|
*/
|
|
@@ -396,11 +391,11 @@ exports.objectTypes = void 0;
|
|
|
396
391
|
*/
|
|
397
392
|
class Vector2 {
|
|
398
393
|
/**
|
|
399
|
-
* The x position of
|
|
394
|
+
* The x position of this vector.
|
|
400
395
|
*/
|
|
401
396
|
x;
|
|
402
397
|
/**
|
|
403
|
-
* The y position of
|
|
398
|
+
* The y position of this vector.
|
|
404
399
|
*/
|
|
405
400
|
y;
|
|
406
401
|
constructor(x, y) {
|
|
@@ -408,11 +403,22 @@ class Vector2 {
|
|
|
408
403
|
this.y = y;
|
|
409
404
|
}
|
|
410
405
|
/**
|
|
411
|
-
* Multiplies
|
|
406
|
+
* Multiplies this vector with another vector.
|
|
407
|
+
*
|
|
408
|
+
* @param vec The other vector.
|
|
409
|
+
* @returns The multiplied vector.
|
|
412
410
|
*/
|
|
413
411
|
multiply(vec) {
|
|
414
412
|
return new Vector2(this.x * vec.x, this.y * vec.y);
|
|
415
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Divides this vector with a scalar.
|
|
416
|
+
*
|
|
417
|
+
* Attempting to divide by 0 will throw an error.
|
|
418
|
+
*
|
|
419
|
+
* @param divideFactor The factor to divide the vector by.
|
|
420
|
+
* @returns The divided vector.
|
|
421
|
+
*/
|
|
416
422
|
divide(divideFactor) {
|
|
417
423
|
if (divideFactor === 0) {
|
|
418
424
|
throw new Error("Division by 0");
|
|
@@ -420,37 +426,52 @@ class Vector2 {
|
|
|
420
426
|
return new Vector2(this.x / divideFactor, this.y / divideFactor);
|
|
421
427
|
}
|
|
422
428
|
/**
|
|
423
|
-
* Adds
|
|
429
|
+
* Adds this vector with another vector.
|
|
430
|
+
*
|
|
431
|
+
* @param vec The other vector.
|
|
432
|
+
* @returns The added vector.
|
|
424
433
|
*/
|
|
425
434
|
add(vec) {
|
|
426
435
|
return new Vector2(this.x + vec.x, this.y + vec.y);
|
|
427
436
|
}
|
|
428
437
|
/**
|
|
429
|
-
* Subtracts
|
|
438
|
+
* Subtracts this vector with another vector.
|
|
439
|
+
*
|
|
440
|
+
* @param vec The other vector.
|
|
441
|
+
* @returns The subtracted vector.
|
|
430
442
|
*/
|
|
431
443
|
subtract(vec) {
|
|
432
444
|
return new Vector2(this.x - vec.x, this.y - vec.y);
|
|
433
445
|
}
|
|
434
446
|
/**
|
|
435
|
-
* The length of
|
|
447
|
+
* The length of this vector.
|
|
436
448
|
*/
|
|
437
449
|
get length() {
|
|
438
450
|
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
|
|
439
451
|
}
|
|
440
452
|
/**
|
|
441
453
|
* Performs a dot multiplication with another vector.
|
|
454
|
+
*
|
|
455
|
+
* @param vec The other vector.
|
|
456
|
+
* @returns The dot product of both vectors.
|
|
442
457
|
*/
|
|
443
458
|
dot(vec) {
|
|
444
459
|
return this.x * vec.x + this.y * vec.y;
|
|
445
460
|
}
|
|
446
461
|
/**
|
|
447
|
-
* Scales
|
|
462
|
+
* Scales this vector.
|
|
463
|
+
*
|
|
464
|
+
* @param scaleFactor The factor to scale the vector by.
|
|
465
|
+
* @returns The scaled vector.
|
|
448
466
|
*/
|
|
449
467
|
scale(scaleFactor) {
|
|
450
468
|
return new Vector2(this.x * scaleFactor, this.y * scaleFactor);
|
|
451
469
|
}
|
|
452
470
|
/**
|
|
453
471
|
* Gets the distance between this vector and another vector.
|
|
472
|
+
*
|
|
473
|
+
* @param vec The other vector.
|
|
474
|
+
* @returns The distance between this vector and the other vector.
|
|
454
475
|
*/
|
|
455
476
|
getDistance(vec) {
|
|
456
477
|
const x = this.x - vec.x;
|
|
@@ -469,6 +490,7 @@ class Vector2 {
|
|
|
469
490
|
* Checks whether this vector is equal to another vector.
|
|
470
491
|
*
|
|
471
492
|
* @param other The other vector.
|
|
493
|
+
* @returns Whether this vector is equal to the other vector.
|
|
472
494
|
*/
|
|
473
495
|
equals(other) {
|
|
474
496
|
return this.x === other.x && this.y === other.y;
|
|
@@ -511,31 +533,6 @@ class HitObject {
|
|
|
511
533
|
get duration() {
|
|
512
534
|
return this.endTime - this.startTime;
|
|
513
535
|
}
|
|
514
|
-
/**
|
|
515
|
-
* The stacked position of the hitobject.
|
|
516
|
-
*/
|
|
517
|
-
get stackedPosition() {
|
|
518
|
-
if (this.type & exports.objectTypes.spinner) {
|
|
519
|
-
return this.position;
|
|
520
|
-
}
|
|
521
|
-
return this.position.add(this.stackOffset);
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* The stacked end position of the hitobject.
|
|
525
|
-
*/
|
|
526
|
-
get stackedEndPosition() {
|
|
527
|
-
if (this.type & exports.objectTypes.spinner) {
|
|
528
|
-
return this.position;
|
|
529
|
-
}
|
|
530
|
-
return this.endPosition.add(this.stackOffset);
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* The stack vector to calculate offset for stacked positions.
|
|
534
|
-
*/
|
|
535
|
-
get stackOffset() {
|
|
536
|
-
const coordinate = this.stackHeight * this.scale * -6.4;
|
|
537
|
-
return new Vector2(coordinate, coordinate);
|
|
538
|
-
}
|
|
539
536
|
/**
|
|
540
537
|
* Whether this hit object represents a new combo.
|
|
541
538
|
*/
|
|
@@ -556,15 +553,13 @@ class HitObject {
|
|
|
556
553
|
*/
|
|
557
554
|
stackHeight = 0;
|
|
558
555
|
/**
|
|
559
|
-
* The scale used to calculate stacked position and radius.
|
|
556
|
+
* The osu!droid scale used to calculate stacked position and radius.
|
|
560
557
|
*/
|
|
561
|
-
|
|
558
|
+
droidScale = 1;
|
|
562
559
|
/**
|
|
563
|
-
* The
|
|
560
|
+
* The osu!standard scale used to calculate stacked position and radius.
|
|
564
561
|
*/
|
|
565
|
-
|
|
566
|
-
return 64 * this.scale;
|
|
567
|
-
}
|
|
562
|
+
osuScale = 1;
|
|
568
563
|
constructor(values) {
|
|
569
564
|
this.startTime = values.startTime;
|
|
570
565
|
this.endTime = values.endTime ?? values.startTime;
|
|
@@ -574,6 +569,62 @@ class HitObject {
|
|
|
574
569
|
this.isNewCombo = values.newCombo ?? false;
|
|
575
570
|
this.comboOffset = values.comboOffset ?? 0;
|
|
576
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Evaluates the radius of the hitobject.
|
|
574
|
+
*
|
|
575
|
+
* @param mode The gamemode to evaluate for.
|
|
576
|
+
* @returns The radius of the hitobject with respect to the gamemode.
|
|
577
|
+
*/
|
|
578
|
+
getRadius(mode) {
|
|
579
|
+
let radius = 64;
|
|
580
|
+
switch (mode) {
|
|
581
|
+
case exports.modes.droid:
|
|
582
|
+
radius *= this.droidScale;
|
|
583
|
+
break;
|
|
584
|
+
case exports.modes.osu:
|
|
585
|
+
radius *= this.osuScale;
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
return radius;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Evaluates the stack offset vector of the hitobject.
|
|
592
|
+
*
|
|
593
|
+
* This is used to calculate offset for stacked positions.
|
|
594
|
+
*
|
|
595
|
+
* @param mode The gamemode to evaluate for.
|
|
596
|
+
* @returns The stack offset with respect to the gamemode.
|
|
597
|
+
*/
|
|
598
|
+
getStackOffset(mode) {
|
|
599
|
+
let coordinate = this.stackHeight * -6.4;
|
|
600
|
+
switch (mode) {
|
|
601
|
+
case exports.modes.droid:
|
|
602
|
+
coordinate *= this.droidScale;
|
|
603
|
+
break;
|
|
604
|
+
case exports.modes.osu:
|
|
605
|
+
coordinate *= this.osuScale;
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
return new Vector2(coordinate, coordinate);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Evaluates the stacked position of the hitobject.
|
|
612
|
+
*
|
|
613
|
+
* @param mode The gamemode to evaluate for.
|
|
614
|
+
* @returns The stacked position with respect to the gamemode.
|
|
615
|
+
*/
|
|
616
|
+
getStackedPosition(mode) {
|
|
617
|
+
return this.evaluateStackedPosition(this.position, mode);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Evaluates the stacked end position of the hitobject.
|
|
621
|
+
*
|
|
622
|
+
* @param mode The gamemode to evaluate for.
|
|
623
|
+
* @returns The stacked end position with respect to the gamemode.
|
|
624
|
+
*/
|
|
625
|
+
getStackedEndPosition(mode) {
|
|
626
|
+
return this.evaluateStackedPosition(this.endPosition, mode);
|
|
627
|
+
}
|
|
577
628
|
/**
|
|
578
629
|
* Returns the hitobject type.
|
|
579
630
|
*/
|
|
@@ -590,6 +641,19 @@ class HitObject {
|
|
|
590
641
|
}
|
|
591
642
|
return res.substring(0, Math.max(0, res.length - 3));
|
|
592
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Evaluates the stacked position of the specified position.
|
|
646
|
+
*
|
|
647
|
+
* @param position The position to evaluate.
|
|
648
|
+
* @param mode The gamemode to evaluate for.
|
|
649
|
+
* @returns The stacked position.
|
|
650
|
+
*/
|
|
651
|
+
evaluateStackedPosition(position, mode) {
|
|
652
|
+
if (this.type & exports.objectTypes.spinner) {
|
|
653
|
+
return position;
|
|
654
|
+
}
|
|
655
|
+
return position.add(this.getStackOffset(mode));
|
|
656
|
+
}
|
|
593
657
|
}
|
|
594
658
|
|
|
595
659
|
/**
|
|
@@ -1193,15 +1257,7 @@ class ControlPointManager {
|
|
|
1193
1257
|
}
|
|
1194
1258
|
existing = this.controlPointAt(controlPoint.time);
|
|
1195
1259
|
}
|
|
1196
|
-
|
|
1197
|
-
for (let i = 0; i < this._points.length; ++i) {
|
|
1198
|
-
if (this._points[i].time >= controlPoint.time) {
|
|
1199
|
-
this._points.splice(i - 1, 0, controlPoint);
|
|
1200
|
-
return true;
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
// Append the control point if it hasn't been added yet.
|
|
1204
|
-
this._points.push(controlPoint);
|
|
1260
|
+
this._points.splice(this.findInsertionIndex(controlPoint.time), 0, controlPoint);
|
|
1205
1261
|
return true;
|
|
1206
1262
|
}
|
|
1207
1263
|
/**
|
|
@@ -1235,6 +1291,12 @@ class ControlPointManager {
|
|
|
1235
1291
|
removeAt(index) {
|
|
1236
1292
|
return this._points.splice(index, 1)[0];
|
|
1237
1293
|
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Clears all control points of this type.
|
|
1296
|
+
*/
|
|
1297
|
+
clear() {
|
|
1298
|
+
this._points.length = 0;
|
|
1299
|
+
}
|
|
1238
1300
|
/**
|
|
1239
1301
|
* Binary searches one of the control point lists to find the active control point at the given time.
|
|
1240
1302
|
*
|
|
@@ -1274,9 +1336,37 @@ class ControlPointManager {
|
|
|
1274
1336
|
return this._points[pivot];
|
|
1275
1337
|
}
|
|
1276
1338
|
}
|
|
1277
|
-
// l will be the first control point with time > this.
|
|
1339
|
+
// l will be the first control point with time > this._points[l].time, but we want the one before it
|
|
1278
1340
|
return this._points[l - 1];
|
|
1279
1341
|
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Finds the insertion index of a control point in a given time.
|
|
1344
|
+
*
|
|
1345
|
+
* @param time The start time of the control point.
|
|
1346
|
+
*/
|
|
1347
|
+
findInsertionIndex(time) {
|
|
1348
|
+
if (this._points.length === 0 || time < this._points[0].time) {
|
|
1349
|
+
return 0;
|
|
1350
|
+
}
|
|
1351
|
+
if (time >= this._points.at(-1).time) {
|
|
1352
|
+
return this._points.length;
|
|
1353
|
+
}
|
|
1354
|
+
let l = 0;
|
|
1355
|
+
let r = this._points.length - 2;
|
|
1356
|
+
while (l <= r) {
|
|
1357
|
+
const pivot = l + ((r - l) >> 1);
|
|
1358
|
+
if (this._points[pivot].time < time) {
|
|
1359
|
+
l = pivot + 1;
|
|
1360
|
+
}
|
|
1361
|
+
else if (this._points[pivot].time > time) {
|
|
1362
|
+
r = pivot - 1;
|
|
1363
|
+
}
|
|
1364
|
+
else {
|
|
1365
|
+
return pivot;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return l;
|
|
1369
|
+
}
|
|
1280
1370
|
}
|
|
1281
1371
|
|
|
1282
1372
|
/**
|
|
@@ -1437,7 +1527,7 @@ class SampleControlPointManager extends ControlPointManager {
|
|
|
1437
1527
|
}
|
|
1438
1528
|
|
|
1439
1529
|
/**
|
|
1440
|
-
* Contains information about timing
|
|
1530
|
+
* Contains information about timing (control) points of a beatmap.
|
|
1441
1531
|
*/
|
|
1442
1532
|
class BeatmapControlPoints {
|
|
1443
1533
|
/**
|
|
@@ -1456,6 +1546,15 @@ class BeatmapControlPoints {
|
|
|
1456
1546
|
* The manager for sample control points of the beatmap.
|
|
1457
1547
|
*/
|
|
1458
1548
|
sample = new SampleControlPointManager();
|
|
1549
|
+
/**
|
|
1550
|
+
* Clears all control points in the beatmap.
|
|
1551
|
+
*/
|
|
1552
|
+
clear() {
|
|
1553
|
+
this.timing.clear();
|
|
1554
|
+
this.difficulty.clear();
|
|
1555
|
+
this.effect.clear();
|
|
1556
|
+
this.sample.clear();
|
|
1557
|
+
}
|
|
1459
1558
|
}
|
|
1460
1559
|
|
|
1461
1560
|
/**
|
|
@@ -1573,7 +1672,7 @@ class BeatmapHitObjects {
|
|
|
1573
1672
|
}
|
|
1574
1673
|
}
|
|
1575
1674
|
}
|
|
1576
|
-
else
|
|
1675
|
+
else {
|
|
1577
1676
|
++this._spinners;
|
|
1578
1677
|
}
|
|
1579
1678
|
}
|
|
@@ -1622,12 +1721,28 @@ class BeatmapHitObjects {
|
|
|
1622
1721
|
* @param startTime The start time of the hitobject.
|
|
1623
1722
|
*/
|
|
1624
1723
|
findInsertionIndex(startTime) {
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1724
|
+
if (this._objects.length === 0 ||
|
|
1725
|
+
startTime < this._objects[0].startTime) {
|
|
1726
|
+
return 0;
|
|
1727
|
+
}
|
|
1728
|
+
if (startTime >= this._objects.at(-1).startTime) {
|
|
1729
|
+
return this._objects.length;
|
|
1730
|
+
}
|
|
1731
|
+
let l = 0;
|
|
1732
|
+
let r = this._objects.length - 2;
|
|
1733
|
+
while (l <= r) {
|
|
1734
|
+
const pivot = l + ((r - l) >> 1);
|
|
1735
|
+
if (this._objects[pivot].startTime < startTime) {
|
|
1736
|
+
l = pivot + 1;
|
|
1737
|
+
}
|
|
1738
|
+
else if (this._objects[pivot].startTime > startTime) {
|
|
1739
|
+
r = pivot - 1;
|
|
1740
|
+
}
|
|
1741
|
+
else {
|
|
1742
|
+
return pivot;
|
|
1628
1743
|
}
|
|
1629
1744
|
}
|
|
1630
|
-
return
|
|
1745
|
+
return l;
|
|
1631
1746
|
}
|
|
1632
1747
|
}
|
|
1633
1748
|
|
|
@@ -1893,15 +2008,6 @@ class BeatmapBackground {
|
|
|
1893
2008
|
}
|
|
1894
2009
|
}
|
|
1895
2010
|
|
|
1896
|
-
/**
|
|
1897
|
-
* Mode enum to switch things between osu!droid and osu!standard.
|
|
1898
|
-
*/
|
|
1899
|
-
exports.modes = void 0;
|
|
1900
|
-
(function (modes) {
|
|
1901
|
-
modes["droid"] = "droid";
|
|
1902
|
-
modes["osu"] = "osu";
|
|
1903
|
-
})(exports.modes || (exports.modes = {}));
|
|
1904
|
-
|
|
1905
2011
|
class HitWindow {
|
|
1906
2012
|
/**
|
|
1907
2013
|
* The overall difficulty of this hit window.
|
|
@@ -2373,7 +2479,7 @@ class ModUtil {
|
|
|
2373
2479
|
* @param options Options for parsing behavior.
|
|
2374
2480
|
*/
|
|
2375
2481
|
static pcModbitsToMods(modbits, options) {
|
|
2376
|
-
return this.processParsingOptions(this.allMods.filter((m) => m.isApplicableToOsu() &&
|
|
2482
|
+
return this.processParsingOptions(this.allMods.filter((m) => m.isApplicableToOsu() && m.bitwise & modbits), options);
|
|
2377
2483
|
}
|
|
2378
2484
|
/**
|
|
2379
2485
|
* Gets a list of mods from a PC mod string, such as "HDHR".
|
|
@@ -2416,8 +2522,7 @@ class ModUtil {
|
|
|
2416
2522
|
for (const incompatibleMod of this.incompatibleMods) {
|
|
2417
2523
|
const fulfilledMods = mods.filter((m) => incompatibleMod.some((v) => m.acronym === v.acronym));
|
|
2418
2524
|
if (fulfilledMods.length > 1) {
|
|
2419
|
-
mods = mods.filter((m) => incompatibleMod
|
|
2420
|
-
.every((v) => m.acronym !== v.acronym));
|
|
2525
|
+
mods = mods.filter((m) => incompatibleMod.every((v) => m.acronym !== v.acronym));
|
|
2421
2526
|
// Keep the first selected mod
|
|
2422
2527
|
mods.push(fulfilledMods[0]);
|
|
2423
2528
|
}
|
|
@@ -2743,6 +2848,49 @@ class Precision {
|
|
|
2743
2848
|
return (this.almostEqualsNumber(vec1.x, vec2.x, acceptableDifference) &&
|
|
2744
2849
|
this.almostEqualsNumber(vec1.y, vec2.y, acceptableDifference));
|
|
2745
2850
|
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Checks whether two real numbers are almost equal.
|
|
2853
|
+
*
|
|
2854
|
+
* @param a The first number.
|
|
2855
|
+
* @param b The second number.
|
|
2856
|
+
* @param maximumError The accuracy required for being almost equal. Defaults to `10 * 2^(-53)`.
|
|
2857
|
+
* @returns Whether the two values differ by no more than 10 * 2^(-52).
|
|
2858
|
+
*/
|
|
2859
|
+
static almostEqualRelative(a, b, maximumError = 10 * Math.pow(2, -53)) {
|
|
2860
|
+
return this.almostEqualNormRelative(a, b, a - b, maximumError);
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Compares two numbers and determines if they are equal within the specified maximum error.
|
|
2864
|
+
*
|
|
2865
|
+
* @param a The norm of the first value (can be negative).
|
|
2866
|
+
* @param b The norm of the second value (can be negative).
|
|
2867
|
+
* @param diff The norm of the difference of the two values (can be negative).
|
|
2868
|
+
* @param maximumError The accuracy required for being almost equal.
|
|
2869
|
+
* @returns Whether both numbers are almost equal up to the specified maximum error.
|
|
2870
|
+
*/
|
|
2871
|
+
static almostEqualNormRelative(a, b, diff, maximumError) {
|
|
2872
|
+
// If A or B are infinity (positive or negative) then
|
|
2873
|
+
// only return true if they are exactly equal to each other -
|
|
2874
|
+
// that is, if they are both infinities of the same sign.
|
|
2875
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) {
|
|
2876
|
+
return a === b;
|
|
2877
|
+
}
|
|
2878
|
+
// If A or B are a NAN, return false. NANs are equal to nothing,
|
|
2879
|
+
// not even themselves.
|
|
2880
|
+
if (Number.isNaN(a) || Number.isNaN(b)) {
|
|
2881
|
+
return false;
|
|
2882
|
+
}
|
|
2883
|
+
// If one is almost zero, fall back to absolute equality.
|
|
2884
|
+
const doublePrecision = Math.pow(2, -53);
|
|
2885
|
+
if (Math.abs(a) < doublePrecision || Math.abs(b) < doublePrecision) {
|
|
2886
|
+
return Math.abs(diff) < maximumError;
|
|
2887
|
+
}
|
|
2888
|
+
if ((a === 0 && Math.abs(b) < maximumError) ||
|
|
2889
|
+
(b === 0 && Math.abs(a) < maximumError)) {
|
|
2890
|
+
return true;
|
|
2891
|
+
}
|
|
2892
|
+
return (Math.abs(diff) < maximumError * Math.max(Math.abs(a), Math.abs(b)));
|
|
2893
|
+
}
|
|
2746
2894
|
}
|
|
2747
2895
|
|
|
2748
2896
|
/**
|
|
@@ -4302,7 +4450,12 @@ class RGBColor {
|
|
|
4302
4450
|
* Returns a string representation of the color.
|
|
4303
4451
|
*/
|
|
4304
4452
|
toString() {
|
|
4305
|
-
|
|
4453
|
+
if (this.a === 1) {
|
|
4454
|
+
return `${this.r},${this.g},${this.b}`;
|
|
4455
|
+
}
|
|
4456
|
+
else {
|
|
4457
|
+
return `${this.r},${this.g},${this.b},${this.a}`;
|
|
4458
|
+
}
|
|
4306
4459
|
}
|
|
4307
4460
|
/**
|
|
4308
4461
|
* Checks whether this color is equal to another color.
|
|
@@ -4310,7 +4463,10 @@ class RGBColor {
|
|
|
4310
4463
|
* @param other The other color.
|
|
4311
4464
|
*/
|
|
4312
4465
|
equals(other) {
|
|
4313
|
-
return this.r === other.r &&
|
|
4466
|
+
return (this.r === other.r &&
|
|
4467
|
+
this.g === other.g &&
|
|
4468
|
+
this.b === other.b &&
|
|
4469
|
+
this.a === other.a);
|
|
4314
4470
|
}
|
|
4315
4471
|
}
|
|
4316
4472
|
|
|
@@ -4664,21 +4820,11 @@ class CommandTimelineGroup {
|
|
|
4664
4820
|
this.flipHorizontal,
|
|
4665
4821
|
this.flipVertical,
|
|
4666
4822
|
];
|
|
4667
|
-
/**
|
|
4668
|
-
* The earliest visible time. Will be `null` unless this group's first alpha command has a start value of zero.
|
|
4669
|
-
*/
|
|
4670
|
-
get earliestDisplayedTime() {
|
|
4671
|
-
const first = this.alpha.commands.at(0);
|
|
4672
|
-
return first?.startValue === 0 ? first.startTime : null;
|
|
4673
|
-
}
|
|
4674
4823
|
/**
|
|
4675
4824
|
* The start time of commands.
|
|
4676
4825
|
*/
|
|
4677
4826
|
get commandsStartTime() {
|
|
4678
|
-
|
|
4679
|
-
// This is due to it creating a state where the target is not present before that time, causing any other events to not be visible.
|
|
4680
|
-
return (this.earliestDisplayedTime ??
|
|
4681
|
-
Math.min(...this.timelines.map((t) => t.startTime)));
|
|
4827
|
+
return Math.min(...this.timelines.map((t) => t.startTime));
|
|
4682
4828
|
}
|
|
4683
4829
|
/**
|
|
4684
4830
|
* The end time of commands.
|
|
@@ -4719,7 +4865,7 @@ class CommandTimelineGroup {
|
|
|
4719
4865
|
/**
|
|
4720
4866
|
* Gets the commands from a command timeline.
|
|
4721
4867
|
*
|
|
4722
|
-
* @param timelineSelector A function
|
|
4868
|
+
* @param timelineSelector A function to select the command timeline to retrieve commands from.
|
|
4723
4869
|
* @param offset The offset to apply to all commands.
|
|
4724
4870
|
*/
|
|
4725
4871
|
getCommands(timelineSelector, offset = 0) {
|
|
@@ -4831,20 +4977,14 @@ class StoryboardElement {
|
|
|
4831
4977
|
* Represents a storyboard sprite.
|
|
4832
4978
|
*/
|
|
4833
4979
|
class StoryboardSprite extends StoryboardElement {
|
|
4834
|
-
_loops = [];
|
|
4835
|
-
_triggers = [];
|
|
4836
4980
|
/**
|
|
4837
4981
|
* The loop commands of the sprite.
|
|
4838
4982
|
*/
|
|
4839
|
-
|
|
4840
|
-
return this._loops;
|
|
4841
|
-
}
|
|
4983
|
+
loops = [];
|
|
4842
4984
|
/**
|
|
4843
4985
|
* The trigger commands of the sprite.
|
|
4844
4986
|
*/
|
|
4845
|
-
|
|
4846
|
-
return this._triggers;
|
|
4847
|
-
}
|
|
4987
|
+
triggers = [];
|
|
4848
4988
|
/**
|
|
4849
4989
|
* The origin of the sprite.
|
|
4850
4990
|
*/
|
|
@@ -4858,28 +4998,57 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4858
4998
|
*/
|
|
4859
4999
|
timelineGroup = new CommandTimelineGroup();
|
|
4860
5000
|
get startTime() {
|
|
4861
|
-
//
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
5001
|
+
// To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a start value of zero.
|
|
5002
|
+
// A start value of zero governs, above all else, the first valid display time of a sprite.
|
|
5003
|
+
//
|
|
5004
|
+
// You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero,
|
|
5005
|
+
// anything before that point can be ignored (the sprite is not visible after all).
|
|
5006
|
+
const alphaCommands = [];
|
|
5007
|
+
let command = this.timelineGroup.alpha.commands[0];
|
|
5008
|
+
if (command) {
|
|
5009
|
+
alphaCommands.push({
|
|
5010
|
+
startTime: command.startTime,
|
|
5011
|
+
isZeroStartValue: command.startValue === 0,
|
|
5012
|
+
});
|
|
5013
|
+
}
|
|
5014
|
+
for (const l of this.loops) {
|
|
5015
|
+
command = l.alpha.commands[0];
|
|
5016
|
+
if (command) {
|
|
5017
|
+
alphaCommands.push({
|
|
5018
|
+
startTime: command.startTime + l.loopStartTime,
|
|
5019
|
+
isZeroStartValue: command.startValue === 0,
|
|
5020
|
+
});
|
|
4868
5021
|
}
|
|
4869
5022
|
}
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
5023
|
+
if (alphaCommands.length > 0) {
|
|
5024
|
+
const firstAlpha = alphaCommands.sort((a, b) => a.startTime - b.startTime)[0];
|
|
5025
|
+
if (firstAlpha.isZeroStartValue) {
|
|
5026
|
+
return firstAlpha.startTime;
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
return this.earliestTransformTime;
|
|
5030
|
+
}
|
|
5031
|
+
/**
|
|
5032
|
+
* The time at which the first transformation occurs.
|
|
5033
|
+
*/
|
|
5034
|
+
get earliestTransformTime() {
|
|
5035
|
+
// If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value.
|
|
5036
|
+
// The sprite's start time will be determined by the earliest command, regardless of type.
|
|
5037
|
+
let earliestStartTime = this.timelineGroup.startTime;
|
|
5038
|
+
for (const l of this.loops) {
|
|
5039
|
+
earliestStartTime = Math.min(earliestStartTime, l.startTime);
|
|
5040
|
+
}
|
|
5041
|
+
return earliestStartTime;
|
|
4873
5042
|
}
|
|
4874
5043
|
get endTime() {
|
|
4875
|
-
return Math.max(this.timelineGroup.endTime, ...this.
|
|
5044
|
+
return Math.max(this.timelineGroup.endTime, ...this.loops.map((l) => l.endTime));
|
|
4876
5045
|
}
|
|
4877
5046
|
/**
|
|
4878
5047
|
* Whether this sprite has at least one command.
|
|
4879
5048
|
*/
|
|
4880
5049
|
get hasCommands() {
|
|
4881
5050
|
return (this.timelineGroup.hasCommands ||
|
|
4882
|
-
this.
|
|
5051
|
+
this.loops.some((l) => l.hasCommands));
|
|
4883
5052
|
}
|
|
4884
5053
|
constructor(path, origin, initialPosition) {
|
|
4885
5054
|
super(path);
|
|
@@ -4895,7 +5064,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4895
5064
|
*/
|
|
4896
5065
|
addLoop(startTime, repeatCount) {
|
|
4897
5066
|
const loop = new CommandLoop(startTime, repeatCount);
|
|
4898
|
-
this.
|
|
5067
|
+
this.loops.push(loop);
|
|
4899
5068
|
return loop;
|
|
4900
5069
|
}
|
|
4901
5070
|
/**
|
|
@@ -4909,7 +5078,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4909
5078
|
*/
|
|
4910
5079
|
addTrigger(triggerName, startTime, endTime, groupNumber) {
|
|
4911
5080
|
const trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber);
|
|
4912
|
-
this.
|
|
5081
|
+
this.triggers.push(trigger);
|
|
4913
5082
|
return trigger;
|
|
4914
5083
|
}
|
|
4915
5084
|
toString() {
|
|
@@ -5349,16 +5518,23 @@ class BeatmapDecoder extends Decoder {
|
|
|
5349
5518
|
else {
|
|
5350
5519
|
hitObjectsDecoder.applyStackingOld();
|
|
5351
5520
|
}
|
|
5352
|
-
const
|
|
5521
|
+
const droidCircleSize = new MapStats({
|
|
5522
|
+
cs: this.finalResult.difficulty.cs,
|
|
5523
|
+
mods,
|
|
5524
|
+
}).calculate({ mode: exports.modes.droid }).cs;
|
|
5525
|
+
const droidScale = (1 - (0.7 * (droidCircleSize - 5)) / 5) / 2;
|
|
5526
|
+
const osuCircleSize = new MapStats({
|
|
5353
5527
|
cs: this.finalResult.difficulty.cs,
|
|
5354
5528
|
mods,
|
|
5355
|
-
}).calculate().cs;
|
|
5356
|
-
const
|
|
5529
|
+
}).calculate({ mode: exports.modes.osu }).cs;
|
|
5530
|
+
const osuScale = (1 - (0.7 * (osuCircleSize - 5)) / 5) / 2;
|
|
5357
5531
|
this.finalResult.hitObjects.objects.forEach((h) => {
|
|
5358
|
-
h.
|
|
5532
|
+
h.droidScale = droidScale;
|
|
5533
|
+
h.osuScale = osuScale;
|
|
5359
5534
|
if (h instanceof Slider) {
|
|
5360
5535
|
h.nestedHitObjects.forEach((n) => {
|
|
5361
|
-
n.
|
|
5536
|
+
n.droidScale = droidScale;
|
|
5537
|
+
n.osuScale = osuScale;
|
|
5362
5538
|
});
|
|
5363
5539
|
}
|
|
5364
5540
|
});
|
|
@@ -6118,6 +6294,236 @@ class BeatmapEncoder extends Encoder {
|
|
|
6118
6294
|
}
|
|
6119
6295
|
}
|
|
6120
6296
|
|
|
6297
|
+
class ZeroCrossingBracketing {
|
|
6298
|
+
/**
|
|
6299
|
+
* Detect a range containing at least one root.
|
|
6300
|
+
*
|
|
6301
|
+
* This iterative method stops when two values with opposite signs are found.
|
|
6302
|
+
*
|
|
6303
|
+
* @param f The function to detect roots from.
|
|
6304
|
+
* @param bounds The upper and lower value of the range.
|
|
6305
|
+
* @param factor The growing factor of research. Defaults to 1.6.
|
|
6306
|
+
* @param maxIterations Maximum number of iterations. Defaults to 50.
|
|
6307
|
+
* @returns Whether the bracketing operation succeeded.
|
|
6308
|
+
*/
|
|
6309
|
+
static expand(f, bounds, factor = 1.6, maxIterations = 50) {
|
|
6310
|
+
const originalUpperBound = bounds.upperBound;
|
|
6311
|
+
const originalLowerBound = bounds.lowerBound;
|
|
6312
|
+
if (originalLowerBound >= originalUpperBound) {
|
|
6313
|
+
throw new RangeError("Upper bound must be greater than lower bound.");
|
|
6314
|
+
}
|
|
6315
|
+
let fmin = f(originalLowerBound);
|
|
6316
|
+
let fmax = f(originalUpperBound);
|
|
6317
|
+
for (let i = 0; i < maxIterations; ++i) {
|
|
6318
|
+
if (Math.sign(fmin) !== Math.sign(fmax)) {
|
|
6319
|
+
return true;
|
|
6320
|
+
}
|
|
6321
|
+
if (Math.abs(fmin) < Math.abs(fmax)) {
|
|
6322
|
+
bounds.lowerBound +=
|
|
6323
|
+
factor * (bounds.lowerBound - bounds.upperBound);
|
|
6324
|
+
fmin = f(bounds.lowerBound);
|
|
6325
|
+
}
|
|
6326
|
+
else {
|
|
6327
|
+
bounds.upperBound +=
|
|
6328
|
+
factor * (bounds.upperBound - bounds.lowerBound);
|
|
6329
|
+
fmax = f(bounds.upperBound);
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
bounds.lowerBound = originalLowerBound;
|
|
6333
|
+
bounds.upperBound = originalUpperBound;
|
|
6334
|
+
return false;
|
|
6335
|
+
}
|
|
6336
|
+
static reduce(f, bounds, subdivisions = 1000) {
|
|
6337
|
+
const originalUpperBound = bounds.upperBound;
|
|
6338
|
+
const originalLowerBound = bounds.lowerBound;
|
|
6339
|
+
if (originalLowerBound >= originalUpperBound) {
|
|
6340
|
+
throw new RangeError("Upper bound must be greater than lower bound.");
|
|
6341
|
+
}
|
|
6342
|
+
// TODO: Consider binary-style search instead of linear scan
|
|
6343
|
+
const fmin = f(bounds.lowerBound);
|
|
6344
|
+
const fmax = f(bounds.upperBound);
|
|
6345
|
+
if (Math.sign(fmin) != Math.sign(fmax)) {
|
|
6346
|
+
return true;
|
|
6347
|
+
}
|
|
6348
|
+
const subdiv = (bounds.upperBound - bounds.lowerBound) / subdivisions;
|
|
6349
|
+
let smin = bounds.lowerBound;
|
|
6350
|
+
const sign = Math.sign(fmin);
|
|
6351
|
+
for (let i = 0; i < subdivisions; ++i) {
|
|
6352
|
+
const smax = smin + subdiv;
|
|
6353
|
+
const sfmax = f(smax);
|
|
6354
|
+
if (!Number.isFinite(sfmax)) {
|
|
6355
|
+
// expand interval to include pole
|
|
6356
|
+
smin = smax;
|
|
6357
|
+
continue;
|
|
6358
|
+
}
|
|
6359
|
+
if (Math.sign(sfmax) != sign) {
|
|
6360
|
+
bounds.upperBound = smax;
|
|
6361
|
+
bounds.lowerBound = smin;
|
|
6362
|
+
return true;
|
|
6363
|
+
}
|
|
6364
|
+
smin = smax;
|
|
6365
|
+
}
|
|
6366
|
+
bounds.lowerBound = originalLowerBound;
|
|
6367
|
+
bounds.upperBound = originalUpperBound;
|
|
6368
|
+
return false;
|
|
6369
|
+
}
|
|
6370
|
+
static expandReduce(f, bounds, expansionFactor = 1.6, expansionMaxIterations = 50, reduceSubdivisions = 100) {
|
|
6371
|
+
return (this.expand(f, bounds, expansionFactor, expansionMaxIterations) ||
|
|
6372
|
+
this.reduce(f, bounds, reduceSubdivisions));
|
|
6373
|
+
}
|
|
6374
|
+
}
|
|
6375
|
+
|
|
6376
|
+
/**
|
|
6377
|
+
* Algorithm by Brent, Van Wijngaarden, Dekker et al.
|
|
6378
|
+
*
|
|
6379
|
+
* Implementation inspired by Press, Teukolsky, Vetterling, and Flannery, "Numerical Recipes in C", 2nd edition, Cambridge University Press.
|
|
6380
|
+
*/
|
|
6381
|
+
class Brent {
|
|
6382
|
+
/**
|
|
6383
|
+
* Finds a solution to the equation f(x) = 0.
|
|
6384
|
+
*
|
|
6385
|
+
* @param f The function to find roots from.
|
|
6386
|
+
* @param bounds The upper and lower root bounds.
|
|
6387
|
+
* @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.
|
|
6388
|
+
* @param maxIterations The maximum number of iterations. Defaults to 100.
|
|
6389
|
+
* @param expandFactor The factor at which to expand the bounds, if needed. Defaults to 1.6.
|
|
6390
|
+
* @param maxExpandIterations The maximum number of expand iterations. Defaults to 100.
|
|
6391
|
+
* @returns The root with the specified accuracy. Throws an error if the algorithm failed to converge.
|
|
6392
|
+
*/
|
|
6393
|
+
static findRootExpand(f, bounds, accuracy = 1e-8, maxIterations = 100, expandFactor = 1.6, maxExpandIterations = 100) {
|
|
6394
|
+
ZeroCrossingBracketing.expandReduce(f, bounds, expandFactor, maxExpandIterations, maxExpandIterations * 10);
|
|
6395
|
+
return this.findRoot(f, bounds, accuracy, maxIterations);
|
|
6396
|
+
}
|
|
6397
|
+
/**
|
|
6398
|
+
* Finds a solution to the equation f(x) = 0.
|
|
6399
|
+
*
|
|
6400
|
+
* @param f The function to find roots from.
|
|
6401
|
+
* @param bounds The upper and lower root bounds.
|
|
6402
|
+
* @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.
|
|
6403
|
+
* @param maxIterations The maximum number of iterations. Defaults to 100.
|
|
6404
|
+
* @returns The root with the specified accuracy. Throws an error if the algorithm failed to converge.
|
|
6405
|
+
*/
|
|
6406
|
+
static findRoot(f, bounds, accuracy = 1e-8, maxIterations = 100) {
|
|
6407
|
+
const root = this.tryFindRoot(f, bounds, accuracy, maxIterations);
|
|
6408
|
+
if (root === null) {
|
|
6409
|
+
throw new Error("The algorithm has failed, exceeded the number of iterations allowed or there is no root within the provided bounds.");
|
|
6410
|
+
}
|
|
6411
|
+
return root;
|
|
6412
|
+
}
|
|
6413
|
+
/**
|
|
6414
|
+
* Finds a solution to the equation f(x) = 0.
|
|
6415
|
+
*
|
|
6416
|
+
* @param f The function to find roots from.
|
|
6417
|
+
* @param bounds The upper and lower root bounds.
|
|
6418
|
+
* @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.
|
|
6419
|
+
* @param maxIterations The maximum number of iterations. Usually 100.
|
|
6420
|
+
* @returns The root with the specified accuracy, `null` if not found.
|
|
6421
|
+
*/
|
|
6422
|
+
static tryFindRoot(f, bounds, accuracy, maxIterations) {
|
|
6423
|
+
if (accuracy <= 0) {
|
|
6424
|
+
throw new RangeError("Accuracy must be greater than 0.");
|
|
6425
|
+
}
|
|
6426
|
+
let { lowerBound, upperBound } = bounds;
|
|
6427
|
+
let fmin = f(lowerBound);
|
|
6428
|
+
let fmax = f(upperBound);
|
|
6429
|
+
let froot = fmax;
|
|
6430
|
+
let d = 0;
|
|
6431
|
+
let e = 0;
|
|
6432
|
+
let root = upperBound;
|
|
6433
|
+
let xMid = Number.NaN;
|
|
6434
|
+
// Root must be bracketed.
|
|
6435
|
+
if (Math.sign(fmin) === Math.sign(fmax)) {
|
|
6436
|
+
return null;
|
|
6437
|
+
}
|
|
6438
|
+
for (let i = 0; i <= maxIterations; ++i) {
|
|
6439
|
+
// Adjust bounds.
|
|
6440
|
+
if (Math.sign(froot) === Math.sign(fmax)) {
|
|
6441
|
+
upperBound = lowerBound;
|
|
6442
|
+
fmax = fmin;
|
|
6443
|
+
e = d = root - lowerBound;
|
|
6444
|
+
}
|
|
6445
|
+
if (Math.abs(fmax) < Math.abs(froot)) {
|
|
6446
|
+
lowerBound = root;
|
|
6447
|
+
root = upperBound;
|
|
6448
|
+
upperBound = lowerBound;
|
|
6449
|
+
fmin = froot;
|
|
6450
|
+
froot = fmax;
|
|
6451
|
+
fmax = fmin;
|
|
6452
|
+
}
|
|
6453
|
+
// Convergence check
|
|
6454
|
+
const xAcc1 = 2 * Math.pow(2, -53) * Math.abs(root) + accuracy / 2;
|
|
6455
|
+
const xMidOld = xMid;
|
|
6456
|
+
xMid = (upperBound - root) / 2;
|
|
6457
|
+
if (Math.abs(xMid) <= xAcc1 ||
|
|
6458
|
+
Precision.almostEqualNormRelative(froot, 0, froot, accuracy)) {
|
|
6459
|
+
return root;
|
|
6460
|
+
}
|
|
6461
|
+
if (xMid === xMidOld) {
|
|
6462
|
+
// accuracy not sufficient, but cannot be improved further
|
|
6463
|
+
return null;
|
|
6464
|
+
}
|
|
6465
|
+
if (Math.abs(e) >= xAcc1 && Math.abs(fmin) > Math.abs(froot)) {
|
|
6466
|
+
// Attempt inverse quadratic interpolation
|
|
6467
|
+
const s = froot / fmin;
|
|
6468
|
+
let p;
|
|
6469
|
+
let q;
|
|
6470
|
+
if (Precision.almostEqualRelative(lowerBound, upperBound)) {
|
|
6471
|
+
p = 2 * xMid * s;
|
|
6472
|
+
q = 1 - s;
|
|
6473
|
+
}
|
|
6474
|
+
else {
|
|
6475
|
+
q = fmin / fmax;
|
|
6476
|
+
const r = froot / fmax;
|
|
6477
|
+
p =
|
|
6478
|
+
s *
|
|
6479
|
+
(2 * xMid * q * (q - r) -
|
|
6480
|
+
(root - lowerBound) * (r - 1));
|
|
6481
|
+
q = (q - 1) * (r - 1) * (s - 1);
|
|
6482
|
+
}
|
|
6483
|
+
if (p > 0) {
|
|
6484
|
+
// Check whether in bounds
|
|
6485
|
+
q = -q;
|
|
6486
|
+
}
|
|
6487
|
+
p = Math.abs(p);
|
|
6488
|
+
if (2 * p <
|
|
6489
|
+
Math.min(3 * xMid * q - Math.abs(xAcc1 * q), Math.abs(e * q))) {
|
|
6490
|
+
// Accept interpolation
|
|
6491
|
+
e = d;
|
|
6492
|
+
d = p / q;
|
|
6493
|
+
}
|
|
6494
|
+
else {
|
|
6495
|
+
// Interpolation failed, use bisection
|
|
6496
|
+
d = xMid;
|
|
6497
|
+
e = d;
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
else {
|
|
6501
|
+
// Bounds decreasing too slowly, use bisection
|
|
6502
|
+
d = xMid;
|
|
6503
|
+
e = d;
|
|
6504
|
+
}
|
|
6505
|
+
lowerBound = root;
|
|
6506
|
+
fmin = froot;
|
|
6507
|
+
if (Math.abs(d) > xAcc1) {
|
|
6508
|
+
root += d;
|
|
6509
|
+
}
|
|
6510
|
+
else {
|
|
6511
|
+
root += this.sign(xAcc1, xMid);
|
|
6512
|
+
}
|
|
6513
|
+
froot = f(root);
|
|
6514
|
+
}
|
|
6515
|
+
return null;
|
|
6516
|
+
}
|
|
6517
|
+
/**
|
|
6518
|
+
* Helper method useful for preventing rounding errors.
|
|
6519
|
+
*
|
|
6520
|
+
* @returns a * sign(b)
|
|
6521
|
+
*/
|
|
6522
|
+
static sign(a, b) {
|
|
6523
|
+
return b >= 0 ? (a >= 0 ? a : -a) : a >= 0 ? -a : a;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6121
6527
|
/**
|
|
6122
6528
|
* Types of easing.
|
|
6123
6529
|
*
|
|
@@ -6583,6 +6989,29 @@ class ErrorFunction {
|
|
|
6583
6989
|
}
|
|
6584
6990
|
return this.erfImp(x, false);
|
|
6585
6991
|
}
|
|
6992
|
+
/**
|
|
6993
|
+
* Calculates the complementary error function.
|
|
6994
|
+
*
|
|
6995
|
+
* @param x The value to evaluate.
|
|
6996
|
+
* @returns The complementary error function evaluated at given value, or:
|
|
6997
|
+
* - 0 if `x === Number.POSITIVE_INFINITY`;
|
|
6998
|
+
* - 2 if `x === Number.NEGATIVE_INFINITY`.
|
|
6999
|
+
*/
|
|
7000
|
+
static erfc(x) {
|
|
7001
|
+
if (x === 0) {
|
|
7002
|
+
return 1;
|
|
7003
|
+
}
|
|
7004
|
+
if (x === Number.POSITIVE_INFINITY) {
|
|
7005
|
+
return 0;
|
|
7006
|
+
}
|
|
7007
|
+
if (x === Number.NEGATIVE_INFINITY) {
|
|
7008
|
+
return 2;
|
|
7009
|
+
}
|
|
7010
|
+
if (Number.isNaN(x)) {
|
|
7011
|
+
return Number.NaN;
|
|
7012
|
+
}
|
|
7013
|
+
return this.erfImp(x, true);
|
|
7014
|
+
}
|
|
6586
7015
|
/**
|
|
6587
7016
|
* Calculates the inverse error function evaluated at z.
|
|
6588
7017
|
*
|
|
@@ -7426,6 +7855,16 @@ class MapInfo {
|
|
|
7426
7855
|
}
|
|
7427
7856
|
}
|
|
7428
7857
|
|
|
7858
|
+
/**
|
|
7859
|
+
* Represents the osu! playfield.
|
|
7860
|
+
*/
|
|
7861
|
+
class Playfield {
|
|
7862
|
+
/**
|
|
7863
|
+
* The size of the playfield, which is 512x384.
|
|
7864
|
+
*/
|
|
7865
|
+
static baseSize = new Vector2(512, 384);
|
|
7866
|
+
}
|
|
7867
|
+
|
|
7429
7868
|
dotenv.config();
|
|
7430
7869
|
|
|
7431
7870
|
exports.Accuracy = Accuracy;
|
|
@@ -7444,6 +7883,7 @@ exports.BeatmapMetadata = BeatmapMetadata;
|
|
|
7444
7883
|
exports.BeatmapVideo = BeatmapVideo;
|
|
7445
7884
|
exports.BlendingParameters = BlendingParameters;
|
|
7446
7885
|
exports.BreakPoint = BreakPoint;
|
|
7886
|
+
exports.Brent = Brent;
|
|
7447
7887
|
exports.Circle = Circle;
|
|
7448
7888
|
exports.Command = Command;
|
|
7449
7889
|
exports.CommandLoop = CommandLoop;
|
|
@@ -7488,6 +7928,7 @@ exports.ModUtil = ModUtil;
|
|
|
7488
7928
|
exports.OsuAPIRequestBuilder = OsuAPIRequestBuilder;
|
|
7489
7929
|
exports.OsuHitWindow = OsuHitWindow;
|
|
7490
7930
|
exports.PathApproximator = PathApproximator;
|
|
7931
|
+
exports.Playfield = Playfield;
|
|
7491
7932
|
exports.Polynomial = Polynomial;
|
|
7492
7933
|
exports.Precision = Precision;
|
|
7493
7934
|
exports.RGBColor = RGBColor;
|
|
@@ -7513,4 +7954,5 @@ exports.TimingControlPoint = TimingControlPoint;
|
|
|
7513
7954
|
exports.TimingControlPointManager = TimingControlPointManager;
|
|
7514
7955
|
exports.Utils = Utils;
|
|
7515
7956
|
exports.Vector2 = Vector2;
|
|
7957
|
+
exports.ZeroCrossingBracketing = ZeroCrossingBracketing;
|
|
7516
7958
|
//# sourceMappingURL=index.js.map
|