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/package.json
CHANGED
package/rpeChartCompiler.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
|
|
1
2
|
import type { Chart, UIName } from "./chart";
|
|
2
3
|
import { type TimeT, type ChartDataRPE, type MetaData, type JudgeLineDataRPE, type EventLayerDataRPE, type EventDataRPELike, EventType, InterpreteAs, type NoteDataRPE, type EventValueESType, EventValueType } from "./chartTypes";
|
|
3
4
|
import { SegmentedEasing, BezierEasing, NormalEasing, fixedEasing, TemplateEasing, Easing } from "./easing";
|
|
4
|
-
import {
|
|
5
|
+
import { err } from "./env";
|
|
6
|
+
import { EasedEvaluator, Evaluator, ExpressionEvaluator, NumericEasedEvaluator, TextEasedEvaluator, type EasedEvaluatorConstructorOfType, type EasedEvaluatorOfType } from "./evaluator";
|
|
5
7
|
import { EventEndNode, EventNode, EventNodeSequence, EventStartNode, type EventNodeLike } from "./event";
|
|
6
8
|
import type { JudgeLine } from "./judgeline";
|
|
7
9
|
import type { NNList, HNList, NNOrHead } from "./note";
|
|
8
|
-
import
|
|
10
|
+
import TC from "./time";
|
|
9
11
|
import { NodeType, numberToRatio } from "./util";
|
|
10
12
|
|
|
11
13
|
/// #declaration:global
|
|
12
14
|
|
|
13
15
|
const getInnerEasing = (easing: Easing) => easing instanceof SegmentedEasing ? easing.easing : easing;
|
|
14
16
|
|
|
17
|
+
type EasedStartNode<VT extends EventValueESType> = EventStartNode<VT> & { evaluator: EasedEvaluatorOfType<VT>};
|
|
18
|
+
|
|
15
19
|
/**
|
|
16
20
|
* 全生命周期只会编译一次,想多次就再构造一个
|
|
17
21
|
*/
|
|
@@ -58,7 +62,9 @@ export class RPEChartCompiler {
|
|
|
58
62
|
eventLayers: [],
|
|
59
63
|
father: target.id,
|
|
60
64
|
isCover: lineData.isCover,
|
|
61
|
-
numOfNotes: 0
|
|
65
|
+
numOfNotes: 0,
|
|
66
|
+
anchor: target.anchor,
|
|
67
|
+
isGif: 0
|
|
62
68
|
} satisfies Partial<JudgeLineDataRPE> as JudgeLineDataRPE)
|
|
63
69
|
} else {
|
|
64
70
|
lineData.attachUI = uiName;
|
|
@@ -89,18 +95,18 @@ export class RPEChartCompiler {
|
|
|
89
95
|
Name: judgeLine.name,
|
|
90
96
|
Texture: judgeLine.texture,
|
|
91
97
|
bpmfactor: 1.0,
|
|
92
|
-
eventLayers: judgeLine.eventLayers.map((layer): EventLayerDataRPE => ({
|
|
93
|
-
moveXEvents: layer.moveX ? this.dumpEventNodeSequence(layer.moveX) :
|
|
94
|
-
moveYEvents: layer.moveY ? this.dumpEventNodeSequence(layer.moveY) :
|
|
95
|
-
rotateEvents: layer.rotate ? this.dumpEventNodeSequence(layer.rotate) :
|
|
96
|
-
alphaEvents: layer.alpha ? this.dumpEventNodeSequence(layer.alpha) :
|
|
97
|
-
speedEvents:
|
|
98
|
+
eventLayers: judgeLine.eventLayers.map((layer, index): EventLayerDataRPE => ({
|
|
99
|
+
moveXEvents: layer.moveX ? this.dumpEventNodeSequence(layer.moveX) : undefined,
|
|
100
|
+
moveYEvents: layer.moveY ? this.dumpEventNodeSequence(layer.moveY) : undefined,
|
|
101
|
+
rotateEvents: layer.rotate ? this.dumpEventNodeSequence(layer.rotate) : undefined,
|
|
102
|
+
alphaEvents: layer.alpha ? this.dumpEventNodeSequence(layer.alpha) : undefined,
|
|
103
|
+
speedEvents: index === 0 ? this.dumpEventNodeSequence(judgeLine.speedSequence) : undefined
|
|
98
104
|
})),
|
|
99
105
|
extended: {
|
|
100
|
-
scaleXEvents: judgeLine.extendedLayer.scaleX ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleX) :
|
|
101
|
-
scaleYEvents: judgeLine.extendedLayer.scaleY ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleY) :
|
|
102
|
-
textEvents: judgeLine.extendedLayer.text ? this.dumpEventNodeSequence(judgeLine.extendedLayer.text) :
|
|
103
|
-
colorEvents: judgeLine.extendedLayer.color ? this.dumpEventNodeSequence(judgeLine.extendedLayer.color) :
|
|
106
|
+
scaleXEvents: judgeLine.extendedLayer.scaleX ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleX) : undefined,
|
|
107
|
+
scaleYEvents: judgeLine.extendedLayer.scaleY ? this.dumpEventNodeSequence(judgeLine.extendedLayer.scaleY) : undefined,
|
|
108
|
+
textEvents: judgeLine.extendedLayer.text ? this.dumpEventNodeSequence(judgeLine.extendedLayer.text) : undefined,
|
|
109
|
+
colorEvents: judgeLine.extendedLayer.color ? this.dumpEventNodeSequence(judgeLine.extendedLayer.color) : undefined
|
|
104
110
|
},
|
|
105
111
|
father: judgeLine.father?.id ?? -1,
|
|
106
112
|
isCover: judgeLine.cover ? 1 : 0,
|
|
@@ -121,7 +127,11 @@ export class RPEChartCompiler {
|
|
|
121
127
|
const easing = evaluator.easing;
|
|
122
128
|
const isSegmented = easing instanceof SegmentedEasing;
|
|
123
129
|
const innerEasing = isSegmented ? easing.easing : easing;
|
|
124
|
-
|
|
130
|
+
const start = getValue(snode);
|
|
131
|
+
const end = getValue(easing === fixedEasing ? snode : endNode)
|
|
132
|
+
// if (isNaN(start) || isNaN(end)) {
|
|
133
|
+
// console.log("????")
|
|
134
|
+
// }
|
|
125
135
|
return {
|
|
126
136
|
bezier: innerEasing instanceof BezierEasing ? 1 : 0,
|
|
127
137
|
bezierPoints: innerEasing instanceof BezierEasing ?
|
|
@@ -129,29 +139,29 @@ export class RPEChartCompiler {
|
|
|
129
139
|
[0, 0, 0, 0],
|
|
130
140
|
easingLeft: isSegmented ? easing.left : 0.0,
|
|
131
141
|
easingRight: isSegmented ? easing.right : 1.0,
|
|
142
|
+
// @ts-expect-error 缓动为贝塞尔型时可以为null
|
|
132
143
|
easingType: easing instanceof NormalEasing ?
|
|
133
144
|
easing.rpeId ?? 1 :
|
|
134
145
|
null,
|
|
135
|
-
end
|
|
146
|
+
end,
|
|
136
147
|
endTime: endNode.time,
|
|
137
148
|
linkgroup: 0, // 假设默认值为 0
|
|
138
|
-
start
|
|
149
|
+
start,
|
|
139
150
|
startTime: snode.time
|
|
140
151
|
}
|
|
141
152
|
}
|
|
142
153
|
|
|
143
|
-
dumpEventNodeSequence<VT>(sequence: EventNodeSequence<VT>): EventDataRPELike<VT>[] {
|
|
154
|
+
dumpEventNodeSequence<VT extends EventValueESType>(sequence: EventNodeSequence<VT>): EventDataRPELike<VT>[] {
|
|
144
155
|
const nodes: EventDataRPELike<VT>[] = [];
|
|
145
156
|
const interpolationStep = this.interpolationStep;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
let node = sequence.head.next;
|
|
157
|
+
sequence = this.substitute(sequence);
|
|
158
|
+
|
|
159
|
+
let node = sequence.head.next!;
|
|
151
160
|
// 唯一真史
|
|
152
161
|
const getValue = (sequence.type === EventType.text
|
|
153
162
|
? (node: EventStartNode<string> | EventEndNode<string>) => {
|
|
154
|
-
const
|
|
163
|
+
const evaluator = (node instanceof EventStartNode ? node.evaluator : node.previous.evaluator) as TextEasedEvaluator;
|
|
164
|
+
const interpretedAs = evaluator.interpretedAs;
|
|
155
165
|
return interpretedAs === InterpreteAs.str ? node.value : "%P%" + node.value;
|
|
156
166
|
}
|
|
157
167
|
: (node: EventStartNode<number> | EventEndNode<number>) => node.value) as unknown as (node: EventStartNode<VT> | EventEndNode<VT>) => VT;
|
|
@@ -159,6 +169,7 @@ export class RPEChartCompiler {
|
|
|
159
169
|
const end = node.next;
|
|
160
170
|
if (end.type === NodeType.TAIL) break;
|
|
161
171
|
if (node.evaluator instanceof ExpressionEvaluator) {
|
|
172
|
+
// 插值
|
|
162
173
|
let cur = node.time;
|
|
163
174
|
const endTime = end.time;
|
|
164
175
|
let value = getValue(node);
|
|
@@ -195,11 +206,17 @@ export class RPEChartCompiler {
|
|
|
195
206
|
})
|
|
196
207
|
|
|
197
208
|
} else {
|
|
198
|
-
nodes.push(this.
|
|
209
|
+
nodes.push(this.compileEasedEvent(node as EasedStartNode<VT>, getValue));
|
|
199
210
|
}
|
|
200
211
|
node = end.next;
|
|
201
212
|
}
|
|
202
|
-
|
|
213
|
+
// 刻意造一个事件,并给它加一个结束节点,距离一拍长(用于处理endValue)
|
|
214
|
+
const newStart = node!.clone();
|
|
215
|
+
// 只是占位而已,并不重要,我们这里期望最后一个事件的缓动为1号,特别注意不要写成0了
|
|
216
|
+
newStart.evaluator = NumericEasedEvaluator.evaluatorsOfNormalEasing[1] as unknown as EasedEvaluator<VT>;
|
|
217
|
+
const newEnd = new EventEndNode(TC.vadd(newStart.time, [1, 0, 1]), newStart.value);
|
|
218
|
+
EventNode.connect(newStart, newEnd);
|
|
219
|
+
nodes.push(this.compileEasedEvent(newStart as EasedStartNode<VT>, getValue));
|
|
203
220
|
|
|
204
221
|
return nodes
|
|
205
222
|
}
|
|
@@ -213,16 +230,17 @@ export class RPEChartCompiler {
|
|
|
213
230
|
if (lists.length === 0) return;
|
|
214
231
|
// 先按最早的时间排序
|
|
215
232
|
lists.sort((a, b) => {
|
|
216
|
-
return
|
|
233
|
+
return TC.gt(time(a), time(b)) ? 1 : -1;
|
|
217
234
|
});
|
|
218
235
|
// 每次从lists中第一个list pop一个data加入到结果,然后冒泡调整这个list的位置
|
|
219
236
|
while (lists[0].length > 0) {
|
|
220
237
|
const list = lists[0];
|
|
221
238
|
// 只需要pop就可以了,pop复杂度O(1),这是倒序的原因
|
|
222
239
|
const node = list.pop();
|
|
223
|
-
|
|
240
|
+
// !: 上面保证了list一定还有至少一个元素,否则的话不满足循环条件
|
|
241
|
+
ret.push(node!);
|
|
224
242
|
let i = 0;
|
|
225
|
-
while (i + 1 < lists.length &&
|
|
243
|
+
while (i + 1 < lists.length && TC.gt(time(lists[i]), time(lists[i + 1]))) {
|
|
226
244
|
const temp = lists[i];
|
|
227
245
|
lists[i] = lists[i + 1];
|
|
228
246
|
lists[i + 1] = temp;
|
|
@@ -242,12 +260,15 @@ export class RPEChartCompiler {
|
|
|
242
260
|
*/
|
|
243
261
|
nnListToArray(nnList: NNList) {
|
|
244
262
|
const notes: NoteDataRPE[] = [];
|
|
245
|
-
|
|
263
|
+
// 这个地方正常来讲不会为null或undefined
|
|
264
|
+
let node: NNOrHead = nnList.tail.previous!;
|
|
265
|
+
// 从最尾往前遍历
|
|
246
266
|
while (node.type !== NodeType.HEAD) {
|
|
247
|
-
for (
|
|
267
|
+
for (const each of node.notes) {
|
|
248
268
|
notes.push(each.dumpRPE(this.chart.timeCalculator));
|
|
249
269
|
}
|
|
250
|
-
|
|
270
|
+
// 同上,正常来讲不会为null或undefined
|
|
271
|
+
node = node.previous!;
|
|
251
272
|
}
|
|
252
273
|
return notes;
|
|
253
274
|
}
|
|
@@ -256,27 +277,32 @@ export class RPEChartCompiler {
|
|
|
256
277
|
* 将当前序列中所有通过模板缓动引用了其他序列的事件直接展开为被引用的序列内容
|
|
257
278
|
* transform all events that reference other sequences by template easing
|
|
258
279
|
* into the content of the referenced sequence
|
|
259
|
-
*
|
|
260
|
-
*
|
|
280
|
+
*
|
|
281
|
+
* 有点类似于MediaWiki的`{{subst:templateName}}`
|
|
261
282
|
* @returns
|
|
262
283
|
*/
|
|
263
284
|
substitute<VT extends EventValueESType>(seq: EventNodeSequence<VT>): EventNodeSequence<VT> {
|
|
264
285
|
const map = this.sequenceMap;
|
|
265
286
|
if (map.has(seq)) {
|
|
266
|
-
|
|
287
|
+
// 都has了还担心啥undefined,TSC也是个人机
|
|
288
|
+
return map.get(seq)!;
|
|
267
289
|
}
|
|
268
|
-
|
|
290
|
+
// 一般不会为null
|
|
291
|
+
let currentNode: EventStartNode<VT> = seq.head.next!;
|
|
269
292
|
const newSeq = new EventNodeSequence<VT>(seq.type, seq.effectiveBeats);
|
|
270
293
|
const valueType = seq.type === EventType.color
|
|
271
294
|
? EventValueType.color : seq.type === EventType.text
|
|
272
295
|
? EventValueType.text : EventValueType.numeric;
|
|
296
|
+
// 加入哈希表缓存,避免重复计算
|
|
273
297
|
map.set(seq, newSeq);
|
|
274
298
|
let currentPos: EventNodeLike<NodeType.HEAD, VT> | EventEndNode<VT> = newSeq.head;
|
|
275
299
|
while (true) {
|
|
276
300
|
if (!currentNode || (currentNode.next.type === NodeType.TAIL)) {
|
|
277
301
|
break;
|
|
278
302
|
}
|
|
303
|
+
/** 原序列当前结束节点 */
|
|
279
304
|
const endNode = currentNode.next;
|
|
305
|
+
/** 原序列当前节点的求值器 */
|
|
280
306
|
const evaluator = currentNode.evaluator;
|
|
281
307
|
let innerEasing: Easing;
|
|
282
308
|
if ( evaluator
|
|
@@ -293,15 +319,15 @@ export class RPEChartCompiler {
|
|
|
293
319
|
const timeDelta = TC.sub(currentNode.next.time, startTime);
|
|
294
320
|
|
|
295
321
|
|
|
296
|
-
let srcStart: number, srcEnd: number, leftDividedNodeSrc: EventStartNode,
|
|
322
|
+
let srcStart: number, srcEnd: number, leftDividedNodeSrc: EventStartNode, rightDividedNodeSrc: EventStartNode,
|
|
297
323
|
srcStartTime: TimeT, srcTimeDelta: TimeT, toStopAt: EventStartNode;
|
|
298
324
|
if (isSegmented) {
|
|
299
|
-
const totalDuration = TC.sub(srcSeq.tail.previous
|
|
325
|
+
const totalDuration = TC.sub(srcSeq.tail.previous!.time, srcSeq.head.next!.time);
|
|
300
326
|
srcStart = srcSeq.getValueAt(easing.left * srcSeq.effectiveBeats)
|
|
301
327
|
srcEnd = srcSeq.getValueAt(easing.right * srcSeq.effectiveBeats, true);
|
|
302
328
|
leftDividedNodeSrc = srcSeq.getNodeAt(easing.left * srcSeq.effectiveBeats);
|
|
303
|
-
|
|
304
|
-
toStopAt =
|
|
329
|
+
rightDividedNodeSrc = srcSeq.getNodeAt(easing.right * srcSeq.effectiveBeats, true);
|
|
330
|
+
toStopAt = rightDividedNodeSrc.next.next!;
|
|
305
331
|
srcStartTime = TC.mul(totalDuration, numberToRatio(easing.left));
|
|
306
332
|
const srcEndTime = TC.mul(totalDuration, numberToRatio(easing.right));
|
|
307
333
|
TC.validateIp(srcStartTime);
|
|
@@ -309,13 +335,13 @@ export class RPEChartCompiler {
|
|
|
309
335
|
srcTimeDelta = TC.sub(srcEndTime, srcStartTime);
|
|
310
336
|
TC.validateIp(srcTimeDelta);
|
|
311
337
|
} else {
|
|
312
|
-
srcStart = srcSeq.head.next
|
|
313
|
-
srcEnd = srcSeq.tail.previous
|
|
314
|
-
leftDividedNodeSrc = srcSeq.head.next
|
|
315
|
-
|
|
316
|
-
toStopAt =
|
|
317
|
-
srcStartTime = srcSeq.head.next
|
|
318
|
-
srcTimeDelta = TC.sub(srcSeq.tail.previous
|
|
338
|
+
srcStart = srcSeq.head.next!.value;
|
|
339
|
+
srcEnd = srcSeq.tail.previous!.value;
|
|
340
|
+
leftDividedNodeSrc = srcSeq.head.next!;
|
|
341
|
+
rightDividedNodeSrc = srcSeq.tail.previous!;
|
|
342
|
+
toStopAt = rightDividedNodeSrc;
|
|
343
|
+
srcStartTime = srcSeq.head.next!.time;
|
|
344
|
+
srcTimeDelta = TC.sub(srcSeq.tail.previous!.time, srcStartTime);
|
|
319
345
|
}
|
|
320
346
|
|
|
321
347
|
const srcDelta = srcEnd - srcStart;
|
|
@@ -327,84 +353,93 @@ export class RPEChartCompiler {
|
|
|
327
353
|
const convertTime: (t: TimeT) => TimeT
|
|
328
354
|
= (time: TimeT) => TC.validateIp(TC.add(startTime, TC.mul(TC.sub(time, srcStartTime), ratio)));
|
|
329
355
|
|
|
356
|
+
/** 目标序列中某次参与替换操作的第一个节点 */
|
|
330
357
|
const first = currentNode.clone();
|
|
331
358
|
EventNode.connect(currentPos, first)
|
|
332
359
|
// 处理第一个节点的截段
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const value = exprEvaluator.func(
|
|
346
|
-
t * (TC.getDelta((first.next as EventEndNode).time, first.time) * (1 - left)) / srcSeq.effectiveBeats
|
|
347
|
-
); // 脑细胞杀器
|
|
348
|
-
return convert(value);
|
|
349
|
-
}
|
|
350
|
-
} else {
|
|
351
|
-
// 否则就是带缓动求值器
|
|
352
|
-
first.evaluator = new EvaluatorConstructor(
|
|
353
|
-
new SegmentedEasing((leftDividedNodeSrc.evaluator as NumericEasedEvaluator).easing, newLeft, 1.0)
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
}
|
|
360
|
+
if (
|
|
361
|
+
isSegmented
|
|
362
|
+
&& (
|
|
363
|
+
easing.left * srcSeq.effectiveBeats - TC.toBeats(leftDividedNodeSrc.time) > 1e-6
|
|
364
|
+
)
|
|
365
|
+
) {
|
|
366
|
+
const left = easing.left * srcSeq.effectiveBeats;
|
|
367
|
+
// 断言:这里left不会大于有效拍数
|
|
368
|
+
const newLeft = left / (TC.toBeats((leftDividedNodeSrc.next as EventEndNode).time) - TC.toBeats(leftDividedNodeSrc.time))
|
|
369
|
+
// 如果切到的这个位置是表达式求值器,这是没办法保留缓动的,只能运用表达式求值器
|
|
370
|
+
if (leftDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
371
|
+
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
357
372
|
} else {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
first.evaluator = new EvaluatorConstructor((leftDividedNodeSrc.evaluator as NumericEasedEvaluator).easing, evaluator.interpretedAs);
|
|
367
|
-
}
|
|
373
|
+
// 否则就是带缓动求值器
|
|
374
|
+
first.evaluator = evaluator.deriveWithEasing(
|
|
375
|
+
new SegmentedEasing((leftDividedNodeSrc.evaluator as NumericEasedEvaluator).easing, newLeft, 1.0)
|
|
376
|
+
) as unknown as Evaluator<VT>;
|
|
377
|
+
// TypeScript Compiler我*你娘啊
|
|
378
|
+
|
|
368
379
|
}
|
|
369
380
|
} else {
|
|
370
381
|
if (leftDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
371
|
-
|
|
372
|
-
// 也不知道对不对
|
|
373
|
-
// @ts-ignore
|
|
374
|
-
first.evaluator.func = (t: number) => {
|
|
375
|
-
return convert(leftDividedNodeSrc.evaluator.eval(leftDividedNodeSrc, t))
|
|
376
|
-
}
|
|
382
|
+
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
377
383
|
} else {
|
|
378
|
-
first.evaluator =
|
|
384
|
+
first.evaluator = evaluator.deriveWithEasing(
|
|
385
|
+
(leftDividedNodeSrc.evaluator as NumericEasedEvaluator).easing
|
|
386
|
+
) as unknown as Evaluator<VT>;
|
|
379
387
|
}
|
|
380
388
|
}
|
|
381
389
|
let prev = first
|
|
382
390
|
// 这里在到toStopAt之前一直都是非尾的
|
|
383
|
-
for (let n: EventEndNode<number> = leftDividedNodeSrc.next as EventEndNode<number>; n.next !== toStopAt;
|
|
391
|
+
for (let n: EventEndNode<number> = leftDividedNodeSrc.next as EventEndNode<number>; n.next !== toStopAt; ) {
|
|
384
392
|
const endNode = n;
|
|
385
393
|
const startNode = n.next;
|
|
394
|
+
// 断言:TSC不理解序列结构,这里截止得可能还早些,一定不是尾结点
|
|
395
|
+
n = startNode.next as EventEndNode<number>;
|
|
386
396
|
const newEnd = new EventEndNode(convertTime(endNode.time), convert(endNode.value));
|
|
387
397
|
const newStart = new EventStartNode(convertTime(startNode.time), convert(startNode.value));
|
|
388
|
-
|
|
398
|
+
if (startNode.evaluator instanceof ExpressionEvaluator) {
|
|
399
|
+
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
400
|
+
} else {
|
|
401
|
+
newStart.evaluator = evaluator.deriveWithEasing(
|
|
402
|
+
(startNode.evaluator as NumericEasedEvaluator).easing,
|
|
403
|
+
) as unknown as Evaluator<VT>;
|
|
404
|
+
}
|
|
389
405
|
EventNode.connect(prev, newEnd)
|
|
390
406
|
EventNode.connect(newEnd, newStart);
|
|
391
407
|
prev = newStart;
|
|
392
408
|
}
|
|
393
409
|
// 处理最后一个节点的截段
|
|
394
|
-
if (
|
|
410
|
+
if (
|
|
411
|
+
isSegmented
|
|
412
|
+
&& (
|
|
413
|
+
TC.toBeats((rightDividedNodeSrc.next as EventEndNode).time) - easing.right * srcSeq.effectiveBeats > 1e-6
|
|
414
|
+
)
|
|
415
|
+
) {
|
|
395
416
|
const right = easing.right * srcSeq.effectiveBeats
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
417
|
+
// 断言:这里left不会大于有效拍数
|
|
418
|
+
const newRight = right / (TC.toBeats((rightDividedNodeSrc.next as EventEndNode).time) - TC.toBeats(rightDividedNodeSrc.time))
|
|
419
|
+
// 如果切到的这个位置是表达式求值器,这是没办法保留缓动的,只能运用表达式求值器
|
|
420
|
+
if (rightDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
421
|
+
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
422
|
+
} else {
|
|
423
|
+
// 否则就是带缓动求值器
|
|
424
|
+
first.evaluator = evaluator.deriveWithEasing(
|
|
425
|
+
new SegmentedEasing((rightDividedNodeSrc.evaluator as NumericEasedEvaluator).easing, 0.0, newRight)
|
|
426
|
+
) as unknown as Evaluator<VT>;
|
|
427
|
+
// TypeScript Compiler我*你娘啊
|
|
402
428
|
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
if (rightDividedNodeSrc.evaluator instanceof ExpressionEvaluator) {
|
|
432
|
+
throw err.CANNOT_DIVIDE_EXPRESSION_EVALUATOR(seq.id);
|
|
433
|
+
} else {
|
|
434
|
+
first.evaluator = evaluator.deriveWithEasing(
|
|
435
|
+
(rightDividedNodeSrc.evaluator as NumericEasedEvaluator).easing
|
|
436
|
+
) as unknown as Evaluator<VT>;
|
|
437
|
+
}
|
|
403
438
|
}
|
|
404
439
|
const endNode = currentNode.next.clone();
|
|
405
440
|
EventNode.connect(prev, endNode);
|
|
406
441
|
currentPos = endNode;
|
|
407
|
-
endNode.value = isSegmented ? endNode.value : convert((srcSeq.tail.previous
|
|
442
|
+
endNode.value = isSegmented ? endNode.value : convert((srcSeq.tail.previous!.previous as EventEndNode).value);
|
|
408
443
|
} else {
|
|
409
444
|
const newStartNode = currentNode.clone();
|
|
410
445
|
const newEndNode = endNode.clone();
|
|
@@ -420,6 +455,6 @@ export class RPEChartCompiler {
|
|
|
420
455
|
return newSeq;
|
|
421
456
|
}
|
|
422
457
|
}
|
|
423
|
-
|
|
458
|
+
// 现在是2025年10月18日,杨哲思已经改掉了此项目最史山的代码之一,但是还是一坨
|
|
424
459
|
|
|
425
460
|
/// #enddeclaration
|