kipphi 2.1.3-beta.2 → 2.1.4-beta.1
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/event.ts +2 -1
- package/index.d.ts +7 -129
- package/index.js +128 -9
- package/judgeline.ts +0 -127
- package/note.ts +6 -1
- package/operation/event.ts +9 -1
- package/package.json +1 -1
- package/rpeChartCompiler.ts +47 -9
- package/time.ts +10 -0
- package/util.ts +50 -0
- package/version.ts +1 -1
package/event.ts
CHANGED
|
@@ -391,7 +391,7 @@ export class EventStartNode<VT extends EventValueESType = number> extends EventN
|
|
|
391
391
|
return super.clone(offset) as EventStartNode<VT>;
|
|
392
392
|
};
|
|
393
393
|
clonePair(offset: TimeT): EventStartNode<VT> {
|
|
394
|
-
const endNode = this.previous.type !== NodeType.HEAD ? this.previous.clone(offset) :
|
|
394
|
+
const endNode = this.previous.type !== NodeType.HEAD ? this.previous.clone(offset) : null;
|
|
395
395
|
const startNode = this.clone(offset);
|
|
396
396
|
EventNode.connect(endNode, startNode);
|
|
397
397
|
return startNode;
|
|
@@ -518,6 +518,7 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
|
|
|
518
518
|
*/
|
|
519
519
|
static fromRPEJSON<T extends EventType, VT extends EventValueESType = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, pos: string, endValue?: number) {
|
|
520
520
|
const {templateEasingLib: templates} = chart
|
|
521
|
+
data.sort((a, b) => TC.cmp(a.startTime, b.startTime));
|
|
521
522
|
const length = data.length;
|
|
522
523
|
// const isSpeed = type === EventType.Speed;
|
|
523
524
|
// console.log(isSpeed)
|
package/index.d.ts
CHANGED
|
@@ -553,7 +553,7 @@ declare module "chartTypes" {
|
|
|
553
553
|
export type ExtendedEventTypeName = "scaleX" | "scaleY" | "text" | "color";
|
|
554
554
|
}
|
|
555
555
|
declare module "version" {
|
|
556
|
-
export const VERSION =
|
|
556
|
+
export const VERSION = 214;
|
|
557
557
|
export const SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
|
|
558
558
|
}
|
|
559
559
|
declare module "util" {
|
|
@@ -1081,133 +1081,6 @@ declare module "judgeline" {
|
|
|
1081
1081
|
cachedFloorPositions: Float64Array;
|
|
1082
1082
|
computeCurrentFloorPosition(beats: number, timeCalculator: TimeCalculator): void;
|
|
1083
1083
|
getRelativeFloorPositionAt(beats: number, timeCalculator: TimeCalculator): number;
|
|
1084
|
-
/**
|
|
1085
|
-
* 通过速度序列的FloorPosition反解出一个时间范围。
|
|
1086
|
-
*
|
|
1087
|
-
* KPA内核代码中最大的一坨史山,没有之一。
|
|
1088
|
-
*
|
|
1089
|
-
* 谱面渲染时最耗时的函数
|
|
1090
|
-
*
|
|
1091
|
-
* startY and endY must not be negative
|
|
1092
|
-
* @param beats
|
|
1093
|
-
* @param timeCalculator
|
|
1094
|
-
* @param startY
|
|
1095
|
-
* @param endY
|
|
1096
|
-
* @returns
|
|
1097
|
-
* /
|
|
1098
|
-
computeTimeRange(beats: number, timeCalculator: TimeCalculator , startY: number, endY: number): [number, number][] {
|
|
1099
|
-
//return [[0, Infinity]]
|
|
1100
|
-
//*
|
|
1101
|
-
// 提取所有有变化的时间点
|
|
1102
|
-
let times: number[] = [];
|
|
1103
|
-
const result: [number, number][] = [];
|
|
1104
|
-
for (const eventLayer of this.eventLayers) {
|
|
1105
|
-
const sequence = eventLayer?.speed;
|
|
1106
|
-
if (!sequence) {
|
|
1107
|
-
continue;
|
|
1108
|
-
}
|
|
1109
|
-
let node: EventStartNode = sequence.getNodeAt(beats);
|
|
1110
|
-
let endNode: EventEndNode | EventNodeLike<NodeType.TAIL>
|
|
1111
|
-
while (true) {
|
|
1112
|
-
times.push(TC.toBeats(node.time))
|
|
1113
|
-
if ((endNode = node.next).type === NodeType.TAIL) {
|
|
1114
|
-
break;
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
node = endNode.next
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
times = [...new Set(times)].sort((a, b) => a - b)
|
|
1121
|
-
const len = times.length;
|
|
1122
|
-
let nextTime = times[0]
|
|
1123
|
-
let nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator)
|
|
1124
|
-
let nextSpeed = this.getStackedValue("speed", nextTime, true)
|
|
1125
|
-
let range: [number, number] = [undefined, undefined];
|
|
1126
|
-
// console.log(times)
|
|
1127
|
-
const computeTime = (speed: number, currentPos: number, fore: number) => timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
|
|
1128
|
-
for (let i = 0; i < len - 1;) {
|
|
1129
|
-
const thisTime = nextTime;
|
|
1130
|
-
const thisPosY = nextPosY;
|
|
1131
|
-
let thisSpeed = this.getStackedValue("speed", thisTime);
|
|
1132
|
-
if (Math.abs(thisSpeed) < 1e-8) {
|
|
1133
|
-
thisSpeed = 0; // 不这样做可能导致下面异号判断为真从而死循环
|
|
1134
|
-
}
|
|
1135
|
-
nextTime = times[i + 1]
|
|
1136
|
-
nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator);
|
|
1137
|
-
nextSpeed = this.getStackedValue("speed", nextTime, true)
|
|
1138
|
-
// console.log(thisSpeed, nextSpeed, thisSpeed * nextSpeed < 0, i, [...result])
|
|
1139
|
-
if (thisSpeed * nextSpeed < 0) { // 有变号零点,再次切断,保证处理的每个区间单调性
|
|
1140
|
-
//debugger;
|
|
1141
|
-
nextTime = (nextTime - thisTime) * (0 - thisSpeed) / (nextSpeed - thisSpeed) + thisTime;
|
|
1142
|
-
nextSpeed = 0
|
|
1143
|
-
nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator)
|
|
1144
|
-
//debugger
|
|
1145
|
-
} else {
|
|
1146
|
-
// console.log("i++")
|
|
1147
|
-
i++
|
|
1148
|
-
}
|
|
1149
|
-
if (range[0] === undefined) {
|
|
1150
|
-
// 变速区间直接全部囊括,匀速要算一下,因为好算
|
|
1151
|
-
/*
|
|
1152
|
-
设两个时间点的位置为a,b
|
|
1153
|
-
开始结束点为s,e
|
|
1154
|
-
选中小段一部分在区间内:
|
|
1155
|
-
a < s <= b
|
|
1156
|
-
或a > e >= b
|
|
1157
|
-
全部在区间内
|
|
1158
|
-
s <= a <= b
|
|
1159
|
-
* /
|
|
1160
|
-
if (thisPosY < startY && startY <= nextPosY
|
|
1161
|
-
|| thisPosY > endY && endY >= nextPosY) {
|
|
1162
|
-
range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(
|
|
1163
|
-
thisSpeed,
|
|
1164
|
-
(thisPosY < nextPosY ? startY : endY) - thisPosY, thisTime)
|
|
1165
|
-
} else if (startY <= thisPosY && thisPosY <= endY) {
|
|
1166
|
-
range[0] = thisTime;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
// 要注意这里不能合成双分支if因为想要的Y片段可能在一个区间内
|
|
1170
|
-
if (range[0] !== undefined) {
|
|
1171
|
-
if (thisPosY < endY && endY <= nextPosY || thisPosY > startY && startY >= nextPosY) {
|
|
1172
|
-
range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(
|
|
1173
|
-
thisSpeed,
|
|
1174
|
-
(thisPosY > nextPosY ? startY : endY) - thisPosY, thisTime)
|
|
1175
|
-
result.push(range)
|
|
1176
|
-
range = [undefined, undefined];
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
const thisPosY = nextPosY;
|
|
1181
|
-
const thisTime = nextTime;
|
|
1182
|
-
const thisSpeed = this.getStackedValue("speed", thisTime);
|
|
1183
|
-
const inf = thisSpeed > 0 ? Infinity : (thisSpeed < 0 ? -Infinity : thisPosY)
|
|
1184
|
-
if (range[0] === undefined) {
|
|
1185
|
-
// 变速区间直接全部囊括,匀速要算一下,因为好算
|
|
1186
|
-
if (thisPosY < startY && startY <= inf || thisPosY >= endY && endY > inf) {
|
|
1187
|
-
range[0] = computeTime(
|
|
1188
|
-
thisSpeed,
|
|
1189
|
-
(thisPosY < inf ? startY : endY) - thisPosY,
|
|
1190
|
-
thisTime)
|
|
1191
|
-
} else if (thisSpeed === 0) {
|
|
1192
|
-
range[0] = 0;
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
// 要注意这里不能合成双分支if因为想要的Y片段可能在一个区间内
|
|
1196
|
-
if (range[0] !== undefined) {
|
|
1197
|
-
if (thisPosY < endY && endY <= inf || thisPosY >= startY && startY > inf) {
|
|
1198
|
-
range[1] = computeTime(
|
|
1199
|
-
thisSpeed,
|
|
1200
|
-
(thisPosY > inf ? startY : endY) - thisPosY,
|
|
1201
|
-
thisTime)
|
|
1202
|
-
result.push(range)
|
|
1203
|
-
} else if (thisSpeed === 0) {
|
|
1204
|
-
range[1] = Infinity;
|
|
1205
|
-
result.push(range)
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
return result;
|
|
1209
|
-
//* /
|
|
1210
|
-
}*/
|
|
1211
1084
|
/**
|
|
1212
1085
|
* 通过速度序列的FloorPosition反解出一个时间范围。
|
|
1213
1086
|
*
|
|
@@ -2415,6 +2288,7 @@ declare module "time" {
|
|
|
2415
2288
|
static gt(beaT1: TimeT, beaT2: TimeT): boolean;
|
|
2416
2289
|
/** @returns beaT1 < beaT2 */
|
|
2417
2290
|
static lt(beaT1: TimeT, beaT2: TimeT): boolean;
|
|
2291
|
+
static cmp(beaT1: TimeT, beaT2: TimeT): number;
|
|
2418
2292
|
/** @returns beaT1 != beaT2 */
|
|
2419
2293
|
static ne(beaT1: TimeT, beaT2: TimeT): boolean;
|
|
2420
2294
|
/**
|
|
@@ -2608,6 +2482,9 @@ declare module "operation/event" {
|
|
|
2608
2482
|
overlapping: boolean;
|
|
2609
2483
|
constructor(node: EventStartNode<VT>, targetPrevious: EventStartNode<VT>, updatesFP?: boolean);
|
|
2610
2484
|
}
|
|
2485
|
+
export class EventNodePairAutoInsertOperation<VT extends EventValueESType> extends EventNodePairInsertOperation<VT> {
|
|
2486
|
+
constructor(node: EventStartNode<VT>, parentSeq: EventNodeSequence<VT>, updatesFP?: boolean);
|
|
2487
|
+
}
|
|
2611
2488
|
export class EventNodeValueChangeOperation<VT extends EventValueESType> extends Operation {
|
|
2612
2489
|
updatesEditor: boolean;
|
|
2613
2490
|
node: EventNode<VT>;
|
|
@@ -3082,7 +2959,7 @@ declare module "rpeChartCompiler" {
|
|
|
3082
2959
|
import type { Chart } from "chart";
|
|
3083
2960
|
import { type TimeT, type ChartDataRPE, type JudgeLineDataRPE, type EventDataRPELike, type NoteDataRPE, type EventValueESType } from "chartTypes";
|
|
3084
2961
|
import { type EasedEvaluatorOfType } from "evaluator";
|
|
3085
|
-
import { EventEndNode, EventNodeSequence, EventStartNode } from "event";
|
|
2962
|
+
import { EventEndNode, EventNodeSequence, EventStartNode, SpeedENS } from "event";
|
|
3086
2963
|
import type { JudgeLine } from "judgeline";
|
|
3087
2964
|
import type { NNList, HNList } from "note";
|
|
3088
2965
|
/**
|
|
@@ -3100,6 +2977,7 @@ declare module "rpeChartCompiler" {
|
|
|
3100
2977
|
evaluator: EasedEvaluatorOfType<VT>;
|
|
3101
2978
|
}, getValue: (node: EventStartNode<VT> | EventEndNode<VT>) => VT): EventDataRPELike<VT>;
|
|
3102
2979
|
dumpEventNodeSequence<VT extends EventValueESType>(sequence: EventNodeSequence<VT>): EventDataRPELike<VT>[];
|
|
2980
|
+
dumpSpeedENS(seq: SpeedENS): EventDataRPELike<number>[];
|
|
3103
2981
|
compileNNLists(nnLists: NNList[], hnLists: HNList[]): NoteDataRPE[];
|
|
3104
2982
|
/**
|
|
3105
2983
|
* 倒序转换为数组
|
package/index.js
CHANGED
|
@@ -24,6 +24,61 @@ var checkType = (value, type) => {
|
|
|
24
24
|
if (Array.isArray(type)) {
|
|
25
25
|
return Array.isArray(value) && value.length === type.length && type.every((t, i) => checkType(value[i], t));
|
|
26
26
|
} else if (typeof type === "string") {
|
|
27
|
+
if (type.startsWith("int")) {
|
|
28
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const match = type.match(/^int(\(|\[)(\-?\d+),(\-?\d+|\+)(\)|\])$/);
|
|
32
|
+
if (!match) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const [, leftBrac, left, right, rightBrac] = match;
|
|
36
|
+
if (!leftBrac) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const leftN = left === "-" ? -Infinity : Number(left);
|
|
40
|
+
const rightN = right === "+" ? Infinity : Number(right);
|
|
41
|
+
if (value < leftN) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (leftBrac === "(" && value === leftN) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (value > rightN) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (rightBrac === ")" && value === rightN) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
} else if (type.startsWith("number")) {
|
|
55
|
+
if (typeof value !== "number") {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const match = type.match(/^number(\(|\[)(\-?\d+),(\-?\d+|\+)(\)|\])$/);
|
|
59
|
+
if (!match) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const [, leftBrac, left, right, rightBrac] = match;
|
|
63
|
+
if (!leftBrac) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
const leftN = left === "-" ? -Infinity : Number(left);
|
|
67
|
+
const rightN = right === "+" ? Infinity : Number(right);
|
|
68
|
+
if (value < leftN) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (leftBrac === "(" && value === leftN) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (value > rightN) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (rightBrac === ")" && value === rightN) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
27
82
|
return typeof value === type;
|
|
28
83
|
} else {
|
|
29
84
|
return value instanceof type;
|
|
@@ -195,6 +250,16 @@ class TC2 {
|
|
|
195
250
|
static lt(beaT1, beaT2) {
|
|
196
251
|
return beaT1[0] < beaT2[0] || beaT1[0] === beaT2[0] && beaT1[1] * beaT2[2] < beaT1[2] * beaT2[1];
|
|
197
252
|
}
|
|
253
|
+
static cmp(beaT1, beaT2) {
|
|
254
|
+
if (beaT1[0] > beaT2[0]) {
|
|
255
|
+
return 1;
|
|
256
|
+
} else if (beaT1[0] < beaT2[0]) {
|
|
257
|
+
return -1;
|
|
258
|
+
} else {
|
|
259
|
+
return beaT1[1] * beaT2[2] > beaT1[2] * beaT2[1] ? 1 : -1;
|
|
260
|
+
}
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
198
263
|
static ne(beaT1, beaT2) {
|
|
199
264
|
return beaT1[0] !== beaT2[0] || beaT1[1] * beaT2[2] !== beaT1[2] * beaT2[1];
|
|
200
265
|
}
|
|
@@ -1645,7 +1710,7 @@ class EventStartNode extends EventNode {
|
|
|
1645
1710
|
return super.clone(offset);
|
|
1646
1711
|
}
|
|
1647
1712
|
clonePair(offset) {
|
|
1648
|
-
const endNode = this.previous.type !== 0 /* HEAD */ ? this.previous.clone(offset) :
|
|
1713
|
+
const endNode = this.previous.type !== 0 /* HEAD */ ? this.previous.clone(offset) : null;
|
|
1649
1714
|
const startNode = this.clone(offset);
|
|
1650
1715
|
EventNode.connect(endNode, startNode);
|
|
1651
1716
|
return startNode;
|
|
@@ -1701,6 +1766,7 @@ class EventNodeSequence {
|
|
|
1701
1766
|
}
|
|
1702
1767
|
static fromRPEJSON(type, data, chart, pos, endValue) {
|
|
1703
1768
|
const { templateEasingLib: templates } = chart;
|
|
1769
|
+
data.sort((a, b) => TC2.cmp(a.startTime, b.startTime));
|
|
1704
1770
|
const length = data.length;
|
|
1705
1771
|
const seq = new EventNodeSequence(type, type === 5 /* easing */ ? TC2.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
|
|
1706
1772
|
let listLength = length;
|
|
@@ -2280,7 +2346,12 @@ class NoteNode extends NoteNodeLike {
|
|
|
2280
2346
|
}
|
|
2281
2347
|
}
|
|
2282
2348
|
remove(note) {
|
|
2283
|
-
this.notes.
|
|
2349
|
+
const index = this.notes.indexOf(note);
|
|
2350
|
+
if (index === -1) {
|
|
2351
|
+
console.warn("Note not found in this node!");
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
this.notes.splice(index, 1);
|
|
2284
2355
|
note.parentNode = null;
|
|
2285
2356
|
}
|
|
2286
2357
|
static disconnect(note1, note2) {
|
|
@@ -3319,7 +3390,7 @@ class TimeCalculator {
|
|
|
3319
3390
|
}
|
|
3320
3391
|
}
|
|
3321
3392
|
// src/version.ts
|
|
3322
|
-
var VERSION =
|
|
3393
|
+
var VERSION = 214;
|
|
3323
3394
|
var SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
|
|
3324
3395
|
|
|
3325
3396
|
// src/chart.ts
|
|
@@ -3857,6 +3928,7 @@ __export(exports_operation, {
|
|
|
3857
3928
|
EventNodePairRemoveOperation: () => EventNodePairRemoveOperation,
|
|
3858
3929
|
EventNodePairInsertOrOverwriteOperation: () => EventNodePairInsertOrOverwriteOperation,
|
|
3859
3930
|
EventNodePairInsertOperation: () => EventNodePairInsertOperation,
|
|
3931
|
+
EventNodePairAutoInsertOperation: () => EventNodePairAutoInsertOperation,
|
|
3860
3932
|
EventNodeMacroTimeReevaluateOperation: () => EventNodeMacroTimeReevaluateOperation,
|
|
3861
3933
|
EventNodeEvaluatorChangeOperation: () => EventNodeEvaluatorChangeOperation,
|
|
3862
3934
|
EventInterpolationOperation: () => EventInterpolationOperation,
|
|
@@ -4192,6 +4264,12 @@ class EventNodePairInsertOrOverwriteOperation extends UnionOperation {
|
|
|
4192
4264
|
}
|
|
4193
4265
|
}
|
|
4194
4266
|
|
|
4267
|
+
class EventNodePairAutoInsertOperation extends EventNodePairInsertOperation {
|
|
4268
|
+
constructor(node, parentSeq, updatesFP = true) {
|
|
4269
|
+
super(node, parentSeq.getNodeAt(TC2.toBeats(node.time)), updatesFP);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4195
4273
|
class EventNodeValueChangeOperation extends Operation {
|
|
4196
4274
|
updatesEditor = true;
|
|
4197
4275
|
node;
|
|
@@ -4404,6 +4482,9 @@ class EncapsuleOperation extends ComplexOperation {
|
|
|
4404
4482
|
const sequence = easing.eventNodeSequence;
|
|
4405
4483
|
sequence.effectiveBeats = TC2.toBeats(nodeArray[nodeArray.length - 1].time);
|
|
4406
4484
|
new MultiNodeAddOperation(nodeArray, sequence).do();
|
|
4485
|
+
const first = sequence.head.next;
|
|
4486
|
+
first.evaluator = nodeArray[0].evaluator;
|
|
4487
|
+
first.value = nodeArray[0].value;
|
|
4407
4488
|
return new EncapsuleOperation(oldArray, easing);
|
|
4408
4489
|
}
|
|
4409
4490
|
}
|
|
@@ -5343,8 +5424,20 @@ class RPEChartCompiler {
|
|
|
5343
5424
|
compileChart() {
|
|
5344
5425
|
const chart2 = this.chart;
|
|
5345
5426
|
const judgeLineGroups = chart2.judgeLineGroups.map((group) => group.name);
|
|
5427
|
+
const hasNotes = (nnList) => {
|
|
5428
|
+
let node = nnList.head.next;
|
|
5429
|
+
while (true) {
|
|
5430
|
+
if (node.type === 1 /* TAIL */) {
|
|
5431
|
+
return false;
|
|
5432
|
+
}
|
|
5433
|
+
if (node.notes.length > 0) {
|
|
5434
|
+
return true;
|
|
5435
|
+
}
|
|
5436
|
+
node = node.next;
|
|
5437
|
+
}
|
|
5438
|
+
};
|
|
5346
5439
|
const filter = this.deletesEmptyLines ? (line2) => {
|
|
5347
|
-
return line2.nnLists.
|
|
5440
|
+
return [...line2.nnLists].some(([_, l]) => hasNotes(l)) || [...line2.hnLists].some(([_, l]) => hasNotes(l)) || line2.eventLayers.length > 0 && ["moveX", "moveY", "rotate", "alpha"].some((evType) => {
|
|
5348
5441
|
const seq = line2.eventLayers[0][evType];
|
|
5349
5442
|
let node = seq.head.next;
|
|
5350
5443
|
for (let i = 0;i < 2; i++) {
|
|
@@ -5427,7 +5520,7 @@ class RPEChartCompiler {
|
|
|
5427
5520
|
moveYEvents: layer.moveY ? this.dumpEventNodeSequence(layer.moveY) : undefined,
|
|
5428
5521
|
rotateEvents: layer.rotate ? this.dumpEventNodeSequence(layer.rotate) : undefined,
|
|
5429
5522
|
alphaEvents: layer.alpha ? this.dumpEventNodeSequence(layer.alpha) : undefined,
|
|
5430
|
-
speedEvents: index === 0 ? this.
|
|
5523
|
+
speedEvents: index === 0 ? this.dumpSpeedENS(judgeLine.speedSequence) : undefined
|
|
5431
5524
|
})),
|
|
5432
5525
|
extended: {
|
|
5433
5526
|
scaleXEvents: judgeLine.extendedLayer.scaleX ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleX) : undefined,
|
|
@@ -5525,6 +5618,32 @@ class RPEChartCompiler {
|
|
|
5525
5618
|
nodes.push(this.compileEasedEvent(newStart, getValue));
|
|
5526
5619
|
return nodes;
|
|
5527
5620
|
}
|
|
5621
|
+
dumpSpeedENS(seq) {
|
|
5622
|
+
const ret = [];
|
|
5623
|
+
let node = seq.head.next;
|
|
5624
|
+
while (true) {
|
|
5625
|
+
const end = node.next;
|
|
5626
|
+
if (end.type === 1 /* TAIL */) {
|
|
5627
|
+
break;
|
|
5628
|
+
}
|
|
5629
|
+
ret.push({
|
|
5630
|
+
start: node.value,
|
|
5631
|
+
end: end.value,
|
|
5632
|
+
startTime: node.time,
|
|
5633
|
+
endTime: end.time,
|
|
5634
|
+
linkgroup: 0
|
|
5635
|
+
});
|
|
5636
|
+
node = end.next;
|
|
5637
|
+
}
|
|
5638
|
+
ret.push({
|
|
5639
|
+
start: node.value,
|
|
5640
|
+
end: node.value,
|
|
5641
|
+
startTime: node.time,
|
|
5642
|
+
endTime: TC2.vadd(node.time, [1, 0, 1]),
|
|
5643
|
+
linkgroup: 0
|
|
5644
|
+
});
|
|
5645
|
+
return ret;
|
|
5646
|
+
}
|
|
5528
5647
|
compileNNLists(nnLists, hnLists) {
|
|
5529
5648
|
const noteLists = nnLists.map((list) => this.nnListToArray(list));
|
|
5530
5649
|
const holdLists = hnLists.map((list) => this.nnListToArray(list));
|
|
@@ -5608,8 +5727,8 @@ class RPEChartCompiler {
|
|
|
5608
5727
|
srcStart = srcSeq.head.next.value;
|
|
5609
5728
|
srcEnd = srcSeq.tail.previous.value;
|
|
5610
5729
|
leftDividedNodeSrc = srcSeq.head.next;
|
|
5611
|
-
rightDividedNodeSrc = srcSeq.tail.previous;
|
|
5612
|
-
toStopAt =
|
|
5730
|
+
rightDividedNodeSrc = srcSeq.tail.previous.previous.previous;
|
|
5731
|
+
toStopAt = srcSeq.tail.previous;
|
|
5613
5732
|
srcStartTime = srcSeq.head.next.time;
|
|
5614
5733
|
srcTimeDelta = TC2.sub(srcSeq.tail.previous.time, srcStartTime);
|
|
5615
5734
|
}
|
|
@@ -5656,13 +5775,13 @@ class RPEChartCompiler {
|
|
|
5656
5775
|
if (rightDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
5657
5776
|
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
5658
5777
|
} else {
|
|
5659
|
-
|
|
5778
|
+
prev.evaluator = evaluator.deriveWithEasing(new SegmentedEasing(rightDividedNodeSrc.evaluator.easing, 0, newRight));
|
|
5660
5779
|
}
|
|
5661
5780
|
} else {
|
|
5662
5781
|
if (rightDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
5663
5782
|
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
5664
5783
|
} else {
|
|
5665
|
-
|
|
5784
|
+
prev.evaluator = evaluator.deriveWithEasing(rightDividedNodeSrc.evaluator.easing);
|
|
5666
5785
|
}
|
|
5667
5786
|
}
|
|
5668
5787
|
const endNode2 = currentNode.next.clone();
|
package/judgeline.ts
CHANGED
|
@@ -405,133 +405,6 @@ export class JudgeLine {
|
|
|
405
405
|
getRelativeFloorPositionAt(beats: number, timeCalculator: TimeCalculator) {
|
|
406
406
|
return this.speedSequence.getFloorPositionAt(beats, timeCalculator) - this.currentFloorPosition;
|
|
407
407
|
}
|
|
408
|
-
/**
|
|
409
|
-
* 通过速度序列的FloorPosition反解出一个时间范围。
|
|
410
|
-
*
|
|
411
|
-
* KPA内核代码中最大的一坨史山,没有之一。
|
|
412
|
-
*
|
|
413
|
-
* 谱面渲染时最耗时的函数
|
|
414
|
-
*
|
|
415
|
-
* startY and endY must not be negative
|
|
416
|
-
* @param beats
|
|
417
|
-
* @param timeCalculator
|
|
418
|
-
* @param startY
|
|
419
|
-
* @param endY
|
|
420
|
-
* @returns
|
|
421
|
-
* /
|
|
422
|
-
computeTimeRange(beats: number, timeCalculator: TimeCalculator , startY: number, endY: number): [number, number][] {
|
|
423
|
-
//return [[0, Infinity]]
|
|
424
|
-
//*
|
|
425
|
-
// 提取所有有变化的时间点
|
|
426
|
-
let times: number[] = [];
|
|
427
|
-
const result: [number, number][] = [];
|
|
428
|
-
for (const eventLayer of this.eventLayers) {
|
|
429
|
-
const sequence = eventLayer?.speed;
|
|
430
|
-
if (!sequence) {
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
let node: EventStartNode = sequence.getNodeAt(beats);
|
|
434
|
-
let endNode: EventEndNode | EventNodeLike<NodeType.TAIL>
|
|
435
|
-
while (true) {
|
|
436
|
-
times.push(TC.toBeats(node.time))
|
|
437
|
-
if ((endNode = node.next).type === NodeType.TAIL) {
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
node = endNode.next
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
times = [...new Set(times)].sort((a, b) => a - b)
|
|
445
|
-
const len = times.length;
|
|
446
|
-
let nextTime = times[0]
|
|
447
|
-
let nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator)
|
|
448
|
-
let nextSpeed = this.getStackedValue("speed", nextTime, true)
|
|
449
|
-
let range: [number, number] = [undefined, undefined];
|
|
450
|
-
// console.log(times)
|
|
451
|
-
const computeTime = (speed: number, currentPos: number, fore: number) => timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
|
|
452
|
-
for (let i = 0; i < len - 1;) {
|
|
453
|
-
const thisTime = nextTime;
|
|
454
|
-
const thisPosY = nextPosY;
|
|
455
|
-
let thisSpeed = this.getStackedValue("speed", thisTime);
|
|
456
|
-
if (Math.abs(thisSpeed) < 1e-8) {
|
|
457
|
-
thisSpeed = 0; // 不这样做可能导致下面异号判断为真从而死循环
|
|
458
|
-
}
|
|
459
|
-
nextTime = times[i + 1]
|
|
460
|
-
nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator);
|
|
461
|
-
nextSpeed = this.getStackedValue("speed", nextTime, true)
|
|
462
|
-
// console.log(thisSpeed, nextSpeed, thisSpeed * nextSpeed < 0, i, [...result])
|
|
463
|
-
if (thisSpeed * nextSpeed < 0) { // 有变号零点,再次切断,保证处理的每个区间单调性
|
|
464
|
-
//debugger;
|
|
465
|
-
nextTime = (nextTime - thisTime) * (0 - thisSpeed) / (nextSpeed - thisSpeed) + thisTime;
|
|
466
|
-
nextSpeed = 0
|
|
467
|
-
nextPosY = this.getStackedFloorPosition(nextTime, timeCalculator)
|
|
468
|
-
//debugger
|
|
469
|
-
} else {
|
|
470
|
-
// console.log("i++")
|
|
471
|
-
i++
|
|
472
|
-
}
|
|
473
|
-
if (range[0] === undefined) {
|
|
474
|
-
// 变速区间直接全部囊括,匀速要算一下,因为好算
|
|
475
|
-
/*
|
|
476
|
-
设两个时间点的位置为a,b
|
|
477
|
-
开始结束点为s,e
|
|
478
|
-
选中小段一部分在区间内:
|
|
479
|
-
a < s <= b
|
|
480
|
-
或a > e >= b
|
|
481
|
-
全部在区间内
|
|
482
|
-
s <= a <= b
|
|
483
|
-
* /
|
|
484
|
-
if (thisPosY < startY && startY <= nextPosY
|
|
485
|
-
|| thisPosY > endY && endY >= nextPosY) {
|
|
486
|
-
range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(
|
|
487
|
-
thisSpeed,
|
|
488
|
-
(thisPosY < nextPosY ? startY : endY) - thisPosY, thisTime)
|
|
489
|
-
} else if (startY <= thisPosY && thisPosY <= endY) {
|
|
490
|
-
range[0] = thisTime;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
// 要注意这里不能合成双分支if因为想要的Y片段可能在一个区间内
|
|
494
|
-
if (range[0] !== undefined) {
|
|
495
|
-
if (thisPosY < endY && endY <= nextPosY || thisPosY > startY && startY >= nextPosY) {
|
|
496
|
-
range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(
|
|
497
|
-
thisSpeed,
|
|
498
|
-
(thisPosY > nextPosY ? startY : endY) - thisPosY, thisTime)
|
|
499
|
-
result.push(range)
|
|
500
|
-
range = [undefined, undefined];
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
const thisPosY = nextPosY;
|
|
505
|
-
const thisTime = nextTime;
|
|
506
|
-
const thisSpeed = this.getStackedValue("speed", thisTime);
|
|
507
|
-
const inf = thisSpeed > 0 ? Infinity : (thisSpeed < 0 ? -Infinity : thisPosY)
|
|
508
|
-
if (range[0] === undefined) {
|
|
509
|
-
// 变速区间直接全部囊括,匀速要算一下,因为好算
|
|
510
|
-
if (thisPosY < startY && startY <= inf || thisPosY >= endY && endY > inf) {
|
|
511
|
-
range[0] = computeTime(
|
|
512
|
-
thisSpeed,
|
|
513
|
-
(thisPosY < inf ? startY : endY) - thisPosY,
|
|
514
|
-
thisTime)
|
|
515
|
-
} else if (thisSpeed === 0) {
|
|
516
|
-
range[0] = 0;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
// 要注意这里不能合成双分支if因为想要的Y片段可能在一个区间内
|
|
520
|
-
if (range[0] !== undefined) {
|
|
521
|
-
if (thisPosY < endY && endY <= inf || thisPosY >= startY && startY > inf) {
|
|
522
|
-
range[1] = computeTime(
|
|
523
|
-
thisSpeed,
|
|
524
|
-
(thisPosY > inf ? startY : endY) - thisPosY,
|
|
525
|
-
thisTime)
|
|
526
|
-
result.push(range)
|
|
527
|
-
} else if (thisSpeed === 0) {
|
|
528
|
-
range[1] = Infinity;
|
|
529
|
-
result.push(range)
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
return result;
|
|
533
|
-
//* /
|
|
534
|
-
}*/
|
|
535
408
|
/**
|
|
536
409
|
* 通过速度序列的FloorPosition反解出一个时间范围。
|
|
537
410
|
*
|
package/note.ts
CHANGED
|
@@ -307,7 +307,12 @@ export class NoteNode extends NoteNodeLike<NodeType.MIDDLE> {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
remove(note: Note) {
|
|
310
|
-
this.notes.
|
|
310
|
+
const index = this.notes.indexOf(note);
|
|
311
|
+
if (index === -1) {
|
|
312
|
+
console.warn("Note not found in this node!")
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
this.notes.splice(index, 1)
|
|
311
316
|
note.parentNode = null
|
|
312
317
|
}
|
|
313
318
|
static disconnect(note1: NNOrHead, note2: NNOrTail) {
|
package/operation/event.ts
CHANGED
|
@@ -117,7 +117,11 @@ extends UnionOperation<LazyOperation<typeof EventNodePairInsertOperation<VT>> |
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
export class EventNodePairAutoInsertOperation<VT extends EventValueESType> extends EventNodePairInsertOperation<VT> {
|
|
121
|
+
constructor(node: EventStartNode<VT>, parentSeq: EventNodeSequence<VT>, updatesFP = true) {
|
|
122
|
+
super(node, parentSeq.getNodeAt(TC.toBeats(node.time)), updatesFP);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
121
125
|
|
|
122
126
|
export class EventNodeValueChangeOperation <VT extends EventValueESType> extends Operation {
|
|
123
127
|
updatesEditor = true
|
|
@@ -362,6 +366,10 @@ export class EncapsuleOperation extends ComplexOperation<[MultiNodeDeleteOperati
|
|
|
362
366
|
// 直接do,这个不需要做成可撤销的
|
|
363
367
|
// @ts-expect-error 这里序列类型确定,为easing,不需要传入谱面
|
|
364
368
|
new MultiNodeAddOperation(nodeArray, sequence).do();
|
|
369
|
+
// 上面这个操作不能顶替原来的第一个startnode,所以手动来一遍
|
|
370
|
+
const first = sequence.head.next;
|
|
371
|
+
first.evaluator = nodeArray[0].evaluator;
|
|
372
|
+
first.value = nodeArray[0].value;
|
|
365
373
|
|
|
366
374
|
return new EncapsuleOperation(oldArray, easing);
|
|
367
375
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kipphi",
|
|
3
3
|
"description": "Parse your Phigros Chart(.rpe.json or .kpa.json) into an editor-friendly format.",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.4-beta.1",
|
|
5
5
|
"author": "Team Zincs (https://github.com/TeamZincs)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
package/rpeChartCompiler.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type TimeT, type ChartDataRPE, type MetaData, type JudgeLineDataRPE, ty
|
|
|
4
4
|
import { SegmentedEasing, BezierEasing, NormalEasing, fixedEasing, TemplateEasing, Easing } from "./easing";
|
|
5
5
|
import { err } from "./env";
|
|
6
6
|
import { EasedEvaluator, Evaluator, ExpressionEvaluator, NumericEasedEvaluator, TextEasedEvaluator, type EasedEvaluatorConstructorOfType, type EasedEvaluatorOfType } from "./evaluator";
|
|
7
|
-
import { EventEndNode, EventNode, EventNodeSequence, EventStartNode, type EventNodeLike } from "./event";
|
|
7
|
+
import { EventEndNode, EventNode, EventNodeSequence, EventStartNode, SpeedENS, type EventNodeLike } from "./event";
|
|
8
8
|
import type { JudgeLine } from "./judgeline";
|
|
9
9
|
import type { NNList, HNList, NNOrHead } from "./note";
|
|
10
10
|
import TC from "./time";
|
|
@@ -31,11 +31,23 @@ export class RPEChartCompiler {
|
|
|
31
31
|
// console.time("compileChart")
|
|
32
32
|
const chart = this.chart;
|
|
33
33
|
const judgeLineGroups = chart.judgeLineGroups.map(group => group.name);
|
|
34
|
+
const hasNotes = (nnList: NNList) => {
|
|
35
|
+
let node = nnList.head.next;
|
|
36
|
+
while (true) {
|
|
37
|
+
if (node.type === NodeType.TAIL) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (node.notes.length > 0) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
node = node.next;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
34
46
|
const filter = this.deletesEmptyLines ? (line: JudgeLine) => {
|
|
35
|
-
return line.nnLists.
|
|
36
|
-
|| line.hnLists.
|
|
47
|
+
return [...line.nnLists].some(([_, l]) => hasNotes(l))
|
|
48
|
+
|| [...line.hnLists].some(([_, l]) => hasNotes(l))
|
|
37
49
|
|| line.eventLayers.length > 0
|
|
38
|
-
|
|
50
|
+
&& (["moveX", "moveY", "rotate", "alpha"] as const).some((evType) => {
|
|
39
51
|
const seq = line.eventLayers[0][evType];
|
|
40
52
|
let node = seq.head.next;
|
|
41
53
|
for (let i = 0; i < 2; i++) {
|
|
@@ -128,7 +140,7 @@ export class RPEChartCompiler {
|
|
|
128
140
|
moveYEvents: layer.moveY ? this.dumpEventNodeSequence(layer.moveY) : undefined,
|
|
129
141
|
rotateEvents: layer.rotate ? this.dumpEventNodeSequence(layer.rotate) : undefined,
|
|
130
142
|
alphaEvents: layer.alpha ? this.dumpEventNodeSequence(layer.alpha) : undefined,
|
|
131
|
-
speedEvents: index === 0 ? this.
|
|
143
|
+
speedEvents: index === 0 ? this.dumpSpeedENS(judgeLine.speedSequence) : undefined
|
|
132
144
|
})),
|
|
133
145
|
extended: {
|
|
134
146
|
scaleXEvents: judgeLine.extendedLayer.scaleX ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleX) : undefined,
|
|
@@ -249,6 +261,32 @@ export class RPEChartCompiler {
|
|
|
249
261
|
|
|
250
262
|
return nodes
|
|
251
263
|
}
|
|
264
|
+
dumpSpeedENS(seq: SpeedENS): EventDataRPELike<number>[] {
|
|
265
|
+
const ret: EventDataRPELike<number>[] = [];
|
|
266
|
+
let node = seq.head.next;
|
|
267
|
+
while (true) {
|
|
268
|
+
const end = node.next;
|
|
269
|
+
if (end.type === NodeType.TAIL) {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
ret.push({
|
|
273
|
+
start: node.value,
|
|
274
|
+
end: end.value,
|
|
275
|
+
startTime: node.time,
|
|
276
|
+
endTime: end.time,
|
|
277
|
+
linkgroup: 0
|
|
278
|
+
} as EventDataRPELike<number>);
|
|
279
|
+
node = end.next;
|
|
280
|
+
}
|
|
281
|
+
ret.push({
|
|
282
|
+
start: node.value,
|
|
283
|
+
end: node.value,
|
|
284
|
+
startTime: node.time,
|
|
285
|
+
endTime: TC.vadd(node.time, [1, 0, 1]),
|
|
286
|
+
linkgroup: 0
|
|
287
|
+
} as EventDataRPELike<number>);
|
|
288
|
+
return ret;
|
|
289
|
+
}
|
|
252
290
|
|
|
253
291
|
compileNNLists(nnLists: NNList[], hnLists: HNList[]): NoteDataRPE[] {
|
|
254
292
|
const noteLists = nnLists.map(list => this.nnListToArray(list));
|
|
@@ -367,8 +405,8 @@ export class RPEChartCompiler {
|
|
|
367
405
|
srcStart = srcSeq.head.next!.value;
|
|
368
406
|
srcEnd = srcSeq.tail.previous!.value;
|
|
369
407
|
leftDividedNodeSrc = srcSeq.head.next!;
|
|
370
|
-
rightDividedNodeSrc = srcSeq.tail.previous!;
|
|
371
|
-
toStopAt =
|
|
408
|
+
rightDividedNodeSrc = srcSeq.tail.previous!.previous.previous!;
|
|
409
|
+
toStopAt = srcSeq.tail.previous!;
|
|
372
410
|
srcStartTime = srcSeq.head.next!.time;
|
|
373
411
|
srcTimeDelta = TC.sub(srcSeq.tail.previous!.time, srcStartTime);
|
|
374
412
|
}
|
|
@@ -450,7 +488,7 @@ export class RPEChartCompiler {
|
|
|
450
488
|
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
451
489
|
} else {
|
|
452
490
|
// 否则就是带缓动求值器
|
|
453
|
-
|
|
491
|
+
prev.evaluator = evaluator.deriveWithEasing(
|
|
454
492
|
new SegmentedEasing((rightDividedNodeSrc.evaluator as NumericEasedEvaluator).easing, 0.0, newRight)
|
|
455
493
|
) as unknown as Evaluator<VT>;
|
|
456
494
|
// TypeScript Compiler我*你娘啊
|
|
@@ -460,7 +498,7 @@ export class RPEChartCompiler {
|
|
|
460
498
|
if (rightDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
461
499
|
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
462
500
|
} else {
|
|
463
|
-
|
|
501
|
+
prev.evaluator = evaluator.deriveWithEasing(
|
|
464
502
|
(rightDividedNodeSrc.evaluator as NumericEasedEvaluator).easing
|
|
465
503
|
) as unknown as Evaluator<VT>;
|
|
466
504
|
}
|
package/time.ts
CHANGED
|
@@ -25,6 +25,16 @@ export default class TC {
|
|
|
25
25
|
static lt(beaT1:TimeT, beaT2: TimeT): boolean {
|
|
26
26
|
return beaT1[0] < beaT2[0] || beaT1[0] === beaT2[0] && beaT1[1] * beaT2[2] < beaT1[2] * beaT2[1]
|
|
27
27
|
}
|
|
28
|
+
static cmp(beaT1: TimeT, beaT2: TimeT): number {
|
|
29
|
+
if (beaT1[0] > beaT2[0]) {
|
|
30
|
+
return 1;
|
|
31
|
+
} else if (beaT1[0] < beaT2[0]) {
|
|
32
|
+
return -1;
|
|
33
|
+
} else {
|
|
34
|
+
return beaT1[1] * beaT2[2] > beaT1[2] * beaT2[1] ? 1 : -1;
|
|
35
|
+
}
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
28
38
|
/** @returns beaT1 != beaT2 */
|
|
29
39
|
static ne(beaT1:TimeT, beaT2: TimeT): boolean {
|
|
30
40
|
return beaT1[0] !== beaT2[0] || beaT1[1] * beaT2[2] !== beaT1[2] * beaT2[1]
|
package/util.ts
CHANGED
|
@@ -20,6 +20,56 @@ export const checkType = (value: unknown, type: string | (string | typeof Functi
|
|
|
20
20
|
&& value.length === type.length
|
|
21
21
|
&& type.every((t, i) => checkType(value[i], t))
|
|
22
22
|
} else if (typeof type === "string") {
|
|
23
|
+
if (type.startsWith("int")) {
|
|
24
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const match = type.match(/^int(\(|\[)(\-?\d+),(\-?\d+|\+)(\)|\])$/);
|
|
28
|
+
if (!match) { return true; }
|
|
29
|
+
const [,leftBrac, left, right, rightBrac] = match
|
|
30
|
+
if (!leftBrac) { return true; }
|
|
31
|
+
const leftN = left === "-" ? -Infinity : Number(left);
|
|
32
|
+
const rightN = right === "+" ? +Infinity : Number(right);
|
|
33
|
+
if (value < leftN) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (leftBrac === "(" && value === leftN) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (value > rightN) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (rightBrac === ")" && value === rightN) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
} else if (type.startsWith("number")) {
|
|
48
|
+
|
|
49
|
+
if (typeof value !== "number") {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const match = type.match(/^number(\(|\[)(\-?\d+),(\-?\d+|\+)(\)|\])$/)
|
|
53
|
+
if (!match) { return true; }
|
|
54
|
+
const [, leftBrac, left, right, rightBrac] = match
|
|
55
|
+
if (!leftBrac) { return true; }
|
|
56
|
+
const leftN = left === "-" ? -Infinity : Number(left);
|
|
57
|
+
const rightN = right === "+" ? +Infinity : Number(right);
|
|
58
|
+
if (value < leftN) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (leftBrac === "(" && value === leftN) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (value > rightN) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (rightBrac === ")" && value === rightN) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
23
73
|
return typeof value === type
|
|
24
74
|
} else {
|
|
25
75
|
return value instanceof type
|