kipphi 2.0.0 → 2.0.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/bpm.ts +213 -0
- package/chart.ts +172 -80
- package/chartTypes.ts +62 -20
- package/easing.ts +89 -83
- package/env.ts +173 -0
- package/evaluator.ts +35 -11
- package/event.ts +273 -225
- package/index.ts +14 -11
- package/judgeline.ts +381 -84
- package/jumparray.ts +11 -11
- package/note.ts +31 -54
- package/operation.ts +1378 -0
- package/package.json +1 -1
- package/rpeChartCompiler.ts +133 -98
- package/time.ts +35 -223
- package/tsconfig.json +1 -2
- package/util.ts +21 -1
- package/version.ts +1 -1
package/event.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { Chart } from "./chart";
|
|
2
|
-
import { EventType, type TimeT, type EventDataKPA, type RGB, type EventDataRPELike, InterpreteAs, type ValueTypeOfEventType, type EventNodeSequenceDataKPA, type EventDataKPA2, type EventNodeSequenceDataKPA2, type EventValueESType, EventValueType } from "./chartTypes";
|
|
2
|
+
import { EventType, type TimeT, type EventDataKPA, type RGB, type EventDataRPELike, InterpreteAs, type ValueTypeOfEventType, type EventNodeSequenceDataKPA, type EventDataKPA2, type EventNodeSequenceDataKPA2, type EventValueESType, EventValueType, EventValueTypeOfType } from "./chartTypes";
|
|
3
3
|
import { TemplateEasingLib, BezierEasing, Easing, rpeEasingArray, SegmentedEasing, linearEasing, fixedEasing, TemplateEasing, NormalEasing } from "./easing";
|
|
4
4
|
import { ColorEasedEvaluator, EasedEvaluator, ExpressionEvaluator, NumericEasedEvaluator, TextEasedEvaluator, type Evaluator } from "./evaluator";
|
|
5
5
|
import { JumpArray } from "./jumparray";
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
import TC from "./time";
|
|
8
|
+
import { type TimeCalculator } from "./bpm";
|
|
7
9
|
import { NodeType } from "./util";
|
|
8
10
|
|
|
11
|
+
import { err, ERROR_IDS, KPAError } from "./env";
|
|
12
|
+
import { JudgeLine } from "./judgeline";
|
|
13
|
+
|
|
9
14
|
/// #declaration:global
|
|
10
15
|
export class EventNodeLike<T extends NodeType, VT = number> {
|
|
11
16
|
type: T;
|
|
@@ -44,8 +49,8 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
44
49
|
evaluator: Evaluator<VT>;
|
|
45
50
|
constructor(time: TimeT, value: VT) {
|
|
46
51
|
super(NodeType.MIDDLE);
|
|
47
|
-
this.time =
|
|
48
|
-
// @ts-
|
|
52
|
+
this.time = TC.validateIp([...time]);
|
|
53
|
+
// @ts-expect-error 不清楚什么时候会是undefined,但是留着准没错((
|
|
49
54
|
this.value = value ?? 0;
|
|
50
55
|
if (typeof value === "number") {
|
|
51
56
|
this.evaluator = NumericEasedEvaluator.default as unknown as Evaluator<VT>;
|
|
@@ -57,7 +62,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
57
62
|
}
|
|
58
63
|
clone(offset: TimeT): EventStartNode<VT> | EventEndNode<VT> {
|
|
59
64
|
const ret = new (this.constructor as (typeof EventStartNode | typeof EventEndNode))
|
|
60
|
-
(offset ?
|
|
65
|
+
(offset ? TC.add(this.time, offset) : this.time, this.value);
|
|
61
66
|
ret.evaluator = this.evaluator;
|
|
62
67
|
return ret;
|
|
63
68
|
}
|
|
@@ -69,15 +74,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
69
74
|
* @returns
|
|
70
75
|
* @deprecated
|
|
71
76
|
*/
|
|
72
|
-
static getEasing(data: EventDataKPA<
|
|
77
|
+
static getEasing(data: EventDataKPA<EventValueESType>, templates: TemplateEasingLib, notSegmented = false): Easing {
|
|
73
78
|
const left = data.easingLeft;
|
|
74
79
|
const right = data.easingRight;
|
|
75
|
-
if ((left && right) && (left !== 0.0 || right !== 1.0)) {
|
|
76
|
-
return new SegmentedEasing(EventNode.getEasing(data, templates), left, right)
|
|
80
|
+
if (!notSegmented && (left && right) && (left !== 0.0 || right !== 1.0)) {
|
|
81
|
+
return new SegmentedEasing(EventNode.getEasing(data, templates, true), left, right)
|
|
77
82
|
}
|
|
78
83
|
if (data.bezier) {
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
const bp = data.bezierPoints
|
|
85
|
+
const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
|
|
81
86
|
return easing
|
|
82
87
|
} else if (typeof data.easingType === "string") {
|
|
83
88
|
return templates.get(data.easingType);
|
|
@@ -96,24 +101,25 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
96
101
|
* @returns
|
|
97
102
|
* @deprecated
|
|
98
103
|
*/
|
|
99
|
-
static getEvaluator(data: EventDataKPA<
|
|
104
|
+
static getEvaluator<VT extends EventValueESType>(data: EventDataKPA<VT>, templates: TemplateEasingLib, interpreteAs?: InterpreteAs): Evaluator<VT> {
|
|
100
105
|
const left = data.easingLeft;
|
|
101
106
|
const right = data.easingRight;
|
|
102
|
-
const wrap = (easing: Easing) => {
|
|
107
|
+
const wrap = (easing: Easing): EasedEvaluator<VT> => {
|
|
103
108
|
if (typeof data.start === "number") {
|
|
104
|
-
return new NumericEasedEvaluator(easing)
|
|
109
|
+
return new NumericEasedEvaluator(easing) as EasedEvaluator<VT>;
|
|
105
110
|
} else if (typeof data.start === "string") {
|
|
106
|
-
|
|
111
|
+
// @ts-expect-error
|
|
112
|
+
return new TextEasedEvaluator(easing, interpreteAs);
|
|
107
113
|
} else {
|
|
108
|
-
return new ColorEasedEvaluator(easing)
|
|
114
|
+
return new ColorEasedEvaluator(easing) as EasedEvaluator<VT>;
|
|
109
115
|
}
|
|
110
|
-
}
|
|
116
|
+
};
|
|
111
117
|
if ((left && right) && (left !== 0.0 || right !== 1.0)) {
|
|
112
118
|
return wrap(new SegmentedEasing(EventNode.getEasing(data, templates), left, right))
|
|
113
119
|
}
|
|
114
120
|
if (data.bezier) {
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
const bp = data.bezierPoints
|
|
122
|
+
const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
|
|
117
123
|
return wrap(easing)
|
|
118
124
|
} else if (data.isParametric) {
|
|
119
125
|
if (typeof data.easingType !== "string") {
|
|
@@ -137,8 +143,8 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
137
143
|
* @returns
|
|
138
144
|
*/
|
|
139
145
|
static fromEvent<VT extends RGB | number>(data: EventDataRPELike<VT>, chart: Chart): [EventStartNode<VT>, EventEndNode<VT>] {
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
const start = new EventStartNode(data.startTime, data.start)
|
|
147
|
+
const end = new EventEndNode(data.endTime, data.end);
|
|
142
148
|
start.evaluator = EventNode.getEvaluator(data, chart.templateEasingLib);
|
|
143
149
|
EventNode.connect(start, end);
|
|
144
150
|
return [start, end]
|
|
@@ -161,10 +167,9 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
161
167
|
interpreteAs = InterpreteAs.int;
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
start.
|
|
167
|
-
start.evaluator = EventNode.getEvaluator(data, templates);
|
|
170
|
+
const start = new EventStartNode<string>(data.startTime, startValue);
|
|
171
|
+
const end = new EventEndNode<string>(data.endTime, endValue);
|
|
172
|
+
start.evaluator = EventNode.getEvaluator(data, templates, (data as unknown as EventDataKPA).interpreteAs ?? interpreteAs);
|
|
168
173
|
EventNode.connect(start, end)
|
|
169
174
|
return [start, end]
|
|
170
175
|
}
|
|
@@ -197,7 +202,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
197
202
|
static insert<VT>(node: EventStartNode<VT>, tarPrev: EventStartNode<VT>): [EventNodeLike<NodeType.HEAD, VT> | EventStartNode<VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>] {
|
|
198
203
|
const tarNext = tarPrev.next;
|
|
199
204
|
if (node.previous.type === NodeType.HEAD) {
|
|
200
|
-
throw
|
|
205
|
+
throw err.CANNOT_INSERT_BEFORE_HEAD();
|
|
201
206
|
}
|
|
202
207
|
this.connect(tarPrev, node.previous);
|
|
203
208
|
node.parentSeq = node.previous.parentSeq;
|
|
@@ -259,17 +264,17 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
259
264
|
} else if (node instanceof EventEndNode) {
|
|
260
265
|
return [<EventStartNode<VT>>node.previous, node]
|
|
261
266
|
} else {
|
|
262
|
-
throw new Error("
|
|
267
|
+
throw new Error("unreachable");
|
|
263
268
|
}
|
|
264
269
|
}
|
|
265
270
|
static setToNewOrderedArray<VT>(dest: TimeT, set: Set<EventStartNode<VT>>): [EventStartNode<VT>[], EventStartNode<VT>[]] {
|
|
266
271
|
const nodes = [...set]
|
|
267
|
-
nodes.sort((a, b) =>
|
|
268
|
-
const offset =
|
|
272
|
+
nodes.sort((a, b) => TC.gt(a.time, b.time) ? 1 : -1);
|
|
273
|
+
const offset = TC.sub(dest, nodes[0].time)
|
|
269
274
|
return [nodes, nodes.map(node => node.clonePair(offset))]
|
|
270
275
|
}
|
|
271
276
|
static belongToSequence(nodes: Set<EventStartNode>, sequence: EventNodeSequence): boolean {
|
|
272
|
-
for (
|
|
277
|
+
for (const each of nodes) {
|
|
273
278
|
if (each.parentSeq !== sequence) {
|
|
274
279
|
return false;
|
|
275
280
|
}
|
|
@@ -297,76 +302,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
|
|
|
297
302
|
// #endregion
|
|
298
303
|
}
|
|
299
304
|
|
|
300
|
-
|
|
301
|
-
const getValueFns = [
|
|
302
|
-
(current: number, timeDelta: number, value: number, nextVal: number, easing: Easing) => {
|
|
303
|
-
|
|
304
|
-
// @ts-ignore TSC脑壳有问题
|
|
305
|
-
const valueDelta = nextVal - value
|
|
306
|
-
// 其他类型,包括普通缓动和非钩定模板缓动
|
|
307
|
-
return value + easing.getValue(current / timeDelta) * valueDelta
|
|
308
|
-
},
|
|
309
|
-
(current: number, timeDelta: number, value: string, nextVal: string, easing: Easing, interpretedAs: InterpreteAs): string => {
|
|
310
|
-
|
|
311
|
-
if (interpretedAs === InterpreteAs.float) {
|
|
312
|
-
const start = parseFloat(value);
|
|
313
|
-
const delta = parseFloat(nextVal as string) - start;
|
|
314
|
-
return start + easing.getValue(current / timeDelta) * delta + "";
|
|
315
|
-
} else if (interpretedAs === InterpreteAs.int) {
|
|
316
|
-
const start = parseInt(value);
|
|
317
|
-
const delta = parseInt(nextVal as string) - start;
|
|
318
|
-
return start + Math.round(easing.getValue(current / timeDelta) * delta) + "";
|
|
319
|
-
} else if (value.startsWith(nextVal as string)) {
|
|
320
|
-
const startLen = (nextVal as string).length;
|
|
321
|
-
const deltaLen = value.length - startLen;
|
|
322
|
-
const len = startLen + Math.floor(deltaLen * easing.getValue(current / timeDelta));
|
|
323
|
-
return value.substring(0, len);
|
|
324
|
-
} else if ((nextVal as string).startsWith(value)) {
|
|
325
|
-
const startLen = value.length;
|
|
326
|
-
const deltaLen = (nextVal as string).length - startLen;
|
|
327
|
-
const len = startLen + Math.floor(deltaLen * easing.getValue(current / timeDelta));
|
|
328
|
-
return (nextVal as string).substring(0, len);
|
|
329
|
-
} else {
|
|
330
|
-
return value;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
},
|
|
334
|
-
(current: number, timeDelta: number, value: RGB, nextValue: RGB, easing: Easing) => {
|
|
335
|
-
return (value as RGB).map((v, i) => {
|
|
336
|
-
const nextVal = nextValue[i];
|
|
337
|
-
const value = v;
|
|
338
|
-
if (nextVal === value) {
|
|
339
|
-
return value;
|
|
340
|
-
} else {
|
|
341
|
-
const delta = nextVal - value;
|
|
342
|
-
return value + easing.getValue(current / timeDelta) * delta;
|
|
343
|
-
}
|
|
344
|
-
})
|
|
345
|
-
}
|
|
346
|
-
] as const;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
305
|
export class EventStartNode<VT = number> extends EventNode<VT> {
|
|
351
306
|
override next: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>;
|
|
352
307
|
override previous: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT>;
|
|
353
308
|
/**
|
|
354
|
-
*
|
|
309
|
+
* 对于速度事件,从0时刻到此节点的总积分
|
|
355
310
|
*/
|
|
356
|
-
|
|
311
|
+
floorPosition: number = 0;
|
|
357
312
|
constructor(time: TimeT, value: VT) {
|
|
358
313
|
super(time, value);
|
|
359
|
-
// 最史的一集,what can i say
|
|
360
|
-
if (typeof value === "number") {
|
|
361
|
-
// @ts-ignore
|
|
362
|
-
this.getValueFn = getValueFns[0];
|
|
363
|
-
} else if (typeof value === "string") {
|
|
364
|
-
// @ts-ignore
|
|
365
|
-
this.getValueFn = getValueFns[1];
|
|
366
|
-
} else {
|
|
367
|
-
// @ts-ignore
|
|
368
|
-
this.getValueFn = getValueFns[2];
|
|
369
|
-
}
|
|
370
314
|
}
|
|
371
315
|
override parentSeq: EventNodeSequence<VT>;
|
|
372
316
|
/**
|
|
@@ -384,35 +328,6 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
|
|
|
384
328
|
evaluator: this.evaluator.dump(),
|
|
385
329
|
}
|
|
386
330
|
}
|
|
387
|
-
/**
|
|
388
|
-
* 产生一个一拍长的短钩定事件
|
|
389
|
-
* 仅用于编译至RPE时解决最后一个StartNode的问题
|
|
390
|
-
* 限最后一个StartNode使用
|
|
391
|
-
* @returns
|
|
392
|
-
* /
|
|
393
|
-
dumpAsLast(): EventDataRPELike<VT> {
|
|
394
|
-
const isSegmented = this.easingIsSegmented
|
|
395
|
-
const easing = isSegmented ? (this.easing as SegmentedEasing).easing : this.easing;
|
|
396
|
-
return {
|
|
397
|
-
bezier: easing instanceof BezierEasing ? 1 : 0,
|
|
398
|
-
bezierPoints: easing instanceof BezierEasing ?
|
|
399
|
-
[easing.cp1[0], easing.cp1[1], easing.cp2[0], easing.cp2[1]] : // 修正了这里 cp2.y 的引用
|
|
400
|
-
[0, 0, 0, 0],
|
|
401
|
-
easingLeft: isSegmented ? (this.easing as SegmentedEasing).left : 0.0,
|
|
402
|
-
easingRight: isSegmented ? (this.easing as SegmentedEasing).right : 1.0,
|
|
403
|
-
easingType: easing instanceof TemplateEasing ?
|
|
404
|
-
(easing.name) :
|
|
405
|
-
easing instanceof NormalEasing ?
|
|
406
|
-
easing.rpeId :
|
|
407
|
-
null,
|
|
408
|
-
end: this.value,
|
|
409
|
-
endTime: TimeCalculator.add(this.time, [1, 0, 1]),
|
|
410
|
-
linkgroup: 0, // 假设默认值为 0
|
|
411
|
-
start: this.value,
|
|
412
|
-
startTime: this.time,
|
|
413
|
-
}
|
|
414
|
-
}//*/
|
|
415
|
-
interpretedAs: InterpreteAs = InterpreteAs.str;
|
|
416
331
|
getValueAt(beats: number): VT {
|
|
417
332
|
// 除了尾部的开始节点,其他都有下个节点
|
|
418
333
|
// 钩定型缓动也有
|
|
@@ -421,34 +336,29 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
|
|
|
421
336
|
}
|
|
422
337
|
return this.evaluator.eval(this, beats);
|
|
423
338
|
}
|
|
424
|
-
private getValueFn: (current: number, timeDelta: number, value: VT, nextVal: VT, easing: Easing, interpreteAs?: InterpreteAs) => VT
|
|
425
339
|
getSpeedValueAt(this: EventStartNode<number>, beats: number) {
|
|
426
340
|
if (this.next.type === NodeType.TAIL) {
|
|
427
|
-
return this.value
|
|
428
|
-
}
|
|
429
|
-
let timeDelta = TimeCalculator.getDelta(this.next.time, this.time)
|
|
430
|
-
let valueDelta = this.next.value - this.value
|
|
431
|
-
let current = beats - TimeCalculator.toBeats(this.time)
|
|
432
|
-
if (current > timeDelta || current < 0) {
|
|
433
|
-
console.warn("超过事件时间范围!", this, beats)
|
|
434
|
-
// debugger
|
|
341
|
+
return this.value;
|
|
435
342
|
}
|
|
343
|
+
const timeDelta = TC.getDelta(this.next.time, this.time);
|
|
344
|
+
const valueDelta = this.next.value - this.value;
|
|
345
|
+
const current = beats - TC.toBeats(this.time);
|
|
436
346
|
return this.value + linearEasing.getValue(current / timeDelta) * valueDelta;
|
|
437
347
|
}
|
|
438
348
|
/**
|
|
439
349
|
* 积分获取位移
|
|
440
350
|
*/
|
|
441
|
-
|
|
442
|
-
return timeCalculator.segmentToSeconds(
|
|
351
|
+
getLocalFloorPos(this: EventStartNode<number>, beats: number, timeCalculator: TimeCalculator) {
|
|
352
|
+
return timeCalculator.segmentToSeconds(TC.toBeats(this.time), beats) * (this.value + this.getSpeedValueAt(beats)) / 2 * 120 // 每单位120px
|
|
443
353
|
}
|
|
444
|
-
|
|
354
|
+
getFullLocalFloorPos(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
|
|
445
355
|
if (this.next.type === NodeType.TAIL) {
|
|
446
356
|
console.log(this)
|
|
447
|
-
throw
|
|
357
|
+
throw err.CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE();
|
|
448
358
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
359
|
+
const end = this.next;
|
|
360
|
+
const endBeats = TC.toBeats(end.time)
|
|
361
|
+
const startBeats = TC.toBeats(this.time)
|
|
452
362
|
// 原来这里写反了,气死偶咧!
|
|
453
363
|
return timeCalculator.segmentToSeconds(startBeats, endBeats) * (this.value + end.value) / 2 * 120
|
|
454
364
|
}
|
|
@@ -458,6 +368,9 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
|
|
|
458
368
|
isLastStart() {
|
|
459
369
|
return this.next && this.next.type === NodeType.TAIL
|
|
460
370
|
}
|
|
371
|
+
isSpeed() {
|
|
372
|
+
return this.parentSeq.type === EventType.speed;
|
|
373
|
+
}
|
|
461
374
|
override clone(offset?: TimeT): EventStartNode<VT> {
|
|
462
375
|
return super.clone(offset) as EventStartNode<VT>;
|
|
463
376
|
};
|
|
@@ -467,32 +380,15 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
|
|
|
467
380
|
EventNode.connect(endNode, startNode);
|
|
468
381
|
return startNode;
|
|
469
382
|
};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
drawCurve(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number, matrix: Matrix): void {
|
|
473
|
-
if (!(this.easing instanceof ParametricEquationEasing)) {
|
|
474
|
-
return this.easing.drawCurve(context, startX, startY, endX, endY);
|
|
475
|
-
}
|
|
476
|
-
const getValue = (ratio: number) => {
|
|
477
|
-
return matrix.ymul(0, this.easing.getValue(ratio))
|
|
478
|
-
}
|
|
479
|
-
const timeDelta = endX - startX;
|
|
480
|
-
let last = startY;
|
|
481
|
-
context.beginPath()
|
|
482
|
-
context.moveTo(startX, last)
|
|
483
|
-
for (let t = 4; t <= timeDelta; t += 4) {
|
|
484
|
-
const ratio = t / timeDelta
|
|
485
|
-
const curPosY = getValue(ratio);
|
|
486
|
-
context.lineTo(startX + t, curPosY);
|
|
487
|
-
last = curPosY;
|
|
488
|
-
}
|
|
489
|
-
context.stroke();
|
|
490
|
-
}
|
|
491
383
|
}
|
|
492
384
|
|
|
385
|
+
export type NonLastStartNode<VT extends EventValueESType> = EventStartNode<VT> & {next: EventEndNode<VT>}
|
|
386
|
+
|
|
493
387
|
export class EventEndNode<VT = number> extends EventNode<VT> {
|
|
494
388
|
override next!: EventStartNode<VT>;
|
|
495
389
|
override previous!: EventStartNode<VT>;
|
|
390
|
+
|
|
391
|
+
// @ts-expect-error 这里没有类型安全问题
|
|
496
392
|
override get parentSeq(): EventNodeSequence<VT> {return this.previous?.parentSeq || null}
|
|
497
393
|
override set parentSeq(_parent: EventNodeSequence<VT>) {}
|
|
498
394
|
constructor(time: TimeT, value: VT) {
|
|
@@ -507,6 +403,12 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
|
|
|
507
403
|
}
|
|
508
404
|
|
|
509
405
|
|
|
406
|
+
export enum Monotonicity {
|
|
407
|
+
increasing,
|
|
408
|
+
decreasing,
|
|
409
|
+
swinging
|
|
410
|
+
}
|
|
411
|
+
|
|
510
412
|
/**
|
|
511
413
|
* 为一个链表结构。会有一个数组进行快跳。
|
|
512
414
|
* is the list of event nodes, but not purely start nodes.
|
|
@@ -534,31 +436,51 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
|
|
|
534
436
|
* Remember to update the jump array when inserting or deleting nodes.
|
|
535
437
|
*/
|
|
536
438
|
export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
439
|
+
/**
|
|
440
|
+
* @deprecated 谱面属性未实装,以后有必要的时候会添加为其赋值的逻辑
|
|
441
|
+
*/
|
|
537
442
|
chart: Chart;
|
|
538
|
-
/**
|
|
443
|
+
/** 标识名默认遵循`#${lineid}.${layerid}.${typename}`格式
|
|
444
|
+
* id follows the format `#${lineid}.${layerid}.${typename}` by default
|
|
445
|
+
*/
|
|
539
446
|
id: string;
|
|
540
|
-
/** has no time or value */
|
|
541
447
|
head: EventNodeLike<NodeType.HEAD, VT>;
|
|
542
|
-
/** has no time or value */
|
|
543
448
|
tail: EventNodeLike<NodeType.TAIL, VT>;
|
|
544
449
|
jump?: JumpArray<AnyEN<VT>>;
|
|
545
450
|
listLength: number;
|
|
546
451
|
/** 一定是二的幂,避免浮点误差 */
|
|
547
452
|
jumpAverageBeats: number;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* 用于速度事件优化
|
|
456
|
+
*/
|
|
457
|
+
monotonicity: Monotonicity = Monotonicity.swinging;
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* 使用了该序列的判定线,当前仅用于速度事件对fp的更新
|
|
461
|
+
*/
|
|
462
|
+
consumerLines = new Set<JudgeLine>;
|
|
463
|
+
|
|
552
464
|
constructor(public type: EventType, public effectiveBeats: number) {
|
|
553
465
|
this.head = new EventNodeLike(NodeType.HEAD);
|
|
554
466
|
this.tail = new EventNodeLike(NodeType.TAIL);
|
|
555
467
|
this.head.parentSeq = this.tail.parentSeq = this;
|
|
556
468
|
this.listLength = 1;
|
|
557
|
-
// this.head = this.tail = new EventStartNode([0, 0, 0], 0)
|
|
558
|
-
// this.nodes = [];
|
|
559
|
-
// this.startNodes = [];
|
|
560
|
-
// this.endNodes = [];
|
|
561
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* 获取指定事件类型的事件默认值。
|
|
472
|
+
* Get the default value of the specified event type.
|
|
473
|
+
*
|
|
474
|
+
* 对于文字事件,默认值为空字符串。
|
|
475
|
+
*
|
|
476
|
+
* 对于缩放事件,默认值为1.0。
|
|
477
|
+
*
|
|
478
|
+
* 对于颜色事件,默认值为黑色([0, 0, 0])。
|
|
479
|
+
*
|
|
480
|
+
* 对于速度事件,默认值为10(1200像素/秒)。
|
|
481
|
+
*
|
|
482
|
+
* 对于一般数值事件,默认值为0。
|
|
483
|
+
*/
|
|
562
484
|
static getDefaultValueFromEventType(type: EventType) {
|
|
563
485
|
|
|
564
486
|
return type === EventType.speed ? 10 :
|
|
@@ -567,15 +489,15 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
567
489
|
type === EventType.color ? [0, 0, 0] :
|
|
568
490
|
0
|
|
569
491
|
}
|
|
570
|
-
static fromRPEJSON<T extends EventType, VT = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, endValue?: number) {
|
|
492
|
+
static fromRPEJSON<T extends EventType, VT = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, pos: string, endValue?: number) {
|
|
571
493
|
const {templateEasingLib: templates} = chart
|
|
572
494
|
const length = data.length;
|
|
573
495
|
// const isSpeed = type === EventType.Speed;
|
|
574
496
|
// console.log(isSpeed)
|
|
575
|
-
const seq = new EventNodeSequence<VT>(type, type === EventType.easing ?
|
|
497
|
+
const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TC.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
|
|
576
498
|
let listLength = length;
|
|
577
499
|
let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
|
|
578
|
-
// 如果第一个事件不从0
|
|
500
|
+
// 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫底
|
|
579
501
|
if (data[0] && TC.ne(data[0].startTime, [0, 0, 1])) {
|
|
580
502
|
const value = data[0].start
|
|
581
503
|
const start = new EventStartNode<VT>([0, 0, 1], value as VT);
|
|
@@ -585,59 +507,80 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
585
507
|
lastEnd = end;
|
|
586
508
|
}
|
|
587
509
|
|
|
588
|
-
let
|
|
510
|
+
let lastEndTime: TimeT = [0, 0, 1];
|
|
511
|
+
|
|
512
|
+
// 读取事件列表
|
|
589
513
|
for (let index = 0; index < length; index++) {
|
|
590
514
|
const event = data[index];
|
|
591
|
-
//
|
|
592
|
-
|
|
515
|
+
if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
|
|
516
|
+
err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
|
|
517
|
+
}
|
|
518
|
+
if (!TC.lt(event.startTime, event.endTime)) {
|
|
519
|
+
err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
|
|
520
|
+
}
|
|
521
|
+
lastEndTime = event.endTime;
|
|
522
|
+
const [start, end] = (type === EventType.text
|
|
523
|
+
? EventNode.fromTextEvent(event as EventDataRPELike<string>, templates)
|
|
524
|
+
: EventNode.fromEvent(event as EventDataRPELike<number | RGB>, chart)) as unknown as [EventStartNode<VT>, EventEndNode<VT>];
|
|
525
|
+
// 刚开始时,上个节点是头
|
|
593
526
|
if (lastEnd.type === NodeType.HEAD) {
|
|
594
527
|
EventNode.connect(lastEnd, start)
|
|
595
528
|
// 如果上一个是钩定事件,那么一块捋平
|
|
596
529
|
} else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
|
|
597
530
|
lastEnd.time = start.time
|
|
598
531
|
EventNode.connect(lastEnd, start)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
532
|
+
|
|
533
|
+
// 如果上一个事件的结束时间和下一个事件的开始时间不一样,那么中间插入一对面对面节点
|
|
534
|
+
} else if (TC.toBeats(lastEnd.time) !== TC.toBeats(start.time)) {
|
|
535
|
+
const val = lastEnd.value;
|
|
536
|
+
const midStart = new EventStartNode(lastEnd.time, val);
|
|
537
|
+
const midEnd = new EventEndNode(start.time, val);
|
|
603
538
|
midStart.evaluator = lastEnd.previous.evaluator;
|
|
604
539
|
EventNode.connect(lastEnd, midStart);
|
|
605
540
|
EventNode.connect(midStart, midEnd);
|
|
606
541
|
EventNode.connect(midEnd, start)
|
|
607
|
-
// seq.startNodes.push(midStart);
|
|
608
|
-
// seq.endNodes.push(midEnd);
|
|
609
542
|
listLength++;
|
|
543
|
+
// 最后应该就是上一个事件和这个事件首尾相接的情况
|
|
610
544
|
} else {
|
|
611
545
|
|
|
612
546
|
EventNode.connect(lastEnd, start)
|
|
613
547
|
}
|
|
614
548
|
|
|
615
|
-
// seq.startNodes.push(start);
|
|
616
|
-
// seq.endNodes.push(end);
|
|
617
549
|
lastEnd = end;
|
|
618
|
-
// seq.nodes.push(start, end);
|
|
619
550
|
}
|
|
620
551
|
const last = lastEnd;
|
|
621
552
|
const tail = new EventStartNode(
|
|
622
553
|
last.type === NodeType.HEAD ? [0, 0, 1] : last.time,
|
|
623
|
-
|
|
554
|
+
endValue ?? last.value
|
|
624
555
|
);
|
|
625
556
|
EventNode.connect(last, tail);
|
|
626
557
|
// last can be a header, in which case easing is undefined.
|
|
627
558
|
// then we use the easing that initialized in the EventStartNode constructor.
|
|
628
559
|
tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
|
|
629
|
-
tail.cachedIntegral = lastIntegral
|
|
630
560
|
EventNode.connect(tail, seq.tail)
|
|
631
561
|
seq.listLength = listLength;
|
|
632
562
|
seq.initJump();
|
|
563
|
+
if (type === EventType.speed) {
|
|
564
|
+
(seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
|
|
565
|
+
}
|
|
633
566
|
return seq;
|
|
634
567
|
}
|
|
635
|
-
static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(
|
|
636
|
-
|
|
568
|
+
static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(
|
|
569
|
+
type: T,
|
|
570
|
+
data: EventDataKPA2<VT>[],
|
|
571
|
+
chart: Chart,
|
|
572
|
+
pos: string,
|
|
573
|
+
endValue?: VT
|
|
574
|
+
)
|
|
575
|
+
{
|
|
637
576
|
const length = data.length;
|
|
638
577
|
// const isSpeed = type === EventType.Speed;
|
|
639
578
|
// console.log(isSpeed)
|
|
640
|
-
const seq = new EventNodeSequence<VT>(
|
|
579
|
+
const seq = new EventNodeSequence<VT>(
|
|
580
|
+
type,
|
|
581
|
+
type === EventType.easing
|
|
582
|
+
? TC.toBeats(data[length - 1].endTime)
|
|
583
|
+
: chart.effectiveBeats);
|
|
641
584
|
let listLength = length;
|
|
642
585
|
let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
|
|
643
586
|
// 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫背
|
|
@@ -650,24 +593,38 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
650
593
|
lastEnd = end;
|
|
651
594
|
}
|
|
652
595
|
|
|
653
|
-
const valueType = type === EventType.color ? EventValueType.color
|
|
596
|
+
const valueType = (type === EventType.color ? EventValueType.color
|
|
654
597
|
: type === EventType.text ? EventValueType.text
|
|
655
|
-
: EventValueType.numeric
|
|
598
|
+
: EventValueType.numeric) as EventValueTypeOfType<VT>;
|
|
656
599
|
|
|
657
|
-
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
let lastEndTime: TimeT = [0, 0, 1];
|
|
658
603
|
for (let index = 0; index < length; index++) {
|
|
659
604
|
const event = data[index];
|
|
660
|
-
|
|
605
|
+
const [start, end] = chart.createEventFromData<VT>(event, valueType);
|
|
606
|
+
// 从前面复制了,复用性减一
|
|
607
|
+
// KPA2没有更改RPE的按事件存储的机制。
|
|
608
|
+
if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
|
|
609
|
+
err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
|
|
610
|
+
}
|
|
611
|
+
if (!TC.lt(event.startTime, event.endTime)) {
|
|
612
|
+
err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
|
|
613
|
+
}
|
|
614
|
+
lastEndTime = event.endTime;
|
|
661
615
|
if (lastEnd.type === NodeType.HEAD) {
|
|
662
616
|
EventNode.connect(lastEnd, start)
|
|
663
617
|
// 如果上一个是钩定事件,那么一块捋平
|
|
664
|
-
} else if (
|
|
618
|
+
} else if (
|
|
619
|
+
lastEnd.value === lastEnd.previous.value
|
|
620
|
+
&& lastEnd.previous.evaluator instanceof EasedEvaluator
|
|
621
|
+
) {
|
|
665
622
|
lastEnd.time = start.time
|
|
666
623
|
EventNode.connect(lastEnd, start)
|
|
667
|
-
} else if (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
624
|
+
} else if (TC.eq(lastEnd.time, start.time)) {
|
|
625
|
+
const val = lastEnd.value;
|
|
626
|
+
const midStart = new EventStartNode(lastEnd.time, val);
|
|
627
|
+
const midEnd = new EventEndNode(start.time, val);
|
|
671
628
|
midStart.evaluator = lastEnd.previous.evaluator;
|
|
672
629
|
EventNode.connect(lastEnd, midStart);
|
|
673
630
|
EventNode.connect(midStart, midEnd);
|
|
@@ -689,10 +646,12 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
689
646
|
// last can be a header, in which case easing is undefined.
|
|
690
647
|
// then we use the easing that initialized in the EventStartNode constructor.
|
|
691
648
|
tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
|
|
692
|
-
tail.cachedIntegral = lastIntegral
|
|
693
649
|
EventNode.connect(tail, seq.tail)
|
|
694
650
|
seq.listLength = listLength;
|
|
695
651
|
seq.initJump();
|
|
652
|
+
if (type === EventType.speed) {
|
|
653
|
+
(seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
|
|
654
|
+
}
|
|
696
655
|
return seq;
|
|
697
656
|
}
|
|
698
657
|
/**
|
|
@@ -702,11 +661,16 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
702
661
|
* @param effectiveBeats
|
|
703
662
|
* @returns
|
|
704
663
|
*/
|
|
705
|
-
static newSeq<T extends EventType>(
|
|
664
|
+
static newSeq<T extends EventType>(
|
|
665
|
+
type: T,
|
|
666
|
+
effectiveBeats: number
|
|
667
|
+
): EventNodeSequence<ValueTypeOfEventType<T>>
|
|
668
|
+
{
|
|
706
669
|
type V = ValueTypeOfEventType<T>
|
|
707
670
|
const sequence = new EventNodeSequence<V>(type, effectiveBeats);
|
|
708
671
|
const node = new EventStartNode<V>(
|
|
709
|
-
[0, 0, 1],
|
|
672
|
+
[0, 0, 1],
|
|
673
|
+
EventNodeSequence.getDefaultValueFromEventType(type) as V
|
|
710
674
|
);
|
|
711
675
|
EventNode.connect(sequence.head, node)
|
|
712
676
|
EventNode.connect(node, sequence.tail)
|
|
@@ -736,7 +700,7 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
736
700
|
return [0, node.next]
|
|
737
701
|
}
|
|
738
702
|
const endNode = (node as EventStartNode<VT>).next as EventEndNode<VT>;
|
|
739
|
-
const time =
|
|
703
|
+
const time = TC.toBeats(endNode.time);
|
|
740
704
|
const nextNode = endNode.next;
|
|
741
705
|
if (nextNode.next.type === NodeType.TAIL) {
|
|
742
706
|
return [time, nextNode.next] // Tailer代替最后一个StartNode去占位
|
|
@@ -745,7 +709,7 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
745
709
|
}
|
|
746
710
|
},
|
|
747
711
|
(node: EventStartNode<VT>, beats: number) => {
|
|
748
|
-
return
|
|
712
|
+
return TC.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
|
|
749
713
|
},
|
|
750
714
|
(node: EventStartNode) => {
|
|
751
715
|
return node.next && node.next.type === NodeType.TAIL ? node.next : node;
|
|
@@ -762,12 +726,10 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
762
726
|
|
|
763
727
|
}
|
|
764
728
|
this.jump.updateRange(from, to);
|
|
765
|
-
}
|
|
766
|
-
insert() {
|
|
767
|
-
|
|
768
729
|
}
|
|
769
730
|
getNodeAt(beats: number, usePrev: boolean = false): EventStartNode<VT> {
|
|
770
|
-
let node
|
|
731
|
+
let node
|
|
732
|
+
= this.jump?.getNodeAt(beats) as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>)
|
|
771
733
|
|| this.head.next as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>);
|
|
772
734
|
if (node.type === NodeType.TAIL) {
|
|
773
735
|
if (usePrev) {
|
|
@@ -776,13 +738,13 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
776
738
|
// 最后一个事件节点本身具有无限延伸的特性
|
|
777
739
|
return node.previous;
|
|
778
740
|
}
|
|
779
|
-
if (usePrev &&
|
|
741
|
+
if (usePrev && TC.toBeats(node.time) === beats) {
|
|
780
742
|
const prev = node.previous;
|
|
781
743
|
if (!(prev.type === NodeType.HEAD)) {
|
|
782
744
|
node = prev.previous;
|
|
783
745
|
}
|
|
784
746
|
}
|
|
785
|
-
if (
|
|
747
|
+
if (TC.toBeats(node.time) > beats && beats >= 0) {
|
|
786
748
|
console.warn("Got a node after the given beats. This would only happen when the given beats is negative. Beats and Node:", beats, node)
|
|
787
749
|
}
|
|
788
750
|
return node;
|
|
@@ -790,20 +752,40 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
790
752
|
getValueAt(beats: number, usePrev: boolean = false): VT {
|
|
791
753
|
return this.getNodeAt(beats, usePrev).getValueAt(beats);
|
|
792
754
|
}
|
|
793
|
-
|
|
755
|
+
getFloorPositionAt(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
|
|
794
756
|
const node: EventStartNode<number> = this.getNodeAt(beats);
|
|
795
|
-
|
|
757
|
+
const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
|
|
758
|
+
if (isNaN(value)) {
|
|
759
|
+
debugger;
|
|
760
|
+
}
|
|
761
|
+
return value
|
|
796
762
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
763
|
+
/**
|
|
764
|
+
* 更新某个速度节点之后的FP
|
|
765
|
+
*
|
|
766
|
+
* 注意,修改节点并不会影响它的FP,而是它后面的所有节点的FP,节点存的是它所在位置的FP,节点在事件头部
|
|
767
|
+
* @param this
|
|
768
|
+
* @param node
|
|
769
|
+
* @param tc
|
|
770
|
+
*/
|
|
771
|
+
updateFloorPositionAfter(this: SpeedENS, node: EventStartNode, tc: TimeCalculator): void {
|
|
772
|
+
let currentFP: number;
|
|
773
|
+
if (node.floorPosition) {
|
|
774
|
+
currentFP = node.floorPosition;
|
|
775
|
+
} else if (node.previous.type !== NodeType.HEAD) {
|
|
776
|
+
const prevStart = node.previous.previous;
|
|
777
|
+
currentFP = prevStart.floorPosition + prevStart.getFullLocalFloorPos(tc);
|
|
778
|
+
} else {
|
|
779
|
+
currentFP = 0;
|
|
780
|
+
}
|
|
781
|
+
while (true) {
|
|
782
|
+
const canBeEnd = node.next;
|
|
783
|
+
if (canBeEnd.type === NodeType.TAIL) {
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
currentFP += node.getFullLocalFloorPos(tc);
|
|
787
|
+
node = canBeEnd.next;
|
|
788
|
+
node.floorPosition = currentFP
|
|
807
789
|
}
|
|
808
790
|
}
|
|
809
791
|
dump(): EventNodeSequenceDataKPA2<VT> {
|
|
@@ -824,8 +806,8 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
824
806
|
};
|
|
825
807
|
}
|
|
826
808
|
|
|
827
|
-
getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT) {
|
|
828
|
-
const arr = []
|
|
809
|
+
getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT): EventStartNode<VT>[] {
|
|
810
|
+
const arr: EventStartNode<VT>[] = []
|
|
829
811
|
for (; !TC.gt(node.time, rangeRight); ) {
|
|
830
812
|
const next = node.next;
|
|
831
813
|
arr.push(node);
|
|
@@ -836,8 +818,8 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
836
818
|
}
|
|
837
819
|
return arr;
|
|
838
820
|
}
|
|
839
|
-
getNodesAfterOne(node: EventStartNode<VT>) {
|
|
840
|
-
const arr = []
|
|
821
|
+
getNodesAfterOne(node: EventStartNode<VT>): EventStartNode<VT>[] {
|
|
822
|
+
const arr: EventStartNode<VT>[] = []
|
|
841
823
|
while (true) {
|
|
842
824
|
const next = node.next;
|
|
843
825
|
if (next.type === NodeType.TAIL) {
|
|
@@ -848,6 +830,72 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
|
|
|
848
830
|
}
|
|
849
831
|
return arr;
|
|
850
832
|
}
|
|
833
|
+
// connectLine(consumer: JudgeLine) {
|
|
834
|
+
// this.consumerLines.add(consumer);
|
|
835
|
+
// }
|
|
836
|
+
// disconnectLine(consumer: JudgeLine) {
|
|
837
|
+
// this.consumerLines.delete(consumer);
|
|
838
|
+
// }
|
|
839
|
+
/**
|
|
840
|
+
* 将多个事件层上的数值性事件节点序列合为一个
|
|
841
|
+
*
|
|
842
|
+
* 目前仅用于速度序列,因为就他是线性的(
|
|
843
|
+
*
|
|
844
|
+
* 会创建新序列
|
|
845
|
+
* @param sequences
|
|
846
|
+
* @throws {KPAError<ERROR_IDS.NEEDS_AT_LEAST_ONE_ENS>}
|
|
847
|
+
*/
|
|
848
|
+
static mergeSequences(sequences: EventNodeSequence[]) {
|
|
849
|
+
if (sequences.length < 1) {
|
|
850
|
+
throw err.NEEDS_AT_LEAST_ONE_ENS();
|
|
851
|
+
}
|
|
852
|
+
const dest = EventNodeSequence.newSeq(EventType.speed, sequences[0].effectiveBeats);
|
|
853
|
+
dest.head.next.value = 0;
|
|
854
|
+
dest.id = sequences[0].id + "_merged";
|
|
855
|
+
for (const seq of sequences) {
|
|
856
|
+
if (seq.type !== EventType.speed) {
|
|
857
|
+
throw err.SEQUENCE_TYPE_NOT_CONSISTENT("speed", EventType[seq.type]);
|
|
858
|
+
}
|
|
859
|
+
const nodesToChange: [EventStartNode | EventEndNode, number][] = [];
|
|
860
|
+
const inserts: [toInsert: EventStartNode, targetBeats: number][] = [];
|
|
861
|
+
let endNode = seq.head.next.next;
|
|
862
|
+
// 这里是背靠背遍历,不是面对面遍历
|
|
863
|
+
// 我把节点关系都写这里了,通义灵码还不会看,乱给补全,罚他看114514遍
|
|
864
|
+
while (endNode.type !== NodeType.TAIL) {
|
|
865
|
+
const startNode = endNode.next;
|
|
866
|
+
const endTime = endNode.time;
|
|
867
|
+
const endBeats = TC.toBeats(endTime);
|
|
868
|
+
const targetStart = dest.getNodeAt(endBeats)
|
|
869
|
+
if (TC.eq(targetStart.time, endTime)) {
|
|
870
|
+
const targetEnd = targetStart.previous as EventEndNode;
|
|
871
|
+
nodesToChange.push([targetStart, startNode.value]);
|
|
872
|
+
nodesToChange.push([targetEnd, endNode.value]);
|
|
873
|
+
} else {
|
|
874
|
+
const newEnd = new EventEndNode(endTime, endNode.value + targetStart.getValueAt(endBeats));
|
|
875
|
+
const newStart = new EventStartNode(endTime, startNode.value + targetStart.getValueAt(endBeats));
|
|
876
|
+
EventNode.connect(newEnd, newStart);
|
|
877
|
+
inserts.push([newStart, endBeats]);
|
|
878
|
+
}
|
|
879
|
+
endNode = startNode.next;
|
|
880
|
+
}
|
|
881
|
+
dest.head.next.value += seq.head.next.value;
|
|
882
|
+
const len1 = nodesToChange.length;
|
|
883
|
+
for (let i = 0; i < len1; i++) {
|
|
884
|
+
const tup = nodesToChange[i];
|
|
885
|
+
tup[0].value += tup[1];
|
|
886
|
+
}
|
|
887
|
+
const len2 = inserts.length;
|
|
888
|
+
for (let i = 0; i < len2; i++) {
|
|
889
|
+
const tup = inserts[i];
|
|
890
|
+
// 这都updateJump
|
|
891
|
+
dest.updateJump(...EventNode.insert(tup[0], dest.getNodeAt(tup[1])));
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
dest.initJump();
|
|
895
|
+
return dest;
|
|
896
|
+
}
|
|
851
897
|
}
|
|
852
898
|
|
|
899
|
+
export type SpeedENS = EventNodeSequence<number> & { type: EventType.speed };
|
|
900
|
+
|
|
853
901
|
/// #enddeclaration
|