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/time.ts CHANGED
@@ -1,245 +1,55 @@
1
- import { EventType, type BPMSegmentData, type TimeT } from "./chartTypes";
2
- import { EventEndNode, EventNodeLike, EventNodeSequence, EventStartNode, type AnyEN, type ENOrHead, type ENOrTail } from "./event";
3
- import { JumpArray } from "./jumparray";
4
- import { NodeType } from "./util";
5
-
6
-
7
- /// #declaration:global
8
-
1
+ import { TimeT } from "./chartTypes";
2
+ import { err } from "./env";
9
3
  /**
10
- *
4
+ * @static @final
11
5
  */
12
- class BPMStartNode extends EventStartNode {
13
- spb: number;
14
- cachedStartIntegral?: number;
15
- override cachedIntegral?: number;
16
- override next: BPMEndNode | BPMNodeLike<NodeType.TAIL>;
17
- override previous: BPMEndNode | BPMNodeLike<NodeType.HEAD>;
18
- constructor(startTime: TimeT, bpm: number) {
19
- super(startTime, bpm);
20
- this.spb = 60 / bpm;
21
- }
22
- override getIntegral(beats: number): number {
23
- return (beats - TimeCalculator.toBeats(this.time)) * 60 / this.value;
24
- }
25
- /**
26
- * may only used with a startnode whose next is not tail
27
- * @returns
28
- */
29
- override getFullIntegral(): number {
30
- return (TimeCalculator.toBeats(this.next.time) - TimeCalculator.toBeats(this.time)) * 60 / this.value;
31
- }
32
- /*
33
- static connect(node1: BPMStartNode, node2: BPMEndNode | Tailer<EventStartNode>): void;
34
- static connect(node1: BPMEndNode | Header<BPMStartNode>, node2: BPMStartNode): void;
35
- static connect(node1: EventNode | Header<EventNode>, node2: EventNode | Tailer<EventNode>): void {
36
- super.connect(node1, node2);
37
- }
38
- */
39
- }
40
- class BPMEndNode extends EventEndNode {
41
- spb: number;
42
- override previous: BPMStartNode;
43
- override next: BPMStartNode;
44
- constructor(endTime: TimeT) {
45
- super(endTime, null);
46
- }
47
- // @ts-expect-error
48
- get value(): number {
49
- return this.previous.value
50
- }
51
- set value(val) {}
52
- }
53
-
54
- interface BPMNodeLike<T extends NodeType> extends EventNodeLike<T> {
55
- next: [BPMStartNode, null, BNOrTail][T] | null;
56
- previous: [null, BPMStartNode, BNOrHead][T] | null;
57
- }
58
- type BPMNode = BPMStartNode | BPMEndNode;
59
- type AnyBN = (BPMNode | BPMNodeLike<NodeType.TAIL> | BPMNodeLike<NodeType.HEAD>);
60
- type BNOrTail = BPMNode | BPMNodeLike<NodeType.TAIL>;
61
- type BNOrHead = BPMNode | BPMNodeLike<NodeType.HEAD>;
62
-
63
- /**
64
- * 拥有与事件类似的逻辑
65
- * 每对节点之间代表一个BPM相同的片段
66
- * 片段之间BPM可以发生改变
67
- */
68
-
69
- export class BPMSequence extends EventNodeSequence {
70
- override head: BPMNodeLike<NodeType.HEAD>;
71
- override tail: BPMNodeLike<NodeType.TAIL>;
72
- /** 从拍数访问节点 */
73
- override jump: JumpArray<AnyEN>;
74
- /** 以秒计时的跳数组,处理从秒访问节点 */
75
- secondJump: JumpArray<AnyBN>;
76
- constructor(bpmList: BPMSegmentData[], public duration: number) {
77
- super(EventType.bpm, null);
78
- let curPos: BPMNodeLike<NodeType.HEAD> | BPMEndNode = this.head;
79
- let next = bpmList[0];
80
- this.listLength = bpmList.length;
81
- for (let i = 1; i < bpmList.length; i++) {
82
- const each = next;
83
- next = bpmList[i];
84
- const startNode = new BPMStartNode(each.startTime, each.bpm);
85
- const endNode = new BPMEndNode(next.startTime);
86
- BPMStartNode.connect(startNode, endNode);
87
- BPMStartNode.connect(curPos, startNode);
88
- curPos = endNode;
89
- }
90
- const last = new BPMStartNode(next.startTime, next.bpm)
91
- BPMStartNode.connect(curPos, last);
92
- BPMStartNode.connect(last, this.tail);
93
- this.initJump();
94
- }
95
- override initJump(): void {
96
- console.log(this)
97
- this.effectiveBeats = TimeCalculator.toBeats(this.tail.previous.time)
98
- if (this.effectiveBeats !== 0) {
99
- super.initJump(); // 为0可以跳过jumpArray,用不到
100
- // 只有一个BPM片段就会这样
101
- }
102
- this.updateSecondJump();
103
- }
104
- updateSecondJump(): void {
105
- let integral = 0;
106
- // 计算积分并缓存到BPMNode
107
- let node: BPMStartNode = this.head.next;
108
- while (true) {
109
- node.cachedStartIntegral = integral;
110
- if (node.next.type === NodeType.TAIL) {
111
- break;
112
- }
113
- const endNode = <BPMEndNode>(<BPMStartNode>node).next;
114
- integral += node.getFullIntegral();
115
- node.cachedIntegral = integral;
116
-
117
- node = endNode.next;
118
- }
119
- node.cachedStartIntegral = integral;
120
- if (this.effectiveBeats === 0) {
121
- return;
122
- }
123
- const originalListLength = this.listLength;
124
- this.secondJump = new JumpArray<AnyBN>(
125
- this.head,
126
- this.tail,
127
- originalListLength,
128
- this.duration,
129
- (node: BPMStartNode) => {
130
- if (node.type === NodeType.TAIL) {
131
- return [null, null];
132
- }
133
- if (node.type === NodeType.HEAD) {
134
- return [0, node.next];
135
- }
136
- const endNode = <BPMEndNode>(<BPMStartNode>node).next;
137
- const time = node.cachedIntegral;
138
- const nextNode = endNode.next;
139
- if (nextNode.next.type === NodeType.TAIL) {
140
- return [time, nextNode.next]; // Tailer代替最后一个StartNode去占位
141
- } else {
142
- return [time, nextNode];
143
- }
144
- },
145
- (node: BPMStartNode, seconds: number) => {
146
- return node.cachedIntegral > seconds ? false : (<BPMEndNode>node.next).next;
147
- }
148
- );
149
- }
150
- override updateJump(from: ENOrHead, to: ENOrTail): void {
151
- super.updateJump(from, to);
152
- this.updateSecondJump();
153
- }
154
-
155
- getNodeBySeconds(seconds: number): BPMStartNode {
156
- if (this.effectiveBeats === 0) {
157
- return this.tail.previous
158
- }
159
- const node = this.secondJump.getNodeAt(seconds);
160
- if (node.type === NodeType.TAIL) {
161
- return node.previous;
162
- }
163
- return node as BPMStartNode;
164
- }
165
- dumpBPM(): BPMSegmentData[] {
166
- let cur = this.head.next;
167
- const ret: BPMSegmentData[] = [];
168
- while (true) {
169
- ret.push({
170
- bpm: cur.value,
171
- startTime: cur.time
172
- })
173
- const end = cur.next;
174
- if (end.type === NodeType.TAIL) {
175
- break;
176
- }
177
- cur = end.next;
178
- }
179
- return ret;
180
- }
181
- }
182
-
183
- /**
184
- * @alias TC
185
- */
186
- export class TimeCalculator {
187
- bpmList: BPMSegmentData[];
188
- bpmSequence: BPMSequence;
189
- duration: number;
190
-
191
- constructor() {
192
- }
193
-
194
- update() {
195
- let bpmList = this.bpmList;
196
- this.bpmSequence = new BPMSequence(bpmList, this.duration);
197
- }
198
- toSeconds(beats: number) {
199
- const node: BPMStartNode = this.bpmSequence.getNodeAt(beats);
200
- return node.cachedStartIntegral + node.getIntegral(beats)
201
- }
202
- segmentToSeconds(beats1: number, beats2: number): number {
203
- let ret = this.toSeconds(beats2) - this.toSeconds(beats1)
204
- if (ret < 0) {
205
- console.warn("segmentToSeconds的第二个参数需大于第一个!", "得到的参数:", arguments)
206
- }
207
- return ret
208
- }
209
- secondsToBeats(seconds: number) {
210
- const node = this.bpmSequence.getNodeBySeconds(seconds);
211
- // console.log("node:", node)
212
- const beats = (seconds - node.cachedStartIntegral) / node.spb;
213
- return TimeCalculator.toBeats(node.time) + beats
214
- }
6
+ export default class TC {
7
+ private constructor() {}
215
8
  static toBeats(beaT: TimeT): number {
216
- if (!beaT) debugger
217
9
  return beaT[0] + beaT[1] / beaT[2]
218
10
  }
219
11
  static getDelta(beaT1: TimeT, beaT2: TimeT): number {
220
12
  return this.toBeats(beaT1) - this.toBeats(beaT2)
221
13
  }
14
+ /**
15
+ * @returns beaT1 == beaT2
16
+ */
222
17
  static eq(beaT1: TimeT, beaT2: TimeT): boolean {
223
18
  return beaT1[0] === beaT2 [0] && beaT1[1] * beaT2[2] === beaT1[2] * beaT2[1] // 这里曾经把两个都写成beaT1,特此留念(
224
19
  }
20
+ /** @returns beaT1 > beaT2 */
225
21
  static gt(beaT1:TimeT, beaT2: TimeT): boolean {
226
22
  return beaT1[0] > beaT2[0] || beaT1[0] === beaT2[0] && beaT1[1] * beaT2[2] > beaT1[2] * beaT2[1]
227
23
  }
24
+ /** @returns beaT1 < beaT2 */
228
25
  static lt(beaT1:TimeT, beaT2: TimeT): boolean {
229
26
  return beaT1[0] < beaT2[0] || beaT1[0] === beaT2[0] && beaT1[1] * beaT2[2] < beaT1[2] * beaT2[1]
230
27
  }
28
+ /** @returns beaT1 != beaT2 */
231
29
  static ne(beaT1:TimeT, beaT2: TimeT): boolean {
232
30
  return beaT1[0] !== beaT2[0] || beaT1[1] * beaT2[2] !== beaT1[2] * beaT2[1]
233
31
  }
32
+ /**
33
+ * @returns beaT1 + beaT2
34
+ */
234
35
  static add(beaT1: TimeT, beaT2: TimeT): TimeT {
235
36
  return [beaT1[0] + beaT2[0], beaT1[1] * beaT2[2] + beaT1[2] * beaT2[1], beaT1[2] * beaT2[2]]
236
37
  }
38
+ /**
39
+ * @returns beaT1 - beaT2
40
+ */
237
41
  static sub(beaT1: TimeT, beaT2: TimeT): TimeT {
238
42
  return [beaT1[0] - beaT2[0], beaT1[1] * beaT2[2] - beaT1[2] * beaT2[1], beaT1[2] * beaT2[2]]
239
43
  }
44
+ /**
45
+ * @returns Ratio(a 2-number tuple) = beaT1 / beaT2
46
+ */
240
47
  static div(beaT1: TimeT, beaT2: TimeT): [number, number] {
241
48
  return [(beaT1[0] * beaT1[2] + beaT1[1]) * beaT2[2], (beaT2[0] * beaT2[2] + beaT2[1]) * beaT1[2]]
242
49
  }
50
+ /**
51
+ * @returns beaT1 * [numerator, denominator]
52
+ */
243
53
  static mul(beaT: TimeT, ratio: [number, number]): TimeT {
244
54
  // 将带分数beaT: TimeT乘一个分数[number, number]得到一个新的带分数returnval: TimeT,不要求这个带分数分子不超过分母,但所有的数都是整数
245
55
  // (输入的两个元组都是整数元组)
@@ -251,6 +61,7 @@ export class TimeCalculator {
251
61
  } else {
252
62
  return [Math.floor(b0nume / denominator), beaT[1] * numerator + remainder * beaT[2], beaT[2] * denominator]
253
63
  }
64
+
254
65
  }
255
66
  /**
256
67
  * 原地规范化时间元组,但仍然返回这个元组,方便使用
@@ -259,7 +70,7 @@ export class TimeCalculator {
259
70
  */
260
71
  static validateIp(beaT: TimeT): TimeT {
261
72
  if (beaT === undefined || beaT[2] === 0) {
262
- throw new Error("Invalid time" + beaT.valueOf());
73
+ throw err.INVALID_TIME_TUPLE(beaT);
263
74
  }
264
75
  if (beaT[1] >= beaT[2]) {
265
76
  const quotient = Math.floor(beaT[1] / beaT[2]);
@@ -283,8 +94,17 @@ export class TimeCalculator {
283
94
  }
284
95
  return beaT;
285
96
  }
97
+ /**
98
+ * 相加并约简
99
+ */
286
100
  static vadd(beaT1: TimeT, beaT2: TimeT) { return this.validateIp(this.add(beaT1, beaT2)); }
101
+ /**
102
+ * 相减并约简
103
+ */
287
104
  static vsub(beaT1: TimeT, beaT2: TimeT) { return this.validateIp(this.sub(beaT1, beaT2)); }
105
+ /**
106
+ * 相乘并约简
107
+ */
288
108
  static vmul(beaT: TimeT, ratio: [number, number]): TimeT { return this.validateIp(this.mul(beaT, ratio)); }
289
109
  static gcd(a: number, b: number): number {
290
110
  if (a === 0 || b === 0) {
@@ -297,12 +117,4 @@ export class TimeCalculator {
297
117
  }
298
118
  return a;
299
119
  }
300
- dump(): BPMSegmentData[] {
301
- return this.bpmSequence.dumpBPM();
302
- }
303
- }
304
-
305
- export const TC = TimeCalculator;
306
-
307
-
308
- /// #enddeclaration
120
+ }
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  // Environment setup & latest features
4
- "lib": ["ES2017"],
4
+ "lib": ["ES2021"],
5
5
  "target": "ES2016",
6
6
  "module": "es2020",
7
7
  "moduleDetection": "force",
@@ -16,7 +16,6 @@
16
16
  "skipLibCheck": true,
17
17
  "noFallthroughCasesInSwitch": true,
18
18
  "noUncheckedIndexedAccess": true,
19
- "noImplicitOverride": true,
20
19
 
21
20
  // Some stricter flags (disabled by default)
22
21
  "noUnusedLocals": false,
package/util.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { RGB } from "./chartTypes";
1
+ import type { RGB, TimeT } from "./chartTypes";
2
2
 
3
3
  /// #declaration:global
4
4
 
@@ -9,6 +9,22 @@ export enum NodeType {
9
9
  }
10
10
 
11
11
  export type TupleCoord = [x: number, y: number]
12
+ /**
13
+ * 检查值的类型
14
+ * @param value
15
+ * @param type 为字符串时,用typeof检测,为构造函数时,用instanceof检测,为数组时,识别为元组类型。
16
+ */
17
+ export const checkType = (value: unknown, type: string | (string | typeof Function)[] | typeof Function) => {
18
+ if (Array.isArray(type)) {
19
+ return Array.isArray(value)
20
+ && value.length === type.length
21
+ && type.every((t, i) => checkType(value[i], t))
22
+ } else if (typeof type === "string") {
23
+ return typeof value === type
24
+ } else {
25
+ return value instanceof type
26
+ }
27
+ }
12
28
 
13
29
  export const rgb2hex = (rgb: RGB) => {
14
30
  return rgb[0] << 16 | rgb[1] << 8 | rgb[2];
@@ -23,4 +39,8 @@ export const numberToRatio = (num: number): [number, number] => {
23
39
  return [Math.round(num * 10000), 10000]
24
40
  }
25
41
 
42
+
43
+ export const toTimeString = (beaT: TimeT): string =>
44
+ `${beaT[0]}:${beaT[1]}/${beaT[2]}`;
45
+
26
46
  /// #enddeclaration
package/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /// #declaration:global
4
4
 
5
- export const VERSION = 200;
5
+ export const VERSION = 201;
6
6
 
7
7
 
8
8
  /// #enddeclaration