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/easing.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { EventNodeSequence } from "./event";
|
|
1
|
+
import { type TemplateEasingBodyData, type EasingDataKPA2, EasingType, EventType, type SegmentedEasingData, type NormalEasingData, type BezierEasingData, type TemplateEasingData, WrapperEasingData, WrapperEasingBodyData } from "./chartTypes";
|
|
2
|
+
import { type EventNodeSequence } from "./event";
|
|
3
3
|
import { type TupleCoord } from "./util";
|
|
4
4
|
import Environment from "./env";
|
|
5
|
+
import { type ExpressionEvaluator } from "./evaluator";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
/// #declaration:global
|
|
@@ -43,10 +44,6 @@ const easeOutBack = (x: number): number =>{
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const linear = (x: number): number => x
|
|
46
|
-
const linearLine: CurveDrawer = (context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number) =>
|
|
47
|
-
drawLine(context, startX, startY, endX, endY);
|
|
48
|
-
|
|
49
|
-
|
|
50
47
|
|
|
51
48
|
const easeOutSine = (x: number): number => Math.sin((x * Math.PI) / 2);
|
|
52
49
|
|
|
@@ -95,7 +92,12 @@ const easeInOutBack = toEaseInOut(easeInBack, easeOutBack);
|
|
|
95
92
|
const easeInOutElastic = toEaseInOut(easeInElastic, easeOutElastic);
|
|
96
93
|
const easeInOutBounce = toEaseInOut(easeInBounce, easeOutBounce);
|
|
97
94
|
|
|
98
|
-
|
|
95
|
+
|
|
96
|
+
type FuncType = "linear" | "sine" | "quad" | "cubic" | "quart" | "quint" | "expo" | "circ" | "back" | "elastic" | "bounce"
|
|
97
|
+
|
|
98
|
+
export const easingFnMap: {
|
|
99
|
+
[k in FuncType]: [(x: number) => number, (x: number) => number, (x: number) => number]
|
|
100
|
+
} = {
|
|
99
101
|
"linear": [linear, linear, linear],
|
|
100
102
|
"sine": [easeInSine, easeOutSine, toEaseInOut(easeInSine, easeOutSine)],
|
|
101
103
|
"quad": [easeInQuad, easeOutQuad, toEaseInOut(easeInQuad, easeOutQuad)],
|
|
@@ -134,24 +136,9 @@ export abstract class Easing {
|
|
|
134
136
|
}
|
|
135
137
|
return (t: number) => (this.getValue(easingLeft + timeDelta * t) - leftValue) / delta;
|
|
136
138
|
}
|
|
137
|
-
drawCurve(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number): void {
|
|
138
|
-
const delta = endY - startY;
|
|
139
|
-
const timeDelta = endX - startX;
|
|
140
|
-
let last = startY;
|
|
141
|
-
context.beginPath()
|
|
142
|
-
context.moveTo(startX, last)
|
|
143
|
-
for (let t = 4; t <= timeDelta; t += 4) {
|
|
144
|
-
const ratio = t / timeDelta
|
|
145
|
-
const curPosY = this.getValue(ratio) * delta + startY;
|
|
146
|
-
context.lineTo(startX + t, curPosY);
|
|
147
|
-
last = curPosY;
|
|
148
|
-
}
|
|
149
|
-
context.stroke();
|
|
150
|
-
}
|
|
151
139
|
}
|
|
152
140
|
|
|
153
141
|
|
|
154
|
-
type CurveDrawer = (context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number) => void
|
|
155
142
|
|
|
156
143
|
|
|
157
144
|
/**
|
|
@@ -191,15 +178,11 @@ export class NormalEasing extends Easing {
|
|
|
191
178
|
funcType: string;
|
|
192
179
|
easeType: string;
|
|
193
180
|
_getValue: (t: number) => number;
|
|
194
|
-
_drawCurve: CurveDrawer;
|
|
195
181
|
constructor(fn: (t: number) => number);
|
|
196
|
-
constructor(fn: (t: number) => number
|
|
197
|
-
constructor(fn: (t: number) => number
|
|
182
|
+
constructor(fn: (t: number) => number);
|
|
183
|
+
constructor(fn: (t: number) => number) {
|
|
198
184
|
super()
|
|
199
185
|
this._getValue = fn;
|
|
200
|
-
if (curveDrawer) {
|
|
201
|
-
this._drawCurve = curveDrawer;
|
|
202
|
-
}
|
|
203
186
|
}
|
|
204
187
|
getValue(t: number): number {
|
|
205
188
|
if (t > 1 || t < 0) {
|
|
@@ -216,13 +199,6 @@ export class NormalEasing extends Easing {
|
|
|
216
199
|
identifier: this.rpeId
|
|
217
200
|
}
|
|
218
201
|
}
|
|
219
|
-
drawCurve(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number) {
|
|
220
|
-
if (this._drawCurve) {
|
|
221
|
-
this._drawCurve(context, startX, startY, endX, endY)
|
|
222
|
-
} else {
|
|
223
|
-
super.drawCurve(context, startX, startY, endX, endY);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
202
|
}
|
|
227
203
|
|
|
228
204
|
|
|
@@ -234,18 +210,18 @@ export class NormalEasing extends Easing {
|
|
|
234
210
|
* uses the Bezier curve formula to describe an easing.
|
|
235
211
|
*/
|
|
236
212
|
export class BezierEasing extends Easing {
|
|
237
|
-
readonly xs:
|
|
238
|
-
readonly ys:
|
|
239
|
-
readonly jumper:
|
|
213
|
+
readonly xs: Float64Array;
|
|
214
|
+
readonly ys: Float64Array;
|
|
215
|
+
readonly jumper: Uint8Array;
|
|
240
216
|
constructor(public readonly cp1: TupleCoord, public readonly cp2: TupleCoord) {
|
|
241
217
|
super()
|
|
242
218
|
const BEZIER_INTERPOLATION_DENSITY = Environment.BEZIER_INTERPOLATION_DENSITY;
|
|
243
219
|
const BEZIER_INTERPOLATION_STEP = 1 / BEZIER_INTERPOLATION_DENSITY;
|
|
244
220
|
// 插值,把贝塞尔曲线近似成256段折线
|
|
245
|
-
const xs
|
|
246
|
-
const ys
|
|
221
|
+
const xs = new Float64Array(BEZIER_INTERPOLATION_DENSITY - 1);
|
|
222
|
+
const ys = new Float64Array(BEZIER_INTERPOLATION_DENSITY - 1);
|
|
247
223
|
/** 一把尺子,刻度均匀,从`插值步长*下标`映射到xs里面的下标 */
|
|
248
|
-
const jumper
|
|
224
|
+
const jumper = new Uint8Array(BEZIER_INTERPOLATION_DENSITY);
|
|
249
225
|
let nextToFill = 0;
|
|
250
226
|
for (let i = 1; i < BEZIER_INTERPOLATION_DENSITY; i++) {
|
|
251
227
|
// 这个t是贝塞尔曲线生成参数
|
|
@@ -261,9 +237,9 @@ export class BezierEasing extends Easing {
|
|
|
261
237
|
for (; 1 > nextToFill * BEZIER_INTERPOLATION_STEP; nextToFill++) {
|
|
262
238
|
jumper[nextToFill] = BEZIER_INTERPOLATION_DENSITY - 1;
|
|
263
239
|
}
|
|
264
|
-
this.xs =
|
|
265
|
-
this.ys =
|
|
266
|
-
this.jumper =
|
|
240
|
+
this.xs = xs;
|
|
241
|
+
this.ys = ys;
|
|
242
|
+
this.jumper = jumper;
|
|
267
243
|
}
|
|
268
244
|
/**
|
|
269
245
|
* 从横坐标获得纵坐标
|
|
@@ -296,19 +272,6 @@ export class BezierEasing extends Easing {
|
|
|
296
272
|
bezier: [this.cp1[0], this.cp1[1], this.cp2[0], this.cp2[1]]
|
|
297
273
|
}
|
|
298
274
|
}
|
|
299
|
-
drawCurve(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number): void {
|
|
300
|
-
const [cp1x, cp1y] = this.cp1;
|
|
301
|
-
const [cp2x, cp2y] = this.cp2
|
|
302
|
-
const delta = endY - startY;
|
|
303
|
-
const timeDelta = endX - startX;
|
|
304
|
-
drawBezierCurve(
|
|
305
|
-
context,
|
|
306
|
-
startX, startY,
|
|
307
|
-
endX, endY,
|
|
308
|
-
startX + cp1x * timeDelta, startY + cp1y * delta,
|
|
309
|
-
startX + cp2x * timeDelta, startY + cp2y * delta,
|
|
310
|
-
)
|
|
311
|
-
}
|
|
312
275
|
}
|
|
313
276
|
|
|
314
277
|
/**
|
|
@@ -341,7 +304,7 @@ export class TemplateEasing extends Easing {
|
|
|
341
304
|
}
|
|
342
305
|
}
|
|
343
306
|
get valueDelta(): number {
|
|
344
|
-
|
|
307
|
+
const seq = this.eventNodeSequence;
|
|
345
308
|
return seq.tail.previous.value - seq.head.next.value;
|
|
346
309
|
}
|
|
347
310
|
get headValue(): number {
|
|
@@ -349,6 +312,24 @@ export class TemplateEasing extends Easing {
|
|
|
349
312
|
}
|
|
350
313
|
}
|
|
351
314
|
|
|
315
|
+
export class WrapperEasing extends Easing {
|
|
316
|
+
// 需要一对面对面节点
|
|
317
|
+
constructor(public evaluator: ExpressionEvaluator<number>, public start: number, public end: number, public name: string) {
|
|
318
|
+
super()
|
|
319
|
+
}
|
|
320
|
+
getValue(t: number): number {
|
|
321
|
+
const end = this.end;
|
|
322
|
+
const start = this.start;
|
|
323
|
+
return (this.evaluator.func(t) - start) / (end - start);
|
|
324
|
+
}
|
|
325
|
+
dump(): WrapperEasingData {
|
|
326
|
+
return {
|
|
327
|
+
type: EasingType.wrapper,
|
|
328
|
+
identifier: this.name
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
352
333
|
|
|
353
334
|
|
|
354
335
|
|
|
@@ -356,8 +337,10 @@ export class TemplateEasing extends Easing {
|
|
|
356
337
|
* 缓动库
|
|
357
338
|
* 用于管理模板缓动
|
|
358
339
|
* for template easing management
|
|
340
|
+
*
|
|
359
341
|
* 谱面的一个属性
|
|
360
342
|
* a property of chart
|
|
343
|
+
*
|
|
361
344
|
* 加载谱面时,先加载事件序列,所需的模板缓动会被加入到缓动库,但并不立即实现,在读取模板缓动时,才实现缓动。
|
|
362
345
|
* To load a chart, the eventNodeSquences will be first loaded, during which process
|
|
363
346
|
* the easings will be added to the easing library but not implemented immediately.
|
|
@@ -365,31 +348,42 @@ export class TemplateEasing extends Easing {
|
|
|
365
348
|
*
|
|
366
349
|
*/
|
|
367
350
|
export class TemplateEasingLib {
|
|
368
|
-
easings
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
constructor() {
|
|
372
|
-
this.easings = {};
|
|
351
|
+
easings = new Map<string, TemplateEasing>();
|
|
352
|
+
wrapperEasings = new Map<string, WrapperEasing>();
|
|
353
|
+
// 被迫给一个静态方法进行依赖注入,恨死你了,ESM
|
|
354
|
+
constructor(public getNewSequence: (type: EventType, effectiveBeats: number) => EventNodeSequence<number>, public ExpressionEvaluatorCon: typeof ExpressionEvaluator) {
|
|
373
355
|
}
|
|
374
356
|
getOrNew(name: string): TemplateEasing {
|
|
375
357
|
const DEFAULT_TEMPLATE_LENGTH = Environment.DEFAULT_TEMPLATE_LENGTH;
|
|
376
|
-
if (this.easings
|
|
377
|
-
return this.easings
|
|
358
|
+
if (this.easings.has(name)) {
|
|
359
|
+
return this.easings.get(name);
|
|
378
360
|
} else {
|
|
379
|
-
const easing = new TemplateEasing(name,
|
|
361
|
+
const easing = new TemplateEasing(name, this.getNewSequence(EventType.easing, DEFAULT_TEMPLATE_LENGTH));
|
|
380
362
|
easing.eventNodeSequence.id = "*" + name;
|
|
381
|
-
|
|
363
|
+
this.easings.set(name, easing)
|
|
364
|
+
return easing;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
readWrapperEasings(data: WrapperEasingBodyData[]) {
|
|
368
|
+
const len = data.length;
|
|
369
|
+
for (let i = 0; i < len; i++) {
|
|
370
|
+
const datum = data[i];
|
|
371
|
+
// 属于是油饼
|
|
372
|
+
this.wrapperEasings.set(datum.id, new WrapperEasing(new this.ExpressionEvaluatorCon(datum.jsExpr), datum.start, datum.end, datum.id))
|
|
382
373
|
}
|
|
383
374
|
}
|
|
375
|
+
getWrapper(name: string): WrapperEasing {
|
|
376
|
+
return this.wrapperEasings.get(name);
|
|
377
|
+
}
|
|
384
378
|
/**
|
|
385
379
|
* 注册一个模板缓动,但不会实现它
|
|
386
380
|
* register a template easing when reading eventNodeSequences, but does not implement it immediately
|
|
387
381
|
*/
|
|
388
382
|
require(name: string) {
|
|
389
|
-
this.easings
|
|
383
|
+
this.easings.set(name, new TemplateEasing(name, null));
|
|
390
384
|
}
|
|
391
385
|
implement(name: string, sequence: EventNodeSequence) {
|
|
392
|
-
this.easings
|
|
386
|
+
this.easings.get(name).eventNodeSequence = sequence;
|
|
393
387
|
}
|
|
394
388
|
/**
|
|
395
389
|
* 检查所有模板缓动是否实现
|
|
@@ -398,20 +392,19 @@ export class TemplateEasingLib {
|
|
|
398
392
|
* should be invoked after all template easings are read
|
|
399
393
|
*/
|
|
400
394
|
check() {
|
|
401
|
-
for (
|
|
402
|
-
if (!
|
|
403
|
-
console.warn(`未实现的缓动:${
|
|
395
|
+
for (const [name, easing] of this.easings) {
|
|
396
|
+
if (!easing.eventNodeSequence) {
|
|
397
|
+
console.warn(`未实现的缓动:${name}`);
|
|
404
398
|
}
|
|
405
399
|
}
|
|
406
400
|
}
|
|
407
401
|
get(key: string): TemplateEasing | undefined {
|
|
408
|
-
return this.easings
|
|
402
|
+
return this.easings.get(key);
|
|
409
403
|
}
|
|
410
404
|
|
|
411
|
-
dump(eventNodeSequences: Set<EventNodeSequence>):
|
|
412
|
-
const customEasingDataList:
|
|
413
|
-
for (
|
|
414
|
-
const templateEasing = this.easings[key];
|
|
405
|
+
dump(eventNodeSequences: Set<EventNodeSequence>): TemplateEasingBodyData[] {
|
|
406
|
+
const customEasingDataList: TemplateEasingBodyData[] = [];
|
|
407
|
+
for (const [key, templateEasing] of this.easings) {
|
|
415
408
|
const eventNodeSequence = templateEasing.eventNodeSequence;
|
|
416
409
|
if (eventNodeSequences.has(eventNodeSequence)) {
|
|
417
410
|
continue;
|
|
@@ -419,16 +412,26 @@ export class TemplateEasingLib {
|
|
|
419
412
|
eventNodeSequences.add(eventNodeSequence);
|
|
420
413
|
customEasingDataList.push({
|
|
421
414
|
name: key,
|
|
422
|
-
content: eventNodeSequence.id
|
|
423
|
-
usedBy: [],
|
|
424
|
-
dependencies: []
|
|
415
|
+
content: eventNodeSequence.id // 这里只存储编号,具体内容在保存时再编码
|
|
425
416
|
});
|
|
426
417
|
}
|
|
427
418
|
return customEasingDataList;
|
|
428
419
|
}
|
|
420
|
+
dumpWrapperEasings(): WrapperEasingBodyData[] {
|
|
421
|
+
const wrapperEasingDataList: WrapperEasingBodyData[] = [];
|
|
422
|
+
for (const [key, wrapperEasing] of this.wrapperEasings) {
|
|
423
|
+
wrapperEasingDataList.push({
|
|
424
|
+
id: key,
|
|
425
|
+
jsExpr: wrapperEasing.evaluator.jsExpr,
|
|
426
|
+
start: wrapperEasing.start,
|
|
427
|
+
end: wrapperEasing.end
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
return wrapperEasingDataList;
|
|
431
|
+
}
|
|
429
432
|
}
|
|
430
433
|
|
|
431
|
-
export const linearEasing = new NormalEasing(linear
|
|
434
|
+
export const linearEasing = new NormalEasing(linear);
|
|
432
435
|
export const fixedEasing = new NormalEasing((x: number): number => (x === 1 ? 1 : 0));
|
|
433
436
|
|
|
434
437
|
export const easingMap = {
|
|
@@ -446,8 +449,8 @@ export const easingMap = {
|
|
|
446
449
|
"bounce": {in: new NormalEasing(easeInBounce), out: new NormalEasing(easeOutBounce), inout: new NormalEasing(easeInOutBounce)}
|
|
447
450
|
}
|
|
448
451
|
|
|
449
|
-
for (
|
|
450
|
-
for (
|
|
452
|
+
for (const funcType in easingMap) {
|
|
453
|
+
for (const easeType in easingMap[funcType]) {
|
|
451
454
|
const easing = easingMap[funcType][easeType];
|
|
452
455
|
easing.funcType = funcType;
|
|
453
456
|
easing.easeType = easeType;
|
|
@@ -539,5 +542,8 @@ rpeEasingArray.forEach((easing, index) => {
|
|
|
539
542
|
}
|
|
540
543
|
easing.rpeId = index;
|
|
541
544
|
})
|
|
545
|
+
// 强行添加,避免存储不了这些缓动
|
|
546
|
+
easingMap.expo.inout.rpeId = 101;
|
|
547
|
+
easingMap.quint.inout.rpeId = 102;
|
|
542
548
|
|
|
543
549
|
/// #enddeclaration
|
package/env.ts
CHANGED
|
@@ -1,7 +1,180 @@
|
|
|
1
1
|
|
|
2
|
+
// chart 1
|
|
3
|
+
// bpm 2
|
|
4
|
+
// nnnlist 3
|
|
5
|
+
// judgeline 4
|
|
6
|
+
// nnList 5
|
|
7
|
+
// EventNodeSequence 6
|
|
8
|
+
// notenode 7
|
|
9
|
+
// eventnode 8
|
|
10
|
+
// evaluator 9
|
|
11
|
+
// easing a
|
|
12
|
+
// note b
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// 就挺神奇的!明明typeof后面跟值,这里却可以写成类型导入
|
|
16
|
+
import { type TimeT, type EventValueType } from "./chartTypes";
|
|
17
|
+
import { toTimeString } from "./util";
|
|
18
|
+
|
|
19
|
+
// occupied 1
|
|
20
|
+
// invalid data 2
|
|
21
|
+
// invalid usage 3
|
|
22
|
+
|
|
23
|
+
const
|
|
24
|
+
CHART = 0x100,
|
|
25
|
+
BPM = 0x200,
|
|
26
|
+
NNN_LIST = 0x300,
|
|
27
|
+
JUDGE_LINE = 0x400,
|
|
28
|
+
NN_LIST = 0x500,
|
|
29
|
+
ENS = 0x600,
|
|
30
|
+
NOTE_NODE = 0x700,
|
|
31
|
+
EVENT_NODE = 0x800,
|
|
32
|
+
EVALUATOR = 0x900,
|
|
33
|
+
EASING = 0xA00,
|
|
34
|
+
NOTE = 0xB00,
|
|
35
|
+
TC = 0xC00,
|
|
36
|
+
INTERNAL = 0xF00,
|
|
37
|
+
|
|
38
|
+
OCCPIED = 0x10,
|
|
39
|
+
INVALID_DATA = 0x20, // 一般是指读取谱面时的无效事件类型、缓动号等等
|
|
40
|
+
INVALID_USAGE = 0x30, // 不知道归入哪里就放这吧(((
|
|
41
|
+
INVALID_TYPE = 0x40 // 这里的type指ECMAScript数据类型,事件类型错误可以归入此类
|
|
42
|
+
;
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export enum ERROR_IDS {
|
|
47
|
+
UI_OCCUPIED = CHART | OCCPIED,
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
SEQUENCE_NAME_OCCUPIED = ENS | OCCPIED | 0,
|
|
51
|
+
SEQUENCE_NODE_TIME_OCCUPIED = ENS | OCCPIED | 1,
|
|
52
|
+
INVALID_EVENT_NODE_SEQUENCE_TYPE = ENS | INVALID_DATA | 0,
|
|
53
|
+
EVENT_NODE_TIME_NOT_INCREMENTAL = ENS | INVALID_DATA | 1,
|
|
54
|
+
PARENT_SEQUENCE_NOT_FOUND = ENS | INVALID_USAGE | 1,
|
|
55
|
+
NEEDS_AT_LEAST_ONE_ENS = ENS | INVALID_USAGE | 2,
|
|
56
|
+
SEQUENCE_TYPE_NOT_CONSISTENT = ENS | INVALID_USAGE | 3,
|
|
57
|
+
|
|
58
|
+
EXPECTED_TYPED_ENS = ENS | INVALID_TYPE | 0,
|
|
59
|
+
|
|
60
|
+
CANNOT_SUBSTITUTE_EXPRESSION_EVALUATOR = EVENT_NODE | INVALID_USAGE | 0,
|
|
61
|
+
CANNOT_INSERT_BEFORE_HEAD = EVENT_NODE | INVALID_USAGE | 1,
|
|
62
|
+
CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE = EVENT_NODE | INVALID_USAGE | 2,
|
|
63
|
+
CANNOT_INTERPOLATE_TAILING_START_NODE = EVENT_NODE | INVALID_USAGE | 3,
|
|
64
|
+
|
|
65
|
+
INVALID_EASING_ID = EASING | INVALID_DATA | 0,
|
|
66
|
+
CANNOT_IMPLEMENT_TEMEAS_WITH_NON_EASING_ENS = EASING | INVALID_DATA | 1,
|
|
67
|
+
CANNOT_IMPLEMENT_TEMEAS_WITH_NON_NUMERIC_ENS = EASING | INVALID_DATA | 2,
|
|
68
|
+
MUST_INTERPOLATE_TEMPLATE_EASING = EASING | INVALID_USAGE | 0,
|
|
69
|
+
NODES_NOT_CONTINUOUS = EASING | INVALID_USAGE | 1,
|
|
70
|
+
NODES_NOT_BELONG_TO_SAME_SEQUENCE = EASING | INVALID_USAGE | 2,
|
|
71
|
+
NODES_HAS_ZERO_DELTA = EASING | INVALID_USAGE | 3,
|
|
72
|
+
|
|
73
|
+
CANNOT_DIVIDE_EXPRESSION_EVALUATOR = EVALUATOR | INVALID_USAGE | 0,
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
INVALID_NOTE_PROP_TYPE = NOTE | INVALID_TYPE | 0,
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
INVALID_TIME_TUPLE = TC | INVALID_DATA | 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const ERRORS = {
|
|
84
|
+
UI_OCCUPIED: (name: string) =>
|
|
85
|
+
`UI '${name}' is occupied`,
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
SEQUENCE_NAME_OCCUPIED: (name: string) =>
|
|
89
|
+
`Sequence name '${name}' is occupied`,
|
|
90
|
+
SEQUENCE_NODE_TIME_OCCUPIED: (time: TimeT, id: string) =>
|
|
91
|
+
`Time ${toTimeString(time)} already has a node (in sequence ${id})`,
|
|
92
|
+
INVALID_EVENT_NODE_SEQUENCE_TYPE: (type: any) =>
|
|
93
|
+
`Invalid event node sequence type: '${type}'`,
|
|
94
|
+
EXPECTED_TYPED_ENS: (typeStr: keyof typeof EventValueType, id: string, value: unknown) =>
|
|
95
|
+
`Expected EventNodeSequence for ${typeStr} but seen value ${value} (Processing ${id})`,
|
|
96
|
+
EVENT_NODE_TIME_NOT_INCREMENTAL: (pos: string) =>
|
|
97
|
+
`EventNode time is not incremental (at ${pos})`,
|
|
98
|
+
PARENT_SEQUENCE_NOT_FOUND: (nodeTime: TimeT) =>
|
|
99
|
+
`Parent EventNodeSequence not found for an EventNode at time ${toTimeString(nodeTime)} (Did you forget to add it to a sequence?)`,
|
|
100
|
+
NEEDS_AT_LEAST_ONE_ENS: () =>
|
|
101
|
+
`Needs at least one EventNodeSequence`,
|
|
102
|
+
SEQUENCE_TYPE_NOT_CONSISTENT: (typeStr: string, but: string) =>
|
|
103
|
+
`EventNodeSequence type is not consistent (expected ${typeStr} but seen value ${but})`,
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
CANNOT_SUBSTITUTE_EXPRESSION_EVALUATOR: () =>
|
|
107
|
+
`Cannot substitute ExpressionEvaluator`,
|
|
108
|
+
CANNOT_INSERT_BEFORE_HEAD: () =>
|
|
109
|
+
"Cannot insert before the first EventStartNode",
|
|
110
|
+
CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE: () =>
|
|
111
|
+
"Cannot get full integral of final start node",
|
|
112
|
+
CANNOT_INTERPOLATE_TAILING_START_NODE: () =>
|
|
113
|
+
"Cannot interpolate tailing start node",
|
|
114
|
+
|
|
115
|
+
INVALID_EASING_ID: (id: string) =>
|
|
116
|
+
`Invalid easing id: '${id}'`,
|
|
117
|
+
CANNOT_IMPLEMENT_TEMEAS_WITH_NON_EASING_ENS: (temEasName: string) =>
|
|
118
|
+
`Cannot implement TemplateEasing with a non-easing-typed EventNodeSequence (Processing template '${temEasName}')`,
|
|
119
|
+
CANNOT_IMPLEMENT_TEMEAS_WITH_NON_NUMERIC_ENS: (temEasName: string) =>
|
|
120
|
+
`Cannot implement TemplateEasing with a non-numeric EventNodeSequence (Processing template '${temEasName}')`,
|
|
121
|
+
MUST_INTERPOLATE_TEMPLATE_EASING: () =>
|
|
122
|
+
"Must interpolate template easing",
|
|
123
|
+
|
|
124
|
+
NODES_NOT_CONTINUOUS: () =>
|
|
125
|
+
"The EventNodes to encapsulate are not continuous",
|
|
126
|
+
NODES_NOT_BELONG_TO_SAME_SEQUENCE: () =>
|
|
127
|
+
"The EventNodes to encapsulate does not belong to same sequence",
|
|
128
|
+
NODES_HAS_ZERO_DELTA: () =>
|
|
129
|
+
"The EventNodes to encapsulate has zero delta",
|
|
130
|
+
|
|
131
|
+
INVALID_NOTE_PROP_TYPE: (prop: string, value: any, type: any) =>
|
|
132
|
+
`Invalid type for ${prop}. Got *${value}*, expected ${type}`,
|
|
133
|
+
|
|
134
|
+
INVALID_TIME_TUPLE: (tuple: any) =>
|
|
135
|
+
`Invalid time tuple: '${typeof tuple === "undefined" ? 'undefined' : tuple.valueOf()}'`,
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
CANNOT_DIVIDE_EXPRESSION_EVALUATOR: (id: string) =>
|
|
141
|
+
`Cannot divide ExpressionEvaluator (Compiling ${id})`,
|
|
142
|
+
|
|
143
|
+
} satisfies Record<keyof typeof ERROR_IDS, (...args: any[]) => string>
|
|
144
|
+
|
|
145
|
+
export class KPAError<ET extends ERROR_IDS> extends Error {
|
|
146
|
+
constructor(message: string, public id: ET) {
|
|
147
|
+
super(message);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 对于解析谱面等场景,有时可能需要找出全部的错误,不宜直接抛出错误中断代码执行
|
|
151
|
+
*
|
|
152
|
+
* 此时可以调用该方法,该方法会输出错误并把它保存到KPAError的一个`buffer`静态属性下。
|
|
153
|
+
*/
|
|
154
|
+
warn() {
|
|
155
|
+
console.warn(this.stack);
|
|
156
|
+
KPAError.buffer.push(this);
|
|
157
|
+
}
|
|
158
|
+
static buffer: KPAError<ERROR_IDS>[] = [];
|
|
159
|
+
static flush() {
|
|
160
|
+
KPAError.buffer = [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const err = new Proxy(ERRORS, {
|
|
165
|
+
get(target, name) {
|
|
166
|
+
return (...args: any[]) => new KPAError(target[name](...args) + `(KP${ERROR_IDS[name].toString(16)})`, ERROR_IDS[name]);
|
|
167
|
+
}
|
|
168
|
+
}) as unknown as { [key in keyof typeof ERRORS]: (...args: Parameters<typeof ERRORS[key]>) => KPAError<typeof ERROR_IDS[key]>};
|
|
169
|
+
|
|
2
170
|
export default {
|
|
3
171
|
DEFAULT_TEMPLATE_LENGTH: 16,
|
|
4
172
|
BEZIER_INTERPOLATION_DENSITY: 256,
|
|
5
173
|
NNLIST_Y_OFFSET_HALF_SPAN: 100,
|
|
174
|
+
JUMPARRAY_MIN_LENGTH: 64,
|
|
175
|
+
JUMPARRAY_MAX_LENGTH: 4096,
|
|
176
|
+
JUMPARRAY_MINOR_SCALE_COUNT: 16,
|
|
177
|
+
ERROR_IDS,
|
|
178
|
+
err,
|
|
6
179
|
freeze() { Object.freeze(this); }
|
|
7
180
|
}
|
package/evaluator.ts
CHANGED
|
@@ -6,11 +6,10 @@ import {
|
|
|
6
6
|
} from "./easing";
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import TC from "./time";
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
import type
|
|
13
|
-
import { EvaluatorType, EventValueType, InterpreteAs, type ColorEasedEvaluatorKPA2, type EasedEvaluatorDataOfType, type EvaluatorDataKPA2, type EventValueESType, type EventValueTypeOfType, type ExpressionEvaluatorDataKPA2, type NumericEasedEvaluatorKPA2, type RGB, type TextEasedEvaluatorKPA2 } from "./chartTypes";
|
|
11
|
+
import type { EventEndNode, EventStartNode, NonLastStartNode } from "./event";
|
|
12
|
+
import { EvaluatorType, InterpreteAs, type ColorEasedEvaluatorKPA2, type EvaluatorDataKPA2, type EventValueESType, type ExpressionEvaluatorDataKPA2, type NumericEasedEvaluatorKPA2, type RGB, type TextEasedEvaluatorKPA2 } from "./chartTypes";
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
/// #declaration:global
|
|
@@ -30,13 +29,13 @@ export abstract class Evaluator<T> {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
export abstract class EasedEvaluator<T> extends Evaluator<T> {
|
|
32
|
+
export abstract class EasedEvaluator<T extends EventValueESType> extends Evaluator<T> {
|
|
34
33
|
readonly easing: Easing;
|
|
35
34
|
constructor(easing: Easing) {
|
|
36
35
|
super();
|
|
37
36
|
this.easing = easing;
|
|
38
37
|
}
|
|
39
|
-
override eval(startNode:
|
|
38
|
+
override eval(startNode: NonLastStartNode<T>, beats: number): T {
|
|
40
39
|
const next = startNode.next;
|
|
41
40
|
const timeDelta = TC.getDelta(next.time, startNode.time)
|
|
42
41
|
const current = beats - TC.toBeats(startNode.time)
|
|
@@ -48,7 +47,13 @@ export abstract class EasedEvaluator<T> extends Evaluator<T> {
|
|
|
48
47
|
// 其他类型,包括普通缓动和非钩定模板缓动
|
|
49
48
|
return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
|
|
50
49
|
}
|
|
51
|
-
abstract convert(start: T, end: T,
|
|
50
|
+
abstract convert(start: T, end: T, progress: number): T;
|
|
51
|
+
/**
|
|
52
|
+
* 派生一个类型相同但使用不同缓动的求值器
|
|
53
|
+
* @param easing
|
|
54
|
+
* @returns
|
|
55
|
+
*/
|
|
56
|
+
abstract deriveWithEasing(easing: Easing): EasedEvaluator<T>;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
export type EasedEvaluatorOfType<T extends EventValueESType> = T extends number ? NumericEasedEvaluator : T extends RGB ? ColorEasedEvaluator : TextEasedEvaluator;
|
|
@@ -70,6 +75,13 @@ export class NumericEasedEvaluator extends EasedEvaluator<number> {
|
|
|
70
75
|
}
|
|
71
76
|
static default = new NumericEasedEvaluator(linearEasing);
|
|
72
77
|
static evaluatorsOfNormalEasing: NumericEasedEvaluator[] = rpeEasingArray.map(easing => new NumericEasedEvaluator(easing));
|
|
78
|
+
override deriveWithEasing(easing: Easing): NumericEasedEvaluator {
|
|
79
|
+
if (easing instanceof NormalEasing) {
|
|
80
|
+
return NumericEasedEvaluator.evaluatorsOfNormalEasing[easing.rpeId];
|
|
81
|
+
} else {
|
|
82
|
+
return new NumericEasedEvaluator(easing);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
export class ColorEasedEvaluator extends EasedEvaluator<RGB> {
|
|
@@ -90,6 +102,13 @@ export class ColorEasedEvaluator extends EasedEvaluator<RGB> {
|
|
|
90
102
|
}
|
|
91
103
|
static default = new ColorEasedEvaluator(linearEasing);
|
|
92
104
|
static evaluatorsOfNormalEasing: ColorEasedEvaluator[] = rpeEasingArray.map(easing => new ColorEasedEvaluator(easing));
|
|
105
|
+
override deriveWithEasing(easing: Easing): ColorEasedEvaluator {
|
|
106
|
+
if (easing instanceof NormalEasing) {
|
|
107
|
+
return ColorEasedEvaluator.evaluatorsOfNormalEasing[easing.rpeId];
|
|
108
|
+
} else {
|
|
109
|
+
return new ColorEasedEvaluator(easing);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
|
|
@@ -102,8 +121,7 @@ export class ColorEasedEvaluator extends EasedEvaluator<RGB> {
|
|
|
102
121
|
*/
|
|
103
122
|
export class TextEasedEvaluator extends EasedEvaluator<string> {
|
|
104
123
|
constructor(easing: Easing,
|
|
105
|
-
public readonly interpretedAs: InterpreteAs = InterpreteAs.str
|
|
106
|
-
public readonly font: string = "cmdysj.ttf"
|
|
124
|
+
public readonly interpretedAs: InterpreteAs = InterpreteAs.str
|
|
107
125
|
)
|
|
108
126
|
{
|
|
109
127
|
super(easing);
|
|
@@ -112,8 +130,7 @@ export class TextEasedEvaluator extends EasedEvaluator<string> {
|
|
|
112
130
|
return {
|
|
113
131
|
type: EvaluatorType.eased,
|
|
114
132
|
easing: this.easing.dump(),
|
|
115
|
-
interpretedAs: this.interpretedAs
|
|
116
|
-
font: this.font
|
|
133
|
+
interpretedAs: this.interpretedAs
|
|
117
134
|
}
|
|
118
135
|
}
|
|
119
136
|
override convert(value: string, nextValue: string, progress: number): string {
|
|
@@ -148,6 +165,13 @@ export class TextEasedEvaluator extends EasedEvaluator<string> {
|
|
|
148
165
|
new TextEasedEvaluator(easing, InterpreteAs.int),
|
|
149
166
|
new TextEasedEvaluator(easing, InterpreteAs.float),
|
|
150
167
|
]);
|
|
168
|
+
override deriveWithEasing(easing: Easing): EasedEvaluator<string> {
|
|
169
|
+
if (easing instanceof NormalEasing) {
|
|
170
|
+
return TextEasedEvaluator.evaluatorsOfNoEzAndItpAs[easing.rpeId][this.interpretedAs];
|
|
171
|
+
} else {
|
|
172
|
+
return new TextEasedEvaluator(easing, this.interpretedAs);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
151
175
|
}
|
|
152
176
|
|
|
153
177
|
export class ExpressionEvaluator<T> extends Evaluator<T> {
|