@mlightcad/cad-simple-viewer 1.7.5 → 1.7.6-preview.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.
Files changed (80) hide show
  1. package/dist/cad-simple-viewer.js +8295 -7508
  2. package/dist/cad-simple-viewer.umd.cjs +83 -83
  3. package/dist/mtext-renderer-worker.js +2987 -2815
  4. package/find.ts +7 -0
  5. package/lib/app/AcApDocManager.d.ts +5 -0
  6. package/lib/app/AcApDocManager.d.ts.map +1 -1
  7. package/lib/app/AcApDocManager.js +28 -15
  8. package/lib/app/AcApDocManager.js.map +1 -1
  9. package/lib/app/AcApOpenDatabaseOptions.d.ts +8 -6
  10. package/lib/app/AcApOpenDatabaseOptions.d.ts.map +1 -1
  11. package/lib/app/AcApOpenFileProgressController.d.ts +17 -9
  12. package/lib/app/AcApOpenFileProgressController.d.ts.map +1 -1
  13. package/lib/app/AcApOpenFileProgressController.js +41 -20
  14. package/lib/app/AcApOpenFileProgressController.js.map +1 -1
  15. package/lib/app/AcApProgress.d.ts +5 -0
  16. package/lib/app/AcApProgress.d.ts.map +1 -1
  17. package/lib/app/AcApProgress.js +10 -0
  18. package/lib/app/AcApProgress.js.map +1 -1
  19. package/lib/app/openFileProgress.d.ts +8 -1
  20. package/lib/app/openFileProgress.d.ts.map +1 -1
  21. package/lib/app/openFileProgress.js +13 -2
  22. package/lib/app/openFileProgress.js.map +1 -1
  23. package/lib/app/syncCurrentSpaceFromTileMode.d.ts +25 -0
  24. package/lib/app/syncCurrentSpaceFromTileMode.d.ts.map +1 -0
  25. package/lib/app/syncCurrentSpaceFromTileMode.js +85 -0
  26. package/lib/app/syncCurrentSpaceFromTileMode.js.map +1 -0
  27. package/lib/find/AcApFindNavigator.d.ts +59 -0
  28. package/lib/find/AcApFindNavigator.d.ts.map +1 -0
  29. package/lib/find/AcApFindNavigator.js +145 -0
  30. package/lib/find/AcApFindNavigator.js.map +1 -0
  31. package/lib/find/findDrawing.d.ts +100 -0
  32. package/lib/find/findDrawing.d.ts.map +1 -0
  33. package/lib/find/findDrawing.js +183 -0
  34. package/lib/find/findDrawing.js.map +1 -0
  35. package/lib/find/findText.d.ts +147 -0
  36. package/lib/find/findText.d.ts.map +1 -0
  37. package/lib/find/findText.js +527 -0
  38. package/lib/find/findText.js.map +1 -0
  39. package/lib/find/index.d.ts +3 -0
  40. package/lib/find/index.d.ts.map +1 -0
  41. package/lib/find/index.js +3 -0
  42. package/lib/find/index.js.map +1 -0
  43. package/lib/index.d.ts +2 -0
  44. package/lib/index.d.ts.map +1 -1
  45. package/lib/index.js +2 -0
  46. package/lib/index.js.map +1 -1
  47. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts +29 -7
  48. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts.map +1 -1
  49. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js +77 -38
  50. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js.map +1 -1
  51. package/lib/view/AcTrConvertQueue.d.ts +78 -0
  52. package/lib/view/AcTrConvertQueue.d.ts.map +1 -0
  53. package/lib/view/AcTrConvertQueue.js +143 -0
  54. package/lib/view/AcTrConvertQueue.js.map +1 -0
  55. package/lib/view/AcTrGroupWcsBboxAssert.d.ts +9 -0
  56. package/lib/view/AcTrGroupWcsBboxAssert.d.ts.map +1 -1
  57. package/lib/view/AcTrGroupWcsBboxAssert.js +12 -0
  58. package/lib/view/AcTrGroupWcsBboxAssert.js.map +1 -1
  59. package/lib/view/AcTrLayer.d.ts.map +1 -1
  60. package/lib/view/AcTrLayer.js +5 -1
  61. package/lib/view/AcTrLayer.js.map +1 -1
  62. package/lib/view/AcTrLayout.d.ts.map +1 -1
  63. package/lib/view/AcTrLayout.js +15 -0
  64. package/lib/view/AcTrLayout.js.map +1 -1
  65. package/lib/view/AcTrLayoutView.d.ts.map +1 -1
  66. package/lib/view/AcTrLayoutView.js +3 -1
  67. package/lib/view/AcTrLayoutView.js.map +1 -1
  68. package/lib/view/AcTrProgressiveOpenFitController.d.ts +1 -0
  69. package/lib/view/AcTrProgressiveOpenFitController.d.ts.map +1 -1
  70. package/lib/view/AcTrProgressiveOpenFitController.js +2 -1
  71. package/lib/view/AcTrProgressiveOpenFitController.js.map +1 -1
  72. package/lib/view/AcTrView2d.d.ts +167 -18
  73. package/lib/view/AcTrView2d.d.ts.map +1 -1
  74. package/lib/view/AcTrView2d.js +746 -212
  75. package/lib/view/AcTrView2d.js.map +1 -1
  76. package/package.json +17 -10
  77. package/src/find/AcApFindNavigator.ts +190 -0
  78. package/src/find/findDrawing.ts +211 -0
  79. package/src/find/findText.ts +595 -0
  80. package/src/find/index.ts +2 -0
@@ -119,6 +119,7 @@ import { collectTextStyleFontNames, planTextStyleFontPreload } from '../util/AcA
119
119
  import { ACAP_READING_MODE_BACKGROUND, AcApReadingModeState } from './AcApReadingMode';
120
120
  import { trySelectReviewOverlay, trySelectReviewOverlaysByBox } from './AcEdReviewOverlayPick';
121
121
  import { AcEdViewKeyHandler } from './AcEdViewKeyHandler';
122
+ import { absorbConvertQueue, adaptConvertSliceSize, createConvertPending, hasPendingActiveOwner } from './AcTrConvertQueue';
122
123
  import { shouldExtendBboxForDirectEntity, tryBuildDirectEntityMeta } from './AcTrDirectBatch';
123
124
  import { AcTrEntityDisplayController } from './AcTrEntityDisplayController';
124
125
  import { assertAcTrGroupWcsBboxesConsistent, unionGroupWcsChildBoxes } from './AcTrGroupWcsBboxAssert';
