kipphi 2.0.0

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/jumparray.ts ADDED
@@ -0,0 +1,234 @@
1
+ import { NodeType } from "./util";
2
+
3
+ // type EndBeats = number;
4
+ const MIN_LENGTH = 128
5
+ const MAX_LENGTH = 1024
6
+ const MINOR_PARTS = 16;
7
+ /// #declaration:global
8
+
9
+ type EndNextFn<T extends TwoDirectionNodeLike> = (node: T) => [endBeats: number, next: T];
10
+
11
+
12
+
13
+ interface TwoDirectionNodeLike {
14
+ next: this | null;
15
+ previous: this | null;
16
+ type: NodeType;
17
+ }
18
+
19
+ export class JumpArray<T extends TwoDirectionNodeLike> {
20
+ header: T;
21
+ tailer: T;
22
+ array: (T[] | T)[];
23
+ averageBeats: number;
24
+ effectiveBeats: number;
25
+ goPrev: (node: T) => T;
26
+
27
+ /**
28
+ *
29
+ * @param head 链表头
30
+ * @param tail 链表尾
31
+ * @param originalListLength
32
+ * @param effectiveBeats 有效拍数(等同于音乐拍数)
33
+ * @param endNextFn 接收一个节点,返回该节点分管区段拍数,并给出下个节点。若抵达尾部,返回[null, null](停止遍历的条件是抵达尾部而不是得到null)
34
+ * @param nextFn 接收一个节点,返回下个节点。如果应当停止,返回false。
35
+ */
36
+ constructor(
37
+ head: T,
38
+ tail: T,
39
+ originalListLength: number,
40
+ effectiveBeats: number,
41
+ public endNextFn: EndNextFn<T>,
42
+ public nextFn: (node: T, beats: number) => T | false,
43
+ public resolveLastNode: (node: T) => T = (node) => node
44
+ // goPrev: (node: T) => T
45
+ ) {
46
+ this.header = head;
47
+ this.tailer = tail;
48
+ // const originalListLength = this.listLength
49
+ const listLength: number = Math.max(MIN_LENGTH, Math.min(originalListLength * 4, MAX_LENGTH));
50
+ const averageBeats: number = Math.pow(2, Math.ceil(Math.log2(effectiveBeats / listLength)));
51
+ const exactLength: number = Math.ceil(effectiveBeats / averageBeats);
52
+ // console.log(exactLength, listLength, averageBeats, exactLength)
53
+ // console.log(originalListLength, effectiveBeats, averageBeats, minorBeats, exactLength)
54
+ const jumpArray: (T | T[])[] = new Array(exactLength);
55
+ this.array = jumpArray;
56
+ this.averageBeats = averageBeats;
57
+ this.effectiveBeats = exactLength * averageBeats;
58
+ this.updateRange(head, tail)
59
+ }
60
+ updateEffectiveBeats(val: number) {
61
+ this.effectiveBeats = val;
62
+ const averageBeats = this.averageBeats;
63
+ const exactLength: number = Math.ceil(val / averageBeats);
64
+ const currentLength = this.array.length
65
+ if (exactLength < currentLength) {
66
+ this.array.splice(exactLength, currentLength - exactLength)
67
+ }
68
+ }
69
+ updateAverageBeats() {
70
+ const length = this.array.length;
71
+ if (length >= 1024) {
72
+ return
73
+ }
74
+ let crowded = 0
75
+ for (let i = 0; i < 50; i++) {
76
+ const index = Math.floor(Math.random() * length);
77
+ if (Array.isArray(this.array[index])) {
78
+ crowded++
79
+ }
80
+ }
81
+ if (crowded > 30) {
82
+ this.averageBeats /= 2
83
+ this.updateRange(this.header, this.tailer)
84
+ }
85
+ }
86
+ /**
87
+ *
88
+ * @param firstNode 不含
89
+ * @param lastNode 含
90
+ */
91
+ updateRange(firstNode: T, lastNode: T) {
92
+ const {endNextFn, effectiveBeats, resolveLastNode} = this;
93
+ lastNode = resolveLastNode(lastNode);
94
+ // console.log(firstNode, lastNode)
95
+ /**
96
+ *
97
+ * @param startTime
98
+ * @param endTime 就是节点管辖范围的终止点,可以超过该刻度的最大值
99
+ */
100
+ const fillMinor = (startTime: number, endTime: number) => {
101
+ const minorArray: T[] = <T[]>jumpArray[jumpIndex];
102
+ const currentJumpBeats: number = jumpIndex * averageBeats
103
+ const startsFrom: number = startTime < currentJumpBeats ? 0 : Math.ceil((startTime - currentJumpBeats) / minorBeats)
104
+ const endsBefore: number = endTime > currentJumpBeats + averageBeats ? MINOR_PARTS : Math.ceil((endTime - currentJumpBeats) / minorBeats)
105
+ for (let minorIndex = startsFrom; minorIndex < endsBefore; minorIndex++) {
106
+ minorArray[minorIndex] = currentNode;
107
+ }
108
+ // console.log(jumpIndex, arrayForIn(minorArray, (n) => node2string(n)).join("]["))
109
+ // console.log("cur:", currentNode)
110
+ }
111
+ const jumpArray = this.array
112
+ const averageBeats: number = this.averageBeats;
113
+ const minorBeats: number = averageBeats / MINOR_PARTS;
114
+ let [previousEndTime, currentNode] = endNextFn(firstNode);
115
+ let jumpIndex = Math.floor(previousEndTime / averageBeats); // 这里写漏了特此留念
116
+ for (;;) {
117
+ let [endTime, nextNode] = endNextFn(currentNode);
118
+ // console.log("----Node:", currentNode, "next:", nextNode, "endTime:", endTime, "previousEndTime:", previousEndTime )
119
+ if (endTime === null) {
120
+ endTime = effectiveBeats;
121
+ }
122
+ // Hold树可能会不出现这种情况,故需特别考虑
123
+ if (endTime >= previousEndTime) {
124
+ while (endTime >= (jumpIndex + 1) * averageBeats) {
125
+ if (Array.isArray(jumpArray[jumpIndex])) {
126
+ fillMinor(previousEndTime, endTime)
127
+ } else {
128
+ try {
129
+ // console.log(jumpIndex, currentNode)
130
+ jumpArray[jumpIndex] = currentNode;
131
+ } catch (E) {console.log(jumpIndex, jumpArray);debugger}
132
+ }
133
+ jumpIndex++;
134
+ }
135
+ const currentJumpBeats: number = jumpIndex * averageBeats // 放错了
136
+ if (endTime > currentJumpBeats) {
137
+ let minor = jumpArray[jumpIndex];
138
+ if (!Array.isArray(minor)) {
139
+ jumpArray[jumpIndex] = new Array(MINOR_PARTS);
140
+ }
141
+ fillMinor(previousEndTime, endTime)
142
+ }
143
+ previousEndTime = endTime;
144
+ }
145
+ if (currentNode === lastNode) {
146
+ currentNode = nextNode; // 为了后续可能的填充,防止刻度不满引发错误
147
+ break
148
+ }
149
+ currentNode = nextNode
150
+ }
151
+ const minor = jumpArray[jumpIndex];
152
+ if (Array.isArray(minor)) {
153
+ // console.log("minor", arrayForIn(minor, (n) => node2string(n)))
154
+ if (!minor[MINOR_PARTS - 1]) {
155
+ if (!currentNode) {
156
+ currentNode = this.tailer
157
+ fillMinor(previousEndTime, effectiveBeats)
158
+ return;
159
+ }
160
+ do {
161
+ let [endTime, nextNode] = endNextFn(currentNode);
162
+ if (endTime === null) {
163
+ endTime = this.effectiveBeats;
164
+ }
165
+ if (endTime > previousEndTime) {
166
+ fillMinor(previousEndTime, endTime)
167
+ previousEndTime = endTime;
168
+ }
169
+ currentNode = nextNode;
170
+ } while (previousEndTime < (jumpIndex + 1) * averageBeats)
171
+ }
172
+ }
173
+ }
174
+ getPreviousOf(node: T, beats: number) {
175
+ const jumpAverageBeats = this.averageBeats;
176
+ const jumpPos = Math.floor(beats / jumpAverageBeats);
177
+ const rest = beats - jumpPos * jumpAverageBeats;
178
+ for (let i = jumpPos; i >= 0; i--) {
179
+ let canBeNodeOrArray: T | T[] = this.array[i];
180
+ if (Array.isArray(canBeNodeOrArray)) {
181
+ const minorIndex = Math.floor(rest / (jumpAverageBeats / MINOR_PARTS)) - 1;
182
+ for (let j = minorIndex; j >= 0; j--) {
183
+ const minorNode = canBeNodeOrArray[j];
184
+ if (minorNode !== node) {
185
+ return minorNode as T;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return this.header
191
+ }
192
+ /**
193
+ *
194
+ * @param beats 拍数
195
+ * @ param usePrev 可选,若设为true,则在取到事件头部时会返回前一个事件(即视为左开右闭)
196
+ * @returns 时间索引链表的节点,一般不是head
197
+ */
198
+ getNodeAt(beats: number): T {
199
+ if (beats < 0) {
200
+ return this.header.next;
201
+ }
202
+ if (beats >= this.effectiveBeats) {
203
+ return this.tailer;
204
+ }
205
+ const jumpAverageBeats = this.averageBeats;
206
+ const jumpPos = Math.floor(beats / jumpAverageBeats);
207
+ const rest = beats - jumpPos * jumpAverageBeats;
208
+ const nextFn = this.nextFn;
209
+ let canBeNodeOrArray: T | T[] = this.array[jumpPos]
210
+ let node: T = Array.isArray(canBeNodeOrArray)
211
+ ? canBeNodeOrArray[Math.floor(rest / (jumpAverageBeats / MINOR_PARTS))]
212
+ : canBeNodeOrArray;
213
+ if (node.type === NodeType.TAIL) {
214
+ return node;
215
+ }
216
+ // console.log(this, node, jumpPos, beats)
217
+ if (!node) {
218
+ console.warn("No node:", node, beats)
219
+ debugger
220
+ }
221
+ let next: T | false;
222
+ // console.log(this)
223
+ while (next = nextFn(node, beats)) {
224
+ node = next;
225
+ if (node.type === NodeType.TAIL) {
226
+ break;
227
+ }
228
+ }
229
+ return node
230
+ }
231
+ }
232
+
233
+
234
+ /// #enddeclaration