adofai 3.2.0 → 3.3.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.
@@ -10,8 +10,6 @@ export declare class Level {
10
10
  settings: Record<string, any>;
11
11
  __decorations: AdofaiEvent[];
12
12
  tiles: Tile[];
13
- private _angleDir;
14
- private _twirlCount;
15
13
  /** 预计算的事件缓存 */
16
14
  private _precomputedEvents;
17
15
  /** 是否启用预计算模式 */
@@ -20,68 +18,21 @@ export declare class Level {
20
18
  private _lightweightData;
21
19
  constructor(opt: string | LevelOptions, provider?: ParseProvider);
22
20
  generateGUID(): string;
23
- /**
24
- * 触发进度事件
25
- */
26
21
  private _emitProgress;
27
- /**
28
- * 启用预计算模式 - 在 load() 和 calculateTilePosition() 过程中不触发事件,
29
- * 而是将所有事件缓存起来,之后可以通过 getPrecomputedEvents() 获取
30
- */
31
22
  enablePrecomputeMode(): void;
32
- /**
33
- * 禁用预计算模式
34
- */
35
23
  disablePrecomputeMode(): void;
36
- /**
37
- * 获取预计算的事件缓存
38
- * 返回所有缓存的进度事件,可以用于渲染器按帧播放
39
- */
40
24
  getPrecomputedEvents(): PrecomputedProgressEvents | null;
41
- /**
42
- * 清除预计算的事件缓存
43
- */
44
25
  clearPrecomputedEvents(): void;
45
- /**
46
- * 按进度百分比获取事件(用于渲染器按帧渲染)
47
- * @param percent 0-100 的百分比
48
- * @param stage 可选,指定阶段
49
- */
50
26
  getEventsAtPercent(percent: number, stage?: ParseProgressEvent['stage']): ParseProgressEvent[];
51
- /**
52
- * 获取指定阶段的总事件数
53
- */
54
27
  getPrecomputedEventCount(stage?: ParseProgressEvent['stage']): number;
55
- /**
56
- * 获取轻量级预计算数据
57
- * 只包含渲染必需的数据:angles, positions, twirlFlags
58
- * 内存占用极低,适合大物量谱面
59
- */
60
28
  getLightweightData(): LightweightPrecomputedData | null;
61
- /**
62
- * 轻量级预计算 - 只计算渲染必需的数据
63
- * 不存储完整的 tile 对象,极大减少内存占用
64
- *
65
- * @param skipPositionCalculation 是否跳过坐标计算(如果只需要角度数据)
66
- */
67
29
  precomputeLightweight(skipPositionCalculation?: boolean): LightweightPrecomputedData;
68
- /**
69
- * 获取指定范围内的轻量级渲染数据(分片获取,避免一次性加载全部)
70
- * @param startIndex 起始索引
71
- * @param count 数量
72
- */
73
30
  getLightweightDataRange(startIndex: number, count: number): {
74
31
  angles: number[];
75
32
  positions: [number, number][];
76
33
  twirlFlags: boolean[];
77
34
  } | null;
78
- /**
79
- * 清除轻量级预计算数据
80
- */
81
35
  clearLightweightData(): void;
82
- /**
83
- * 获取单个 tile 的渲染数据(按需获取,不预加载全部)
84
- */
85
36
  getTileRenderData(index: number): {
86
37
  angle: number;
87
38
  position: [number, number] | null;
@@ -91,16 +42,6 @@ export declare class Level {
91
42
  on(eventName: string, callback: Function): string;
92
43
  trigger(eventName: string, data: any): void;
93
44
  off(guid: string): void;
94
- private _createArray;
95
- private _changeAngle;
96
- private _normalizeAngle;
97
- private _parsechangedAngle;
98
- private _filterByFloor;
99
- private _flattenAngleDatas;
100
- private _flattenActionsWithFloor;
101
- private _filterByFloorwithDeco;
102
- private _flattenDecorationsWithFloor;
103
- private _parseAngle;
104
45
  filterActionsByEventType(en: string): {
105
46
  index: number;
106
47
  action: ActionData;
@@ -110,12 +51,6 @@ export declare class Level {
110
51
  actions: ActionData[];
111
52
  };
112
53
  calculateTileCoordinates(): void;
113
- /**
114
- * 计算所有 Tile 的坐标位置
115
- * 触发 parse:tilePosition 和 parse:progress 事件报告进度
116
- *
117
- * 性能优化:预先构建 PositionTrack 索引,避免循环内重复遍历
118
- */
119
54
  calculateTilePosition(): number[][];
120
55
  floorOperation(info?: {
121
56
  type: 'append' | 'insert' | 'delete';
@@ -1,27 +1,9 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- var __rest = (this && this.__rest) || function (s, e) {
11
- var t = {};
12
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
- t[p] = s[p];
14
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
- t[p[i]] = s[p[i]];
18
- }
19
- return t;
20
- };
21
1
  import pathData from '../pathdata';
22
2
  import exportAsADOFAI from './format';
23
3
  import effectProcessor from '../filter/effectProcessor';
24
4
  import { EffectCleanerType } from '../filter/effectProcessor';
5
+ import * as presets from '../filter/presets';
6
+ import { createTiles, changeAngles, filterActionsByEventType as filterActions, getActionsByIndex as getActions, calculateTilePositions, precomputePositions, flattenAngleDatas, flattenActionsWithFloor, flattenDecorationsWithFloor } from './levelAngle';
25
7
  function uuid() {
26
8
  var _a;
27
9
  const r = new Uint8Array(16);
@@ -42,7 +24,6 @@ function uuid() {
42
24
  + h(r[8]) + h(r[9]) + '-'
43
25
  + h(r[10]) + h(r[11]) + h(r[12]) + h(r[13]) + h(r[14]) + h(r[15]);
44
26
  }
45
- import * as presets from '../filter/presets';
46
27
  export class Level {
47
28
  constructor(opt, provider) {
48
29
  /** 预计算的事件缓存 */
@@ -59,9 +40,6 @@ export class Level {
59
40
  generateGUID() {
60
41
  return `event_${uuid()}`;
61
42
  }
62
- /**
63
- * 触发进度事件
64
- */
65
43
  _emitProgress(stage, current, total, data) {
66
44
  const progressEvent = {
67
45
  stage,
@@ -70,7 +48,6 @@ export class Level {
70
48
  percent: total > 0 ? Math.round((current / total) * 100) : 0,
71
49
  data
72
50
  };
73
- // 如果是预计算模式,存储事件而不是触发
74
51
  if (this._precomputeMode && this._precomputedEvents) {
75
52
  this._precomputedEvents[stage].push(progressEvent);
76
53
  }
@@ -79,10 +56,6 @@ export class Level {
79
56
  this.trigger(`parse:${stage}`, progressEvent);
80
57
  }
81
58
  }
82
- /**
83
- * 启用预计算模式 - 在 load() 和 calculateTilePosition() 过程中不触发事件,
84
- * 而是将所有事件缓存起来,之后可以通过 getPrecomputedEvents() 获取
85
- */
86
59
  enablePrecomputeMode() {
87
60
  this._precomputeMode = true;
88
61
  this._precomputedEvents = {
@@ -94,30 +67,15 @@ export class Level {
94
67
  complete: []
95
68
  };
96
69
  }
97
- /**
98
- * 禁用预计算模式
99
- */
100
70
  disablePrecomputeMode() {
101
71
  this._precomputeMode = false;
102
72
  }
103
- /**
104
- * 获取预计算的事件缓存
105
- * 返回所有缓存的进度事件,可以用于渲染器按帧播放
106
- */
107
73
  getPrecomputedEvents() {
108
74
  return this._precomputedEvents;
109
75
  }
110
- /**
111
- * 清除预计算的事件缓存
112
- */
113
76
  clearPrecomputedEvents() {
114
77
  this._precomputedEvents = null;
115
78
  }
116
- /**
117
- * 按进度百分比获取事件(用于渲染器按帧渲染)
118
- * @param percent 0-100 的百分比
119
- * @param stage 可选,指定阶段
120
- */
121
79
  getEventsAtPercent(percent, stage) {
122
80
  if (!this._precomputedEvents)
123
81
  return [];
@@ -127,9 +85,7 @@ export class Level {
127
85
  const events = this._precomputedEvents[s];
128
86
  for (const event of events) {
129
87
  if (event.percent <= percent) {
130
- // 获取不超过目标百分比的最后一个事件
131
88
  if (result.length === 0 || result[result.length - 1].percent <= event.percent) {
132
- // 避免重复添加相同百分比的事件
133
89
  const lastEvent = result[result.length - 1];
134
90
  if (!lastEvent || lastEvent.stage !== event.stage || lastEvent.current !== event.current) {
135
91
  result.push(event);
@@ -140,81 +96,31 @@ export class Level {
140
96
  }
141
97
  return result;
142
98
  }
143
- /**
144
- * 获取指定阶段的总事件数
145
- */
146
99
  getPrecomputedEventCount(stage) {
147
100
  if (!this._precomputedEvents)
148
101
  return 0;
149
- if (stage) {
102
+ if (stage)
150
103
  return this._precomputedEvents[stage].length;
151
- }
152
104
  return Object.values(this._precomputedEvents).reduce((sum, arr) => sum + arr.length, 0);
153
105
  }
154
- /**
155
- * 获取轻量级预计算数据
156
- * 只包含渲染必需的数据:angles, positions, twirlFlags
157
- * 内存占用极低,适合大物量谱面
158
- */
159
106
  getLightweightData() {
160
107
  return this._lightweightData;
161
108
  }
162
- /**
163
- * 轻量级预计算 - 只计算渲染必需的数据
164
- * 不存储完整的 tile 对象,极大减少内存占用
165
- *
166
- * @param skipPositionCalculation 是否跳过坐标计算(如果只需要角度数据)
167
- */
168
109
  precomputeLightweight(skipPositionCalculation = false) {
169
110
  var _a, _b;
170
111
  const totalTiles = this.tiles.length;
171
- // 预分配数组
172
112
  const angles = new Array(totalTiles);
173
113
  const positions = skipPositionCalculation ? [] : new Array(totalTiles);
174
114
  const twirlFlags = new Array(totalTiles);
175
- // 提取角度和 twirl 标记
176
115
  for (let i = 0; i < totalTiles; i++) {
177
116
  const tile = this.tiles[i];
178
117
  angles[i] = (_a = tile.angle) !== null && _a !== void 0 ? _a : 0;
179
118
  twirlFlags[i] = ((_b = tile.twirl) !== null && _b !== void 0 ? _b : 0) % 2 === 1;
180
119
  }
181
- // 计算坐标(如果需要)
182
120
  if (!skipPositionCalculation) {
183
- const startPos = [0, 0];
184
- const angleData = this.angleData;
185
- // 预构建 PositionTrack 索引
186
- const positionTrackMap = new Map();
187
- for (const action of this.actions) {
188
- if (action.eventType === 'PositionTrack' && action.positionOffset) {
189
- if (action.editorOnly !== true && action.editorOnly !== 'Enabled') {
190
- positionTrackMap.set(action.floor, action);
191
- }
192
- }
193
- }
121
+ const result = precomputePositions(this.angleData, totalTiles, this.actions);
194
122
  for (let i = 0; i < totalTiles; i++) {
195
- // 处理 PositionTrack
196
- const posTrack = positionTrackMap.get(i);
197
- if (posTrack === null || posTrack === void 0 ? void 0 : posTrack.positionOffset) {
198
- startPos[0] += posTrack.positionOffset[0];
199
- startPos[1] += posTrack.positionOffset[1];
200
- }
201
- positions[i] = [startPos[0], startPos[1]];
202
- // 计算下一个位置(向前回溯连续999)
203
- let angle;
204
- if (angleData[i] === 999) {
205
- let minus = 1;
206
- while (i - minus >= 0 && angleData[i - minus] === 999) {
207
- minus++;
208
- }
209
- const realAngle = i - minus >= 0 ? angleData[i - minus] : 0;
210
- angle = realAngle + (minus - 1) * 180;
211
- }
212
- else {
213
- angle = angleData[i];
214
- }
215
- const rad = angle * Math.PI / 180;
216
- startPos[0] += Math.cos(rad);
217
- startPos[1] += Math.sin(rad);
123
+ positions[i] = result.positions[i];
218
124
  }
219
125
  }
220
126
  this._lightweightData = {
@@ -226,11 +132,6 @@ export class Level {
226
132
  };
227
133
  return this._lightweightData;
228
134
  }
229
- /**
230
- * 获取指定范围内的轻量级渲染数据(分片获取,避免一次性加载全部)
231
- * @param startIndex 起始索引
232
- * @param count 数量
233
- */
234
135
  getLightweightDataRange(startIndex, count) {
235
136
  if (!this._lightweightData)
236
137
  return null;
@@ -241,15 +142,9 @@ export class Level {
241
142
  twirlFlags: this._lightweightData.twirlFlags.slice(startIndex, end)
242
143
  };
243
144
  }
244
- /**
245
- * 清除轻量级预计算数据
246
- */
247
145
  clearLightweightData() {
248
146
  this._lightweightData = null;
249
147
  }
250
- /**
251
- * 获取单个 tile 的渲染数据(按需获取,不预加载全部)
252
- */
253
148
  getTileRenderData(index) {
254
149
  var _a, _b;
255
150
  if (index < 0 || index >= this.tiles.length)
@@ -273,7 +168,6 @@ export class Level {
273
168
  if (typeof opt === 'string' || isArrayBuffer || isUint8Array || isBuffer) {
274
169
  try {
275
170
  let input = isArrayBuffer ? new Uint8Array(opt) : opt;
276
- // 确保此时 input 是 string, Uint8Array 或 Buffer
277
171
  options = (_a = this._provider) === null || _a === void 0 ? void 0 : _a.parse(input);
278
172
  }
279
173
  catch (e) {
@@ -288,15 +182,12 @@ export class Level {
288
182
  reject("Options must be String, Buffer, ArrayBuffer or Object");
289
183
  return;
290
184
  }
291
- // 阶段2: 处理 pathData 或 angleData
292
185
  const hasPathData = options && typeof options === 'object' && options !== null && typeof options.pathData !== 'undefined';
293
186
  const hasAngleData = options && typeof options === 'object' && options !== null && typeof options.angleData !== 'undefined';
294
187
  if (hasPathData) {
295
188
  const pathDataStr = options['pathData'];
296
- // 开始转换 pathData
297
189
  this._emitProgress('pathData', 0, pathDataStr.length, { source: pathDataStr });
298
190
  this.angleData = pathData.parseToangleData(pathDataStr);
299
- // 转换完成,返回结果
300
191
  this._emitProgress('pathData', pathDataStr.length, pathDataStr.length, {
301
192
  source: pathDataStr,
302
193
  processed: this.angleData
@@ -312,7 +203,6 @@ export class Level {
312
203
  reject("There is not any angle datas.");
313
204
  return;
314
205
  }
315
- // 阶段3: 提取其他数据
316
206
  if (options && typeof options === 'object' && options !== null && Array.isArray(options.actions)) {
317
207
  this.actions = options['actions'];
318
208
  }
@@ -333,11 +223,16 @@ export class Level {
333
223
  this.__decorations = [];
334
224
  }
335
225
  this.tiles = [];
336
- this._angleDir = -180;
337
- this._twirlCount = 0;
338
- // 阶段4: 创建 Tile 数组(带进度回调)
339
- this._createArray(this.angleData.length, { angleData: this.angleData, actions: this.actions, decorations: this.__decorations })
340
- .then(e => {
226
+ let twirlCount = 0;
227
+ createTiles(this.angleData.length, {
228
+ angleData: this.angleData,
229
+ actions: this.actions,
230
+ decorations: this.__decorations
231
+ }, {
232
+ onProgress: (stage, current, total, data) => this._emitProgress(stage, current, total, data),
233
+ onTwirl: (count) => { twirlCount = count; },
234
+ getTwirl: () => twirlCount,
235
+ }).then(e => {
341
236
  this.tiles = e;
342
237
  this._emitProgress('complete', this.angleData.length, this.angleData.length);
343
238
  this.trigger('load', this);
@@ -376,284 +271,17 @@ export class Level {
376
271
  callbacks.splice(index, 1);
377
272
  }
378
273
  }
379
- _createArray(xLength, opt) {
380
- return __awaiter(this, void 0, void 0, function* () {
381
- const tiles = new Array(xLength);
382
- const batchSize = Math.max(100, Math.floor(xLength / 100)); // 每批处理的数量,提高最小批量
383
- // 性能优化:预先按 floor 分组 actions 和 decorations
384
- const actionsByFloor = new Map();
385
- if (Array.isArray(opt.actions)) {
386
- for (const action of opt.actions) {
387
- if (!actionsByFloor.has(action.floor)) {
388
- actionsByFloor.set(action.floor, []);
389
- }
390
- actionsByFloor.get(action.floor).push(action);
391
- }
392
- }
393
- const decorationsByFloor = new Map();
394
- if (Array.isArray(opt.decorations)) {
395
- for (const deco of opt.decorations) {
396
- if (!decorationsByFloor.has(deco.floor)) {
397
- decorationsByFloor.set(deco.floor, []);
398
- }
399
- decorationsByFloor.get(deco.floor).push(deco);
400
- }
401
- }
402
- for (let i = 0; i < xLength; i++) {
403
- // 从 Map 中直接获取当前 floor 的 actions
404
- const floorActions = actionsByFloor.get(i) || [];
405
- const floorDecos = decorationsByFloor.get(i) || [];
406
- // 更新 _twirlCount
407
- for (const action of floorActions) {
408
- if (action.eventType === 'Twirl') {
409
- this._twirlCount++;
410
- }
411
- }
412
- // 计算相对角度
413
- const angle = this._parseAngle(opt.angleData, i, this._twirlCount % 2);
414
- // 移除 floor 属性并创建 tile
415
- const tileActions = floorActions.map((_a) => {
416
- var { floor } = _a, rest = __rest(_a, ["floor"]);
417
- return rest;
418
- });
419
- const tileDecos = floorDecos.map((_a) => {
420
- var { floor } = _a, rest = __rest(_a, ["floor"]);
421
- return rest;
422
- });
423
- tiles[i] = {
424
- direction: opt.angleData[i],
425
- _lastdir: opt.angleData[i - 1] || 0,
426
- actions: tileActions,
427
- angle: angle,
428
- addDecorations: tileDecos,
429
- twirl: this._twirlCount,
430
- extraProps: {}
431
- };
432
- // 降低进度汇报和让出循环的频率
433
- if (i % batchSize === 0 || i === xLength - 1) {
434
- this._emitProgress('relativeAngle', i + 1, xLength, {
435
- tileIndex: i,
436
- angle: opt.angleData[i],
437
- relativeAngle: angle
438
- });
439
- // 每 10% 让出一次,或者对于超大谱面增加频率
440
- if (i % (batchSize * 10) === 0) {
441
- yield new Promise(r => setTimeout(r, 0));
442
- }
443
- }
444
- }
445
- return tiles;
446
- });
447
- }
448
- _changeAngle() {
449
- let y = 0;
450
- let m = this.tiles.map(t => {
451
- y++;
452
- t.angle = this._parsechangedAngle(t.direction, y, t.twirl, t._lastdir);
453
- return t;
454
- });
455
- return m;
456
- }
457
- _normalizeAngle(v) {
458
- return ((v % 360) + 360) % 360;
459
- }
460
- _parsechangedAngle(agd, i, isTwirl, lstagd) {
461
- var _a;
462
- let prev = 0;
463
- if (i === 0) {
464
- this._angleDir = 180;
465
- }
466
- if (agd === 999) {
467
- // 向前回溯(1-based i → tiles 0-based index: i-1 是当前tile)
468
- let minus = 1;
469
- while (i - minus - 1 >= 0 && ((_a = this.tiles[i - minus - 1]) === null || _a === void 0 ? void 0 : _a.direction) === 999) {
470
- minus++;
471
- }
472
- const realAngle = i - minus - 1 >= 0 ? this.tiles[i - minus - 1].direction : 0;
473
- this._angleDir = this._normalizeAngle(realAngle + (minus - 1) * 180);
474
- if (isNaN(this._angleDir)) {
475
- this._angleDir = 0;
476
- }
477
- prev = 0;
478
- }
479
- else {
480
- const delta = this._normalizeAngle(this._angleDir - agd);
481
- if (isTwirl === 0) {
482
- prev = delta;
483
- }
484
- else {
485
- prev = this._normalizeAngle(360 - delta);
486
- }
487
- if (prev === 0) {
488
- prev = 360;
489
- }
490
- this._angleDir = this._normalizeAngle(agd + 180);
491
- }
492
- return prev;
493
- }
494
- _filterByFloor(arr, i) {
495
- if (!Array.isArray(arr))
496
- return [];
497
- let actionT = arr.filter(item => item.floor === i);
498
- this._twirlCount += actionT.filter(t => t.eventType === 'Twirl').length;
499
- return actionT.map((_a) => {
500
- var { floor } = _a, rest = __rest(_a, ["floor"]);
501
- return rest;
502
- });
503
- }
504
- _flattenAngleDatas(arr) {
505
- return arr.map(item => item.direction);
506
- }
507
- _flattenActionsWithFloor(arr) {
508
- return arr.flatMap((tile, index) => (Array.isArray(tile === null || tile === void 0 ? void 0 : tile.actions) ? tile.actions : []).map((_a) => {
509
- var { floor } = _a, rest = __rest(_a, ["floor"]);
510
- return (Object.assign({ floor: index }, rest));
511
- }));
512
- }
513
- _filterByFloorwithDeco(arr, i) {
514
- if (!Array.isArray(arr))
515
- return [];
516
- let actionT = arr.filter(item => item.floor === i);
517
- return actionT.map((_a) => {
518
- var { floor } = _a, rest = __rest(_a, ["floor"]);
519
- return rest;
520
- });
521
- }
522
- _flattenDecorationsWithFloor(arr) {
523
- return arr.flatMap((tile, index) => (Array.isArray(tile === null || tile === void 0 ? void 0 : tile.addDecorations) ? tile.addDecorations : []).map((_a) => {
524
- var { floor } = _a, rest = __rest(_a, ["floor"]);
525
- return (Object.assign({ floor: index }, rest));
526
- }));
527
- }
528
- _parseAngle(agd, i, isTwirl) {
529
- let prev = 0;
530
- if (i === 0) {
531
- this._angleDir = 180;
532
- }
533
- if (agd[i] === 999) {
534
- // 向前回溯,找到第一个非999的真实角度
535
- let minus = 1;
536
- while (i - minus >= 0 && agd[i - minus] === 999) {
537
- minus++;
538
- }
539
- const realAngle = i - minus >= 0 ? agd[i - minus] : 0;
540
- this._angleDir = this._normalizeAngle(realAngle + (minus - 1) * 180);
541
- if (isNaN(this._angleDir)) {
542
- this._angleDir = 0;
543
- }
544
- prev = 0;
545
- }
546
- else {
547
- const delta = this._normalizeAngle(this._angleDir - agd[i]);
548
- if (isTwirl === 0) {
549
- prev = delta;
550
- }
551
- else {
552
- prev = this._normalizeAngle(360 - delta);
553
- }
554
- if (prev === 0) {
555
- prev = 360;
556
- }
557
- this._angleDir = this._normalizeAngle(agd[i] + 180);
558
- }
559
- return prev;
560
- }
561
274
  filterActionsByEventType(en) {
562
- return Object.entries(this.tiles)
563
- .flatMap(([index, a]) => (Array.isArray(a.actions) ? a.actions : []).map(b => ({ b, index })))
564
- .filter(({ b }) => b.eventType === en)
565
- .map(({ b, index }) => ({
566
- index: Number(index),
567
- action: b
568
- }));
275
+ return filterActions(this.tiles, en);
569
276
  }
570
277
  getActionsByIndex(en, index) {
571
- const filtered = this.filterActionsByEventType(en);
572
- const matches = filtered.filter(item => item.index === index);
573
- return {
574
- count: matches.length,
575
- actions: matches.map(item => item.action)
576
- };
278
+ return getActions(this.tiles, en, index);
577
279
  }
578
280
  calculateTileCoordinates() {
579
281
  console.warn("calculateTileCoordinates is deprecated. Use calculateTilePosition instead.");
580
282
  }
581
- /**
582
- * 计算所有 Tile 的坐标位置
583
- * 触发 parse:tilePosition 和 parse:progress 事件报告进度
584
- *
585
- * 性能优化:预先构建 PositionTrack 索引,避免循环内重复遍历
586
- */
587
283
  calculateTilePosition() {
588
- const angles = this.angleData;
589
- const totalTiles = this.tiles.length;
590
- const positions = [];
591
- const startPos = [0, 0];
592
- // 性能优化:预先构建 PositionTrack 索引 Map,O(n) 预处理
593
- const positionTrackMap = new Map();
594
- for (const action of this.actions) {
595
- if (action.eventType === 'PositionTrack' && action.positionOffset) {
596
- if (action.editorOnly !== true && action.editorOnly !== 'Enabled') {
597
- positionTrackMap.set(action.floor, action);
598
- }
599
- }
600
- }
601
- // 触发开始事件
602
- this._emitProgress('tilePosition', 0, totalTiles);
603
- // 预处理 floats 数组(向前回溯连续999)
604
- const floats = new Array(totalTiles);
605
- for (let i = 0; i < totalTiles; i++) {
606
- if (angles[i] === 999) {
607
- let minus = 1;
608
- while (i - minus >= 0 && angles[i - minus] === 999) {
609
- minus++;
610
- }
611
- const realAngle = i - minus >= 0 ? angles[i - minus] : 0;
612
- floats[i] = realAngle + (minus - 1) * 180;
613
- }
614
- else {
615
- floats[i] = angles[i];
616
- }
617
- }
618
- // 进度事件触发频率:每 1% 或最少每 100 个 tile 触发一次
619
- const progressInterval = Math.max(100, Math.floor(totalTiles / 100));
620
- for (let i = 0; i <= totalTiles; i++) {
621
- const isLastTile = i === totalTiles;
622
- const angle1 = isLastTile ? (floats[i - 1] || 0) : floats[i];
623
- const angle2 = i === 0 ? 0 : (floats[i - 1] || 0);
624
- const currentTile = this.tiles[i];
625
- // 使用索引 Map 直接查询,O(1) 复杂度
626
- const posTrack = positionTrackMap.get(i);
627
- if (posTrack === null || posTrack === void 0 ? void 0 : posTrack.positionOffset) {
628
- startPos[0] += posTrack.positionOffset[0];
629
- startPos[1] += posTrack.positionOffset[1];
630
- }
631
- const tempPos = [startPos[0], startPos[1]];
632
- positions.push(tempPos);
633
- if (currentTile) {
634
- currentTile.position = tempPos;
635
- currentTile.extraProps.angle1 = angle1;
636
- currentTile.extraProps.angle2 = angle2 - 180;
637
- currentTile.extraProps.cangle = isLastTile ? floats[i - 1] + 180 : floats[i];
638
- }
639
- // 更新位置
640
- const rad = angle1 * Math.PI / 180;
641
- startPos[0] += Math.cos(rad);
642
- startPos[1] += Math.sin(rad);
643
- // 触发进度事件(降低频率,轻量级数据)
644
- if (i % progressInterval === 0 || isLastTile) {
645
- this._emitProgress('tilePosition', i, totalTiles, {
646
- tileIndex: i,
647
- position: [tempPos[0], tempPos[1]],
648
- angle: angle1
649
- });
650
- }
651
- }
652
- // 触发完成事件
653
- this._emitProgress('tilePosition', totalTiles, totalTiles, {
654
- processed: positions.flat()
655
- });
656
- return positions;
284
+ return calculateTilePositions(this.angleData, this.tiles, this.actions, (stage, current, total, data) => this._emitProgress(stage, current, total, data));
657
285
  }
658
286
  floorOperation(info = { type: 'append', direction: 0 }) {
659
287
  switch (info.type) {
@@ -678,7 +306,7 @@ export class Level {
678
306
  }
679
307
  break;
680
308
  }
681
- this._changeAngle();
309
+ changeAngles(this.tiles);
682
310
  }
683
311
  appendFloor(args) {
684
312
  this.tiles.push({
@@ -690,7 +318,7 @@ export class Level {
690
318
  twirl: this.tiles[this.tiles.length - 1].twirl,
691
319
  extraProps: {}
692
320
  });
693
- this._changeAngle();
321
+ changeAngles(this.tiles);
694
322
  }
695
323
  clearDeco() {
696
324
  this.tiles = effectProcessor.clearDecorations(this.tiles);
@@ -709,10 +337,10 @@ export class Level {
709
337
  }
710
338
  export(type, indent, useAdofaiStyle = true, indentChar, indentStep) {
711
339
  const ADOFAI = {
712
- angleData: this._flattenAngleDatas(this.tiles),
340
+ angleData: flattenAngleDatas(this.tiles),
713
341
  settings: this.settings,
714
- actions: this._flattenActionsWithFloor(this.tiles),
715
- decorations: this._flattenDecorationsWithFloor(this.tiles)
342
+ actions: flattenActionsWithFloor(this.tiles),
343
+ decorations: flattenDecorationsWithFloor(this.tiles)
716
344
  };
717
345
  return type === 'object' ? ADOFAI : exportAsADOFAI(ADOFAI, indent, useAdofaiStyle, indentChar, indentStep);
718
346
  }