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/easing.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { type CustomEasingData, type EasingDataKPA2, EasingType, EventType, type SegmentedEasingData, type NormalEasingData, type BezierEasingData, type TemplateEasingData } from "./chartTypes";
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
- export const easingFnMap = {
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, curveDrawer?: CurveDrawer);
197
- constructor(fn: (t: number) => number, curveDrawer?: CurveDrawer) {
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: readonly number[];
238
- readonly ys: readonly number[];
239
- readonly jumper: readonly number[];
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: number[] = new Array(BEZIER_INTERPOLATION_DENSITY - 1);
246
- const ys: number[] = new Array(BEZIER_INTERPOLATION_DENSITY - 1);
221
+ const xs = new Float64Array(BEZIER_INTERPOLATION_DENSITY - 1);
222
+ const ys = new Float64Array(BEZIER_INTERPOLATION_DENSITY - 1);
247
223
  /** 一把尺子,刻度均匀,从`插值步长*下标`映射到xs里面的下标 */
248
- const jumper: number[] = new Array(BEZIER_INTERPOLATION_DENSITY);
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 = Object.freeze(xs);
265
- this.ys = Object.freeze(ys);
266
- this.jumper = Object.freeze(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
- let seq = this.eventNodeSequence;
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
- [name: string]: TemplateEasing
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[name]) {
377
- return this.easings[name];
358
+ if (this.easings.has(name)) {
359
+ return this.easings.get(name);
378
360
  } else {
379
- const easing = new TemplateEasing(name, EventNodeSequence.newSeq(EventType.easing, DEFAULT_TEMPLATE_LENGTH));
361
+ const easing = new TemplateEasing(name, this.getNewSequence(EventType.easing, DEFAULT_TEMPLATE_LENGTH));
380
362
  easing.eventNodeSequence.id = "*" + name;
381
- return this.easings[name] = easing;
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[name] = new TemplateEasing(name, null);
383
+ this.easings.set(name, new TemplateEasing(name, null));
390
384
  }
391
385
  implement(name: string, sequence: EventNodeSequence) {
392
- this.easings[name].eventNodeSequence = sequence;
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 (let key in this.easings) {
402
- if (!this.easings[key].eventNodeSequence) {
403
- console.warn(`未实现的缓动:${key}`);
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[key];
402
+ return this.easings.get(key);
409
403
  }
410
404
 
411
- dump(eventNodeSequences: Set<EventNodeSequence>): CustomEasingData[] {
412
- const customEasingDataList: CustomEasingData[] = [];
413
- for (let key in this.easings) {
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, linearLine);
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 (let funcType in easingMap) {
450
- for (let easeType in easingMap[funcType]) {
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 { TimeCalculator as TC } from "./time"
9
+ import TC from "./time";
10
10
 
11
- import type { Chart } from "./chart";
12
- import type { EventEndNode, EventStartNode } from "./event";
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: EventStartNode<T> & { next: EventEndNode }, beats: number): T {
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, t: number): 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> {