@@ -288,6 +289,13 @@ var AcTrView2d = /** @class */ (function (_super) {
288
289
  * `setTimeout` storm after the loading overlay hides.
289
290
  */
290
291
  _this._convertQueue = [];
292
+ /**
293
+ * Paper-space block table record ids whose viewport entities were converted
294
+ * before an {@link AcTrLayoutView} existed, so {@link batchConvert} could
295
+ * not create the {@link AcTrViewportView}. Repaired when that layout view
296
+ * is created.
297
+ */
298
+ _this._viewportRepairOwnerIds = new Set();
291
299
  /** In-flight progressive convert drain; shared so awaiters wait for the queue. */
292
300
  _this._convertDrainPromise = null;
293
301
  /**
@@ -296,8 +304,15 @@ var AcTrView2d = /** @class */ (function (_super) {
296
304
  * document open.
297
305
  */
298
306
  _this._convertEpoch = 0;
299
- /** Last time progressive open marked the canvas dirty for paint. */
307
+ /** Last time progressive open actually painted mid-convert. */
300
308
  _this._lastProgressivePaintAt = 0;
309
+ /**
310
+ * When true, the next progressive mid-open paint must not be throttled
311
+ * (camera moved — user pan/zoom or incremental auto-fit).
312
+ */
313
+ _this._progressivePaintUrgent = false;
314
+ /** Pause convert until this `performance.now()` after user input. */
315
+ _this._progressiveInputPauseUntil = 0;
301
316
  /** Mid-open WebGL paints while progressive convert was still running. */
302
317
  _this._progressivePaintCount = 0;
303
318
  /** Cooperative yields taken inside progressive {@link batchConvert}. */
@@ -376,17 +391,39 @@ var AcTrView2d = /** @class */ (function (_super) {
376
391
  camera: _this.internalCamera
377
392
  });
378
393
  var stillLoading = _this._numOfEntitiesToProcess > 0;
379
- var deferRenderWhileLoading = stillLoading && !_this._progressiveRendering;
380
- if (!_this._isDirty && !_this._htmlDirty && !stillLoading)
394
+ var sceneBusy = stillLoading || _this._pendingGeometryJobs > 0;
395
+ // Headless non-progressive (`cooperativeYield` false): skip WebGL until
396
+ // idle. Export reads scene batches, not the framebuffer, and software
397
+ // WebGL + uncapped rAF spent most of open time rasterizing. Interactive
398
+ // non-progressive still paints under "Rendering drawing ..." — throttled
399
+ // below so each glyph completion does not redraw the full scene.
400
+ var deferRenderWhileLoading = !_this._progressiveRendering && !_this._cooperativeYield && sceneBusy;
401
+ if (!_this._isDirty && !_this._htmlDirty && !sceneBusy) {
381
402
  return;
403
+ }
382
404
  if (deferRenderWhileLoading)
383
405
  return;
384
406
  if (!_this._isDirty && !_this._htmlDirty)
385
407
  return;
408
+ // Mid-open throttle: progressive convert paints, and interactive
409
+ // non-progressive paints while the scene is still busy. Camera changes
410
+ // set `_progressivePaintUrgent` so pan/zoom still redraw now.
411
+ var countMidOpenPaint = _this.shouldCountMidOpenPaint(stillLoading, sceneBusy);
412
+ var throttleOpenPaint = _this._isDirty && !_this._progressivePaintUrgent && countMidOpenPaint;
413
+ if (throttleOpenPaint) {
414
+ var intervalMs = _this._progressiveRendering
415
+ ? AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS
416
+ : AcTrView2d.NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS;
417
+ if (performance.now() - _this._lastProgressivePaintAt < intervalMs) {
418
+ return;
419
+ }
420
+ }
386
421
  var needsRedraw = false;
387
422
  if (_this._isDirty) {
388
- if (_this._progressiveRendering && stillLoading) {
423
+ if (countMidOpenPaint) {
389
424
  _this._progressivePaintCount++;
425
+ _this._lastProgressivePaintAt = performance.now();
426
+ _this._progressivePaintUrgent = false;
390
427
  }
391
428
  needsRedraw = _this._layoutViewManager.render(_this._scene);
392
429
  }
@@ -404,6 +441,20 @@ var AcTrView2d = /** @class */ (function (_super) {
404
441
  _this._htmlDirty = false;
405
442
  };
406
443
  _this._disposeCanvasTouchCallout = disposeCanvasTouchCallout;
444
+ var onProgressiveInput = function () { return _this.noteProgressiveUserInput(); };
445
+ renderer.domElement.addEventListener('pointerdown', onProgressiveInput, {
446
+ capture: true
447
+ });
448
+ renderer.domElement.addEventListener('wheel', onProgressiveInput, {
449
+ capture: true,
450
+ passive: true
451
+ });
452
+ _this._disposeProgressiveInputWatch = function () {
453
+ renderer.domElement.removeEventListener('pointerdown', onProgressiveInput, { capture: true });
454
+ renderer.domElement.removeEventListener('wheel', onProgressiveInput, {
455
+ capture: true
456
+ });
457
+ };
407
458
  _this._gripManager = new AcEdGripManager(_this);
408
459
  _this._keyHandler = new AcEdViewKeyHandler(_this);
409
460
  if (options.calculateSizeCallback) {
@@ -910,10 +961,9 @@ var AcTrView2d = /** @class */ (function (_super) {
910
961
  * The open-file progress overlay uses this when
911
962
  * {@link AcApOpenDatabaseOptions.progressiveRendering} is off, so
912
963
  * "Rendering drawing ..." stays up until deferred glyph jobs finish.
913
- * When progressive rendering is on, the overlay uses
914
- * {@link isConvertingEntities} and may hide while text geometry continues
915
- * in the deferred pool (pan/zoom enabled). Deprecated `waitForTextGeometry`
916
- * no longer selects this gate.
964
+ * When progressive rendering is on, the overlay hides at CONVERSION `END`
965
+ * and does not wait on this flag (pan/zoom enabled while convert continues).
966
+ * Deprecated `waitForTextGeometry` no longer selects this gate.
917
967
  */
918
968
  get: function () {
919
969
  return this._numOfEntitiesToProcess > 0 || this._pendingGeometryJobs > 0;
@@ -1004,7 +1054,9 @@ var AcTrView2d = /** @class */ (function (_super) {
1004
1054
  });
1005
1055
  Object.defineProperty(AcTrView2d.prototype, "cooperativeYield", {
1006
1056
  /**
1007
- * When false, entity convert skips cooperative yields (headless export).
1057
+ * When false, entity convert skips cooperative yields and non-progressive
1058
+ * mid-open WebGL paints (headless export). Interactive viewers keep this
1059
+ * `true` so "Rendering drawing ..." can still refresh the canvas.
1008
1060
  */
1009
1061
  get: function () {
1010
1062
  return this._cooperativeYield;
@@ -1018,8 +1070,8 @@ var AcTrView2d = /** @class */ (function (_super) {
1018
1070
  Object.defineProperty(AcTrView2d.prototype, "progressiveOpenStats", {
1019
1071
  /**
1020
1072
  * Open-convert counters for OPENPROF / palette (cooperative yields during
1021
- * convert, and mid-open paints when progressive rendering is enabled).
1022
- * Reset when progressive mode is enabled for an open.
1073
+ * convert, and throttled mid-open paints). Reset when progressive mode is
1074
+ * enabled for an open.
1023
1075
  */
1024
1076
  get: function () {
1025
1077
  return {
@@ -1034,6 +1086,85 @@ var AcTrView2d = /** @class */ (function (_super) {
1034
1086
  this._progressivePaintCount = 0;
1035
1087
  this._progressiveYieldCount = 0;
1036
1088
  this._lastProgressivePaintAt = 0;
1089
+ this._progressivePaintUrgent = false;
1090
+ this._progressiveInputPauseUntil = 0;
1091
+ };
1092
+ /**
1093
+ * True while a recent canvas pointerdown/wheel asked convert to pause.
1094
+ * Does not treat hover mousemove as a pause (that starved convert).
1095
+ */
1096
+ AcTrView2d.prototype.isProgressiveGesturePauseActive = function () {
1097
+ return performance.now() < this._progressiveInputPauseUntil;
1098
+ };
1099
+ /**
1100
+ * True when the browser reports pending discrete input (clicks, keys).
1101
+ * Continuous events (mousemove) are excluded so hover does not throttle
1102
+ * progressive convert.
1103
+ */
1104
+ AcTrView2d.prototype.isProgressiveDiscreteInputPending = function () {
1105
+ var _a;
1106
+ var scheduling = navigator.scheduling;
1107
+ return ((_a = scheduling === null || scheduling === void 0 ? void 0 : scheduling.isInputPending) === null || _a === void 0 ? void 0 : _a.call(scheduling)) === true;
1108
+ };
1109
+ /**
1110
+ * Notes user interaction during progressive convert so paints stay urgent
1111
+ * and the drain loop pauses briefly for OrbitControls.
1112
+ */
1113
+ AcTrView2d.prototype.noteProgressiveUserInput = function () {
1114
+ if (!this._progressiveRendering || this._numOfEntitiesToProcess <= 0) {
1115
+ return;
1116
+ }
1117
+ this._progressivePaintUrgent = true;
1118
+ this._isDirty = true;
1119
+ this._progressiveInputPauseUntil =
1120
+ performance.now() + AcTrView2d.PROGRESSIVE_INPUT_PAUSE_MS;
1121
+ };
1122
+ /**
1123
+ * Yields during progressive convert: honor the time budget; after
1124
+ * pointerdown/wheel pause briefly for OrbitControls; on discrete pending
1125
+ * input yield once so handlers run without extending the pause window.
1126
+ */
1127
+ AcTrView2d.prototype.progressiveYieldForInput = function (yieldGate, yieldToEventLoop) {
1128
+ return __awaiter(this, void 0, void 0, function () {
1129
+ var didYield;
1130
+ return __generator(this, function (_a) {
1131
+ switch (_a.label) {
1132
+ case 0:
1133
+ if (!this.isProgressiveGesturePauseActive()) return [3 /*break*/, 5];
1134
+ return [4 /*yield*/, yieldToEventLoop()];
1135
+ case 1:
1136
+ _a.sent();
1137
+ _a.label = 2;
1138
+ case 2:
1139
+ if (!(performance.now() < this._progressiveInputPauseUntil)) return [3 /*break*/, 4];
1140
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 16); })];
1141
+ case 3:
1142
+ _a.sent();
1143
+ return [3 /*break*/, 2];
1144
+ case 4:
1145
+ this._progressiveYieldCount++;
1146
+ return [2 /*return*/];
1147
+ case 5:
1148
+ if (!this.isProgressiveDiscreteInputPending()) return [3 /*break*/, 7];
1149
+ return [4 /*yield*/, yieldToEventLoop()];
1150
+ case 6:
1151
+ _a.sent();
1152
+ this._progressiveYieldCount++;
1153
+ return [2 /*return*/];
1154
+ case 7:
1155
+ if (!yieldGate) {
1156
+ return [2 /*return*/];
1157
+ }
1158
+ return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
1159
+ case 8:
1160
+ didYield = _a.sent();
1161
+ if (didYield) {
1162
+ this._progressiveYieldCount++;
1163
+ }
1164
+ return [2 /*return*/];
1165
+ }
1166
+ });
1167
+ });
1037
1168
  };
1038
1169
  /**
1039
1170
  * Enables progressive camera framing while entities are batch-converted at
@@ -1480,6 +1611,31 @@ var AcTrView2d = /** @class */ (function (_super) {
1480
1611
  includeLayouts = (options === null || options === void 0 ? void 0 : options.includeLayouts) !== false;
1481
1612
  db = AcApDocManager.instance.curDocument.database;
1482
1613
  pending = [];
1614
+ // Open streams entities through `drainConvertQueue` and claims each id
1615
+ // before the scene commit (deferred glyphs / INSERT text). Headless
1616
+ // export used to scan here as soon as parse returned, while that drain
1617
+ // was still running. Claimed ids are not in the scene yet, so they
1618
+ // looked missing and `batchConvert({ forExport: true })` expanded them
1619
+ // again — serially, because export convert does not defer glyph work.
1620
+ // On a text-heavy drawing that second pass dominates wall time and
1621
+ // leaves duplicate geometry in the snapshot. Wait until the open drain
1622
+ // has claimed or committed every streamed entity, then skip those ids.
1623
+ return [4 /*yield*/, this.drainConvertQueue()
1624
+ // Paper-space tabs the user never visited exist in the layout table but
1625
+ // may be missing from the scene until first switch. HTML export needs
1626
+ // every layout's geometry, so register those BTRs before collecting.
1627
+ ];
1628
+ case 1:
1629
+ // Open streams entities through `drainConvertQueue` and claims each id
1630
+ // before the scene commit (deferred glyphs / INSERT text). Headless
1631
+ // export used to scan here as soon as parse returned, while that drain
1632
+ // was still running. Claimed ids are not in the scene yet, so they
1633
+ // looked missing and `batchConvert({ forExport: true })` expanded them
1634
+ // again — serially, because export convert does not defer glyph work.
1635
+ // On a text-heavy drawing that second pass dominates wall time and
1636
+ // leaves duplicate geometry in the snapshot. Wait until the open drain
1637
+ // has claimed or committed every streamed entity, then skip those ids.
1638
+ _j.sent();
1483
1639
  layoutTable = (_h = db.objects) === null || _h === void 0 ? void 0 : _h.layout;
1484
1640
  if (includeLayouts && (layoutTable === null || layoutTable === void 0 ? void 0 : layoutTable.newIterator)) {
1485
1641
  try {
@@ -1509,7 +1665,7 @@ var AcTrView2d = /** @class */ (function (_super) {
1509
1665
  if (!blockTableRecord) {
1510
1666
  continue;
1511
1667
  }
1512
- pending.push.apply(pending, __spreadArray([], __read(this._entityDisplay.collectMissingEntitiesForExport(blockTableRecord, function (objectId) { return _this.hasEntity(objectId); }, includeInvisibleLayers)), false));
1668
+ pending.push.apply(pending, __spreadArray([], __read(this._entityDisplay.collectMissingEntitiesForExport(blockTableRecord, function (objectId) { return _this.isEntityConvertScheduled(objectId); }, includeInvisibleLayers)), false));
1513
1669
  }
1514
1670
  }
1515
1671
  catch (e_5_1) { e_5 = { error: e_5_1 }; }
@@ -1519,14 +1675,14 @@ var AcTrView2d = /** @class */ (function (_super) {
1519
1675
  }
1520
1676
  finally { if (e_5) throw e_5.error; }
1521
1677
  }
1522
- if (!(pending.length > 0)) return [3 /*break*/, 2];
1678
+ if (!(pending.length > 0)) return [3 /*break*/, 3];
1523
1679
  this._numOfEntitiesToProcess += pending.length;
1524
1680
  return [4 /*yield*/, this.batchConvert(pending, { forExport: true })];
1525
- case 1:
1681
+ case 2:
1526
1682
  _j.sent();
1527
- _j.label = 2;
1528
- case 2: return [4 /*yield*/, this.waitUntilIdle()];
1529
- case 3:
1683
+ _j.label = 3;
1684
+ case 3: return [4 /*yield*/, this.waitUntilIdle(120000)];
1685
+ case 4:
1530
1686
  idle = _j.sent();
1531
1687
  if (!idle) {
1532
1688
  log.warn('[AcTrView2d] Timed out waiting for deferred geometry before export');
@@ -2359,6 +2515,17 @@ var AcTrView2d = /** @class */ (function (_super) {
2359
2515
  AcTrView2d.prototype.hasEntity = function (objectId) {
2360
2516
  return this._scene.hasEntity(objectId);
2361
2517
  };
2518
+ /**
2519
+ * True when an entity is already in the scene or claimed by an in-flight
2520
+ * convert (including deferred glyph commits that have not called
2521
+ * `addEntity` yet).
2522
+ */
2523
+ AcTrView2d.prototype.isEntityConvertScheduled = function (objectId) {
2524
+ var id = String(objectId !== null && objectId !== void 0 ? objectId : '');
2525
+ if (!id)
2526
+ return false;
2527
+ return this.hasEntity(objectId) || this._claimedConvertObjectIds.has(id);
2528
+ };
2362
2529
  /**
2363
2530
  * @inheritdoc
2364
2531
  */
@@ -2517,6 +2684,17 @@ var AcTrView2d = /** @class */ (function (_super) {
2517
2684
  this._initializedLayouts.add(layoutBtrId);
2518
2685
  this._externallyFramedLayouts.add(layoutBtrId);
2519
2686
  };
2687
+ /**
2688
+ * Whether the layout has already been visited (or framed by the
2689
+ * application layer), i.e. a switch to it will not run the first-visit
2690
+ * auto zoom. Lets callers that frame a layout themselves (FIND) call
2691
+ * {@link markLayoutAsInitialized} only when it is still needed.
2692
+ *
2693
+ * @param layoutBtrId Input the block table record id of the layout
2694
+ */
2695
+ AcTrView2d.prototype.isLayoutInitialized = function (layoutBtrId) {
2696
+ return this._initializedLayouts.has(layoutBtrId);
2697
+ };
2520
2698
  /**
2521
2699
  * Returns the 2D box of drawable geometry in the active layout.
2522
2700
  *
@@ -2618,6 +2796,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2618
2796
  this._convertEpoch++;
2619
2797
  this.clearFontLoadedRedrawTimer();
2620
2798
  this._convertQueue.length = 0;
2799
+ this._viewportRepairOwnerIds.clear();
2621
2800
  this._numOfEntitiesToProcess = 0;
2622
2801
  this._claimedConvertObjectIds.clear();
2623
2802
  this._entityConvertGeneration.clear();
@@ -2664,6 +2843,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2664
2843
  this.clearFontLoadedRedrawTimer();
2665
2844
  this.cancelOpenLineworkFrame();
2666
2845
  this._convertQueue.length = 0;
2846
+ this._viewportRepairOwnerIds.clear();
2667
2847
  this._numOfEntitiesToProcess = 0;
2668
2848
  this._claimedConvertObjectIds.clear();
2669
2849
  this._entityConvertGeneration.clear();
@@ -2696,6 +2876,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2696
2876
  this.clearFontLoadedRedrawTimer();
2697
2877
  this.cancelOpenLineworkFrame();
2698
2878
  this._convertQueue.length = 0;
2879
+ this._viewportRepairOwnerIds.clear();
2699
2880
  this._numOfEntitiesToProcess = 0;
2700
2881
  this._claimedConvertObjectIds.clear();
2701
2882
  this._entityConvertGeneration.clear();
@@ -2770,21 +2951,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2770
2951
  _a.label = 3;
2771
2952
  case 3: return [2 /*return*/];
2772
2953
  case 4:
2773
- this._convertDrainPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
2774
- var batch;
2775
- return __generator(this, function (_a) {
2776
- switch (_a.label) {
2777
- case 0:
2778
- if (!(this._convertQueue.length > 0)) return [3 /*break*/, 2];
2779
- batch = this._convertQueue.splice(0, this._convertQueue.length);
2780
- return [4 /*yield*/, this.batchConvert(batch)];
2781
- case 1:
2782
- _a.sent();
2783
- return [3 /*break*/, 0];
2784
- case 2: return [2 /*return*/];
2785
- }
2786
- });
2787
- }); })().finally(function () {
2954
+ this._convertDrainPromise = this.runConvertDrain().finally(function () {
2788
2955
  _this._convertDrainPromise = null;
2789
2956
  });
2790
2957
  return [4 /*yield*/, this._convertDrainPromise];
@@ -2795,9 +2962,188 @@ var AcTrView2d = /** @class */ (function (_super) {
2795
2962
  });
2796
2963
  });
2797
2964
  };
2965
+ /**
2966
+ * Converts the queued entities in slices, active layout first.
2967
+ *
2968
+ * `drainConvertQueue` used to hand the whole queue to one
2969
+ * {@link batchConvert}. Model-space block references queued ahead of the
2970
+ * sheet then blocked the sheet's frame and viewports for the entire batch,
2971
+ * and a layout activated during that batch stayed starved until it
2972
+ * finished. Slices return here so a newly active layout is ordered ahead
2973
+ * of the remainder. Paper-active opens use a modest slice budget so the
2974
+ * sheet can paint soon; model-active opens match the inner
2975
+ * {@link batchConvert} yield cadence and grow slices for throughput so
2976
+ * Model-first drawings are not slowed by fine outer slicing. Headless
2977
+ * opens (`cooperativeYield === false`) still convert in one batch after
2978
+ * that reorder.
2979
+ */
2980
+ AcTrView2d.prototype.runConvertDrain = function () {
2981
+ return __awaiter(this, void 0, void 0, function () {
2982
+ var epoch, interactive, slice, lastYieldAt, state, queued, activeNow, activeChanged, end, startedAt, took, modelActive, modelYieldBudget, budget;
2983
+ return __generator(this, function (_a) {
2984
+ switch (_a.label) {
2985
+ case 0:
2986
+ epoch = this._convertEpoch;
2987
+ interactive = this._cooperativeYield;
2988
+ slice = !interactive
2989
+ ? Number.POSITIVE_INFINITY
2990
+ : this.activeLayoutBtrId === this.modelSpaceBtrId
2991
+ ? AcTrView2d.CONVERT_SLICE_MAX
2992
+ : 1;
2993
+ lastYieldAt = performance.now();
2994
+ state = createConvertPending(this._convertQueue.splice(0, this._convertQueue.length), this.activeLayoutBtrId);
2995
+ this.repairViewportViewsFor(state.activeOwnerId);
2996
+ _a.label = 1;
2997
+ case 1:
2998
+ if (!(state.cursor < state.pending.length ||
2999
+ this._convertQueue.length > 0)) return [3 /*break*/, 12];
3000
+ if (this._convertEpoch !== epoch)
3001
+ return [2 /*return*/];
3002
+ queued = this._convertQueue.splice(0, this._convertQueue.length);
3003
+ activeNow = this.activeLayoutBtrId;
3004
+ activeChanged = activeNow !== state.activeOwnerId;
3005
+ state = absorbConvertQueue(state, queued, activeNow);
3006
+ if (activeChanged) {
3007
+ this.repairViewportViewsFor(activeNow);
3008
+ // Drop back to a small slice so the newly active layout's own
3009
+ // entities are not buried inside a leftover large model-space bite.
3010
+ if (interactive)
3011
+ slice = 1;
3012
+ }
3013
+ this.flushViewportViewRepairs();
3014
+ if (state.cursor >= state.pending.length)
3015
+ return [3 /*break*/, 12];
3016
+ end = Math.min(state.pending.length, state.cursor + slice);
3017
+ startedAt = performance.now();
3018
+ return [4 /*yield*/, this.batchConvert(state.pending.slice(state.cursor, end))];
3019
+ case 2:
3020
+ _a.sent();
3021
+ state.cursor = end;
3022
+ if (this._convertEpoch !== epoch)
3023
+ return [2 /*return*/];
3024
+ if (!interactive)
3025
+ return [3 /*break*/, 1];
3026
+ took = performance.now() - startedAt;
3027
+ modelActive = state.activeOwnerId === this.modelSpaceBtrId;
3028
+ modelYieldBudget = this._progressiveRendering
3029
+ ? AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS
3030
+ : AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS;
3031
+ budget = modelActive
3032
+ ? modelYieldBudget
3033
+ : AcTrView2d.CONVERT_SLICE_BUDGET_MS;
3034
+ slice = adaptConvertSliceSize({
3035
+ slice: slice,
3036
+ tookMs: took,
3037
+ budgetMs: budget,
3038
+ maxSlice: AcTrView2d.CONVERT_SLICE_MAX,
3039
+ // Fine 2× growth only while a paper sheet still needs converting.
3040
+ // Model-active (or paper already drained) prefers 4× throughput.
3041
+ activeLayoutPending: !modelActive && hasPendingActiveOwner(state)
3042
+ });
3043
+ if (!(this._progressiveRendering &&
3044
+ this.isProgressiveGesturePauseActive())) return [3 /*break*/, 7];
3045
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3046
+ case 3:
3047
+ _a.sent();
3048
+ _a.label = 4;
3049
+ case 4:
3050
+ if (!(performance.now() < this._progressiveInputPauseUntil)) return [3 /*break*/, 6];
3051
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 16); })];
3052
+ case 5:
3053
+ _a.sent();
3054
+ return [3 /*break*/, 4];
3055
+ case 6:
3056
+ lastYieldAt = performance.now();
3057
+ if (this._convertEpoch !== epoch)
3058
+ return [2 /*return*/];
3059
+ return [3 /*break*/, 11];
3060
+ case 7:
3061
+ if (!(this._progressiveRendering &&
3062
+ this.isProgressiveDiscreteInputPending())) return [3 /*break*/, 9];
3063
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3064
+ case 8:
3065
+ _a.sent();
3066
+ lastYieldAt = performance.now();
3067
+ if (this._convertEpoch !== epoch)
3068
+ return [2 /*return*/];
3069
+ return [3 /*break*/, 11];
3070
+ case 9:
3071
+ if (!(performance.now() - lastYieldAt >= budget)) return [3 /*break*/, 11];
3072
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3073
+ case 10:
3074
+ _a.sent();
3075
+ lastYieldAt = performance.now();
3076
+ if (this._convertEpoch !== epoch)
3077
+ return [2 /*return*/];
3078
+ _a.label = 11;
3079
+ case 11: return [3 /*break*/, 1];
3080
+ case 12:
3081
+ this.repairViewportViewsFor(this.activeLayoutBtrId);
3082
+ this.flushViewportViewRepairs();
3083
+ return [2 /*return*/];
3084
+ }
3085
+ });
3086
+ });
3087
+ };
3088
+ /**
3089
+ * Creates missing {@link AcTrViewportView}s for one paper layout.
3090
+ *
3091
+ * Safe to call before the layout's entities have been committed to the
3092
+ * scene: the database block table record already holds the viewports.
3093
+ * Model space is skipped so a large model is not walked on every slice.
3094
+ *
3095
+ * @param layoutBtrId - Block table record id of the layout to repair.
3096
+ */
3097
+ AcTrView2d.prototype.repairViewportViewsFor = function (layoutBtrId) {
3098
+ var _a;
3099
+ if (!layoutBtrId || layoutBtrId === this.modelSpaceBtrId)
3100
+ return;
3101
+ var layoutView = this._layoutViewManager.getAt(layoutBtrId);
3102
+ if (!layoutView || layoutView.viewportCount > 0) {
3103
+ if (layoutView && layoutView.viewportCount > 0) {
3104
+ this._viewportRepairOwnerIds.delete(layoutBtrId);
3105
+ }
3106
+ return;
3107
+ }
3108
+ try {
3109
+ var db = (_a = AcApDocManager.instance.curDocument) === null || _a === void 0 ? void 0 : _a.database;
3110
+ var blockTableRecord = db === null || db === void 0 ? void 0 : db.tables.blockTable.getIdAt(layoutBtrId);
3111
+ if (!blockTableRecord)
3112
+ return;
3113
+ var before = layoutView.viewportCount;
3114
+ this.ensureViewportViews(blockTableRecord, layoutView);
3115
+ if (layoutView.viewportCount > before) {
3116
+ this._isDirty = true;
3117
+ }
3118
+ // The block table record was readable. Drop the id even when every
3119
+ // viewport was the default paper-space one, so a model-sized record
3120
+ // is not walked again on the next slice.
3121
+ this._viewportRepairOwnerIds.delete(layoutBtrId);
3122
+ }
3123
+ catch (error) {
3124
+ log.error('[AcTrView2d] Error repairing viewport views:', error);
3125
+ }
3126
+ };
3127
+ /**
3128
+ * Retries viewport-view creation for owners recorded while their layout
3129
+ * view did not exist yet.
3130
+ */
3131
+ AcTrView2d.prototype.flushViewportViewRepairs = function () {
3132
+ if (this._viewportRepairOwnerIds.size === 0)
3133
+ return;
3134
+ var ownerIds = Array.from(this._viewportRepairOwnerIds);
3135
+ for (var i = 0; i < ownerIds.length; i++) {
3136
+ this.repairViewportViewsFor(ownerIds[i]);
3137
+ }
3138
+ };
2798
3139
  /**
2799
3140
  * Marks the canvas dirty for progressive open, throttled to avoid painting
2800
3141
  * on every entity (which dominated total open time).
3142
+ *
3143
+ * Does not stamp {@link _lastProgressivePaintAt} — that updates only when
3144
+ * {@link animate} actually paints. Does not set
3145
+ * {@link _progressivePaintUrgent}; camera moves do that so pan/zoom stay
3146
+ * immediate while convert-driven paints remain rare.
2801
3147
  */
2802
3148
  AcTrView2d.prototype.markProgressiveDirty = function (force) {
2803
3149
  if (force === void 0) { force = false; }
@@ -2806,9 +3152,28 @@ var AcTrView2d = /** @class */ (function (_super) {
2806
3152
  now - this._lastProgressivePaintAt >=
2807
3153
  AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS) {
2808
3154
  this._isDirty = true;
2809
- this._lastProgressivePaintAt = now;
2810
3155
  }
2811
3156
  };
3157
+ /**
3158
+ * Interactive non-progressive open: dirty the canvas so {@link animate} can
3159
+ * refresh under "Rendering drawing ...". Headless (`cooperativeYield`
3160
+ * false) leaves this unset; the pool-drain / convert-end dirty is enough.
3161
+ * {@link animate} applies {@link NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS}.
3162
+ */
3163
+ AcTrView2d.prototype.markInteractiveOpenDirty = function () {
3164
+ if (this._progressiveRendering || !this._cooperativeYield) {
3165
+ return;
3166
+ }
3167
+ this._isDirty = true;
3168
+ };
3169
+ /**
3170
+ * True when this frame's WebGL paint should count toward OPENPROF mid-open
3171
+ * paints and use the open-time throttle (unless the camera marked urgent).
3172
+ */
3173
+ AcTrView2d.prototype.shouldCountMidOpenPaint = function (stillLoading, sceneBusy) {
3174
+ return ((this._progressiveRendering && stillLoading) ||
3175
+ (!this._progressiveRendering && this._cooperativeYield && sceneBusy));
3176
+ };
2812
3177
  /**
2813
3178
  * @inheritdoc
2814
3179
  */
@@ -2864,9 +3229,11 @@ var AcTrView2d = /** @class */ (function (_super) {
2864
3229
  * view teardown). Safe to call more than once.
2865
3230
  */
2866
3231
  AcTrView2d.prototype.dispose = function () {
2867
- var _a;
3232
+ var _a, _b;
2868
3233
  (_a = this._disposeCanvasTouchCallout) === null || _a === void 0 ? void 0 : _a.call(this);
2869
3234
  this._disposeCanvasTouchCallout = undefined;
3235
+ (_b = this._disposeProgressiveInputWatch) === null || _b === void 0 ? void 0 : _b.call(this);
3236
+ this._disposeProgressiveInputWatch = undefined;
2870
3237
  this.clearFontLoadedRedrawTimer();
2871
3238
  this.cancelOpenLineworkFrame();
2872
3239
  this.stopAnimationLoop();
@@ -2934,11 +3301,19 @@ var AcTrView2d = /** @class */ (function (_super) {
2934
3301
  layoutView.events.viewChanged.addEventListener(function () {
2935
3302
  _this._progressiveOpenFit.onLayoutViewChanged();
2936
3303
  _this._isDirty = true;
3304
+ // Bypass mid-open paint throttle so pan/zoom (and incremental fit)
3305
+ // remain visible while convert continues.
3306
+ if (_this._progressiveRendering && _this._numOfEntitiesToProcess > 0) {
3307
+ _this._progressivePaintUrgent = true;
3308
+ }
2937
3309
  _this.events.viewChanged.dispatch();
2938
3310
  _this.clearHover();
2939
3311
  });
2940
3312
  this._layoutViewManager.add(layoutView);
2941
3313
  }
3314
+ // Viewport entities may already have been converted while this view was
3315
+ // missing. Creating it is the moment those AcTrViewportViews can exist.
3316
+ this.flushViewportViewRepairs();
2942
3317
  return layoutView;
2943
3318
  };
2944
3319
  /**
@@ -2974,41 +3349,20 @@ var AcTrView2d = /** @class */ (function (_super) {
2974
3349
  var existingLayout = this._scene.layouts.get(layoutBtrId);
2975
3350
  if (existingLayout && existingLayout.isLoaded) {
2976
3351
  // Streamed layouts flip `isLoaded` from `addEntity` before scene
2977
- // commits finish. Still repair the viewport-view race when entities
2978
- // are already in the scene but AcTrViewportView creation was skipped.
2979
- var layoutView_1 = this._layoutViewManager.getAt(layoutBtrId);
2980
- if (existingLayout.entityCount > 0 &&
2981
- layoutView_1 &&
2982
- layoutView_1.viewportCount === 0) {
2983
- this.ensureViewportViews(blockTableRecord, layoutView_1);
2984
- }
3352
+ // commits finish (`entityCount` still 0). Viewport views are read
3353
+ // from the block table record, so repair them even while the sheet's
3354
+ // own entities are still queued behind model space.
3355
+ this.repairViewportViewsFor(layoutBtrId);
2985
3356
  return;
2986
3357
  }
2987
3358
  if (this._loadingLayouts.has(layoutBtrId)) {
2988
3359
  return;
2989
3360
  }
2990
- // Ensure `AcTrViewportView`s exist for every real `AcDbViewport`
2991
- // in this layout when the layout's entities were already streamed
2992
- // in by the document parser. There is a race in the parser-driven
2993
- // load path: `addLayout(layout)` creates the `AcTrLayoutView`,
2994
- // but the parser may dispatch the AcDbViewport entities before
2995
- // that happens. When that races, `batchConvert`'s viewport
2996
- // handler does `_layoutViewManager.getAt(entity.ownerId)`, gets
2997
- // `undefined`, and **silently skips creating the
2998
- // AcTrViewportView**. The reload path below used to mask this by
2999
- // re-running batchConvert after the layoutView existed, but the
3000
- // entityCount-skip optimization that follows removes that
3001
- // side-effect, so we do the viewport-view-only pass explicitly
3002
- // here. Skipped when the layout is empty — in that case the
3003
- // batchConvert path below will create the viewport views directly
3004
- // as it processes each entity.
3005
- var layoutView = this._layoutViewManager.getAt(layoutBtrId);
3006
- if (existingLayout &&
3007
- existingLayout.entityCount > 0 &&
3008
- layoutView &&
3009
- layoutView.viewportCount === 0) {
3010
- this.ensureViewportViews(blockTableRecord, layoutView);
3011
- }
3361
+ // Same repair as the isLoaded path: the layout view can exist before
3362
+ // any of its entities have been committed to the scene. Viewport
3363
+ // entities converted before `addLayout` created that view are recorded
3364
+ // in `_viewportRepairOwnerIds` and attached here.
3365
+ this.repairViewportViewsFor(layoutBtrId);
3012
3366
  // Model space (and any other layout pre-populated by the document
3013
3367
  // parser at open time) lands here without `isLoaded` ever having
3014
3368
  // been flipped — the initial entity stream goes through
@@ -3586,9 +3940,13 @@ var AcTrView2d = /** @class */ (function (_super) {
3586
3940
  void job
3587
3941
  .run()
3588
3942
  .then(function () {
3589
- // Convert counter often hits 0 before fonts finish; without this,
3590
- // text added later never paints until the user pans/zooms.
3591
- if (job.epoch === _this._convertEpoch) {
3943
+ // Progressive: dirty every glyph (animate throttles paints).
3944
+ // Interactive non-progressive: same, under a longer open-paint
3945
+ // interval so "Rendering drawing ..." still shows progress.
3946
+ // Headless non-progressive: skip — the pool-drain dirty below
3947
+ // queues the single frame export/scripts need.
3948
+ if (job.epoch === _this._convertEpoch &&
3949
+ (_this._progressiveRendering || _this._cooperativeYield)) {
3592
3950
  _this._isDirty = true;
3593
3951
  }
3594
3952
  })
@@ -3648,6 +4006,31 @@ var AcTrView2d = /** @class */ (function (_super) {
3648
4006
  });
3649
4007
  });
3650
4008
  };
4009
+ /**
4010
+ * Adds an {@link AcTrViewportView} for a real paper-space viewport.
4011
+ *
4012
+ * When the layout view does not exist yet, the owner id is remembered and
4013
+ * {@link flushViewportViewRepairs} creates the view once the layout view
4014
+ * does. Adding twice for the same viewport id replaces the previous view.
4015
+ *
4016
+ * @param entity - Viewport entity, already past the default-viewport filter
4017
+ * at the caller's discretion. Checked again here.
4018
+ */
4019
+ AcTrView2d.prototype.attachViewportView = function (entity) {
4020
+ if (AcTrViewportView.isDefaultPaperSpaceViewport(entity))
4021
+ return;
4022
+ var ownerId = entity.ownerId;
4023
+ var layoutView = ownerId
4024
+ ? this._layoutViewManager.getAt(ownerId)
4025
+ : undefined;
4026
+ if (!layoutView) {
4027
+ if (ownerId)
4028
+ this._viewportRepairOwnerIds.add(ownerId);
4029
+ return;
4030
+ }
4031
+ layoutView.addViewport(new AcTrViewportView(layoutView, this.toGiViewportWithFrozenLayers(entity), this._renderer));
4032
+ this._isDirty = true;
4033
+ };
3651
4034
  /**
3652
4035
  * Walks the given block table record once and creates one
3653
4036
  * `AcTrViewportView` for every real `AcDbViewport` entity it finds
@@ -3676,7 +4059,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3676
4059
  continue;
3677
4060
  if (AcTrViewportView.isDefaultPaperSpaceViewport(entity))
3678
4061
  continue;
3679
- var viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this._renderer);
4062
+ var viewportView = new AcTrViewportView(layoutView, this.toGiViewportWithFrozenLayers(entity), this._renderer);
3680
4063
  layoutView.addViewport(viewportView);
3681
4064
  }
3682
4065
  }
@@ -3688,6 +4071,44 @@ var AcTrView2d = /** @class */ (function (_super) {
3688
4071
  finally { if (e_16) throw e_16.error; }
3689
4072
  }
3690
4073
  };
4074
+ /**
4075
+ * Builds an {@link AcGiViewport} and ensures VPLAYER frozen layers are
4076
+ * present as resolved names. Older data-model builds read group 331 into
4077
+ * {@link AcDbViewport.frozenLayerIds} but omit them from `toGiViewport()`.
4078
+ */
4079
+ AcTrView2d.prototype.toGiViewportWithFrozenLayers = function (entity) {
4080
+ var e_17, _a;
4081
+ var _b;
4082
+ var gi = entity.toGiViewport();
4083
+ if (gi.frozenLayers && gi.frozenLayers.length > 0) {
4084
+ return gi;
4085
+ }
4086
+ var ids = entity.frozenLayerIds;
4087
+ if (!(ids === null || ids === void 0 ? void 0 : ids.length))
4088
+ return gi;
4089
+ var names = [];
4090
+ var layerTable = (_b = entity.database) === null || _b === void 0 ? void 0 : _b.tables.layerTable;
4091
+ if (layerTable) {
4092
+ try {
4093
+ for (var ids_1 = __values(ids), ids_1_1 = ids_1.next(); !ids_1_1.done; ids_1_1 = ids_1.next()) {
4094
+ var id = ids_1_1.value;
4095
+ var layer = layerTable.getIdAt(id);
4096
+ if (layer)
4097
+ names.push(layer.name);
4098
+ }
4099
+ }
4100
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
4101
+ finally {
4102
+ try {
4103
+ if (ids_1_1 && !ids_1_1.done && (_a = ids_1.return)) _a.call(ids_1);
4104
+ }
4105
+ finally { if (e_17) throw e_17.error; }
4106
+ }
4107
+ }
4108
+ gi.frozenLayerIds = __spreadArray([], __read(ids), false);
4109
+ gi.frozenLayers = names;
4110
+ return gi;
4111
+ };
3691
4112
  /**
3692
4113
  * Converts the specified database entities to three entities
3693
4114
  * @param entities - The database entities
@@ -3695,12 +4116,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3695
4116
  */
3696
4117
  AcTrView2d.prototype.batchConvert = function (entities_3) {
3697
4118
  return __awaiter(this, arguments, void 0, function (entities, options) {
3698
- var epoch, db, progressive, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
4119
+ var epoch, db, progressive, yieldBudgetMs, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
3699
4120
  var _this = this;
3700
- var _a, _b, _c;
4121
+ var _a, _b, _c, _d;
3701
4122
  if (options === void 0) { options = {}; }
3702
- return __generator(this, function (_d) {
3703
- switch (_d.label) {
4123
+ return __generator(this, function (_e) {
4124
+ switch (_e.label) {
3704
4125
  case 0:
3705
4126
  epoch = this._convertEpoch;
3706
4127
  // Fallback: if STYLE-stage preload never started (e.g. non-conversion open
@@ -3713,21 +4134,24 @@ var AcTrView2d = /** @class */ (function (_super) {
3713
4134
  }
3714
4135
  }
3715
4136
  progressive = this._progressiveRendering && !options.forExport;
4137
+ yieldBudgetMs = progressive
4138
+ ? AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS
4139
+ : AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS;
3716
4140
  yieldGate = options.forExport || !this._cooperativeYield
3717
4141
  ? undefined
3718
- : new AcCmUiYieldGate(AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS);
4142
+ : new AcCmUiYieldGate(yieldBudgetMs);
3719
4143
  yieldToEventLoop = function () {
3720
4144
  return new Promise(function (resolve) { return setTimeout(resolve, 0); });
3721
4145
  };
3722
4146
  _loop_4 = function (i) {
3723
- var entity, shouldConvert, objectId_1, convertGen_1, directMeta, added, superseded, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, layoutView, viewportView, fileName, error_3, didYield;
3724
- return __generator(this, function (_e) {
3725
- switch (_e.label) {
4147
+ var entity, shouldConvert, objectId_1, convertGen_1, directMeta, added, superseded, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, fileName, error_3, failedId, didYield;
4148
+ return __generator(this, function (_f) {
4149
+ switch (_f.label) {
3726
4150
  case 0:
3727
4151
  entity = entities[i];
3728
- _e.label = 1;
4152
+ _f.label = 1;
3729
4153
  case 1:
3730
- _e.trys.push([1, 9, 10, 11]);
4154
+ _f.trys.push([1, 9, 10, 11]);
3731
4155
  // Document was cleared / replaced while this batch was draining.
3732
4156
  if (epoch !== this_3._convertEpoch) {
3733
4157
  return [2 /*return*/, "continue"];
@@ -3755,16 +4179,20 @@ var AcTrView2d = /** @class */ (function (_super) {
3755
4179
  }
3756
4180
  objectId_1 = String((_a = entity.objectId) !== null && _a !== void 0 ? _a : '');
3757
4181
  convertGen_1 = 0;
3758
- if (!options.forExport) {
3759
- if (objectId_1 &&
3760
- (this_3.hasEntity(objectId_1) ||
3761
- this_3._claimedConvertObjectIds.has(objectId_1))) {
3762
- return [2 /*return*/, "continue"];
3763
- }
3764
- if (objectId_1) {
3765
- this_3._claimedConvertObjectIds.add(objectId_1);
3766
- convertGen_1 = (_b = this_3._entityConvertGeneration.get(objectId_1)) !== null && _b !== void 0 ? _b : 0;
4182
+ if (objectId_1 &&
4183
+ (this_3.hasEntity(objectId_1) ||
4184
+ this_3._claimedConvertObjectIds.has(objectId_1))) {
4185
+ // The border may already be in the scene from a pass that ran
4186
+ // before the layout view existed. Still attach the viewport
4187
+ // view; a second border is not drawn because we continue below.
4188
+ if (entity instanceof AcDbViewport) {
4189
+ this_3.attachViewportView(entity);
3767
4190
  }
4191
+ return [2 /*return*/, "continue"];
4192
+ }
4193
+ if (objectId_1) {
4194
+ this_3._claimedConvertObjectIds.add(objectId_1);
4195
+ convertGen_1 = (_b = this_3._entityConvertGeneration.get(objectId_1)) !== null && _b !== void 0 ? _b : 0;
3768
4196
  }
3769
4197
  directMeta = tryBuildDirectEntityMeta(entity, this_3._renderer);
3770
4198
  if (directMeta) {
@@ -3783,6 +4211,9 @@ var AcTrView2d = /** @class */ (function (_super) {
3783
4211
  this_3.markProgressiveDirty();
3784
4212
  this_3._progressiveOpenFit.afterGeometryBatch(function () { return _this.getDrawingExtents(); }, i);
3785
4213
  }
4214
+ else {
4215
+ this_3.markInteractiveOpenDirty();
4216
+ }
3786
4217
  }
3787
4218
  }
3788
4219
  }
@@ -3797,8 +4228,14 @@ var AcTrView2d = /** @class */ (function (_super) {
3797
4228
  threeEntity_1 = this_3.drawEntity(entity, false);
3798
4229
  // Viewports may produce no border geometry (e.g. on a no-plot layer) while
3799
4230
  // still needing an AcTrViewportView for model content below.
3800
- if (!threeEntity_1 && !(entity instanceof AcDbViewport))
4231
+ if (!threeEntity_1 && !(entity instanceof AcDbViewport)) {
4232
+ // Release the claim so a later export pass can retry; otherwise
4233
+ // `forExport` claim checks would skip this id forever.
4234
+ if (objectId_1) {
4235
+ this_3._claimedConvertObjectIds.delete(objectId_1);
4236
+ }
3801
4237
  return [2 /*return*/, "continue"];
4238
+ }
3802
4239
  if (!threeEntity_1) return [3 /*break*/, 8];
3803
4240
  threeEntity_1.objectId = entity.objectId;
3804
4241
  threeEntity_1.ownerId = entity.ownerId;
@@ -3823,8 +4260,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3823
4260
  return [3 /*break*/, 4];
3824
4261
  case 2: return [4 /*yield*/, this_3.handleGroup(threeEntity_1, progressive, epoch, convertGen_1)];
3825
4262
  case 3:
3826
- _e.sent();
3827
- _e.label = 4;
4263
+ _f.sent();
4264
+ _f.label = 4;
3828
4265
  case 4: return [3 /*break*/, 8];
3829
4266
  case 5:
3830
4267
  isExtendBbox_1 = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
@@ -3863,6 +4300,9 @@ var AcTrView2d = /** @class */ (function (_super) {
3863
4300
  this.markProgressiveDirty();
3864
4301
  this._progressiveOpenFit.afterGeometryBatch(function () { return _this.getDrawingExtents(); }, i);
3865
4302
  }
4303
+ else {
4304
+ this.markInteractiveOpenDirty();
4305
+ }
3866
4306
  return [2 /*return*/];
3867
4307
  }
3868
4308
  });
@@ -3872,8 +4312,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3872
4312
  return [3 /*break*/, 8];
3873
4313
  case 6: return [4 /*yield*/, commitEntity()];
3874
4314
  case 7:
3875
- _e.sent();
3876
- _e.label = 8;
4315
+ _f.sent();
4316
+ _f.label = 8;
3877
4317
  case 8:
3878
4318
  if (epoch !== this_3._convertEpoch) {
3879
4319
  return [2 /*return*/, "continue"];
@@ -3884,13 +4324,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3884
4324
  // user-created viewport. The redundant check below is kept as
3885
4325
  // a defensive guard in case a future refactor reorders the
3886
4326
  // early-skip — it costs ~nothing and prevents a regression.
3887
- if (!AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
3888
- layoutView = this_3._layoutViewManager.getAt(entity.ownerId);
3889
- if (layoutView) {
3890
- viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this_3._renderer);
3891
- layoutView.addViewport(viewportView);
3892
- }
3893
- }
4327
+ this_3.attachViewportView(entity);
3894
4328
  }
3895
4329
  else if (entity instanceof AcDbRasterImage) {
3896
4330
  fileName = entity.imageFileName;
@@ -3903,7 +4337,11 @@ var AcTrView2d = /** @class */ (function (_super) {
3903
4337
  }
3904
4338
  return [3 /*break*/, 11];
3905
4339
  case 9:
3906
- error_3 = _e.sent();
4340
+ error_3 = _f.sent();
4341
+ failedId = String((_d = entity.objectId) !== null && _d !== void 0 ? _d : '');
4342
+ if (failedId) {
4343
+ this_3._claimedConvertObjectIds.delete(failedId);
4344
+ }
3907
4345
  log.error("[AcTrView2d] Failed to convert entity ".concat(entity.objectId, " (").concat(entity.type, "):"), error_3);
3908
4346
  return [3 /*break*/, 11];
3909
4347
  case 10:
@@ -3917,27 +4355,37 @@ var AcTrView2d = /** @class */ (function (_super) {
3917
4355
  if (epoch !== this_3._convertEpoch) {
3918
4356
  return [2 /*return*/, "continue"];
3919
4357
  }
3920
- if (!yieldGate) return [3 /*break*/, 13];
3921
- return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
4358
+ if (!progressive) return [3 /*break*/, 13];
4359
+ // Prefer input-aware yields so pan/zoom are not starved after the
4360
+ // overlay is gone; paint throttling stays in markProgressiveDirty.
4361
+ return [4 /*yield*/, this_3.progressiveYieldForInput(yieldGate, yieldToEventLoop)];
3922
4362
  case 12:
3923
- didYield = _e.sent();
4363
+ // Prefer input-aware yields so pan/zoom are not starved after the
4364
+ // overlay is gone; paint throttling stays in markProgressiveDirty.
4365
+ _f.sent();
4366
+ return [3 /*break*/, 15];
4367
+ case 13:
4368
+ if (!yieldGate) return [3 /*break*/, 15];
4369
+ return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
4370
+ case 14:
4371
+ didYield = _f.sent();
3924
4372
  if (didYield) {
3925
4373
  this_3._progressiveYieldCount++;
3926
4374
  }
3927
- _e.label = 13;
3928
- case 13: return [2 /*return*/];
4375
+ _f.label = 15;
4376
+ case 15: return [2 /*return*/];
3929
4377
  }
3930
4378
  });
3931
4379
  };
3932
4380
  this_3 = this;
3933
4381
  i = 0;
3934
- _d.label = 1;
4382
+ _e.label = 1;
3935
4383
  case 1:
3936
4384
  if (!(i < entities.length)) return [3 /*break*/, 4];
3937
4385
  return [5 /*yield**/, _loop_4(i)];
3938
4386
  case 2:
3939
- _d.sent();
3940
- _d.label = 3;
4387
+ _e.sent();
4388
+ _e.label = 3;
3941
4389
  case 3:
3942
4390
  ++i;
3943
4391
  return [3 /*break*/, 1];
@@ -3951,6 +4399,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3951
4399
  * with their union before spatial-index registration.
3952
4400
  */
3953
4401
  AcTrView2d.prototype.syncGroupSpatialBoundsForIndexing = function (group) {
4402
+ var lazy = group.peekLazyChildBoxes();
4403
+ if (lazy && lazy.boxes.length > 0) {
4404
+ var userData_1 = group.userData;
4405
+ userData_1.lazySpatialChildren = lazy;
4406
+ return;
4407
+ }
3954
4408
  group.refreshWcsChildBoxesFromChildren();
3955
4409
  if (group.wcsChildBoxes.length === 0) {
3956
4410
  return;
@@ -3968,45 +4422,45 @@ var AcTrView2d = /** @class */ (function (_super) {
3968
4422
  };
3969
4423
  AcTrView2d.prototype.handleGroup = function (group_1, progressive_1) {
3970
4424
  return __awaiter(this, arguments, void 0, function (group, progressive, epoch, convertGen) {
3971
- var t0, objectId, children, objectsGroupByLayer_1, children_1, children_1_1, child, renderContext_1, groupObjectId_1, groupLayerName_1, groupChildBoxes_1, aggregateSpatialBbox_1, registeredChildIndex_1;
3972
- var e_17, _a;
4425
+ var t0, objectId, children, objectsGroupByLayer_2, children_1, children_1_1, child, renderContext, groupObjectId, groupLayerName, lazyChildren, groupChildBoxes, aggregateSpatialBbox, registeredChildIndex, yieldToEventLoop, lastGroupYieldAt, _loop_5, this_4, objectsGroupByLayer_1, objectsGroupByLayer_1_1, _a, layerName, objects, e_18_1;
4426
+ var e_19, _b, e_18, _c;
3973
4427
  var _this = this;
3974
- var _b, _c;
4428
+ var _d, _e;
3975
4429
  if (epoch === void 0) { epoch = this._convertEpoch; }
3976
4430
  if (convertGen === void 0) { convertGen = 0; }
3977
- return __generator(this, function (_d) {
3978
- switch (_d.label) {
4431
+ return __generator(this, function (_f) {
4432
+ switch (_f.label) {
3979
4433
  case 0:
3980
4434
  t0 = performance.now();
3981
- _d.label = 1;
4435
+ _f.label = 1;
3982
4436
  case 1:
3983
- _d.trys.push([1, , 3, 4]);
4437
+ _f.trys.push([1, , 11, 12]);
3984
4438
  return [4 /*yield*/, this.finishEntityGeometry(group, progressive)];
3985
4439
  case 2:
3986
- _d.sent();
4440
+ _f.sent();
3987
4441
  if (epoch !== this._convertEpoch) {
3988
4442
  group.dispose();
3989
4443
  return [2 /*return*/];
3990
4444
  }
3991
- objectId = String((_b = group.objectId) !== null && _b !== void 0 ? _b : '');
4445
+ objectId = String((_d = group.objectId) !== null && _d !== void 0 ? _d : '');
3992
4446
  if (objectId &&
3993
- ((_c = this._entityConvertGeneration.get(objectId)) !== null && _c !== void 0 ? _c : 0) !== convertGen) {
4447
+ ((_e = this._entityConvertGeneration.get(objectId)) !== null && _e !== void 0 ? _e : 0) !== convertGen) {
3994
4448
  group.dispose();
3995
4449
  return [2 /*return*/];
3996
4450
  }
3997
4451
  this.syncGroupSpatialBoundsForIndexing(group);
3998
4452
  children = group.children;
3999
- objectsGroupByLayer_1 = new Map();
4453
+ objectsGroupByLayer_2 = new Map();
4000
4454
  children.forEach(function (child) {
4001
4455
  var _a;
4002
4456
  if (child.visible === false) {
4003
4457
  return;
4004
4458
  }
4005
4459
  var layerName = child.userData.layerName;
4006
- if (!objectsGroupByLayer_1.has(layerName)) {
4007
- objectsGroupByLayer_1.set(layerName, []);
4460
+ if (!objectsGroupByLayer_2.has(layerName)) {
4461
+ objectsGroupByLayer_2.set(layerName, []);
4008
4462
  }
4009
- (_a = objectsGroupByLayer_1.get(layerName)) === null || _a === void 0 ? void 0 : _a.push(child);
4463
+ (_a = objectsGroupByLayer_2.get(layerName)) === null || _a === void 0 ? void 0 : _a.push(child);
4010
4464
  });
4011
4465
  // Important:
4012
4466
  // Sometimes one group may contain huge amount of objects (> 100,000). So it is important
@@ -4024,89 +4478,136 @@ var AcTrView2d = /** @class */ (function (_super) {
4024
4478
  child.parent = null;
4025
4479
  }
4026
4480
  }
4027
- catch (e_17_1) { e_17 = { error: e_17_1 }; }
4481
+ catch (e_19_1) { e_19 = { error: e_19_1 }; }
4028
4482
  finally {
4029
4483
  try {
4030
- if (children_1_1 && !children_1_1.done && (_a = children_1.return)) _a.call(children_1);
4484
+ if (children_1_1 && !children_1_1.done && (_b = children_1.return)) _b.call(children_1);
4031
4485
  }
4032
- finally { if (e_17) throw e_17.error; }
4486
+ finally { if (e_19) throw e_19.error; }
4033
4487
  }
4034
- renderContext_1 = group.renderContext;
4035
- groupObjectId_1 = group.objectId;
4036
- groupLayerName_1 = group.layerName;
4037
- // AcDbRenderingCache.draw (and similar paths such as AcDbTable) already call
4038
- // applyMatrix on the group, which updates wcsBbbox and wcsChildBoxes to WCS.
4039
- // Do not multiply group.matrix here — that would double-transform spatial bounds.
4040
- if (process.env.NODE_ENV !== 'production') {
4488
+ renderContext = group.renderContext;
4489
+ groupObjectId = group.objectId;
4490
+ groupLayerName = group.layerName;
4491
+ lazyChildren = group.userData.lazySpatialChildren;
4492
+ if (process.env.NODE_ENV !== 'production' && !lazyChildren) {
4041
4493
  assertAcTrGroupWcsBboxesConsistent(group);
4042
4494
  }
4043
- groupChildBoxes_1 = group.wcsChildBoxes.map(function (box) { return ({
4044
- minX: box.minX,
4045
- minY: box.minY,
4046
- maxX: box.maxX,
4047
- maxY: box.maxY,
4048
- id: box.id
4049
- }); });
4050
- aggregateSpatialBbox_1 = groupChildBoxes_1.length > 0
4051
- ? unionGroupWcsChildBoxes(group)
4052
- : group.wcsBbox.clone();
4053
- if (groupChildBoxes_1.length > 0) {
4054
- group.wcsBbox = aggregateSpatialBbox_1.clone();
4495
+ groupChildBoxes = lazyChildren
4496
+ ? []
4497
+ : group.wcsChildBoxes.map(function (box) { return ({
4498
+ minX: box.minX,
4499
+ minY: box.minY,
4500
+ maxX: box.maxX,
4501
+ maxY: box.maxY,
4502
+ id: box.id
4503
+ }); });
4504
+ aggregateSpatialBbox = lazyChildren
4505
+ ? group.wcsBbox.clone()
4506
+ : groupChildBoxes.length > 0
4507
+ ? unionGroupWcsChildBoxes(group)
4508
+ : group.wcsBbox.clone();
4509
+ if (groupChildBoxes.length > 0) {
4510
+ group.wcsBbox = aggregateSpatialBbox.clone();
4055
4511
  }
4056
- registeredChildIndex_1 = false;
4057
- objectsGroupByLayer_1.forEach(function (objects, layerName) {
4058
- // Nested layer-0 may already be resolved to an inner INSERT layer during
4059
- // flatten. Remaining "0" buckets inherit this (outermost) INSERT layer.
4060
- var effectiveLayerName = layerName === '0' ? groupLayerName_1 : layerName;
4061
- // Material remap must still treat authored layer-0 drawables as layer-0
4062
- // ByLayer even when nest resolution already rewrote layerName.
4063
- var sourceLayerForMaterials = objects.some(function (object) {
4064
- var _a;
4065
- var data = object.userData;
4066
- return ((_a = data.authoredLayerName) !== null && _a !== void 0 ? _a : layerName) === '0';
4067
- })
4068
- ? '0'
4069
- : layerName;
4070
- // Keep runtime layer metadata/material cache aligned with the inherited layer so
4071
- // later layer style edits (color, linetype, lineweight, transparency) target this
4072
- // object set correctly.
4073
- _this._inheritedLayerMaterialMapper.remap(objects, sourceLayerForMaterials, effectiveLayerName);
4074
- // One INSERT can expand to children from multiple layers. Here we create one
4075
- // render entity per layer bucket but preserve the INSERT object id for all
4076
- // buckets, so selection/highlight still maps back to the same database object.
4077
- // Within each layer bucket, the object id remains unique in scene indexing.
4078
- var entity = new AcTrEntity(renderContext_1);
4079
- // Copy the INSERT matrix exactly — applyMatrix4 decomposes and drops
4080
- // reflections from mirrored block scales.
4081
- entity.matrix.copy(group.matrix);
4082
- entity.matrixAutoUpdate = false;
4083
- entity.matrixWorldNeedsUpdate = true;
4084
- entity.objectId = groupObjectId_1;
4085
- entity.ownerId = group.ownerId;
4086
- // If block-definition entities are on layer "0", this bucket now uses the layer
4087
- // of the block reference itself (effectiveLayerName).
4088
- entity.layerName = effectiveLayerName;
4089
- entity.userData.insertLayerName = groupLayerName_1;
4090
- entity.wcsBbox = aggregateSpatialBbox_1.clone();
4091
- var entityUserData = entity.userData;
4092
- if (!registeredChildIndex_1 && groupChildBoxes_1.length > 0) {
4093
- entityUserData.spatialIndexChildBoxes = groupChildBoxes_1;
4094
- registeredChildIndex_1 = true;
4095
- }
4096
- // Important:
4097
- // DO NOT USE spread operator when adding objects because it may be one very large array
4098
- // and can result in maximum call stack size exceeded
4099
- for (var i = 0; i < objects.length; i++) {
4100
- entity.add(objects[i]);
4101
- }
4102
- entity.updateMatrixWorld(true);
4103
- _this._layerAppearance.refreshTextMaterialsInObjectTree(entity);
4104
- var tAdd = performance.now();
4105
- _this._scene.addEntity(entity, true);
4106
- _this._convertPhaseMs.addEntity += performance.now() - tAdd;
4107
- _this.applySessionHiddenObjectState(groupObjectId_1);
4108
- entity.dispose();
4109
- });
4512
+ registeredChildIndex = false;
4513
+ yieldToEventLoop = function () {
4514
+ return new Promise(function (resolve) { return setTimeout(resolve, 0); });
4515
+ };
4516
+ lastGroupYieldAt = progressive ? performance.now() : 0;
4517
+ _loop_5 = function (layerName, objects) {
4518
+ var effectiveLayerName, sourceLayerForMaterials, entity, entityUserData, i, tAdd;
4519
+ return __generator(this, function (_g) {
4520
+ switch (_g.label) {
4521
+ case 0:
4522
+ effectiveLayerName = layerName === '0' ? groupLayerName : layerName;
4523
+ sourceLayerForMaterials = objects.some(function (object) {
4524
+ var _a;
4525
+ var data = object.userData;
4526
+ return ((_a = data.authoredLayerName) !== null && _a !== void 0 ? _a : layerName) === '0';
4527
+ })
4528
+ ? '0'
4529
+ : layerName;
4530
+ // Keep runtime layer metadata/material cache aligned with the inherited layer so
4531
+ // later layer style edits (color, linetype, lineweight, transparency) target this
4532
+ // object set correctly.
4533
+ this_4._inheritedLayerMaterialMapper.remap(objects, sourceLayerForMaterials, effectiveLayerName);
4534
+ entity = new AcTrEntity(renderContext);
4535
+ // Copy the INSERT matrix exactly — applyMatrix4 decomposes and drops
4536
+ // reflections from mirrored block scales.
4537
+ entity.matrix.copy(group.matrix);
4538
+ entity.matrixAutoUpdate = false;
4539
+ entity.matrixWorldNeedsUpdate = true;
4540
+ entity.objectId = groupObjectId;
4541
+ entity.ownerId = group.ownerId;
4542
+ // If block-definition entities are on layer "0", this bucket now uses the layer
4543
+ // of the block reference itself (effectiveLayerName).
4544
+ entity.layerName = effectiveLayerName;
4545
+ entity.userData.insertLayerName = groupLayerName;
4546
+ entity.wcsBbox = aggregateSpatialBbox.clone();
4547
+ entityUserData = entity.userData;
4548
+ if (!registeredChildIndex && lazyChildren) {
4549
+ ;
4550
+ entity.userData.lazySpatialChildren = lazyChildren;
4551
+ registeredChildIndex = true;
4552
+ }
4553
+ else if (!registeredChildIndex && groupChildBoxes.length > 0) {
4554
+ entityUserData.spatialIndexChildBoxes = groupChildBoxes;
4555
+ registeredChildIndex = true;
4556
+ }
4557
+ // Important:
4558
+ // DO NOT USE spread operator when adding objects because it may be one very large array
4559
+ // and can result in maximum call stack size exceeded
4560
+ for (i = 0; i < objects.length; i++) {
4561
+ entity.add(objects[i]);
4562
+ }
4563
+ entity.updateMatrixWorld(true);
4564
+ this_4._layerAppearance.refreshTextMaterialsInObjectTree(entity);
4565
+ tAdd = performance.now();
4566
+ this_4._scene.addEntity(entity, true);
4567
+ this_4._convertPhaseMs.addEntity += performance.now() - tAdd;
4568
+ this_4.applySessionHiddenObjectState(groupObjectId);
4569
+ entity.dispose();
4570
+ if (!(progressive &&
4571
+ epoch === this_4._convertEpoch &&
4572
+ performance.now() - lastGroupYieldAt >=
4573
+ AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS)) return [3 /*break*/, 2];
4574
+ return [4 /*yield*/, yieldToEventLoop()];
4575
+ case 1:
4576
+ _g.sent();
4577
+ lastGroupYieldAt = performance.now();
4578
+ _g.label = 2;
4579
+ case 2: return [2 /*return*/];
4580
+ }
4581
+ });
4582
+ };
4583
+ this_4 = this;
4584
+ _f.label = 3;
4585
+ case 3:
4586
+ _f.trys.push([3, 8, 9, 10]);
4587
+ objectsGroupByLayer_1 = __values(objectsGroupByLayer_2), objectsGroupByLayer_1_1 = objectsGroupByLayer_1.next();
4588
+ _f.label = 4;
4589
+ case 4:
4590
+ if (!!objectsGroupByLayer_1_1.done) return [3 /*break*/, 7];
4591
+ _a = __read(objectsGroupByLayer_1_1.value, 2), layerName = _a[0], objects = _a[1];
4592
+ return [5 /*yield**/, _loop_5(layerName, objects)];
4593
+ case 5:
4594
+ _f.sent();
4595
+ _f.label = 6;
4596
+ case 6:
4597
+ objectsGroupByLayer_1_1 = objectsGroupByLayer_1.next();
4598
+ return [3 /*break*/, 4];
4599
+ case 7: return [3 /*break*/, 10];
4600
+ case 8:
4601
+ e_18_1 = _f.sent();
4602
+ e_18 = { error: e_18_1 };
4603
+ return [3 /*break*/, 10];
4604
+ case 9:
4605
+ try {
4606
+ if (objectsGroupByLayer_1_1 && !objectsGroupByLayer_1_1.done && (_c = objectsGroupByLayer_1.return)) _c.call(objectsGroupByLayer_1);
4607
+ }
4608
+ finally { if (e_18) throw e_18.error; }
4609
+ return [7 /*endfinally*/];
4610
+ case 10:
4110
4611
  group.dispose();
4111
4612
  if (progressive) {
4112
4613
  this.markProgressiveDirty();
@@ -4114,11 +4615,14 @@ var AcTrView2d = /** @class */ (function (_super) {
4114
4615
  return _this.getDrawingExtents();
4115
4616
  });
4116
4617
  }
4117
- return [3 /*break*/, 4];
4118
- case 3:
4618
+ else {
4619
+ this.markInteractiveOpenDirty();
4620
+ }
4621
+ return [3 /*break*/, 12];
4622
+ case 11:
4119
4623
  this._convertPhaseMs.handleGroup += performance.now() - t0;
4120
4624
  return [7 /*endfinally*/];
4121
- case 4: return [2 /*return*/];
4625
+ case 12: return [2 /*return*/];
4122
4626
  }
4123
4627
  });
4124
4628
  });
@@ -4173,27 +4677,57 @@ var AcTrView2d = /** @class */ (function (_super) {
4173
4677
  }
4174
4678
  };
4175
4679
  /**
4176
- * Wall-time between cooperative yields during scene convert (ms).
4177
- * Used for both progressive and non-progressive opens so large drawings
4178
- * (e.g. multi-sheet architectural DWGs) cannot freeze the main thread long
4179
- * enough for Chromium to show "Page Unresponsive" while the overlay still
4180
- * reads "Rendering drawing ...". Kept relatively large so convert
4181
- * throughput stays high; smaller budgets made open 2–3× slower.
4680
+ * Wall-time between cooperative yields during scene convert (ms) when
4681
+ * progressive rendering is off. Overlay still blocks input, so a larger
4682
+ * budget keeps throughput high while avoiding Chromium "Page Unresponsive".
4182
4683
  */
4183
4684
  AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS = 300;
4184
4685
  /**
4185
- * Minimum interval between progressive mid-open paints (ms).
4186
- * Full-scene WebGL paints dominate open wall time on large drawings;
4187
- * paint much less often than we yield. Independent of convert yields —
4188
- * non-progressive opens still yield without mid-open WebGL paints.
4686
+ * Yield cadence during progressive open (ms). Overlay stays hidden; keep
4687
+ * slices near one frame so pan/zoom handlers can run. Discrete
4688
+ * `navigator.scheduling.isInputPending` yields once so handlers run;
4689
+ * pointerdown/wheel set a short pause so OrbitControls can catch up.
4690
+ */
4691
+ AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS = 32;
4692
+ /** Extra idle window after canvas pointerdown/wheel so gestures stay fluid. */
4693
+ AcTrView2d.PROGRESSIVE_INPUT_PAUSE_MS = 120;
4694
+ /**
4695
+ * Minimum interval between progressive mid-open paints driven by convert
4696
+ * (ms). Full-scene WebGL paints dominate open wall time; user camera
4697
+ * changes bypass this throttle via {@link _progressivePaintUrgent}.
4189
4698
  */
4190
- AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 1000;
4699
+ AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 2500;
4700
+ /**
4701
+ * Minimum interval between interactive non-progressive mid-open paints (ms)
4702
+ * while convert / deferred glyphs run under "Rendering drawing ...".
4703
+ * Longer than {@link PROGRESSIVE_OPEN_PAINT_INTERVAL_MS}: the overlay is
4704
+ * semi-transparent (`rgba(0,0,0,0.45)`), but each paint still redraws the
4705
+ * full scene and used to fire on every glyph completion. Headless
4706
+ * (`cooperativeYield` false) skips these paints entirely.
4707
+ */
4708
+ AcTrView2d.NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 4000;
4191
4709
  /**
4192
4710
  * Max concurrent deferred glyph/INSERT geometry finalizers. Large drawings
4193
4711
  * enqueue thousands of jobs; keep this modest to limit peak JS heap during
4194
4712
  * open while retaining reasonable text throughput after convert finishes.
4195
4713
  */
4196
4714
  AcTrView2d.DEFERRED_GEOMETRY_CONCURRENCY = 8;
4715
+ /**
4716
+ * Target wall time of one interactive convert slice (ms) while a paper
4717
+ * layout is active.
4718
+ *
4719
+ * Slices return to the drain so a layout activated mid-open can jump ahead
4720
+ * of a long model-space batch. Kept well above one frame (≈50–100 ms) so
4721
+ * the fixed cost of each slice — yield, paint, queue absorb — does not
4722
+ * dominate total load time. The 300ms yield inside {@link batchConvert}
4723
+ * still applies within a slice.
4724
+ */
4725
+ AcTrView2d.CONVERT_SLICE_BUDGET_MS = 64;
4726
+ /**
4727
+ * Upper bound on entities per slice. Heavy block references stay at 1
4728
+ * because a slice that overruns the time budget is halved.
4729
+ */
4730
+ AcTrView2d.CONVERT_SLICE_MAX = 512;
4197
4731
  return AcTrView2d;
4198
4732
  }(AcEdBaseView));
4199
4733
  export { AcTrView2d };