@shijiu/jsview-vue 2.0.1021 → 2.0.1073

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.
Files changed (33) hide show
  1. package/package.json +4 -3
  2. package/utils/JsViewEngineWidget/CheckType.js +82 -0
  3. package/utils/JsViewEngineWidget/MetroWidget/AnimationManager.ts +72 -0
  4. package/utils/JsViewEngineWidget/MetroWidget/Const.ts +24 -0
  5. package/utils/JsViewEngineWidget/MetroWidget/ListWidget.vue +295 -0
  6. package/utils/JsViewEngineWidget/MetroWidget/MetroWidget.vue +110 -1651
  7. package/utils/JsViewEngineWidget/MetroWidget/MetroWidgetSetup.js +1867 -0
  8. package/utils/JsViewEngineWidget/MetroWidget/PageUpdater.ts +111 -0
  9. package/utils/JsViewEngineWidget/MetroWidget/RenderItem.ts +153 -0
  10. package/utils/JsViewEngineWidget/MetroWidget/VisibleInfo.ts +43 -0
  11. package/utils/JsViewEngineWidget/MetroWidget/WidgetRectInfo.ts +49 -0
  12. package/utils/JsViewEngineWidget/TemplateParser/CommonMetroTemplate.ts +1424 -0
  13. package/utils/JsViewEngineWidget/TemplateParser/Fence.ts +135 -0
  14. package/utils/JsViewEngineWidget/TemplateParser/ListMetroTemplate.ts +177 -0
  15. package/utils/JsViewEngineWidget/TemplateParser/MetroTemplate.ts +334 -0
  16. package/utils/JsViewEngineWidget/TemplateParser/TemplateItemAdder.ts +147 -0
  17. package/utils/JsViewEngineWidget/TemplateParser/index.ts +4 -0
  18. package/utils/JsViewEngineWidget/{WidgetCommon.js → WidgetCommon.ts} +64 -71
  19. package/utils/JsViewEngineWidget/index.js +2 -1
  20. package/utils/JsViewPlugin/JsvAudio/AudioProxy.js +26 -1
  21. package/utils/JsViewPlugin/JsvAudio/JsvAudio.vue +120 -133
  22. package/utils/JsViewPlugin/JsvAudio/JsvAudioBrowser.vue +11 -7
  23. package/utils/JsViewPlugin/JsvPlayer/GetVersion.js +1 -1
  24. package/utils/JsViewPlugin/JsvPlayer/JsvPlayerBrowser.vue +379 -41
  25. package/utils/JsViewPlugin/JsvPlayer/version.mjs +5 -5
  26. package/utils/JsViewVueTools/JsvHashHistory.js +2 -1
  27. package/utils/JsViewVueWidget/JsvRadarChart.vue +220 -0
  28. package/utils/JsViewVueWidget/JsvSystemAudio.vue +76 -44
  29. package/utils/JsViewVueWidget/index.js +1 -0
  30. package/utils/JsViewEngineWidget/MetroWidget/Const.js +0 -11
  31. package/utils/JsViewEngineWidget/MetroWidget/PageUpdater.js +0 -136
  32. package/utils/JsViewEngineWidget/MetroWidget/ToolFunctions.js +0 -18
  33. package/utils/JsViewEngineWidget/TemplateParser.js +0 -2004
@@ -0,0 +1,1424 @@
1
+ import { Fence, GapInfo } from "./Fence";
2
+ import { MetroTemplate, TemplateItem, MeasureInfo } from "./MetroTemplate"
3
+ import { RangesModel, SingleRangeModel } from "../RangeModel";
4
+ import { VERTICAL, HORIZONTAL, FocusMoveType } from "../WidgetCommon";
5
+
6
+ const TAG = "CommonTemplateParser";
7
+
8
+ /** neighbor related interface */
9
+ interface Edge {
10
+ position: number,
11
+ range: RangesModel,
12
+ }
13
+
14
+ interface EdgeInfo {
15
+ index: number,
16
+ edgeObj: {
17
+ leftEdge: Edge | null,
18
+ rightEdge: Edge | null,
19
+ topEdge: Edge | null,
20
+ bottomEdge: Edge | null,
21
+ }
22
+ }
23
+
24
+ /** get visible list related interface */
25
+ interface VisibleSearchConfigCommon {
26
+ resultStartIdx: number,
27
+ resultEndIdx: number,
28
+ enableBackwardSearching: boolean,
29
+ enableForwardSearching: boolean,
30
+ checkingStartLinePos: number,
31
+ checkingEndLinePos: number,
32
+ };
33
+
34
+ interface VisibleSearchConfig {
35
+ searchDirect: number;
36
+ baseIdx: number;
37
+ checkingStartLineRanges: RangesModel;
38
+ startLineRangesFulled: boolean;
39
+ latestOverStartLineIdx: number;
40
+ checkingEndLineRanges: RangesModel;
41
+ endLineRangesFulled: boolean;
42
+ latestOverEndLineIdx: number;
43
+ endSearchingIndex: number;
44
+ firstStartLineBlockIdx: number;
45
+ firstEndLineBlockIdx: number;
46
+ searchToBorder: boolean;
47
+ common: VisibleSearchConfigCommon;
48
+ };
49
+
50
+ class CommonMetroTemplate extends MetroTemplate {
51
+ private _FenceStack: Array<Fence>;
52
+ private _FenceEdge: { StartX: number, StartY: number };
53
+ private _NotOccupiedEdgeList: Array<EdgeInfo>;
54
+
55
+ constructor(direction: Symbol, lineMax: number, pageSize: number,
56
+ layoutType: string, supportHistoryPath: boolean) {
57
+ super(direction, lineMax, pageSize, layoutType, supportHistoryPath);
58
+ this._FenceStack = [new Fence(0, 0, this.lineMax, 0)];
59
+ this._FenceEdge = { StartX: 0, StartY: 0 }; // 新的未占用边列表生成
60
+ this._NotOccupiedEdgeList = []; // 未占用边列表
61
+
62
+ }
63
+
64
+ public calculateNeighborWhenAddStop(): void {
65
+ // Update Last Fence Edge(更新最后一行的fence edge)
66
+ this._UpdateLastFenceEdge();
67
+ // Last fence edge to get neighbor
68
+ this._CalculateNeighborItem();
69
+ this._CalculateNeighborItem(true);
70
+ const templateList = this.templateList;
71
+ //处理找到的neighbor均是占位符的情况
72
+ for (let j = templateList.length - 1; j >= 0; j--) {
73
+ const item = templateList[j];
74
+ if (item.focusable) {
75
+ this._FindFocusableNeighbor("bottom", item);
76
+ this._FindFocusableNeighbor("top", item);
77
+ this._FindFocusableNeighbor("left", item);
78
+ this._FindFocusableNeighbor("right", item);
79
+ }
80
+ }
81
+ }
82
+
83
+ private _UpdateLastFenceEdge() {
84
+ const oldFence = this._FenceEdge;
85
+ const newMinFence = { StartX: -1, StartY: -1 };
86
+ // 查找最后一条FenceEdge
87
+ for (let i = 0; i < this._NotOccupiedEdgeList.length; i++) {
88
+ const crossItem = this._NotOccupiedEdgeList[i];
89
+ const crossEdgeObj = crossItem.edgeObj;
90
+ if (this.direction === VERTICAL) {
91
+ if (crossEdgeObj.bottomEdge !== null) {
92
+ if (crossEdgeObj.bottomEdge.position > oldFence.StartY) {
93
+ if (newMinFence.StartY === -1) {
94
+ newMinFence.StartY = crossEdgeObj.bottomEdge.position;
95
+ } else if (crossEdgeObj.bottomEdge.position < newMinFence.StartY) {
96
+ newMinFence.StartY = crossEdgeObj.bottomEdge.position;
97
+ }
98
+ }
99
+ }
100
+ } else {
101
+ if (crossEdgeObj.rightEdge !== null) {
102
+ if (crossEdgeObj.rightEdge.position > oldFence.StartX) {
103
+ if (newMinFence.StartX === -1) {
104
+ newMinFence.StartX = crossEdgeObj.rightEdge.position;
105
+ } else if (crossEdgeObj.rightEdge.position < newMinFence.StartX) {
106
+ newMinFence.StartX = crossEdgeObj.rightEdge.position;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ this._FenceEdge = newMinFence;
114
+ }
115
+
116
+ private _CheckIdsHasFocusable(ids: Array<number>) {
117
+ const templateList = this.templateList;
118
+ let found = false;
119
+ for (let i = 0; i < ids.length; i++) {
120
+ const id = ids[i];
121
+ if (templateList[id].focusable) {
122
+ found = true;
123
+ break;
124
+ }
125
+ }
126
+ return found;
127
+ }
128
+
129
+
130
+ private _GetValidNeighborIndex(srcItemId: number, lastItemId: number, neighborDirection: string) {
131
+ const templateList = this.templateList;
132
+ let validId = -1;
133
+ const item = templateList[lastItemId];
134
+ const srcItem = templateList[srcItemId];
135
+ let firstIds: Array<number>;
136
+ let secondIds: Array<number>;
137
+
138
+ if (neighborDirection === "top") {
139
+ // 查找左侧邻居节点
140
+ secondIds = item.neighborIndexList.left;
141
+ firstIds = item.neighborIndexList.right;
142
+ } else {
143
+ // left, right
144
+ secondIds = item.neighborIndexList.right;
145
+ firstIds = item.neighborIndexList.left;
146
+ }
147
+
148
+ if (firstIds.length > 0) {
149
+ for (let i = 0; i < firstIds.length; i++) {
150
+ const id = firstIds[i];
151
+ const checkItem = templateList[id];
152
+ if (
153
+ checkItem.focusable &&
154
+ ((neighborDirection === "top" &&
155
+ checkItem.top + checkItem.height <= srcItem.top) ||
156
+ (neighborDirection === "bottom" &&
157
+ checkItem.top >= srcItem.top + srcItem.height))
158
+ ) {
159
+ // 并且在srcItemId之上或之下
160
+ validId = id;
161
+ break;
162
+ }
163
+ }
164
+ }
165
+ if (validId === -1) {
166
+ if (secondIds.length > 0) {
167
+ for (let i = 0; i < secondIds.length; i++) {
168
+ const id = secondIds[i];
169
+ const checkItem = templateList[id];
170
+ if (
171
+ checkItem.focusable &&
172
+ ((neighborDirection === "top" &&
173
+ checkItem.top + checkItem.height <= srcItem.top) ||
174
+ (neighborDirection === "bottom" &&
175
+ checkItem.top >= srcItem.top + srcItem.height))
176
+ ) {
177
+ // 并且在srcItemId之上或之下
178
+ validId = id;
179
+ break;
180
+ }
181
+ }
182
+ }
183
+ }
184
+ return validId;
185
+ }
186
+
187
+ private _FindFocusableNeighbor(direction: string, item: TemplateItem) {
188
+ if (
189
+ item.neighborIndexList[direction].length > 0 &&
190
+ !this._CheckIdsHasFocusable(item.neighborIndexList[direction])
191
+ ) {
192
+ const lastItemId =
193
+ item.neighborIndexList[direction][
194
+ item.neighborIndexList[direction].length - 1
195
+ ];
196
+ const idx = this._GetValidNeighborIndex(item.id, lastItemId, direction); // 查找其左右邻居
197
+ if (idx !== -1) {
198
+ item.neighborIndexList[direction] = []; // 清除无效项
199
+ item.neighborIndexList[direction].push(idx);
200
+ }
201
+ }
202
+ }
203
+
204
+ public addItem(itemObj: MeasureInfo): TemplateItem {
205
+ const templateItem = MetroTemplate.getTemplateItem(itemObj);
206
+ // Layout template items
207
+ const fenceStack = this._FenceStack;
208
+ const itemWidth = templateItem.width;
209
+ const itemHeight = templateItem.height;
210
+ // Add view to contents view layer
211
+ let xPos: number;
212
+ let yPos: number;
213
+ let xWidth: number;
214
+ let yWidth: number;
215
+ let itemKeyPos = 0;
216
+ let itemSecondPos = 0;
217
+
218
+ let itemKeyWidth = 0;
219
+ let itemSecondWidth = 0;
220
+ let itemKeyMargin = 0;
221
+ let itemSecondMargin = 0;
222
+ if (this.layoutType === "relative") {
223
+ if (this.direction === VERTICAL) {
224
+ itemKeyWidth = itemWidth;
225
+ itemSecondWidth = itemHeight;
226
+ itemKeyMargin = templateItem.marginRight;
227
+ itemSecondMargin = templateItem.marginBottom;
228
+ } else {
229
+ itemKeyWidth = itemHeight;
230
+ itemSecondWidth = itemWidth;
231
+ itemKeyMargin = templateItem.marginBottom;
232
+ itemSecondMargin = templateItem.marginRight;
233
+ }
234
+ // Find capability fence in stack
235
+ let topFence: Fence | null = null;
236
+ let gapInfo: GapInfo | null = null;
237
+ while (fenceStack.length > 0) {
238
+ const checkingFence = fenceStack[fenceStack.length - 1];
239
+ gapInfo = checkingFence.HasGapFor(itemKeyWidth + itemKeyMargin);
240
+ if (gapInfo !== null) {
241
+ topFence = checkingFence;
242
+ break;
243
+ }
244
+ fenceStack.pop();
245
+ }
246
+
247
+ // Layout the item in top fence
248
+ if (gapInfo !== null && topFence != null) {
249
+ itemKeyPos = gapInfo.GapStart;
250
+ itemSecondPos = topFence.AheadOffset;
251
+ } else {
252
+ throw new Error(
253
+ `${TAG} item size ${itemWidth}x${itemHeight} is greater than widget line max ${this.lineMax}`
254
+ );
255
+ // console.error(TAG, "ERROR: coding error, header full gap fence lost");
256
+ }
257
+
258
+ if (this.direction === VERTICAL) {
259
+ xPos = itemKeyPos;
260
+ yPos = itemSecondPos;
261
+ xWidth = itemKeyWidth;
262
+ yWidth = itemSecondWidth;
263
+ } else {
264
+ xPos = itemSecondPos;
265
+ yPos = itemKeyPos;
266
+ xWidth = itemSecondWidth;
267
+ yWidth = itemKeyWidth;
268
+ }
269
+ } else {
270
+ xPos = templateItem.left;
271
+ yPos = templateItem.top;
272
+ xWidth = itemWidth;
273
+ yWidth = itemHeight;
274
+ }
275
+
276
+ templateItem.left = xPos;
277
+ templateItem.top = yPos;
278
+ templateItem.centerXPos = Math.floor(xPos + xWidth / 2) - 1;
279
+ templateItem.centerYPos = Math.floor(yPos + yWidth / 2) - 1;
280
+ //分页相关的计算
281
+ let curPageNumber = 0;
282
+ let curPageHeadIndex = 0;
283
+ if (this.templateList.length > 0) {
284
+ const lastItem = this.templateList[this.templateList.length - 1];
285
+ curPageNumber = lastItem.pageNumber;
286
+ curPageHeadIndex = lastItem.pageHeadIndex;
287
+ let positionKey = "left";
288
+ let sizeKey = "width";
289
+ if (this.direction === VERTICAL) {
290
+ positionKey = "top";
291
+ sizeKey = "height";
292
+ }
293
+ const curPageStart =
294
+ this.templateList[curPageHeadIndex][positionKey];
295
+ if (
296
+ templateItem[positionKey] + templateItem[sizeKey] >
297
+ curPageStart + this.pageSize
298
+ ) {
299
+ //新的页
300
+ curPageNumber++;
301
+ curPageHeadIndex = this.templateList.length;
302
+ }
303
+ }
304
+
305
+ templateItem.pageNumber = curPageNumber;
306
+ templateItem.pageHeadIndex = curPageHeadIndex;
307
+ this.addTemplateItem(templateItem);
308
+ // 计算相邻项
309
+ this._TryCalculateNeighborItem(
310
+ this.templateList.length - 1,
311
+ templateItem.left,
312
+ templateItem.top,
313
+ templateItem.width + templateItem.marginRight,
314
+ templateItem.height + templateItem.marginBottom
315
+ );
316
+
317
+ const itemSecondEndPos =
318
+ itemSecondPos + itemSecondWidth + itemSecondMargin - 1;
319
+
320
+ // Ensure header fence
321
+ if (fenceStack[0].AheadOffset <= itemSecondEndPos) {
322
+ // Need new header fence
323
+ fenceStack.splice(
324
+ 0,
325
+ 0,
326
+ new Fence(0, 0, this.lineMax, itemSecondEndPos + 1)
327
+ );
328
+ }
329
+
330
+ // Mark gap used in fence stack
331
+ let endFenceExisted = false;
332
+ let farAwayEndFenceExisted = false;
333
+ let oneAheadLastFenceIdx = -1;
334
+ for (let fenceIdx = fenceStack.length - 1; fenceIdx >= 0; fenceIdx--) {
335
+ if (fenceStack[fenceIdx].AheadOffset <= itemSecondEndPos) {
336
+ if (fenceStack[fenceIdx].AheadOffset === itemSecondEndPos)
337
+ endFenceExisted = true;
338
+ const leftGaps = fenceStack[fenceIdx].MarkGapUsed(
339
+ itemKeyPos,
340
+ itemKeyWidth + itemKeyMargin
341
+ );
342
+ if (leftGaps === 0) {
343
+ // remove fence
344
+ fenceStack.splice(fenceIdx, 1);
345
+ }
346
+ } else {
347
+ if (fenceStack[fenceIdx].AheadOffset === itemSecondEndPos + 1)
348
+ farAwayEndFenceExisted = true;
349
+ oneAheadLastFenceIdx = fenceIdx;
350
+ break;
351
+ }
352
+ }
353
+
354
+ const fenceAheadOffset = itemSecondEndPos;
355
+ // Create fences
356
+ if (!endFenceExisted) {
357
+ // Build fence edge which close to the beginning
358
+ if (oneAheadLastFenceIdx + 1 <= fenceStack.length - 1) {
359
+ const fence =
360
+ fenceStack[oneAheadLastFenceIdx + 1].Fork(fenceAheadOffset);
361
+ fenceStack.splice(oneAheadLastFenceIdx + 1, 0, fence);
362
+ }
363
+ }
364
+ if (!farAwayEndFenceExisted) {
365
+ // Build fence edge which far away from the beginning
366
+ if (oneAheadLastFenceIdx > 0) {
367
+ const fence = fenceStack[oneAheadLastFenceIdx].Fork(
368
+ fenceAheadOffset + 1
369
+ );
370
+ fenceStack.splice(oneAheadLastFenceIdx + 1, 0, fence);
371
+ }
372
+ }
373
+ return templateItem;
374
+ }
375
+
376
+ /** get next item functions */
377
+ public getNextItem(baseId: number, vOffset: number,
378
+ hOffset: number, moveType: number): TemplateItem | null {
379
+ if (vOffset === 0 && hOffset === 0)
380
+ console.log("GetNextItem(): offset is 0");
381
+ // Forge.ThrowError("GetNextItem(): offset is 0");
382
+
383
+ if (vOffset !== 0 && hOffset !== 0) {
384
+ console.log(
385
+ "GetNextItem(): not support change vertical and horizontal at the same time"
386
+ );
387
+ // Forge.ThrowError("GetNextItem(): not support change vertical and horizontal and the same time");
388
+ }
389
+
390
+ const itemIndex = this.id2Index(baseId);
391
+ const offset = vOffset !== 0 ? vOffset : hOffset;
392
+ let nextTemplateItem = this.getNextItemInner(
393
+ itemIndex,
394
+ offset,
395
+ vOffset !== 0
396
+ );
397
+ const isVertical = this.direction === VERTICAL;
398
+
399
+ if (nextTemplateItem === null) {
400
+ // 在次方向的焦点移动才会loop
401
+ if (
402
+ isVertical &&
403
+ moveType & FocusMoveType.ROW_LOOP &&
404
+ hOffset != 0
405
+ ) {
406
+ nextTemplateItem = this._GetNextLoopItem(
407
+ itemIndex,
408
+ hOffset,
409
+ isVertical
410
+ );
411
+ } else if (
412
+ !isVertical &&
413
+ moveType & FocusMoveType.COLUMN_LOOP &&
414
+ vOffset != 0
415
+ ) {
416
+ nextTemplateItem = this._GetNextLoopItem(
417
+ itemIndex,
418
+ vOffset,
419
+ isVertical
420
+ );
421
+ }
422
+ }
423
+
424
+ if (nextTemplateItem === null) {
425
+ // 临近行的焦点移动
426
+ if (
427
+ vOffset != 0 &&
428
+ moveType & FocusMoveType.ROW_FIND_NEAR
429
+ ) {
430
+ nextTemplateItem = this._GetNearLineItem(itemIndex, vOffset, true);
431
+ } else if (
432
+ hOffset != 0 &&
433
+ moveType & FocusMoveType.COLUMN_FIND_NEAR
434
+ ) {
435
+ nextTemplateItem = this._GetNearLineItem(itemIndex, hOffset, false);
436
+ }
437
+ }
438
+
439
+ return nextTemplateItem;
440
+ }
441
+
442
+
443
+ private getNextItemInner(index: number, offset: number, vertical: boolean) {
444
+ const templateList = this.templateList;
445
+ const baseItem = templateList[index];
446
+ let checkAnchor = -1;
447
+ if (baseItem.findNextAnchor) {
448
+ //有自定义的 anchor
449
+ // TODO 优化
450
+ if (vertical) {
451
+ if (offset > 0) {
452
+ if (baseItem.findNextAnchor.bottom) {
453
+ checkAnchor =
454
+ baseItem.left +
455
+ baseItem.findNextAnchor.bottom *
456
+ (baseItem.width + baseItem.marginRight);
457
+ }
458
+ } else {
459
+ if (baseItem.findNextAnchor.top) {
460
+ checkAnchor =
461
+ baseItem.left +
462
+ baseItem.findNextAnchor.top *
463
+ (baseItem.width + baseItem.marginRight);
464
+ }
465
+ }
466
+ } else {
467
+ if (offset > 0) {
468
+ if (baseItem.findNextAnchor.right) {
469
+ checkAnchor =
470
+ baseItem.top +
471
+ baseItem.findNextAnchor.right *
472
+ (baseItem.height + baseItem.marginBottom);
473
+ }
474
+ } else {
475
+ if (baseItem.findNextAnchor.left) {
476
+ checkAnchor =
477
+ baseItem.top +
478
+ baseItem.findNextAnchor.left *
479
+ (baseItem.height + baseItem.marginBottom);
480
+ }
481
+ }
482
+ }
483
+ }
484
+ if (checkAnchor < 0) {
485
+ checkAnchor = vertical ? baseItem.centerXPos : baseItem.centerYPos;
486
+ }
487
+ const posKey = vertical ? "left" : "top";
488
+ const sizeKey = vertical ? "width" : "height";
489
+ let direction = vertical ? "top" : "left";
490
+ let pathHistoryDirection = vertical ? "bottom" : "right";
491
+ // Normalize offset
492
+ if (offset > 0) {
493
+ pathHistoryDirection = vertical ? "top" : "left";
494
+ direction = vertical ? "bottom" : "right";
495
+ }
496
+ const notRecordHistoryDirection = vertical ? "bottom" : "right";
497
+ const notRecordHistoryOrient = vertical ? HORIZONTAL : VERTICAL;
498
+ let lastItemIndex = templateList.length - 1;
499
+ const orientType = this.direction;
500
+ // 查找有效的最后项
501
+ for (let i = templateList.length - 1; i >= 0; i--) {
502
+ const lastItem = templateList[i];
503
+ if (lastItem.focusable) {
504
+ lastItemIndex = i;
505
+ break;
506
+ }
507
+ }
508
+ let found = false;
509
+ let nextIndex = -1;
510
+ let pathHistoryIndex = -1;
511
+ if (this.supportHistoryPath && !baseItem.findNextAnchor) {
512
+ pathHistoryIndex = templateList[index].pathHistory[direction];
513
+ }
514
+ if (pathHistoryIndex < 0) {
515
+ // 无历史路径
516
+ // 获得邻近点列表
517
+ let neighborList = templateList[index].neighborIndexList[direction];
518
+ if (templateList[index].tmpNeighborIndexList[direction].length > 0) {
519
+ neighborList = neighborList.concat(
520
+ templateList[index].tmpNeighborIndexList[direction]
521
+ );
522
+ }
523
+ if (neighborList.length === 1) {
524
+ nextIndex = neighborList[0];
525
+ if (
526
+ lastItemIndex !== index ||
527
+ direction !== notRecordHistoryDirection ||
528
+ orientType !== notRecordHistoryOrient
529
+ ) {
530
+ // horizontal lastItemIndex bottom 不记历史路径
531
+ templateList[index].pathHistory[direction] = nextIndex; // 更新历史路径
532
+ templateList[nextIndex].pathHistory[pathHistoryDirection] = index; // 更新历史路径
533
+ }
534
+ found = true;
535
+ } else {
536
+ for (let i = 0; i < neighborList.length; i++) {
537
+ nextIndex = neighborList[i];
538
+ const checkingItem = templateList[nextIndex];
539
+ if (
540
+ checkAnchor <=
541
+ checkingItem[posKey] + checkingItem[sizeKey] - 1 &&
542
+ checkAnchor >= checkingItem[posKey]
543
+ ) {
544
+ if (
545
+ lastItemIndex !== index ||
546
+ direction !== notRecordHistoryDirection ||
547
+ orientType !== notRecordHistoryOrient
548
+ ) {
549
+ // horizontal lastItemIndex bottom 不记历史路径
550
+ templateList[index].pathHistory[direction] = nextIndex; // 更新历史路径
551
+ templateList[nextIndex].pathHistory[pathHistoryDirection] =
552
+ index; // 更新历史路径
553
+ }
554
+ found = true;
555
+ break;
556
+ }
557
+ }
558
+ // 若未找到中间位置的项目
559
+ // 若找到了项目,但是该项为非 focusable
560
+ // 则使用neighbor list中focusable项
561
+ if (!found || !templateList[nextIndex].focusable) {
562
+ for (let i = 0; i < neighborList.length; i++) {
563
+ const neighborIndex = neighborList[i];
564
+ const checkingItem = templateList[neighborIndex];
565
+ if (checkingItem.focusable) {
566
+ if (
567
+ lastItemIndex !== index ||
568
+ direction !== notRecordHistoryDirection ||
569
+ orientType !== notRecordHistoryOrient
570
+ ) {
571
+ // horizontal lastItemIndex bottom 不记历史路径
572
+ templateList[index].pathHistory[direction] = nextIndex; // 更新历史路径
573
+ templateList[nextIndex].pathHistory[pathHistoryDirection] =
574
+ index; // 更新历史路径
575
+ }
576
+ nextIndex = neighborIndex;
577
+ found = true;
578
+ break;
579
+ }
580
+ }
581
+ }
582
+ }
583
+ } else {
584
+ nextIndex = pathHistoryIndex;
585
+ if (
586
+ lastItemIndex !== index ||
587
+ direction !== notRecordHistoryDirection ||
588
+ orientType !== notRecordHistoryOrient
589
+ ) {
590
+ // horizontal lastItemIndex bottom 不记历史路径
591
+ templateList[nextIndex].pathHistory[pathHistoryDirection] = index; // 更新历史路径
592
+ }
593
+ found = true;
594
+ }
595
+
596
+ if (found) {
597
+ if (!templateList[nextIndex].focusable) {
598
+ // 如果是占位符,则查找占位符的下一个
599
+ return this.getNextItemInner(nextIndex, offset, vertical);
600
+ }
601
+ return templateList[nextIndex];
602
+ }
603
+ return null;
604
+ }
605
+
606
+ private _GetItemDistance(item1: TemplateItem, item2: TemplateItem) {
607
+ return (
608
+ Math.pow(item1.centerXPos - item2.centerXPos, 2) +
609
+ Math.pow(item1.centerYPos - item2.centerYPos, 2)
610
+ );
611
+ }
612
+
613
+ private _GetTotalNeighbor(index: number, direction: string) {
614
+ const item = this.templateList[index];
615
+ return item.neighborIndexList[direction].concat(
616
+ item.tmpNeighborIndexList[direction]
617
+ );
618
+ }
619
+
620
+ private _GetNearestItem(targetItemIndex: number, itemIndexList: Array<number>) {
621
+ let nearestIndex = -1,
622
+ minDistance = Number.POSITIVE_INFINITY;
623
+ let targetItem = this.templateList[targetItemIndex];
624
+ for (let i of itemIndexList) {
625
+ const d = this._GetItemDistance(this.templateList[i], targetItem);
626
+ if (d < minDistance) {
627
+ nearestIndex = i;
628
+ minDistance = d;
629
+ }
630
+ }
631
+ return nearestIndex;
632
+ }
633
+
634
+ private _GetNextLoopItem(index: number, offset: number, isVertical: boolean) {
635
+ const templateList = this.templateList;
636
+ const baseItem = templateList[index];
637
+ let searchDirection = isVertical ? "right" : "bottom";
638
+ let startDirection = isVertical ? "top" : "left";
639
+ if (offset > 0) {
640
+ searchDirection = isVertical ? "left" : "top";
641
+ startDirection = isVertical ? "bottom" : "right";
642
+ }
643
+ let targetIndex = -1;
644
+ let focusableTarget = -1;
645
+ let startIndex = -1;
646
+ let neighborList = [];
647
+ //寻找下一列的起始item
648
+ neighborList = this._GetTotalNeighbor(index, searchDirection);
649
+ while (startIndex < 0 && neighborList.length > 0) {
650
+ for (let i of neighborList) {
651
+ if (this._GetTotalNeighbor(i, startDirection).length > 0) {
652
+ startIndex = i;
653
+ break;
654
+ } else {
655
+ //TODO 是否要用递归
656
+ neighborList = this._GetTotalNeighbor(i, searchDirection);
657
+ }
658
+ }
659
+ }
660
+ if (startIndex >= 0) {
661
+ neighborList = this._GetTotalNeighbor(startIndex, startDirection);
662
+
663
+ //寻找最近的neighbor
664
+ let nearestIndex = -1,
665
+ minDistance = Number.POSITIVE_INFINITY;
666
+ for (let i of neighborList) {
667
+ const d = this._GetItemDistance(templateList[i], baseItem);
668
+ if (d < minDistance) {
669
+ nearestIndex = i;
670
+ minDistance = d;
671
+ }
672
+ }
673
+
674
+ neighborList = this._GetTotalNeighbor(nearestIndex, searchDirection);
675
+ while (neighborList.length > 0) {
676
+ targetIndex = this._GetNearestItem(nearestIndex, neighborList);
677
+ if (templateList[targetIndex].focusable) {
678
+ focusableTarget = targetIndex;
679
+ }
680
+ neighborList = this._GetTotalNeighbor(targetIndex, searchDirection);
681
+ }
682
+ if (focusableTarget >= 0) {
683
+ return templateList[focusableTarget];
684
+ } else if (targetIndex >= 0) {
685
+ return this._GetNextLoopItem(targetIndex, offset, isVertical);
686
+ }
687
+ }
688
+ return null;
689
+ }
690
+
691
+ //获取临近行的item
692
+ private _GetNearLineItem(index: number, offset: number, isVertical: boolean) {
693
+ const templateList = this.templateList;
694
+ let searchDirection = isVertical ? "right" : "bottom";
695
+ let startDirection = isVertical ? "top" : "left";
696
+ if (offset > 0) {
697
+ searchDirection = isVertical ? "left" : "top";
698
+ startDirection = isVertical ? "bottom" : "right";
699
+ }
700
+ let startIndex = -1;
701
+ let neighborList = [];
702
+ //寻找下一列的起始item
703
+ neighborList = this._GetTotalNeighbor(index, searchDirection);
704
+ while (startIndex < 0 && neighborList.length > 0) {
705
+ for (let i of neighborList) {
706
+ const n = this._GetTotalNeighbor(i, startDirection);
707
+ if (n.length > 0) {
708
+ return templateList[this._GetNearestItem(index, n)];
709
+ } else {
710
+ //TODO 是否要用递归
711
+ neighborList = this._GetTotalNeighbor(i, searchDirection);
712
+ }
713
+ }
714
+ }
715
+ return null;
716
+ }
717
+
718
+ public updateItemSize(index: number, newSize: { width: number, height: number }): void {
719
+ console.error(TAG, "updateItemSize is not supported.");
720
+ }
721
+
722
+ /** get visible item list functions */
723
+ public getVisibleItemList(
724
+ visibleStart: number,
725
+ visibleEnd: number,
726
+ baseId: number): [number, number] {
727
+ if (visibleStart > visibleEnd) {
728
+ // No visible item
729
+ return [-1, -1];
730
+ }
731
+ //取整以避免starline和endline找不到骑线的item的bug
732
+ visibleStart = Math.round(visibleStart);
733
+ visibleEnd = Math.round(visibleEnd);
734
+ let searchBaseItemIdx = this.id2Index(baseId);
735
+ if (typeof searchBaseItemIdx === "undefined") searchBaseItemIdx = 0;
736
+ const commonData: VisibleSearchConfigCommon = {
737
+ resultStartIdx: -1,
738
+ resultEndIdx: -1,
739
+ enableBackwardSearching: true,
740
+ enableForwardSearching: true,
741
+ checkingStartLinePos: visibleStart,
742
+ checkingEndLinePos: visibleEnd,
743
+ };
744
+ const backwardConfigure: VisibleSearchConfig = {
745
+ searchDirect: -1,
746
+ baseIdx: searchBaseItemIdx,
747
+ checkingStartLineRanges: new RangesModel(0, 0),
748
+ startLineRangesFulled: false,
749
+ latestOverStartLineIdx: -1,
750
+ checkingEndLineRanges: new RangesModel(0, 0),
751
+ endLineRangesFulled: false,
752
+ latestOverEndLineIdx: -1,
753
+ endSearchingIndex: 0,
754
+ firstStartLineBlockIdx: -1,
755
+ firstEndLineBlockIdx: -1,
756
+ searchToBorder: false,
757
+ common: commonData,
758
+ };
759
+ const forwardConfigure: VisibleSearchConfig = {
760
+ searchDirect: 1,
761
+ baseIdx: searchBaseItemIdx,
762
+ checkingStartLineRanges: new RangesModel(0, 0),
763
+ startLineRangesFulled: false,
764
+ latestOverStartLineIdx: -1,
765
+ checkingEndLineRanges: new RangesModel(0, 0),
766
+ endLineRangesFulled: false,
767
+ latestOverEndLineIdx: -1,
768
+ endSearchingIndex: this.templateList.length - 1,
769
+ firstStartLineBlockIdx: -1,
770
+ firstEndLineBlockIdx: -1,
771
+ searchToBorder: false,
772
+ common: commonData,
773
+ };
774
+
775
+ const isTemplateVertical = this.direction === VERTICAL;
776
+ let offset = 0;
777
+ while (
778
+ commonData.enableBackwardSearching ||
779
+ commonData.enableForwardSearching
780
+ ) {
781
+ if (commonData.enableBackwardSearching) {
782
+ this._DoSearching(backwardConfigure, -offset, isTemplateVertical);
783
+ }
784
+
785
+ if (commonData.enableForwardSearching) {
786
+ this._DoSearching(forwardConfigure, offset, isTemplateVertical);
787
+ }
788
+ offset++;
789
+ }
790
+ // console.log(
791
+ // "GetVisibleItemList [" +
792
+ // JSON.stringify(commonData) +
793
+ // ", " +
794
+ // JSON.stringify(backwardConfigure) +
795
+ // ", " +
796
+ // JSON.stringify(forwardConfigure) +
797
+ // "]"
798
+ // );
799
+
800
+ if (commonData.resultStartIdx < 0) {
801
+ // When start index not found
802
+ // Use just over start line block as start idx
803
+ if (
804
+ backwardConfigure.latestOverStartLineIdx >= 0 &&
805
+ backwardConfigure.firstStartLineBlockIdx >= 0
806
+ )
807
+ commonData.resultStartIdx = backwardConfigure.latestOverStartLineIdx;
808
+ else if (forwardConfigure.firstStartLineBlockIdx >= 0)
809
+ commonData.resultStartIdx = forwardConfigure.firstStartLineBlockIdx;
810
+ }
811
+
812
+ if (commonData.resultEndIdx < 0) {
813
+ // When end index not found
814
+ // Use just over end line block as end idx
815
+ if (
816
+ forwardConfigure.latestOverEndLineIdx >= 0 &&
817
+ forwardConfigure.firstEndLineBlockIdx >= 0
818
+ )
819
+ commonData.resultEndIdx = forwardConfigure.latestOverEndLineIdx;
820
+ else if (backwardConfigure.firstEndLineBlockIdx >= 0)
821
+ commonData.resultEndIdx = backwardConfigure.firstEndLineBlockIdx;
822
+ }
823
+ // Recheck if whole contents inside the range
824
+ if (commonData.resultStartIdx < 0 && commonData.resultEndIdx < 0) {
825
+ // Check if whole contents inside the range
826
+ const firstItem = this.templateList[0];
827
+ const firstSecondPos = isTemplateVertical
828
+ ? firstItem.top
829
+ : firstItem.left;
830
+ const lastItem = this.templateList[this.templateList.length - 1];
831
+ const lastSecondPos = isTemplateVertical
832
+ ? lastItem.top
833
+ : lastItem.left;
834
+ const lastSecondWidth = isTemplateVertical
835
+ ? lastItem.height
836
+ : lastItem.width;
837
+ if (
838
+ firstSecondPos > visibleStart &&
839
+ lastSecondPos + lastSecondWidth < visibleEnd
840
+ ) {
841
+ commonData.resultStartIdx = 0;
842
+ commonData.resultEndIdx = this.templateList.length - 1;
843
+ }
844
+ }
845
+ let ret: [number, number] = [-1, -1];
846
+ if (commonData.resultStartIdx >= 0 || commonData.resultEndIdx >= 0) {
847
+ const startIdx =
848
+ commonData.resultStartIdx >= 0 ? commonData.resultStartIdx : 0;
849
+ const endIdx =
850
+ commonData.resultEndIdx >= 0
851
+ ? commonData.resultEndIdx
852
+ : this.templateList.length - 1;
853
+ ret = [startIdx, endIdx];
854
+ }
855
+ return ret;
856
+ }
857
+
858
+ private _DoSearching(configures: VisibleSearchConfig, offset: number, beVerticalTemplate: boolean) {
859
+ const checkItem = this.templateList[configures.baseIdx + offset];
860
+ const keyPos = beVerticalTemplate ? checkItem.left : checkItem.top;
861
+ const keyWidth = beVerticalTemplate
862
+ ? checkItem.width + checkItem.marginRight
863
+ : checkItem.height + checkItem.marginBottom;
864
+ const keyStart = Math.round(keyPos);
865
+ const keyEnd = Math.round(keyPos + keyWidth - 1);
866
+ const secondPos = beVerticalTemplate ? checkItem.top : checkItem.left;
867
+ const secondWidth = beVerticalTemplate
868
+ ? checkItem.height + checkItem.marginBottom
869
+ : checkItem.width + checkItem.marginRight;
870
+ const secondStart = Math.round(secondPos);
871
+ const secondEnd = Math.round(secondPos + secondWidth - 1);
872
+ // console.log(
873
+ // "idx=" +
874
+ // (configures.baseIdx + offset) +
875
+ // " pos=" +
876
+ // secondPos +
877
+ // " posend=" +
878
+ // (secondPos + secondWidth - 1) +
879
+ // " linePos=" +
880
+ // configures.common.checkingStartLinePos
881
+ // );
882
+
883
+ if (
884
+ configures.common.resultStartIdx < 0 &&
885
+ !configures.startLineRangesFulled
886
+ ) {
887
+ let isRangeChanged = false;
888
+ if (
889
+ secondStart <= configures.common.checkingStartLinePos &&
890
+ secondEnd >= configures.common.checkingStartLinePos
891
+ ) {
892
+ // Overlap the visbible start line
893
+ configures.latestOverStartLineIdx = configures.baseIdx + offset;
894
+ if (configures.firstStartLineBlockIdx < 0)
895
+ configures.firstStartLineBlockIdx = configures.baseIdx + offset;
896
+ configures.checkingStartLineRanges.Merge(
897
+ new SingleRangeModel(keyStart, keyEnd)
898
+ );
899
+ isRangeChanged = true;
900
+ } else if (
901
+ configures.searchDirect < 0 &&
902
+ secondEnd <= configures.common.checkingStartLinePos
903
+ ) {
904
+ // above the visible start line
905
+ configures.checkingStartLineRanges.Merge(
906
+ new SingleRangeModel(keyStart, keyEnd)
907
+ );
908
+ isRangeChanged = true;
909
+ }
910
+ }
911
+ // console.log(
912
+ // "idx=" +
913
+ // (configures.baseIdx + offset) +
914
+ // " pos=" +
915
+ // secondPos +
916
+ // " posend=" +
917
+ // (secondPos + secondWidth - 1) +
918
+ // " linePos=" +
919
+ // configures.common.checkingEndLinePos
920
+ // );
921
+ if (configures.common.resultEndIdx < 0 && !configures.endLineRangesFulled) {
922
+ let isRangeChanged = false;
923
+ if (
924
+ secondStart <= configures.common.checkingEndLinePos &&
925
+ secondEnd >= configures.common.checkingEndLinePos
926
+ ) {
927
+ // Overlap the visbible end line
928
+ configures.latestOverEndLineIdx = configures.baseIdx + offset;
929
+ if (configures.firstEndLineBlockIdx < 0)
930
+ configures.firstEndLineBlockIdx = configures.baseIdx + offset;
931
+ configures.checkingEndLineRanges.Merge(
932
+ new SingleRangeModel(keyStart, keyEnd)
933
+ );
934
+ isRangeChanged = true;
935
+ } else if (
936
+ configures.searchDirect > 0 &&
937
+ secondStart >= configures.common.checkingEndLinePos
938
+ ) {
939
+ // below the visible end line
940
+ configures.checkingEndLineRanges.Merge(
941
+ new SingleRangeModel(keyStart, keyEnd)
942
+ );
943
+ isRangeChanged = true;
944
+ }
945
+ }
946
+ if (configures.baseIdx + offset === configures.endSearchingIndex) {
947
+ if (configures.searchDirect > 0) {
948
+ configures.common.enableForwardSearching = false;
949
+ } else {
950
+ configures.common.enableBackwardSearching = false;
951
+ }
952
+ configures.searchToBorder = true;
953
+ }
954
+ }
955
+
956
+ /** neighbor calculate functions */
957
+ private _TryCalculateNeighborItem(index: number, xPos: number, yPos: number, width: number, height: number) {
958
+ const curNotOccupiedEdge: EdgeInfo = {
959
+ index,
960
+ edgeObj: {
961
+ leftEdge: {
962
+ position: xPos,
963
+ range: new RangesModel(yPos, yPos + height - 1),
964
+ },
965
+ rightEdge: {
966
+ position: xPos + width,
967
+ range: new RangesModel(yPos, yPos + height - 1),
968
+ },
969
+ topEdge: {
970
+ position: yPos,
971
+ range: new RangesModel(xPos, xPos + width - 1),
972
+ },
973
+ bottomEdge: {
974
+ position: yPos + height,
975
+ range: new RangesModel(xPos, xPos + width - 1),
976
+ },
977
+ },
978
+ };
979
+ // 边界处理,重置leftEdge和topEdge;
980
+ if (xPos === 0) {
981
+ curNotOccupiedEdge.edgeObj.leftEdge = null;
982
+ }
983
+ if (yPos === 0) {
984
+ curNotOccupiedEdge.edgeObj.topEdge = null;
985
+ }
986
+ let newFance = false;
987
+ // 边界处理,重置bootomEdge和rightEdge
988
+ const widthMax = this.lineMax;
989
+ let newFenceEdgeStart_x = this._FenceEdge.StartX;
990
+ let newFenceEdgeStart_y = this._FenceEdge.StartY;
991
+ if (this.direction === VERTICAL) {
992
+ if (xPos + width === widthMax) {
993
+ curNotOccupiedEdge.edgeObj.rightEdge = null;
994
+ }
995
+ // 判断FenceEdge,是否产生新的fence
996
+ if (yPos > this._FenceEdge.StartY) {
997
+ // 产生新的Fence
998
+ newFance = true;
999
+ newFenceEdgeStart_y = yPos;
1000
+ }
1001
+ } else {
1002
+ if (yPos + height === widthMax) {
1003
+ curNotOccupiedEdge.edgeObj.bottomEdge = null;
1004
+ }
1005
+ // 判断FenceEdge,是否产生新的fence
1006
+ if (xPos > this._FenceEdge.StartX) {
1007
+ // 产生新的Fence
1008
+ newFance = true;
1009
+ newFenceEdgeStart_x = xPos;
1010
+ }
1011
+ }
1012
+ this._FenceEdge.StartX = newFenceEdgeStart_x;
1013
+ this._FenceEdge.StartY = newFenceEdgeStart_y;
1014
+ // 添加节点
1015
+ this._NotOccupiedEdgeList.push(curNotOccupiedEdge);
1016
+ // 如果产生新fence,则对NotOccupiedEdgeList进行相邻节点计算
1017
+ if (newFance) {
1018
+ this._CalculateNeighborItem();
1019
+ }
1020
+ }
1021
+
1022
+ private _CalculateNeighborItem(tmpNeighbor: boolean = false) {
1023
+ const templateList = this.templateList;
1024
+ // 更新邻近边,以及未占用的边
1025
+ for (let i = 0; i < this._NotOccupiedEdgeList.length; i++) {
1026
+ const crossItem = this._NotOccupiedEdgeList[i];
1027
+ const crossEdgeObj = crossItem.edgeObj;
1028
+ const crossItemIndex = crossItem.index;
1029
+ if (
1030
+ crossEdgeObj.leftEdge == null &&
1031
+ crossEdgeObj.rightEdge == null &&
1032
+ crossEdgeObj.topEdge == null &&
1033
+ crossEdgeObj.bottomEdge == null
1034
+ ) {
1035
+ this._NotOccupiedEdgeList.splice(i, 1);
1036
+ i--;
1037
+ continue;
1038
+ }
1039
+ if (this.direction === VERTICAL) {
1040
+ // 如果下边被占用,则将其从列表中删除
1041
+ // if (crossEdgeObj.bottomEdge === null) {
1042
+ // this._NotOccupiedEdgeList.splice(i, 1);
1043
+ // i--;
1044
+ // continue;
1045
+ // }
1046
+ // 临时邻居不触发fence edge
1047
+ // 超过fenceedge范围,不作为对比相
1048
+ if (
1049
+ !tmpNeighbor && crossEdgeObj.bottomEdge != null &&
1050
+ crossEdgeObj.bottomEdge.position > this._FenceEdge.StartY
1051
+ ) {
1052
+ continue;
1053
+ }
1054
+ } else {
1055
+ // 如果右边被占用,则将其从列表中删除
1056
+ // if (crossEdgeObj.rightEdge === null) {
1057
+ // this._NotOccupiedEdgeList.splice(i, 1);
1058
+ // i--;
1059
+ // continue;
1060
+ // }
1061
+ // 临时邻居不触发fence edge
1062
+ // 超过fenceedge范围,不作为对比相
1063
+ if (
1064
+ !tmpNeighbor && crossEdgeObj.rightEdge &&
1065
+ crossEdgeObj.rightEdge.position > this._FenceEdge.StartX
1066
+ ) {
1067
+ continue;
1068
+ }
1069
+ }
1070
+
1071
+ // 查找与交叉项上下左右最近的各项目,做交叉
1072
+ const neighbor = this._FindNearestNeighbor(crossItem);
1073
+ // console.log(
1074
+ // "%c cchtest find nearest neighbor",
1075
+ // "color:red;",
1076
+ // crossItem.index,
1077
+ // {
1078
+ // left: this._NotOccupiedEdgeList[neighbor.left]?.index,
1079
+ // right: this._NotOccupiedEdgeList[neighbor.right]?.index,
1080
+ // top: this._NotOccupiedEdgeList[neighbor.top]?.index,
1081
+ // bottom: this._NotOccupiedEdgeList[neighbor.bottom]?.index,
1082
+ // }
1083
+ // );
1084
+
1085
+ for (let key in neighbor) {
1086
+ //更新每个方向找到的邻居
1087
+ if (neighbor[key] !== -1) {
1088
+ const targetItem = this._NotOccupiedEdgeList[neighbor[key]];
1089
+ this._TryUpdateItemNeighbor(
1090
+ tmpNeighbor,
1091
+ key,
1092
+ crossItem,
1093
+ targetItem
1094
+ );
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+
1100
+ private _IfBlockedByOtherNeighbor(direction: string, crossItem: EdgeInfo, targetItem: EdgeInfo) {
1101
+ //检查是否被其他neighbor遮挡
1102
+ let positionKey = "top";
1103
+ let sizeKey = "height";
1104
+ let subPositionKey = "left";
1105
+ switch (direction) {
1106
+ case "left":
1107
+ break;
1108
+ case "right":
1109
+ break;
1110
+ case "top":
1111
+ positionKey = "left";
1112
+ subPositionKey = "top";
1113
+ sizeKey = "width";
1114
+ break;
1115
+ case "bottom":
1116
+ positionKey = "left";
1117
+ subPositionKey = "top";
1118
+ sizeKey = "width";
1119
+ break;
1120
+ default:
1121
+ console.log(`_IfBlockedByOtherNeighbor unknown direction:${direction}`);
1122
+ break;
1123
+ }
1124
+ const crossTemplateItem = this.getItemByIndex(crossItem.index);
1125
+ const targetTemplateItem = this.getItemByIndex(targetItem.index);
1126
+
1127
+ let valid = true;
1128
+ if (crossTemplateItem && targetTemplateItem) {
1129
+ const crossNeighborList =
1130
+ crossTemplateItem.neighborIndexList[direction];
1131
+ const targetStart = targetTemplateItem[positionKey];
1132
+ const targetEnd = targetStart + targetTemplateItem[sizeKey] - 1;
1133
+ for (let i = crossNeighborList.length - 1; i >= 0; i--) {
1134
+ const item = this.getItemByIndex(crossNeighborList[i]);
1135
+
1136
+ if (item == null) continue;
1137
+
1138
+ const itemStart = item[positionKey];
1139
+ const itemEnd = itemStart + item[sizeKey] - 1;
1140
+ if (itemStart <= targetEnd && itemEnd >= targetStart) {
1141
+ //有重叠, 检查两个item那个离 cross item 更近
1142
+ if (
1143
+ Math.abs(
1144
+ item[subPositionKey] - crossTemplateItem[subPositionKey]
1145
+ ) <
1146
+ Math.abs(
1147
+ targetTemplateItem[subPositionKey] -
1148
+ crossTemplateItem[subPositionKey]
1149
+ )
1150
+ ) {
1151
+ //已有的neighbor更近
1152
+ valid = false;
1153
+ break;
1154
+ } else {
1155
+ //target item更近, 删除已有的neighbor
1156
+ crossNeighborList.splice(i, 1);
1157
+ }
1158
+ }
1159
+ }
1160
+ }
1161
+ return valid;
1162
+ }
1163
+
1164
+ private _CheckNeighborItemMatching(direction: string, crossItem: EdgeInfo, targetItem: EdgeInfo) {
1165
+ const crossEdgeObj = crossItem.edgeObj;
1166
+ const targetEdgeObj = targetItem.edgeObj;
1167
+ let targetEdgeName = "rightEdge";
1168
+ let crossEdgeName = "leftEdge";
1169
+ switch (direction) {
1170
+ case "left":
1171
+ targetEdgeName = "rightEdge";
1172
+ crossEdgeName = "leftEdge";
1173
+ break;
1174
+ case "right":
1175
+ targetEdgeName = "leftEdge";
1176
+ crossEdgeName = "rightEdge";
1177
+ break;
1178
+ case "top":
1179
+ targetEdgeName = "bottomEdge";
1180
+ crossEdgeName = "topEdge";
1181
+ break;
1182
+ case "bottom":
1183
+ crossEdgeName = "bottomEdge";
1184
+ targetEdgeName = "topEdge";
1185
+ break;
1186
+ default:
1187
+ console.log(
1188
+ `_CheckNeighborItemMatching unknown direction:${direction}`
1189
+ );
1190
+ break;
1191
+ }
1192
+ // console.log(
1193
+ // "cchtest exclude edge",
1194
+ // crossItem.index,
1195
+ // targetItem.index,
1196
+ // crossEdgeName,
1197
+ // targetEdgeName,
1198
+ // crossEdgeObj[crossEdgeName].range.Clone().Ranges,
1199
+ // targetEdgeObj[targetEdgeName].range.Clone().Ranges
1200
+ // );
1201
+
1202
+ const targetEdgeRangeClone =
1203
+ targetEdgeObj[targetEdgeName].range.Clone();
1204
+ targetEdgeObj[targetEdgeName].range.ExcludeRanges(
1205
+ crossEdgeObj[crossEdgeName].range
1206
+ );
1207
+ crossEdgeObj[crossEdgeName].range.ExcludeRanges(
1208
+ targetEdgeRangeClone
1209
+ );
1210
+
1211
+ // console.log(
1212
+ // "cchtest after exclude",
1213
+ // crossEdgeObj[crossEdgeName].range.Clone().Ranges,
1214
+ // targetEdgeObj[targetEdgeName].range.Clone().Ranges
1215
+ // );
1216
+
1217
+ if (targetEdgeObj[targetEdgeName].range.IsEmpty()) {
1218
+ // 完全匹配,更新该边的状态为已被占用
1219
+ targetEdgeObj[targetEdgeName] = null;
1220
+ }
1221
+
1222
+ if (crossEdgeObj[crossEdgeName].range.IsEmpty()) {
1223
+ // 清除该边
1224
+ crossEdgeObj[crossEdgeName] = null;
1225
+ }
1226
+ }
1227
+
1228
+ private _FindNearestNeighbor(crossItem: EdgeInfo) {
1229
+ const crossEdgeObj = crossItem.edgeObj;
1230
+
1231
+ let nearestLeftIndex = -1;
1232
+ let nearestRightIndex = -1;
1233
+ let nearestTopIndex = -1;
1234
+ let nearestBottomIndex = -1;
1235
+ let leftDistance = -1;
1236
+ let rightDistance = -1;
1237
+ let topDistance = -1;
1238
+ let bottomDistance = -1;
1239
+
1240
+ for (let j = 0; j < this._NotOccupiedEdgeList.length; j++) {
1241
+ const targetItem = this._NotOccupiedEdgeList[j];
1242
+ const targetEdgeObj = targetItem.edgeObj;
1243
+ if (crossItem === targetItem) {
1244
+ continue;
1245
+ }
1246
+ // 左边最近项
1247
+ if (
1248
+ crossEdgeObj.leftEdge !== null &&
1249
+ targetEdgeObj.rightEdge !== null
1250
+ ) {
1251
+ if (
1252
+ crossEdgeObj.leftEdge.position >= targetEdgeObj.rightEdge.position &&
1253
+ crossEdgeObj.leftEdge.range.IsInterAct(
1254
+ targetEdgeObj.rightEdge.range
1255
+ )
1256
+ ) {
1257
+ // 在其左侧,且有交叉,才为neighbor
1258
+ // 计算最短距离
1259
+ if (leftDistance === -1) {
1260
+ leftDistance =
1261
+ crossEdgeObj.leftEdge.position - targetEdgeObj.rightEdge.position;
1262
+ nearestLeftIndex = j;
1263
+ continue;
1264
+ } else if (
1265
+ leftDistance >
1266
+ crossEdgeObj.leftEdge.position - targetEdgeObj.rightEdge.position
1267
+ ) {
1268
+ leftDistance =
1269
+ crossEdgeObj.leftEdge.position - targetEdgeObj.rightEdge.position;
1270
+ nearestLeftIndex = j;
1271
+ continue;
1272
+ }
1273
+ }
1274
+ }
1275
+ // 右边最近项
1276
+ if (
1277
+ crossEdgeObj.rightEdge !== null &&
1278
+ targetEdgeObj.leftEdge !== null
1279
+ ) {
1280
+ if (
1281
+ crossEdgeObj.rightEdge.position <= targetEdgeObj.leftEdge.position &&
1282
+ crossEdgeObj.rightEdge.range.IsInterAct(
1283
+ targetEdgeObj.leftEdge.range
1284
+ )
1285
+ ) {
1286
+ // 在其左侧,且有交叉,才为neighbor
1287
+ // 计算最短距离
1288
+ if (rightDistance === -1) {
1289
+ rightDistance =
1290
+ targetEdgeObj.leftEdge.position - crossEdgeObj.rightEdge.position;
1291
+ nearestRightIndex = j;
1292
+ continue;
1293
+ } else if (
1294
+ rightDistance >
1295
+ targetEdgeObj.leftEdge.position - crossEdgeObj.rightEdge.position
1296
+ ) {
1297
+ rightDistance =
1298
+ targetEdgeObj.leftEdge.position - crossEdgeObj.rightEdge.position;
1299
+ nearestRightIndex = j;
1300
+ continue;
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+ // 上边最近项
1306
+ if (
1307
+ crossEdgeObj.topEdge !== null &&
1308
+ targetEdgeObj.bottomEdge !== null
1309
+ ) {
1310
+ if (
1311
+ crossEdgeObj.topEdge.position >= targetEdgeObj.bottomEdge.position &&
1312
+ crossEdgeObj.topEdge.range.IsInterAct(
1313
+ targetEdgeObj.bottomEdge.range
1314
+ )
1315
+ ) {
1316
+ // 在其左侧,且有交叉,才为neighbor
1317
+ // 计算最短距离
1318
+ if (topDistance === -1) {
1319
+ topDistance =
1320
+ crossEdgeObj.topEdge.position - targetEdgeObj.bottomEdge.position;
1321
+ nearestTopIndex = j;
1322
+ continue;
1323
+ } else if (
1324
+ topDistance >
1325
+ crossEdgeObj.topEdge.position - targetEdgeObj.bottomEdge.position
1326
+ ) {
1327
+ topDistance =
1328
+ crossEdgeObj.topEdge.position - targetEdgeObj.bottomEdge.position;
1329
+ nearestTopIndex = j;
1330
+ continue;
1331
+ }
1332
+ }
1333
+ }
1334
+ // 下边的最近项
1335
+ if (
1336
+ crossEdgeObj.bottomEdge !== null &&
1337
+ targetEdgeObj.topEdge !== null
1338
+ ) {
1339
+ if (
1340
+ crossEdgeObj.bottomEdge.position <= targetEdgeObj.topEdge.position &&
1341
+ crossEdgeObj.bottomEdge.range.IsInterAct(
1342
+ targetEdgeObj.topEdge.range
1343
+ )
1344
+ ) {
1345
+ // 在其左侧,且有交叉,才为neighbor
1346
+ // 计算最短距离
1347
+ if (bottomDistance === -1) {
1348
+ bottomDistance =
1349
+ targetEdgeObj.topEdge.position - crossEdgeObj.bottomEdge.position;
1350
+ nearestBottomIndex = j;
1351
+ continue;
1352
+ } else if (
1353
+ bottomDistance >
1354
+ targetEdgeObj.topEdge.position - crossEdgeObj.bottomEdge.position
1355
+ ) {
1356
+ bottomDistance =
1357
+ targetEdgeObj.topEdge.position - crossEdgeObj.bottomEdge.position;
1358
+ nearestBottomIndex = j;
1359
+ continue;
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ return {
1366
+ left: nearestLeftIndex,
1367
+ right: nearestRightIndex,
1368
+ top: nearestTopIndex,
1369
+ bottom: nearestBottomIndex,
1370
+ };
1371
+ }
1372
+
1373
+ private _TryUpdateItemNeighbor(tmpNeighbor: boolean, direction: string, crossItem: EdgeInfo, targetItem: EdgeInfo) {
1374
+ const keyMap = {
1375
+ target: {
1376
+ left: "right",
1377
+ right: "left",
1378
+ top: "bottom",
1379
+ bottom: "top",
1380
+ },
1381
+ cross: {
1382
+ left: "left",
1383
+ right: "right",
1384
+ top: "top",
1385
+ bottom: "bottom",
1386
+ },
1387
+ };
1388
+ let crossNeighborKey = keyMap.cross[direction];
1389
+ let targetNeighborKey = keyMap.target[direction];
1390
+ if (!crossNeighborKey || !targetNeighborKey) {
1391
+ console.error("_TryUpdateItemNeighbor: undefined direction", direction);
1392
+ }
1393
+
1394
+ const targetIndex = targetItem.index;
1395
+ const crossItemIndex = crossItem.index;
1396
+ const templateList = this.templateList;
1397
+ if (this._IfBlockedByOtherNeighbor(direction, crossItem, targetItem)) {
1398
+ if (!tmpNeighbor) {
1399
+ this._CheckNeighborItemMatching(direction, crossItem, targetItem);
1400
+ // 查看是否有交集,有交集,更新临近边
1401
+ templateList[targetIndex].neighborIndexList[targetNeighborKey].push(
1402
+ crossItemIndex
1403
+ );
1404
+ templateList[crossItemIndex].neighborIndexList[
1405
+ crossNeighborKey
1406
+ ].push(targetIndex);
1407
+ templateList[targetIndex].tmpNeighborIndexList[targetNeighborKey] =
1408
+ [];
1409
+ templateList[crossItemIndex].tmpNeighborIndexList[
1410
+ crossNeighborKey
1411
+ ] = [];
1412
+ } else {
1413
+ templateList[targetIndex].tmpNeighborIndexList[
1414
+ targetNeighborKey
1415
+ ].push(crossItemIndex);
1416
+ templateList[crossItemIndex].tmpNeighborIndexList[
1417
+ crossNeighborKey
1418
+ ].push(targetIndex);
1419
+ }
1420
+ }
1421
+ }
1422
+ }
1423
+
1424
+ export { CommonMetroTemplate };